smartmontools  SVN Rev 4814
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: http://www.smartmontools.org
5  *
6  * Copyright (C) 2008-18 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_intelliprop.h"
15 #include "dev_tunnelled.h"
16 #include "atacmds.h" // ATA_SMART_CMD/STATUS
17 #include "utility.h"
18 
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <stdexcept>
22 
23 #if defined(HAVE_GETTIMEOFDAY)
24 #include <sys/time.h>
25 #elif defined(HAVE_FTIME)
26 #include <sys/timeb.h>
27 #endif
28 
29 const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
31 
32 /////////////////////////////////////////////////////////////////////////////
33 // smart_device
34 
36 
37 smart_device::smart_device(smart_interface * intf, const char * dev_name,
38  const char * dev_type, const char * req_type)
39 : m_intf(intf), m_info(dev_name, dev_type, req_type),
40  m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
41 {
42  s_num_objects++;
43 }
44 
46 : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
47 {
48  throw std::logic_error("smart_device: wrong constructor called in implementation class");
49 }
50 
52 {
53  s_num_objects--;
54 }
55 
57 {
58  if (get_errno() == ENOSYS)
59  return true;
60 #ifdef ENOTSUP
61  if (get_errno() == ENOTSUP)
62  return true;
63 #endif
64  return false;
65 }
66 
67 bool smart_device::set_err(int no, const char * msg, ...)
68 {
69  if (!msg)
70  return set_err(no);
71  m_err.no = no;
72  va_list ap; va_start(ap, msg);
73  m_err.msg = vstrprintf(msg, ap);
74  va_end(ap);
75  return false;
76 }
77 
79 {
80  return smi()->set_err_var(&m_err, no);
81 }
82 
84 {
85  open();
86  return this;
87 }
88 
90 {
91  return false;
92 }
93 
94 bool smart_device::owns(const smart_device * /*dev*/) const
95 {
96  return false;
97 }
98 
99 void smart_device::release(const smart_device * /*dev*/)
100 {
101 }
102 
103 
104 /////////////////////////////////////////////////////////////////////////////
105 // ata_device
106 
108 : features_16(features, prev.features),
109  sector_count_16(sector_count, prev.sector_count),
110  lba_low_16(lba_low, prev.lba_low),
111  lba_mid_16(lba_mid, prev.lba_mid),
112  lba_high_16(lba_high, prev.lba_high),
113  lba_48( lba_low, lba_mid, lba_high,
114  prev.lba_low, prev.lba_mid, prev.lba_high)
115 {
116 }
117 
119 : sector_count_16(sector_count, prev.sector_count),
120  lba_low_16(lba_low, prev.lba_low),
121  lba_mid_16(lba_mid, prev.lba_mid),
122  lba_high_16(lba_high, prev.lba_high),
123  lba_48( lba_low, lba_mid, lba_high,
124  prev.lba_low, prev.lba_mid, prev.lba_high)
125 {
126 }
127 
129 : direction(no_data),
130  buffer(0),
131  size(0)
132 {
133 }
134 
136 {
137 }
138 
140 {
141  ata_cmd_out dummy;
142  return ata_pass_through(in, dummy);
143 }
144 
146  unsigned flags, const char * type /* = 0 */)
147 {
148  // Check DATA IN/OUT
149  switch (in.direction) {
150  case ata_cmd_in::no_data: break;
151  case ata_cmd_in::data_in: break;
152  case ata_cmd_in::data_out: break;
153  default:
154  return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
155  }
156 
157  // Check buffer size
158  if (in.direction == ata_cmd_in::no_data) {
159  if (in.size)
160  return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
161  }
162  else {
163  if (!in.buffer)
164  return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
165  unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
166  // TODO: Add check for sector count == 0
167  if (count * 512 != in.size)
168  return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
169  }
170 
171  // Check features
172  const char * errmsg = 0;
173  if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out))
174  errmsg = "DATA OUT ATA commands not implemented";
175  else if ( in.out_needed.is_set() && !(flags & supports_output_regs)
176  && !( in.in_regs.command == ATA_SMART_CMD
178  && (flags & supports_smart_status)))
179  errmsg = "Read of ATA output registers not implemented";
180  else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
181  errmsg = "Multi-sector ATA commands not implemented";
182  else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit)))
183  errmsg = "48-bit ATA commands not implemented";
184  else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
185  errmsg = "48-bit ATA commands not fully implemented";
186 
187  if (errmsg)
188  return set_err(ENOSYS, "%s%s%s%s", errmsg,
189  (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
190 
191  return true;
192 }
193 
195 {
196  return false;
197 }
198 
199 
200 /////////////////////////////////////////////////////////////////////////////
201 // nvme_device
202 
203 bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
204 {
205  if (!status)
206  throw std::logic_error("nvme_device: set_nvme_err() called with status=0");
207 
208  out.status = status;
209  out.status_valid = true;
210  return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status);
211 }
212 
213 
214 /////////////////////////////////////////////////////////////////////////////
215 // tunnelled_device_base
216 
218 : smart_device(never_called),
219  m_tunnel_base_dev(tunnel_dev)
220 {
221 }
222 
224 {
225  delete m_tunnel_base_dev;
226 }
227 
229 {
231 }
232 
234 {
235  if (!m_tunnel_base_dev)
236  return set_err(ENOSYS);
237  if (!m_tunnel_base_dev->open())
238  return set_err(m_tunnel_base_dev->get_err());
239  return true;
240 }
241 
243 {
244  if (!m_tunnel_base_dev)
245  return true;
246  if (!m_tunnel_base_dev->close())
247  return set_err(m_tunnel_base_dev->get_err());
248  return true;
249 }
250 
252 {
253  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
254 }
255 
257 {
258  if (m_tunnel_base_dev == dev)
259  m_tunnel_base_dev = 0;
260 }
261 
262 
263 /////////////////////////////////////////////////////////////////////////////
264 // smart_interface
265 
266 // Pointer to (usually singleton) interface object returned by ::smi()
268 
270 {
271  return SMARTMONTOOLS_BUILD_HOST;
272 }
273 
275 {
276  // default
277  std::string s =
278  "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
279  "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, intelliprop,N[+TYPE]";
280  // append custom
281  std::string s2 = get_valid_custom_dev_types_str();
282  if (!s2.empty()) {
283  s += ", "; s += s2;
284  }
285  return s;
286 }
287 
288 std::string smart_interface::get_app_examples(const char * /*appname*/)
289 {
290  return "";
291 }
292 
294 {
295 #if defined(HAVE_GETTIMEOFDAY)
296  #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
297  {
298  static bool have_clock_monotonic = true;
299  if (have_clock_monotonic) {
300  struct timespec ts;
301  if (!clock_gettime(CLOCK_MONOTONIC, &ts))
302  return ts.tv_sec * 1000000LL + ts.tv_nsec/1000;
303  have_clock_monotonic = false;
304  }
305  }
306  #endif
307  {
308  struct timeval tv;
309  gettimeofday(&tv, 0);
310  return tv.tv_sec * 1000000LL + tv.tv_usec;
311  }
312 #elif defined(HAVE_FTIME)
313  {
314  struct timeb tb;
315  ftime(&tb);
316  return tb.time * 1000000LL + tb.millitm * 1000;
317  }
318 #else
319  return -1;
320 #endif
321 }
322 
324 {
325  return set_err(ENOSYS);
326 }
327 
328 bool smart_interface::set_err(int no, const char * msg, ...)
329 {
330  if (!msg)
331  return set_err(no);
332  m_err.no = no;
333  va_list ap; va_start(ap, msg);
334  m_err.msg = vstrprintf(msg, ap);
335  va_end(ap);
336  return false;
337 }
338 
340 {
341  return set_err_var(&m_err, no);
342 }
343 
345 {
346  err->no = no;
347  err->msg = get_msg_for_errno(no);
348  if (err->msg.empty() && no != 0)
349  err->msg = strprintf("Unknown error %d", no);
350  return false;
351 }
352 
354 {
355  return strerror(no);
356 }
357 
358 
359 /////////////////////////////////////////////////////////////////////////////
360 // Default device factory
361 
362 smart_device * smart_interface::get_smart_device(const char * name, const char * type)
363 {
364  clear_err();
365 
366  // Call platform specific autodetection if no device type specified
367  smart_device * dev;
368  if (!type || !*type) {
369  dev = autodetect_smart_device(name);
370  if (!dev && !get_errno())
371  set_err(EINVAL, "Unable to detect device type");
372  return dev;
373  }
374 
375  // First check for platform specific device types
376  dev = get_custom_smart_device(name, type);
377  if (dev || get_errno())
378  return dev;
379 
380  if (!strcmp(type, "ata"))
381  dev = get_ata_device(name, type);
382  else if (!strcmp(type, "scsi"))
383  dev = get_scsi_device(name, type);
384 
385  else if (str_starts_with(type, "nvme")) {
386  int n1 = -1, n2 = -1, len = strlen(type);
387  unsigned nsid = 0; // invalid namespace id -> use default
388  sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
389  if (!(n1 == len || n2 == len)) {
390  set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
391  return 0;
392  }
393  dev = get_nvme_device(name, type, nsid);
394  }
395 
396  else if ( (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
397  || str_starts_with(type, "scsi+")
398  || str_starts_with(type, "usb") ) {
399  // Split "sat...+base..." -> ("sat...", "base...")
400  unsigned satlen = strcspn(type, "+");
401  std::string sattype(type, satlen);
402  const char * basetype = (type[satlen] ? type+satlen+1 : "");
403  // Recurse to allocate base device, default is standard SCSI
404  if (!*basetype)
405  basetype = "scsi";
406  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
407  if (!basedev) {
408  set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
409  return 0;
410  }
411  // Result must be SCSI
412  if (!basedev->is_scsi()) {
413  set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
414  return 0;
415  }
416  // Attach SAT tunnel
417  return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
418  }
419 
420  else if (str_starts_with(type, "intelliprop")) {
421  // Parse "intelliprop,N[+base...]"
422  unsigned phydrive = ~0; int n = -1; char c = 0;
423  sscanf(type, "intelliprop,%u%n%c", &phydrive, &n, &c);
424  if (!((n == (int)strlen(type) || c == '+') && phydrive <= 3)) {
425  set_err(EINVAL, "Option '-d intelliprop,N' requires N between 0 and 3");
426  return 0;
427  }
428  const char * basetype = (type[n] ? type + n + 1 : "");
429  // Recurse to allocate base device, default is standard ATA
430  if (!*basetype)
431  basetype = "ata";
432  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
433  if (!basedev) {
434  set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
435  return 0;
436  }
437  // Result must be ATA
438  if (!basedev->is_ata()) {
439  set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
440  return 0;
441  }
442  return get_intelliprop_device(this, phydrive, basedev.release()->to_ata());
443  }
444 
445  else {
446  set_err(EINVAL, "Unknown device type '%s'", type);
447  return 0;
448  }
449  if (!dev && !get_errno())
450  set_err(EINVAL, "Not a device of type '%s'", type);
451  return dev;
452 }
453 
455  const smart_devtype_list & types, const char * pattern /* = 0 */)
456 {
457  unsigned n = types.size();
458  if (n == 0)
459  return scan_smart_devices(devlist, (const char *)0, pattern);
460  if (n == 1)
461  return scan_smart_devices(devlist, types.front().c_str(), pattern);
462 
463  for (unsigned i = 0; i < n; i++) {
464  smart_device_list tmplist;
465  if (!scan_smart_devices(tmplist, types[i].c_str(), pattern))
466  return false;
467  devlist.append(tmplist);
468  }
469 
470  return true;
471 }
472 
473 nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
474 {
475  set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
476  return 0;
477 }
478 
479 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
480 {
481  return 0;
482 }
483 
485 {
486  return "";
487 }
virtual int64_t get_timer_usec()
Get microseconds since some unspecified starting point.
Error (number,message) pair.
Definition: dev_interface.h:52
int get_errno() const
Get last error number.
virtual void release(const smart_device *dev)
Release ownership of other device.
u16 flags
Definition: megaraid.h:103
u16 s[6]
Definition: megaraid.h:107
ata_out_regs_flags out_needed
True if output register value needed.
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.
u32 size
Definition: megaraid.h:89
std::string msg
Error message.
Definition: dev_interface.h:61
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:1480
do_not_use_in_implementation_classes
Dummy enum for dummy constructor.
Definition: dev_interface.h:72
ata_in_regs prev
"previous content"
#define ATA_SMART_CMD
Definition: atacmds.h:63
smart_device::error_info m_err
tunnelled_device_base(smart_device *tunnel_dev)
int get_errno() const
Get last error number.
virtual void release(const smart_device *dev)
Release ownership of other device.
virtual bool open()=0
Open device, return false on error.
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:52
void clear_err()
Clear last error info.
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.
Smart pointer class for device pointers.
virtual bool is_powered_down()
Early test if device is powered up or down.
virtual const char * get_msg_for_errno(int no)
Convert error number into message, used by set_err(no).
virtual std::string get_valid_custom_dev_types_str()
Return valid 'type' args accepted by above.
virtual bool close()=0
Close device, return false on error.
const char const char va_list ap if(!print_as_json)
Definition: smartctl.cpp:1271
u32 count
Definition: megaraid.h:90
#define ENOTSUP
Definition: os_linux.cpp:92
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
#define DEV_INTERFACE_H_CVSID
Definition: dev_interface.h:14
bool is_real_48bit_cmd() const
Return true if 48-bit command with any nonzero high byte.
virtual smart_device * get_smart_device(const char *name, const char *type)
Return device object for device 'name' with some 'type'.
bool ata_cmd_is_supported(const ata_cmd_in &in, unsigned flags, const char *type=0)
Check command input parameters.
uint32_t nsid
ata_in_regs_48bit in_regs
Input registers.
virtual ata_device * get_ata_device(const char *name, const char *type)=0
Return standard ATA device.
List of devices for DEVICESCAN.
device_type * release()
Return the pointer and release ownership.
bool set_err_var(smart_device::error_info *err, int no)
Set last error number and default message to any error_info.
virtual bool is_open() const =0
Return true if device is open.
enum ata_cmd_in::@29 direction
I/O direction.
The platform interface abstraction.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)=0
ATA pass through.
std::vector< std::string > smart_devtype_list
List of types for DEVICESCAN.
NVMe device access.
bool status_valid
true if status is valid
error_info m_err
static smart_interface * s_instance
Pointer to the interface object.
virtual bool open()
Open device, return false on error.
virtual smart_device * autodetect_smart_device(const char *name)=0
Autodetect device if no device type specified.
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid)
Return standard NVMe device.
virtual smart_device * get_custom_smart_device(const char *name, const char *type)
Return device for platform specific 'type'.
virtual std::string get_app_examples(const char *appname)
Return example string for program 'appname'.
ptr_t buffer
Definition: megaraid.h:99
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
bool is_set() const
Return true if any flag is set.
virtual bool owns(const smart_device *dev) const
Return true if other device is owned by this device.
unsigned size
Size of buffer.
virtual ~tunnelled_device_base()
std::string std::string vstrprintf(const char *fmt, va_list ap)
ata_register features
bool is_48bit_cmd() const
Return true if 48-bit command.
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0)=0
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'...
Base class for all devices.
Definition: dev_interface.h:32
int no
Error number.
Definition: dev_interface.h:60
ata_register sector_count
virtual bool disable_system_auto_standby(bool disable)
Disable/Enable system auto standby/sleep mode.
smart_device * m_tunnel_base_dev
Definition: dev_tunnelled.h:43
ata_device * get_intelliprop_device(smart_interface *intf, unsigned phydrive, ata_device *atadev)
virtual scsi_device * get_scsi_device(const char *name, const char *type)=0
Return standard SCSI device.
ata_register command
virtual bool close()
Close device, return false on error.
virtual smart_device * autodetect_open()
Open device with autodetection support.
ATA pass through input parameters.
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:728
const char * dev_interface_cpp_cvsid
virtual ~smart_device()
void append(smart_device_list &devlist)
smart_interface * smi()
Get interface which produced this object.
const error_info & get_err() const
Get last error info struct.
unsigned short status
Status Field (DW3 31:17)
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
virtual bool is_open() const
Return true if device is open.
virtual bool ata_identify_is_cached() const
Return true if OS caches ATA identify sector.
virtual bool is_syscall_unsup() const
Return true if last error indicates an unsupported system call.
ATA pass through output parameters.
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 owns(const smart_device *dev) const
Return true if other device is owned by this device.
static int s_num_objects
virtual std::string get_os_version_str()
Return info string about build host and/or OS version.
#define ATA_SMART_STATUS
Definition: atacmds.h:98