smartmontools  SVN Rev 5304
Utility to control and monitor storage systems with "S.M.A.R.T."
os_solaris.cpp
Go to the documentation of this file.
1 /*
2  * os_solaris.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2003-08 SAWADA Keiji
7  * Copyright (C) 2003-15 Casper Dik
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <dirent.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <sys/param.h>
19 
20 // These are needed to define prototypes for the functions defined below
21 #include "config.h"
22 
23 #include "atacmds.h"
24 #include "scsicmds.h"
25 #include "utility.h"
26 
27 // This is to include whatever prototypes you define in os_solaris.h
28 #include "os_solaris.h"
29 
30 #define ARGUSED(x) ((void)(x))
31 
32 const char *os_XXXX_cpp_cvsid = "$Id: os_solaris.cpp 5065 2020-06-20 13:47:36Z chrfranke $"
34 
35 // print examples for smartctl
37  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
38  " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n\n"
39  " smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n"
40  " (Enables SMART on first disk)\n\n"
41  " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n"
42  " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n"
43  " (Prints Self-Test & Attribute errors)\n"
44  );
45  return;
46 }
47 
48 static const char *uscsidrvrs[] = {
49  "sd",
50  "ssd",
51  "disk", // SATA devices
52  "st"
53 };
54 
55 static const char *atadrvrs[] = {
56  "cmdk",
57  "dad",
58 };
59 
60 static int
61 isdevtype(const char *dev_name, const char *table[], int tsize)
62 {
63  char devpath[MAXPATHLEN];
64  int i;
65  char *basename;
66 
67  if (realpath(dev_name, devpath) == NULL)
68  return 0;
69 
70  if ((basename = strrchr(devpath, '/')) == NULL)
71  return 0;
72 
73  basename++;
74 
75  for (i = 0; i < tsize; i++) {
76  int l = strlen(table[i]);
77  if (strncmp(basename, table[i], l) == 0 && basename[l] == '@')
78  return 1;
79  }
80  return 0;
81 }
82 
83 static int
84 isscsidev(const char *path)
85 {
86  return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *));
87 }
88 
89 static int
90 isatadev(const char *path)
91 {
92  return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *));
93 }
94 
95 // tries to guess device type given the name (a path)
96 int guess_device_type (const char* dev_name) {
97  if (isscsidev(dev_name))
98  return CONTROLLER_SCSI;
99  else if (isatadev(dev_name))
100  return CONTROLLER_ATA;
101  else
102  return CONTROLLER_UNKNOWN;
103 }
104 
105 struct pathlist {
106  char **names;
107  int nnames;
108  int maxnames;
109 };
110 
111 static int
112 addpath(const char *path, struct pathlist *res)
113 {
114  if (++res->nnames > res->maxnames) {
115  res->maxnames += 16;
116  res->names = static_cast<char**>(realloc(res->names, res->maxnames * sizeof (char *)));
117  if (res->names == NULL)
118  return -1;
119  }
120  if (!(res->names[res->nnames-1] = strdup(path)))
121  return -1;
122  return 0;
123 }
124 
125 static int
126 grokdir(const char *dir, struct pathlist *res, int testfun(const char *))
127 {
128  char pathbuf[MAXPATHLEN];
129  size_t len;
130  DIR *dp;
131  struct dirent *de;
132  int isdisk = strstr(dir, "dsk") != NULL;
133  char *p;
134 
135  len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir);
136  if (len >= sizeof (pathbuf))
137  return -1;
138 
139  dp = opendir(dir);
140  if (dp == NULL)
141  return 0;
142 
143  while ((de = readdir(dp)) != NULL) {
144  if (de->d_name[0] == '.')
145  continue;
146 
147  if (strlen(de->d_name) + len >= sizeof (pathbuf))
148  continue;
149 
150  if (isdisk) {
151  /* Disk represented by slice 0 */
152  p = strstr(de->d_name, "s0");
153  /* String doesn't end in "s0\0" */
154  if (p == NULL || p[2] != '\0')
155  continue;
156  } else {
157  /* Tape drive represented by the all-digit device */
158  for (p = de->d_name; *p; p++)
159  if (!isdigit((int)(*p)))
160  break;
161  if (*p != '\0')
162  continue;
163  }
164  strcpy(&pathbuf[len], de->d_name);
165  if (testfun(pathbuf)) {
166  if (addpath(pathbuf, res) == -1) {
167  closedir(dp);
168  return -1;
169  }
170  }
171  }
172  closedir(dp);
173 
174  return 0;
175 }
176 
177 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
178 // smartd. Returns number of devices, or -1 if out of memory.
179 int make_device_names (char*** devlist, const char* name) {
180  struct pathlist res;
181 
182  res.nnames = res.maxnames = 0;
183  res.names = NULL;
184  if (strcmp(name, "SCSI") == 0) {
185  if (grokdir("/dev/rdsk", &res, isscsidev) == -1)
186  return -1;
187  if (grokdir("/dev/rmt", &res, isscsidev) == -1)
188  return -1;
189  } else if (strcmp(name, "ATA") == 0) {
190  if (grokdir("/dev/rdsk", &res, isatadev) == -1)
191  return -1;
192  } else {
193  // non-SCSI and non-ATA case not implemented
194  *devlist=NULL;
195  return 0;
196  }
197 
198  // shrink array to min possible size
199  res.names = static_cast<char**>(realloc(res.names, res.nnames * sizeof (char *)));
200 
201  // pass list back
202  *devlist = res.names;
203  return res.nnames;
204 }
205 
206 // Like open(). Return integer handle, used by functions below only.
207 // type="ATA" or "SCSI".
208 int deviceopen(const char *pathname, char *type){
209  if (!strcmp(type,"SCSI"))
210  return open(pathname, O_RDWR | O_NONBLOCK);
211  else if (!strcmp(type,"ATA"))
212  return open(pathname, O_RDONLY | O_NONBLOCK);
213  else
214  return -1;
215 }
216 
217 // Like close(). Acts on handles returned by above function.
218 int deviceclose(int fd){
219  return close(fd);
220 }
221 
222 #if defined(WITH_SOLARIS_SPARC_ATA)
223 // swap each 2-byte pairs in a sector
224 static void swap_sector(void *p)
225 {
226  int i;
227  char t, *cp = static_cast<char*>(p);
228  for(i = 0; i < 256; i++) {
229  t = cp[0]; cp[0] = cp[1]; cp[1] = t;
230  cp += 2;
231  }
232 }
233 #endif
234 
235 // Interface to ATA devices. See os_linux.c
236 int ata_command_interface(int fd, smart_command_set command, int select, char *data){
237 #if defined(WITH_SOLARIS_SPARC_ATA)
238  int err;
239 
240  switch (command){
241  case CHECK_POWER_MODE:
242  /* currently not recognized */
243  return -1;
244  case READ_VALUES:
245  return smart_read_data(fd, data);
246  case READ_THRESHOLDS:
247  return smart_read_thresholds(fd, data);
248  case READ_LOG:
249  return smart_read_log(fd, select, 1, data);
250  case IDENTIFY:
251  err = ata_identify(fd, data);
252  if(err) return err;
253  swap_sector(static_cast<void*>(data));
254  return 0;
255  case PIDENTIFY:
256  err = ata_pidentify(fd, data);
257  if(err) return err;
258  swap_sector(static_cast<void*>(data));
259  return 0;
260  case ENABLE:
261  return smart_enable(fd);
262  case DISABLE:
263  return smart_disable(fd);
264  case STATUS:
265  return smart_status(fd);
266  case AUTO_OFFLINE:
267  return smart_auto_offline(fd, select);
268  case AUTOSAVE:
269  return smart_auto_save(fd, select);
270  case IMMEDIATE_OFFLINE:
271  return smart_immediate_offline(fd, select);
272  case STATUS_CHECK:
273  return smart_status_check(fd);
274  default:
275  pout("Unrecognized command %d in ata_command_interface() of os_solaris.cpp\n", command);
276  errno = EINVAL;
277  return -1;
278  }
279 #else /* WITH_SOLARIS_SPARC_ATA */
280  ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
281 
282  /* Above smart_* routines uses undocumented ioctls of "dada"
283  * driver, which is specific to SPARC Solaris. See
284  * os_solaris_ata.s for further details. */
285 
286  pout("Device type 'ata' not implemented, try '-d sat' or '-d sat,12' instead.\n");
287  errno = ENOSYS;
288 #endif
289  return -1;
290 }
291 
292 #include <errno.h>
293 #include <sys/scsi/generic/commands.h>
294 #include <sys/scsi/generic/status.h>
295 #include <sys/scsi/impl/types.h>
296 #include <sys/scsi/impl/uscsi.h>
297 
298 // Interface to SCSI devices. See os_linux.c
299 int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
300 {
301  struct uscsi_cmd uscsi;
302 
303  if (report > 0) {
304  int k;
305  const unsigned char * ucp = iop->cmnd;
306  const char * np;
307 
308  np = scsi_get_opcode_name(ucp[0]);
309  pout(" [%s: ", np ? np : "<unknown opcode>");
310  for (k = 0; k < (int)iop->cmnd_len; ++k)
311  pout("%02x ", ucp[k]);
312  pout("]\n");
313  if ((report > 1) &&
314  (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
315  int trunc = (iop->dxfer_len > 256) ? 1 : 0;
316 
317  pout(" Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
318  (trunc ? " [only first 256 bytes shown]" : ""));
319  dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
320  }
321  }
322  memset(&uscsi, 0, sizeof (uscsi));
323 
324  uscsi.uscsi_cdb = reinterpret_cast<char*>(iop->cmnd);
325  uscsi.uscsi_cdblen = iop->cmnd_len;
326  if (iop->timeout == 0)
327  uscsi.uscsi_timeout = 60; /* 60 seconds */
328  else
329  uscsi.uscsi_timeout = iop->timeout;
330  uscsi.uscsi_bufaddr = reinterpret_cast<char*>(iop->dxferp);
331  uscsi.uscsi_buflen = iop->dxfer_len;
332  uscsi.uscsi_rqbuf = reinterpret_cast<char*>(iop->sensep);
333  uscsi.uscsi_rqlen = iop->max_sense_len;
334 
335  switch (iop->dxfer_dir) {
336  case DXFER_NONE:
337  case DXFER_FROM_DEVICE:
338  uscsi.uscsi_flags = USCSI_READ;
339  break;
340  case DXFER_TO_DEVICE:
341  uscsi.uscsi_flags = USCSI_WRITE;
342  break;
343  default:
344  return -EINVAL;
345  }
346  uscsi.uscsi_flags |= (USCSI_ISOLATE | USCSI_RQENABLE | USCSI_SILENT);
347 
348  if (ioctl(fd, USCSICMD, &uscsi)) {
349  int err = errno;
350 
351  if (! ((EIO == err) && uscsi.uscsi_status))
352  return -err;
353  /* errno is set to EIO when a non-zero SCSI completion status given */
354  }
355 
356  iop->scsi_status = uscsi.uscsi_status;
357  iop->resid = uscsi.uscsi_resid;
358  iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid;
359 
360  if (report > 0) {
361  int trunc;
362  int len = iop->resp_sense_len;
363 
365  iop->sensep && (len > 3)) {
366  if ((iop->sensep[0] & 0x7f) > 0x71)
367  pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
368  iop->scsi_status, iop->sensep[1] & 0xf,
369  iop->sensep[2], iop->sensep[3]);
370  else
371  pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
372  iop->scsi_status, iop->sensep[2] & 0xf,
373  iop->sensep[12], iop->sensep[13]);
374  if (report > 1) {
375  pout(" >>> Sense buffer, len=%d:\n", len);
376  dStrHex(iop->sensep, ((len > 252) ? 252 : len) , 1);
377  }
378  } else if (iop->scsi_status)
379  pout(" status=%x\n", iop->scsi_status);
380  if (iop->resid)
381  pout(" dxfer_len=%d, resid=%d\n", iop->dxfer_len, iop->resid);
382  if (report > 1) {
383  len = iop->dxfer_len - iop->resid;
384  if (len > 0) {
385  trunc = (len > 256) ? 1 : 0;
386  pout(" Incoming data, len=%d%s:\n", len,
387  (trunc ? " [only first 256 bytes shown]" : ""));
388  dStrHex(iop->dxferp, (trunc ? 256 : len) , 1);
389  }
390  }
391  }
392  return 0;
393 }
smart_command_set
Definition: atacmds.h:29
@ PIDENTIFY
Definition: atacmds.h:43
@ CHECK_POWER_MODE
Definition: atacmds.h:45
@ IDENTIFY
Definition: atacmds.h:42
@ STATUS_CHECK
Definition: atacmds.h:37
@ IMMEDIATE_OFFLINE
Definition: atacmds.h:34
@ AUTO_OFFLINE
Definition: atacmds.h:35
@ ENABLE
Definition: atacmds.h:31
@ READ_VALUES
Definition: atacmds.h:39
@ AUTOSAVE
Definition: atacmds.h:33
@ STATUS
Definition: atacmds.h:36
@ READ_THRESHOLDS
Definition: atacmds.h:40
@ DISABLE
Definition: atacmds.h:32
@ READ_LOG
Definition: atacmds.h:41
ptr_t data
Definition: megaraid.h:15
static int addpath(const char *path, struct pathlist *res)
Definition: os_solaris.cpp:112
static int isatadev(const char *path)
Definition: os_solaris.cpp:90
int deviceclose(int fd)
Definition: os_solaris.cpp:218
int ata_command_interface(int fd, smart_command_set command, int select, char *data)
Definition: os_solaris.cpp:236
int make_device_names(char ***devlist, const char *name)
Definition: os_solaris.cpp:179
static const char * uscsidrvrs[]
Definition: os_solaris.cpp:48
static int isdevtype(const char *dev_name, const char *table[], int tsize)
Definition: os_solaris.cpp:61
const char * os_XXXX_cpp_cvsid
Definition: os_solaris.cpp:32
int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io *iop, int report)
Definition: os_solaris.cpp:299
int deviceopen(const char *pathname, char *type)
Definition: os_solaris.cpp:208
static int grokdir(const char *dir, struct pathlist *res, int testfun(const char *))
Definition: os_solaris.cpp:126
#define ARGUSED(x)
Definition: os_solaris.cpp:30
static const char * atadrvrs[]
Definition: os_solaris.cpp:55
int guess_device_type(const char *dev_name)
Definition: os_solaris.cpp:96
void print_smartctl_examples()
Definition: os_solaris.cpp:36
static int isscsidev(const char *path)
Definition: os_solaris.cpp:84
int smart_read_log(int fd, int s, int count, void *data)
int smart_read_data(int fd, void *data)
int smart_auto_save(int fd, int s)
int smart_status(int fd)
int smart_auto_offline(int fd, int s)
int smart_enable(int fd)
int ata_identify(int fd, void *data)
int smart_status_check(int fd)
#define OS_SOLARIS_H_CVSID
Definition: os_solaris.h:15
int smart_disable(int fd)
int smart_read_thresholds(int fd, void *data)
int ata_pidentify(int fd, void *data)
int smart_immediate_offline(int fd, int s)
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
#define DXFER_NONE
Definition: scsicmds.h:96
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:97
#define DXFER_TO_DEVICE
Definition: scsicmds.h:98
#define SCSI_STATUS_CHECK_CONDITION
Definition: scsicmds.h:298
void pout(const char *fmt,...)
Definition: smartd.cpp:1308
int maxnames
Definition: os_solaris.cpp:108
char ** names
Definition: os_solaris.cpp:106
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