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