smartmontools  SVN Rev 5304
Utility to control and monitor storage systems with "S.M.A.R.T."
os_netbsd.cpp
Go to the documentation of this file.
1 /*
2  * os_netbsd.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2003-8 Sergey Svishchev
7  * Copyright (C) 2016 Kimihiro Nonaka
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 #include "config.h"
13 
14 #include "atacmds.h"
15 #include "scsicmds.h"
16 #include "utility.h"
17 #include "os_netbsd.h"
18 
19 #include <sys/drvctlio.h>
20 #include <sys/utsname.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <unistd.h>
24 
25 // based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
26 #include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
27 
28 const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 5198 2021-02-01 20:36:02Z chrfranke $"
30 
31 #define ARGUSED(x) ((void)(x))
32 
33 /////////////////////////////////////////////////////////////////////////////
34 
35 namespace os_netbsd { // No need to publish anything, name provided for Doxygen
36 
37 static const char *net_dev_prefix = "/dev/";
38 static const char *net_dev_raw_prefix = "/dev/r";
39 static const char *net_dev_ata_disk = "wd";
40 static const char *net_dev_scsi_disk = "sd";
41 static const char *net_dev_scsi_tape = "enrst";
42 static const char *net_dev_nvme_ctrl = "nvme";
43 
44 /////////////////////////////////////////////////////////////////////////////
45 /// Implement shared open/close routines with old functions.
46 
48 : virtual public /*implements*/ smart_device
49 {
50 public:
53  m_fd(-1) { }
54 
55  virtual ~netbsd_smart_device();
56 
57  virtual bool is_open() const;
58 
59  virtual bool open();
60 
61  virtual bool close();
62 
63 protected:
64  /// Return filedesc for derived classes.
65  int get_fd() const
66  { return m_fd; }
67 
68  void set_fd(int fd)
69  { m_fd = fd; }
70 
71 private:
72  int m_fd; ///< filedesc, -1 if not open.
73 };
74 
76 {
77  if (m_fd >= 0)
79 }
80 
82 {
83  return (m_fd >= 0);
84 }
85 
86 
88 {
89  const char *dev = get_dev_name();
90  int fd;
91 
92  if (is_scsi()) {
93  fd = ::open(dev,O_RDWR|O_NONBLOCK);
94  if (fd < 0 && errno == EROFS)
95  fd = ::open(dev,O_RDONLY|O_NONBLOCK);
96  if (fd < 0) {
97  set_err(errno);
98  return false;
99  }
100  } else if (is_ata() || is_nvme()) {
101  if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) {
102  set_err(errno);
103  return false;
104  }
105  } else
106  return false;
107 
108  set_fd(fd);
109  return true;
110 }
111 
113 {
114  int failed = 0;
115  // close device, if open
116  if (is_open())
117  failed=::close(get_fd());
118 
119  set_fd(-1);
120 
121  if(failed) return false;
122  else return true;
123 }
124 
125 /////////////////////////////////////////////////////////////////////////////
126 /// Implement standard ATA support
127 
129 : public /*implements*/ ata_device,
130  public /*extends*/ netbsd_smart_device
131 {
132 public:
133  netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
134  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
135 
136 protected:
137  virtual int do_cmd(struct atareq* request, bool is_48bit_cmd);
138 };
139 
140 netbsd_ata_device::netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
141 : smart_device(intf, dev_name, "ata", req_type),
143 {
144 }
145 
146 int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd)
147 {
148  int fd = get_fd(), ret;
149  ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the ATAIOCCOMMAND
150  ret = ioctl(fd, ATAIOCCOMMAND, request);
151  if (ret) set_err(errno);
152  return ret;
153 }
154 
156 {
157  bool ata_48bit = false; // no ata_48bit_support via ATAIOCCOMMAND
158 
159  if (!ata_cmd_is_ok(in,
160  true, // data_out_support
161  true, // multi_sector_support
162  ata_48bit)
163  ) {
164  set_err(ENOSYS, "48-bit ATA commands not implemented");
165  return false;
166  }
167 
168  struct atareq req;
169  memset(&req, 0, sizeof(req));
170 
171  req.timeout = SCSI_TIMEOUT_DEFAULT * 1000;
172  req.command = in.in_regs.command;
173  req.features = in.in_regs.features;
174  req.sec_count = in.in_regs.sector_count;
175  req.sec_num = in.in_regs.lba_low;
176  req.head = in.in_regs.device;
177  req.cylinder = in.in_regs.lba_mid | (in.in_regs.lba_high << 8);
178 
179  switch (in.direction) {
180  case ata_cmd_in::no_data:
181  req.flags = ATACMD_READREG;
182  break;
183  case ata_cmd_in::data_in:
184  req.flags = ATACMD_READ | ATACMD_READREG;
185  req.databuf = (char *)in.buffer;
186  req.datalen = in.size;
187  break;
189  req.flags = ATACMD_WRITE | ATACMD_READREG;
190  req.databuf = (char *)in.buffer;
191  req.datalen = in.size;
192  break;
193  default:
194  return set_err(ENOSYS);
195  }
196 
197  clear_err();
198  errno = 0;
199  if (do_cmd(&req, in.in_regs.is_48bit_cmd()))
200  return false;
201  if (req.retsts != ATACMD_OK)
202  return set_err(EIO, "request failed, error code 0x%02x", req.retsts);
203 
204  out.out_regs.error = req.error;
205  out.out_regs.sector_count = req.sec_count;
206  out.out_regs.lba_low = req.sec_num;
207  out.out_regs.device = req.head;
208  out.out_regs.lba_mid = req.cylinder;
209  out.out_regs.lba_high = req.cylinder >> 8;
210  out.out_regs.status = req.command;
211  /* Undo byte-swapping for IDENTIFY */
213  for (int i = 0; i < 256; i+=2)
214  swap2 ((char *)req.databuf + i);
215  }
216  return true;
217 }
218 
219 /////////////////////////////////////////////////////////////////////////////
220 /// NVMe support
221 
223 : public /*implements*/ nvme_device,
224  public /*extends*/ netbsd_smart_device
225 {
226 public:
227  netbsd_nvme_device(smart_interface * intf, const char * dev_name,
228  const char * req_type, unsigned nsid);
229 
230  virtual bool open() override;
231 
232  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) override;
233 };
234 
236  const char * req_type, unsigned nsid)
237 : smart_device(intf, dev_name, "nvme", req_type),
238  nvme_device(nsid),
240 {
241 }
242 
244 {
245  const char *dev = get_dev_name();
246  if (strncmp(dev, NVME_PREFIX, strlen(NVME_PREFIX))) {
247  set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'",
248  NVME_PREFIX);
249  return false;
250  }
251 
252  int nsid = -1, ctrlid = -1;
253  char tmp;
254 
255  if(sscanf(dev, NVME_PREFIX"%d%c", &ctrlid, &tmp) == 1)
256  {
257  if(ctrlid < 0) {
258  set_err(EINVAL, "Invalid NVMe controller number");
259  return false;
260  }
261  nsid = 0xFFFFFFFF; // broadcast id
262  }
263  else if (sscanf(dev, NVME_PREFIX "%d" NVME_NS_PREFIX "%d%c",
264  &ctrlid, &nsid, &tmp) == 2)
265  {
266  if(ctrlid < 0 || nsid <= 0) {
267  set_err(EINVAL, "Invalid NVMe controller/namespace number");
268  return false;
269  }
270  }
271  else {
272  set_err(EINVAL, "Invalid NVMe controller/namespace syntax");
273  return false;
274  }
275 
276  // we should always open controller, not namespace device
277  char full_path[64];
278  snprintf(full_path, sizeof(full_path), NVME_PREFIX"%d", ctrlid);
279 
280  int fd;
281  if ((fd = ::open(full_path, O_RDWR))<0) {
282  set_err(errno);
283  return false;
284  }
285  set_fd(fd);
286 
287  if (!get_nsid()) {
288  set_nsid(nsid);
289  }
290 
291  return true;
292 }
293 
295 {
296  struct nvme_pt_command pt;
297  memset(&pt, 0, sizeof(pt));
298 
299  pt.cmd.opcode = in.opcode;
300  pt.cmd.nsid = in.nsid;
301  pt.buf = in.buffer;
302  pt.len = in.size;
303  pt.cmd.cdw10 = in.cdw10;
304  pt.cmd.cdw11 = in.cdw11;
305  pt.cmd.cdw12 = in.cdw12;
306  pt.cmd.cdw13 = in.cdw13;
307  pt.cmd.cdw14 = in.cdw14;
308  pt.cmd.cdw15 = in.cdw15;
309  pt.is_read = 1; // should we use in.direction()?
310 
311  int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt);
312 
313  if (status < 0)
314  return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno));
315 
316  out.result=pt.cpl.cdw0; // Command specific result (DW0)
317 
318  if (nvme_completion_is_error(&pt.cpl))
319  return set_nvme_err(out, nvme_completion_is_error(&pt.cpl));
320 
321  return true;
322 }
323 
324 /////////////////////////////////////////////////////////////////////////////
325 /// Standard SCSI support
326 
328 : public /*implements*/ scsi_device,
329  public /*extends*/ netbsd_smart_device
330 {
331 public:
332  netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning = false);
333 
334  virtual smart_device * autodetect_open() override;
335 
336  virtual bool scsi_pass_through(scsi_cmnd_io * iop) override;
337 
338 private:
339  bool m_scanning; ///< true if created within scan_smart_devices
340 };
341 
343  const char * dev_name, const char * req_type, bool scanning /* = false */)
344 : smart_device(intf, dev_name, "scsi", req_type),
346  m_scanning(scanning)
347 {
348 }
349 
351 {
352  struct scsireq sc;
353  int fd = get_fd();
354 
355  if (scsi_debugmode) {
356  unsigned int k;
357  const unsigned char * ucp = iop->cmnd;
358  const char * np;
359 
360  np = scsi_get_opcode_name(ucp[0]);
361  pout(" [%s: ", np ? np : "<unknown opcode>");
362  for (k = 0; k < iop->cmnd_len; ++k)
363  pout("%02x ", ucp[k]);
364  if ((scsi_debugmode > 1) &&
365  (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
366  int trunc = (iop->dxfer_len > 256) ? 1 : 0;
367 
368  pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
369  (trunc ? " [only first 256 bytes shown]" : ""));
370  dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
371  }
372  else
373  pout("]\n");
374  }
375 
376  memset(&sc, 0, sizeof(sc));
377  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
378  sc.cmdlen = iop->cmnd_len;
379  sc.databuf = (char *)iop->dxferp;
380  sc.datalen = iop->dxfer_len;
381  sc.senselen = iop->max_sense_len;
382  sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
383  sc.flags =
384  (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
385  (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
386 
387  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
388  if (scsi_debugmode) {
389  pout(" error sending SCSI ccb\n");
390  }
391  return set_err(EIO);
392  }
393  iop->resid = sc.datalen - sc.datalen_used;
394  iop->scsi_status = sc.status;
395  if (iop->sensep) {
396  memcpy(iop->sensep, sc.sense, sc.senselen_used);
397  iop->resp_sense_len = sc.senselen_used;
398  }
399  if (scsi_debugmode) {
400  int trunc;
401 
402  pout(" status=0\n");
403  trunc = (iop->dxfer_len > 256) ? 1 : 0;
404 
405  pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
406  (trunc ? " [only first 256 bytes shown]" : ""));
407  dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
408  }
409  switch (sc.retsts) {
410  case SCCMD_OK:
411  break;
412  case SCCMD_TIMEOUT:
413  return set_err(ETIMEDOUT);
414  case SCCMD_BUSY:
415  return set_err(EBUSY);
416  default:
417  return set_err(EIO);
418  }
419 
420  return true;
421 }
422 
423 /////////////////////////////////////////////////////////////////////////////
424 ///// SCSI open with autodetection support
425 
427 {
428  // Open device
429  if (!open())
430  return this;
431 
432  // No Autodetection if device type was specified by user
433  bool sat_only = false;
434  if (*get_req_type()) {
435  // Detect SAT if device object was created by scan_smart_devices().
436  if (!(m_scanning && !strcmp(get_req_type(), "sat")))
437  return this;
438  sat_only = true;
439  }
440 
441  // The code below is based on smartd.cpp:SCSIFilterKnown()
442 
443  // Get INQUIRY
444  unsigned char req_buff[64] = {0, };
445  int req_len = 36;
446  if (scsiStdInquiry(this, req_buff, req_len)) {
447  // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
448  // watch this spot ... other devices could lock up here
449  req_len = 64;
450  if (scsiStdInquiry(this, req_buff, req_len)) {
451  // device doesn't like INQUIRY commands
452  close();
453  set_err(EIO, "INQUIRY failed");
454  return this;
455  }
456  }
457 
458  int avail_len = req_buff[4] + 5;
459  int len = (avail_len < req_len ? avail_len : req_len);
460  if (len < 36) {
461  if (sat_only) {
462  close();
463  set_err(EIO, "INQUIRY too short for SAT");
464  }
465  return this;
466  }
467 
468  // Use INQUIRY to detect type
469 
470  // SAT or USB, skip MFI controllers because of bugs
471  {
472  smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
473  if (newdev) {
474  // NOTE: 'this' is now owned by '*newdev'
475  return newdev;
476  }
477  }
478 
479  // Nothing special found
480 
481  if (sat_only) {
482  close();
483  set_err(EIO, "Not a SAT device");
484  }
485  return this;
486 }
487 
488 /////////////////////////////////////////////////////////////////////////////
489 /// Implement platform interface with old functions.
490 
492 : public /*implements*/ smart_interface
493 {
494 public:
495  virtual std::string get_os_version_str() override;
496 
497  virtual std::string get_app_examples(const char * appname) override;
498 
499  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
500  const char * pattern = 0) override;
501 
502 protected:
503  virtual ata_device * get_ata_device(const char * name, const char * type) override;
504 
505  virtual scsi_device * get_scsi_device(const char * name, const char * type) override;
506 
507  virtual nvme_device * get_nvme_device(const char * name, const char * type,
508  unsigned nsid) override;
509 
510  virtual smart_device * autodetect_smart_device(const char * name) override;
511 
512  virtual smart_device * get_custom_smart_device(const char * name, const char * type) override;
513 
514  virtual std::string get_valid_custom_dev_types_str() override;
515 
516 private:
517  int get_dev_names(char ***, const char *);
518 
519  bool get_nvme_devlist(smart_device_list & devlist, const char * type);
520 };
521 
522 
523 //////////////////////////////////////////////////////////////////////
524 
526 {
527  struct utsname osname;
528  uname(&osname);
529  return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
530 }
531 
532 std::string netbsd_smart_interface::get_app_examples(const char * appname)
533 {
534  if (!strcmp(appname, "smartctl")) {
535  char p;
536 
537  p = 'a' + getrawpartition();
538  return strprintf(
539  "=================================================== SMARTCTL EXAMPLES =====\n\n"
540  " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
541  " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
542  " (Enables SMART on first disk)\n\n"
543  " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
544  " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
545  " (Prints Self-Test & Attribute errors)\n"
546  , p, p, p, p);
547  }
548  return "";
549 }
550 
551 ata_device * netbsd_smart_interface::get_ata_device(const char * name, const char * type)
552 {
553  return new netbsd_ata_device(this, name, type);
554 }
555 
556 scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type)
557 {
558  return new netbsd_scsi_device(this, name, type);
559 }
560 
561 nvme_device * netbsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid)
562 {
563  return new netbsd_nvme_device(this, name, type, nsid);
564 }
565 
566 int netbsd_smart_interface::get_dev_names(char ***names, const char *prefix)
567 {
568  char *disknames, *p, **mp;
569  int n = 0;
570  int sysctl_mib[2];
571  size_t sysctl_len;
572 
573  *names = NULL;
574 
575  sysctl_mib[0] = CTL_HW;
576  sysctl_mib[1] = HW_DISKNAMES;
577  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
578  pout("Failed to get value of sysctl `hw.disknames'\n");
579  return -1;
580  }
581  if (!(disknames = (char *)malloc(sysctl_len))) {
582  pout("Out of memory constructing scan device list\n");
583  return -1;
584  }
585  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
586  pout("Failed to get value of sysctl `hw.disknames'\n");
587  return -1;
588  }
589  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
590  pout("Out of memory constructing scan device list\n");
591  return -1;
592  }
593  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
594  if (strncmp(p, prefix, strlen(prefix))) {
595  continue;
596  }
597  mp[n] = (char *)malloc(strlen(net_dev_raw_prefix) + strlen(p) + 2);
598  if (!mp[n]) {
599  pout("Out of memory constructing scan device list\n");
600  return -1;
601  }
602  sprintf(mp[n], "%s%s%c", net_dev_raw_prefix, p, 'a' + getrawpartition());
603  n++;
604  }
605  free(disknames);
606 
607  if (n == 0) {
608  free(mp);
609  return 0;
610  }
611 
612  char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
613  if (NULL == tmp) {
614  pout("Out of memory constructing scan device list\n");
615  free(mp);
616  return -1;
617  }
618  else
619  mp = tmp;
620  *names = mp;
621  return n;
622 }
623 
625  const char * type)
626 {
627  char ctrlpath[64], nspath[64];
628  struct stat sb;
629  struct devlistargs laa;
630  nvme_device * nvmedev;
631 
632  int drvfd = ::open(DRVCTLDEV, O_RDONLY, 0);
633  if (drvfd < 0) {
634  set_err(errno);
635  return false;
636  }
637 
638  for (int ctrl = 0;; ctrl++) {
639  snprintf(ctrlpath, sizeof(ctrlpath), NVME_PREFIX"%d", ctrl);
640  if (stat(ctrlpath, &sb) == -1 || !S_ISCHR(sb.st_mode))
641  break;
642 
643  snprintf(laa.l_devname, sizeof(laa.l_devname), "%s%d", net_dev_nvme_ctrl,
644  ctrl);
645  laa.l_childname = NULL;
646  laa.l_children = 0;
647  if (ioctl(drvfd, DRVLISTDEV, &laa) == -1) {
648  if (errno == ENXIO)
649  continue;
650  break;
651  }
652 
653  nvmedev = get_nvme_device(ctrlpath, type, 0);
654  if (nvmedev)
655  devlist.push_back(nvmedev);
656 
657  uint32_t n = 0;
658  for (int nsid = 1; n < laa.l_children; nsid++) {
659  snprintf(nspath, sizeof(nspath), NVME_PREFIX "%d" NVME_NS_PREFIX "%d",
660  ctrl, nsid);
661  if (stat(nspath, &sb) == -1 || !S_ISCHR(sb.st_mode))
662  break;
663  int nsfd = ::open(nspath, O_RDONLY, 0);
664  if (nsfd < 0)
665  continue;
666  ::close(nsfd);
667 
668  n++;
669  nvmedev = get_nvme_device(nspath, type, nsid);
670  if (nvmedev)
671  devlist.push_back(nvmedev);
672  }
673  }
674 
675  ::close(drvfd);
676  return true;
677 }
678 
680  const char * type, const char * pattern /*= 0*/)
681 {
682  if (pattern) {
683  set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
684  return false;
685  }
686 
687  if (type == NULL)
688  type = "";
689 
690  bool scan_ata = !*type || !strcmp(type, "ata");
691  bool scan_scsi = !*type || !strcmp(type, "scsi") || !strcmp(type, "sat");
692 
693 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
694  bool scan_nvme = !*type || !strcmp(type, "nvme");
695 #else
696  bool scan_nvme = !strcmp(type, "nvme");
697 #endif
698 
699  // Make namelists
700  char * * atanames = 0; int numata = 0;
701  if (scan_ata) {
702  numata = get_dev_names(&atanames, net_dev_ata_disk);
703  if (numata < 0) {
704  set_err(ENOMEM);
705  return false;
706  }
707  }
708 
709  char * * scsinames = 0; int numscsi = 0;
710  char * * scsitapenames = 0; int numscsitape = 0;
711  if (scan_scsi) {
712  numscsi = get_dev_names(&scsinames, net_dev_scsi_disk);
713  if (numscsi < 0) {
714  set_err(ENOMEM);
715  return false;
716  }
717  numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape);
718  if (numscsitape < 0) {
719  set_err(ENOMEM);
720  return false;
721  }
722  }
723 
724  // Add to devlist
725  int i;
726  for (i = 0; i < numata; i++) {
727  ata_device * atadev = get_ata_device(atanames[i], type);
728  if (atadev)
729  devlist.push_back(atadev);
730  free(atanames[i]);
731  }
732  if(numata) free(atanames);
733 
734  for (i = 0; i < numscsi; i++) {
735  scsi_device * scsidev = new netbsd_scsi_device(this, scsinames[i], type, true /*scanning*/);
736  if (scsidev)
737  devlist.push_back(scsidev);
738  free(scsinames[i]);
739  }
740  if(numscsi) free(scsinames);
741 
742  for (i = 0; i < numscsitape; i++) {
743  scsi_device * scsidev = get_scsi_device(scsitapenames[i], type);
744  if (scsidev)
745  devlist.push_back(scsidev);
746  free(scsitapenames[i]);
747  }
748  if(numscsitape) free(scsitapenames);
749 
750  if (scan_nvme)
751  get_nvme_devlist(devlist, type);
752 
753  return true;
754 }
755 
757 {
758  const char * test_name = name;
759 
760  // if dev_name null, or string length zero
761  if (!name || !*name)
762  return 0;
763 
764  // Dereference symlinks
765  struct stat st;
766  std::string pathbuf;
767  if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
768  char * p = realpath(name, (char *)0);
769  if (p) {
770  pathbuf = p;
771  free(p);
772  test_name = pathbuf.c_str();
773  }
774  }
775 
776  if (str_starts_with(test_name, net_dev_raw_prefix))
777  test_name += strlen(net_dev_raw_prefix);
778  else if (str_starts_with(test_name, net_dev_prefix))
779  test_name += strlen(net_dev_prefix);
780  else
781  return 0; // device is not starting with /dev/ or /dev/r*
782 
783  if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
784  return get_ata_device(name, "ata");
785 
786  if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
787  // XXX Try to detect possible USB->(S)ATA bridge
788  // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
789  // XXX check sat device via get_usb_dev_type_by_id().
790  // No USB bridge found, assume regular SCSI device
791  return get_scsi_device(name, "scsi");
792  }
793 
794  if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
795  return get_scsi_device(name, "scsi");
796 
797  if (!strncmp(net_dev_nvme_ctrl, test_name, strlen(net_dev_nvme_ctrl)))
798  return get_nvme_device(name, "nvme", 0 /* use default nsid */);
799 
800  // device type unknown
801  return 0;
802 }
803 
804 smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
805 {
806  ARGUSED(name);
807  ARGUSED(type);
808  return 0;
809 }
810 
812 {
813  return "";
814 }
815 
816 } // namespace
817 
818 /////////////////////////////////////////////////////////////////////////////
819 /// Initialize platform interface and register with smi()
820 
822 {
823  static os_netbsd::netbsd_smart_interface the_interface;
824  smart_interface::set(&the_interface);
825 }
826 
827 /* vim: set ts=2 sw=2 et ff=unix : */
void swap2(char *location)
Definition: atacmds.cpp:304
#define ATA_IDENTIFY_DEVICE
Definition: atacmds.h:53
ATA device access.
bool ata_cmd_is_ok(const ata_cmd_in &in, bool data_out_support=false, bool multi_sector_support=false, bool ata_48bit_support=false)
Check command input parameters (old version).
NVMe device access.
unsigned get_nsid() const
Get namespace id.
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.
void set_nsid(unsigned nsid)
Set namespace id.
Implement standard ATA support.
Definition: os_netbsd.cpp:131
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out) override
ATA pass through.
Definition: os_netbsd.cpp:155
virtual int do_cmd(struct atareq *request, bool is_48bit_cmd)
Definition: os_netbsd.cpp:146
netbsd_ata_device(smart_interface *intf, const char *dev_name, const char *req_type)
Definition: os_netbsd.cpp:140
virtual bool nvme_pass_through(const nvme_cmd_in &in, nvme_cmd_out &out) override
NVMe pass through.
Definition: os_netbsd.cpp:294
netbsd_nvme_device(smart_interface *intf, const char *dev_name, const char *req_type, unsigned nsid)
Definition: os_netbsd.cpp:235
virtual bool open() override
Open device, return false on error.
Definition: os_netbsd.cpp:243
Standard SCSI support.
Definition: os_netbsd.cpp:330
virtual smart_device * autodetect_open() override
Open device with autodetection support.
Definition: os_netbsd.cpp:426
netbsd_scsi_device(smart_interface *intf, const char *dev_name, const char *req_type, bool scanning=false)
Definition: os_netbsd.cpp:342
virtual bool scsi_pass_through(scsi_cmnd_io *iop) override
SCSI pass through.
Definition: os_netbsd.cpp:350
bool m_scanning
true if created within scan_smart_devices
Definition: os_netbsd.cpp:339
Implement shared open/close routines with old functions.
Definition: os_netbsd.cpp:49
virtual bool is_open() const
Return true if device is open.
Definition: os_netbsd.cpp:81
int m_fd
filedesc, -1 if not open.
Definition: os_netbsd.cpp:72
virtual bool close()
Close device, return false on error.
Definition: os_netbsd.cpp:112
virtual bool open()
Open device, return false on error.
Definition: os_netbsd.cpp:87
int get_fd() const
Return filedesc for derived classes.
Definition: os_netbsd.cpp:65
Implement platform interface with old functions.
Definition: os_netbsd.cpp:493
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0) override
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'.
Definition: os_netbsd.cpp:679
virtual smart_device * autodetect_smart_device(const char *name) override
Autodetect device if no device type specified.
Definition: os_netbsd.cpp:756
int get_dev_names(char ***, const char *)
Definition: os_netbsd.cpp:566
virtual scsi_device * get_scsi_device(const char *name, const char *type) override
Return standard SCSI device.
Definition: os_netbsd.cpp:556
virtual std::string get_app_examples(const char *appname) override
Return example string for program 'appname'.
Definition: os_netbsd.cpp:532
bool get_nvme_devlist(smart_device_list &devlist, const char *type)
Definition: os_netbsd.cpp:624
virtual std::string get_os_version_str() override
Return info string about build host and/or OS version.
Definition: os_netbsd.cpp:525
virtual ata_device * get_ata_device(const char *name, const char *type) override
Return standard ATA device.
Definition: os_netbsd.cpp:551
virtual smart_device * get_custom_smart_device(const char *name, const char *type) override
Return device for platform specific 'type'.
Definition: os_netbsd.cpp:804
virtual std::string get_valid_custom_dev_types_str() override
Return valid 'type' args accepted by above.
Definition: os_netbsd.cpp:811
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid) override
Return standard NVMe device.
Definition: os_netbsd.cpp:561
SCSI device access.
List of devices for DEVICESCAN.
void push_back(smart_device *dev)
Base class for all devices.
Definition: dev_interface.h:33
bool is_scsi() const
Return true if SCSI device.
Definition: dev_interface.h:89
const char * get_req_type() const
Get type requested by user, empty if none.
bool is_nvme() const
Return true if NVMe device.
Definition: dev_interface.h:92
smart_interface * smi()
Get interface which produced this object.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
const char * get_dev_name() const
Get device (path)name.
bool is_ata() const
Return true if ATA device.
Definition: dev_interface.h:86
void clear_err()
Clear last error info.
The platform interface abstraction.
static void set(smart_interface *intf)
Set interface to use, must be called from init().
static void init()
Initialize platform interface and register with smi().
Definition: dev_legacy.cpp:334
virtual ata_device * autodetect_sat_device(scsi_device *scsidev, const unsigned char *inqdata, unsigned inqsize)
Try to detect a SAT device behind a SCSI interface.
Definition: scsiata.cpp:1482
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
#define NVME_PASSTHROUGH_CMD
#define NVME_NS_PREFIX
#define nvme_completion_is_error(cpl)
static const char * net_dev_scsi_disk
Definition: os_netbsd.cpp:40
static const char * net_dev_nvme_ctrl
Definition: os_netbsd.cpp:42
static const char * net_dev_raw_prefix
Definition: os_netbsd.cpp:38
static const char * net_dev_ata_disk
Definition: os_netbsd.cpp:39
static const char * net_dev_prefix
Definition: os_netbsd.cpp:37
static const char * net_dev_scsi_tape
Definition: os_netbsd.cpp:41
#define NVME_PREFIX
uint32_t nsid
#define ARGUSED(x)
Definition: os_netbsd.cpp:31
const char * os_netbsd_cpp_cvsid
Definition: os_netbsd.cpp:28
#define OS_NETBSD_H_CVSID
Definition: os_netbsd.h:14
int scsiStdInquiry(scsi_device *device, uint8_t *pBuf, int bufLen)
Definition: scsicmds.cpp:870
const char * scsi_get_opcode_name(uint8_t opcode)
Definition: scsicmds.cpp:233
void dStrHex(const uint8_t *up, int len, int no_ascii)
Definition: scsicmds.cpp:78
unsigned char scsi_debugmode
Definition: scsicmds.cpp:45
#define DXFER_NONE
Definition: scsicmds.h:96
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:97
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:352
#define DXFER_TO_DEVICE
Definition: scsicmds.h:98
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 pass through output parameters.
ata_out_regs_48bit out_regs
Output registers.
bool is_48bit_cmd() const
Return true if 48-bit command.
ata_register device
ata_register lba_high
ata_register sector_count
ata_register lba_low
ata_register features
ata_register command
ata_register lba_mid
ata_register sector_count
ata_register lba_low
ata_register lba_mid
ata_register error
ata_register lba_high
ata_register device
ata_register status
NVMe pass through input parameters.
unsigned cdw10
unsigned cdw13
unsigned cdw11
unsigned char opcode
Opcode (CDW0 07:00)
unsigned size
Size of buffer.
unsigned cdw14
unsigned cdw15
Cmd specific.
unsigned nsid
Namespace ID.
unsigned cdw12
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
unsigned result
Command specific result (DW0)
struct nvme_command cmd
struct nvme_completion cpl
uint8_t * sensep
Definition: scsicmds.h:108
uint8_t * dxferp
Definition: scsicmds.h:106
int dxfer_dir
Definition: scsicmds.h:104
size_t cmnd_len
Definition: scsicmds.h:103
size_t resp_sense_len
Definition: scsicmds.h:112
size_t dxfer_len
Definition: scsicmds.h:107
size_t max_sense_len
Definition: scsicmds.h:110
uint8_t scsi_status
Definition: scsicmds.h:113
uint8_t * cmnd
Definition: scsicmds.h:102
unsigned timeout
Definition: scsicmds.h:111
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:772
bool isbigendian()
Definition: utility.h:81
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:51