]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 <map> | |
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/types.h> | |
25 | #include <unistd.h> | |
26 | ||
27 | #include "auth/KeyRing.h" | |
28 | #include "common/errno.h" | |
29 | #include "common/Formatter.h" | |
30 | #include "common/module.h" | |
31 | #include "common/run_cmd.h" | |
32 | #include "common/safe_io.h" | |
33 | #include "common/secret.h" | |
34 | #include "common/TextTable.h" | |
35 | #include "include/assert.h" | |
36 | #include "include/stringify.h" | |
37 | #include "include/krbd.h" | |
38 | #include "mon/MonMap.h" | |
39 | ||
40 | #include <blkid/blkid.h> | |
41 | #include <libudev.h> | |
42 | ||
43 | using namespace std; | |
44 | ||
45 | const static int POLL_TIMEOUT=120000; | |
46 | ||
47 | struct krbd_ctx { | |
48 | CephContext *cct; | |
49 | struct udev *udev; | |
50 | }; | |
51 | ||
52 | static string get_kernel_rbd_name(const char *id) | |
53 | { | |
54 | return string("/dev/rbd") + id; | |
55 | } | |
56 | ||
57 | static int sysfs_write_rbd(const char *which, const string& buf) | |
58 | { | |
59 | const string s = string("/sys/bus/rbd/") + which; | |
60 | const string t = s + "_single_major"; | |
61 | int fd; | |
62 | int r; | |
63 | ||
64 | /* | |
65 | * 'add' and 'add_single_major' interfaces are identical, but if rbd | |
66 | * kernel module is new enough and is configured to use single-major | |
67 | * scheme, 'add' is disabled in order to prevent old userspace from | |
68 | * doing weird things at unmap time. | |
69 | * | |
70 | * Same goes for 'remove' vs 'remove_single_major'. | |
71 | */ | |
72 | fd = open(t.c_str(), O_WRONLY); | |
73 | if (fd < 0) { | |
74 | if (errno == ENOENT) { | |
75 | fd = open(s.c_str(), O_WRONLY); | |
76 | if (fd < 0) | |
77 | return -errno; | |
78 | } else { | |
79 | return -errno; | |
80 | } | |
81 | } | |
82 | ||
83 | r = safe_write(fd, buf.c_str(), buf.size()); | |
84 | ||
85 | close(fd); | |
86 | return r; | |
87 | } | |
88 | ||
89 | static int sysfs_write_rbd_add(const string& buf) | |
90 | { | |
91 | return sysfs_write_rbd("add", buf); | |
92 | } | |
93 | ||
94 | static int sysfs_write_rbd_remove(const string& buf) | |
95 | { | |
96 | return sysfs_write_rbd("remove", buf); | |
97 | } | |
98 | ||
99 | static int have_minor_attr(void) | |
100 | { | |
101 | /* | |
102 | * 'minor' attribute was added as part of single_major merge, which | |
103 | * exposed the 'single_major' parameter. 'minor' is always present, | |
104 | * regardless of whether single-major scheme is turned on or not. | |
105 | * | |
106 | * (Something like ver >= KERNEL_VERSION(3, 14, 0) is a no-go because | |
107 | * this has to work with rbd.ko backported to various kernels.) | |
108 | */ | |
109 | return access("/sys/module/rbd/parameters/single_major", F_OK) == 0; | |
110 | } | |
111 | ||
112 | static int build_map_buf(CephContext *cct, const char *pool, const char *image, | |
113 | const char *snap, const char *options, string *pbuf) | |
114 | { | |
115 | ostringstream oss; | |
116 | int r; | |
117 | ||
118 | MonMap monmap; | |
119 | r = monmap.build_initial(cct, cerr); | |
120 | if (r < 0) | |
121 | return r; | |
122 | ||
123 | list<entity_addr_t> mon_addr; | |
124 | monmap.list_addrs(mon_addr); | |
125 | ||
126 | for (const auto &p : mon_addr) { | |
127 | if (oss.tellp() > 0) { | |
128 | oss << ","; | |
129 | } | |
130 | oss << p.get_sockaddr(); | |
131 | } | |
132 | ||
133 | oss << " name=" << cct->_conf->name.get_id(); | |
134 | ||
135 | KeyRing keyring; | |
224ce89b WB |
136 | if (cct->_conf->auth_client_required != "none") { |
137 | r = keyring.from_ceph_context(cct); | |
138 | if (r == -ENOENT && !(cct->_conf->keyfile.length() || | |
139 | cct->_conf->key.length())) | |
140 | r = 0; | |
141 | if (r < 0) { | |
142 | cerr << "rbd: failed to get secret" << std::endl; | |
143 | return r; | |
144 | } | |
7c673cae FG |
145 | } |
146 | ||
147 | CryptoKey secret; | |
148 | string key_name = string("client.") + cct->_conf->name.get_id(); | |
149 | if (keyring.get_secret(cct->_conf->name, secret)) { | |
150 | string secret_str; | |
151 | secret.encode_base64(secret_str); | |
152 | ||
153 | r = set_kernel_secret(secret_str.c_str(), key_name.c_str()); | |
154 | if (r >= 0) { | |
155 | if (r == 0) | |
156 | cerr << "rbd: warning: secret has length 0" << std::endl; | |
157 | oss << ",key=" << key_name; | |
158 | } else if (r == -ENODEV || r == -ENOSYS) { | |
159 | // running against older kernel; fall back to secret= in options | |
160 | oss << ",secret=" << secret_str; | |
161 | } else { | |
162 | cerr << "rbd: failed to add secret '" << key_name << "' to kernel" | |
163 | << std::endl; | |
164 | return r; | |
165 | } | |
166 | } else if (is_kernel_secret(key_name.c_str())) { | |
167 | oss << ",key=" << key_name; | |
168 | } | |
169 | ||
170 | if (strcmp(options, "") != 0) | |
171 | oss << "," << options; | |
172 | ||
173 | oss << " " << pool << " " << image << " " << snap; | |
174 | ||
175 | *pbuf = oss.str(); | |
176 | return 0; | |
177 | } | |
178 | ||
179 | static int wait_for_udev_add(struct udev_monitor *mon, const char *pool, | |
180 | const char *image, const char *snap, | |
181 | string *pname) | |
182 | { | |
183 | struct udev_device *bus_dev = NULL; | |
184 | ||
185 | /* | |
186 | * Catch /sys/devices/rbd/<id>/ and wait for the corresponding | |
187 | * block device to show up. This is necessary because rbd devices | |
188 | * and block devices aren't linked together in our sysfs layout. | |
189 | */ | |
190 | for (;;) { | |
191 | struct pollfd fds[1]; | |
192 | struct udev_device *dev; | |
193 | ||
194 | fds[0].fd = udev_monitor_get_fd(mon); | |
195 | fds[0].events = POLLIN; | |
196 | if (poll(fds, 1, POLL_TIMEOUT) < 0) | |
197 | return -errno; | |
198 | ||
199 | dev = udev_monitor_receive_device(mon); | |
200 | if (!dev) | |
201 | continue; | |
202 | ||
203 | if (strcmp(udev_device_get_action(dev), "add") != 0) | |
204 | goto next; | |
205 | ||
206 | if (!bus_dev) { | |
207 | if (strcmp(udev_device_get_subsystem(dev), "rbd") == 0) { | |
208 | const char *this_pool = udev_device_get_sysattr_value(dev, "pool"); | |
209 | const char *this_image = udev_device_get_sysattr_value(dev, "name"); | |
210 | const char *this_snap = udev_device_get_sysattr_value(dev, | |
211 | "current_snap"); | |
212 | ||
213 | if (this_pool && strcmp(this_pool, pool) == 0 && | |
214 | this_image && strcmp(this_image, image) == 0 && | |
215 | this_snap && strcmp(this_snap, snap) == 0) { | |
216 | bus_dev = dev; | |
217 | continue; | |
218 | } | |
219 | } | |
220 | } else { | |
221 | if (strcmp(udev_device_get_subsystem(dev), "block") == 0) { | |
222 | const char *major = udev_device_get_sysattr_value(bus_dev, "major"); | |
223 | const char *minor = udev_device_get_sysattr_value(bus_dev, "minor"); | |
224 | const char *this_major = udev_device_get_property_value(dev, "MAJOR"); | |
225 | const char *this_minor = udev_device_get_property_value(dev, "MINOR"); | |
226 | ||
227 | assert(!minor ^ have_minor_attr()); | |
228 | ||
229 | if (strcmp(this_major, major) == 0 && | |
230 | (!minor || strcmp(this_minor, minor) == 0)) { | |
231 | string name = get_kernel_rbd_name(udev_device_get_sysname(bus_dev)); | |
232 | ||
233 | assert(strcmp(udev_device_get_devnode(dev), name.c_str()) == 0); | |
234 | *pname = name; | |
235 | ||
236 | udev_device_unref(dev); | |
237 | udev_device_unref(bus_dev); | |
238 | break; | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | next: | |
244 | udev_device_unref(dev); | |
245 | } | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static int do_map(struct udev *udev, const char *pool, const char *image, | |
251 | const char *snap, const string& buf, string *pname) | |
252 | { | |
253 | struct udev_monitor *mon; | |
254 | int r; | |
255 | ||
256 | mon = udev_monitor_new_from_netlink(udev, "udev"); | |
257 | if (!mon) | |
258 | return -ENOMEM; | |
259 | ||
260 | r = udev_monitor_filter_add_match_subsystem_devtype(mon, "rbd", NULL); | |
261 | if (r < 0) | |
262 | goto out_mon; | |
263 | ||
264 | r = udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk"); | |
265 | if (r < 0) | |
266 | goto out_mon; | |
267 | ||
268 | r = udev_monitor_enable_receiving(mon); | |
269 | if (r < 0) | |
270 | goto out_mon; | |
271 | ||
272 | r = sysfs_write_rbd_add(buf); | |
273 | if (r < 0) { | |
274 | cerr << "rbd: sysfs write failed" << std::endl; | |
275 | goto out_mon; | |
276 | } | |
277 | ||
278 | r = wait_for_udev_add(mon, pool, image, snap, pname); | |
279 | if (r < 0) { | |
280 | cerr << "rbd: wait failed" << std::endl; | |
281 | goto out_mon; | |
282 | } | |
283 | ||
284 | out_mon: | |
285 | udev_monitor_unref(mon); | |
286 | return r; | |
287 | } | |
288 | ||
289 | static int map_image(struct krbd_ctx *ctx, const char *pool, const char *image, | |
290 | const char *snap, const char *options, string *pname) | |
291 | { | |
292 | string buf; | |
293 | int r; | |
294 | ||
295 | if (strcmp(snap, "") == 0) | |
296 | snap = "-"; | |
297 | ||
298 | r = build_map_buf(ctx->cct, pool, image, snap, options, &buf); | |
299 | if (r < 0) | |
300 | return r; | |
301 | ||
302 | /* | |
303 | * Modprobe rbd kernel module. If it supports single-major device | |
304 | * number allocation scheme, make sure it's turned on. | |
305 | */ | |
306 | if (access("/sys/bus/rbd", F_OK) != 0) { | |
307 | const char *module_options = NULL; | |
308 | if (module_has_param("rbd", "single_major")) | |
309 | module_options = "single_major=Y"; | |
310 | ||
311 | r = module_load("rbd", module_options); | |
312 | if (r) { | |
313 | cerr << "rbd: failed to load rbd kernel module (" << r << ")" | |
314 | << std::endl; | |
315 | /* | |
316 | * Ignore the error: modprobe failing doesn't necessarily prevent | |
317 | * from working. | |
318 | */ | |
319 | } | |
320 | } | |
321 | ||
322 | return do_map(ctx->udev, pool, image, snap, buf, pname); | |
323 | } | |
324 | ||
325 | static int devno_to_krbd_id(struct udev *udev, dev_t devno, string *pid) | |
326 | { | |
327 | struct udev_enumerate *enm; | |
328 | struct udev_list_entry *l; | |
329 | struct udev_device *dev; | |
330 | int r; | |
331 | ||
332 | enm = udev_enumerate_new(udev); | |
333 | if (!enm) | |
334 | return -ENOMEM; | |
335 | ||
336 | r = udev_enumerate_add_match_subsystem(enm, "rbd"); | |
337 | if (r < 0) | |
338 | goto out_enm; | |
339 | ||
340 | r = udev_enumerate_add_match_sysattr(enm, "major", | |
341 | stringify(major(devno)).c_str()); | |
342 | if (r < 0) | |
343 | goto out_enm; | |
344 | ||
345 | if (have_minor_attr()) { | |
346 | r = udev_enumerate_add_match_sysattr(enm, "minor", | |
347 | stringify(minor(devno)).c_str()); | |
348 | if (r < 0) | |
349 | goto out_enm; | |
350 | } | |
351 | ||
352 | r = udev_enumerate_scan_devices(enm); | |
353 | if (r < 0) | |
354 | goto out_enm; | |
355 | ||
356 | l = udev_enumerate_get_list_entry(enm); | |
357 | if (!l) { | |
358 | r = -ENOENT; | |
359 | goto out_enm; | |
360 | } | |
361 | ||
362 | /* make sure there is only one match */ | |
363 | assert(!udev_list_entry_get_next(l)); | |
364 | ||
365 | dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l)); | |
366 | if (!dev) { | |
367 | r = -ENOMEM; | |
368 | goto out_enm; | |
369 | } | |
370 | ||
371 | *pid = udev_device_get_sysname(dev); | |
372 | ||
373 | udev_device_unref(dev); | |
374 | out_enm: | |
375 | udev_enumerate_unref(enm); | |
376 | return r; | |
377 | } | |
378 | ||
379 | static int spec_to_devno_and_krbd_id(struct udev *udev, const char *pool, | |
380 | const char *image, const char *snap, | |
381 | dev_t *pdevno, string *pid) | |
382 | { | |
383 | struct udev_enumerate *enm; | |
384 | struct udev_list_entry *l; | |
385 | struct udev_device *dev; | |
386 | unsigned int maj, min = 0; | |
387 | string err; | |
388 | int r; | |
389 | ||
390 | enm = udev_enumerate_new(udev); | |
391 | if (!enm) | |
392 | return -ENOMEM; | |
393 | ||
394 | r = udev_enumerate_add_match_subsystem(enm, "rbd"); | |
395 | if (r < 0) | |
396 | goto out_enm; | |
397 | ||
398 | r = udev_enumerate_add_match_sysattr(enm, "pool", pool); | |
399 | if (r < 0) | |
400 | goto out_enm; | |
401 | ||
402 | r = udev_enumerate_add_match_sysattr(enm, "name", image); | |
403 | if (r < 0) | |
404 | goto out_enm; | |
405 | ||
406 | r = udev_enumerate_add_match_sysattr(enm, "current_snap", snap); | |
407 | if (r < 0) | |
408 | goto out_enm; | |
409 | ||
410 | r = udev_enumerate_scan_devices(enm); | |
411 | if (r < 0) | |
412 | goto out_enm; | |
413 | ||
414 | l = udev_enumerate_get_list_entry(enm); | |
415 | if (!l) { | |
416 | r = -ENOENT; | |
417 | goto out_enm; | |
418 | } | |
419 | ||
420 | dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l)); | |
421 | if (!dev) { | |
422 | r = -ENOMEM; | |
423 | goto out_enm; | |
424 | } | |
425 | ||
426 | maj = strict_strtoll(udev_device_get_sysattr_value(dev, "major"), 10, &err); | |
427 | if (!err.empty()) { | |
428 | cerr << "rbd: couldn't parse major: " << err << std::endl; | |
429 | r = -EINVAL; | |
430 | goto out_dev; | |
431 | } | |
432 | if (have_minor_attr()) { | |
433 | min = strict_strtoll(udev_device_get_sysattr_value(dev, "minor"), 10, &err); | |
434 | if (!err.empty()) { | |
435 | cerr << "rbd: couldn't parse minor: " << err << std::endl; | |
436 | r = -EINVAL; | |
437 | goto out_dev; | |
438 | } | |
439 | } | |
440 | ||
441 | /* | |
442 | * If an image is mapped more than once don't bother trying to unmap | |
443 | * all devices - let users run unmap the same number of times they | |
444 | * ran map. | |
445 | */ | |
446 | if (udev_list_entry_get_next(l)) | |
447 | cerr << "rbd: " << pool << "/" << image << "@" << snap | |
448 | << ": mapped more than once, unmapping " | |
449 | << get_kernel_rbd_name(udev_device_get_sysname(dev)) | |
450 | << " only" << std::endl; | |
451 | ||
452 | *pdevno = makedev(maj, min); | |
453 | *pid = udev_device_get_sysname(dev); | |
454 | ||
455 | out_dev: | |
456 | udev_device_unref(dev); | |
457 | out_enm: | |
458 | udev_enumerate_unref(enm); | |
459 | return r; | |
460 | } | |
461 | ||
462 | static string build_unmap_buf(const string& id, const char *options) | |
463 | { | |
464 | string buf(id); | |
465 | if (strcmp(options, "") != 0) { | |
466 | buf += " "; | |
467 | buf += options; | |
468 | } | |
469 | return buf; | |
470 | } | |
471 | ||
472 | static int wait_for_udev_remove(struct udev_monitor *mon, dev_t devno) | |
473 | { | |
474 | for (;;) { | |
475 | struct pollfd fds[1]; | |
476 | struct udev_device *dev; | |
477 | ||
478 | fds[0].fd = udev_monitor_get_fd(mon); | |
479 | fds[0].events = POLLIN; | |
480 | if (poll(fds, 1, POLL_TIMEOUT) < 0) | |
481 | return -errno; | |
482 | ||
483 | dev = udev_monitor_receive_device(mon); | |
484 | if (!dev) | |
485 | continue; | |
486 | ||
487 | if (strcmp(udev_device_get_action(dev), "remove") == 0 && | |
488 | udev_device_get_devnum(dev) == devno) { | |
489 | udev_device_unref(dev); | |
490 | break; | |
491 | } | |
492 | ||
493 | udev_device_unref(dev); | |
494 | } | |
495 | ||
496 | return 0; | |
497 | } | |
498 | ||
499 | static int do_unmap(struct udev *udev, dev_t devno, const string& buf) | |
500 | { | |
501 | struct udev_monitor *mon; | |
502 | int r; | |
503 | ||
504 | mon = udev_monitor_new_from_netlink(udev, "udev"); | |
505 | if (!mon) | |
506 | return -ENOMEM; | |
507 | ||
508 | r = udev_monitor_filter_add_match_subsystem_devtype(mon, "block", "disk"); | |
509 | if (r < 0) | |
510 | goto out_mon; | |
511 | ||
512 | r = udev_monitor_enable_receiving(mon); | |
513 | if (r < 0) | |
514 | goto out_mon; | |
515 | ||
516 | /* | |
517 | * On final device close(), kernel sends a block change event, in | |
518 | * response to which udev apparently runs blkid on the device. This | |
519 | * makes unmap fail with EBUSY, if issued right after final close(). | |
520 | * Try to circumvent this with a retry before turning to udev. | |
521 | */ | |
522 | for (int tries = 0; ; tries++) { | |
523 | r = sysfs_write_rbd_remove(buf); | |
524 | if (r >= 0) { | |
525 | break; | |
526 | } else if (r == -EBUSY && tries < 2) { | |
527 | if (!tries) { | |
528 | usleep(250 * 1000); | |
529 | } else { | |
530 | /* | |
531 | * libudev does not provide the "wait until the queue is empty" | |
532 | * API or the sufficient amount of primitives to build it from. | |
533 | */ | |
534 | string err = run_cmd("udevadm", "settle", "--timeout", "10", NULL); | |
535 | if (!err.empty()) | |
536 | cerr << "rbd: " << err << std::endl; | |
537 | } | |
538 | } else { | |
539 | cerr << "rbd: sysfs write failed" << std::endl; | |
540 | goto out_mon; | |
541 | } | |
542 | } | |
543 | ||
544 | r = wait_for_udev_remove(mon, devno); | |
545 | if (r < 0) { | |
546 | cerr << "rbd: wait failed" << std::endl; | |
547 | goto out_mon; | |
548 | } | |
549 | ||
550 | out_mon: | |
551 | udev_monitor_unref(mon); | |
552 | return r; | |
553 | } | |
554 | ||
555 | static int unmap_image(struct krbd_ctx *ctx, const char *devnode, | |
556 | const char *options) | |
557 | { | |
558 | struct stat sb; | |
559 | dev_t wholedevno = 0; | |
560 | string id; | |
561 | int r; | |
562 | ||
563 | if (stat(devnode, &sb) < 0 || !S_ISBLK(sb.st_mode)) { | |
564 | cerr << "rbd: '" << devnode << "' is not a block device" << std::endl; | |
565 | return -EINVAL; | |
566 | } | |
567 | ||
568 | r = blkid_devno_to_wholedisk(sb.st_rdev, NULL, 0, &wholedevno); | |
569 | if (r < 0) { | |
570 | cerr << "rbd: couldn't compute wholedevno: " << cpp_strerror(r) | |
571 | << std::endl; | |
572 | /* | |
573 | * Ignore the error: we are given whole disks most of the time, and | |
574 | * if it turns out this is a partition we will fail later anyway. | |
575 | */ | |
576 | wholedevno = sb.st_rdev; | |
577 | } | |
578 | ||
579 | r = devno_to_krbd_id(ctx->udev, wholedevno, &id); | |
580 | if (r < 0) { | |
581 | if (r == -ENOENT) { | |
582 | cerr << "rbd: '" << devnode << "' is not an rbd device" << std::endl; | |
583 | r = -EINVAL; | |
584 | } | |
585 | return r; | |
586 | } | |
587 | ||
588 | return do_unmap(ctx->udev, wholedevno, build_unmap_buf(id, options)); | |
589 | } | |
590 | ||
591 | static int unmap_image(struct krbd_ctx *ctx, const char *pool, | |
592 | const char *image, const char *snap, | |
593 | const char *options) | |
594 | { | |
595 | dev_t devno = 0; | |
596 | string id; | |
597 | int r; | |
598 | ||
599 | if (!snap) | |
600 | snap = "-"; | |
601 | ||
602 | r = spec_to_devno_and_krbd_id(ctx->udev, pool, image, snap, &devno, &id); | |
603 | if (r < 0) { | |
604 | if (r == -ENOENT) { | |
605 | cerr << "rbd: " << pool << "/" << image << "@" << snap | |
606 | << ": not a mapped image or snapshot" << std::endl; | |
607 | r = -EINVAL; | |
608 | } | |
609 | return r; | |
610 | } | |
611 | ||
612 | return do_unmap(ctx->udev, devno, build_unmap_buf(id, options)); | |
613 | } | |
614 | ||
615 | static bool dump_one_image(Formatter *f, TextTable *tbl, | |
616 | struct udev_device *dev) | |
617 | { | |
618 | const char *id = udev_device_get_sysname(dev); | |
619 | const char *pool = udev_device_get_sysattr_value(dev, "pool"); | |
620 | const char *image = udev_device_get_sysattr_value(dev, "name"); | |
621 | const char *snap = udev_device_get_sysattr_value(dev, "current_snap"); | |
622 | string kname = get_kernel_rbd_name(id); | |
623 | ||
624 | if (!pool || !image || !snap) | |
625 | return false; | |
626 | ||
627 | if (f) { | |
628 | f->open_object_section(id); | |
629 | f->dump_string("pool", pool); | |
630 | f->dump_string("name", image); | |
631 | f->dump_string("snap", snap); | |
632 | f->dump_string("device", kname); | |
633 | f->close_section(); | |
634 | } else { | |
635 | *tbl << id << pool << image << snap << kname << TextTable::endrow; | |
636 | } | |
637 | ||
638 | return true; | |
639 | } | |
640 | ||
641 | static int do_dump(struct udev *udev, Formatter *f, TextTable *tbl) | |
642 | { | |
643 | struct udev_enumerate *enm; | |
644 | struct udev_list_entry *l; | |
645 | bool have_output = false; | |
646 | int r; | |
647 | ||
648 | enm = udev_enumerate_new(udev); | |
649 | if (!enm) | |
650 | return -ENOMEM; | |
651 | ||
652 | r = udev_enumerate_add_match_subsystem(enm, "rbd"); | |
653 | if (r < 0) | |
654 | goto out_enm; | |
655 | ||
656 | r = udev_enumerate_scan_devices(enm); | |
657 | if (r < 0) | |
658 | goto out_enm; | |
659 | ||
660 | udev_list_entry_foreach(l, udev_enumerate_get_list_entry(enm)) { | |
661 | struct udev_device *dev; | |
662 | ||
663 | dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(l)); | |
664 | if (dev) { | |
665 | have_output |= dump_one_image(f, tbl, dev); | |
666 | udev_device_unref(dev); | |
667 | } | |
668 | } | |
669 | ||
670 | r = have_output; | |
671 | out_enm: | |
672 | udev_enumerate_unref(enm); | |
673 | return r; | |
674 | } | |
675 | ||
676 | int dump_images(struct krbd_ctx *ctx, Formatter *f) | |
677 | { | |
678 | TextTable tbl; | |
679 | int r; | |
680 | ||
681 | if (f) { | |
682 | f->open_object_section("devices"); | |
683 | } else { | |
684 | tbl.define_column("id", TextTable::LEFT, TextTable::LEFT); | |
685 | tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT); | |
686 | tbl.define_column("image", TextTable::LEFT, TextTable::LEFT); | |
687 | tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT); | |
688 | tbl.define_column("device", TextTable::LEFT, TextTable::LEFT); | |
689 | } | |
690 | ||
691 | r = do_dump(ctx->udev, f, &tbl); | |
692 | ||
693 | if (f) { | |
694 | f->close_section(); | |
695 | f->flush(cout); | |
696 | } else { | |
697 | if (r > 0) | |
698 | cout << tbl; | |
699 | } | |
700 | ||
701 | return r; | |
702 | } | |
703 | ||
704 | extern "C" int krbd_create_from_context(rados_config_t cct, | |
705 | struct krbd_ctx **pctx) | |
706 | { | |
707 | struct krbd_ctx *ctx = new struct krbd_ctx(); | |
708 | ||
709 | ctx->cct = reinterpret_cast<CephContext *>(cct); | |
710 | ctx->udev = udev_new(); | |
711 | if (!ctx->udev) { | |
712 | delete ctx; | |
713 | return -ENOMEM; | |
714 | } | |
715 | ||
716 | *pctx = ctx; | |
717 | return 0; | |
718 | } | |
719 | ||
720 | extern "C" void krbd_destroy(struct krbd_ctx *ctx) | |
721 | { | |
722 | if (!ctx) | |
723 | return; | |
724 | ||
725 | udev_unref(ctx->udev); | |
726 | ||
727 | delete ctx; | |
728 | } | |
729 | ||
730 | extern "C" int krbd_map(struct krbd_ctx *ctx, const char *pool, | |
731 | const char *image, const char *snap, | |
732 | const char *options, char **pdevnode) | |
733 | { | |
734 | string name; | |
735 | char *devnode; | |
736 | int r; | |
737 | ||
738 | r = map_image(ctx, pool, image, snap, options, &name); | |
739 | if (r < 0) | |
740 | return r; | |
741 | ||
742 | devnode = strdup(name.c_str()); | |
743 | if (!devnode) | |
744 | return -ENOMEM; | |
745 | ||
746 | *pdevnode = devnode; | |
747 | return r; | |
748 | } | |
749 | ||
750 | extern "C" int krbd_unmap(struct krbd_ctx *ctx, const char *devnode, | |
751 | const char *options) | |
752 | { | |
753 | return unmap_image(ctx, devnode, options); | |
754 | } | |
755 | ||
756 | extern "C" int krbd_unmap_by_spec(struct krbd_ctx *ctx, const char *pool, | |
757 | const char *image, const char *snap, | |
758 | const char *options) | |
759 | { | |
760 | return unmap_image(ctx, pool, image, snap, options); | |
761 | } | |
762 | ||
763 | int krbd_showmapped(struct krbd_ctx *ctx, Formatter *f) | |
764 | { | |
765 | return dump_images(ctx, f); | |
766 | } |