]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd_nbd/rbd-nbd.cc
db964d389374f18414a9e3d75e788f8dc6bc972a
[ceph.git] / ceph / src / tools / rbd_nbd / rbd-nbd.cc
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 "acconfig.h"
20 #include "include/int_types.h"
21 #include "include/scope_guard.h"
22
23 #include <boost/endian/conversion.hpp>
24
25 #include <libgen.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <poll.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35
36 #include <linux/nbd.h>
37 #include <linux/fs.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <sys/syscall.h>
41
42 #include "nbd-netlink.h"
43 #include <libnl3/netlink/genl/genl.h>
44 #include <libnl3/netlink/genl/ctrl.h>
45 #include <libnl3/netlink/genl/mngt.h>
46
47 #include <filesystem>
48 #include <fstream>
49 #include <iostream>
50 #include <memory>
51 #include <regex>
52 #include <boost/algorithm/string/predicate.hpp>
53 #include <boost/lexical_cast.hpp>
54
55 #include "common/Formatter.h"
56 #include "common/Preforker.h"
57 #include "common/SubProcess.h"
58 #include "common/TextTable.h"
59 #include "common/ceph_argparse.h"
60 #include "common/config.h"
61 #include "common/dout.h"
62 #include "common/errno.h"
63 #include "common/event_socket.h"
64 #include "common/module.h"
65 #include "common/safe_io.h"
66 #include "common/version.h"
67
68 #include "global/global_init.h"
69 #include "global/signal_handler.h"
70
71 #include "include/rados/librados.hpp"
72 #include "include/rbd/librbd.hpp"
73 #include "include/stringify.h"
74 #include "include/xlist.h"
75
76 #include "mon/MonClient.h"
77
78 #define dout_context g_ceph_context
79 #define dout_subsys ceph_subsys_rbd
80 #undef dout_prefix
81 #define dout_prefix *_dout << "rbd-nbd: "
82
83 using namespace std;
84 namespace fs = std::filesystem;
85
86 using boost::endian::big_to_native;
87 using boost::endian::native_to_big;
88
89 enum Command {
90 None,
91 Map,
92 Unmap,
93 Attach,
94 Detach,
95 List
96 };
97
98 struct Config {
99 int nbds_max = 0;
100 int max_part = 255;
101 int io_timeout = -1;
102 int reattach_timeout = 30;
103
104 bool exclusive = false;
105 bool notrim = false;
106 bool quiesce = false;
107 bool readonly = false;
108 bool set_max_part = false;
109 bool try_netlink = false;
110 bool show_cookie = false;
111
112 std::string poolname;
113 std::string nsname;
114 std::string imgname;
115 std::string snapname;
116 std::string devpath;
117 std::string quiesce_hook = CMAKE_INSTALL_LIBEXECDIR "/rbd-nbd/rbd-nbd_quiesce";
118
119 std::string format;
120 bool pretty_format = false;
121
122 std::optional<librbd::encryption_format_t> encryption_format;
123 std::optional<std::string> encryption_passphrase_file;
124
125 Command command = None;
126 int pid = 0;
127 std::string cookie;
128
129 std::string image_spec() const {
130 std::string spec = poolname + "/";
131
132 if (!nsname.empty()) {
133 spec += nsname + "/";
134 }
135 spec += imgname;
136
137 if (!snapname.empty()) {
138 spec += "@" + snapname;
139 }
140
141 return spec;
142 }
143 };
144
145 static void usage()
146 {
147 std::cout << "Usage: rbd-nbd [options] map <image-or-snap-spec> Map image to nbd device\n"
148 << " detach <device|image-or-snap-spec> Detach image from nbd device\n"
149 << " [options] attach <image-or-snap-spec> Attach image to nbd device\n"
150 << " unmap <device|image-or-snap-spec> Unmap nbd device\n"
151 << " [options] list-mapped List mapped nbd devices\n"
152 << "Map and attach options:\n"
153 << " --device <device path> Specify nbd device path (/dev/nbd{num})\n"
154 << " --encryption-format Image encryption format\n"
155 << " (possible values: luks1, luks2)\n"
156 << " --encryption-passphrase-file Path of file containing passphrase for unlocking image encryption\n"
157 << " --exclusive Forbid writes by other clients\n"
158 << " --notrim Turn off trim/discard\n"
159 << " --io-timeout <sec> Set nbd IO timeout\n"
160 << " --max_part <limit> Override for module param max_part\n"
161 << " --nbds_max <limit> Override for module param nbds_max\n"
162 << " --quiesce Use quiesce callbacks\n"
163 << " --quiesce-hook <path> Specify quiesce hook path\n"
164 << " (default: " << Config().quiesce_hook << ")\n"
165 << " --read-only Map read-only\n"
166 << " --reattach-timeout <sec> Set nbd re-attach timeout\n"
167 << " (default: " << Config().reattach_timeout << ")\n"
168 << " --try-netlink Use the nbd netlink interface\n"
169 << " --show-cookie Show device cookie\n"
170 << " --cookie Specify device cookie\n"
171 << "\n"
172 << "List options:\n"
173 << " --format plain|json|xml Output format (default: plain)\n"
174 << " --pretty-format Pretty formatting (json and xml)\n"
175 << std::endl;
176 generic_server_usage();
177 }
178
179 static int nbd = -1;
180 static int nbd_index = -1;
181 static EventSocket terminate_event_sock;
182
183 #define RBD_NBD_BLKSIZE 512UL
184
185 #define HELP_INFO 1
186 #define VERSION_INFO 2
187
188 static int parse_args(vector<const char*>& args, std::ostream *err_msg,
189 Config *cfg);
190 static int netlink_disconnect(int index);
191 static int netlink_resize(int nbd_index, uint64_t size);
192
193 static int run_quiesce_hook(const std::string &quiesce_hook,
194 const std::string &devpath,
195 const std::string &command);
196
197 static std::string get_cookie(const std::string &devpath);
198
199 class NBDServer
200 {
201 public:
202 uint64_t quiesce_watch_handle = 0;
203
204 private:
205 int fd;
206 librbd::Image &image;
207 Config *cfg;
208
209 public:
210 NBDServer(int fd, librbd::Image& image, Config *cfg)
211 : fd(fd)
212 , image(image)
213 , cfg(cfg)
214 , reader_thread(*this, &NBDServer::reader_entry)
215 , writer_thread(*this, &NBDServer::writer_entry)
216 , quiesce_thread(*this, &NBDServer::quiesce_entry)
217 {
218 std::vector<librbd::config_option_t> options;
219 image.config_list(&options);
220 for (auto &option : options) {
221 if ((option.name == std::string("rbd_cache") ||
222 option.name == std::string("rbd_cache_writethrough_until_flush")) &&
223 option.value == "false") {
224 allow_internal_flush = true;
225 break;
226 }
227 }
228 }
229
230 Config *get_cfg() const {
231 return cfg;
232 }
233
234 private:
235 int terminate_event_fd = -1;
236 ceph::mutex disconnect_lock =
237 ceph::make_mutex("NBDServer::DisconnectLocker");
238 ceph::condition_variable disconnect_cond;
239 std::atomic<bool> terminated = { false };
240 std::atomic<bool> allow_internal_flush = { false };
241
242 struct IOContext
243 {
244 xlist<IOContext*>::item item;
245 NBDServer *server = nullptr;
246 struct nbd_request request;
247 struct nbd_reply reply;
248 bufferlist data;
249 int command = 0;
250
251 IOContext()
252 : item(this)
253 {}
254 };
255
256 friend std::ostream &operator<<(std::ostream &os, const IOContext &ctx);
257
258 ceph::mutex lock = ceph::make_mutex("NBDServer::Locker");
259 ceph::condition_variable cond;
260 xlist<IOContext*> io_pending;
261 xlist<IOContext*> io_finished;
262
263 void io_start(IOContext *ctx)
264 {
265 std::lock_guard l{lock};
266 io_pending.push_back(&ctx->item);
267 }
268
269 void io_finish(IOContext *ctx)
270 {
271 std::lock_guard l{lock};
272 ceph_assert(ctx->item.is_on_list());
273 ctx->item.remove_myself();
274 io_finished.push_back(&ctx->item);
275 cond.notify_all();
276 }
277
278 IOContext *wait_io_finish()
279 {
280 std::unique_lock l{lock};
281 cond.wait(l, [this] {
282 return !io_finished.empty() ||
283 (io_pending.empty() && terminated);
284 });
285
286 if (io_finished.empty())
287 return NULL;
288
289 IOContext *ret = io_finished.front();
290 io_finished.pop_front();
291
292 return ret;
293 }
294
295 void wait_clean()
296 {
297 std::unique_lock l{lock};
298 cond.wait(l, [this] { return io_pending.empty(); });
299
300 while(!io_finished.empty()) {
301 std::unique_ptr<IOContext> free_ctx(io_finished.front());
302 io_finished.pop_front();
303 }
304 }
305
306 void assert_clean()
307 {
308 std::unique_lock l{lock};
309
310 ceph_assert(!reader_thread.is_started());
311 ceph_assert(!writer_thread.is_started());
312 ceph_assert(io_pending.empty());
313 ceph_assert(io_finished.empty());
314 }
315
316 static void aio_callback(librbd::completion_t cb, void *arg)
317 {
318 librbd::RBD::AioCompletion *aio_completion =
319 reinterpret_cast<librbd::RBD::AioCompletion*>(cb);
320
321 IOContext *ctx = reinterpret_cast<IOContext *>(arg);
322 int ret = aio_completion->get_return_value();
323
324 dout(20) << __func__ << ": " << *ctx << dendl;
325
326 if (ret == -EINVAL) {
327 // if shrinking an image, a pagecache writeback might reference
328 // extents outside of the range of the new image extents
329 dout(0) << __func__ << ": masking IO out-of-bounds error" << dendl;
330 ctx->data.clear();
331 ret = 0;
332 }
333
334 if (ret < 0) {
335 ctx->reply.error = native_to_big<uint32_t>(-ret);
336 } else if ((ctx->command == NBD_CMD_READ) &&
337 ret < static_cast<int>(ctx->request.len)) {
338 int pad_byte_count = static_cast<int> (ctx->request.len) - ret;
339 ctx->data.append_zero(pad_byte_count);
340 dout(20) << __func__ << ": " << *ctx << ": Pad byte count: "
341 << pad_byte_count << dendl;
342 ctx->reply.error = native_to_big<uint32_t>(0);
343 } else {
344 ctx->reply.error = native_to_big<uint32_t>(0);
345 }
346 ctx->server->io_finish(ctx);
347
348 aio_completion->release();
349 }
350
351 void reader_entry()
352 {
353 struct pollfd poll_fds[2];
354 memset(poll_fds, 0, sizeof(struct pollfd) * 2);
355 poll_fds[0].fd = fd;
356 poll_fds[0].events = POLLIN;
357 poll_fds[1].fd = terminate_event_fd;
358 poll_fds[1].events = POLLIN;
359
360 while (true) {
361 std::unique_ptr<IOContext> ctx(new IOContext());
362 ctx->server = this;
363
364 dout(20) << __func__ << ": waiting for nbd request" << dendl;
365
366 int r = poll(poll_fds, 2, -1);
367 if (r == -1) {
368 if (errno == EINTR) {
369 continue;
370 }
371 r = -errno;
372 derr << "failed to poll nbd: " << cpp_strerror(r) << dendl;
373 goto error;
374 }
375
376 if ((poll_fds[1].revents & POLLIN) != 0) {
377 dout(0) << __func__ << ": terminate received" << dendl;
378 goto signal;
379 }
380
381 if ((poll_fds[0].revents & POLLIN) == 0) {
382 dout(20) << __func__ << ": nothing to read" << dendl;
383 continue;
384 }
385
386 r = safe_read_exact(fd, &ctx->request, sizeof(struct nbd_request));
387 if (r < 0) {
388 derr << "failed to read nbd request header: " << cpp_strerror(r)
389 << dendl;
390 goto error;
391 }
392
393 if (ctx->request.magic != htonl(NBD_REQUEST_MAGIC)) {
394 derr << "invalid nbd request header" << dendl;
395 goto signal;
396 }
397
398 ctx->request.from = big_to_native(ctx->request.from);
399 ctx->request.type = big_to_native(ctx->request.type);
400 ctx->request.len = big_to_native(ctx->request.len);
401
402 ctx->reply.magic = native_to_big<uint32_t>(NBD_REPLY_MAGIC);
403 memcpy(ctx->reply.handle, ctx->request.handle, sizeof(ctx->reply.handle));
404
405 ctx->command = ctx->request.type & 0x0000ffff;
406
407 dout(20) << *ctx << ": start" << dendl;
408
409 switch (ctx->command)
410 {
411 case NBD_CMD_DISC:
412 // NBD_DO_IT will return when pipe is closed
413 dout(0) << "disconnect request received" << dendl;
414 goto signal;
415 case NBD_CMD_WRITE:
416 bufferptr ptr(ctx->request.len);
417 r = safe_read_exact(fd, ptr.c_str(), ctx->request.len);
418 if (r < 0) {
419 derr << *ctx << ": failed to read nbd request data: "
420 << cpp_strerror(r) << dendl;
421 goto error;
422 }
423 ctx->data.push_back(ptr);
424 break;
425 }
426
427 IOContext *pctx = ctx.release();
428 io_start(pctx);
429 librbd::RBD::AioCompletion *c = new librbd::RBD::AioCompletion(pctx, aio_callback);
430 switch (pctx->command)
431 {
432 case NBD_CMD_WRITE:
433 image.aio_write(pctx->request.from, pctx->request.len, pctx->data, c);
434 break;
435 case NBD_CMD_READ:
436 image.aio_read(pctx->request.from, pctx->request.len, pctx->data, c);
437 break;
438 case NBD_CMD_FLUSH:
439 image.aio_flush(c);
440 allow_internal_flush = true;
441 break;
442 case NBD_CMD_TRIM:
443 image.aio_discard(pctx->request.from, pctx->request.len, c);
444 break;
445 default:
446 derr << *pctx << ": invalid request command" << dendl;
447 c->release();
448 goto signal;
449 }
450 }
451 error:
452 {
453 int r = netlink_disconnect(nbd_index);
454 if (r == 1) {
455 ioctl(nbd, NBD_DISCONNECT);
456 }
457 }
458 signal:
459 std::lock_guard l{lock};
460 terminated = true;
461 cond.notify_all();
462
463 std::lock_guard disconnect_l{disconnect_lock};
464 disconnect_cond.notify_all();
465
466 dout(20) << __func__ << ": terminated" << dendl;
467 }
468
469 void writer_entry()
470 {
471 while (true) {
472 dout(20) << __func__ << ": waiting for io request" << dendl;
473 std::unique_ptr<IOContext> ctx(wait_io_finish());
474 if (!ctx) {
475 dout(20) << __func__ << ": no io requests, terminating" << dendl;
476 goto done;
477 }
478
479 dout(20) << __func__ << ": got: " << *ctx << dendl;
480
481 int r = safe_write(fd, &ctx->reply, sizeof(struct nbd_reply));
482 if (r < 0) {
483 derr << *ctx << ": failed to write reply header: " << cpp_strerror(r)
484 << dendl;
485 goto error;
486 }
487 if (ctx->command == NBD_CMD_READ && ctx->reply.error == htonl(0)) {
488 r = ctx->data.write_fd(fd);
489 if (r < 0) {
490 derr << *ctx << ": failed to write replay data: " << cpp_strerror(r)
491 << dendl;
492 goto error;
493 }
494 }
495 dout(20) << *ctx << ": finish" << dendl;
496 }
497 error:
498 wait_clean();
499 done:
500 ::shutdown(fd, SHUT_RDWR);
501
502 dout(20) << __func__ << ": terminated" << dendl;
503 }
504
505 bool wait_quiesce() {
506 dout(20) << __func__ << dendl;
507
508 std::unique_lock locker{lock};
509 cond.wait(locker, [this] { return quiesce || terminated; });
510
511 if (terminated) {
512 return false;
513 }
514
515 dout(20) << __func__ << ": got quiesce request" << dendl;
516 return true;
517 }
518
519 void wait_unquiesce(std::unique_lock<ceph::mutex> &locker) {
520 dout(20) << __func__ << dendl;
521
522 cond.wait(locker, [this] { return !quiesce || terminated; });
523
524 dout(20) << __func__ << ": got unquiesce request" << dendl;
525 }
526
527 void wait_inflight_io() {
528 if (!allow_internal_flush) {
529 return;
530 }
531
532 uint64_t features = 0;
533 image.features(&features);
534 if ((features & RBD_FEATURE_EXCLUSIVE_LOCK) != 0) {
535 bool is_owner = false;
536 image.is_exclusive_lock_owner(&is_owner);
537 if (!is_owner) {
538 return;
539 }
540 }
541
542 dout(20) << __func__ << dendl;
543
544 int r = image.flush();
545 if (r < 0) {
546 derr << "flush failed: " << cpp_strerror(r) << dendl;
547 }
548 }
549
550 void quiesce_entry()
551 {
552 ceph_assert(cfg->quiesce);
553
554 while (wait_quiesce()) {
555
556 int r = run_quiesce_hook(cfg->quiesce_hook, cfg->devpath, "quiesce");
557
558 wait_inflight_io();
559
560 {
561 std::unique_lock locker{lock};
562 ceph_assert(quiesce == true);
563
564 image.quiesce_complete(quiesce_watch_handle, r);
565
566 if (r < 0) {
567 quiesce = false;
568 continue;
569 }
570
571 wait_unquiesce(locker);
572 }
573
574 run_quiesce_hook(cfg->quiesce_hook, cfg->devpath, "unquiesce");
575 }
576
577 dout(20) << __func__ << ": terminated" << dendl;
578 }
579
580 class ThreadHelper : public Thread
581 {
582 public:
583 typedef void (NBDServer::*entry_func)();
584 private:
585 NBDServer &server;
586 entry_func func;
587 public:
588 ThreadHelper(NBDServer &_server, entry_func _func)
589 :server(_server)
590 ,func(_func)
591 {}
592 protected:
593 void* entry() override
594 {
595 (server.*func)();
596 return NULL;
597 }
598 } reader_thread, writer_thread, quiesce_thread;
599
600 bool started = false;
601 bool quiesce = false;
602
603 public:
604 void start()
605 {
606 if (!started) {
607 dout(10) << __func__ << ": starting" << dendl;
608
609 started = true;
610
611 terminate_event_fd = eventfd(0, EFD_NONBLOCK);
612 ceph_assert(terminate_event_fd > 0);
613 int r = terminate_event_sock.init(terminate_event_fd,
614 EVENT_SOCKET_TYPE_EVENTFD);
615 ceph_assert(r >= 0);
616
617 reader_thread.create("rbd_reader");
618 writer_thread.create("rbd_writer");
619 if (cfg->quiesce) {
620 quiesce_thread.create("rbd_quiesce");
621 }
622 }
623 }
624
625 void wait_for_disconnect()
626 {
627 if (!started)
628 return;
629
630 std::unique_lock l{disconnect_lock};
631 disconnect_cond.wait(l);
632 }
633
634 void notify_quiesce() {
635 dout(10) << __func__ << dendl;
636
637 ceph_assert(cfg->quiesce);
638
639 std::unique_lock locker{lock};
640 ceph_assert(quiesce == false);
641 quiesce = true;
642 cond.notify_all();
643 }
644
645 void notify_unquiesce() {
646 dout(10) << __func__ << dendl;
647
648 ceph_assert(cfg->quiesce);
649
650 std::unique_lock locker{lock};
651 ceph_assert(quiesce == true);
652 quiesce = false;
653 cond.notify_all();
654 }
655
656 ~NBDServer()
657 {
658 if (started) {
659 dout(10) << __func__ << ": terminating" << dendl;
660
661 terminate_event_sock.notify();
662
663 reader_thread.join();
664 writer_thread.join();
665 if (cfg->quiesce) {
666 quiesce_thread.join();
667 }
668
669 assert_clean();
670
671 close(terminate_event_fd);
672 started = false;
673 }
674 }
675 };
676
677 std::ostream &operator<<(std::ostream &os, const NBDServer::IOContext &ctx) {
678
679 os << "[" << std::hex << big_to_native(*((uint64_t *)ctx.request.handle));
680
681 switch (ctx.command)
682 {
683 case NBD_CMD_WRITE:
684 os << " WRITE ";
685 break;
686 case NBD_CMD_READ:
687 os << " READ ";
688 break;
689 case NBD_CMD_FLUSH:
690 os << " FLUSH ";
691 break;
692 case NBD_CMD_TRIM:
693 os << " TRIM ";
694 break;
695 case NBD_CMD_DISC:
696 os << " DISC ";
697 break;
698 default:
699 os << " UNKNOWN(" << ctx.command << ") ";
700 break;
701 }
702
703 os << ctx.request.from << "~" << ctx.request.len << " "
704 << std::dec << big_to_native(ctx.reply.error) << "]";
705
706 return os;
707 }
708
709 class NBDQuiesceWatchCtx : public librbd::QuiesceWatchCtx
710 {
711 public:
712 NBDQuiesceWatchCtx(NBDServer *server) : server(server) {
713 }
714
715 void handle_quiesce() override {
716 server->notify_quiesce();
717 }
718
719 void handle_unquiesce() override {
720 server->notify_unquiesce();
721 }
722
723 private:
724 NBDServer *server;
725 };
726
727 class NBDWatchCtx : public librbd::UpdateWatchCtx
728 {
729 private:
730 int fd;
731 int nbd_index;
732 bool use_netlink;
733 librados::IoCtx &io_ctx;
734 librbd::Image &image;
735 unsigned long size;
736 public:
737 NBDWatchCtx(int _fd,
738 int _nbd_index,
739 bool _use_netlink,
740 librados::IoCtx &_io_ctx,
741 librbd::Image &_image,
742 unsigned long _size)
743 : fd(_fd)
744 , nbd_index(_nbd_index)
745 , use_netlink(_use_netlink)
746 , io_ctx(_io_ctx)
747 , image(_image)
748 , size(_size)
749 { }
750
751 ~NBDWatchCtx() override {}
752
753 void handle_notify() override
754 {
755 librbd::image_info_t info;
756 if (image.stat(info, sizeof(info)) == 0) {
757 unsigned long new_size = info.size;
758 int ret;
759
760 if (new_size != size) {
761 dout(5) << "resize detected" << dendl;
762 if (ioctl(fd, BLKFLSBUF, NULL) < 0)
763 derr << "invalidate page cache failed: " << cpp_strerror(errno)
764 << dendl;
765 if (use_netlink) {
766 ret = netlink_resize(nbd_index, new_size);
767 } else {
768 ret = ioctl(fd, NBD_SET_SIZE, new_size);
769 if (ret < 0)
770 derr << "resize failed: " << cpp_strerror(errno) << dendl;
771 }
772
773 if (!ret)
774 size = new_size;
775
776 if (ioctl(fd, BLKRRPART, NULL) < 0) {
777 derr << "rescan of partition table failed: " << cpp_strerror(errno)
778 << dendl;
779 }
780 if (image.invalidate_cache() < 0)
781 derr << "invalidate rbd cache failed" << dendl;
782 }
783 }
784 }
785 };
786
787 class NBDListIterator {
788 public:
789 bool get(Config *cfg) {
790 while (true) {
791 std::string nbd_path = "/sys/block/nbd" + stringify(m_index);
792 if(access(nbd_path.c_str(), F_OK) != 0) {
793 return false;
794 }
795
796 *cfg = Config();
797 cfg->devpath = "/dev/nbd" + stringify(m_index++);
798
799 int pid;
800 std::ifstream ifs;
801 ifs.open(nbd_path + "/pid", std::ifstream::in);
802 if (!ifs.is_open()) {
803 continue;
804 }
805 ifs >> pid;
806 ifs.close();
807
808 // If the rbd-nbd is re-attached the pid may store garbage
809 // here. We are sure this is the case when it is negative or
810 // zero. Then we just try to find the attached process scanning
811 // /proc fs. If it is positive we check the process with this
812 // pid first and if it is not rbd-nbd fallback to searching the
813 // attached process.
814 do {
815 if (pid <= 0) {
816 pid = find_attached(cfg->devpath);
817 if (pid <= 0) {
818 break;
819 }
820 }
821
822 if (get_mapped_info(pid, cfg) >= 0) {
823 return true;
824 }
825 pid = -1;
826 } while (true);
827 }
828 }
829
830 private:
831 int m_index = 0;
832 std::map<int, Config> m_mapped_info_cache;
833
834 int get_mapped_info(int pid, Config *cfg) {
835 ceph_assert(!cfg->devpath.empty());
836
837 auto it = m_mapped_info_cache.find(pid);
838 if (it != m_mapped_info_cache.end()) {
839 if (it->second.devpath != cfg->devpath) {
840 return -EINVAL;
841 }
842 *cfg = it->second;
843 return 0;
844 }
845
846 m_mapped_info_cache[pid] = {};
847
848 int r;
849 std::string path = "/proc/" + stringify(pid) + "/comm";
850 std::ifstream ifs;
851 std::string comm;
852 ifs.open(path.c_str(), std::ifstream::in);
853 if (!ifs.is_open())
854 return -1;
855 ifs >> comm;
856 if (comm != "rbd-nbd") {
857 return -EINVAL;
858 }
859 ifs.close();
860
861 path = "/proc/" + stringify(pid) + "/cmdline";
862 std::string cmdline;
863 std::vector<const char*> args;
864
865 ifs.open(path.c_str(), std::ifstream::in);
866 if (!ifs.is_open())
867 return -1;
868 ifs >> cmdline;
869
870 if (cmdline.empty()) {
871 return -EINVAL;
872 }
873
874 for (unsigned i = 0; i < cmdline.size(); i++) {
875 char *arg = &cmdline[i];
876 if (i == 0) {
877 if (strcmp(basename(arg) , "rbd-nbd") != 0) {
878 return -EINVAL;
879 }
880 } else {
881 args.push_back(arg);
882 }
883
884 while (cmdline[i] != '\0') {
885 i++;
886 }
887 }
888
889 std::ostringstream err_msg;
890 Config c;
891 r = parse_args(args, &err_msg, &c);
892 if (r < 0) {
893 return r;
894 }
895
896 if (c.command != Map && c.command != Attach) {
897 return -ENOENT;
898 }
899
900 c.pid = pid;
901 m_mapped_info_cache.erase(pid);
902 if (!c.devpath.empty()) {
903 m_mapped_info_cache[pid] = c;
904 if (c.devpath != cfg->devpath) {
905 return -ENOENT;
906 }
907 } else {
908 c.devpath = cfg->devpath;
909 }
910
911 c.cookie = get_cookie(cfg->devpath);
912 *cfg = c;
913 return 0;
914 }
915
916 int find_attached(const std::string &devpath) {
917 for (auto &entry : fs::directory_iterator("/proc")) {
918 if (!fs::is_directory(entry.status())) {
919 continue;
920 }
921
922 int pid;
923 try {
924 pid = boost::lexical_cast<uint64_t>(entry.path().filename().c_str());
925 } catch (boost::bad_lexical_cast&) {
926 continue;
927 }
928
929 Config cfg;
930 cfg.devpath = devpath;
931 if (get_mapped_info(pid, &cfg) >=0 && cfg.command == Attach) {
932 return cfg.pid;
933 }
934 }
935
936 return -1;
937 }
938 };
939
940 static std::string get_cookie(const std::string &devpath)
941 {
942 std::string cookie;
943 std::ifstream ifs;
944 std::string path = "/sys/block/" + devpath.substr(sizeof("/dev/") - 1) + "/backend";
945
946 ifs.open(path, std::ifstream::in);
947 if (ifs.is_open()) {
948 std::getline(ifs, cookie);
949 ifs.close();
950 }
951 return cookie;
952 }
953
954 static int load_module(Config *cfg)
955 {
956 ostringstream param;
957 int ret;
958
959 if (cfg->nbds_max)
960 param << "nbds_max=" << cfg->nbds_max;
961
962 if (cfg->max_part)
963 param << " max_part=" << cfg->max_part;
964
965 if (!access("/sys/module/nbd", F_OK)) {
966 if (cfg->nbds_max || cfg->set_max_part)
967 cerr << "rbd-nbd: ignoring kernel module parameter options: nbd module already loaded"
968 << std::endl;
969 return 0;
970 }
971
972 ret = module_load("nbd", param.str().c_str());
973 if (ret < 0)
974 cerr << "rbd-nbd: failed to load nbd kernel module: " << cpp_strerror(-ret)
975 << std::endl;
976
977 return ret;
978 }
979
980 static int check_device_size(int nbd_index, unsigned long expected_size)
981 {
982 // There are bugs with some older kernel versions that result in an
983 // overflow for large image sizes. This check is to ensure we are
984 // not affected.
985
986 unsigned long size = 0;
987 std::string path = "/sys/block/nbd" + stringify(nbd_index) + "/size";
988 std::ifstream ifs;
989 ifs.open(path.c_str(), std::ifstream::in);
990 if (!ifs.is_open()) {
991 cerr << "rbd-nbd: failed to open " << path << std::endl;
992 return -EINVAL;
993 }
994 ifs >> size;
995 size *= RBD_NBD_BLKSIZE;
996
997 if (size == 0) {
998 // Newer kernel versions will report real size only after nbd
999 // connect. Assume this is the case and return success.
1000 return 0;
1001 }
1002
1003 if (size != expected_size) {
1004 cerr << "rbd-nbd: kernel reported invalid device size (" << size
1005 << ", expected " << expected_size << ")" << std::endl;
1006 return -EINVAL;
1007 }
1008
1009 return 0;
1010 }
1011
1012 static int parse_nbd_index(const std::string& devpath)
1013 {
1014 int index, ret;
1015
1016 ret = sscanf(devpath.c_str(), "/dev/nbd%d", &index);
1017 if (ret <= 0) {
1018 // mean an early matching failure. But some cases need a negative value.
1019 if (ret == 0)
1020 ret = -EINVAL;
1021 cerr << "rbd-nbd: invalid device path: " << devpath
1022 << " (expected /dev/nbd{num})" << std::endl;
1023 return ret;
1024 }
1025
1026 return index;
1027 }
1028
1029 static int try_ioctl_setup(Config *cfg, int fd, uint64_t size,
1030 uint64_t blksize, uint64_t flags)
1031 {
1032 int index = 0, r;
1033
1034 if (cfg->devpath.empty()) {
1035 char dev[64];
1036 const char *path = "/sys/module/nbd/parameters/nbds_max";
1037 int nbds_max = -1;
1038 if (access(path, F_OK) == 0) {
1039 std::ifstream ifs;
1040 ifs.open(path, std::ifstream::in);
1041 if (ifs.is_open()) {
1042 ifs >> nbds_max;
1043 ifs.close();
1044 }
1045 }
1046
1047 while (true) {
1048 snprintf(dev, sizeof(dev), "/dev/nbd%d", index);
1049
1050 nbd = open(dev, O_RDWR);
1051 if (nbd < 0) {
1052 if (nbd == -EPERM && nbds_max != -1 && index < (nbds_max-1)) {
1053 ++index;
1054 continue;
1055 }
1056 r = nbd;
1057 cerr << "rbd-nbd: failed to find unused device" << std::endl;
1058 goto done;
1059 }
1060
1061 r = ioctl(nbd, NBD_SET_SOCK, fd);
1062 if (r < 0) {
1063 close(nbd);
1064 ++index;
1065 continue;
1066 }
1067
1068 cfg->devpath = dev;
1069 break;
1070 }
1071 } else {
1072 r = parse_nbd_index(cfg->devpath);
1073 if (r < 0)
1074 goto done;
1075 index = r;
1076
1077 nbd = open(cfg->devpath.c_str(), O_RDWR);
1078 if (nbd < 0) {
1079 r = nbd;
1080 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
1081 goto done;
1082 }
1083
1084 r = ioctl(nbd, NBD_SET_SOCK, fd);
1085 if (r < 0) {
1086 r = -errno;
1087 cerr << "rbd-nbd: the device " << cfg->devpath << " is busy" << std::endl;
1088 close(nbd);
1089 goto done;
1090 }
1091 }
1092
1093 r = ioctl(nbd, NBD_SET_BLKSIZE, blksize);
1094 if (r < 0) {
1095 r = -errno;
1096 cerr << "rbd-nbd: NBD_SET_BLKSIZE failed" << std::endl;
1097 goto close_nbd;
1098 }
1099
1100 r = ioctl(nbd, NBD_SET_SIZE, size);
1101 if (r < 0) {
1102 cerr << "rbd-nbd: NBD_SET_SIZE failed" << std::endl;
1103 r = -errno;
1104 goto close_nbd;
1105 }
1106
1107 ioctl(nbd, NBD_SET_FLAGS, flags);
1108
1109 if (cfg->io_timeout >= 0) {
1110 r = ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long)cfg->io_timeout);
1111 if (r < 0) {
1112 r = -errno;
1113 cerr << "rbd-nbd: failed to set IO timeout: " << cpp_strerror(r)
1114 << std::endl;
1115 goto close_nbd;
1116 }
1117 }
1118
1119 dout(10) << "ioctl setup complete for " << cfg->devpath << dendl;
1120 nbd_index = index;
1121 return 0;
1122
1123 close_nbd:
1124 if (r < 0) {
1125 ioctl(nbd, NBD_CLEAR_SOCK);
1126 cerr << "rbd-nbd: failed to map, status: " << cpp_strerror(-r) << std::endl;
1127 }
1128 close(nbd);
1129 done:
1130 return r;
1131 }
1132
1133 static void netlink_cleanup(struct nl_sock *sock)
1134 {
1135 if (!sock)
1136 return;
1137
1138 nl_close(sock);
1139 nl_socket_free(sock);
1140 }
1141
1142 static struct nl_sock *netlink_init(int *id)
1143 {
1144 struct nl_sock *sock;
1145 int ret;
1146
1147 sock = nl_socket_alloc();
1148 if (!sock) {
1149 cerr << "rbd-nbd: Could not allocate netlink socket." << std::endl;
1150 return NULL;
1151 }
1152
1153 ret = genl_connect(sock);
1154 if (ret < 0) {
1155 cerr << "rbd-nbd: Could not connect netlink socket. Error " << ret
1156 << std::endl;
1157 goto free_sock;
1158 }
1159
1160 *id = genl_ctrl_resolve(sock, "nbd");
1161 if (*id < 0)
1162 // nbd netlink interface not supported.
1163 goto close_sock;
1164
1165 return sock;
1166
1167 close_sock:
1168 nl_close(sock);
1169 free_sock:
1170 nl_socket_free(sock);
1171 return NULL;
1172 }
1173
1174 static int netlink_disconnect(int index)
1175 {
1176 struct nl_sock *sock;
1177 struct nl_msg *msg;
1178 int ret, nl_id;
1179
1180 sock = netlink_init(&nl_id);
1181 if (!sock)
1182 // Try ioctl
1183 return 1;
1184
1185 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL);
1186
1187 msg = nlmsg_alloc();
1188 if (!msg) {
1189 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
1190 goto free_sock;
1191 }
1192
1193 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0,
1194 NBD_CMD_DISCONNECT, 0)) {
1195 cerr << "rbd-nbd: Could not setup message." << std::endl;
1196 goto nla_put_failure;
1197 }
1198
1199 NLA_PUT_U32(msg, NBD_ATTR_INDEX, index);
1200
1201 ret = nl_send_sync(sock, msg);
1202 netlink_cleanup(sock);
1203 if (ret < 0) {
1204 cerr << "rbd-nbd: netlink disconnect failed: " << nl_geterror(-ret)
1205 << std::endl;
1206 return -EIO;
1207 }
1208
1209 return 0;
1210
1211 nla_put_failure:
1212 nlmsg_free(msg);
1213 free_sock:
1214 netlink_cleanup(sock);
1215 return -EIO;
1216 }
1217
1218 static int netlink_disconnect_by_path(const std::string& devpath)
1219 {
1220 int index;
1221
1222 index = parse_nbd_index(devpath);
1223 if (index < 0)
1224 return index;
1225
1226 return netlink_disconnect(index);
1227 }
1228
1229 static int netlink_resize(int nbd_index, uint64_t size)
1230 {
1231 struct nl_sock *sock;
1232 struct nl_msg *msg;
1233 int nl_id, ret;
1234
1235 sock = netlink_init(&nl_id);
1236 if (!sock) {
1237 cerr << "rbd-nbd: Netlink interface not supported." << std::endl;
1238 return 1;
1239 }
1240
1241 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL);
1242
1243 msg = nlmsg_alloc();
1244 if (!msg) {
1245 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
1246 goto free_sock;
1247 }
1248
1249 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0,
1250 NBD_CMD_RECONFIGURE, 0)) {
1251 cerr << "rbd-nbd: Could not setup message." << std::endl;
1252 goto free_msg;
1253 }
1254
1255 NLA_PUT_U32(msg, NBD_ATTR_INDEX, nbd_index);
1256 NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, size);
1257
1258 ret = nl_send_sync(sock, msg);
1259 if (ret < 0) {
1260 cerr << "rbd-nbd: netlink resize failed: " << nl_geterror(ret) << std::endl;
1261 goto free_sock;
1262 }
1263
1264 netlink_cleanup(sock);
1265 dout(10) << "netlink resize complete for nbd" << nbd_index << dendl;
1266 return 0;
1267
1268 nla_put_failure:
1269 free_msg:
1270 nlmsg_free(msg);
1271 free_sock:
1272 netlink_cleanup(sock);
1273 return -EIO;
1274 }
1275
1276 static int netlink_connect_cb(struct nl_msg *msg, void *arg)
1277 {
1278 struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
1279 Config *cfg = (Config *)arg;
1280 struct nlattr *msg_attr[NBD_ATTR_MAX + 1];
1281 uint32_t index;
1282 int ret;
1283
1284 ret = nla_parse(msg_attr, NBD_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1285 genlmsg_attrlen(gnlh, 0), NULL);
1286 if (ret) {
1287 cerr << "rbd-nbd: Unsupported netlink reply" << std::endl;
1288 return -NLE_MSGTYPE_NOSUPPORT;
1289 }
1290
1291 if (!msg_attr[NBD_ATTR_INDEX]) {
1292 cerr << "rbd-nbd: netlink connect reply missing device index." << std::endl;
1293 return -NLE_MSGTYPE_NOSUPPORT;
1294 }
1295
1296 index = nla_get_u32(msg_attr[NBD_ATTR_INDEX]);
1297 cfg->devpath = "/dev/nbd" + stringify(index);
1298 nbd_index = index;
1299
1300 return NL_OK;
1301 }
1302
1303 static int netlink_connect(Config *cfg, struct nl_sock *sock, int nl_id, int fd,
1304 uint64_t size, uint64_t flags, bool reconnect)
1305 {
1306 struct nlattr *sock_attr;
1307 struct nlattr *sock_opt;
1308 struct nl_msg *msg;
1309 int ret;
1310
1311 if (reconnect) {
1312 dout(10) << "netlink try reconnect for " << cfg->devpath << dendl;
1313
1314 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL);
1315 } else {
1316 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, netlink_connect_cb,
1317 cfg);
1318 }
1319
1320 msg = nlmsg_alloc();
1321 if (!msg) {
1322 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
1323 return -ENOMEM;
1324 }
1325
1326 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0,
1327 reconnect ? NBD_CMD_RECONFIGURE : NBD_CMD_CONNECT, 0)) {
1328 cerr << "rbd-nbd: Could not setup message." << std::endl;
1329 goto free_msg;
1330 }
1331
1332 if (!cfg->devpath.empty()) {
1333 ret = parse_nbd_index(cfg->devpath);
1334 if (ret < 0)
1335 goto free_msg;
1336
1337 NLA_PUT_U32(msg, NBD_ATTR_INDEX, ret);
1338 if (reconnect) {
1339 nbd_index = ret;
1340 }
1341 }
1342
1343 if (cfg->io_timeout >= 0)
1344 NLA_PUT_U64(msg, NBD_ATTR_TIMEOUT, cfg->io_timeout);
1345
1346 NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, size);
1347 NLA_PUT_U64(msg, NBD_ATTR_BLOCK_SIZE_BYTES, RBD_NBD_BLKSIZE);
1348 NLA_PUT_U64(msg, NBD_ATTR_SERVER_FLAGS, flags);
1349 NLA_PUT_U64(msg, NBD_ATTR_DEAD_CONN_TIMEOUT, cfg->reattach_timeout);
1350 if (!cfg->cookie.empty())
1351 NLA_PUT_STRING(msg, NBD_ATTR_BACKEND_IDENTIFIER, cfg->cookie.c_str());
1352
1353 sock_attr = nla_nest_start(msg, NBD_ATTR_SOCKETS);
1354 if (!sock_attr) {
1355 cerr << "rbd-nbd: Could not init sockets in netlink message." << std::endl;
1356 goto free_msg;
1357 }
1358
1359 sock_opt = nla_nest_start(msg, NBD_SOCK_ITEM);
1360 if (!sock_opt) {
1361 cerr << "rbd-nbd: Could not init sock in netlink message." << std::endl;
1362 goto free_msg;
1363 }
1364
1365 NLA_PUT_U32(msg, NBD_SOCK_FD, fd);
1366 nla_nest_end(msg, sock_opt);
1367 nla_nest_end(msg, sock_attr);
1368
1369 ret = nl_send_sync(sock, msg);
1370 if (ret < 0) {
1371 cerr << "rbd-nbd: netlink connect failed: " << nl_geterror(ret)
1372 << std::endl;
1373 return -EIO;
1374 }
1375
1376 dout(10) << "netlink connect complete for " << cfg->devpath << dendl;
1377 return 0;
1378
1379 nla_put_failure:
1380 free_msg:
1381 nlmsg_free(msg);
1382 return -EIO;
1383 }
1384
1385 static int try_netlink_setup(Config *cfg, int fd, uint64_t size, uint64_t flags,
1386 bool reconnect)
1387 {
1388 struct nl_sock *sock;
1389 int nl_id, ret;
1390
1391 sock = netlink_init(&nl_id);
1392 if (!sock) {
1393 cerr << "rbd-nbd: Netlink interface not supported. Using ioctl interface."
1394 << std::endl;
1395 return 1;
1396 }
1397
1398 dout(10) << "netlink interface supported." << dendl;
1399
1400 ret = netlink_connect(cfg, sock, nl_id, fd, size, flags, reconnect);
1401 netlink_cleanup(sock);
1402
1403 if (ret != 0)
1404 return ret;
1405
1406 nbd = open(cfg->devpath.c_str(), O_RDWR);
1407 if (nbd < 0) {
1408 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
1409 return nbd;
1410 }
1411
1412 return 0;
1413 }
1414
1415 static int run_quiesce_hook(const std::string &quiesce_hook,
1416 const std::string &devpath,
1417 const std::string &command) {
1418 dout(10) << __func__ << ": " << quiesce_hook << " " << devpath << " "
1419 << command << dendl;
1420
1421 SubProcess hook(quiesce_hook.c_str(), SubProcess::CLOSE, SubProcess::PIPE,
1422 SubProcess::PIPE);
1423 hook.add_cmd_args(devpath.c_str(), command.c_str(), NULL);
1424 bufferlist err;
1425 int r = hook.spawn();
1426 if (r < 0) {
1427 err.append("subprocess spawn failed");
1428 } else {
1429 err.read_fd(hook.get_stderr(), 16384);
1430 r = hook.join();
1431 if (r > 0) {
1432 r = -r;
1433 }
1434 }
1435 if (r < 0) {
1436 derr << __func__ << ": " << quiesce_hook << " " << devpath << " "
1437 << command << " failed: " << err.to_str() << dendl;
1438 } else {
1439 dout(10) << " succeeded: " << err.to_str() << dendl;
1440 }
1441
1442 return r;
1443 }
1444
1445 static void handle_signal(int signum)
1446 {
1447 ceph_assert(signum == SIGINT || signum == SIGTERM);
1448 derr << "*** Got signal " << sig_str(signum) << " ***" << dendl;
1449
1450 dout(20) << __func__ << ": " << "notifying terminate" << dendl;
1451
1452 ceph_assert(terminate_event_sock.is_valid());
1453 terminate_event_sock.notify();
1454 }
1455
1456 static NBDServer *start_server(int fd, librbd::Image& image, Config *cfg)
1457 {
1458 NBDServer *server;
1459
1460 server = new NBDServer(fd, image, cfg);
1461 server->start();
1462
1463 init_async_signal_handler();
1464 register_async_signal_handler(SIGHUP, sighup_handler);
1465 register_async_signal_handler_oneshot(SIGINT, handle_signal);
1466 register_async_signal_handler_oneshot(SIGTERM, handle_signal);
1467
1468 return server;
1469 }
1470
1471 static void run_server(Preforker& forker, NBDServer *server, bool netlink_used)
1472 {
1473 if (g_conf()->daemonize) {
1474 global_init_postfork_finish(g_ceph_context);
1475 forker.daemonize();
1476 }
1477
1478 if (netlink_used)
1479 server->wait_for_disconnect();
1480 else
1481 ioctl(nbd, NBD_DO_IT);
1482
1483 unregister_async_signal_handler(SIGHUP, sighup_handler);
1484 unregister_async_signal_handler(SIGINT, handle_signal);
1485 unregister_async_signal_handler(SIGTERM, handle_signal);
1486 shutdown_async_signal_handler();
1487 }
1488
1489 // Eventually it should be removed when pidfd_open is widely supported.
1490
1491 static int wait_for_terminate_legacy(int pid, int timeout)
1492 {
1493 for (int i = 0; ; i++) {
1494 if (kill(pid, 0) == -1) {
1495 if (errno == ESRCH) {
1496 return 0;
1497 }
1498 int r = -errno;
1499 cerr << "rbd-nbd: kill(" << pid << ", 0) failed: "
1500 << cpp_strerror(r) << std::endl;
1501 return r;
1502 }
1503 if (i >= timeout * 2) {
1504 break;
1505 }
1506 usleep(500000);
1507 }
1508
1509 cerr << "rbd-nbd: waiting for process exit timed out" << std::endl;
1510 return -ETIMEDOUT;
1511 }
1512
1513 // Eventually it should be replaced with glibc' pidfd_open
1514 // when it is widely available.
1515
1516 #ifdef __NR_pidfd_open
1517 static int pidfd_open(pid_t pid, unsigned int flags)
1518 {
1519 return syscall(__NR_pidfd_open, pid, flags);
1520 }
1521 #else
1522 static int pidfd_open(pid_t pid, unsigned int flags)
1523 {
1524 errno = ENOSYS;
1525 return -1;
1526 }
1527 #endif
1528
1529 static int wait_for_terminate(int pid, int timeout)
1530 {
1531 int fd = pidfd_open(pid, 0);
1532 if (fd == -1) {
1533 if (errno == ENOSYS) {
1534 return wait_for_terminate_legacy(pid, timeout);
1535 }
1536 if (errno == ESRCH) {
1537 return 0;
1538 }
1539 int r = -errno;
1540 cerr << "rbd-nbd: pidfd_open(" << pid << ") failed: "
1541 << cpp_strerror(r) << std::endl;
1542 return r;
1543 }
1544
1545 struct pollfd poll_fds[1];
1546 memset(poll_fds, 0, sizeof(struct pollfd));
1547 poll_fds[0].fd = fd;
1548 poll_fds[0].events = POLLIN;
1549
1550 int r = poll(poll_fds, 1, timeout * 1000);
1551 if (r == -1) {
1552 r = -errno;
1553 cerr << "rbd-nbd: failed to poll rbd-nbd process: " << cpp_strerror(r)
1554 << std::endl;
1555 goto done;
1556 } else {
1557 r = 0;
1558 }
1559
1560 if ((poll_fds[0].revents & POLLIN) == 0) {
1561 cerr << "rbd-nbd: waiting for process exit timed out" << std::endl;
1562 r = -ETIMEDOUT;
1563 }
1564
1565 done:
1566 close(fd);
1567
1568 return r;
1569 }
1570
1571 static int do_map(int argc, const char *argv[], Config *cfg, bool reconnect)
1572 {
1573 int r;
1574
1575 librados::Rados rados;
1576 librbd::RBD rbd;
1577 librados::IoCtx io_ctx;
1578 librbd::Image image;
1579
1580 int read_only = 0;
1581 unsigned long flags;
1582 unsigned long size;
1583 unsigned long blksize = RBD_NBD_BLKSIZE;
1584 bool use_netlink;
1585
1586 int fd[2];
1587
1588 librbd::image_info_t info;
1589
1590 Preforker forker;
1591 NBDServer *server;
1592
1593 auto args = argv_to_vec(argc, argv);
1594 if (args.empty()) {
1595 cerr << argv[0] << ": -h or --help for usage" << std::endl;
1596 exit(1);
1597 }
1598 if (ceph_argparse_need_usage(args)) {
1599 usage();
1600 exit(0);
1601 }
1602
1603 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
1604 CODE_ENVIRONMENT_DAEMON,
1605 CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS);
1606 g_ceph_context->_conf.set_val_or_die("pid_file", "");
1607
1608 if (global_init_prefork(g_ceph_context) >= 0) {
1609 std::string err;
1610 r = forker.prefork(err);
1611 if (r < 0) {
1612 cerr << err << std::endl;
1613 return r;
1614 }
1615 if (forker.is_parent()) {
1616 if (forker.parent_wait(err) != 0) {
1617 return -ENXIO;
1618 }
1619 return 0;
1620 }
1621 global_init_postfork_start(g_ceph_context);
1622 }
1623
1624 common_init_finish(g_ceph_context);
1625 global_init_chdir(g_ceph_context);
1626
1627 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
1628 r = -errno;
1629 goto close_ret;
1630 }
1631
1632 r = rados.init_with_context(g_ceph_context);
1633 if (r < 0)
1634 goto close_fd;
1635
1636 r = rados.connect();
1637 if (r < 0)
1638 goto close_fd;
1639
1640 r = rados.ioctx_create(cfg->poolname.c_str(), io_ctx);
1641 if (r < 0)
1642 goto close_fd;
1643
1644 io_ctx.set_namespace(cfg->nsname);
1645
1646 r = rbd.open(io_ctx, image, cfg->imgname.c_str());
1647 if (r < 0)
1648 goto close_fd;
1649
1650 if (cfg->exclusive) {
1651 r = image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE);
1652 if (r < 0) {
1653 cerr << "rbd-nbd: failed to acquire exclusive lock: " << cpp_strerror(r)
1654 << std::endl;
1655 goto close_fd;
1656 }
1657 }
1658
1659 if (!cfg->snapname.empty()) {
1660 r = image.snap_set(cfg->snapname.c_str());
1661 if (r < 0)
1662 goto close_fd;
1663 }
1664
1665 if (cfg->encryption_format.has_value()) {
1666 if (!cfg->encryption_passphrase_file.has_value()) {
1667 r = -EINVAL;
1668 cerr << "rbd-nbd: missing encryption-passphrase-file" << std::endl;
1669 goto close_fd;
1670 }
1671 std::ifstream file(cfg->encryption_passphrase_file.value().c_str());
1672 if (file.fail()) {
1673 r = -errno;
1674 std::cerr << "rbd-nbd: unable to open passphrase file:"
1675 << cpp_strerror(errno) << std::endl;
1676 goto close_fd;
1677 }
1678 std::string passphrase((std::istreambuf_iterator<char>(file)),
1679 (std::istreambuf_iterator<char>()));
1680 auto sg = make_scope_guard([&] {
1681 ceph_memzero_s(&passphrase[0], passphrase.size(), passphrase.size()); });
1682 file.close();
1683 if (!passphrase.empty() && passphrase[passphrase.length() - 1] == '\n') {
1684 passphrase.erase(passphrase.length() - 1);
1685 }
1686
1687 switch (cfg->encryption_format.value()) {
1688 case RBD_ENCRYPTION_FORMAT_LUKS1: {
1689 librbd::encryption_luks1_format_options_t opts = {};
1690 opts.passphrase = passphrase;
1691 r = image.encryption_load(
1692 RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts));
1693 break;
1694 }
1695 case RBD_ENCRYPTION_FORMAT_LUKS2: {
1696 librbd::encryption_luks2_format_options_t opts = {};
1697 opts.passphrase = passphrase;
1698 r = image.encryption_load(
1699 RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts));
1700 blksize = 4096;
1701 break;
1702 }
1703 default:
1704 r = -ENOTSUP;
1705 cerr << "rbd-nbd: unsupported encryption format" << std::endl;
1706 goto close_fd;
1707 }
1708
1709 if (r != 0) {
1710 cerr << "rbd-nbd: failed to load encryption: " << cpp_strerror(r)
1711 << std::endl;
1712 goto close_fd;
1713 }
1714 }
1715
1716 r = image.stat(info, sizeof(info));
1717 if (r < 0)
1718 goto close_fd;
1719
1720 flags = NBD_FLAG_SEND_FLUSH | NBD_FLAG_HAS_FLAGS;
1721 if (!cfg->notrim) {
1722 flags |= NBD_FLAG_SEND_TRIM;
1723 }
1724 if (!cfg->snapname.empty() || cfg->readonly) {
1725 flags |= NBD_FLAG_READ_ONLY;
1726 read_only = 1;
1727 }
1728
1729 if (info.size > ULONG_MAX) {
1730 r = -EFBIG;
1731 cerr << "rbd-nbd: image is too large (" << byte_u_t(info.size)
1732 << ", max is " << byte_u_t(ULONG_MAX) << ")" << std::endl;
1733 goto close_fd;
1734 }
1735
1736 size = info.size;
1737
1738 r = load_module(cfg);
1739 if (r < 0)
1740 goto close_fd;
1741
1742 server = start_server(fd[1], image, cfg);
1743
1744 use_netlink = cfg->try_netlink || reconnect;
1745 if (use_netlink) {
1746 // generate when the cookie is not supplied at CLI
1747 if (!reconnect && cfg->cookie.empty()) {
1748 uuid_d uuid_gen;
1749 uuid_gen.generate_random();
1750 cfg->cookie = uuid_gen.to_string();
1751 }
1752 r = try_netlink_setup(cfg, fd[0], size, flags, reconnect);
1753 if (r < 0) {
1754 goto free_server;
1755 } else if (r == 1) {
1756 use_netlink = false;
1757 }
1758 }
1759
1760 if (!use_netlink) {
1761 r = try_ioctl_setup(cfg, fd[0], size, blksize, flags);
1762 if (r < 0)
1763 goto free_server;
1764 }
1765
1766 r = check_device_size(nbd_index, size);
1767 if (r < 0)
1768 goto close_nbd;
1769
1770 r = ioctl(nbd, BLKROSET, (unsigned long) &read_only);
1771 if (r < 0) {
1772 r = -errno;
1773 goto close_nbd;
1774 }
1775
1776 {
1777 NBDQuiesceWatchCtx quiesce_watch_ctx(server);
1778 if (cfg->quiesce) {
1779 r = image.quiesce_watch(&quiesce_watch_ctx,
1780 &server->quiesce_watch_handle);
1781 if (r < 0) {
1782 goto close_nbd;
1783 }
1784 }
1785
1786 uint64_t handle;
1787
1788 NBDWatchCtx watch_ctx(nbd, nbd_index, use_netlink, io_ctx, image,
1789 info.size);
1790 r = image.update_watch(&watch_ctx, &handle);
1791 if (r < 0)
1792 goto close_nbd;
1793
1794 std::string cookie;
1795 if (use_netlink) {
1796 cookie = get_cookie(cfg->devpath);
1797 ceph_assert(cookie == cfg->cookie || cookie.empty());
1798 }
1799 if (cfg->show_cookie && !cookie.empty()) {
1800 cout << cfg->devpath << " " << cookie << std::endl;
1801 } else {
1802 cout << cfg->devpath << std::endl;
1803 }
1804
1805 run_server(forker, server, use_netlink);
1806
1807 if (cfg->quiesce) {
1808 r = image.quiesce_unwatch(server->quiesce_watch_handle);
1809 ceph_assert(r == 0);
1810 }
1811
1812 r = image.update_unwatch(handle);
1813 ceph_assert(r == 0);
1814 }
1815
1816 close_nbd:
1817 if (r < 0) {
1818 if (use_netlink) {
1819 netlink_disconnect(nbd_index);
1820 } else {
1821 ioctl(nbd, NBD_CLEAR_SOCK);
1822 cerr << "rbd-nbd: failed to map, status: " << cpp_strerror(-r)
1823 << std::endl;
1824 }
1825 }
1826 close(nbd);
1827 free_server:
1828 delete server;
1829 close_fd:
1830 close(fd[0]);
1831 close(fd[1]);
1832 close_ret:
1833 image.close();
1834 io_ctx.close();
1835 rados.shutdown();
1836
1837 forker.exit(r < 0 ? EXIT_FAILURE : 0);
1838 // Unreachable;
1839 return r;
1840 }
1841
1842 static int do_detach(Config *cfg)
1843 {
1844 int r = kill(cfg->pid, SIGTERM);
1845 if (r == -1) {
1846 r = -errno;
1847 cerr << "rbd-nbd: failed to terminate " << cfg->pid << ": "
1848 << cpp_strerror(r) << std::endl;
1849 return r;
1850 }
1851
1852 return wait_for_terminate(cfg->pid, cfg->reattach_timeout);
1853 }
1854
1855 static int do_unmap(Config *cfg)
1856 {
1857 /*
1858 * The netlink disconnect call supports devices setup with netlink or ioctl,
1859 * so we always try that first.
1860 */
1861 int r = netlink_disconnect_by_path(cfg->devpath);
1862 if (r < 0) {
1863 return r;
1864 }
1865
1866 if (r == 1) {
1867 int nbd = open(cfg->devpath.c_str(), O_RDWR);
1868 if (nbd < 0) {
1869 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
1870 return nbd;
1871 }
1872
1873 r = ioctl(nbd, NBD_DISCONNECT);
1874 if (r < 0) {
1875 cerr << "rbd-nbd: the device is not used" << std::endl;
1876 }
1877
1878 close(nbd);
1879
1880 if (r < 0) {
1881 return r;
1882 }
1883 }
1884
1885 if (cfg->pid > 0) {
1886 r = wait_for_terminate(cfg->pid, cfg->reattach_timeout);
1887 }
1888
1889 return 0;
1890 }
1891
1892 static int parse_imgpath(const std::string &imgpath, Config *cfg,
1893 std::ostream *err_msg) {
1894 std::regex pattern("^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$");
1895 std::smatch match;
1896 if (!std::regex_match(imgpath, match, pattern)) {
1897 std::cerr << "rbd-nbd: invalid spec '" << imgpath << "'" << std::endl;
1898 return -EINVAL;
1899 }
1900
1901 if (match[1].matched) {
1902 cfg->poolname = match[1];
1903 }
1904
1905 if (match[2].matched) {
1906 cfg->nsname = match[2];
1907 }
1908
1909 cfg->imgname = match[3];
1910
1911 if (match[4].matched)
1912 cfg->snapname = match[4];
1913
1914 return 0;
1915 }
1916
1917 static int do_list_mapped_devices(const std::string &format, bool pretty_format)
1918 {
1919 bool should_print = false;
1920 std::unique_ptr<ceph::Formatter> f;
1921 TextTable tbl;
1922
1923 if (format == "json") {
1924 f.reset(new JSONFormatter(pretty_format));
1925 } else if (format == "xml") {
1926 f.reset(new XMLFormatter(pretty_format));
1927 } else if (!format.empty() && format != "plain") {
1928 std::cerr << "rbd-nbd: invalid output format: " << format << std::endl;
1929 return -EINVAL;
1930 }
1931
1932 if (f) {
1933 f->open_array_section("devices");
1934 } else {
1935 tbl.define_column("id", TextTable::LEFT, TextTable::LEFT);
1936 tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT);
1937 tbl.define_column("namespace", TextTable::LEFT, TextTable::LEFT);
1938 tbl.define_column("image", TextTable::LEFT, TextTable::LEFT);
1939 tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT);
1940 tbl.define_column("device", TextTable::LEFT, TextTable::LEFT);
1941 tbl.define_column("cookie", TextTable::LEFT, TextTable::LEFT);
1942 }
1943
1944 Config cfg;
1945 NBDListIterator it;
1946 while (it.get(&cfg)) {
1947 if (f) {
1948 f->open_object_section("device");
1949 f->dump_int("id", cfg.pid);
1950 f->dump_string("pool", cfg.poolname);
1951 f->dump_string("namespace", cfg.nsname);
1952 f->dump_string("image", cfg.imgname);
1953 f->dump_string("snap", cfg.snapname);
1954 f->dump_string("device", cfg.devpath);
1955 f->dump_string("cookie", cfg.cookie);
1956 f->close_section();
1957 } else {
1958 should_print = true;
1959 if (cfg.snapname.empty()) {
1960 cfg.snapname = "-";
1961 }
1962 tbl << cfg.pid << cfg.poolname << cfg.nsname << cfg.imgname
1963 << cfg.snapname << cfg.devpath << cfg.cookie << TextTable::endrow;
1964 }
1965 }
1966
1967 if (f) {
1968 f->close_section(); // devices
1969 f->flush(std::cout);
1970 }
1971 if (should_print) {
1972 std::cout << tbl;
1973 }
1974 return 0;
1975 }
1976
1977 static bool find_mapped_dev_by_spec(Config *cfg, int skip_pid=-1) {
1978 Config c;
1979 NBDListIterator it;
1980 while (it.get(&c)) {
1981 if (c.pid != skip_pid &&
1982 c.poolname == cfg->poolname && c.nsname == cfg->nsname &&
1983 c.imgname == cfg->imgname && c.snapname == cfg->snapname &&
1984 (cfg->devpath.empty() || c.devpath == cfg->devpath)) {
1985 *cfg = c;
1986 return true;
1987 }
1988 }
1989 return false;
1990 }
1991
1992 static int find_proc_by_dev(Config *cfg) {
1993 Config c;
1994 NBDListIterator it;
1995 while (it.get(&c)) {
1996 if (c.devpath == cfg->devpath) {
1997 *cfg = c;
1998 return true;
1999 }
2000 }
2001 return false;
2002 }
2003
2004 static int parse_args(vector<const char*>& args, std::ostream *err_msg,
2005 Config *cfg) {
2006 std::string conf_file_list;
2007 std::string cluster;
2008 CephInitParameters iparams = ceph_argparse_early_args(
2009 args, CEPH_ENTITY_TYPE_CLIENT, &cluster, &conf_file_list);
2010
2011 ConfigProxy config{false};
2012 config->name = iparams.name;
2013 config->cluster = cluster;
2014
2015 if (!conf_file_list.empty()) {
2016 config.parse_config_files(conf_file_list.c_str(), nullptr, 0);
2017 } else {
2018 config.parse_config_files(nullptr, nullptr, 0);
2019 }
2020 config.parse_env(CEPH_ENTITY_TYPE_CLIENT);
2021 config.parse_argv(args);
2022 cfg->poolname = config.get_val<std::string>("rbd_default_pool");
2023
2024 std::vector<const char*>::iterator i;
2025 std::ostringstream err;
2026 std::string arg_value;
2027
2028 for (i = args.begin(); i != args.end(); ) {
2029 if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
2030 return HELP_INFO;
2031 } else if (ceph_argparse_flag(args, i, "-v", "--version", (char*)NULL)) {
2032 return VERSION_INFO;
2033 } else if (ceph_argparse_witharg(args, i, &cfg->devpath, "--device", (char *)NULL)) {
2034 } else if (ceph_argparse_witharg(args, i, &cfg->io_timeout, err,
2035 "--io-timeout", (char *)NULL)) {
2036 if (!err.str().empty()) {
2037 *err_msg << "rbd-nbd: " << err.str();
2038 return -EINVAL;
2039 }
2040 if (cfg->io_timeout < 0) {
2041 *err_msg << "rbd-nbd: Invalid argument for io-timeout!";
2042 return -EINVAL;
2043 }
2044 } else if (ceph_argparse_witharg(args, i, &cfg->nbds_max, err, "--nbds_max", (char *)NULL)) {
2045 if (!err.str().empty()) {
2046 *err_msg << "rbd-nbd: " << err.str();
2047 return -EINVAL;
2048 }
2049 if (cfg->nbds_max < 0) {
2050 *err_msg << "rbd-nbd: Invalid argument for nbds_max!";
2051 return -EINVAL;
2052 }
2053 } else if (ceph_argparse_witharg(args, i, &cfg->max_part, err, "--max_part", (char *)NULL)) {
2054 if (!err.str().empty()) {
2055 *err_msg << "rbd-nbd: " << err.str();
2056 return -EINVAL;
2057 }
2058 if ((cfg->max_part < 0) || (cfg->max_part > 255)) {
2059 *err_msg << "rbd-nbd: Invalid argument for max_part(0~255)!";
2060 return -EINVAL;
2061 }
2062 cfg->set_max_part = true;
2063 } else if (ceph_argparse_flag(args, i, "--quiesce", (char *)NULL)) {
2064 cfg->quiesce = true;
2065 } else if (ceph_argparse_witharg(args, i, &cfg->quiesce_hook,
2066 "--quiesce-hook", (char *)NULL)) {
2067 } else if (ceph_argparse_flag(args, i, "--read-only", (char *)NULL)) {
2068 cfg->readonly = true;
2069 } else if (ceph_argparse_witharg(args, i, &cfg->reattach_timeout, err,
2070 "--reattach-timeout", (char *)NULL)) {
2071 if (!err.str().empty()) {
2072 *err_msg << "rbd-nbd: " << err.str();
2073 return -EINVAL;
2074 }
2075 if (cfg->reattach_timeout < 0) {
2076 *err_msg << "rbd-nbd: Invalid argument for reattach-timeout!";
2077 return -EINVAL;
2078 }
2079 } else if (ceph_argparse_flag(args, i, "--exclusive", (char *)NULL)) {
2080 cfg->exclusive = true;
2081 } else if (ceph_argparse_flag(args, i, "--notrim", (char *)NULL)) {
2082 cfg->notrim = true;
2083 } else if (ceph_argparse_witharg(args, i, &cfg->io_timeout, err,
2084 "--timeout", (char *)NULL)) {
2085 if (!err.str().empty()) {
2086 *err_msg << "rbd-nbd: " << err.str();
2087 return -EINVAL;
2088 }
2089 if (cfg->io_timeout < 0) {
2090 *err_msg << "rbd-nbd: Invalid argument for timeout!";
2091 return -EINVAL;
2092 }
2093 *err_msg << "rbd-nbd: --timeout is deprecated (use --io-timeout)";
2094 } else if (ceph_argparse_witharg(args, i, &cfg->format, err, "--format",
2095 (char *)NULL)) {
2096 } else if (ceph_argparse_flag(args, i, "--pretty-format", (char *)NULL)) {
2097 cfg->pretty_format = true;
2098 } else if (ceph_argparse_flag(args, i, "--try-netlink", (char *)NULL)) {
2099 cfg->try_netlink = true;
2100 } else if (ceph_argparse_flag(args, i, "--show-cookie", (char *)NULL)) {
2101 cfg->show_cookie = true;
2102 } else if (ceph_argparse_witharg(args, i, &cfg->cookie, "--cookie", (char *)NULL)) {
2103 } else if (ceph_argparse_witharg(args, i, &arg_value,
2104 "--encryption-format", (char *)NULL)) {
2105 if (arg_value == "luks1") {
2106 cfg->encryption_format =
2107 std::make_optional(RBD_ENCRYPTION_FORMAT_LUKS1);
2108 } else if (arg_value == "luks2") {
2109 cfg->encryption_format =
2110 std::make_optional(RBD_ENCRYPTION_FORMAT_LUKS2);
2111 } else {
2112 *err_msg << "rbd-nbd: Invalid encryption format";
2113 return -EINVAL;
2114 }
2115 } else if (ceph_argparse_witharg(args, i, &arg_value,
2116 "--encryption-passphrase-file",
2117 (char *)NULL)) {
2118 cfg->encryption_passphrase_file = std::make_optional(arg_value);
2119 } else {
2120 ++i;
2121 }
2122 }
2123
2124 Command cmd = None;
2125 if (args.begin() != args.end()) {
2126 if (strcmp(*args.begin(), "map") == 0) {
2127 cmd = Map;
2128 } else if (strcmp(*args.begin(), "unmap") == 0) {
2129 cmd = Unmap;
2130 } else if (strcmp(*args.begin(), "attach") == 0) {
2131 cmd = Attach;
2132 } else if (strcmp(*args.begin(), "detach") == 0) {
2133 cmd = Detach;
2134 } else if (strcmp(*args.begin(), "list-mapped") == 0) {
2135 cmd = List;
2136 } else {
2137 *err_msg << "rbd-nbd: unknown command: " << *args.begin();
2138 return -EINVAL;
2139 }
2140 args.erase(args.begin());
2141 }
2142
2143 if (cmd == None) {
2144 *err_msg << "rbd-nbd: must specify command";
2145 return -EINVAL;
2146 }
2147
2148 std::string cookie;
2149 switch (cmd) {
2150 case Attach:
2151 if (cfg->devpath.empty()) {
2152 *err_msg << "rbd-nbd: must specify device to attach";
2153 return -EINVAL;
2154 }
2155 // Allowing attach without --cookie option for kernel without
2156 // NBD_ATTR_BACKEND_IDENTIFIER support for compatibility
2157 cookie = get_cookie(cfg->devpath);
2158 if (!cookie.empty()) {
2159 if (cfg->cookie.empty()) {
2160 *err_msg << "rbd-nbd: must specify cookie to attach";
2161 return -EINVAL;
2162 } else if (cookie != cfg->cookie) {
2163 *err_msg << "rbd-nbd: cookie mismatch";
2164 return -EINVAL;
2165 }
2166 } else if (!cfg->cookie.empty()) {
2167 *err_msg << "rbd-nbd: kernel does not have cookie support";
2168 return -EINVAL;
2169 }
2170 [[fallthrough]];
2171 case Map:
2172 if (args.begin() == args.end()) {
2173 *err_msg << "rbd-nbd: must specify image-or-snap-spec";
2174 return -EINVAL;
2175 }
2176 if (parse_imgpath(*args.begin(), cfg, err_msg) < 0) {
2177 return -EINVAL;
2178 }
2179 args.erase(args.begin());
2180 break;
2181 case Detach:
2182 case Unmap:
2183 if (args.begin() == args.end()) {
2184 *err_msg << "rbd-nbd: must specify nbd device or image-or-snap-spec";
2185 return -EINVAL;
2186 }
2187 if (boost::starts_with(*args.begin(), "/dev/")) {
2188 cfg->devpath = *args.begin();
2189 } else {
2190 if (parse_imgpath(*args.begin(), cfg, err_msg) < 0) {
2191 return -EINVAL;
2192 }
2193 }
2194 args.erase(args.begin());
2195 break;
2196 default:
2197 //shut up gcc;
2198 break;
2199 }
2200
2201 if (args.begin() != args.end()) {
2202 *err_msg << "rbd-nbd: unknown args: " << *args.begin();
2203 return -EINVAL;
2204 }
2205
2206 cfg->command = cmd;
2207 return 0;
2208 }
2209
2210 static int rbd_nbd(int argc, const char *argv[])
2211 {
2212 int r;
2213 Config cfg;
2214 auto args = argv_to_vec(argc, argv);
2215 std::ostringstream err_msg;
2216 r = parse_args(args, &err_msg, &cfg);
2217 if (r == HELP_INFO) {
2218 usage();
2219 return 0;
2220 } else if (r == VERSION_INFO) {
2221 std::cout << pretty_version_to_str() << std::endl;
2222 return 0;
2223 } else if (r < 0) {
2224 cerr << err_msg.str() << std::endl;
2225 return r;
2226 }
2227
2228 if (!err_msg.str().empty()) {
2229 cerr << err_msg.str() << std::endl;
2230 }
2231
2232 switch (cfg.command) {
2233 case Attach:
2234 ceph_assert(!cfg.devpath.empty());
2235 if (find_mapped_dev_by_spec(&cfg, getpid())) {
2236 cerr << "rbd-nbd: " << cfg.devpath << " has process " << cfg.pid
2237 << " connected" << std::endl;
2238 return -EBUSY;
2239 }
2240 [[fallthrough]];
2241 case Map:
2242 if (cfg.imgname.empty()) {
2243 cerr << "rbd-nbd: image name was not specified" << std::endl;
2244 return -EINVAL;
2245 }
2246
2247 r = do_map(argc, argv, &cfg, cfg.command == Attach);
2248 if (r < 0)
2249 return -EINVAL;
2250 break;
2251 case Detach:
2252 if (cfg.devpath.empty()) {
2253 if (!find_mapped_dev_by_spec(&cfg)) {
2254 cerr << "rbd-nbd: " << cfg.image_spec() << " is not mapped"
2255 << std::endl;
2256 return -ENOENT;
2257 }
2258 } else if (!find_proc_by_dev(&cfg)) {
2259 cerr << "rbd-nbd: no process attached to " << cfg.devpath << " found"
2260 << std::endl;
2261 return -ENOENT;
2262 }
2263 r = do_detach(&cfg);
2264 if (r < 0)
2265 return -EINVAL;
2266 break;
2267 case Unmap:
2268 if (cfg.devpath.empty()) {
2269 if (!find_mapped_dev_by_spec(&cfg)) {
2270 cerr << "rbd-nbd: " << cfg.image_spec() << " is not mapped"
2271 << std::endl;
2272 return -ENOENT;
2273 }
2274 } else if (!find_proc_by_dev(&cfg)) {
2275 // still try to send disconnect to the device
2276 }
2277 r = do_unmap(&cfg);
2278 if (r < 0)
2279 return -EINVAL;
2280 break;
2281 case List:
2282 r = do_list_mapped_devices(cfg.format, cfg.pretty_format);
2283 if (r < 0)
2284 return -EINVAL;
2285 break;
2286 default:
2287 usage();
2288 break;
2289 }
2290
2291 return 0;
2292 }
2293
2294 int main(int argc, const char *argv[])
2295 {
2296 int r = rbd_nbd(argc, argv);
2297 if (r < 0) {
2298 return EXIT_FAILURE;
2299 }
2300 return 0;
2301 }