]>
git.proxmox.com Git - ceph.git/blob - ceph/src/common/blkdev.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "include/compat.h"
18 #include <sys/param.h>
19 #include <geom/geom_disk.h>
25 #include <sys/ioctl.h>
27 #include <sys/types.h>
31 #include <boost/algorithm/string/replace.hpp>
32 //#include "common/debug.h"
33 #include "include/scope_guard.h"
34 #include "include/uuid.h"
35 #include "include/stringify.h"
39 #include "json_spirit/json_spirit_reader.h"
42 int get_device_by_path ( const char * path
, char * partition
, char * device
,
45 int fd
= :: open ( path
, O_RDONLY
| O_DIRECTORY
);
49 auto close_fd
= make_scope_guard ([ fd
] {
53 if ( auto ret
= blkdev
. partition ( partition
, max
); ret
) {
56 if ( auto ret
= blkdev
. wholedisk ( device
, max
); ret
) {
63 #include "common/blkdev.h"
68 #include <linux/kdev_t.h>
69 #include <blkid/blkid.h>
73 #include "common/SubProcess.h"
74 #include "common/errno.h"
81 using namespace std :: literals
;
85 using ceph :: bufferlist
;
92 BlkDev :: BlkDev ( const std :: string
& devname
)
96 int BlkDev :: get_devid ( dev_t
* id
) const
104 snprintf ( path
, sizeof ( path
), "/dev/%s" , devname
. c_str ());
110 * id
= S_ISBLK ( st
. st_mode
) ? st
. st_rdev
: st
. st_dev
;
116 const char * BlkDev :: sysfsdir () const {
120 int BlkDev :: get_size ( int64_t * psize
) const
123 int ret
= :: ioctl ( fd
, BLKGETSIZE64
, psize
);
124 #elif defined(BLKGETSIZE)
125 unsigned long sectors
= 0 ;
126 int ret
= :: ioctl ( fd
, BLKGETSIZE
, & sectors
);
127 * psize
= sectors
* 512ULL ;
129 // cppcheck-suppress preprocessorErrorDirective
130 # error "Linux configuration error (get_size)"
138 * get a block device property as a string
140 * store property in *val, up to maxlen chars
141 * return 0 on success
142 * return negative error on error
144 int64_t BlkDev :: get_string_property ( const char * prop
,
145 char * val
, size_t maxlen
) const
147 char filename
[ PATH_MAX
], wd
[ PATH_MAX
];
148 const char * dev
= nullptr ;
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 )
157 dev
= devname
. c_str ();
159 if ( snprintf ( filename
, sizeof ( filename
), "%s/block/%s/%s" , sysfsdir (), dev
,
160 prop
) >= static_cast < int >( sizeof ( filename
))) {
164 FILE * fp
= fopen ( filename
, "r" );
170 if ( fgets ( val
, maxlen
- 1 , fp
)) {
171 // truncate at newline
173 while (* p
&& * p
!= ' \n ' )
184 * get a block device property
186 * return the value (we assume it is positive)
187 * return negative error on error
189 int64_t BlkDev :: get_int_property ( const char * prop
) const
191 char buff
[ 256 ] = { 0 };
192 int r
= get_string_property ( prop
, buff
, sizeof ( buff
));
196 for ( char * p
= buff
; * p
; ++ p
) {
203 r
= strtoll ( buff
, & endptr
, 10 );
204 if ( endptr
!= buff
+ strlen ( buff
))
209 bool BlkDev :: support_discard () const
211 return get_int_property ( "queue/discard_granularity" ) > 0 ;
214 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
216 uint64_t range
[ 2 ] = {( uint64_t ) offset
, ( uint64_t ) len
};
217 return ioctl ( fd
, BLKDISCARD
, range
);
220 int BlkDev :: get_optimal_io_size () const
222 return get_int_property ( "queue/optimal_io_size" );
225 bool BlkDev :: is_rotational () const
227 return get_int_property ( "queue/rotational" ) > 0 ;
230 int BlkDev :: get_numa_node ( int * node
) const
232 int numa
= get_int_property ( "device/device/numa_node" );
239 int BlkDev :: dev ( char * dev
, size_t max
) const
241 return get_string_property ( "dev" , dev
, max
);
244 int BlkDev :: vendor ( char * vendor
, size_t max
) const
246 return get_string_property ( "device/device/vendor" , vendor
, max
);
249 int BlkDev :: model ( char * model
, size_t max
) const
251 return get_string_property ( "device/model" , model
, max
);
254 int BlkDev :: serial ( char * serial
, size_t max
) const
256 return get_string_property ( "device/serial" , serial
, max
);
259 int BlkDev :: partition ( char * partition
, size_t max
) const
262 int r
= get_devid (& id
);
264 return - EINVAL
; // hrm.
266 char * t
= blkid_devno_to_devname ( id
);
270 strncpy ( partition
, t
, max
);
275 int BlkDev :: wholedisk ( char * device
, size_t max
) const
278 int r
= get_devid (& id
);
280 return - EINVAL
; // hrm.
282 r
= blkid_devno_to_wholedisk ( id
, device
, max
, nullptr );
289 static int easy_readdir ( const std :: string
& dir
, std :: set
< std :: string
> * out
)
291 DIR * h
= :: opendir ( dir
. c_str ());
295 struct dirent
* de
= nullptr ;
296 while (( de
= :: readdir ( h
))) {
297 if ( strcmp ( de
-> d_name
, "." ) == 0 ||
298 strcmp ( de
-> d_name
, ".." ) == 0 ) {
301 out
-> insert ( de
-> d_name
);
307 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
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
) {
314 // recurse in case it is dm-on-dm
315 if ( d
. find ( "dm-" ) == 0 ) {
316 get_dm_parents ( d
, ls
);
321 void get_raw_devices ( const std :: string
& in
,
322 std :: set
< std :: string
> * ls
)
324 if ( in
. substr ( 0 , 3 ) == "dm-" ) {
325 std :: set
< std :: string
> o
;
326 get_dm_parents ( in
, & o
);
328 get_raw_devices ( d
, ls
);
332 std :: string wholedisk
;
333 if ( d
. wholedisk (& wholedisk
) == 0 ) {
334 ls
-> insert ( wholedisk
);
341 std :: string
_decode_model_enc ( const std :: string
& in
)
343 auto v
= boost :: replace_all_copy ( in
, " \\ x20" , " " );
344 if ( auto found
= v
. find_last_not_of ( " " ); found
!= v
. npos
) {
347 std :: replace ( v
. begin (), v
. end (), ' ' , '_' );
349 // remove "__", which seems to come up on by ubuntu box for some reason.
351 auto p
= v
. find ( "__" );
352 if ( p
== std :: string :: npos
) break ;
353 v
. replace ( p
, 2 , "_" );
359 // trying to use udev first, and if it doesn't work, we fall back to
360 // reading /sys/block/$devname/device/(vendor/model/serial).
361 std :: string
get_device_id ( const std :: string
& devname
,
364 struct udev_device
* dev
;
365 static struct udev
* udev
;
371 * err
= "udev_new failed" ;
375 dev
= udev_device_new_from_subsystem_sysname ( udev
, "block" , devname
. c_str ());
378 * err
= std :: string ( "udev_device_new_from_subsystem_sysname failed on '" )
386 // NOTE: please keep this implementation in sync with _get_device_id() in
387 // src/ceph-volume/ceph_volume/util/device.py
390 std :: string id_vendor
, id_model
, id_serial
, id_serial_short
, id_scsi_serial
;
391 data
= udev_device_get_property_value ( dev
, "ID_VENDOR" );
395 data
= udev_device_get_property_value ( dev
, "ID_MODEL" );
398 // sometimes, ID_MODEL is "LVM ..." but ID_MODEL_ENC is correct (but
399 // encoded with \x20 for space).
400 if ( id_model
. substr ( 0 , 7 ) == "LVM PV " ) {
401 const char * enc
= udev_device_get_property_value ( dev
, "ID_MODEL_ENC" );
403 id_model
= _decode_model_enc ( enc
);
405 // ignore ID_MODEL then
410 data
= udev_device_get_property_value ( dev
, "ID_SERIAL_SHORT" );
412 id_serial_short
= data
;
414 data
= udev_device_get_property_value ( dev
, "ID_SCSI_SERIAL" );
416 id_scsi_serial
= data
;
418 data
= udev_device_get_property_value ( dev
, "ID_SERIAL" );
422 udev_device_unref ( dev
);
425 // ID_SERIAL is usually $vendor_$model_$serial, but not always
426 // ID_SERIAL_SHORT is mostly always just the serial
427 // ID_MODEL is sometimes $vendor_$model, but
428 // 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)
429 std :: string device_id
;
430 if ( id_vendor
. size () && id_model
. size () && id_scsi_serial
. size ()) {
431 device_id
= id_vendor
+ '_' + id_model
+ '_' + id_scsi_serial
;
432 } else if ( id_model
. size () && id_serial_short
. size ()) {
433 device_id
= id_model
+ '_' + id_serial_short
;
434 } else if ( id_serial
. size ()) {
435 device_id
= id_serial
;
436 if ( device_id
. substr ( 0 , 4 ) == "MTFD" ) {
437 // Micron NVMes hide the vendor
438 device_id
= "Micron_" + device_id
;
441 if ( device_id
. size ()) {
442 std :: replace ( device_id
. begin (), device_id
. end (), ' ' , '_' );
446 // either udev_device_get_property_value() failed, or succeeded but
447 // returned nothing; trying to read from files. note that the 'vendor'
448 // file rarely contains the actual vendor; it's usually 'ATA'.
449 std :: string model
, serial
;
450 char buf
[ 1024 ] = { 0 };
451 BlkDev
blkdev ( devname
);
452 if (! blkdev
. model ( buf
, sizeof ( buf
))) {
455 if (! blkdev
. serial ( buf
, sizeof ( buf
))) {
459 if ( model
. empty () && serial
. empty ()) {
460 * err
= std :: string ( "fallback method has no model nor serial" );
462 } else if ( model
. empty ()) {
463 * err
= std :: string ( "fallback method has serial '" ) + serial
466 } else if ( serial
. empty ()) {
467 * err
= std :: string ( "fallback method has model '" ) + model
468 + "' but no serial'" ;
473 device_id
= model
+ "_" + serial
;
474 std :: replace ( device_id
. begin (), device_id
. end (), ' ' , '_' );
478 static std :: string
get_device_vendor ( const std :: string
& devname
)
480 struct udev_device
* dev
;
481 static struct udev
* udev
;
488 dev
= udev_device_new_from_subsystem_sysname ( udev
, "block" , devname
. c_str ());
494 std :: string id_vendor
, id_model
;
495 data
= udev_device_get_property_value ( dev
, "ID_VENDOR" );
499 data
= udev_device_get_property_value ( dev
, "ID_MODEL" );
503 udev_device_unref ( dev
);
506 std :: transform ( id_vendor
. begin (), id_vendor
. end (), id_vendor
. begin (),
508 std :: transform ( id_model
. begin (), id_model
. end (), id_model
. begin (),
511 if ( id_vendor
. size ()) {
514 if ( id_model
. size ()) {
515 int pos
= id_model
. find ( " " );
517 return id_model
. substr ( 0 , pos
);
523 std :: string vendor
, model
;
524 char buf
[ 1024 ] = { 0 };
525 BlkDev
blkdev ( devname
);
526 if (! blkdev
. vendor ( buf
, sizeof ( buf
))) {
529 if (! blkdev
. model ( buf
, sizeof ( buf
))) {
536 int pos
= model
. find ( " " );
538 return model
. substr ( 0 , pos
);
547 static int block_device_run_vendor_nvme (
548 const string
& devname
, const string
& vendor
, int timeout
,
551 string device
= "/dev/" + devname
;
553 SubProcessTimed
nvmecli (
554 "sudo" , SubProcess :: CLOSE
, SubProcess :: PIPE
, SubProcess :: CLOSE
,
556 nvmecli
. add_cmd_args (
563 int ret
= nvmecli
. spawn ();
565 * result
= std :: string ( "error spawning nvme command: " ) + nvmecli
. err ();
570 ret
= output
. read_fd ( nvmecli
. get_stdout (), 100 * 1024 );
573 err
. read_fd ( nvmecli
. get_stderr (), 100 * 1024 );
574 * result
= std :: string ( "failed to execute nvme: " ) + err
. to_str ();
577 * result
= output
. to_str ();
580 if ( nvmecli
. join () != 0 ) {
581 * result
= std :: string ( "nvme returned an error: " ) + nvmecli
. err ();
588 std :: string
get_device_path ( const std :: string
& devname
,
591 std :: set
< std :: string
> links
;
592 int r
= easy_readdir ( "/dev/disk/by-path" , & links
);
594 * err
= "unable to list contents of /dev/disk/by-path: " s
+
598 for ( auto & i
: links
) {
600 char target
[ PATH_MAX
+ 1 ];
601 snprintf ( fn
, sizeof ( fn
), "/dev/disk/by-path/%s" , i
. c_str ());
602 int r
= readlink ( fn
, target
, sizeof ( target
));
603 if ( r
< 0 || r
>= ( int ) sizeof ( target
))
606 if (( unsigned ) r
> devname
. size () + 1 &&
607 strncmp ( target
+ r
- devname
. size (), devname
. c_str (), r
) == 0 &&
608 target
[ r
- devname
. size () - 1 ] == '/' ) {
612 * err
= "no symlink to " s
+ devname
+ " in /dev/disk/by-path" ;
616 static int block_device_run_smartctl ( const string
& devname
, int timeout
,
619 string device
= "/dev/" + devname
;
621 // when using --json, smartctl will report its errors in JSON format to stdout
622 SubProcessTimed
smartctl (
623 "sudo" , SubProcess :: CLOSE
, SubProcess :: PIPE
, SubProcess :: CLOSE
,
625 smartctl
. add_cmd_args (
627 //"-a", // all SMART info
628 "-x" , // all SMART and non-SMART info
633 int ret
= smartctl
. spawn ();
635 * result
= std :: string ( "error spawning smartctl: " ) + smartctl
. err ();
640 ret
= output
. read_fd ( smartctl
. get_stdout (), 100 * 1024 );
642 * result
= std :: string ( "failed read smartctl output: " ) + cpp_strerror (- ret
);
645 * result
= output
. to_str ();
648 int joinerr
= smartctl
. join ();
649 // Bit 0: Command line did not parse.
650 // 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).
651 // 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).
652 // Bit 3: SMART status check returned "DISK FAILING".
653 // Bit 4: We found prefail Attributes <= threshold.
654 // 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.
655 // Bit 6: The device error log contains records of errors.
656 // 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.
658 * result
= "smartctl returned an error (" s
+ stringify ( joinerr
) +
659 "): stderr: \n " s
+ smartctl
. err () + " \n stdout: \n " s
+ * result
;
666 static std :: string
escape_quotes ( const std :: string
& s
)
669 auto pos
= r
. find ( " \" " );
670 while ( pos
!= std :: string :: npos
) {
671 r
. replace ( pos
, 1 , " \" " );
672 pos
= r
. find ( " \" " , pos
+ 1 );
677 int block_device_get_metrics ( const string
& devname
, int timeout
,
678 json_spirit :: mValue
* result
)
683 if ( int r
= block_device_run_smartctl ( devname
, timeout
, & s
);
686 s
= "{ \" error \" : \" smartctl failed \" , \" dev \" : \" /dev/" ;
688 s
+= " \" , \" smartctl_error_code \" : " + stringify ( r
);
689 s
+= ", \" smartctl_output \" : \" " + escape_quotes ( orig
);
691 } else if (! json_spirit :: read ( s
, * result
)) {
693 s
= "{ \" error \" : \" smartctl returned invalid JSON \" , \" dev \" : \" /dev/" ;
695 s
+= " \" , \" output \" : \" " ;
696 s
+= escape_quotes ( orig
);
699 if (! json_spirit :: read ( s
, * result
)) {
703 json_spirit :: mObject
& base
= result
-> get_obj ();
704 string vendor
= get_device_vendor ( devname
);
706 base
[ "nvme_vendor" ] = vendor
;
708 json_spirit :: mValue nvme_json
;
709 if ( int r
= block_device_run_vendor_nvme ( devname
, vendor
, timeout
, & s
);
711 if ( json_spirit :: read ( s
, nvme_json
) != 0 ) {
712 base
[ "nvme_smart_health_information_add_log" ] = nvme_json
;
714 base
[ "nvme_smart_health_information_add_log_error" ] = "bad json output: "
718 base
[ "nvme_smart_health_information_add_log_error_code" ] = r
;
719 base
[ "nvme_smart_health_information_add_log_error" ] = s
;
722 base
[ "nvme_vendor" ] = "unknown" ;
728 #elif defined(__APPLE__)
729 #include <sys/disk.h>
731 const char * BlkDev :: sysfsdir () const {
732 assert ( false ); // Should never be called on Apple
736 int BlkDev :: dev ( char * dev
, size_t max
) const
740 if ( fstat ( fd
, & sb
) < 0 )
743 snprintf ( dev
, max
, "%" PRIu64
, ( uint64_t ) sb
. st_rdev
);
748 int BlkDev :: get_size ( int64_t * psize
) const
750 unsigned long blocksize
= 0 ;
751 int ret
= :: ioctl ( fd
, DKIOCGETBLOCKSIZE
, & blocksize
);
753 unsigned long nblocks
;
754 ret
= :: ioctl ( fd
, DKIOCGETBLOCKCOUNT
, & nblocks
);
756 * psize
= ( int64_t ) nblocks
* blocksize
;
763 int64_t BlkDev :: get_int_property ( const char * prop
) const
768 bool BlkDev :: support_discard () const
773 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
778 int BlkDev :: get_optimal_io_size () const
783 bool BlkDev :: is_rotational () const
788 int BlkDev :: get_numa_node ( int * node
) const
793 int BlkDev :: model ( char * model
, size_t max
) const
798 int BlkDev :: serial ( char * serial
, size_t max
) const
803 int BlkDev :: partition ( char * partition
, size_t max
) const
808 int BlkDev :: wholedisk ( char * device
, size_t max
) const
813 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
817 void get_raw_devices ( const std :: string
& in
,
818 std :: set
< std :: string
> * ls
)
822 std :: string
get_device_id ( const std :: string
& devname
,
825 // FIXME: implement me
827 * err
= "not implemented" ;
829 return std :: string ();
832 std :: string
get_device_path ( const std :: string
& devname
,
835 // FIXME: implement me
837 * err
= "not implemented" ;
839 return std :: string ();
842 #elif defined(__FreeBSD__)
844 const char * BlkDev :: sysfsdir () const {
845 assert ( false ); // Should never be called on FreeBSD
849 int BlkDev :: dev ( char * dev
, size_t max
) const
853 if ( fstat ( fd
, & sb
) < 0 )
856 snprintf ( dev
, max
, "%" PRIu64
, ( uint64_t ) sb
. st_rdev
);
861 int BlkDev :: get_size ( int64_t * psize
) const
863 int ret
= :: ioctl ( fd
, DIOCGMEDIASIZE
, psize
);
869 int64_t BlkDev :: get_int_property ( const char * prop
) const
874 bool BlkDev :: support_discard () const
876 #ifdef FREEBSD_WITH_TRIM
877 // there is no point to claim support of discard, but
879 struct diocgattr_arg arg
;
881 strlcpy ( arg
. name
, "GEOM::candelete" , sizeof ( arg
. name
));
882 arg
. len
= sizeof ( arg
. value
. i
);
883 if ( ioctl ( fd
, DIOCGATTR
, & arg
) == 0 ) {
884 return ( arg
. value
. i
!= 0 );
892 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
897 int BlkDev :: get_optimal_io_size () const
902 bool BlkDev :: is_rotational () const
904 #if __FreeBSD_version >= 1200049
905 struct diocgattr_arg arg
;
907 strlcpy ( arg
. name
, "GEOM::rotation_rate" , sizeof ( arg
. name
));
908 arg
. len
= sizeof ( arg
. value
. u16
);
910 int ioctl_ret
= ioctl ( fd
, DIOCGATTR
, & arg
);
912 if ( ioctl_ret
< 0 || arg
. value
. u16
== DISK_RR_UNKNOWN
)
913 // DISK_RR_UNKNOWN usually indicates an old drive, which is usually spinny
915 else if ( arg
. value
. u16
== DISK_RR_NON_ROTATING
)
917 else if ( arg
. value
. u16
>= DISK_RR_MIN
&& arg
. value
. u16
<= DISK_RR_MAX
)
920 ret
= true ; // Invalid value. Probably spinny?
924 return true ; // When in doubt, it's probably spinny
928 int BlkDev :: get_numa_node ( int * node
) const
930 int numa
= get_int_property ( "device/device/numa_node" );
937 int BlkDev :: model ( char * model
, size_t max
) const
939 struct diocgattr_arg arg
;
941 strlcpy ( arg
. name
, "GEOM::descr" , sizeof ( arg
. name
));
942 arg
. len
= sizeof ( arg
. value
. str
);
943 if ( ioctl ( fd
, DIOCGATTR
, & arg
) < 0 ) {
947 // The GEOM description is of the form "vendor product" for SCSI disks
948 // and "ATA device_model" for ATA disks. Some vendors choose to put the
949 // vendor name in device_model, and some don't. Strip the first bit.
950 char * p
= arg
. value
. str
;
951 if ( p
== NULL
|| * p
== '\0' ) {
954 ( void ) strsep (& p
, " " );
955 snprintf ( model
, max
, "%s" , p
);
961 int BlkDev :: serial ( char * serial
, size_t max
) const
963 char ident
[ DISK_IDENT_SIZE
];
965 if ( ioctl ( fd
, DIOCGIDENT
, ident
) < 0 )
968 snprintf ( serial
, max
, "%s" , ident
);
973 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
977 void get_raw_devices ( const std :: string
& in
,
978 std :: set
< std :: string
> * ls
)
982 std :: string
get_device_id ( const std :: string
& devname
,
985 // FIXME: implement me for freebsd
987 * err
= "not implemented for FreeBSD" ;
989 return std :: string ();
992 std :: string
get_device_path ( const std :: string
& devname
,
995 // FIXME: implement me for freebsd
997 * err
= "not implemented for FreeBSD" ;
999 return std :: string ();
1002 int block_device_run_smartctl ( const char * device
, int timeout
,
1003 std :: string
* result
)
1005 // FIXME: implement me for freebsd
1009 int block_device_get_metrics ( const string
& devname
, int timeout
,
1010 json_spirit :: mValue
* result
)
1012 // FIXME: implement me for freebsd
1016 int block_device_run_nvme ( const char * device
, const char * vendor
, int timeout
,
1017 std :: string
* result
)
1022 static int block_device_devname ( int fd
, char * devname
, size_t max
)
1024 struct fiodgname_arg arg
;
1028 if ( ioctl ( fd
, FIODGNAME
, & arg
) < 0 )
1033 int BlkDev :: partition ( char * partition
, size_t max
) const
1035 char devname
[ PATH_MAX
];
1037 if ( block_device_devname ( fd
, devname
, sizeof ( devname
)) < 0 )
1039 snprintf ( partition
, max
, "/dev/%s" , devname
);
1043 int BlkDev :: wholedisk ( char * wd
, size_t max
) const
1045 char devname
[ PATH_MAX
];
1047 if ( block_device_devname ( fd
, devname
, sizeof ( devname
)) < 0 )
1050 size_t first_digit
= strcspn ( devname
, "0123456789" );
1051 // first_digit now indexes the first digit or null character of devname
1052 size_t next_nondigit
= strspn (& devname
[ first_digit
], "0123456789" );
1053 next_nondigit
+= first_digit
;
1054 // next_nondigit now indexes the first alphabetic or null character after the
1056 strlcpy ( wd
, devname
, next_nondigit
+ 1 );
1062 const char * BlkDev :: sysfsdir () const {
1063 assert ( false ); // Should never be called on non-Linux
1067 int BlkDev :: dev ( char * dev
, size_t max
) const
1072 int BlkDev :: get_size ( int64_t * psize
) const
1077 bool BlkDev :: support_discard () const
1082 int BlkDev :: discard ( int fd
, int64_t offset
, int64_t len
) const
1087 bool BlkDev :: is_rotational ( const char * devname
) const
1092 int BlkDev :: model ( char * model
, size_t max
) const
1097 int BlkDev :: serial ( char * serial
, size_t max
) const
1102 int BlkDev :: partition ( char * partition
, size_t max
) const
1107 int BlkDev :: wholedisk ( char * wd
, size_t max
) const
1112 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
1116 void get_raw_devices ( const std :: string
& in
,
1117 std :: set
< std :: string
> * ls
)
1121 std :: string
get_device_id ( const std :: string
& devname
,
1126 * err
= "not implemented" ;
1128 return std :: string ();
1131 std :: string
get_device_path ( const std :: string
& devname
,
1136 * err
= "not implemented" ;
1138 return std :: string ();
1141 int block_device_run_smartctl ( const char * device
, int timeout
,
1142 std :: string
* result
)
1147 int block_device_get_metrics ( const string
& devname
, int timeout
,
1148 json_spirit :: mValue
* result
)
1153 int block_device_run_nvme ( const char * device
, const char * vendor
, int timeout
,
1154 std :: string
* result
)
1163 void get_device_metadata (
1164 const std :: set
< std :: string
>& devnames
,
1165 std :: map
< std :: string
, std :: string
> * pm
,
1166 std :: map
< std :: string
, std :: string
> * errs
)
1168 (* pm
)[ "devices" ] = stringify ( devnames
);
1169 string
& devids
= (* pm
)[ "device_ids" ];
1170 string
& devpaths
= (* pm
)[ "device_paths" ];
1171 for ( auto & dev
: devnames
) {
1173 string id
= get_device_id ( dev
, & err
);
1175 if (! devids
. empty ()) {
1178 devids
+= dev
+ "=" + id
;
1180 (* errs
)[ dev
] = " no unique device id for " s
+ dev
+ ": " + err
;
1182 string path
= get_device_path ( dev
, & err
);
1184 if (! devpaths
. empty ()) {
1187 devpaths
+= dev
+ "=" + path
;
1189 (* errs
)[ dev
] + " no unique device path for " s
+ dev
+ ": " + err
;