smartmontools  SVN Rev 5304
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-21 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 "utility.h"
18 
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <stdlib.h> // realpath()
22 #include <stdexcept>
23 
24 const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 5219 2021-06-04 16:39:50Z chrfranke $"
26 
27 /////////////////////////////////////////////////////////////////////////////
28 // smart_device
29 
31 
32 smart_device::smart_device(smart_interface * intf, const char * dev_name,
33  const char * dev_type, const char * req_type)
34 : m_intf(intf), m_info(dev_name, dev_type, req_type),
35  m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
36 {
37  s_num_objects++;
38 }
39 
41 : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
42 {
43  throw std::logic_error("smart_device: wrong constructor called in implementation class");
44 }
45 
47 {
48  s_num_objects--;
49 }
50 
52 {
53  if (get_errno() == ENOSYS)
54  return true;
55 #ifdef ENOTSUP
56  if (get_errno() == ENOTSUP)
57  return true;
58 #endif
59  return false;
60 }
61 
62 bool smart_device::set_err(int no, const char * msg, ...)
63 {
64  if (!msg)
65  return set_err(no);
66  m_err.no = no;
67  va_list ap; va_start(ap, msg);
68  m_err.msg = vstrprintf(msg, ap);
69  va_end(ap);
70  return false;
71 }
72 
74 {
75  return smi()->set_err_var(&m_err, no);
76 }
77 
79 {
80  open();
81  return this;
82 }
83 
85 {
86  return false;
87 }
88 
89 bool smart_device::owns(const smart_device * /*dev*/) const
90 {
91  return false;
92 }
93 
94 void smart_device::release(const smart_device * /*dev*/)
95 {
96 }
97 
98 
99 /////////////////////////////////////////////////////////////////////////////
100 // ata_device
101 
103 : features_16(features, prev.features),
104  sector_count_16(sector_count, prev.sector_count),
105  lba_low_16(lba_low, prev.lba_low),
106  lba_mid_16(lba_mid, prev.lba_mid),
107  lba_high_16(lba_high, prev.lba_high),
108  lba_48( lba_low, lba_mid, lba_high,
109  prev.lba_low, prev.lba_mid, prev.lba_high)
110 {
111 }
112 
114 : sector_count_16(sector_count, prev.sector_count),
115  lba_low_16(lba_low, prev.lba_low),
116  lba_mid_16(lba_mid, prev.lba_mid),
117  lba_high_16(lba_high, prev.lba_high),
118  lba_48( lba_low, lba_mid, lba_high,
119  prev.lba_low, prev.lba_mid, prev.lba_high)
120 {
121 }
122 
124 : direction(no_data),
125  buffer(0),
126  size(0)
127 {
128 }
129 
131 {
132 }
133 
135 {
136  ata_cmd_out dummy;
137  return ata_pass_through(in, dummy);
138 }
139 
141  unsigned flags, const char * type /* = 0 */)
142 {
143  // Check DATA IN/OUT
144  switch (in.direction) {
145  case ata_cmd_in::no_data: break;
146  case ata_cmd_in::data_in: break;
147  case ata_cmd_in::data_out: break;
148  default:
149  return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
150  }
151 
152  // Check buffer size
153  if (in.direction == ata_cmd_in::no_data) {
154  if (in.size)
155  return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
156  }
157  else {
158  if (!in.buffer)
159  return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
160  unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
161  // TODO: Add check for sector count == 0
162  if (count * 512 != in.size)
163  return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
164  }
165 
166  // Check features
167  const char * errmsg = 0;
169  errmsg = "DATA OUT ATA commands not implemented";
170  else if ( in.out_needed.is_set() && !(flags & supports_output_regs)
171  && !( in.in_regs.command == ATA_SMART_CMD
174  errmsg = "Read of ATA output registers not implemented";
175  else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
176  errmsg = "Multi-sector ATA commands not implemented";
178  errmsg = "48-bit ATA commands not implemented";
179  else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
180  errmsg = "48-bit ATA commands not fully implemented";
181 
182  if (errmsg)
183  return set_err(ENOSYS, "%s%s%s%s", errmsg,
184  (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
185 
186  return true;
187 }
188 
190 {
191  return false;
192 }
193 
194 /////////////////////////////////////////////////////////////////////////////
195 // scsi_device
196 
198  const char * msg)
199 {
200  // Provide sense buffer
201  unsigned char sense[32] = {0, };
202  iop->sensep = sense;
203  iop->max_sense_len = sizeof(sense);
205 
206  // Run cmd
207  if (!scsi_pass_through(iop)) {
208  if (scsi_debugmode > 0)
209  pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
210  msg, get_errno(), get_errmsg());
211  return false;
212  }
213 
214  // Check sense
215  scsi_sense_disect sinfo;
216  scsi_do_sense_disect(iop, &sinfo);
217  int err = scsiSimpleSenseFilter(&sinfo);
218  if (err) {
219  if (scsi_debugmode > 0)
220  pout("%sscsi error: %s\n", msg, scsiErrString(err));
221  return set_err(EIO, "scsi error %s", scsiErrString(err));
222  }
223 
224  return true;
225 }
226 
227 /////////////////////////////////////////////////////////////////////////////
228 // nvme_device
229 
230 bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
231 {
232  if (!status)
233  throw std::logic_error("nvme_device: set_nvme_err() called with status=0");
234 
235  out.status = status;
236  out.status_valid = true;
237  return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status);
238 }
239 
240 
241 /////////////////////////////////////////////////////////////////////////////
242 // tunnelled_device_base
243 
245 : smart_device(never_called),
246  m_tunnel_base_dev(tunnel_dev)
247 {
248 }
249 
251 {
252  delete m_tunnel_base_dev;
253 }
254 
256 {
258 }
259 
261 {
262  if (!m_tunnel_base_dev)
263  return set_err(ENOSYS);
264  if (!m_tunnel_base_dev->open())
265  return set_err(m_tunnel_base_dev->get_err());
266  return true;
267 }
268 
270 {
271  if (!m_tunnel_base_dev)
272  return true;
273  if (!m_tunnel_base_dev->close())
274  return set_err(m_tunnel_base_dev->get_err());
275  return true;
276 }
277 
279 {
280  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
281 }
282 
284 {
285  if (m_tunnel_base_dev == dev)
286  m_tunnel_base_dev = 0;
287 }
288 
289 
290 /////////////////////////////////////////////////////////////////////////////
291 // smart_interface
292 
293 // Pointer to (usually singleton) interface object returned by ::smi()
295 
297 {
298  return SMARTMONTOOLS_BUILD_HOST;
299 }
300 
302 {
303  // default
304  std::string s =
305  "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
306  "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntasmedia, sntjmicron[,NSID], "
307  "sntrealtek, intelliprop,N[+TYPE], jmb39x[-q],N[,sLBA][,force][+TYPE], "
308  "jms56x,N[,sLBA][,force][+TYPE]";
309  // append custom
310  std::string s2 = get_valid_custom_dev_types_str();
311  if (!s2.empty()) {
312  s += ", "; s += s2;
313  }
314  return s;
315 }
316 
317 std::string smart_interface::get_app_examples(const char * /*appname*/)
318 {
319  return "";
320 }
321 
323 {
324  return set_err(ENOSYS);
325 }
326 
327 bool smart_interface::set_err(int no, const char * msg, ...)
328 {
329  if (!msg)
330  return set_err(no);
331  m_err.no = no;
332  va_list ap; va_start(ap, msg);
333  m_err.msg = vstrprintf(msg, ap);
334  va_end(ap);
335  return false;
336 }
337 
339 {
340  return set_err_var(&m_err, no);
341 }
342 
344 {
345  err->no = no;
346  err->msg = get_msg_for_errno(no);
347  if (err->msg.empty() && no != 0)
348  err->msg = strprintf("Unknown error %d", no);
349  return false;
350 }
351 
353 {
354  return strerror(no);
355 }
356 
357 std::string smart_interface::get_unique_dev_name(const char * name, const char * type) const
358 {
359  std::string unique_name;
360 #if defined(HAVE_UNISTD_H) && !defined(_WIN32) && !defined(__OS2__)
361  char * p = realpath(name, (char *)0); // nullptr requires POSIX.1.2008 compatibility
362  if (p) {
363  unique_name = p;
364  free(p);
365  }
366  else
367 #endif
368  unique_name = name;
369 
370  if (*type && is_raid_dev_type(type)) {
371  // -d TYPE options must match if RAID drive number is specified
372  unique_name += " ["; unique_name += type; unique_name += ']';
373  }
374  return unique_name;
375 }
376 
377 bool smart_interface::is_raid_dev_type(const char * type) const
378 {
379  if (!strchr(type, ','))
380  return false;
381  if (str_starts_with(type, "sat,"))
382  return false;
383  int i;
384  if (sscanf(type, "%*[^,],%d", &i) != 1)
385  return false;
386  return true;
387 }
388 
389 
390 /////////////////////////////////////////////////////////////////////////////
391 // Default device factory
392 
393 smart_device * smart_interface::get_smart_device(const char * name, const char * type)
394 {
395  clear_err();
396 
397  // Call platform specific autodetection if no device type specified
398  smart_device * dev;
399  if (!type || !*type) {
400  dev = autodetect_smart_device(name);
401  if (!dev && !get_errno())
402  set_err(EINVAL, "Unable to detect device type");
403  return dev;
404  }
405 
406  // First check for platform specific device types
407  dev = get_custom_smart_device(name, type);
408  if (dev || get_errno())
409  return dev;
410 
411  if (!strcmp(type, "ata"))
412  dev = get_ata_device(name, type);
413  else if (!strcmp(type, "scsi"))
414  dev = get_scsi_device(name, type);
415 
416  else if (str_starts_with(type, "nvme")) {
417  int n1 = -1, n2 = -1, len = strlen(type);
418  unsigned nsid = 0; // invalid namespace id -> use default
419  sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
420  if (!(n1 == len || n2 == len)) {
421  set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
422  return 0;
423  }
424  dev = get_nvme_device(name, type, nsid);
425  }
426  // TODO: Unify handling of '-d TYPE...+BASETYPE...'
427  else if ( (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
428  || str_starts_with(type, "scsi+")
429  || str_starts_with(type, "usb") ) {
430  // Split "sat...+base..." -> ("sat...", "base...")
431  unsigned satlen = strcspn(type, "+");
432  std::string sattype(type, satlen);
433  const char * basetype = (type[satlen] ? type+satlen+1 : "");
434  // Recurse to allocate base device, default is standard SCSI
435  if (!*basetype)
436  basetype = "scsi";
437  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
438  if (!basedev) {
439  set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
440  return 0;
441  }
442  // Result must be SCSI
443  if (!basedev->is_scsi()) {
444  set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
445  return 0;
446  }
447  // Attach SAT tunnel
448  return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
449  }
450 
451  else if (str_starts_with(type, "snt")) {
452  smart_device_auto_ptr basedev( get_smart_device(name, "scsi") );
453  if (!basedev) {
454  set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
455  return 0;
456  }
457 
458  return get_snt_device(type, basedev.release()->to_scsi());
459  }
460 
461  else if (str_starts_with(type, "jmb39x") || str_starts_with(type, "jms56x")) {
462  // Split "jmb39x...+base..." -> ("jmb39x...", "base...")
463  unsigned jmblen = strcspn(type, "+");
464  std::string jmbtype(type, jmblen);
465  const char * basetype = (type[jmblen] ? type+jmblen+1 : "");
466  // Recurse to allocate base device, default is standard SCSI
467  if (!*basetype)
468  basetype = "scsi";
469  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
470  if (!basedev) {
471  set_err(EINVAL, "Type '%s+...': %s", jmbtype.c_str(), get_errmsg());
472  return 0;
473  }
474  // Attach JMB39x tunnel
475  return get_jmb39x_device(jmbtype.c_str(), basedev.release());
476  }
477 
478  else if (str_starts_with(type, "intelliprop")) {
479  // Split "intelliprop...+base..." -> ("intelliprop...", "base...")
480  unsigned itllen = strcspn(type, "+");
481  std::string itltype(type, itllen);
482  const char * basetype = (type[itllen] ? type+itllen+1 : "");
483  // Recurse to allocate base device, default is standard ATA
484  if (!*basetype)
485  basetype = "ata";
486  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
487  if (!basedev) {
488  set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
489  return 0;
490  }
491  // Result must be ATA
492  if (!basedev->is_ata()) {
493  set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
494  return 0;
495  }
496  return get_intelliprop_device(itltype.c_str(), basedev.release()->to_ata());
497  }
498 
499  else {
500  set_err(EINVAL, "Unknown device type '%s'", type);
501  return 0;
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 
533 nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
534 {
535  set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
536  return 0;
537 }
538 
539 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
540 {
541  return 0;
542 }
543 
545 {
546  return "";
547 }
548 
550 {
551  if (!strncmp(type, "snt", 3)) {
552  return get_snt_device(type, scsidev);
553  }
554 
555  return get_sat_device(type, scsidev);
556 }
#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.
error_info m_err
const char * get_errmsg() const
Get last error message.
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.
virtual bool close()=0
Close device, return false on error.
const error_info & get_err() const
Get last error info struct.
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:399
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:1401
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_intelliprop_device(const char *type, ata_device *atadev)
Return filter for Intelliprop controllers.
int get_errno() const
Get last error number.
virtual ata_device * get_ata_device(const char *name, const char *type)=0
Return standard ATA device.
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'.
const char * get_errmsg() const
Get last error message.
virtual std::string get_valid_dev_types_str()
Return valid args for device type option/directive.
virtual smart_device * autodetect_smart_device(const char *name)=0
Autodetect device if no device type specified.
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.
virtual scsi_device * get_scsi_device(const char *name, const char *type)=0
Return standard SCSI 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
#define ENOTSUP
Definition: os_linux.cpp:87
int scsiSimpleSenseFilter(const struct scsi_sense_disect *sinfo)
Definition: scsicmds.cpp:272
const char * scsiErrString(int scsiErr)
Definition: scsicmds.cpp:310
void scsi_do_sense_disect(const struct scsi_cmnd_io *io_buf, struct scsi_sense_disect *out)
Definition: scsicmds.cpp:250
unsigned char scsi_debugmode
Definition: scsicmds.cpp:45
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:352
const char const char va_list ap
Definition: smartctl.cpp:1296
void pout(const char *fmt,...)
Definition: smartd.cpp:1308
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:108
size_t max_sense_len
Definition: scsicmds.h:110
unsigned timeout
Definition: scsicmds.h:111
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:772
std::string std::string vstrprintf(const char *fmt, va_list ap)
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:51