]> git.proxmox.com Git - ceph.git/blob - ceph/src/krbd.cc
import ceph nautilus 14.2.2
[ceph.git] / ceph / src / krbd.cc
1 /*
2 * Ceph - scalable distributed file system
3 *
4 * Copyright (C) 2014 Inktank Storage, Inc.
5 *
6 * This is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software
9 * Foundation. See file COPYING.
10 *
11 */
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <iostream>
16 #include <optional>
17 #include <poll.h>
18 #include <sstream>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <string>
23 #include <sys/stat.h>
24 #include <sys/sysmacros.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include "auth/KeyRing.h"
29 #include "common/errno.h"
30 #include "common/Formatter.h"
31 #include "common/module.h"
32 #include "common/run_cmd.h"
33 #include "common/safe_io.h"
34 #include "common/secret.h"
35 #include "common/TextTable.h"
36 #include "include/ceph_assert.h"
37 #include "include/stringify.h"
38 #include "include/krbd.h"
39 #include "mon/MonMap.h"
40
41 #include <blkid/blkid.h>
42 #include <libudev.h>
43
44
45 const static int POLL_TIMEOUT=120000;
46
47 struct krbd_ctx {
48 CephContext *cct;
49 struct udev *udev;
50 };
51
52 static const std::string SNAP_HEAD_NAME("-");
53
54 struct krbd_spec {
55 std::string pool_name;
56 std::string nspace_name;
57 std::string image_name;
58 std::string snap_name;
59
60 krbd_spec(const char *pool_name, const char *nspace_name,
61 const char *image_name, const char *snap_name)
62 : pool_name(pool_name),
63 nspace_name(nspace_name),
64 image_name(image_name),
65 snap_name(*snap_name ? snap_name : SNAP_HEAD_NAME) { }
66
67 bool operator==(const krbd_spec& rhs) const {
68 return pool_name == rhs.pool_name &&
69 nspace_name == rhs.nspace_name &&
70 image_name == rhs.image_name &&
71 snap_name == rhs.snap_name;
72 }
73 };
74
75 std::ostream& operator<<(std::ostream& os, const krbd_spec& spec) {
76 os << spec.pool_name << "/";
77 if (!spec.nspace_name.empty())
78 os << spec.nspace_name << "/";
79 os << spec.image_name;
80 if (spec.snap_name != SNAP_HEAD_NAME)
81 os << "@" << spec.snap_name;
82 return os;
83 }
84
85 std::optional<krbd_spec> spec_from_dev(udev_device *dev) {
86 const char *pool_name = udev_device_get_sysattr_value(dev, "pool");
87 const char *nspace_name = udev_device_get_sysattr_value(dev, "pool_ns");
88 const char *image_name = udev_device_get_sysattr_value(dev, "name");
89 const char *snap_name = udev_device_get_sysattr_value(dev, "current_snap");
90
91 if (!pool_name || !image_name || !snap_name)
92 return std::nullopt;
93
94 return std::make_optional<krbd_spec>(
95 pool_name, nspace_name ?: "", image_name, snap_name);
96 }
97
98 static string get_kernel_rbd_name(const char *id)
99 {
100 return string("/dev/rbd") + id;
101 }
102
103 static int sysfs_write_rbd(const char *which, const string& buf)
104 {
105 const string s = string("/sys/bus/rbd/") + which;
106 const string t = s + "_single_major";
107 int fd;
108 int r;
109
110 /*
111 * 'add' and 'add_single_major' interfaces are identical, but if rbd
112 * kernel module is new enough and is configured to use single-major
113 * scheme, 'add' is disabled in order to prevent old userspace from
114 * doing weird things at unmap time.
115 *
116 * Same goes for 'remove' vs 'remove_single_major'.
117 */
118 fd = open(t.c_str(), O_WRONLY);
119 if (fd < 0) {
120 if (errno == ENOENT) {
121 fd = open(s.c_str(), O_WRONLY);
122 if (fd < 0)
123 return -errno;
124 } else {
125 return -errno;
126 }
127 }
128
129 r = safe_write(fd, buf.c_str(), buf.size());
130
131 close(fd);
132 return r;
133 }
134
135 static int sysfs_write_rbd_add(const string& buf)
136 {
137 return sysfs_write_rbd("add", buf);
138 }
139
140 static int sysfs_write_rbd_remove(const string& buf)
141 {
142 return sysfs_write_rbd("remove", buf);
143 }
144
145 static int have_minor_attr(void)
146 {
147 /*
148 * 'minor' attribute was added as part of single_major merge, which
149 * exposed the 'single_major' parameter. 'minor' is always present,
150 * regardless of whether single-major scheme is turned on or not.
151 *
152 * (Something like ver >= KERNEL_VERSION(3, 14, 0) is a no-go because
153 * this has to work with rbd.ko backported to various kernels.)
154 */
155 return access("/sys/module/rbd/parameters/single_major", F_OK) == 0;
156 }
157
158 static int build_map_buf(CephContext *cct, const krbd_spec& spec,
159 const char *options, string *pbuf)
160 {
161 ostringstream oss;
162 int r;
163
164 MonMap monmap;
165 r = monmap.build_initial(cct, false, cerr);
166 if (r < 0)
167 return r;
168
169 list<entity_addr_t> mon_addr;
170 monmap.list_addrs(mon_addr);
171
172 for (const auto &p : mon_addr) {
173 if (oss.tellp() > 0) {
174 oss << ",";
175 }
176 oss << p.get_sockaddr();
177 }
178
179 oss << " name=" << cct->_conf->name.get_id();
180
181 KeyRing keyring;
182 auto auth_client_required =
183 cct->_conf.get_val<std::string>("auth_client_required");
184 if (auth_client_required != "none") {
185 r = keyring.from_ceph_context(cct);
186 auto keyfile = cct->_conf.get_val<std::string>("keyfile");
187 auto key = cct->_conf.get_val<std::string>("key");
188 if (r == -ENOENT && keyfile.empty() && key.empty())
189 r = 0;
190 if (r < 0) {
191 cerr << "rbd: failed to get secret" << std::endl;
192 return r;
193 }
194 }
195
196 CryptoKey secret;
197 string key_name = string("client.") + cct->_conf->name.get_id();
198 if (keyring.get_secret(cct->_conf->name, secret)) {
199 string secret_str;
200 secret.encode_base64(secret_str);
201
202 r = set_kernel_secret(secret_str.c_str(), key_name.c_str());
203 if (r >= 0) {
204 if (r == 0)
205 cerr << "rbd: warning: secret has length 0" << std::endl;
206 oss << ",key=" << key_name;
207 } else if (r == -ENODEV || r == -ENOSYS) {
208 // running against older kernel; fall back to secret= in options
209 oss << ",secret=" << secret_str;
210 } else {
211 cerr << "rbd: failed to add secret '" << key_name << "' to kernel"
212 << std::endl;
213 return r;
214 }
215 } else if (is_kernel_secret(key_name.c_str())) {
216 oss << ",key=" << key_name;
217 }
218
219 if (strcmp(options, "") != 0)
220 oss << "," << options;
221 if (!spec.nspace_name.empty())
222 oss << ",_pool_ns=" << spec.nspace_name;
223
224 oss << " " << spec.pool_name << " " << spec.image_name << " "
225 << spec.snap_name;
226
227 *pbuf = oss.str();
228 return 0;
229 }
230
231 static int wait_for_udev_add(struct udev_monitor *mon, const krbd_spec& spec,
232 string *pname)
233 {
234 struct udev_device *bus_dev = nullptr;
235 std::vector<struct udev_device*> block_dev_vec;
236 int r;
237
238 /*
239 * Catch /sys/devices/rbd/<id>/ and wait for the corresponding
240 * block device to show up. This is necessary because rbd devices
241 * and block devices aren't linked together in our sysfs layout.
242 */
243 for (;;) {
244 struct pollfd fds[1];
245 struct udev_device *dev;
246
247 fds[0].fd = udev_monitor_get_fd(mon);
248 fds[0].events = POLLIN;
249 r = poll(fds, 1, POLL_TIMEOUT);
250 if (r > 0) {
251 r = 0;
252 } else {
253 r = (r == 0) ? -ETIMEDOUT : -errno;
254 break;
255 }
256
257 dev = udev_monitor_receive_device(mon);
258 if (!dev)
259 continue;
260
261 if (strcmp(udev_device_get_action(dev), "add") != 0)
262 goto next;
263
264 if (strcmp(udev_device_get_subsystem(dev), "rbd") == 0) {
265 if (!bus_dev) {
266 auto cur_spec = spec_from_dev(dev);
267 if (cur_spec && *cur_spec == spec) {
268 bus_dev = dev;
269 goto check;
270 }
271 }
272 } else if (strcmp(udev_device_get_subsystem(dev), "block") == 0) {
273 block_dev_vec.push_back(dev);
274 goto check;
275 }
276
277 next:
278 udev_device_unref(dev);
279 continue;
280
281 check:
282 if (bus_dev && !block_dev_vec.empty()) {
283 const char *major = udev_device_get_sysattr_value(bus_dev, "major");
284 const char *minor = udev_device_get_sysattr_value(bus_dev, "minor");
285 ceph_assert(!minor ^ have_minor_attr());
286
287 for (auto p : block_dev_vec) {
288 const char *this_major = udev_device_get_property_value(p, "MAJOR");
289 const char *this_minor = udev_device_get_property_value(p, "MINOR");
290
291 if (strcmp(this_major, major) == 0 &&
292 (!minor || strcmp(this_minor, minor) == 0)) {
293 string name = get_kernel_rbd_name(udev_device_get_sysname(bus_dev));
294
295 ceph_assert(strcmp(udev_device_get_devnode(p), name.c_str()) == 0);
296 *pname = name;
297 goto done;
298 }
299 }
300 }
301 }
302
303 done:
304 if (bus_dev) {
305 udev_device_unref(bus_dev);
306 }
307
308 for (auto p : block_dev_vec) {
309 udev_device_unref(p);
310 }
311
312 return r;
313 }
314
315 static int do_map(struct udev *udev, const krbd_spec& spec, const string& buf,
316 string *pname)
317 {
318 struct udev_monitor *mon;
319 int r;
320
321 mon = udev_monitor_new_from_netlink(udev, "udev");
322 if (!mon)
323 return -ENOMEM;
324
325 r = udev_monitor_filter_add_match_subsystem_devtype(mon, "rbd", nullptr);
326 if (r < 0)
327 goto out_mon;
328
329 r = udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk");
330 if (r < 0)
331 goto out_mon;
332
333 r = udev_monitor_enable_receiving(mon);
334 if (r < 0)
335 goto out_mon;
336
337 r = sysfs_write_rbd_add(buf);
338 if (r < 0) {
339 cerr << "rbd: sysfs write failed" << std::endl;
340 goto out_mon;
341 }
342
343 r = wait_for_udev_add(mon, spec, pname);
344 if (r < 0) {
345 cerr << "rbd: wait failed" << std::endl;
346 goto out_mon;
347 }
348
349 out_mon:
350 udev_monitor_unref(mon);
351 return r;
352 }
353
354 static int map_image(struct krbd_ctx *ctx, const krbd_spec& spec,
355 const char *options, string *pname)
356 {
357 string buf;
358 int r;
359
360 r = build_map_buf(ctx->cct, spec, options, &buf);
361 if (r < 0)
362 return r;
363
364 /*
365 * Modprobe rbd kernel module. If it supports single-major device
366 * number allocation scheme, make sure it's turned on.
367 */
368 if (access("/sys/bus/rbd", F_OK) != 0) {
369 const char *module_options = NULL;
370 if (module_has_param("rbd", "single_major"))
371 module_options = "single_major=Y";
372
373 r = module_load("rbd", module_options);
374 if (r) {
375 cerr << "rbd: failed to load rbd kernel module (" << r << ")"
376 << std::endl;
377 /*
378 * Ignore the error: modprobe failing doesn't necessarily prevent
379 * from working.
380 */
381 }
382 }
383
384 return do_map(ctx->udev, spec, buf, pname);
385 }
386
387 static int devno_to_krbd_id(struct udev *udev, dev_t devno, string *pid)
388 {
389 struct udev_enumerate *enm;
390 struct udev_list_entry *l;
391 struct udev_device *dev;
392 int r;
393
394 enm = udev_enumerate_new(udev);
395 if (!enm)
396 return -ENOMEM;
397
398 r = udev_enumerate_add_match_subsystem(enm, "rbd");
399 if (r < 0)
400 goto out_enm;
401
402 r = udev_enumerate_add_match_sysattr(enm, "major",
403 stringify(major(devno)).c_str());
404 if (r < 0)
405 goto out_enm;
406
407 if (have_minor_attr()) {
408 r = udev_enumerate_add_match_sysattr(enm, "minor",
409 stringify(minor(devno)).c_str());
410 if (r < 0)
411 goto out_enm;
412 }
413
414 r = udev_enumerate_scan_devices(enm);
415 if (r < 0)
416 goto out_enm;
417
418 l = udev_enumerate_get_list_entry(enm);
419 if (!l) {
420 r = -ENOENT;
421 goto out_enm;
422 }
423
424 /* make sure there is only one match */
425 ceph_assert(!udev_list_entry_get_next(l));
426
427 dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l));
428 if (!dev) {
429 r = -ENOMEM;
430 goto out_enm;
431 }
432
433 *pid = udev_device_get_sysname(dev);
434
435 udev_device_unref(dev);
436 out_enm:
437 udev_enumerate_unref(enm);
438 return r;
439 }
440
441 static int __enumerate_devices(struct udev *udev, const krbd_spec& spec,
442 bool match_nspace, struct udev_enumerate **penm)
443 {
444 struct udev_enumerate *enm;
445 int r;
446
447 enm = udev_enumerate_new(udev);
448 if (!enm)
449 return -ENOMEM;
450
451 r = udev_enumerate_add_match_subsystem(enm, "rbd");
452 if (r < 0)
453 goto out_enm;
454
455 r = udev_enumerate_add_match_sysattr(enm, "pool", spec.pool_name.c_str());
456 if (r < 0)
457 goto out_enm;
458
459 if (match_nspace) {
460 r = udev_enumerate_add_match_sysattr(enm, "pool_ns",
461 spec.nspace_name.c_str());
462 } else {
463 /*
464 * Match _only_ devices that don't have pool_ns attribute.
465 * If the kernel supports namespaces, the result will be empty.
466 */
467 r = udev_enumerate_add_nomatch_sysattr(enm, "pool_ns", nullptr);
468 }
469 if (r < 0)
470 goto out_enm;
471
472 r = udev_enumerate_add_match_sysattr(enm, "name", spec.image_name.c_str());
473 if (r < 0)
474 goto out_enm;
475
476 r = udev_enumerate_add_match_sysattr(enm, "current_snap",
477 spec.snap_name.c_str());
478 if (r < 0)
479 goto out_enm;
480
481 r = udev_enumerate_scan_devices(enm);
482 if (r < 0)
483 goto out_enm;
484
485 *penm = enm;
486 return 0;
487
488 out_enm:
489 udev_enumerate_unref(enm);
490 return r;
491 }
492
493 static int enumerate_devices(struct udev *udev, const krbd_spec& spec,
494 struct udev_enumerate **penm)
495 {
496 struct udev_enumerate *enm;
497 int r;
498
499 r = __enumerate_devices(udev, spec, true, &enm);
500 if (r < 0)
501 return r;
502
503 /*
504 * If no namespace is set, try again with match_nspace=false to
505 * handle older kernels. On a newer kernel the result will remain
506 * the same (i.e. empty).
507 */
508 if (!udev_enumerate_get_list_entry(enm) && spec.nspace_name.empty()) {
509 udev_enumerate_unref(enm);
510 r = __enumerate_devices(udev, spec, false, &enm);
511 if (r < 0)
512 return r;
513 }
514
515 *penm = enm;
516 return 0;
517 }
518
519 static int spec_to_devno_and_krbd_id(struct udev *udev, const krbd_spec& spec,
520 dev_t *pdevno, string *pid)
521 {
522 struct udev_enumerate *enm;
523 struct udev_list_entry *l;
524 struct udev_device *dev;
525 unsigned int maj, min = 0;
526 string err;
527 int r;
528
529 r = enumerate_devices(udev, spec, &enm);
530 if (r < 0)
531 return r;
532
533 l = udev_enumerate_get_list_entry(enm);
534 if (!l) {
535 r = -ENOENT;
536 goto out_enm;
537 }
538
539 dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l));
540 if (!dev) {
541 r = -ENOMEM;
542 goto out_enm;
543 }
544
545 maj = strict_strtoll(udev_device_get_sysattr_value(dev, "major"), 10, &err);
546 if (!err.empty()) {
547 cerr << "rbd: couldn't parse major: " << err << std::endl;
548 r = -EINVAL;
549 goto out_dev;
550 }
551 if (have_minor_attr()) {
552 min = strict_strtoll(udev_device_get_sysattr_value(dev, "minor"), 10, &err);
553 if (!err.empty()) {
554 cerr << "rbd: couldn't parse minor: " << err << std::endl;
555 r = -EINVAL;
556 goto out_dev;
557 }
558 }
559
560 /*
561 * If an image is mapped more than once don't bother trying to unmap
562 * all devices - let users run unmap the same number of times they
563 * ran map.
564 */
565 if (udev_list_entry_get_next(l))
566 cerr << "rbd: " << spec << ": mapped more than once, unmapping "
567 << get_kernel_rbd_name(udev_device_get_sysname(dev))
568 << " only" << std::endl;
569
570 *pdevno = makedev(maj, min);
571 *pid = udev_device_get_sysname(dev);
572
573 out_dev:
574 udev_device_unref(dev);
575 out_enm:
576 udev_enumerate_unref(enm);
577 return r;
578 }
579
580 static string build_unmap_buf(const string& id, const char *options)
581 {
582 string buf(id);
583 if (strcmp(options, "") != 0) {
584 buf += " ";
585 buf += options;
586 }
587 return buf;
588 }
589
590 static int wait_for_udev_remove(struct udev_monitor *mon, dev_t devno)
591 {
592 for (;;) {
593 struct pollfd fds[1];
594 struct udev_device *dev;
595 int r;
596
597 fds[0].fd = udev_monitor_get_fd(mon);
598 fds[0].events = POLLIN;
599 r = poll(fds, 1, POLL_TIMEOUT);
600 if (r < 0)
601 return -errno;
602
603 if (r == 0)
604 return -ETIMEDOUT;
605
606 dev = udev_monitor_receive_device(mon);
607 if (!dev)
608 continue;
609
610 if (strcmp(udev_device_get_action(dev), "remove") == 0 &&
611 udev_device_get_devnum(dev) == devno) {
612 udev_device_unref(dev);
613 break;
614 }
615
616 udev_device_unref(dev);
617 }
618
619 return 0;
620 }
621
622 static int do_unmap(struct udev *udev, dev_t devno, const string& buf)
623 {
624 struct udev_monitor *mon;
625 int r;
626
627 mon = udev_monitor_new_from_netlink(udev, "udev");
628 if (!mon)
629 return -ENOMEM;
630
631 r = udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk");
632 if (r < 0)
633 goto out_mon;
634
635 r = udev_monitor_enable_receiving(mon);
636 if (r < 0)
637 goto out_mon;
638
639 /*
640 * On final device close(), kernel sends a block change event, in
641 * response to which udev apparently runs blkid on the device. This
642 * makes unmap fail with EBUSY, if issued right after final close().
643 * Try to circumvent this with a retry before turning to udev.
644 */
645 for (int tries = 0; ; tries++) {
646 r = sysfs_write_rbd_remove(buf);
647 if (r >= 0) {
648 break;
649 } else if (r == -EBUSY && tries < 2) {
650 if (!tries) {
651 usleep(250 * 1000);
652 } else {
653 /*
654 * libudev does not provide the "wait until the queue is empty"
655 * API or the sufficient amount of primitives to build it from.
656 */
657 string err = run_cmd("udevadm", "settle", "--timeout", "10", (char*)NULL);
658 if (!err.empty())
659 cerr << "rbd: " << err << std::endl;
660 }
661 } else {
662 cerr << "rbd: sysfs write failed" << std::endl;
663 goto out_mon;
664 }
665 }
666
667 r = wait_for_udev_remove(mon, devno);
668 if (r < 0) {
669 cerr << "rbd: wait failed" << std::endl;
670 goto out_mon;
671 }
672
673 out_mon:
674 udev_monitor_unref(mon);
675 return r;
676 }
677
678 static int unmap_image(struct krbd_ctx *ctx, const char *devnode,
679 const char *options)
680 {
681 struct stat sb;
682 dev_t wholedevno = 0;
683 string id;
684 int r;
685
686 if (stat(devnode, &sb) < 0 || !S_ISBLK(sb.st_mode)) {
687 cerr << "rbd: '" << devnode << "' is not a block device" << std::endl;
688 return -EINVAL;
689 }
690
691 r = blkid_devno_to_wholedisk(sb.st_rdev, NULL, 0, &wholedevno);
692 if (r < 0) {
693 cerr << "rbd: couldn't compute wholedevno: " << cpp_strerror(r)
694 << std::endl;
695 /*
696 * Ignore the error: we are given whole disks most of the time, and
697 * if it turns out this is a partition we will fail later anyway.
698 */
699 wholedevno = sb.st_rdev;
700 }
701
702 r = devno_to_krbd_id(ctx->udev, wholedevno, &id);
703 if (r < 0) {
704 if (r == -ENOENT) {
705 cerr << "rbd: '" << devnode << "' is not an rbd device" << std::endl;
706 r = -EINVAL;
707 }
708 return r;
709 }
710
711 return do_unmap(ctx->udev, wholedevno, build_unmap_buf(id, options));
712 }
713
714 static int unmap_image(struct krbd_ctx *ctx, const krbd_spec& spec,
715 const char *options)
716 {
717 dev_t devno = 0;
718 string id;
719 int r;
720
721 r = spec_to_devno_and_krbd_id(ctx->udev, spec, &devno, &id);
722 if (r < 0) {
723 if (r == -ENOENT) {
724 cerr << "rbd: " << spec << ": not a mapped image or snapshot"
725 << std::endl;
726 r = -EINVAL;
727 }
728 return r;
729 }
730
731 return do_unmap(ctx->udev, devno, build_unmap_buf(id, options));
732 }
733
734 static bool dump_one_image(Formatter *f, TextTable *tbl,
735 struct udev_device *dev)
736 {
737 const char *id = udev_device_get_sysname(dev);
738 auto spec = spec_from_dev(dev);
739 string kname = get_kernel_rbd_name(id);
740
741 if (!spec)
742 return false;
743
744 if (f) {
745 f->open_object_section("device");
746 f->dump_string("id", id);
747 f->dump_string("pool", spec->pool_name);
748 f->dump_string("namespace", spec->nspace_name);
749 f->dump_string("name", spec->image_name);
750 f->dump_string("snap", spec->snap_name);
751 f->dump_string("device", kname);
752 f->close_section();
753 } else {
754 *tbl << id << spec->pool_name << spec->nspace_name << spec->image_name
755 << spec->snap_name << kname << TextTable::endrow;
756 }
757
758 return true;
759 }
760
761 static int do_dump(struct udev *udev, Formatter *f, TextTable *tbl)
762 {
763 struct udev_enumerate *enm;
764 struct udev_list_entry *l = NULL;
765 bool have_output = false;
766 int r;
767
768 enm = udev_enumerate_new(udev);
769 if (!enm)
770 return -ENOMEM;
771
772 r = udev_enumerate_add_match_subsystem(enm, "rbd");
773 if (r < 0)
774 goto out_enm;
775
776 r = udev_enumerate_scan_devices(enm);
777 if (r < 0)
778 goto out_enm;
779
780 udev_list_entry_foreach(l, udev_enumerate_get_list_entry(enm)) {
781 struct udev_device *dev;
782
783 dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l));
784 if (dev) {
785 have_output |= dump_one_image(f, tbl, dev);
786 udev_device_unref(dev);
787 }
788 }
789
790 r = have_output;
791 out_enm:
792 udev_enumerate_unref(enm);
793 return r;
794 }
795
796 int dump_images(struct krbd_ctx *ctx, Formatter *f)
797 {
798 TextTable tbl;
799 int r;
800
801 if (f) {
802 f->open_array_section("devices");
803 } else {
804 tbl.define_column("id", TextTable::LEFT, TextTable::LEFT);
805 tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT);
806 tbl.define_column("namespace", TextTable::LEFT, TextTable::LEFT);
807 tbl.define_column("image", TextTable::LEFT, TextTable::LEFT);
808 tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT);
809 tbl.define_column("device", TextTable::LEFT, TextTable::LEFT);
810 }
811
812 r = do_dump(ctx->udev, f, &tbl);
813
814 if (f) {
815 f->close_section();
816 f->flush(cout);
817 } else {
818 if (r > 0)
819 cout << tbl;
820 }
821
822 return r;
823 }
824
825 static int is_mapped_image(struct udev *udev, const krbd_spec& spec,
826 string *pname)
827 {
828 struct udev_enumerate *enm;
829 struct udev_list_entry *l;
830 int r;
831
832 r = enumerate_devices(udev, spec, &enm);
833 if (r < 0)
834 return r;
835
836 l = udev_enumerate_get_list_entry(enm);
837 if (l) {
838 struct udev_device *dev;
839
840 dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l));
841 if (!dev) {
842 r = -ENOMEM;
843 goto out_enm;
844 }
845
846 r = 1;
847 *pname = get_kernel_rbd_name(udev_device_get_sysname(dev));
848 udev_device_unref(dev);
849 } else {
850 r = 0; /* not mapped */
851 }
852
853 out_enm:
854 udev_enumerate_unref(enm);
855 return r;
856 }
857
858 extern "C" int krbd_create_from_context(rados_config_t cct,
859 struct krbd_ctx **pctx)
860 {
861 struct krbd_ctx *ctx = new struct krbd_ctx();
862
863 ctx->cct = reinterpret_cast<CephContext *>(cct);
864 ctx->udev = udev_new();
865 if (!ctx->udev) {
866 delete ctx;
867 return -ENOMEM;
868 }
869
870 *pctx = ctx;
871 return 0;
872 }
873
874 extern "C" void krbd_destroy(struct krbd_ctx *ctx)
875 {
876 if (!ctx)
877 return;
878
879 udev_unref(ctx->udev);
880
881 delete ctx;
882 }
883
884 extern "C" int krbd_map(struct krbd_ctx *ctx,
885 const char *pool_name,
886 const char *nspace_name,
887 const char *image_name,
888 const char *snap_name,
889 const char *options,
890 char **pdevnode)
891 {
892 krbd_spec spec(pool_name, nspace_name, image_name, snap_name);
893 string name;
894 char *devnode;
895 int r;
896
897 r = map_image(ctx, spec, options, &name);
898 if (r < 0)
899 return r;
900
901 devnode = strdup(name.c_str());
902 if (!devnode)
903 return -ENOMEM;
904
905 *pdevnode = devnode;
906 return r;
907 }
908
909 extern "C" int krbd_unmap(struct krbd_ctx *ctx, const char *devnode,
910 const char *options)
911 {
912 return unmap_image(ctx, devnode, options);
913 }
914
915 extern "C" int krbd_unmap_by_spec(struct krbd_ctx *ctx,
916 const char *pool_name,
917 const char *nspace_name,
918 const char *image_name,
919 const char *snap_name,
920 const char *options)
921 {
922 krbd_spec spec(pool_name, nspace_name, image_name, snap_name);
923 return unmap_image(ctx, spec, options);
924 }
925
926 int krbd_showmapped(struct krbd_ctx *ctx, Formatter *f)
927 {
928 return dump_images(ctx, f);
929 }
930
931 extern "C" int krbd_is_mapped(struct krbd_ctx *ctx,
932 const char *pool_name,
933 const char *nspace_name,
934 const char *image_name,
935 const char *snap_name,
936 char **pdevnode)
937 {
938 krbd_spec spec(pool_name, nspace_name, image_name, snap_name);
939 string name;
940 char *devnode;
941 int r;
942
943 r = is_mapped_image(ctx->udev, spec, &name);
944 if (r <= 0) /* error or not mapped */
945 return r;
946
947 devnode = strdup(name.c_str());
948 if (!devnode)
949 return -ENOMEM;
950
951 *pdevnode = devnode;
952 return r;
953 }