smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
os_solaris.cpp
Go to the documentation of this file.
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 }