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