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