]>
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 | ||
20effc67 TL |
220 | int BlkDev::get_optimal_io_size() const |
221 | { | |
222 | return get_int_property("queue/optimal_io_size"); | |
223 | } | |
224 | ||
11fdf7f2 | 225 | bool BlkDev::is_rotational() const |
7c673cae | 226 | { |
9f95a23c | 227 | return get_int_property("queue/rotational") > 0; |
7c673cae FG |
228 | } |
229 | ||
11fdf7f2 | 230 | int BlkDev::get_numa_node(int *node) const |
7c673cae | 231 | { |
9f95a23c | 232 | int numa = get_int_property("device/device/numa_node"); |
11fdf7f2 TL |
233 | if (numa < 0) |
234 | return -1; | |
235 | *node = numa; | |
236 | return 0; | |
7c673cae FG |
237 | } |
238 | ||
11fdf7f2 | 239 | int BlkDev::dev(char *dev, size_t max) const |
7c673cae | 240 | { |
9f95a23c | 241 | return get_string_property("dev", dev, max); |
11fdf7f2 | 242 | } |
7c673cae | 243 | |
11fdf7f2 TL |
244 | int BlkDev::vendor(char *vendor, size_t max) const |
245 | { | |
9f95a23c | 246 | return get_string_property("device/device/vendor", vendor, max); |
11fdf7f2 | 247 | } |
7c673cae | 248 | |
11fdf7f2 TL |
249 | int BlkDev::model(char *model, size_t max) const |
250 | { | |
9f95a23c | 251 | return get_string_property("device/model", model, max); |
11fdf7f2 TL |
252 | } |
253 | ||
254 | int BlkDev::serial(char *serial, size_t max) const | |
255 | { | |
9f95a23c | 256 | return get_string_property("device/serial", serial, max); |
11fdf7f2 TL |
257 | } |
258 | ||
259 | int BlkDev::partition(char *partition, size_t max) const | |
260 | { | |
261 | dev_t id; | |
262 | int r = get_devid(&id); | |
263 | if (r < 0) | |
264 | return -EINVAL; // hrm. | |
265 | ||
266 | char *t = blkid_devno_to_devname(id); | |
267 | if (!t) { | |
268 | return -EINVAL; | |
269 | } | |
270 | strncpy(partition, t, max); | |
271 | free(t); | |
272 | return 0; | |
273 | } | |
274 | ||
275 | int BlkDev::wholedisk(char *device, size_t max) const | |
276 | { | |
277 | dev_t id; | |
278 | int r = get_devid(&id); | |
279 | if (r < 0) | |
280 | return -EINVAL; // hrm. | |
281 | ||
282 | r = blkid_devno_to_wholedisk(id, device, max, nullptr); | |
283 | if (r < 0) { | |
7c673cae | 284 | return -EINVAL; |
11fdf7f2 TL |
285 | } |
286 | return 0; | |
287 | } | |
288 | ||
289 | static int easy_readdir(const std::string& dir, std::set<std::string> *out) | |
290 | { | |
291 | DIR *h = ::opendir(dir.c_str()); | |
292 | if (!h) { | |
293 | return -errno; | |
294 | } | |
295 | struct dirent *de = nullptr; | |
296 | while ((de = ::readdir(h))) { | |
297 | if (strcmp(de->d_name, ".") == 0 || | |
298 | strcmp(de->d_name, "..") == 0) { | |
299 | continue; | |
300 | } | |
301 | out->insert(de->d_name); | |
302 | } | |
303 | closedir(h); | |
304 | return 0; | |
305 | } | |
7c673cae | 306 | |
11fdf7f2 TL |
307 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) |
308 | { | |
309 | std::string p = std::string("/sys/block/") + dev + "/slaves"; | |
310 | std::set<std::string> parents; | |
311 | easy_readdir(p, &parents); | |
312 | for (auto& d : parents) { | |
313 | ls->insert(d); | |
314 | // recurse in case it is dm-on-dm | |
315 | if (d.find("dm-") == 0) { | |
316 | get_dm_parents(d, ls); | |
317 | } | |
318 | } | |
319 | } | |
320 | ||
321 | void get_raw_devices(const std::string& in, | |
322 | std::set<std::string> *ls) | |
323 | { | |
324 | if (in.substr(0, 3) == "dm-") { | |
325 | std::set<std::string> o; | |
326 | get_dm_parents(in, &o); | |
327 | for (auto& d : o) { | |
328 | get_raw_devices(d, ls); | |
329 | } | |
330 | } else { | |
331 | BlkDev d(in); | |
332 | std::string wholedisk; | |
333 | if (d.wholedisk(&wholedisk) == 0) { | |
334 | ls->insert(wholedisk); | |
7c673cae | 335 | } else { |
11fdf7f2 TL |
336 | ls->insert(in); |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | int _get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
342 | { | |
343 | int vdo_fd = -1; | |
344 | ||
345 | // we need to go from the raw devname (e.g., dm-4) to the VDO volume name. | |
346 | // currently the best way seems to be to look at /dev/mapper/* ... | |
347 | std::string expect = std::string("../") + devname; // expected symlink target | |
348 | DIR *dir = ::opendir("/dev/mapper"); | |
349 | if (!dir) { | |
350 | return -1; | |
351 | } | |
352 | struct dirent *de = nullptr; | |
353 | while ((de = ::readdir(dir))) { | |
354 | if (de->d_name[0] == '.') | |
355 | continue; | |
356 | char fn[4096], target[4096]; | |
357 | snprintf(fn, sizeof(fn), "/dev/mapper/%s", de->d_name); | |
358 | int r = readlink(fn, target, sizeof(target)); | |
359 | if (r < 0 || r >= (int)sizeof(target)) | |
360 | continue; | |
361 | target[r] = 0; | |
362 | if (expect == target) { | |
363 | snprintf(fn, sizeof(fn), "/sys/kvdo/%s/statistics", de->d_name); | |
364 | vdo_fd = ::open(fn, O_RDONLY|O_CLOEXEC); //DIRECTORY); | |
365 | if (vdo_fd >= 0) { | |
366 | *vdo_name = de->d_name; | |
367 | break; | |
368 | } | |
369 | } | |
370 | } | |
371 | closedir(dir); | |
372 | return vdo_fd; | |
373 | } | |
374 | ||
375 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
376 | { | |
377 | std::set<std::string> devs = { devname }; | |
378 | while (!devs.empty()) { | |
379 | std::string dev = *devs.begin(); | |
380 | devs.erase(devs.begin()); | |
381 | int fd = _get_vdo_stats_handle(dev.c_str(), vdo_name); | |
382 | if (fd >= 0) { | |
383 | // yay, it's vdo | |
384 | return fd; | |
385 | } | |
386 | // ok, see if there are constituent devices | |
387 | if (dev.find("dm-") == 0) { | |
388 | get_dm_parents(dev, &devs); | |
389 | } | |
390 | } | |
391 | return -1; | |
392 | } | |
393 | ||
394 | int64_t get_vdo_stat(int vdo_fd, const char *property) | |
395 | { | |
396 | int64_t ret = 0; | |
397 | int fd = ::openat(vdo_fd, property, O_RDONLY|O_CLOEXEC); | |
398 | if (fd < 0) { | |
399 | return 0; | |
400 | } | |
401 | char buf[1024]; | |
402 | int r = ::read(fd, buf, sizeof(buf) - 1); | |
403 | if (r > 0) { | |
404 | buf[r] = 0; | |
405 | ret = atoll(buf); | |
406 | } | |
407 | TEMP_FAILURE_RETRY(::close(fd)); | |
408 | return ret; | |
409 | } | |
410 | ||
411 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
412 | { | |
413 | int64_t block_size = get_vdo_stat(fd, "block_size"); | |
414 | int64_t physical_blocks = get_vdo_stat(fd, "physical_blocks"); | |
415 | int64_t overhead_blocks_used = get_vdo_stat(fd, "overhead_blocks_used"); | |
416 | int64_t data_blocks_used = get_vdo_stat(fd, "data_blocks_used"); | |
417 | if (!block_size | |
418 | || !physical_blocks | |
419 | || !overhead_blocks_used | |
420 | || !data_blocks_used) { | |
421 | return false; | |
422 | } | |
423 | int64_t avail_blocks = | |
424 | physical_blocks - overhead_blocks_used - data_blocks_used; | |
425 | *total = block_size * physical_blocks; | |
426 | *avail = block_size * avail_blocks; | |
427 | return true; | |
428 | } | |
429 | ||
430 | std::string _decode_model_enc(const std::string& in) | |
431 | { | |
432 | auto v = boost::replace_all_copy(in, "\\x20", " "); | |
433 | if (auto found = v.find_last_not_of(" "); found != v.npos) { | |
434 | v.erase(found + 1); | |
435 | } | |
436 | std::replace(v.begin(), v.end(), ' ', '_'); | |
20effc67 TL |
437 | |
438 | // remove "__", which seems to come up on by ubuntu box for some reason. | |
439 | while (true) { | |
440 | auto p = v.find("__"); | |
441 | if (p == std::string::npos) break; | |
442 | v.replace(p, 2, "_"); | |
443 | } | |
444 | ||
11fdf7f2 TL |
445 | return v; |
446 | } | |
447 | ||
448 | // trying to use udev first, and if it doesn't work, we fall back to | |
449 | // reading /sys/block/$devname/device/(vendor/model/serial). | |
450 | std::string get_device_id(const std::string& devname, | |
451 | std::string *err) | |
452 | { | |
453 | struct udev_device *dev; | |
454 | static struct udev *udev; | |
455 | const char *data; | |
456 | ||
457 | udev = udev_new(); | |
458 | if (!udev) { | |
459 | if (err) { | |
460 | *err = "udev_new failed"; | |
461 | } | |
462 | return {}; | |
463 | } | |
464 | dev = udev_device_new_from_subsystem_sysname(udev, "block", devname.c_str()); | |
465 | if (!dev) { | |
466 | if (err) { | |
467 | *err = std::string("udev_device_new_from_subsystem_sysname failed on '") | |
468 | + devname + "'"; | |
469 | } | |
470 | udev_unref(udev); | |
471 | return {}; | |
472 | } | |
473 | ||
474 | // **** | |
475 | // NOTE: please keep this implementation in sync with _get_device_id() in | |
476 | // src/ceph-volume/ceph_volume/util/device.py | |
477 | // **** | |
478 | ||
479 | std::string id_vendor, id_model, id_serial, id_serial_short, id_scsi_serial; | |
480 | data = udev_device_get_property_value(dev, "ID_VENDOR"); | |
481 | if (data) { | |
482 | id_vendor = data; | |
483 | } | |
484 | data = udev_device_get_property_value(dev, "ID_MODEL"); | |
485 | if (data) { | |
486 | id_model = data; | |
487 | // sometimes, ID_MODEL is "LVM ..." but ID_MODEL_ENC is correct (but | |
488 | // encoded with \x20 for space). | |
489 | if (id_model.substr(0, 7) == "LVM PV ") { | |
490 | const char *enc = udev_device_get_property_value(dev, "ID_MODEL_ENC"); | |
491 | if (enc) { | |
492 | id_model = _decode_model_enc(enc); | |
493 | } else { | |
494 | // ignore ID_MODEL then | |
495 | id_model.clear(); | |
496 | } | |
7c673cae | 497 | } |
11fdf7f2 TL |
498 | } |
499 | data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT"); | |
500 | if (data) { | |
501 | id_serial_short = data; | |
502 | } | |
503 | data = udev_device_get_property_value(dev, "ID_SCSI_SERIAL"); | |
504 | if (data) { | |
505 | id_scsi_serial = data; | |
506 | } | |
507 | data = udev_device_get_property_value(dev, "ID_SERIAL"); | |
508 | if (data) { | |
509 | id_serial = data; | |
510 | } | |
511 | udev_device_unref(dev); | |
512 | udev_unref(udev); | |
513 | ||
514 | // ID_SERIAL is usually $vendor_$model_$serial, but not always | |
515 | // ID_SERIAL_SHORT is mostly always just the serial | |
516 | // ID_MODEL is sometimes $vendor_$model, but | |
517 | // 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) | |
518 | std::string device_id; | |
519 | if (id_vendor.size() && id_model.size() && id_scsi_serial.size()) { | |
520 | device_id = id_vendor + '_' + id_model + '_' + id_scsi_serial; | |
521 | } else if (id_model.size() && id_serial_short.size()) { | |
522 | device_id = id_model + '_' + id_serial_short; | |
523 | } else if (id_serial.size()) { | |
524 | device_id = id_serial; | |
525 | if (device_id.substr(0, 4) == "MTFD") { | |
526 | // Micron NVMes hide the vendor | |
527 | device_id = "Micron_" + device_id; | |
528 | } | |
529 | } | |
530 | if (device_id.size()) { | |
531 | std::replace(device_id.begin(), device_id.end(), ' ', '_'); | |
532 | return device_id; | |
533 | } | |
534 | ||
535 | // either udev_device_get_property_value() failed, or succeeded but | |
536 | // returned nothing; trying to read from files. note that the 'vendor' | |
537 | // file rarely contains the actual vendor; it's usually 'ATA'. | |
538 | std::string model, serial; | |
539 | char buf[1024] = {0}; | |
540 | BlkDev blkdev(devname); | |
541 | if (!blkdev.model(buf, sizeof(buf))) { | |
542 | model = buf; | |
543 | } | |
9f95a23c | 544 | if (!blkdev.serial(buf, sizeof(buf))) { |
11fdf7f2 TL |
545 | serial = buf; |
546 | } | |
9f95a23c TL |
547 | if (err) { |
548 | if (model.empty() && serial.empty()) { | |
20effc67 | 549 | *err = std::string("fallback method has no model nor serial"); |
9f95a23c TL |
550 | return {}; |
551 | } else if (model.empty()) { | |
11fdf7f2 | 552 | *err = std::string("fallback method has serial '") + serial |
9f95a23c TL |
553 | + "' but no model'"; |
554 | return {}; | |
555 | } else if (serial.empty()) { | |
556 | *err = std::string("fallback method has model '") + model | |
557 | + "' but no serial'"; | |
558 | return {}; | |
11fdf7f2 | 559 | } |
11fdf7f2 TL |
560 | } |
561 | ||
562 | device_id = model + "_" + serial; | |
563 | std::replace(device_id.begin(), device_id.end(), ' ', '_'); | |
564 | return device_id; | |
565 | } | |
566 | ||
567 | static std::string get_device_vendor(const std::string& devname) | |
568 | { | |
569 | struct udev_device *dev; | |
570 | static struct udev *udev; | |
571 | const char *data; | |
572 | ||
573 | udev = udev_new(); | |
574 | if (!udev) { | |
575 | return {}; | |
576 | } | |
577 | dev = udev_device_new_from_subsystem_sysname(udev, "block", devname.c_str()); | |
578 | if (!dev) { | |
579 | udev_unref(udev); | |
580 | return {}; | |
581 | } | |
582 | ||
583 | std::string id_vendor, id_model; | |
584 | data = udev_device_get_property_value(dev, "ID_VENDOR"); | |
585 | if (data) { | |
586 | id_vendor = data; | |
587 | } | |
588 | data = udev_device_get_property_value(dev, "ID_MODEL"); | |
589 | if (data) { | |
590 | id_model = data; | |
591 | } | |
592 | udev_device_unref(dev); | |
593 | udev_unref(udev); | |
594 | ||
595 | std::transform(id_vendor.begin(), id_vendor.end(), id_vendor.begin(), | |
596 | ::tolower); | |
597 | std::transform(id_model.begin(), id_model.end(), id_model.begin(), | |
598 | ::tolower); | |
599 | ||
600 | if (id_vendor.size()) { | |
601 | return id_vendor; | |
602 | } | |
603 | if (id_model.size()) { | |
604 | int pos = id_model.find(" "); | |
605 | if (pos > 0) { | |
606 | return id_model.substr(0, pos); | |
607 | } else { | |
608 | return id_model; | |
609 | } | |
610 | } | |
611 | ||
612 | std::string vendor, model; | |
613 | char buf[1024] = {0}; | |
614 | BlkDev blkdev(devname); | |
615 | if (!blkdev.vendor(buf, sizeof(buf))) { | |
616 | vendor = buf; | |
617 | } | |
618 | if (!blkdev.model(buf, sizeof(buf))) { | |
619 | model = buf; | |
620 | } | |
621 | if (vendor.size()) { | |
622 | return vendor; | |
623 | } | |
624 | if (model.size()) { | |
625 | int pos = model.find(" "); | |
626 | if (pos > 0) { | |
627 | return model.substr(0, pos); | |
628 | } else { | |
629 | return model; | |
630 | } | |
631 | } | |
632 | ||
633 | return {}; | |
634 | } | |
635 | ||
636 | static int block_device_run_vendor_nvme( | |
637 | const string& devname, const string& vendor, int timeout, | |
638 | std::string *result) | |
639 | { | |
640 | string device = "/dev/" + devname; | |
641 | ||
642 | SubProcessTimed nvmecli( | |
643 | "sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE, | |
644 | timeout); | |
645 | nvmecli.add_cmd_args( | |
646 | "nvme", | |
647 | vendor.c_str(), | |
648 | "smart-log-add", | |
649 | "--json", | |
650 | device.c_str(), | |
651 | NULL); | |
652 | int ret = nvmecli.spawn(); | |
653 | if (ret != 0) { | |
654 | *result = std::string("error spawning nvme command: ") + nvmecli.err(); | |
655 | return ret; | |
656 | } | |
657 | ||
658 | bufferlist output; | |
659 | ret = output.read_fd(nvmecli.get_stdout(), 100*1024); | |
660 | if (ret < 0) { | |
661 | bufferlist err; | |
662 | err.read_fd(nvmecli.get_stderr(), 100 * 1024); | |
663 | *result = std::string("failed to execute nvme: ") + err.to_str(); | |
7c673cae | 664 | } else { |
11fdf7f2 TL |
665 | ret = 0; |
666 | *result = output.to_str(); | |
667 | } | |
668 | ||
669 | if (nvmecli.join() != 0) { | |
670 | *result = std::string("nvme returned an error: ") + nvmecli.err(); | |
671 | return -EINVAL; | |
7c673cae FG |
672 | } |
673 | ||
11fdf7f2 | 674 | return ret; |
7c673cae FG |
675 | } |
676 | ||
9f95a23c TL |
677 | std::string get_device_path(const std::string& devname, |
678 | std::string *err) | |
679 | { | |
680 | std::set<std::string> links; | |
681 | int r = easy_readdir("/dev/disk/by-path", &links); | |
682 | if (r < 0) { | |
683 | *err = "unable to list contents of /dev/disk/by-path: "s + | |
684 | cpp_strerror(r); | |
685 | return {}; | |
686 | } | |
687 | for (auto& i : links) { | |
688 | char fn[PATH_MAX]; | |
689 | char target[PATH_MAX+1]; | |
690 | snprintf(fn, sizeof(fn), "/dev/disk/by-path/%s", i.c_str()); | |
691 | int r = readlink(fn, target, sizeof(target)); | |
692 | if (r < 0 || r >= (int)sizeof(target)) | |
693 | continue; | |
694 | target[r] = 0; | |
695 | if ((unsigned)r > devname.size() + 1 && | |
696 | strncmp(target + r - devname.size(), devname.c_str(), r) == 0 && | |
697 | target[r - devname.size() - 1] == '/') { | |
698 | return fn; | |
699 | } | |
700 | } | |
701 | *err = "no symlink to "s + devname + " in /dev/disk/by-path"; | |
702 | return {}; | |
703 | } | |
704 | ||
11fdf7f2 TL |
705 | static int block_device_run_smartctl(const string& devname, int timeout, |
706 | std::string *result) | |
7c673cae | 707 | { |
11fdf7f2 TL |
708 | string device = "/dev/" + devname; |
709 | ||
710 | // when using --json, smartctl will report its errors in JSON format to stdout | |
711 | SubProcessTimed smartctl( | |
712 | "sudo", SubProcess::CLOSE, SubProcess::PIPE, SubProcess::CLOSE, | |
713 | timeout); | |
714 | smartctl.add_cmd_args( | |
715 | "smartctl", | |
f67539c2 TL |
716 | //"-a", // all SMART info |
717 | "-x", // all SMART and non-SMART info | |
9f95a23c | 718 | "--json=o", |
11fdf7f2 TL |
719 | device.c_str(), |
720 | NULL); | |
721 | ||
722 | int ret = smartctl.spawn(); | |
723 | if (ret != 0) { | |
724 | *result = std::string("error spawning smartctl: ") + smartctl.err(); | |
725 | return ret; | |
7c673cae | 726 | } |
11fdf7f2 TL |
727 | |
728 | bufferlist output; | |
729 | ret = output.read_fd(smartctl.get_stdout(), 100*1024); | |
730 | if (ret < 0) { | |
731 | *result = std::string("failed read smartctl output: ") + cpp_strerror(-ret); | |
732 | } else { | |
733 | ret = 0; | |
734 | *result = output.to_str(); | |
735 | } | |
736 | ||
9f95a23c TL |
737 | int joinerr = smartctl.join(); |
738 | // Bit 0: Command line did not parse. | |
739 | // 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). | |
740 | // 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). | |
741 | // Bit 3: SMART status check returned "DISK FAILING". | |
742 | // Bit 4: We found prefail Attributes <= threshold. | |
743 | // 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. | |
744 | // Bit 6: The device error log contains records of errors. | |
745 | // 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. | |
746 | if (joinerr & 3) { | |
747 | *result = "smartctl returned an error ("s + stringify(joinerr) + | |
748 | "): stderr:\n"s + smartctl.err() + "\nstdout:\n"s + *result; | |
7c673cae FG |
749 | return -EINVAL; |
750 | } | |
11fdf7f2 TL |
751 | |
752 | return ret; | |
753 | } | |
754 | ||
9f95a23c TL |
755 | static std::string escape_quotes(const std::string& s) |
756 | { | |
757 | std::string r = s; | |
758 | auto pos = r.find("\""); | |
759 | while (pos != std::string::npos) { | |
760 | r.replace(pos, 1, "\""); | |
761 | pos = r.find("\"", pos + 1); | |
762 | } | |
763 | return r; | |
764 | } | |
765 | ||
11fdf7f2 TL |
766 | int block_device_get_metrics(const string& devname, int timeout, |
767 | json_spirit::mValue *result) | |
768 | { | |
769 | std::string s; | |
770 | ||
771 | // smartctl | |
772 | if (int r = block_device_run_smartctl(devname, timeout, &s); | |
773 | r != 0) { | |
9f95a23c | 774 | string orig = s; |
11fdf7f2 TL |
775 | s = "{\"error\": \"smartctl failed\", \"dev\": \"/dev/"; |
776 | s += devname; | |
777 | s += "\", \"smartctl_error_code\": " + stringify(r); | |
9f95a23c | 778 | s += ", \"smartctl_output\": \"" + escape_quotes(orig); |
11fdf7f2 | 779 | s += + "\"}"; |
9f95a23c TL |
780 | } else if (!json_spirit::read(s, *result)) { |
781 | string orig = s; | |
11fdf7f2 TL |
782 | s = "{\"error\": \"smartctl returned invalid JSON\", \"dev\": \"/dev/"; |
783 | s += devname; | |
9f95a23c TL |
784 | s += "\",\"output\":\""; |
785 | s += escape_quotes(orig); | |
11fdf7f2 TL |
786 | s += "\"}"; |
787 | } | |
788 | if (!json_spirit::read(s, *result)) { | |
7c673cae FG |
789 | return -EINVAL; |
790 | } | |
11fdf7f2 TL |
791 | |
792 | json_spirit::mObject& base = result->get_obj(); | |
793 | string vendor = get_device_vendor(devname); | |
794 | if (vendor.size()) { | |
795 | base["nvme_vendor"] = vendor; | |
796 | s.clear(); | |
797 | json_spirit::mValue nvme_json; | |
798 | if (int r = block_device_run_vendor_nvme(devname, vendor, timeout, &s); | |
799 | r == 0) { | |
800 | if (json_spirit::read(s, nvme_json) != 0) { | |
801 | base["nvme_smart_health_information_add_log"] = nvme_json; | |
802 | } else { | |
803 | base["nvme_smart_health_information_add_log_error"] = "bad json output: " | |
804 | + s; | |
805 | } | |
806 | } else { | |
807 | base["nvme_smart_health_information_add_log_error_code"] = r; | |
808 | base["nvme_smart_health_information_add_log_error"] = s; | |
809 | } | |
810 | } else { | |
811 | base["nvme_vendor"] = "unknown"; | |
812 | } | |
813 | ||
7c673cae FG |
814 | return 0; |
815 | } | |
816 | ||
817 | #elif defined(__APPLE__) | |
818 | #include <sys/disk.h> | |
819 | ||
11fdf7f2 TL |
820 | const char *BlkDev::sysfsdir() const { |
821 | assert(false); // Should never be called on Apple | |
822 | return ""; | |
823 | } | |
824 | ||
825 | int BlkDev::dev(char *dev, size_t max) const | |
826 | { | |
827 | struct stat sb; | |
828 | ||
829 | if (fstat(fd, &sb) < 0) | |
830 | return -errno; | |
831 | ||
832 | snprintf(dev, max, "%" PRIu64, (uint64_t)sb.st_rdev); | |
833 | ||
834 | return 0; | |
835 | } | |
836 | ||
837 | int BlkDev::get_size(int64_t *psize) const | |
7c673cae FG |
838 | { |
839 | unsigned long blocksize = 0; | |
840 | int ret = ::ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize); | |
841 | if (!ret) { | |
842 | unsigned long nblocks; | |
843 | ret = ::ioctl(fd, DKIOCGETBLOCKCOUNT, &nblocks); | |
844 | if (!ret) | |
845 | *psize = (int64_t)nblocks * blocksize; | |
846 | } | |
847 | if (ret < 0) | |
848 | ret = -errno; | |
849 | return ret; | |
850 | } | |
851 | ||
9f95a23c | 852 | int64_t BlkDev::get_int_property(const char* prop) const |
11fdf7f2 TL |
853 | { |
854 | return 0; | |
855 | } | |
856 | ||
857 | bool BlkDev::support_discard() const | |
7c673cae FG |
858 | { |
859 | return false; | |
860 | } | |
861 | ||
11fdf7f2 | 862 | int BlkDev::discard(int64_t offset, int64_t len) const |
7c673cae FG |
863 | { |
864 | return -EOPNOTSUPP; | |
865 | } | |
866 | ||
20effc67 TL |
867 | int BlkDev::get_optimal_io_size() const |
868 | { | |
869 | return 0; | |
870 | } | |
871 | ||
11fdf7f2 | 872 | bool BlkDev::is_rotational() const |
7c673cae FG |
873 | { |
874 | return false; | |
875 | } | |
876 | ||
11fdf7f2 TL |
877 | int BlkDev::get_numa_node(int *node) const |
878 | { | |
879 | return -1; | |
880 | } | |
881 | ||
882 | int BlkDev::model(char *model, size_t max) const | |
883 | { | |
884 | return -EOPNOTSUPP; | |
885 | } | |
886 | ||
887 | int BlkDev::serial(char *serial, size_t max) const | |
7c673cae FG |
888 | { |
889 | return -EOPNOTSUPP; | |
890 | } | |
11fdf7f2 TL |
891 | |
892 | int BlkDev::partition(char *partition, size_t max) const | |
893 | { | |
894 | return -EOPNOTSUPP; | |
895 | } | |
896 | ||
897 | int BlkDev::wholedisk(char *device, size_t max) const | |
898 | { | |
899 | } | |
900 | ||
901 | ||
902 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
903 | { | |
904 | } | |
905 | ||
906 | void get_raw_devices(const std::string& in, | |
907 | std::set<std::string> *ls) | |
908 | { | |
909 | } | |
910 | ||
911 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
912 | { | |
913 | return -1; | |
914 | } | |
915 | ||
916 | int64_t get_vdo_stat(int fd, const char *property) | |
917 | { | |
918 | return 0; | |
919 | } | |
920 | ||
921 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
922 | { | |
923 | return false; | |
924 | } | |
925 | ||
926 | std::string get_device_id(const std::string& devname, | |
927 | std::string *err) | |
928 | { | |
929 | // FIXME: implement me | |
930 | if (err) { | |
931 | *err = "not implemented"; | |
932 | } | |
933 | return std::string(); | |
934 | } | |
935 | ||
9f95a23c TL |
936 | std::string get_device_path(const std::string& devname, |
937 | std::string *err) | |
938 | { | |
939 | // FIXME: implement me | |
940 | if (err) { | |
941 | *err = "not implemented"; | |
942 | } | |
943 | return std::string(); | |
944 | } | |
945 | ||
7c673cae | 946 | #elif defined(__FreeBSD__) |
7c673cae | 947 | |
11fdf7f2 TL |
948 | const char *BlkDev::sysfsdir() const { |
949 | assert(false); // Should never be called on FreeBSD | |
950 | return ""; | |
951 | } | |
952 | ||
953 | int BlkDev::dev(char *dev, size_t max) const | |
954 | { | |
955 | struct stat sb; | |
956 | ||
957 | if (fstat(fd, &sb) < 0) | |
958 | return -errno; | |
959 | ||
960 | snprintf(dev, max, "%" PRIu64, (uint64_t)sb.st_rdev); | |
961 | ||
962 | return 0; | |
963 | } | |
964 | ||
965 | int BlkDev::get_size(int64_t *psize) const | |
7c673cae FG |
966 | { |
967 | int ret = ::ioctl(fd, DIOCGMEDIASIZE, psize); | |
968 | if (ret < 0) | |
969 | ret = -errno; | |
970 | return ret; | |
971 | } | |
972 | ||
9f95a23c | 973 | int64_t BlkDev::get_int_property(const char* prop) const |
7c673cae | 974 | { |
11fdf7f2 TL |
975 | return 0; |
976 | } | |
977 | ||
978 | bool BlkDev::support_discard() const | |
979 | { | |
980 | #ifdef FREEBSD_WITH_TRIM | |
981 | // there is no point to claim support of discard, but | |
982 | // unable to do so. | |
983 | struct diocgattr_arg arg; | |
984 | ||
985 | strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name)); | |
986 | arg.len = sizeof(arg.value.i); | |
987 | if (ioctl(fd, DIOCGATTR, &arg) == 0) { | |
988 | return (arg.value.i != 0); | |
989 | } else { | |
990 | return false; | |
991 | } | |
992 | #endif | |
7c673cae FG |
993 | return false; |
994 | } | |
995 | ||
11fdf7f2 | 996 | int BlkDev::discard(int64_t offset, int64_t len) const |
7c673cae FG |
997 | { |
998 | return -EOPNOTSUPP; | |
999 | } | |
1000 | ||
20effc67 TL |
1001 | int BlkDev::get_optimal_io_size() const |
1002 | { | |
1003 | return 0; | |
1004 | } | |
1005 | ||
11fdf7f2 TL |
1006 | bool BlkDev::is_rotational() const |
1007 | { | |
1008 | #if __FreeBSD_version >= 1200049 | |
1009 | struct diocgattr_arg arg; | |
1010 | ||
1011 | strlcpy(arg.name, "GEOM::rotation_rate", sizeof(arg.name)); | |
1012 | arg.len = sizeof(arg.value.u16); | |
1013 | ||
1014 | int ioctl_ret = ioctl(fd, DIOCGATTR, &arg); | |
1015 | bool ret; | |
1016 | if (ioctl_ret < 0 || arg.value.u16 == DISK_RR_UNKNOWN) | |
1017 | // DISK_RR_UNKNOWN usually indicates an old drive, which is usually spinny | |
1018 | ret = true; | |
1019 | else if (arg.value.u16 == DISK_RR_NON_ROTATING) | |
1020 | ret = false; | |
1021 | else if (arg.value.u16 >= DISK_RR_MIN && arg.value.u16 <= DISK_RR_MAX) | |
1022 | ret = true; | |
1023 | else | |
1024 | ret = true; // Invalid value. Probably spinny? | |
1025 | ||
1026 | return ret; | |
1027 | #else | |
1028 | return true; // When in doubt, it's probably spinny | |
1029 | #endif | |
1030 | } | |
1031 | ||
1032 | int BlkDev::get_numa_node(int *node) const | |
1033 | { | |
9f95a23c | 1034 | int numa = get_int_property("device/device/numa_node"); |
11fdf7f2 TL |
1035 | if (numa < 0) |
1036 | return -1; | |
1037 | *node = numa; | |
1038 | return 0; | |
1039 | } | |
1040 | ||
1041 | int BlkDev::model(char *model, size_t max) const | |
1042 | { | |
1043 | struct diocgattr_arg arg; | |
1044 | ||
1045 | strlcpy(arg.name, "GEOM::descr", sizeof(arg.name)); | |
1046 | arg.len = sizeof(arg.value.str); | |
1047 | if (ioctl(fd, DIOCGATTR, &arg) < 0) { | |
1048 | return -errno; | |
1049 | } | |
1050 | ||
1051 | // The GEOM description is of the form "vendor product" for SCSI disks | |
1052 | // and "ATA device_model" for ATA disks. Some vendors choose to put the | |
1053 | // vendor name in device_model, and some don't. Strip the first bit. | |
1054 | char *p = arg.value.str; | |
1055 | if (p == NULL || *p == '\0') { | |
1056 | *model = '\0'; | |
1057 | } else { | |
1058 | (void) strsep(&p, " "); | |
1059 | snprintf(model, max, "%s", p); | |
1060 | } | |
1061 | ||
1062 | return 0; | |
1063 | } | |
1064 | ||
1065 | int BlkDev::serial(char *serial, size_t max) const | |
1066 | { | |
1067 | char ident[DISK_IDENT_SIZE]; | |
1068 | ||
1069 | if (ioctl(fd, DIOCGIDENT, ident) < 0) | |
1070 | return -errno; | |
1071 | ||
1072 | snprintf(serial, max, "%s", ident); | |
1073 | ||
1074 | return 0; | |
1075 | } | |
1076 | ||
1077 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
1078 | { | |
1079 | } | |
1080 | ||
1081 | void get_raw_devices(const std::string& in, | |
1082 | std::set<std::string> *ls) | |
1083 | { | |
1084 | } | |
1085 | ||
1086 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
1087 | { | |
1088 | return -1; | |
1089 | } | |
1090 | ||
1091 | int64_t get_vdo_stat(int fd, const char *property) | |
1092 | { | |
1093 | return 0; | |
1094 | } | |
1095 | ||
1096 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
7c673cae FG |
1097 | { |
1098 | return false; | |
1099 | } | |
1100 | ||
11fdf7f2 TL |
1101 | std::string get_device_id(const std::string& devname, |
1102 | std::string *err) | |
7c673cae | 1103 | { |
11fdf7f2 TL |
1104 | // FIXME: implement me for freebsd |
1105 | if (err) { | |
1106 | *err = "not implemented for FreeBSD"; | |
1107 | } | |
1108 | return std::string(); | |
1109 | } | |
1110 | ||
9f95a23c TL |
1111 | std::string get_device_path(const std::string& devname, |
1112 | std::string *err) | |
1113 | { | |
1114 | // FIXME: implement me for freebsd | |
1115 | if (err) { | |
1116 | *err = "not implemented for FreeBSD"; | |
1117 | } | |
1118 | return std::string(); | |
1119 | } | |
1120 | ||
11fdf7f2 TL |
1121 | int block_device_run_smartctl(const char *device, int timeout, |
1122 | std::string *result) | |
1123 | { | |
1124 | // FIXME: implement me for freebsd | |
1125 | return -EOPNOTSUPP; | |
7c673cae | 1126 | } |
11fdf7f2 TL |
1127 | |
1128 | int block_device_get_metrics(const string& devname, int timeout, | |
1129 | json_spirit::mValue *result) | |
1130 | { | |
1131 | // FIXME: implement me for freebsd | |
1132 | return -EOPNOTSUPP; | |
1133 | } | |
1134 | ||
1135 | int block_device_run_nvme(const char *device, const char *vendor, int timeout, | |
1136 | std::string *result) | |
7c673cae FG |
1137 | { |
1138 | return -EOPNOTSUPP; | |
1139 | } | |
11fdf7f2 TL |
1140 | |
1141 | static int block_device_devname(int fd, char *devname, size_t max) | |
1142 | { | |
1143 | struct fiodgname_arg arg; | |
1144 | ||
1145 | arg.buf = devname; | |
1146 | arg.len = max; | |
1147 | if (ioctl(fd, FIODGNAME, &arg) < 0) | |
1148 | return -errno; | |
1149 | return 0; | |
1150 | } | |
1151 | ||
1152 | int BlkDev::partition(char *partition, size_t max) const | |
1153 | { | |
1154 | char devname[PATH_MAX]; | |
1155 | ||
1156 | if (block_device_devname(fd, devname, sizeof(devname)) < 0) | |
1157 | return -errno; | |
1158 | snprintf(partition, max, "/dev/%s", devname); | |
1159 | return 0; | |
1160 | } | |
1161 | ||
1162 | int BlkDev::wholedisk(char *wd, size_t max) const | |
1163 | { | |
1164 | char devname[PATH_MAX]; | |
1165 | ||
1166 | if (block_device_devname(fd, devname, sizeof(devname)) < 0) | |
1167 | return -errno; | |
1168 | ||
1169 | size_t first_digit = strcspn(devname, "0123456789"); | |
1170 | // first_digit now indexes the first digit or null character of devname | |
1171 | size_t next_nondigit = strspn(&devname[first_digit], "0123456789"); | |
1172 | next_nondigit += first_digit; | |
1173 | // next_nondigit now indexes the first alphabetic or null character after the | |
1174 | // unit number | |
1175 | strlcpy(wd, devname, next_nondigit + 1); | |
1176 | return 0; | |
1177 | } | |
1178 | ||
7c673cae | 1179 | #else |
11fdf7f2 TL |
1180 | |
1181 | const char *BlkDev::sysfsdir() const { | |
1182 | assert(false); // Should never be called on non-Linux | |
1183 | return ""; | |
1184 | } | |
1185 | ||
1186 | int BlkDev::dev(char *dev, size_t max) const | |
1187 | { | |
1188 | return -EOPNOTSUPP; | |
1189 | } | |
1190 | ||
1191 | int BlkDev::get_size(int64_t *psize) const | |
1192 | { | |
1193 | return -EOPNOTSUPP; | |
1194 | } | |
1195 | ||
1196 | bool BlkDev::support_discard() const | |
1197 | { | |
1198 | return false; | |
1199 | } | |
1200 | ||
1201 | int BlkDev::discard(int fd, int64_t offset, int64_t len) const | |
7c673cae FG |
1202 | { |
1203 | return -EOPNOTSUPP; | |
1204 | } | |
1205 | ||
11fdf7f2 | 1206 | bool BlkDev::is_rotational(const char *devname) const |
7c673cae FG |
1207 | { |
1208 | return false; | |
1209 | } | |
1210 | ||
11fdf7f2 TL |
1211 | int BlkDev::model(char *model, size_t max) const |
1212 | { | |
1213 | return -EOPNOTSUPP; | |
1214 | } | |
1215 | ||
1216 | int BlkDev::serial(char *serial, size_t max) const | |
1217 | { | |
1218 | return -EOPNOTSUPP; | |
1219 | } | |
1220 | ||
1221 | int BlkDev::partition(char *partition, size_t max) const | |
7c673cae FG |
1222 | { |
1223 | return -EOPNOTSUPP; | |
1224 | } | |
1225 | ||
11fdf7f2 TL |
1226 | int BlkDev::wholedisk(char *wd, size_t max) const |
1227 | { | |
1228 | return -EOPNOTSUPP; | |
1229 | } | |
1230 | ||
1231 | void get_dm_parents(const std::string& dev, std::set<std::string> *ls) | |
1232 | { | |
1233 | } | |
1234 | ||
1235 | void get_raw_devices(const std::string& in, | |
1236 | std::set<std::string> *ls) | |
1237 | { | |
1238 | } | |
1239 | ||
1240 | int get_vdo_stats_handle(const char *devname, std::string *vdo_name) | |
1241 | { | |
1242 | return -1; | |
1243 | } | |
1244 | ||
1245 | int64_t get_vdo_stat(int fd, const char *property) | |
1246 | { | |
1247 | return 0; | |
1248 | } | |
1249 | ||
1250 | bool get_vdo_utilization(int fd, uint64_t *total, uint64_t *avail) | |
7c673cae FG |
1251 | { |
1252 | return false; | |
1253 | } | |
1254 | ||
11fdf7f2 TL |
1255 | std::string get_device_id(const std::string& devname, |
1256 | std::string *err) | |
1257 | { | |
1258 | // not implemented | |
1259 | if (err) { | |
1260 | *err = "not implemented"; | |
1261 | } | |
1262 | return std::string(); | |
1263 | } | |
1264 | ||
9f95a23c TL |
1265 | std::string get_device_path(const std::string& devname, |
1266 | std::string *err) | |
1267 | { | |
1268 | // not implemented | |
1269 | if (err) { | |
1270 | *err = "not implemented"; | |
1271 | } | |
1272 | return std::string(); | |
1273 | } | |
1274 | ||
11fdf7f2 TL |
1275 | int block_device_run_smartctl(const char *device, int timeout, |
1276 | std::string *result) | |
7c673cae FG |
1277 | { |
1278 | return -EOPNOTSUPP; | |
1279 | } | |
1280 | ||
11fdf7f2 TL |
1281 | int block_device_get_metrics(const string& devname, int timeout, |
1282 | json_spirit::mValue *result) | |
7c673cae FG |
1283 | { |
1284 | return -EOPNOTSUPP; | |
1285 | } | |
11fdf7f2 TL |
1286 | |
1287 | int block_device_run_nvme(const char *device, const char *vendor, int timeout, | |
1288 | std::string *result) | |
1289 | { | |
1290 | return -EOPNOTSUPP; | |
1291 | } | |
1292 | ||
7c673cae | 1293 | #endif |
9f95a23c TL |
1294 | |
1295 | ||
1296 | ||
1297 | void get_device_metadata( | |
1298 | const std::set<std::string>& devnames, | |
1299 | std::map<std::string,std::string> *pm, | |
1300 | std::map<std::string,std::string> *errs) | |
1301 | { | |
1302 | (*pm)["devices"] = stringify(devnames); | |
1303 | string &devids = (*pm)["device_ids"]; | |
1304 | string &devpaths = (*pm)["device_paths"]; | |
1305 | for (auto& dev : devnames) { | |
1306 | string err; | |
1307 | string id = get_device_id(dev, &err); | |
1308 | if (id.size()) { | |
1309 | if (!devids.empty()) { | |
1310 | devids += ","; | |
1311 | } | |
1312 | devids += dev + "=" + id; | |
1313 | } else { | |
1314 | (*errs)[dev] = " no unique device id for "s + dev + ": " + err; | |
1315 | } | |
1316 | string path = get_device_path(dev, &err); | |
1317 | if (path.size()) { | |
1318 | if (!devpaths.empty()) { | |
1319 | devpaths += ","; | |
1320 | } | |
1321 | devpaths += dev + "=" + path; | |
1322 | } else { | |
1323 | (*errs)[dev] + " no unique device path for "s + dev + ": " + err; | |
1324 | } | |
1325 | } | |
1326 | } |