smartmontools  SVN Rev 4392
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-16 Christian Franke <smartmontools-support@lists.sourceforge.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * You should have received a copy of the GNU General Public License
14  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "config.h"
19 #include "int64.h"
20 #include "dev_interface.h"
21 #include "dev_intelliprop.h"
22 #include "dev_tunnelled.h"
23 #include "atacmds.h" // ATA_SMART_CMD/STATUS
24 #include "utility.h"
25 
26 #include <errno.h>
27 #include <stdarg.h>
28 #include <stdexcept>
29 
30 #if defined(HAVE_GETTIMEOFDAY)
31 #include <sys/time.h>
32 #elif defined(HAVE_FTIME)
33 #include <sys/timeb.h>
34 #endif
35 
36 const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4370 2017-01-11 20:35:38Z chrfranke $"
38 
39 /////////////////////////////////////////////////////////////////////////////
40 // smart_device
41 
43 
44 smart_device::smart_device(smart_interface * intf, const char * dev_name,
45  const char * dev_type, const char * req_type)
46 : m_intf(intf), m_info(dev_name, dev_type, req_type),
47  m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
48 {
49  s_num_objects++;
50 }
51 
53 : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
54 {
55  throw std::logic_error("smart_device: wrong constructor called in implementation class");
56 }
57 
59 {
60  s_num_objects--;
61 }
62 
64 {
65  if (get_errno() == ENOSYS)
66  return true;
67 #ifdef ENOTSUP
68  if (get_errno() == ENOTSUP)
69  return true;
70 #endif
71  return false;
72 }
73 
74 bool smart_device::set_err(int no, const char * msg, ...)
75 {
76  if (!msg)
77  return set_err(no);
78  m_err.no = no;
79  va_list ap; va_start(ap, msg);
80  m_err.msg = vstrprintf(msg, ap);
81  va_end(ap);
82  return false;
83 }
84 
86 {
87  return smi()->set_err_var(&m_err, no);
88 }
89 
91 {
92  open();
93  return this;
94 }
95 
97 {
98  return false;
99 }
100 
101 bool smart_device::owns(const smart_device * /*dev*/) const
102 {
103  return false;
104 }
105 
106 void smart_device::release(const smart_device * /*dev*/)
107 {
108 }
109 
110 
111 /////////////////////////////////////////////////////////////////////////////
112 // ata_device
113 
115 : features_16(features, prev.features),
116  sector_count_16(sector_count, prev.sector_count),
117  lba_low_16(lba_low, prev.lba_low),
118  lba_mid_16(lba_mid, prev.lba_mid),
119  lba_high_16(lba_high, prev.lba_high),
120  lba_48( lba_low, lba_mid, lba_high,
121  prev.lba_low, prev.lba_mid, prev.lba_high)
122 {
123 }
124 
126 : sector_count_16(sector_count, prev.sector_count),
127  lba_low_16(lba_low, prev.lba_low),
128  lba_mid_16(lba_mid, prev.lba_mid),
129  lba_high_16(lba_high, prev.lba_high),
130  lba_48( lba_low, lba_mid, lba_high,
131  prev.lba_low, prev.lba_mid, prev.lba_high)
132 {
133 }
134 
136 : direction(no_data),
137  buffer(0),
138  size(0)
139 {
140 }
141 
143 {
144 }
145 
147 {
148  ata_cmd_out dummy;
149  return ata_pass_through(in, dummy);
150 }
151 
153  unsigned flags, const char * type /* = 0 */)
154 {
155  // Check DATA IN/OUT
156  switch (in.direction) {
157  case ata_cmd_in::no_data: break;
158  case ata_cmd_in::data_in: break;
159  case ata_cmd_in::data_out: break;
160  default:
161  return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
162  }
163 
164  // Check buffer size
165  if (in.direction == ata_cmd_in::no_data) {
166  if (in.size)
167  return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
168  }
169  else {
170  if (!in.buffer)
171  return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
172  unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
173  // TODO: Add check for sector count == 0
174  if (count * 512 != in.size)
175  return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
176  }
177 
178  // Check features
179  const char * errmsg = 0;
180  if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out))
181  errmsg = "DATA OUT ATA commands not implemented";
182  else if ( in.out_needed.is_set() && !(flags & supports_output_regs)
183  && !( in.in_regs.command == ATA_SMART_CMD
185  && (flags & supports_smart_status)))
186  errmsg = "Read of ATA output registers not implemented";
187  else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
188  errmsg = "Multi-sector ATA commands not implemented";
189  else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit)))
190  errmsg = "48-bit ATA commands not implemented";
191  else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
192  errmsg = "48-bit ATA commands not fully implemented";
193 
194  if (errmsg)
195  return set_err(ENOSYS, "%s%s%s%s", errmsg,
196  (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
197 
198  return true;
199 }
200 
202 {
203  return false;
204 }
205 
206 
207 /////////////////////////////////////////////////////////////////////////////
208 // nvme_device
209 
210 bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
211 {
212  if (!status)
213  throw std::logic_error("nvme_device: set_nvme_err() called with status=0");
214 
215  out.status = status;
216  out.status_valid = true;
217  return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status);
218 }
219 
220 
221 /////////////////////////////////////////////////////////////////////////////
222 // tunnelled_device_base
223 
225 : smart_device(never_called),
226  m_tunnel_base_dev(tunnel_dev)
227 {
228 }
229 
231 {
232  delete m_tunnel_base_dev;
233 }
234 
236 {
238 }
239 
241 {
242  if (!m_tunnel_base_dev)
243  return set_err(ENOSYS);
244  if (!m_tunnel_base_dev->open())
245  return set_err(m_tunnel_base_dev->get_err());
246  return true;
247 }
248 
250 {
251  if (!m_tunnel_base_dev)
252  return true;
253  if (!m_tunnel_base_dev->close())
254  return set_err(m_tunnel_base_dev->get_err());
255  return true;
256 }
257 
259 {
260  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
261 }
262 
264 {
265  if (m_tunnel_base_dev == dev)
266  m_tunnel_base_dev = 0;
267 }
268 
269 
270 /////////////////////////////////////////////////////////////////////////////
271 // smart_interface
272 
273 // Pointer to (usually singleton) interface object returned by ::smi()
275 
277 {
278  return SMARTMONTOOLS_BUILD_HOST;
279 }
280 
282 {
283  // default
284  std::string s =
285  "ata, scsi, nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
286  "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, intelliprop,N[+TYPE]";
287  // append custom
288  std::string s2 = get_valid_custom_dev_types_str();
289  if (!s2.empty()) {
290  s += ", "; s += s2;
291  }
292  return s;
293 }
294 
295 std::string smart_interface::get_app_examples(const char * /*appname*/)
296 {
297  return "";
298 }
299 
301 {
302 #if defined(HAVE_GETTIMEOFDAY)
303  #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
304  {
305  static bool have_clock_monotonic = true;
306  if (have_clock_monotonic) {
307  struct timespec ts;
308  if (!clock_gettime(CLOCK_MONOTONIC, &ts))
309  return ts.tv_sec * 1000000LL + ts.tv_nsec/1000;
310  have_clock_monotonic = false;
311  }
312  }
313  #endif
314  {
315  struct timeval tv;
316  gettimeofday(&tv, 0);
317  return tv.tv_sec * 1000000LL + tv.tv_usec;
318  }
319 #elif defined(HAVE_FTIME)
320  {
321  struct timeb tb;
322  ftime(&tb);
323  return tb.time * 1000000LL + tb.millitm * 1000;
324  }
325 #else
326  return -1;
327 #endif
328 }
329 
331 {
332  return set_err(ENOSYS);
333 }
334 
335 bool smart_interface::set_err(int no, const char * msg, ...)
336 {
337  if (!msg)
338  return set_err(no);
339  m_err.no = no;
340  va_list ap; va_start(ap, msg);
341  m_err.msg = vstrprintf(msg, ap);
342  va_end(ap);
343  return false;
344 }
345 
347 {
348  return set_err_var(&m_err, no);
349 }
350 
352 {
353  err->no = no;
354  err->msg = get_msg_for_errno(no);
355  if (err->msg.empty() && no != 0)
356  err->msg = strprintf("Unknown error %d", no);
357  return false;
358 }
359 
361 {
362  return strerror(no);
363 }
364 
365 
366 /////////////////////////////////////////////////////////////////////////////
367 // Default device factory
368 
369 smart_device * smart_interface::get_smart_device(const char * name, const char * type)
370 {
371  clear_err();
372 
373  // Call platform specific autodetection if no device type specified
374  smart_device * dev;
375  if (!type || !*type) {
376  dev = autodetect_smart_device(name);
377  if (!dev && !get_errno())
378  set_err(EINVAL, "Unable to detect device type");
379  return dev;
380  }
381 
382  // First check for platform specific device types
383  dev = get_custom_smart_device(name, type);
384  if (dev || get_errno())
385  return dev;
386 
387  if (!strcmp(type, "ata"))
388  dev = get_ata_device(name, type);
389  else if (!strcmp(type, "scsi"))
390  dev = get_scsi_device(name, type);
391 
392  else if (str_starts_with(type, "nvme")) {
393  int n1 = -1, n2 = -1, len = strlen(type);
394  unsigned nsid = 0; // invalid namespace id -> use default
395  sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
396  if (!(n1 == len || n2 == len)) {
397  set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
398  return 0;
399  }
400  dev = get_nvme_device(name, type, nsid);
401  }
402 
403  else if ( ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
404  || (!strncmp(type, "usb", 3)))) {
405  // Split "sat...+base..." -> ("sat...", "base...")
406  unsigned satlen = strcspn(type, "+");
407  std::string sattype(type, satlen);
408  const char * basetype = (type[satlen] ? type+satlen+1 : "");
409  // Recurse to allocate base device, default is standard SCSI
410  if (!*basetype)
411  basetype = "scsi";
412  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
413  if (!basedev) {
414  set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
415  return 0;
416  }
417  // Result must be SCSI
418  if (!basedev->is_scsi()) {
419  set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
420  return 0;
421  }
422  // Attach SAT tunnel
423  return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
424  }
425 
426  else if (str_starts_with(type, "intelliprop")) {
427  // Parse "intelliprop,N[+base...]"
428  unsigned phydrive = ~0; int n = -1; char c = 0;
429  sscanf(type, "intelliprop,%u%n%c", &phydrive, &n, &c);
430  if (!((n == (int)strlen(type) || c == '+') && phydrive <= 3)) {
431  set_err(EINVAL, "Option '-d intelliprop,N' requires N between 0 and 3");
432  return 0;
433  }
434  const char * basetype = (type[n] ? type + n + 1 : "");
435  // Recurse to allocate base device, default is standard ATA
436  if (!*basetype)
437  basetype = "ata";
438  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
439  if (!basedev) {
440  set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
441  return 0;
442  }
443  // Result must be ATA
444  if (!basedev->is_ata()) {
445  set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
446  return 0;
447  }
448  return get_intelliprop_device(this, phydrive, basedev.release()->to_ata());
449  }
450 
451  else {
452  set_err(EINVAL, "Unknown device type '%s'", type);
453  return 0;
454  }
455  if (!dev && !get_errno())
456  set_err(EINVAL, "Not a device of type '%s'", type);
457  return dev;
458 }
459 
461  const smart_devtype_list & types, const char * pattern /* = 0 */)
462 {
463  unsigned n = types.size();
464  if (n == 0)
465  return scan_smart_devices(devlist, (const char *)0, pattern);
466  if (n == 1)
467  return scan_smart_devices(devlist, types.front().c_str(), pattern);
468 
469  for (unsigned i = 0; i < n; i++) {
470  smart_device_list tmplist;
471  if (!scan_smart_devices(tmplist, types[i].c_str(), pattern))
472  return false;
473  devlist.append(tmplist);
474  }
475 
476  return true;
477 }
478 
479 nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
480 {
481  set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
482  return 0;
483 }
484 
485 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
486 {
487  return 0;
488 }
489 
491 {
492  return "";
493 }
virtual int64_t get_timer_usec()
Get microseconds since some unspecified starting point.
Error (number,message) pair.
Definition: dev_interface.h:59
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:93
u16 s[6]
Definition: megaraid.h:97
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:79
std::string msg
Error message.
Definition: dev_interface.h:68
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:1486
do_not_use_in_implementation_classes
Dummy enum for dummy constructor.
Definition: dev_interface.h:79
ata_in_regs prev
"previous content"
#define ATA_SMART_CMD
Definition: atacmds.h:75
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:57
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.
u32 count
Definition: megaraid.h:80
#define ENOTSUP
Definition: os_linux.cpp:100
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
#define DEV_INTERFACE_H_CVSID
Definition: dev_interface.h:21
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
long long int64_t
Definition: int64.h:51
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'.
std::string vstrprintf(const char *fmt, va_list ap)
Definition: utility.cpp:742
ptr_t buffer
Definition: megaraid.h:89
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()
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:39
int no
Error number.
Definition: dev_interface.h:67
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:50
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:750
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:108