smartmontools SVN Rev 5645
Utility to control and monitor storage systems with "S.M.A.R.T."
dev_interface.cpp
Go to the documentation of this file.
1/*
2 * dev_interface.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2008-23 Christian Franke
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11#include "config.h"
12
13#include "dev_interface.h"
14#include "dev_tunnelled.h"
15#include "atacmds.h" // ATA_SMART_CMD/STATUS
16#include "scsicmds.h" // scsi_cmnd_io
17#include "nvmecmds.h" // nvme_status_*()
18#include "utility.h"
19
20#include <errno.h>
21#include <stdarg.h>
22#include <stdlib.h> // realpath()
23#include <stdexcept>
24
25const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 5644 2024-12-10 14:39:22Z chrfranke $"
27
28/////////////////////////////////////////////////////////////////////////////
29// smart_device
30
32
33smart_device::smart_device(smart_interface * intf, const char * dev_name,
34 const char * dev_type, const char * req_type)
35: m_intf(intf), m_info(dev_name, dev_type, req_type),
36 m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
37{
39}
40
42: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
43{
44 throw std::logic_error("smart_device: wrong constructor called in implementation class");
45}
46
48{
50}
51
53{
54 if (get_errno() == ENOSYS)
55 return true;
56#ifdef ENOTSUP
57 if (get_errno() == ENOTSUP)
58 return true;
59#endif
60 return false;
61}
62
63bool smart_device::set_err(int no, const char * msg, ...)
64{
65 if (!msg)
66 return set_err(no);
67 m_err.no = no;
68 va_list ap; va_start(ap, msg);
69 m_err.msg = vstrprintf(msg, ap);
70 va_end(ap);
71 return false;
72}
73
75{
76 return smi()->set_err_var(&m_err, no);
77}
78
80{
81 open();
82 return this;
83}
84
86{
87 return false;
88}
89
90bool smart_device::owns(const smart_device * /*dev*/) const
91{
92 return false;
93}
94
96{
97}
98
99
100/////////////////////////////////////////////////////////////////////////////
101// ata_device
102
104: features_16(features, prev.features),
105 sector_count_16(sector_count, prev.sector_count),
106 lba_low_16(lba_low, prev.lba_low),
107 lba_mid_16(lba_mid, prev.lba_mid),
108 lba_high_16(lba_high, prev.lba_high),
109 lba_48( lba_low, lba_mid, lba_high,
110 prev.lba_low, prev.lba_mid, prev.lba_high)
111{
112}
113
115: sector_count_16(sector_count, prev.sector_count),
116 lba_low_16(lba_low, prev.lba_low),
117 lba_mid_16(lba_mid, prev.lba_mid),
118 lba_high_16(lba_high, prev.lba_high),
119 lba_48( lba_low, lba_mid, lba_high,
120 prev.lba_low, prev.lba_mid, prev.lba_high)
121{
122}
123
125: direction(no_data),
126 buffer(0),
127 size(0)
128{
129}
130
132{
133}
134
136{
137 ata_cmd_out dummy;
138 return ata_pass_through(in, dummy);
139}
140
142 unsigned flags, const char * type /* = 0 */)
143{
144 // Check DATA IN/OUT
145 switch (in.direction) {
146 case ata_cmd_in::no_data: break;
147 case ata_cmd_in::data_in: break;
148 case ata_cmd_in::data_out: break;
149 default:
150 return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
151 }
152
153 // Check buffer size
154 if (in.direction == ata_cmd_in::no_data) {
155 if (in.size)
156 return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
157 }
158 else {
159 if (!in.buffer)
160 return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
161 unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
162 // TODO: Add check for sector count == 0
163 if (count * 512 != in.size)
164 return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
165 }
166
167 // Check features
168 const char * errmsg = 0;
170 errmsg = "DATA OUT ATA commands not implemented";
171 else if ( in.out_needed.is_set() && !(flags & supports_output_regs)
172 && !( in.in_regs.command == ATA_SMART_CMD
175 errmsg = "Read of ATA output registers not implemented";
176 else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
177 errmsg = "Multi-sector ATA commands not implemented";
179 errmsg = "48-bit ATA commands not implemented";
180 else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
181 errmsg = "48-bit ATA commands not fully implemented";
182
183 if (errmsg)
184 return set_err(ENOSYS, "%s%s%s%s", errmsg,
185 (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
186
187 return true;
188}
189
191{
192 return false;
193}
194
195/////////////////////////////////////////////////////////////////////////////
196// scsi_device
197
199 const char * msg)
200{
201 // Provide sense buffer
202 unsigned char sense[32] = {0, };
203 iop->sensep = sense;
204 iop->max_sense_len = sizeof(sense);
206
207 // Run cmd
208 if (!scsi_pass_through(iop)) {
209 if (scsi_debugmode > 0)
210 pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
211 msg, get_errno(), get_errmsg());
212 iop->sensep = nullptr;
213 return false;
214 }
215
216 // Check sense
217 scsi_sense_disect sinfo;
218 scsi_do_sense_disect(iop, &sinfo);
219 int err = scsiSimpleSenseFilter(&sinfo);
220 iop->sensep = nullptr;
221 if (err) {
222 if (scsi_debugmode > 0)
223 pout("%sscsi error: %s\n", msg, scsiErrString(err));
224 return set_err(EIO, "scsi error %s", scsiErrString(err));
225 }
226
227 return true;
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// nvme_device
232
233bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
234{
235 out.status = status;
236 out.status_valid = true;
237 char buf[64];
238 return set_err(nvme_status_to_errno(status), "%s%s (0x%03x)", (msg ? msg : ""),
239 nvme_status_to_info_str(buf, status), status);
240}
241
242
243/////////////////////////////////////////////////////////////////////////////
244// tunnelled_device_base
245
247: smart_device(never_called),
248 m_tunnel_base_dev(tunnel_dev)
249{
250}
251
253{
254 delete m_tunnel_base_dev;
255}
256
258{
260}
261
263{
265 return set_err(ENOSYS);
266 if (!m_tunnel_base_dev->open())
268 return true;
269}
270
272{
274 return true;
275 if (!m_tunnel_base_dev->close())
277 return true;
278}
279
281{
282 return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
283}
284
286{
287 if (m_tunnel_base_dev == dev)
289}
290
291
292/////////////////////////////////////////////////////////////////////////////
293// smart_interface
294
295// Pointer to (usually singleton) interface object returned by ::smi()
297
299{
300 return SMARTMONTOOLS_BUILD_HOST;
301}
302
304{
305 // default
306 std::string s =
307 "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbasm1352r,N, usbcypress[,X], "
308 "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntasmedia, sntjmicron[,NSID], "
309 "sntrealtek, jmb39x[-q[2]],N[,sLBA][,force][+TYPE], "
310 "jms56x,N[,sLBA][,force][+TYPE]";
311 // append custom
312 std::string s2 = get_valid_custom_dev_types_str();
313 if (!s2.empty()) {
314 s += ", "; s += s2;
315 }
316 return s;
317}
318
319std::string smart_interface::get_app_examples(const char * /*appname*/)
320{
321 return "";
322}
323
325{
326 return set_err(ENOSYS);
327}
328
329bool smart_interface::set_err(int no, const char * msg, ...)
330{
331 if (!msg)
332 return set_err(no);
333 m_err.no = no;
334 va_list ap; va_start(ap, msg);
335 m_err.msg = vstrprintf(msg, ap);
336 va_end(ap);
337 return false;
338}
339
340decltype(nullptr) smart_interface::set_err_np(int no, const char * msg, ...)
341{
342 if (!msg) {
343 set_err(no);
344 return nullptr;
345 }
346 m_err.no = no;
347 va_list ap; va_start(ap, msg);
348 m_err.msg = vstrprintf(msg, ap);
349 va_end(ap);
350 return nullptr;
351}
352
354{
355 return set_err_var(&m_err, no);
356}
357
359{
360 err->no = no;
361 err->msg = get_msg_for_errno(no);
362 if (err->msg.empty() && no != 0)
363 err->msg = strprintf("Unknown error %d", no);
364 return false;
365}
366
368{
369 return strerror(no);
370}
371
372std::string smart_interface::get_unique_dev_name(const char * name, const char * type) const
373{
374 std::string unique_name;
375#if defined(HAVE_UNISTD_H) && !defined(_WIN32) && !defined(__OS2__)
376 char * p = realpath(name, (char *)0); // nullptr requires POSIX.1.2008 compatibility
377 if (p) {
378 unique_name = p;
379 free(p);
380 }
381 else
382#endif
383 unique_name = name;
384
385 if (*type && is_raid_dev_type(type)) {
386 // -d TYPE options must match if RAID drive number is specified
387 unique_name += " ["; unique_name += type; unique_name += ']';
388 }
389 return unique_name;
390}
391
392bool smart_interface::is_raid_dev_type(const char * type) const
393{
394 if (!strchr(type, ','))
395 return false;
396 if (str_starts_with(type, "sat,"))
397 return false;
398 int i;
399 if (sscanf(type, "%*[^,],%d", &i) != 1)
400 return false;
401 return true;
402}
403
404
405/////////////////////////////////////////////////////////////////////////////
406// Default device factory
407
408smart_device * smart_interface::get_smart_device(const char * name, const char * type)
409{
410 clear_err();
411
412 // Call platform specific autodetection if no device type specified
413 smart_device * dev;
414 if (!type || !*type) {
415 dev = autodetect_smart_device(name);
416 if (!dev && !get_errno())
417 set_err(EINVAL, "Unable to detect device type");
418 return dev;
419 }
420
421 // First check for platform specific device types
422 dev = get_custom_smart_device(name, type);
423 if (dev || get_errno())
424 return dev;
425
426 if (!strcmp(type, "ata"))
427 dev = get_ata_device(name, type);
428 else if (!strcmp(type, "scsi"))
429 dev = get_scsi_device(name, type);
430
431 else if (str_starts_with(type, "nvme")) {
432 int n1 = -1, n2 = -1, len = strlen(type);
433 unsigned nsid = 0; // invalid namespace id -> use default
434 sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
435 if (!(n1 == len || n2 == len))
436 return set_err_np(EINVAL, "Invalid NVMe namespace id in '%s'", type);
437 dev = get_nvme_device(name, type, nsid);
438 }
439 // TODO: Unify handling of '-d TYPE...+BASETYPE...'
440 else if ( (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
441 || str_starts_with(type, "scsi+")
442 || str_starts_with(type, "usb") ) {
443 // Split "sat...+base..." -> ("sat...", "base...")
444 unsigned satlen = strcspn(type, "+");
445 std::string sattype(type, satlen);
446 const char * basetype = (type[satlen] ? type+satlen+1 : "");
447 // Recurse to allocate base device, default is standard SCSI
448 if (!*basetype)
449 basetype = "scsi";
450 smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
451 if (!basedev)
452 return set_err_np(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
453 // Result must be SCSI
454 if (!basedev->is_scsi())
455 return set_err_np(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
456 // Attach SAT tunnel
457 return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
458 }
459
460 else if (str_starts_with(type, "snt")) {
461 smart_device_auto_ptr basedev( get_smart_device(name, "scsi") );
462 if (!basedev)
463 return set_err_np(EINVAL, "Type '%s': %s", type, get_errmsg());
464
465 return get_snt_device(type, basedev.release()->to_scsi());
466 }
467
468 else if (str_starts_with(type, "jmb39x") || str_starts_with(type, "jms56x")) {
469 // Split "jmb39x...+base..." -> ("jmb39x...", "base...")
470 unsigned jmblen = strcspn(type, "+");
471 std::string jmbtype(type, jmblen);
472 const char * basetype = (type[jmblen] ? type+jmblen+1 : "");
473 // Recurse to allocate base device, default is standard SCSI
474 if (!*basetype)
475 basetype = "scsi";
476 smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
477 if (!basedev)
478 return set_err_np(EINVAL, "Type '%s+...': %s", jmbtype.c_str(), get_errmsg());
479 // Attach JMB39x tunnel
480 return get_jmb39x_device(jmbtype.c_str(), basedev.release());
481 }
482
483 else if (str_starts_with(type, "intelliprop")) {
484 // Split "intelliprop...+base..." -> ("intelliprop...", "base...")
485 unsigned itllen = strcspn(type, "+");
486 std::string itltype(type, itllen);
487 const char * basetype = (type[itllen] ? type+itllen+1 : "");
488 // Recurse to allocate base device, default is standard ATA
489 if (!*basetype)
490 basetype = "ata";
491 smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
492 if (!basedev)
493 return set_err_np(EINVAL, "Type '%s': %s", type, get_errmsg());
494 // Result must be ATA
495 if (!basedev->is_ata())
496 return set_err_np(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
497 return get_intelliprop_device(itltype.c_str(), basedev.release()->to_ata());
498 }
499
500 else {
501 return set_err_np(EINVAL, "Unknown device type '%s'", type);
502 }
503 if (!dev && !get_errno())
504 set_err(EINVAL, "Not a device of type '%s'", type);
505 return dev;
506}
507
509 const char * /*type*/, const char * /*pattern*/ /* = 0 */)
510{
511 return set_err(ENOSYS);
512}
513
515 const smart_devtype_list & types, const char * pattern /* = 0 */)
516{
517 unsigned n = types.size();
518 if (n == 0)
519 return scan_smart_devices(devlist, (const char *)0, pattern);
520 if (n == 1)
521 return scan_smart_devices(devlist, types.front().c_str(), pattern);
522
523 for (unsigned i = 0; i < n; i++) {
524 smart_device_list tmplist;
525 if (!scan_smart_devices(tmplist, types[i].c_str(), pattern))
526 return false;
527 devlist.append(tmplist);
528 }
529
530 return true;
531}
532
533nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
534{
535 return set_err_np(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
536}
537
538smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
539{
540 return nullptr;
541}
542
544{
545 return "";
546}
547
549{
550 if (!strncmp(type, "snt", 3)) {
551 return get_snt_device(type, scsidev);
552 }
553
554 return get_sat_device(type, scsidev);
555}
#define ATA_SMART_STATUS
Definition: atacmds.h:91
#define ATA_SMART_CMD
Definition: atacmds.h:56
Smart pointer class for device pointers.
device_type * release()
Return the pointer and release ownership.
@ supports_output_regs
@ supports_48bit_hi_null
@ supports_multi_sector
@ supports_smart_status
virtual bool ata_identify_is_cached() const
Return true if OS caches ATA identify sector.
bool ata_cmd_is_supported(const ata_cmd_in &in, unsigned flags, const char *type=0)
Check command input parameters.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)=0
ATA pass through.
NVMe device access.
bool set_nvme_err(nvme_cmd_out &out, unsigned status, const char *msg=0)
Set last error number and message if pass-through returns NVMe error status.
SCSI device access.
bool scsi_pass_through_and_check(scsi_cmnd_io *iop, const char *msg="")
virtual bool scsi_pass_through(scsi_cmnd_io *iop)=0
SCSI pass through.
List of devices for DEVICESCAN.
void append(smart_device_list &devlist)
Base class for all devices.
Definition: dev_interface.h:33
int get_errno() const
Get last error number.
const error_info & get_err() const
Get last error info struct.
error_info m_err
virtual void release(const smart_device *dev)
Release ownership of other device.
virtual bool is_powered_down()
Early test if device is powered up or down.
smart_interface * smi()
Get interface which produced this object.
const char * get_errmsg() const
Get last error message.
virtual bool close()=0
Close device, return false on error.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
do_not_use_in_implementation_classes
Dummy enum for dummy constructor.
Definition: dev_interface.h:72
virtual smart_device * autodetect_open()
Open device with autodetection support.
virtual bool owns(const smart_device *dev) const
Return true if other device is owned by this device.
virtual bool is_syscall_unsup() const
Return true if last error indicates an unsupported system call.
virtual bool is_open() const =0
Return true if device is open.
static int s_num_objects
smart_device(smart_interface *intf, const char *dev_name, const char *dev_type, const char *req_type)
Constructor to init interface and device info.
virtual bool open()=0
Open device, return false on error.
virtual ~smart_device()
The platform interface abstraction.
virtual std::string get_app_examples(const char *appname)
Return example string for program 'appname'.
virtual const char * get_msg_for_errno(int no)
Convert error number into message, used by set_err(no).
void clear_err()
Clear last error info.
virtual nvme_device * get_snt_device(const char *type, scsi_device *scsidev)
Return NVMe->SCSI filter for a SNT or USB 'type'.
Definition: scsinvme.cpp:398
static smart_interface * s_instance
Pointer to the interface object.
virtual ata_device * get_jmb39x_device(const char *type, smart_device *smartdev)
Return JMB93x->ATA filter.
virtual ata_device * get_sat_device(const char *type, scsi_device *scsidev)
Return ATA->SCSI filter for a SAT or USB 'type'.
Definition: scsiata.cpp:1405
virtual std::string get_unique_dev_name(const char *name, const char *type) const
Return unique device name which is (only) suitable for duplicate detection.
virtual smart_device * get_smart_device(const char *name, const char *type)
Return device object for device 'name' with some 'type'.
smart_device::error_info m_err
virtual ata_device * get_ata_device(const char *name, const char *type)=0
Return standard ATA device.
const char * get_errmsg() const
Get last error message.
virtual ata_device * get_intelliprop_device(const char *type, ata_device *atadev)
Return filter for Intelliprop controllers.
bool decltype(nullptr) set_err_np(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
int get_errno() const
Get last error number.
virtual smart_device * get_custom_smart_device(const char *name, const char *type)
Return device for platform specific 'type'.
virtual std::string get_os_version_str()
Return info string about build host and/or OS version.
virtual std::string get_valid_custom_dev_types_str()
Return valid 'type' args accepted by above.
virtual bool disable_system_auto_standby(bool disable)
Disable/Enable system auto standby/sleep mode.
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0)
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'.
virtual smart_device * autodetect_smart_device(const char *name)=0
Autodetect device if no device type specified.
virtual std::string get_valid_dev_types_str()
Return valid args for device type option/directive.
virtual scsi_device * get_scsi_device(const char *name, const char *type)=0
Return standard SCSI device.
virtual smart_device * get_scsi_passthrough_device(const char *type, scsi_device *scsidev)
Return ATA->SCSI of NVMe->SCSI filter for a SAT, SNT or USB 'type'.
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid)
Return standard NVMe device.
bool set_err_var(smart_device::error_info *err, int no)
Set last error number and default message to any error_info.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
virtual bool is_raid_dev_type(const char *type) const
Return true if the 'type' string contains a RAID drive number.
virtual bool open() override
Open device, return false on error.
virtual bool owns(const smart_device *dev) const override
Return true if other device is owned by this device.
virtual void release(const smart_device *dev) override
Release ownership of other device.
smart_device * m_tunnel_base_dev
Definition: dev_tunnelled.h:43
virtual ~tunnelled_device_base()
virtual bool is_open() const override
Return true if device is open.
virtual bool close() override
Close device, return false on error.
tunnelled_device_base(smart_device *tunnel_dev)
const char * dev_interface_cpp_cvsid
#define DEV_INTERFACE_H_CVSID
Definition: dev_interface.h:14
std::vector< std::string > smart_devtype_list
List of types for DEVICESCAN.
u16 flags
Definition: megaraid.h:14
u32 count
Definition: megaraid.h:1
ptr_t buffer
Definition: megaraid.h:3
u16 s[6]
Definition: megaraid.h:18
u32 size
Definition: megaraid.h:0
uint32_t nsid
int nvme_status_to_errno(uint16_t status)
Definition: nvmecmds.cpp:472
const char * nvme_status_to_info_str(char *buf, size_t bufsize, uint16_t status)
Definition: nvmecmds.cpp:490
#define ENOTSUP
Definition: os_linux.cpp:88
int scsiSimpleSenseFilter(const struct scsi_sense_disect *sinfo)
Definition: scsicmds.cpp:587
void scsi_do_sense_disect(const struct scsi_cmnd_io *io_buf, struct scsi_sense_disect *out)
Definition: scsicmds.cpp:565
const char * scsiErrString(int scsiErr)
Definition: scsicmds.cpp:630
unsigned char scsi_debugmode
Definition: scsicmds.cpp:45
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:379
const char const char va_list ap
Definition: smartctl.cpp:1326
void pout(const char *fmt,...)
Definition: smartd.cpp:1347
ATA pass through input parameters.
enum ata_cmd_in::@29 direction
I/O direction.
void * buffer
Pointer to data buffer.
ata_in_regs_48bit in_regs
Input registers.
unsigned size
Size of buffer.
ata_out_regs_flags out_needed
True if output register value needed.
ATA pass through output parameters.
bool is_48bit_cmd() const
Return true if 48-bit command.
bool is_real_48bit_cmd() const
Return true if 48-bit command with any nonzero high byte.
ata_in_regs prev
"previous content"
ata_register sector_count
ata_register features
ata_register command
bool is_set() const
Return true if any flag is set.
NVMe pass through output parameters.
bool status_valid
true if status is valid
unsigned short status
Status Field (DW3 31:17)
uint8_t * sensep
Definition: scsicmds.h:123
size_t max_sense_len
Definition: scsicmds.h:125
unsigned timeout
Definition: scsicmds.h:126
Error (number,message) pair.
Definition: dev_interface.h:52
std::string msg
Error message.
Definition: dev_interface.h:61
int no
Error number.
Definition: dev_interface.h:60
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:799
std::string std::string vstrprintf(const char *fmt, va_list ap)
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:52