smartmontools  SVN Rev 4337
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: http://www.smartmontools.org
5  *
6  * Copyright (C) 2003-8 Sergey Svishchev <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, write to the Free Software Foundation,
15  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  *
17  */
18 
19 #include "config.h"
20 #include "int64.h"
21 #include "atacmds.h"
22 #include "scsicmds.h"
23 #include "utility.h"
24 #include "os_netbsd.h"
25 
26 #include <errno.h>
27 #include <unistd.h>
28 
29 const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4320 2016-05-10 13:39:19Z chrfranke $"
31 
32 enum warnings {
34 };
35 
36 /* Utility function for printing warnings */
37 void
38 printwarning(int msgNo, const char *extra)
39 {
40  static int printed[] = {0, 0};
41  static const char *message[] = {
42  "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
43  PACKAGE_STRING " does not currently support twe(4) and twa(4) devices (3ware Escalade, Apache)\n",
44  };
45 
46  if (msgNo >= 0 && msgNo <= MAX_MSG) {
47  if (!printed[msgNo]) {
48  printed[msgNo] = 1;
49  pout("%s", message[msgNo]);
50  if (extra)
51  pout("%s", extra);
52  }
53  }
54  return;
55 }
56 
57 static const char *net_dev_prefix = "/dev/r";
58 static const char *net_dev_ata_disk = "wd";
59 static const char *net_dev_scsi_disk = "sd";
60 static const char *net_dev_scsi_tape = "enrst";
61 
62 /* Guess device type (ATA or SCSI) based on device name */
63 int
64 guess_device_type(const char *dev_name)
65 {
66  int len;
67  int dev_prefix_len = strlen(net_dev_prefix);
68 
69  if (!dev_name || !(len = strlen(dev_name)))
70  return CONTROLLER_UNKNOWN;
71 
72  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
73  if (len <= dev_prefix_len)
74  return CONTROLLER_UNKNOWN;
75  else
76  dev_name += dev_prefix_len;
77  }
78  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
79  return CONTROLLER_ATA;
80 
81  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
82  return CONTROLLER_SCSI;
83 
84  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
85  return CONTROLLER_SCSI;
86 
87  return CONTROLLER_UNKNOWN;
88 }
89 
90 int
91 get_dev_names(char ***names, const char *prefix)
92 {
93  char *disknames, *p, **mp;
94  int n = 0;
95  int sysctl_mib[2];
96  size_t sysctl_len;
97 
98  *names = NULL;
99 
100  sysctl_mib[0] = CTL_HW;
101  sysctl_mib[1] = HW_DISKNAMES;
102  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
103  pout("Failed to get value of sysctl `hw.disknames'\n");
104  return -1;
105  }
106  if (!(disknames = (char *)malloc(sysctl_len))) {
107  pout("Out of memory constructing scan device list\n");
108  return -1;
109  }
110  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
111  pout("Failed to get value of sysctl `hw.disknames'\n");
112  return -1;
113  }
114  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
115  pout("Out of memory constructing scan device list\n");
116  return -1;
117  }
118  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
119  if (strncmp(p, prefix, strlen(prefix))) {
120  continue;
121  }
122  mp[n] = (char *)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
123  if (!mp[n]) {
124  pout("Out of memory constructing scan device list\n");
125  return -1;
126  }
127  sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
128  n++;
129  }
130 
131  char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
132  if (NULL == tmp) {
133  pout("Out of memory constructing scan device list\n");
134  free(mp);
135  return -1;
136  }
137  else
138  mp = tmp;
139  *names = mp;
140  return n;
141 }
142 
143 int
144 make_device_names(char ***devlist, const char *name)
145 {
146  if (!strcmp(name, "SCSI"))
147  return get_dev_names(devlist, net_dev_scsi_disk);
148  else if (!strcmp(name, "ATA"))
149  return get_dev_names(devlist, net_dev_ata_disk);
150  else
151  return 0;
152 }
153 
154 int
155 deviceopen(const char *pathname, char *type)
156 {
157  if (!strcmp(type, "SCSI")) {
158  int fd = open(pathname, O_RDWR | O_NONBLOCK);
159  if (fd < 0 && errno == EROFS)
160  fd = open(pathname, O_RDONLY | O_NONBLOCK);
161  return fd;
162  } else if (!strcmp(type, "ATA"))
163  return open(pathname, O_RDWR | O_NONBLOCK);
164  else
165  return -1;
166 }
167 
168 int
169 deviceclose(int fd)
170 {
171  return close(fd);
172 }
173 
174 int
175 ata_command_interface(int fd, smart_command_set command, int select, char *data)
176 {
177  struct atareq req;
178  unsigned char inbuf[DEV_BSIZE];
179  int retval, copydata = 0;
180 
181  memset(&req, 0, sizeof(req));
182  req.timeout = 1000;
183 
184  memset(&inbuf, 0, sizeof(inbuf));
185 
186  switch (command) {
187  case READ_VALUES:
188  req.flags = ATACMD_READ;
189  req.features = WDSM_RD_DATA;
190  req.command = WDCC_SMART;
191  req.databuf = (char *)inbuf;
192  req.datalen = sizeof(inbuf);
193  req.cylinder = WDSMART_CYL;
194  copydata = 1;
195  break;
196  case READ_THRESHOLDS:
197  req.flags = ATACMD_READ;
198  req.features = WDSM_RD_THRESHOLDS;
199  req.command = WDCC_SMART;
200  req.databuf = (char *)inbuf;
201  req.datalen = sizeof(inbuf);
202  req.cylinder = WDSMART_CYL;
203  copydata = 1;
204  break;
205  case READ_LOG:
206  req.flags = ATACMD_READ;
207  req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */
208  req.command = WDCC_SMART;
209  req.databuf = (char *)inbuf;
210  req.datalen = sizeof(inbuf);
211  req.cylinder = WDSMART_CYL;
212  req.sec_num = select;
213  req.sec_count = 1;
214  copydata = 1;
215  break;
216  case WRITE_LOG:
217  memcpy(inbuf, data, 512);
218  req.flags = ATACMD_WRITE;
219  req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
220  req.command = WDCC_SMART;
221  req.databuf = (char *)inbuf;
222  req.datalen = sizeof(inbuf);
223  req.cylinder = WDSMART_CYL;
224  req.sec_num = select;
225  req.sec_count = 1;
226  break;
227  case IDENTIFY:
228  req.flags = ATACMD_READ;
229  req.command = WDCC_IDENTIFY;
230  req.databuf = (char *)inbuf;
231  req.datalen = sizeof(inbuf);
232  copydata = 1;
233  break;
234  case PIDENTIFY:
235  req.flags = ATACMD_READ;
236  req.command = ATAPI_IDENTIFY_DEVICE;
237  req.databuf = (char *)inbuf;
238  req.datalen = sizeof(inbuf);
239  copydata = 1;
240  break;
241  case ENABLE:
242  req.flags = ATACMD_READREG;
243  req.features = WDSM_ENABLE_OPS;
244  req.command = WDCC_SMART;
245  req.cylinder = WDSMART_CYL;
246  break;
247  case DISABLE:
248  req.flags = ATACMD_READREG;
249  req.features = WDSM_DISABLE_OPS;
250  req.command = WDCC_SMART;
251  req.cylinder = WDSMART_CYL;
252  break;
253  case AUTO_OFFLINE:
254  /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
255  req.flags = ATACMD_READREG;
256  req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */
257  req.command = WDCC_SMART;
258  req.cylinder = WDSMART_CYL;
259  req.sec_count = select;
260  break;
261  case AUTOSAVE:
262  req.flags = ATACMD_READREG;
263  req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
264  req.command = WDCC_SMART;
265  req.cylinder = WDSMART_CYL;
266  req.sec_count = select;
267  break;
268  case IMMEDIATE_OFFLINE:
269  /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
270  req.flags = ATACMD_READREG;
271  req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */
272  req.command = WDCC_SMART;
273  req.cylinder = WDSMART_CYL;
274  req.sec_num = select;
275  req.sec_count = 1;
276  break;
277  case STATUS: /* should return 0 if SMART is enabled at all */
278  case STATUS_CHECK: /* should return 0 if disk's health is ok */
279  req.flags = ATACMD_READREG;
280  req.features = WDSM_STATUS;
281  req.command = WDCC_SMART;
282  req.cylinder = WDSMART_CYL;
283  break;
284  case CHECK_POWER_MODE:
285  req.flags = ATACMD_READREG;
286  req.command = WDCC_CHECK_PWR;
287  break;
288  default:
289  pout("Unrecognized command %d in ata_command_interface()\n", command);
290  errno = ENOSYS;
291  return -1;
292  }
293 
294  if (command == STATUS_CHECK || command == AUTOSAVE || command == AUTO_OFFLINE) {
295  char buf[512];
296 
297  unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
298 
299  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
300  perror("Failed command");
301  return -1;
302  }
303  if (req.retsts != ATACMD_OK) {
304  return -1;
305  }
306  /* Cyl low and Cyl high unchanged means "Good SMART status" */
307  if (req.cylinder == normal)
308  return 0;
309 
310  /* These values mean "Bad SMART status" */
311  if (req.cylinder == failed)
312  return 1;
313 
314  /* We haven't gotten output that makes sense;
315  * print out some debugging info */
316  snprintf(buf, sizeof(buf),
317  "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
318  (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
319  (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
320  (int) req.error);
321  printwarning(BAD_SMART, buf);
322  return 0;
323  }
324 
325  retval = ioctl(fd, ATAIOCCOMMAND, &req);
326  if (retval < 0) {
327  perror("Failed command");
328  return -1;
329  }
330  if (req.retsts != ATACMD_OK) {
331  return -1;
332  }
333 
334  if (command == CHECK_POWER_MODE)
335  data[0] = req.sec_count;
336 
337  if (copydata)
338  memcpy(data, inbuf, 512);
339 
340  return 0;
341 }
342 
343 int
344 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
345 {
346  struct scsireq sc;
347 
348  if (report > 0) {
349  size_t k;
350 
351  const unsigned char *ucp = iop->cmnd;
352  const char *np;
353 
354  np = scsi_get_opcode_name(ucp[0]);
355  pout(" [%s: ", np ? np : "<unknown opcode>");
356  for (k = 0; k < iop->cmnd_len; ++k)
357  pout("%02x ", ucp[k]);
358  if ((report > 1) &&
359  (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
360  int trunc = (iop->dxfer_len > 256) ? 1 : 0;
361 
362  pout("]\n Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
363  (trunc ? " [only first 256 bytes shown]" : ""));
364  dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
365  } else
366  pout("]");
367  }
368  memset(&sc, 0, sizeof(sc));
369  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
370  sc.cmdlen = iop->cmnd_len;
371  sc.databuf = (char *)iop->dxferp;
372  sc.datalen = iop->dxfer_len;
373  sc.senselen = iop->max_sense_len;
374  sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
375  sc.flags =
376  (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
377  (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
378 
379  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
380  warn("error sending SCSI ccb");
381  return -1;
382  }
383  iop->resid = sc.datalen - sc.datalen_used;
384  iop->scsi_status = sc.status;
385  if (iop->sensep) {
386  memcpy(iop->sensep, sc.sense, sc.senselen_used);
387  iop->resp_sense_len = sc.senselen_used;
388  }
389  if (report > 0) {
390  int trunc;
391 
392  pout(" status=0\n");
393  trunc = (iop->dxfer_len > 256) ? 1 : 0;
394 
395  pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
396  (trunc ? " [only first 256 bytes shown]" : ""));
397  dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
398  }
399  switch (sc.retsts) {
400  case SCCMD_OK:
401  return 0;
402  case SCCMD_TIMEOUT:
403  return -ETIMEDOUT;
404  case SCCMD_BUSY:
405  return -EBUSY;
406  default:
407  return -EIO;
408  }
409 }
410 
411 /* print examples for smartctl */
412 void
414 {
415  char p;
416 
417  p = 'a' + getrawpartition();
418  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
419 #ifdef HAVE_GETOPT_LONG
420  printf(
421  " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
422  " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
423  " (Enables SMART on first disk)\n\n"
424  " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
425  " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
426  " (Prints Self-Test & Attribute errors)\n",
427  p, p, p, p
428  );
429 #else
430  printf(
431  " smartctl -a /dev/wd0%c (Prints all SMART information)\n"
432  " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n"
433  " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n"
434  " smartctl -A -l selftest -q errorsonly /dev/wd0%c"
435  " (Prints Self-Test & Attribute errors)\n",
436  p, p, p, p
437  );
438 #endif
439  return;
440 }
#define ATA_SMART_READ_LOG_SECTOR
Definition: atacmds.h:100
#define ATA_SMART_WRITE_LOG_SECTOR
Definition: atacmds.h:101
static const char * net_dev_scsi_tape
Definition: os_netbsd.cpp:60
int make_device_names(char ***devlist, const char *name)
Definition: os_netbsd.cpp:144
UINT8 * sensep
Definition: scsicmds.h:122
warnings
Definition: os_netbsd.cpp:32
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:111
int get_dev_names(char ***names, const char *prefix)
Definition: os_netbsd.cpp:91
const char * scsi_get_opcode_name(UINT8 opcode)
Definition: scsicmds.cpp:177
int guess_device_type(const char *dev_name)
Definition: os_netbsd.cpp:64
unsigned timeout
Definition: scsicmds.h:125
#define ATA_SMART_AUTOSAVE
Definition: atacmds.h:97
#define snprintf
Definition: utility.h:68
size_t resp_sense_len
Definition: scsicmds.h:126
#define OS_NETBSD_H_CVSID
Definition: os_netbsd.h:27
UINT8 * dxferp
Definition: scsicmds.h:120
UINT8 * cmnd
Definition: scsicmds.h:116
ptr_t data
Definition: megaraid.h:94
int deviceclose(int fd)
Definition: os_netbsd.cpp:169
void printwarning(int msgNo, const char *extra)
Definition: os_netbsd.cpp:38
void dStrHex(const char *str, int len, int no_ascii)
Definition: scsicmds.cpp:87
void pout(const char *fmt,...)
Definition: smartctl.cpp:1170
size_t max_sense_len
Definition: scsicmds.h:124
void print_smartctl_examples()
Definition: os_netbsd.cpp:413
int dxfer_dir
Definition: scsicmds.h:118
#define WDSM_RD_THRESHOLDS
Definition: os_netbsd.h:49
static const char * net_dev_prefix
Definition: os_netbsd.cpp:57
Definition: atacmds.h:55
#define WDSMART_CYL
Definition: os_netbsd.h:52
#define DXFER_NONE
Definition: scsicmds.h:110
#define ATA_SMART_AUTO_OFFLINE
Definition: atacmds.h:108
smart_command_set
Definition: atacmds.h:48
#define DXFER_TO_DEVICE
Definition: scsicmds.h:112
int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io *iop, int report)
Definition: os_netbsd.cpp:344
size_t cmnd_len
Definition: scsicmds.h:117
int deviceopen(const char *pathname, char *type)
Definition: os_netbsd.cpp:155
#define ATA_SMART_IMMEDIATE_OFFLINE
Definition: atacmds.h:99
const char * os_netbsd_cpp_cvsid
Definition: os_netbsd.cpp:29
UINT8 scsi_status
Definition: scsicmds.h:127
size_t dxfer_len
Definition: scsicmds.h:121
static const char * net_dev_scsi_disk
Definition: os_netbsd.cpp:59
Definition: atacmds.h:50
static const char * net_dev_ata_disk
Definition: os_netbsd.cpp:58
int ata_command_interface(int fd, smart_command_set command, int select, char *data)
Definition: os_netbsd.cpp:175