|
smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
|
00001 /* 00002 * os_solaris.c 00003 * 00004 * Home page of code is: http://smartmontools.sourceforge.net 00005 * 00006 * Copyright (C) 2003-8 SAWADA Keiji <smartmontools-support@lists.sourceforge.net> 00007 * Copyright (C) 2003-8 Casper Dik <smartmontools-support@lists.sourceforge.net> 00008 * 00009 * This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2, or (at your option) 00012 * any later version. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * (for example COPYING); if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00017 * 00018 */ 00019 00020 #include <stdlib.h> 00021 #include <ctype.h> 00022 #include <string.h> 00023 #include <dirent.h> 00024 #include <stdio.h> 00025 #include <unistd.h> 00026 #include <sys/param.h> 00027 00028 // These are needed to define prototypes for the functions defined below 00029 #include "config.h" 00030 #include "int64.h" 00031 #include "atacmds.h" 00032 #include "scsicmds.h" 00033 #include "utility.h" 00034 00035 // This is to include whatever prototypes you define in os_solaris.h 00036 #include "os_solaris.h" 00037 00038 #define ARGUSED(x) ((void)(x)) 00039 00040 extern long long bytes; 00041 00042 static const char *filenameandversion="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $"; 00043 00044 const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \ 00045 ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; 00046 00047 // The printwarning() function warns about unimplemented functions 00048 int printedout[2]; 00049 char *unimplemented[2]={ 00050 "ATA command routine ata_command_interface()", 00051 "3ware Escalade Controller command routine escalade_command_interface()", 00052 }; 00053 00054 int printwarning(int which){ 00055 if (!unimplemented[which]) 00056 return 0; 00057 00058 if (printedout[which]) 00059 return 1; 00060 00061 printedout[which]=1; 00062 00063 pout("\n" 00064 "#######################################################################\n" 00065 "%s NOT IMPLEMENTED under Solaris.\n" 00066 "Please contact " PACKAGE_BUGREPORT " if\n" 00067 "you want to help in porting smartmontools to Solaris.\n" 00068 "#######################################################################\n" 00069 "\n", 00070 unimplemented[which]); 00071 00072 return 1; 00073 } 00074 00075 // print examples for smartctl 00076 void print_smartctl_examples(){ 00077 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); 00078 #ifdef HAVE_GETOPT_LONG 00079 printf( 00080 " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n\n" 00081 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n" 00082 " (Enables SMART on first disk)\n\n" 00083 " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n" 00084 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n" 00085 " (Prints Self-Test & Attribute errors)\n" 00086 ); 00087 #else 00088 printf( 00089 " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n" 00090 " smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n" 00091 " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n" 00092 " smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n" 00093 " (Prints Self-Test & Attribute errors)\n" 00094 ); 00095 #endif 00096 return; 00097 } 00098 00099 static const char *uscsidrvrs[] = { 00100 "sd", 00101 "ssd", 00102 "st" 00103 }; 00104 00105 static const char *atadrvrs[] = { 00106 "cmdk", 00107 "dad", 00108 }; 00109 00110 static int 00111 isdevtype(const char *dev_name, const char *table[], int tsize) 00112 { 00113 char devpath[MAXPATHLEN]; 00114 int i; 00115 char *basename; 00116 00117 if (realpath(dev_name, devpath) == NULL) 00118 return 0; 00119 00120 if ((basename = strrchr(devpath, '/')) == NULL) 00121 return 0; 00122 00123 basename++; 00124 00125 for (i = 0; i < tsize; i++) { 00126 int l = strlen(table[i]); 00127 if (strncmp(basename, table[i], l) == 0 && basename[l] == '@') 00128 return 1; 00129 } 00130 return 0; 00131 } 00132 00133 static int 00134 isscsidev(const char *path) 00135 { 00136 return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *)); 00137 } 00138 00139 static int 00140 isatadev(const char *path) 00141 { 00142 return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *)); 00143 } 00144 00145 // tries to guess device type given the name (a path) 00146 int guess_device_type (const char* dev_name) { 00147 if (isscsidev(dev_name)) 00148 return CONTROLLER_SCSI; 00149 else if (isatadev(dev_name)) 00150 return CONTROLLER_ATA; 00151 else 00152 return CONTROLLER_UNKNOWN; 00153 } 00154 00155 struct pathlist { 00156 char **names; 00157 int nnames; 00158 int maxnames; 00159 }; 00160 00161 static int 00162 addpath(const char *path, struct pathlist *res) 00163 { 00164 if (++res->nnames > res->maxnames) { 00165 res->maxnames += 16; 00166 res->names = static_cast<char**>(realloc(res->names, res->maxnames * sizeof (char *))); 00167 if (res->names == NULL) 00168 return -1; 00169 bytes += 16*sizeof(char *); 00170 } 00171 if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion))) 00172 return -1; 00173 return 0; 00174 } 00175 00176 static int 00177 grokdir(const char *dir, struct pathlist *res, int testfun(const char *)) 00178 { 00179 char pathbuf[MAXPATHLEN]; 00180 size_t len; 00181 DIR *dp; 00182 struct dirent *de; 00183 int isdisk = strstr(dir, "dsk") != NULL; 00184 char *p; 00185 00186 len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir); 00187 if (len >= sizeof (pathbuf)) 00188 return -1; 00189 00190 dp = opendir(dir); 00191 if (dp == NULL) 00192 return 0; 00193 00194 while ((de = readdir(dp)) != NULL) { 00195 if (de->d_name[0] == '.') 00196 continue; 00197 00198 if (strlen(de->d_name) + len >= sizeof (pathbuf)) 00199 continue; 00200 00201 if (isdisk) { 00202 /* Disk represented by slice 0 */ 00203 p = strstr(de->d_name, "s0"); 00204 /* String doesn't end in "s0\0" */ 00205 if (p == NULL || p[2] != '\0') 00206 continue; 00207 } else { 00208 /* Tape drive represented by the all-digit device */ 00209 for (p = de->d_name; *p; p++) 00210 if (!isdigit((int)(*p))) 00211 break; 00212 if (*p != '\0') 00213 continue; 00214 } 00215 strcpy(&pathbuf[len], de->d_name); 00216 if (testfun(pathbuf)) { 00217 if (addpath(pathbuf, res) == -1) { 00218 closedir(dp); 00219 return -1; 00220 } 00221 } 00222 } 00223 closedir(dp); 00224 00225 return 0; 00226 } 00227 00228 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of 00229 // smartd. Returns number of devices, or -1 if out of memory. 00230 int make_device_names (char*** devlist, const char* name) { 00231 struct pathlist res; 00232 00233 res.nnames = res.maxnames = 0; 00234 res.names = NULL; 00235 if (strcmp(name, "SCSI") == 0) { 00236 if (grokdir("/dev/rdsk", &res, isscsidev) == -1) 00237 return -1; 00238 if (grokdir("/dev/rmt", &res, isscsidev) == -1) 00239 return -1; 00240 } else if (strcmp(name, "ATA") == 0) { 00241 if (grokdir("/dev/rdsk", &res, isatadev) == -1) 00242 return -1; 00243 } else { 00244 // non-SCSI and non-ATA case not implemented 00245 *devlist=NULL; 00246 return 0; 00247 } 00248 00249 // shrink array to min possible size 00250 res.names = static_cast<char**>(realloc(res.names, res.nnames * sizeof (char *))); 00251 bytes -= sizeof(char *)*(res.maxnames-res.nnames); 00252 00253 // pass list back 00254 *devlist = res.names; 00255 return res.nnames; 00256 } 00257 00258 // Like open(). Return integer handle, used by functions below only. 00259 // type="ATA" or "SCSI". 00260 int deviceopen(const char *pathname, char *type){ 00261 if (!strcmp(type,"SCSI")) 00262 return open(pathname, O_RDWR | O_NONBLOCK); 00263 else if (!strcmp(type,"ATA")) 00264 return open(pathname, O_RDONLY | O_NONBLOCK); 00265 else 00266 return -1; 00267 } 00268 00269 // Like close(). Acts on handles returned by above function. 00270 int deviceclose(int fd){ 00271 return close(fd); 00272 } 00273 00274 #if defined(__sparc) 00275 // swap each 2-byte pairs in a sector 00276 static void swap_sector(void *p) 00277 { 00278 int i; 00279 char t, *cp = static_cast<char*>(p); 00280 for(i = 0; i < 256; i++) { 00281 t = cp[0]; cp[0] = cp[1]; cp[1] = t; 00282 cp += 2; 00283 } 00284 } 00285 #endif 00286 00287 // Interface to ATA devices. See os_linux.c 00288 int ata_command_interface(int fd, smart_command_set command, int select, char *data){ 00289 #if defined(__sparc) 00290 int err; 00291 00292 switch (command){ 00293 case CHECK_POWER_MODE: 00294 /* currently not recognized */ 00295 return -1; 00296 case READ_VALUES: 00297 return smart_read_data(fd, data); 00298 case READ_THRESHOLDS: 00299 return smart_read_thresholds(fd, data); 00300 case READ_LOG: 00301 return smart_read_log(fd, select, 1, data); 00302 case IDENTIFY: 00303 err = ata_identify(fd, data); 00304 if(err) return err; 00305 swap_sector(static_cast<void*>(data)); 00306 return 0; 00307 case PIDENTIFY: 00308 err = ata_pidentify(fd, data); 00309 if(err) return err; 00310 swap_sector(static_cast<void*>(data)); 00311 return 0; 00312 case ENABLE: 00313 return smart_enable(fd); 00314 case DISABLE: 00315 return smart_disable(fd); 00316 case STATUS: 00317 return smart_status(fd); 00318 case AUTO_OFFLINE: 00319 return smart_auto_offline(fd, select); 00320 case AUTOSAVE: 00321 return smart_auto_save(fd, select); 00322 case IMMEDIATE_OFFLINE: 00323 return smart_immediate_offline(fd, select); 00324 case STATUS_CHECK: 00325 return smart_status_check(fd); 00326 default: 00327 pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command); 00328 EXIT(1); 00329 break; 00330 } 00331 #else /* __sparc */ 00332 ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data); 00333 00334 /* Above smart_* routines uses undocumented ioctls of "dada" 00335 * driver, which is specific to SPARC Solaris. See 00336 * os_solaris_ata.s for further details. x86 Solaris seems not to 00337 * provide similar or alternative interface... */ 00338 if (printwarning(0)) 00339 return -1; 00340 #endif 00341 return -1; 00342 } 00343 00344 #include <errno.h> 00345 #include <sys/scsi/generic/commands.h> 00346 #include <sys/scsi/generic/status.h> 00347 #include <sys/scsi/impl/types.h> 00348 #include <sys/scsi/impl/uscsi.h> 00349 00350 // Interface to SCSI devices. See os_linux.c 00351 int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) 00352 { 00353 struct uscsi_cmd uscsi; 00354 00355 if (report > 0) { 00356 int k; 00357 const unsigned char * ucp = iop->cmnd; 00358 const char * np; 00359 00360 np = scsi_get_opcode_name(ucp[0]); 00361 pout(" [%s: ", np ? np : "<unknown opcode>"); 00362 for (k = 0; k < (int)iop->cmnd_len; ++k) 00363 pout("%02x ", ucp[k]); 00364 pout("]\n"); 00365 if ((report > 1) && 00366 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { 00367 int trunc = (iop->dxfer_len > 256) ? 1 : 0; 00368 00369 pout(" Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, 00370 (trunc ? " [only first 256 bytes shown]" : "")); 00371 dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); 00372 } 00373 } 00374 memset(&uscsi, 0, sizeof (uscsi)); 00375 00376 uscsi.uscsi_cdb = reinterpret_cast<char*>(iop->cmnd); 00377 uscsi.uscsi_cdblen = iop->cmnd_len; 00378 if (iop->timeout == 0) 00379 uscsi.uscsi_timeout = 60; /* 60 seconds */ 00380 else 00381 uscsi.uscsi_timeout = iop->timeout; 00382 uscsi.uscsi_bufaddr = reinterpret_cast<char*>(iop->dxferp); 00383 uscsi.uscsi_buflen = iop->dxfer_len; 00384 uscsi.uscsi_rqbuf = reinterpret_cast<char*>(iop->sensep); 00385 uscsi.uscsi_rqlen = iop->max_sense_len; 00386 00387 switch (iop->dxfer_dir) { 00388 case DXFER_NONE: 00389 case DXFER_FROM_DEVICE: 00390 uscsi.uscsi_flags = USCSI_READ; 00391 break; 00392 case DXFER_TO_DEVICE: 00393 uscsi.uscsi_flags = USCSI_WRITE; 00394 break; 00395 default: 00396 return -EINVAL; 00397 } 00398 uscsi.uscsi_flags |= (USCSI_ISOLATE | USCSI_RQENABLE); 00399 00400 if (ioctl(fd, USCSICMD, &uscsi)) { 00401 int err = errno; 00402 00403 if (! ((EIO == err) && uscsi.uscsi_status)) 00404 return -err; 00405 /* errno is set to EIO when a non-zero SCSI completion status given */ 00406 } 00407 00408 iop->scsi_status = uscsi.uscsi_status; 00409 iop->resid = uscsi.uscsi_resid; 00410 iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid; 00411 00412 if (report > 0) { 00413 int trunc; 00414 int len = iop->resp_sense_len; 00415 00416 if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 00417 iop->sensep && (len > 3)) { 00418 if ((iop->sensep[0] & 0x7f) > 0x71) 00419 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", 00420 iop->scsi_status, iop->sensep[1] & 0xf, 00421 iop->sensep[2], iop->sensep[3]); 00422 else 00423 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", 00424 iop->scsi_status, iop->sensep[2] & 0xf, 00425 iop->sensep[12], iop->sensep[13]); 00426 if (report > 1) { 00427 pout(" >>> Sense buffer, len=%d:\n", len); 00428 dStrHex((const char *)iop->sensep, ((len > 252) ? 252 : len) , 1); 00429 } 00430 } else if (iop->scsi_status) 00431 pout(" status=%x\n", iop->scsi_status); 00432 if (iop->resid) 00433 pout(" dxfer_len=%d, resid=%d\n", iop->dxfer_len, iop->resid); 00434 if (report > 1) { 00435 len = iop->dxfer_len - iop->resid; 00436 if (len > 0) { 00437 trunc = (len > 256) ? 1 : 0; 00438 pout(" Incoming data, len=%d%s:\n", len, 00439 (trunc ? " [only first 256 bytes shown]" : "")); 00440 dStrHex((char *)iop->dxferp, (trunc ? 256 : len) , 1); 00441 } 00442 } 00443 } 00444 return 0; 00445 }
1.7.4