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