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