]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
7c673cae FG |
3 | /* |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (c) 2015 Hewlett-Packard Development Company, L.P. | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
11fdf7f2 | 10 | * License version 2.1, as published by the Free Software |
7c673cae | 11 | * Foundation. See file COPYING. |
11fdf7f2 | 12 | * |
7c673cae | 13 | */ |
31f18b77 | 14 | |
11fdf7f2 TL |
15 | #include "include/compat.h" |
16 | ||
17 | #ifdef __FreeBSD__ | |
18 | #include <sys/param.h> | |
19 | #include <geom/geom_disk.h> | |
20 | #include <sys/disk.h> | |
21 | #include <fcntl.h> | |
22 | #endif | |
23 | ||
7c673cae | 24 | #include <errno.h> |
7c673cae FG |
25 | #include <sys/ioctl.h> |
26 | #include <sys/stat.h> | |
11fdf7f2 TL |
27 | #include <sys/types.h> |
28 | #include <sys/stat.h> | |
29 | #include <fcntl.h> | |
7c673cae | 30 | #include <dirent.h> |
11fdf7f2 TL |
31 | #include <boost/algorithm/string/replace.hpp> |
32 | //#include "common/debug.h" | |
33 | #include "include/scope_guard.h" | |
7c673cae | 34 | #include "include/uuid.h" |
11fdf7f2 TL |
35 | #include "include/stringify.h" |
36 | #include "blkdev.h" | |
37 | #include "numa.h" | |
38 | ||
39 | #include "json_spirit/json_spirit_reader.h" | |
40 | ||
f67539c2 | 41 | |
11fdf7f2 TL |
42 | int get_device_by_path(const char *path, char* partition, char* device, |
43 | size_t max) | |
44 | { | |
45 | int fd = ::open(path, O_RDONLY|O_DIRECTORY); | |
46 | if (fd < 0) { | |
47 | return -errno; | |
48 | } | |
49 | auto close_fd = make_scope_guard([fd] { | |
50 | ::close(fd); | |
51 | }); | |
52 | BlkDev blkdev(fd); | |
53 | if (auto ret = blkdev.partition(partition, max); ret) { | |
54 | return ret; | |
55 | } | |
56 | if (auto ret = blkdev.wholedisk(device, max); ret) { | |
57 | return ret; | |
58 | } | |
59 | return 0; | |
60 | } | |
61 | ||
62 | ||
63 | #include "common/blkdev.h" | |
7c673cae FG |
64 | |
65 | #ifdef __linux__ | |
11fdf7f2 | 66 | #include <libudev.h> |
7c673cae | 67 | #include <linux/fs.h> |
11fdf7f2 | 68 | #include <linux/kdev_t.h> |
7c673cae FG |
69 | #include <blkid/blkid.h> |
70 | ||
11fdf7f2 TL |
71 | #include <set> |
72 | ||
73 | #include "common/SubProcess.h" | |
74 | #include "common/errno.h" | |
75 | ||
76 | ||
7c673cae FG |
77 | #define UUID_LEN 36 |
78 | ||
11fdf7f2 TL |
79 | #endif |
80 | ||
f67539c2 TL |
81 | using namespace std::literals; |
82 | ||
83 | using std::string; | |
84 | ||
85 | using ceph::bufferlist; | |
86 | ||
7c673cae | 87 | |
11fdf7f2 TL |
88 | BlkDev::BlkDev(int f) |
89 | : fd(f) | |
90 | {} | |
91 | ||
92 | BlkDev::BlkDev(const std::string& devname) | |
93 | : devname(devname) | |
94 | {} | |
95 | ||
96 | int BlkDev::get_devid(dev_t *id) const | |
7c673cae | 97 | { |
11fdf7f2 TL |
98 | struct stat st; |
99 | int r; | |
100 | if (fd >= 0) { | |
101 | r = fstat(fd, &st); | |
102 | } else { | |
103 | char path[PATH_MAX]; | |
104 | snprintf(path, sizeof(path), "/dev/%s", devname.c_str()); | |
105 | r = stat(path, &st); | |
106 | } | |
107 | if (r < 0) { | |
108 | return -errno; | |
109 | } | |
110 | *id = S_ISBLK(st.st_mode) ? st.st_rdev : st.st_dev; | |
111 | return 0; | |
112 | } | |
113 | ||
114 | #ifdef __linux__ | |
11fdf7f2 TL |
115 | |
116 | const char *BlkDev::sysfsdir() const { | |
117 | return "/sys"; | |
7c673cae FG |
118 | } |
119 | ||
11fdf7f2 | 120 | int BlkDev::get_size(int64_t *psize) const |
7c673cae FG |
121 | { |
122 | #ifdef BLKGETSIZE64 | |
123 | int ret = ::ioctl(fd, BLKGETSIZE64, psize); | |
124 | #elif defined(BLKGETSIZE) | |
125 | unsigned long sectors = 0; | |
126 | int ret = ::ioctl(fd, BLKGETSIZE, §ors); | |
127 | *psize = sectors * 512ULL; | |
128 | #else | |
129 | // cppcheck-suppress preprocessorErrorDirective | |
11fdf7f2 | 130 | # error "Linux configuration error (get_size)" |
7c673cae FG |
131 | #endif |
132 | if (ret < 0) | |
133 | ret = -errno; | |
134 | return ret; | |
135 | } | |
136 | ||
7c673cae FG |
137 | /** |
138 | * get a block device property as a string | |
139 | * | |
140 | * store property in *val, up to maxlen chars | |
141 | * return 0 on success | |
142 | * return negative error on error | |
143 | */ | |
9f95a23c | 144 | int64_t BlkDev::get_string_property(const char* prop, |
11fdf7f2 | 145 | char *val, size_t maxlen) const |
7c673cae | 146 | { |
11fdf7f2 TL |
147 | char filename[PATH_MAX], wd[PATH_MAX]; |
148 | const char* dev = nullptr; | |
11fdf7f2 TL |
149 | |
150 | if (fd >= 0) { | |
151 | // sysfs isn't fully populated for partitions, so we need to lookup the sysfs | |
152 | // entry for the underlying whole disk. | |
153 | if (int r = wholedisk(wd, sizeof(wd)); r < 0) | |
154 | return r; | |
155 | dev = wd; | |
156 | } else { | |
157 | dev = devname.c_str(); | |
158 | } | |
9f95a23c TL |
159 | if (snprintf(filename, sizeof(filename), "%s/block/%s/%s", sysfsdir(), dev, |
160 | prop) >= static_cast<int>(sizeof(filename))) { | |
161 | return -ERANGE; | |
162 | } | |
7c673cae FG |
163 | |
164 | FILE *fp = fopen(filename, "r"); | |
165 | if (fp == NULL) { | |
166 | return -errno; | |
167 | } | |
168 | ||
169 | int r = 0; | |
170 | if (fgets(val, maxlen - 1, fp)) { | |
171 | // truncate at newline | |
172 | char *p = val; | |
173 | while (*p && *p != '\n') | |
174 | ++p; | |
175 | *p = 0; | |
176 | } else { | |
177 | r = -EINVAL; | |
178 | } | |
179 | fclose(fp); | |
180 | return r; | |
181 | } | |
182 | ||
183 | /** | |
184 | * get a block device property | |
185 | * | |
186 | * return the value (we assume it is positive) | |
187 | * return negative error on error | |
188 | */ | |
9f95a23c | 189 | int64_t BlkDev::get_int_property(const char* prop) const |
7c673cae FG |
190 | { |
191 | char buff[256] = {0}; | |
11fdf7f2 | 192 | int r = get_string_property(prop, buff, sizeof(buff)); |
7c673cae FG |
193 | if (r < 0) |
194 | return r; | |
195 | // take only digits | |
196 | for (char *p = buff; *p; ++p) { | |
197 | if (!isdigit(*p)) { | |
198 | *p = 0; | |
199 | break; | |
200 | } | |
201 | } | |
202 | char *endptr = 0; | |
203 | r = strtoll(buff, &endptr, 10); | |
204 | if (endptr != buff + strlen(buff)) | |
205 | r = -EINVAL; | |
206 | return r; | |
207 | } | |
208 | ||
11fdf7f2 | 209 | bool BlkDev::support_discard() const |
7c673cae | 210 | { |
9f95a23c | 211 | return get_int_property("queue/discard_granularity") > 0; |
7c673cae FG |
212 | } |
213 | ||
11fdf7f2 | 214 | int BlkDev::discard(int64_t offset, int64_t len) const |
7c673cae FG |
215 | { |
216 | uint64_t range[2] = {(uint64_t)offset, (uint64_t)len}; | |
217 | return ioctl(fd, BLKDISCARD, range); | |
218 | } | |
219 | ||
11fdf7f2 | 220 | bool BlkDev::is_rotational() const |
7c673cae | 221 | { |
9f95a23c | 222 | return get_int_property("queue/rotational") > 0; |
7c673cae FG |
223 | } |
224 | ||
11fdf7f2 | 225 | int BlkDev::get_numa_node(int *node) const |
7c673cae | 226 | { |
9f95a23c | 227 | int numa = get_int_property("device/device/numa_node"); |
11fdf7f2 TL |
228 | if (numa < 0) |
229 | return -1; | |
230 | *node = numa; | |
231 | return 0; | |
7c673cae FG |
232 | } |
233 | ||
11fdf7f2 | 234 | int BlkDev::dev(char *dev, size_t max) const |
7c673cae | 235 | { |
9f95a23c | 236 | return get_string_property("dev", dev, max); |
11fdf7f2 | 237 | } |
7c673cae | 238 | |
11fdf7f2 TL |
239 | int BlkDev::vendor(char *vendor, size_t max) const |
240 | { | |
9f95a23c | 241 | return get_string_property("device/device/vendor", vendor, max); |
11fdf7f2 | 242 | } |
7c673cae | 243 | |
11fdf7f2 TL |
244 | int BlkDev::model(char *model, size_t max) const |
245 | { | |
9f95a23c | 246 | return get_string_property("device/model", model, max); |
11fdf7f2 TL |
247 | } |
248 | ||
249 | int BlkDev::serial(char *serial, size_t max) const | |
250 | { | |
9f95a23c | 251 | return get_string_property("device/serial", serial, max); |
11fdf7f2 TL |
252 | } |
253 | ||
254 | int BlkDev::partition(char *partition, size_t max) const | |
255 | { | |
256 | dev_t id; | |
257 | int r = get_devid(&id); | |
258 | if (r < 0) | |
259 | return -EINVAL; // hrm. | |
260 | ||
261 | char *t = blkid_devno_to_devname(id); | |
262 | if (!t) { | |
263 | return -EINVAL; | |
264 | } | |
265 | strncpy(partition, t, max); | |
266 | free(t); | |
267 | return 0; | |
268 | } | |
269 | ||
270 | int BlkDev::wholedisk(char *device, size_t max) const | |
271 | { | |
272 | dev_t id; | |
273 | int r = get_devid(&id); | |
274 | if (r < 0) | |
275 | return -EINVAL; // hrm. | |
276 | ||
277 | r = blkid_devno_to_wholedisk(id, device, max, nullptr); | |
278 | if (r < 0) { | |
7c673cae | 279 | return -EINVAL; |
11fdf7f2 TL |
280 | } |
281 | return 0; | |
282 | } | |
283 | ||
284 | static int easy_readdir(const std::string& dir, std::set<std::string> *out) | |
285 | { | |
286 | DIR *h = ::opendir(dir.c_str()); | |
287 | if (!h) { | |
288 | return -errno; | |
289 | } | |
290 | struct dirent *de = nullptr; | |
291 | while ((de = ::readdir(h))) { | |
292 | if (strcmp(de->d_name, ".") == 0 || | |
293 | strcmp(de->d_name, "..") == 0) { | |
294 | continue; | |
295 | } | |
296 | out->insert(de->d_name); | |
297 | } | |
298 | closedir(h); | |
299 | return 0; | |
300 | } | |
7c673cae | 301 | |
11fdf7f2 TL |
302 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) |
303 | { | |
304 | std::string p = std::string("/sys/block/") + dev + "/slaves"; | |
305 | std::set<std::string> parents; | |
306 | easy_readdir(p, &parents); | |
307 | for (auto& d : parents) { | |
308 | ls->insert(d); | |
309 | // recurse in case it is dm-on-dm | |
310 | if (d.find("dm-") == 0) { | |
311 | get_dm_parents(d, ls); | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | void get_raw_devices(const std::string& in, | |
317 | std::set<std::string> *ls) | |
318 | { | |
319 | if (in.substr(0, 3) == "dm-") { | |
320 | std::set<std::string> o; | |
321 | get_dm_parents(in, &o); | |
322 | for (auto& d : o) { | |
323 | get_raw_devices(d, ls); | |
324 | } | |
325 | } else { | |
326 | BlkDev d(in); | |
327 | std::string wholedisk; | |
328 | if (d.wholedisk(&wholedisk) == 0) { | |
329 | ls->insert(wholedisk); | |
7c673cae | 330 | } else { |
11fdf7f2 TL |
331 | ls->insert(in); |
332 | } | |
333 | } | |
334 | } | |
335 | ||
336 | int _get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
337 | { | |
338 | int vdo_fd = -1; | |
339 | ||
340 | // we need to go from the raw devname (e.g., dm-4) to the VDO volume name. | |
341 | // currently the best way seems to be to look at /dev/mapper/* ... | |
342 | std::string expect = std::string("../") + devname; // expected symlink target | |
343 | DIR *dir = ::opendir("/dev/mapper"); | |
344 | if (!dir) { | |
345 | return -1; | |
346 | } | |
347 | struct dirent *de = nullptr; | |
348 | while ((de = ::readdir(dir))) { | |
349 | if (de->d_name[0] == '.') | |
350 | continue; | |
351 | char fn[4096], target[4096]; | |
352 | snprintf(fn, sizeof(fn), "/dev/mapper/%s", de->d_name); | |
353 | int r = readlink(fn, target, sizeof(target)); | |
354 | if (r < 0 || r >= (int)sizeof(target)) | |
355 | continue; | |
356 | target[r] = 0; | |
357 | if (expect == target) { | |
358 | snprintf(fn, sizeof(fn), "/sys/kvdo/%s/statistics", de->d_name); | |
359 | vdo_fd = ::open(fn, O_RDONLY|O_CLOEXEC); //DIRECTORY); | |
360 | if (vdo_fd >= 0) { | |
361 | *vdo_name = de->d_name; | |
362 | break; | |
363 | } | |
364 | } | |
365 | } | |
366 | closedir(dir); | |
367 | return vdo_fd; | |
368 | } | |
369 | ||
370 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
371 | { | |
372 | std::set<std::string> devs = { devname }; | |
373 | while (!devs.empty()) { | |
374 | std::string dev = *devs.begin(); | |
375 | devs.erase(devs.begin()); | |
376 | int fd = _get_vdo_stats_handle(dev.c_str(), vdo_name); | |
377 | if (fd >= 0) { | |
378 | // yay, it's vdo | |
379 | return fd; | |
380 | } | |
381 | // ok, see if there are constituent devices | |
382 | if (dev.find("dm-") == 0) { | |
383 | get_dm_parents(dev, &devs); | |
384 | } | |
385 | } | |
386 | return -1; | |
387 | } | |
388 | ||
389 | int64_t get_vdo_stat(int vdo_fd, const char *property) | |
390 | { | |
391 | int64_t ret = 0; | |
392 | int fd = ::openat(vdo_fd, property, O_RDONLY|O_CLOEXEC); | |
393 | if (fd < 0) { | |
394 | return 0; | |
395 | } | |
396 | char buf[1024]; | |
397 | int r = ::read(fd, buf, sizeof(buf) - 1); | |
398 | if (r > 0) { | |
399 | buf[r] = 0; | |
400 | ret = atoll(buf); | |
401 | } | |
402 | TEMP_FAILURE_RETRY(::close(fd)); | |
403 | return ret; | |
404 | } | |
405 | ||
406 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
407 | { | |
408 | int64_t block_size = get_vdo_stat(fd, "block_size"); | |
409 | int64_t physical_blocks = get_vdo_stat(fd, "physical_blocks"); | |
410 | int64_t overhead_blocks_used = get_vdo_stat(fd, "overhead_blocks_used"); | |
411 | int64_t data_blocks_used = get_vdo_stat(fd, "data_blocks_used"); | |
412 | if (!block_size | |
413 | || !physical_blocks | |
414 | || !overhead_blocks_used | |
415 | || !data_blocks_used) { | |
416 | return false; | |
417 | } | |
418 | int64_t avail_blocks = | |
419 | physical_blocks - overhead_blocks_used - data_blocks_used; | |
420 | *total = block_size * physical_blocks; | |
421 | *avail = block_size * avail_blocks; | |
422 | return true; | |
423 | } | |
424 | ||
425 | std::string _decode_model_enc(const std::string& in) | |
426 | { | |
427 | auto v = boost::replace_all_copy(in, "\\x20", " "); | |
428 | if (auto found = v.find_last_not_of(" "); found != v.npos) { | |
429 | v.erase(found + 1); | |
430 | } | |
431 | std::replace(v.begin(), v.end(), ' ', '_'); | |
432 | return v; | |
433 | } | |
434 | ||
435 | // trying to use udev first, and if it doesn't work, we fall back to | |
436 | // reading /sys/block/$devname/device/(vendor/model/serial). | |
437 | std::string get_device_id(const std::string& devname, | |
438 | std::string *err) | |
439 | { | |
440 | struct udev_device *dev; | |
441 | static struct udev *udev; | |
442 | const char *data; | |
443 | ||
444 | udev = udev_new(); | |
445 | if (!udev) { | |
446 | if (err) { | |
447 | *err = "udev_new failed"; | |
448 | } | |
449 | return {}; | |
450 | } | |
451 | dev = udev_device_new_from_subsystem_sysname(udev, "block", devname.c_str()); | |
452 | if (!dev) { | |
453 | if (err) { | |
454 | *err = std::string("udev_device_new_from_subsystem_sysname failed on '") | |
455 | + devname + "'"; | |
456 | } | |
457 | udev_unref(udev); | |
458 | return {}; | |
459 | } | |
460 | ||
461 | // **** | |
462 | // NOTE: please keep this implementation in sync with _get_device_id() in | |
463 | // src/ceph-volume/ceph_volume/util/device.py | |
464 | // **** | |
465 | ||
466 | std::string id_vendor, id_model, id_serial, id_serial_short, id_scsi_serial; | |
467 | data = udev_device_get_property_value(dev, "ID_VENDOR"); | |
468 | if (data) { | |
469 | id_vendor = data; | |
470 | } | |
471 | data = udev_device_get_property_value(dev, "ID_MODEL"); | |
472 | if (data) { | |
473 | id_model = data; | |
474 | // sometimes, ID_MODEL is "LVM ..." but ID_MODEL_ENC is correct (but | |
475 | // encoded with \x20 for space). | |
476 | if (id_model.substr(0, 7) == "LVM PV ") { | |
477 | const char *enc = udev_device_get_property_value(dev, "ID_MODEL_ENC"); | |
478 | if (enc) { | |
479 | id_model = _decode_model_enc(enc); | |
480 | } else { | |
481 | // ignore ID_MODEL then | |
482 | id_model.clear(); | |
483 | } | |
7c673cae | 484 | } |
11fdf7f2 TL |
485 | } |
486 | data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT"); | |
487 | if (data) { | |
488 | id_serial_short = data; | |
489 | } | |
490 | data = udev_device_get_property_value(dev, "ID_SCSI_SERIAL"); | |
491 | if (data) { | |
492 | id_scsi_serial = data; | |
493 | } | |
494 | data = udev_device_get_property_value(dev, "ID_SERIAL"); | |
495 | if (data) { | |
496 | id_serial = data; | |
497 | } | |
498 | udev_device_unref(dev); | |
499 | udev_unref(udev); | |
500 | ||
501 | // ID_SERIAL is usually $vendor_$model_$serial, but not always | |
502 | // ID_SERIAL_SHORT is mostly always just the serial | |
503 | // ID_MODEL is sometimes $vendor_$model, but | |
504 | // ID_VENDOR is sometimes $vendor and ID_MODEL just $model and ID_SCSI_SERIAL the real serial number, with ID_SERIAL and ID_SERIAL_SHORT gibberish (ick) | |
505 | std::string device_id; | |
506 | if (id_vendor.size() && id_model.size() && id_scsi_serial.size()) { | |
507 | device_id = id_vendor + '_' + id_model + '_' + id_scsi_serial; | |
508 | } else if (id_model.size() && id_serial_short.size()) { | |
509 | device_id = id_model + '_' + id_serial_short; | |
510 | } else if (id_serial.size()) { | |
511 | device_id = id_serial; | |
512 | if (device_id.substr(0, 4) == "MTFD") { | |
513 | // Micron NVMes hide the vendor | |
514 | device_id = "Micron_" + device_id; | |
515 | } | |
516 | } | |
517 | if (device_id.size()) { | |
518 | std::replace(device_id.begin(), device_id.end(), ' ', '_'); | |
519 | return device_id; | |
520 | } | |
521 | ||
522 | // either udev_device_get_property_value() failed, or succeeded but | |
523 | // returned nothing; trying to read from files. note that the 'vendor' | |
524 | // file rarely contains the actual vendor; it's usually 'ATA'. | |
525 | std::string model, serial; | |
526 | char buf[1024] = {0}; | |
527 | BlkDev blkdev(devname); | |
528 | if (!blkdev.model(buf, sizeof(buf))) { | |
529 | model = buf; | |
530 | } | |
9f95a23c | 531 | if (!blkdev.serial(buf, sizeof(buf))) { |
11fdf7f2 TL |
532 | serial = buf; |
533 | } | |
9f95a23c TL |
534 | if (err) { |
535 | if (model.empty() && serial.empty()) { | |
536 | *err = std::string("fallback method has no model nor serial'"); | |
537 | return {}; | |
538 | } else if (model.empty()) { | |
11fdf7f2 | 539 | *err = std::string("fallback method has serial '") + serial |
9f95a23c TL |
540 | + "' but no model'"; |
541 | return {}; | |
542 | } else if (serial.empty()) { | |
543 | *err = std::string("fallback method has model '") + model | |
544 | + "' but no serial'"; | |
545 | return {}; | |
11fdf7f2 | 546 | } |
11fdf7f2 TL |
547 | } |
548 | ||
549 | device_id = model + "_" + serial; | |
550 | std::replace(device_id.begin(), device_id.end(), ' ', '_'); | |
551 | return device_id; | |
552 | } | |
553 | ||
554 | static std::string get_device_vendor(const std::string& devname) | |
555 | { | |
556 | struct udev_device *dev; | |
557 | static struct udev *udev; | |
558 | const char *data; | |
559 | ||
560 | udev = udev_new(); | |
561 | if (!udev) { | |
562 | return {}; | |
563 | } | |
564 | dev = udev_device_new_from_subsystem_sysname(udev, "block", devname.c_str()); | |
565 | if (!dev) { | |
566 | udev_unref(udev); | |
567 | return {}; | |
568 | } | |
569 | ||
570 | std::string id_vendor, id_model; | |
571 | data = udev_device_get_property_value(dev, "ID_VENDOR"); | |
572 | if (data) { | |
573 | id_vendor = data; | |
574 | } | |
575 | data = udev_device_get_property_value(dev, "ID_MODEL"); | |
576 | if (data) { | |
577 | id_model = data; | |
578 | } | |
579 | udev_device_unref(dev); | |
580 | udev_unref(udev); | |
581 | ||
582 | std::transform(id_vendor.begin(), id_vendor.end(), id_vendor.begin(), | |
583 | ::tolower); | |
584 | std::transform(id_model.begin(), id_model.end(), id_model.begin(), | |
585 | ::tolower); | |
586 | ||
587 | if (id_vendor.size()) { | |
588 | return id_vendor; | |
589 | } | |
590 | if (id_model.size()) { | |
591 | int pos = id_model.find(" "); | |
592 | if (pos > 0) { | |
593 | return id_model.substr(0, pos); | |
594 | } else { | |
595 | return id_model; | |
596 | } | |
597 | } | |
598 | ||
599 | std::string vendor, model; | |
600 | char buf[1024] = {0}; | |
601 | BlkDev blkdev(devname); | |
602 | if (!blkdev.vendor(buf, sizeof(buf))) { | |
603 | vendor = buf; | |
604 | } | |
605 | if (!blkdev.model(buf, sizeof(buf))) { | |
606 | model = buf; | |
607 | } | |
608 | if (vendor.size()) { | |
609 | return vendor; | |
610 | } | |
611 | if (model.size()) { | |
612 | int pos = model.find(" "); | |
613 | if (pos > 0) { | |
614 | return model.substr(0, pos); | |
615 | } else { | |
616 | return model; | |
617 | } | |
618 | } | |
619 | ||
620 | return {}; | |
621 | } | |
622 | ||
623 | static int block_device_run_vendor_nvme( | |
624 | const string& devname, const string& vendor, int timeout, | |
625 | std::string *result) | |
626 | { | |
627 | string device = "/dev/" + devname; | |
628 | ||
629 | SubProcessTimed nvmecli( | |
630 | "sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE, | |
631 | timeout); | |
632 | nvmecli.add_cmd_args( | |
633 | "nvme", | |
634 | vendor.c_str(), | |
635 | "smart-log-add", | |
636 | "--json", | |
637 | device.c_str(), | |
638 | NULL); | |
639 | int ret = nvmecli.spawn(); | |
640 | if (ret != 0) { | |
641 | *result = std::string("error spawning nvme command: ") + nvmecli.err(); | |
642 | return ret; | |
643 | } | |
644 | ||
645 | bufferlist output; | |
646 | ret = output.read_fd(nvmecli.get_stdout(), 100*1024); | |
647 | if (ret < 0) { | |
648 | bufferlist err; | |
649 | err.read_fd(nvmecli.get_stderr(), 100 * 1024); | |
650 | *result = std::string("failed to execute nvme: ") + err.to_str(); | |
7c673cae | 651 | } else { |
11fdf7f2 TL |
652 | ret = 0; |
653 | *result = output.to_str(); | |
654 | } | |
655 | ||
656 | if (nvmecli.join() != 0) { | |
657 | *result = std::string("nvme returned an error: ") + nvmecli.err(); | |
658 | return -EINVAL; | |
7c673cae FG |
659 | } |
660 | ||
11fdf7f2 | 661 | return ret; |
7c673cae FG |
662 | } |
663 | ||
9f95a23c TL |
664 | std::string get_device_path(const std::string& devname, |
665 | std::string *err) | |
666 | { | |
667 | std::set<std::string> links; | |
668 | int r = easy_readdir("/dev/disk/by-path", &links); | |
669 | if (r < 0) { | |
670 | *err = "unable to list contents of /dev/disk/by-path: "s + | |
671 | cpp_strerror(r); | |
672 | return {}; | |
673 | } | |
674 | for (auto& i : links) { | |
675 | char fn[PATH_MAX]; | |
676 | char target[PATH_MAX+1]; | |
677 | snprintf(fn, sizeof(fn), "/dev/disk/by-path/%s", i.c_str()); | |
678 | int r = readlink(fn, target, sizeof(target)); | |
679 | if (r < 0 || r >= (int)sizeof(target)) | |
680 | continue; | |
681 | target[r] = 0; | |
682 | if ((unsigned)r > devname.size() + 1 && | |
683 | strncmp(target + r - devname.size(), devname.c_str(), r) == 0 && | |
684 | target[r - devname.size() - 1] == '/') { | |
685 | return fn; | |
686 | } | |
687 | } | |
688 | *err = "no symlink to "s + devname + " in /dev/disk/by-path"; | |
689 | return {}; | |
690 | } | |
691 | ||
11fdf7f2 TL |
692 | static int block_device_run_smartctl(const string& devname, int timeout, |
693 | std::string *result) | |
7c673cae | 694 | { |
11fdf7f2 TL |
695 | string device = "/dev/" + devname; |
696 | ||
697 | // when using --json, smartctl will report its errors in JSON format to stdout | |
698 | SubProcessTimed smartctl( | |
699 | "sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE, | |
700 | timeout); | |
701 | smartctl.add_cmd_args( | |
702 | "smartctl", | |
f67539c2 TL |
703 | //"-a", // all SMART info |
704 | "-x", // all SMART and non-SMART info | |
9f95a23c | 705 | "--json=o", |
11fdf7f2 TL |
706 | device.c_str(), |
707 | NULL); | |
708 | ||
709 | int ret = smartctl.spawn(); | |
710 | if (ret != 0) { | |
711 | *result = std::string("error spawning smartctl: ") + smartctl.err(); | |
712 | return ret; | |
7c673cae | 713 | } |
11fdf7f2 TL |
714 | |
715 | bufferlist output; | |
716 | ret = output.read_fd(smartctl.get_stdout(), 100*1024); | |
717 | if (ret < 0) { | |
718 | *result = std::string("failed read smartctl output: ") + cpp_strerror(-ret); | |
719 | } else { | |
720 | ret = 0; | |
721 | *result = output.to_str(); | |
722 | } | |
723 | ||
9f95a23c TL |
724 | int joinerr = smartctl.join(); |
725 | // Bit 0: Command line did not parse. | |
726 | // Bit 1: Device open failed, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode (see '-n' option above). | |
727 | // Bit 2: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure (see '-b' option above). | |
728 | // Bit 3: SMART status check returned "DISK FAILING". | |
729 | // Bit 4: We found prefail Attributes <= threshold. | |
730 | // Bit 5: SMART status check returned "DISK OK" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past. | |
731 | // Bit 6: The device error log contains records of errors. | |
732 | // Bit 7: The device self-test log contains records of errors. [ATA only] Failed self-tests outdated by a newer successful extended self-test are ignored. | |
733 | if (joinerr & 3) { | |
734 | *result = "smartctl returned an error ("s + stringify(joinerr) + | |
735 | "): stderr:\n"s + smartctl.err() + "\nstdout:\n"s + *result; | |
7c673cae FG |
736 | return -EINVAL; |
737 | } | |
11fdf7f2 TL |
738 | |
739 | return ret; | |
740 | } | |
741 | ||
9f95a23c TL |
742 | static std::string escape_quotes(const std::string& s) |
743 | { | |
744 | std::string r = s; | |
745 | auto pos = r.find("\""); | |
746 | while (pos != std::string::npos) { | |
747 | r.replace(pos, 1, "\""); | |
748 | pos = r.find("\"", pos + 1); | |
749 | } | |
750 | return r; | |
751 | } | |
752 | ||
11fdf7f2 TL |
753 | int block_device_get_metrics(const string& devname, int timeout, |
754 | json_spirit::mValue *result) | |
755 | { | |
756 | std::string s; | |
757 | ||
758 | // smartctl | |
759 | if (int r = block_device_run_smartctl(devname, timeout, &s); | |
760 | r != 0) { | |
9f95a23c | 761 | string orig = s; |
11fdf7f2 TL |
762 | s = "{\"error\": \"smartctl failed\", \"dev\": \"/dev/"; |
763 | s += devname; | |
764 | s += "\", \"smartctl_error_code\": " + stringify(r); | |
9f95a23c | 765 | s += ", \"smartctl_output\": \"" + escape_quotes(orig); |
11fdf7f2 | 766 | s += + "\"}"; |
9f95a23c TL |
767 | } else if (!json_spirit::read(s, *result)) { |
768 | string orig = s; | |
11fdf7f2 TL |
769 | s = "{\"error\": \"smartctl returned invalid JSON\", \"dev\": \"/dev/"; |
770 | s += devname; | |
9f95a23c TL |
771 | s += "\",\"output\":\""; |
772 | s += escape_quotes(orig); | |
11fdf7f2 TL |
773 | s += "\"}"; |
774 | } | |
775 | if (!json_spirit::read(s, *result)) { | |
7c673cae FG |
776 | return -EINVAL; |
777 | } | |
11fdf7f2 TL |
778 | |
779 | json_spirit::mObject& base = result->get_obj(); | |
780 | string vendor = get_device_vendor(devname); | |
781 | if (vendor.size()) { | |
782 | base["nvme_vendor"] = vendor; | |
783 | s.clear(); | |
784 | json_spirit::mValue nvme_json; | |
785 | if (int r = block_device_run_vendor_nvme(devname, vendor, timeout, &s); | |
786 | r == 0) { | |
787 | if (json_spirit::read(s, nvme_json) != 0) { | |
788 | base["nvme_smart_health_information_add_log"] = nvme_json; | |
789 | } else { | |
790 | base["nvme_smart_health_information_add_log_error"] = "bad json output: " | |
791 | + s; | |
792 | } | |
793 | } else { | |
794 | base["nvme_smart_health_information_add_log_error_code"] = r; | |
795 | base["nvme_smart_health_information_add_log_error"] = s; | |
796 | } | |
797 | } else { | |
798 | base["nvme_vendor"] = "unknown"; | |
799 | } | |
800 | ||
7c673cae FG |
801 | return 0; |
802 | } | |
803 | ||
804 | #elif defined(__APPLE__) | |
805 | #include <sys/disk.h> | |
806 | ||
11fdf7f2 TL |
807 | const char *BlkDev::sysfsdir() const { |
808 | assert(false); // Should never be called on Apple | |
809 | return ""; | |
810 | } | |
811 | ||
812 | int BlkDev::dev(char *dev, size_t max) const | |
813 | { | |
814 | struct stat sb; | |
815 | ||
816 | if (fstat(fd, &sb) < 0) | |
817 | return -errno; | |
818 | ||
819 | snprintf(dev, max, "%" PRIu64, (uint64_t)sb.st_rdev); | |
820 | ||
821 | return 0; | |
822 | } | |
823 | ||
824 | int BlkDev::get_size(int64_t *psize) const | |
7c673cae FG |
825 | { |
826 | unsigned long blocksize = 0; | |
827 | int ret = ::ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize); | |
828 | if (!ret) { | |
829 | unsigned long nblocks; | |
830 | ret = ::ioctl(fd, DKIOCGETBLOCKCOUNT, &nblocks); | |
831 | if (!ret) | |
832 | *psize = (int64_t)nblocks * blocksize; | |
833 | } | |
834 | if (ret < 0) | |
835 | ret = -errno; | |
836 | return ret; | |
837 | } | |
838 | ||
9f95a23c | 839 | int64_t BlkDev::get_int_property(const char* prop) const |
11fdf7f2 TL |
840 | { |
841 | return 0; | |
842 | } | |
843 | ||
844 | bool BlkDev::support_discard() const | |
7c673cae FG |
845 | { |
846 | return false; | |
847 | } | |
848 | ||
11fdf7f2 | 849 | int BlkDev::discard(int64_t offset, int64_t len) const |
7c673cae FG |
850 | { |
851 | return -EOPNOTSUPP; | |
852 | } | |
853 | ||
11fdf7f2 | 854 | bool BlkDev::is_rotational() const |
7c673cae FG |
855 | { |
856 | return false; | |
857 | } | |
858 | ||
11fdf7f2 TL |
859 | int BlkDev::get_numa_node(int *node) const |
860 | { | |
861 | return -1; | |
862 | } | |
863 | ||
864 | int BlkDev::model(char *model, size_t max) const | |
865 | { | |
866 | return -EOPNOTSUPP; | |
867 | } | |
868 | ||
869 | int BlkDev::serial(char *serial, size_t max) const | |
7c673cae FG |
870 | { |
871 | return -EOPNOTSUPP; | |
872 | } | |
11fdf7f2 TL |
873 | |
874 | int BlkDev::partition(char *partition, size_t max) const | |
875 | { | |
876 | return -EOPNOTSUPP; | |
877 | } | |
878 | ||
879 | int BlkDev::wholedisk(char *device, size_t max) const | |
880 | { | |
881 | } | |
882 | ||
883 | ||
884 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
885 | { | |
886 | } | |
887 | ||
888 | void get_raw_devices(const std::string& in, | |
889 | std::set<std::string> *ls) | |
890 | { | |
891 | } | |
892 | ||
893 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
894 | { | |
895 | return -1; | |
896 | } | |
897 | ||
898 | int64_t get_vdo_stat(int fd, const char *property) | |
899 | { | |
900 | return 0; | |
901 | } | |
902 | ||
903 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
904 | { | |
905 | return false; | |
906 | } | |
907 | ||
908 | std::string get_device_id(const std::string& devname, | |
909 | std::string *err) | |
910 | { | |
911 | // FIXME: implement me | |
912 | if (err) { | |
913 | *err = "not implemented"; | |
914 | } | |
915 | return std::string(); | |
916 | } | |
917 | ||
9f95a23c TL |
918 | std::string get_device_path(const std::string& devname, |
919 | std::string *err) | |
920 | { | |
921 | // FIXME: implement me | |
922 | if (err) { | |
923 | *err = "not implemented"; | |
924 | } | |
925 | return std::string(); | |
926 | } | |
927 | ||
7c673cae | 928 | #elif defined(__FreeBSD__) |
7c673cae | 929 | |
11fdf7f2 TL |
930 | const char *BlkDev::sysfsdir() const { |
931 | assert(false); // Should never be called on FreeBSD | |
932 | return ""; | |
933 | } | |
934 | ||
935 | int BlkDev::dev(char *dev, size_t max) const | |
936 | { | |
937 | struct stat sb; | |
938 | ||
939 | if (fstat(fd, &sb) < 0) | |
940 | return -errno; | |
941 | ||
942 | snprintf(dev, max, "%" PRIu64, (uint64_t)sb.st_rdev); | |
943 | ||
944 | return 0; | |
945 | } | |
946 | ||
947 | int BlkDev::get_size(int64_t *psize) const | |
7c673cae FG |
948 | { |
949 | int ret = ::ioctl(fd, DIOCGMEDIASIZE, psize); | |
950 | if (ret < 0) | |
951 | ret = -errno; | |
952 | return ret; | |
953 | } | |
954 | ||
9f95a23c | 955 | int64_t BlkDev::get_int_property(const char* prop) const |
7c673cae | 956 | { |
11fdf7f2 TL |
957 | return 0; |
958 | } | |
959 | ||
960 | bool BlkDev::support_discard() const | |
961 | { | |
962 | #ifdef FREEBSD_WITH_TRIM | |
963 | // there is no point to claim support of discard, but | |
964 | // unable to do so. | |
965 | struct diocgattr_arg arg; | |
966 | ||
967 | strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name)); | |
968 | arg.len = sizeof(arg.value.i); | |
969 | if (ioctl(fd, DIOCGATTR, &arg) == 0) { | |
970 | return (arg.value.i != 0); | |
971 | } else { | |
972 | return false; | |
973 | } | |
974 | #endif | |
7c673cae FG |
975 | return false; |
976 | } | |
977 | ||
11fdf7f2 | 978 | int BlkDev::discard(int64_t offset, int64_t len) const |
7c673cae FG |
979 | { |
980 | return -EOPNOTSUPP; | |
981 | } | |
982 | ||
11fdf7f2 TL |
983 | bool BlkDev::is_rotational() const |
984 | { | |
985 | #if __FreeBSD_version >= 1200049 | |
986 | struct diocgattr_arg arg; | |
987 | ||
988 | strlcpy(arg.name, "GEOM::rotation_rate", sizeof(arg.name)); | |
989 | arg.len = sizeof(arg.value.u16); | |
990 | ||
991 | int ioctl_ret = ioctl(fd, DIOCGATTR, &arg); | |
992 | bool ret; | |
993 | if (ioctl_ret < 0 || arg.value.u16 == DISK_RR_UNKNOWN) | |
994 | // DISK_RR_UNKNOWN usually indicates an old drive, which is usually spinny | |
995 | ret = true; | |
996 | else if (arg.value.u16 == DISK_RR_NON_ROTATING) | |
997 | ret = false; | |
998 | else if (arg.value.u16 >= DISK_RR_MIN && arg.value.u16 <= DISK_RR_MAX) | |
999 | ret = true; | |
1000 | else | |
1001 | ret = true; // Invalid value. Probably spinny? | |
1002 | ||
1003 | return ret; | |
1004 | #else | |
1005 | return true; // When in doubt, it's probably spinny | |
1006 | #endif | |
1007 | } | |
1008 | ||
1009 | int BlkDev::get_numa_node(int *node) const | |
1010 | { | |
9f95a23c | 1011 | int numa = get_int_property("device/device/numa_node"); |
11fdf7f2 TL |
1012 | if (numa < 0) |
1013 | return -1; | |
1014 | *node = numa; | |
1015 | return 0; | |
1016 | } | |
1017 | ||
1018 | int BlkDev::model(char *model, size_t max) const | |
1019 | { | |
1020 | struct diocgattr_arg arg; | |
1021 | ||
1022 | strlcpy(arg.name, "GEOM::descr", sizeof(arg.name)); | |
1023 | arg.len = sizeof(arg.value.str); | |
1024 | if (ioctl(fd, DIOCGATTR, &arg) < 0) { | |
1025 | return -errno; | |
1026 | } | |
1027 | ||
1028 | // The GEOM description is of the form "vendor product" for SCSI disks | |
1029 | // and "ATA device_model" for ATA disks. Some vendors choose to put the | |
1030 | // vendor name in device_model, and some don't. Strip the first bit. | |
1031 | char *p = arg.value.str; | |
1032 | if (p == NULL || *p == '\0') { | |
1033 | *model = '\0'; | |
1034 | } else { | |
1035 | (void) strsep(&p, " "); | |
1036 | snprintf(model, max, "%s", p); | |
1037 | } | |
1038 | ||
1039 | return 0; | |
1040 | } | |
1041 | ||
1042 | int BlkDev::serial(char *serial, size_t max) const | |
1043 | { | |
1044 | char ident[DISK_IDENT_SIZE]; | |
1045 | ||
1046 | if (ioctl(fd, DIOCGIDENT, ident) < 0) | |
1047 | return -errno; | |
1048 | ||
1049 | snprintf(serial, max, "%s", ident); | |
1050 | ||
1051 | return 0; | |
1052 | } | |
1053 | ||
1054 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
1055 | { | |
1056 | } | |
1057 | ||
1058 | void get_raw_devices(const std::string& in, | |
1059 | std::set<std::string> *ls) | |
1060 | { | |
1061 | } | |
1062 | ||
1063 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
1064 | { | |
1065 | return -1; | |
1066 | } | |
1067 | ||
1068 | int64_t get_vdo_stat(int fd, const char *property) | |
1069 | { | |
1070 | return 0; | |
1071 | } | |
1072 | ||
1073 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
7c673cae FG |
1074 | { |
1075 | return false; | |
1076 | } | |
1077 | ||
11fdf7f2 TL |
1078 | std::string get_device_id(const std::string& devname, |
1079 | std::string *err) | |
7c673cae | 1080 | { |
11fdf7f2 TL |
1081 | // FIXME: implement me for freebsd |
1082 | if (err) { | |
1083 | *err = "not implemented for FreeBSD"; | |
1084 | } | |
1085 | return std::string(); | |
1086 | } | |
1087 | ||
9f95a23c TL |
1088 | std::string get_device_path(const std::string& devname, |
1089 | std::string *err) | |
1090 | { | |
1091 | // FIXME: implement me for freebsd | |
1092 | if (err) { | |
1093 | *err = "not implemented for FreeBSD"; | |
1094 | } | |
1095 | return std::string(); | |
1096 | } | |
1097 | ||
11fdf7f2 TL |
1098 | int block_device_run_smartctl(const char *device, int timeout, |
1099 | std::string *result) | |
1100 | { | |
1101 | // FIXME: implement me for freebsd | |
1102 | return -EOPNOTSUPP; | |
7c673cae | 1103 | } |
11fdf7f2 TL |
1104 | |
1105 | int block_device_get_metrics(const string& devname, int timeout, | |
1106 | json_spirit::mValue *result) | |
1107 | { | |
1108 | // FIXME: implement me for freebsd | |
1109 | return -EOPNOTSUPP; | |
1110 | } | |
1111 | ||
1112 | int block_device_run_nvme(const char *device, const char *vendor, int timeout, | |
1113 | std::string *result) | |
7c673cae FG |
1114 | { |
1115 | return -EOPNOTSUPP; | |
1116 | } | |
11fdf7f2 TL |
1117 | |
1118 | static int block_device_devname(int fd, char *devname, size_t max) | |
1119 | { | |
1120 | struct fiodgname_arg arg; | |
1121 | ||
1122 | arg.buf = devname; | |
1123 | arg.len = max; | |
1124 | if (ioctl(fd, FIODGNAME, &arg) < 0) | |
1125 | return -errno; | |
1126 | return 0; | |
1127 | } | |
1128 | ||
1129 | int BlkDev::partition(char *partition, size_t max) const | |
1130 | { | |
1131 | char devname[PATH_MAX]; | |
1132 | ||
1133 | if (block_device_devname(fd, devname, sizeof(devname)) < 0) | |
1134 | return -errno; | |
1135 | snprintf(partition, max, "/dev/%s", devname); | |
1136 | return 0; | |
1137 | } | |
1138 | ||
1139 | int BlkDev::wholedisk(char *wd, size_t max) const | |
1140 | { | |
1141 | char devname[PATH_MAX]; | |
1142 | ||
1143 | if (block_device_devname(fd, devname, sizeof(devname)) < 0) | |
1144 | return -errno; | |
1145 | ||
1146 | size_t first_digit = strcspn(devname, "0123456789"); | |
1147 | // first_digit now indexes the first digit or null character of devname | |
1148 | size_t next_nondigit = strspn(&devname[first_digit], "0123456789"); | |
1149 | next_nondigit += first_digit; | |
1150 | // next_nondigit now indexes the first alphabetic or null character after the | |
1151 | // unit number | |
1152 | strlcpy(wd, devname, next_nondigit + 1); | |
1153 | return 0; | |
1154 | } | |
1155 | ||
7c673cae | 1156 | #else |
11fdf7f2 TL |
1157 | |
1158 | const char *BlkDev::sysfsdir() const { | |
1159 | assert(false); // Should never be called on non-Linux | |
1160 | return ""; | |
1161 | } | |
1162 | ||
1163 | int BlkDev::dev(char *dev, size_t max) const | |
1164 | { | |
1165 | return -EOPNOTSUPP; | |
1166 | } | |
1167 | ||
1168 | int BlkDev::get_size(int64_t *psize) const | |
1169 | { | |
1170 | return -EOPNOTSUPP; | |
1171 | } | |
1172 | ||
1173 | bool BlkDev::support_discard() const | |
1174 | { | |
1175 | return false; | |
1176 | } | |
1177 | ||
1178 | int BlkDev::discard(int fd, int64_t offset, int64_t len) const | |
7c673cae FG |
1179 | { |
1180 | return -EOPNOTSUPP; | |
1181 | } | |
1182 | ||
11fdf7f2 | 1183 | bool BlkDev::is_rotational(const char *devname) const |
7c673cae FG |
1184 | { |
1185 | return false; | |
1186 | } | |
1187 | ||
11fdf7f2 TL |
1188 | int BlkDev::model(char *model, size_t max) const |
1189 | { | |
1190 | return -EOPNOTSUPP; | |
1191 | } | |
1192 | ||
1193 | int BlkDev::serial(char *serial, size_t max) const | |
1194 | { | |
1195 | return -EOPNOTSUPP; | |
1196 | } | |
1197 | ||
1198 | int BlkDev::partition(char *partition, size_t max) const | |
7c673cae FG |
1199 | { |
1200 | return -EOPNOTSUPP; | |
1201 | } | |
1202 | ||
11fdf7f2 TL |
1203 | int BlkDev::wholedisk(char *wd, size_t max) const |
1204 | { | |
1205 | return -EOPNOTSUPP; | |
1206 | } | |
1207 | ||
1208 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
1209 | { | |
1210 | } | |
1211 | ||
1212 | void get_raw_devices(const std::string& in, | |
1213 | std::set<std::string> *ls) | |
1214 | { | |
1215 | } | |
1216 | ||
1217 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
1218 | { | |
1219 | return -1; | |
1220 | } | |
1221 | ||
1222 | int64_t get_vdo_stat(int fd, const char *property) | |
1223 | { | |
1224 | return 0; | |
1225 | } | |
1226 | ||
1227 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
7c673cae FG |
1228 | { |
1229 | return false; | |
1230 | } | |
1231 | ||
11fdf7f2 TL |
1232 | std::string get_device_id(const std::string& devname, |
1233 | std::string *err) | |
1234 | { | |
1235 | // not implemented | |
1236 | if (err) { | |
1237 | *err = "not implemented"; | |
1238 | } | |
1239 | return std::string(); | |
1240 | } | |
1241 | ||
9f95a23c TL |
1242 | std::string get_device_path(const std::string& devname, |
1243 | std::string *err) | |
1244 | { | |
1245 | // not implemented | |
1246 | if (err) { | |
1247 | *err = "not implemented"; | |
1248 | } | |
1249 | return std::string(); | |
1250 | } | |
1251 | ||
11fdf7f2 TL |
1252 | int block_device_run_smartctl(const char *device, int timeout, |
1253 | std::string *result) | |
7c673cae FG |
1254 | { |
1255 | return -EOPNOTSUPP; | |
1256 | } | |
1257 | ||
11fdf7f2 TL |
1258 | int block_device_get_metrics(const string& devname, int timeout, |
1259 | json_spirit::mValue *result) | |
7c673cae FG |
1260 | { |
1261 | return -EOPNOTSUPP; | |
1262 | } | |
11fdf7f2 TL |
1263 | |
1264 | int block_device_run_nvme(const char *device, const char *vendor, int timeout, | |
1265 | std::string *result) | |
1266 | { | |
1267 | return -EOPNOTSUPP; | |
1268 | } | |
1269 | ||
7c673cae | 1270 | #endif |
9f95a23c TL |
1271 | |
1272 | ||
1273 | ||
1274 | void get_device_metadata( | |
1275 | const std::set<std::string>& devnames, | |
1276 | std::map<std::string,std::string> *pm, | |
1277 | std::map<std::string,std::string> *errs) | |
1278 | { | |
1279 | (*pm)["devices"] = stringify(devnames); | |
1280 | string &devids = (*pm)["device_ids"]; | |
1281 | string &devpaths = (*pm)["device_paths"]; | |
1282 | for (auto& dev : devnames) { | |
1283 | string err; | |
1284 | string id = get_device_id(dev, &err); | |
1285 | if (id.size()) { | |
1286 | if (!devids.empty()) { | |
1287 | devids += ","; | |
1288 | } | |
1289 | devids += dev + "=" + id; | |
1290 | } else { | |
1291 | (*errs)[dev] = " no unique device id for "s + dev + ": " + err; | |
1292 | } | |
1293 | string path = get_device_path(dev, &err); | |
1294 | if (path.size()) { | |
1295 | if (!devpaths.empty()) { | |
1296 | devpaths += ","; | |
1297 | } | |
1298 | devpaths += dev + "=" + path; | |
1299 | } else { | |
1300 | (*errs)[dev] + " no unique device path for "s + dev + ": " + err; | |
1301 | } | |
1302 | } | |
1303 | } |