smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
os_netbsd.cpp
Go to the documentation of this file.
00001 /*
00002  * os_netbsd.cpp
00003  *
00004  * Home page of code is: http://smartmontools.sourceforge.net
00005  *
00006  * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * (for example COPYING); if not, write to the Free Software Foundation,
00015  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016  *
00017  */
00018 
00019 #include "config.h"
00020 #include "int64.h"
00021 #include "atacmds.h"
00022 #include "scsicmds.h"
00023 #include "utility.h"
00024 #include "os_netbsd.h"
00025 
00026 #include <errno.h>
00027 #include <unistd.h>
00028 
00029 const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 3806 2013-03-29 20:17:03Z chrfranke $"
00030   OS_NETBSD_H_CVSID;
00031 
00032 /* global variable holding byte count of allocated memory */
00033 extern long long bytes;
00034 
00035 enum warnings {
00036   BAD_SMART, NO_3WARE, NO_ARECA, MAX_MSG
00037 };
00038 
00039 /* Utility function for printing warnings */
00040 void
00041 printwarning(int msgNo, const char *extra)
00042 {
00043   static int printed[] = {0, 0};
00044   static const char *message[] = {
00045     "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
00046     PACKAGE_STRING " does not currently support twe(4) and twa(4) devices (3ware Escalade, Apache)\n",
00047   };
00048 
00049   if (msgNo >= 0 && msgNo <= MAX_MSG) {
00050     if (!printed[msgNo]) {
00051       printed[msgNo] = 1;
00052       pout("%s", message[msgNo]);
00053       if (extra)
00054         pout("%s", extra);
00055     }
00056   }
00057   return;
00058 }
00059 
00060 static const char *net_dev_prefix = "/dev/";
00061 static const char *net_dev_ata_disk = "wd";
00062 static const char *net_dev_scsi_disk = "sd";
00063 static const char *net_dev_scsi_tape = "enrst";
00064 
00065 /* Guess device type (ATA or SCSI) based on device name */
00066 int
00067 guess_device_type(const char *dev_name)
00068 {
00069   int len;
00070   int dev_prefix_len = strlen(net_dev_prefix);
00071 
00072   if (!dev_name || !(len = strlen(dev_name)))
00073     return CONTROLLER_UNKNOWN;
00074 
00075   if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
00076     if (len <= dev_prefix_len)
00077       return CONTROLLER_UNKNOWN;
00078     else
00079       dev_name += dev_prefix_len;
00080   }
00081   if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
00082     return CONTROLLER_ATA;
00083 
00084   if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
00085     return CONTROLLER_SCSI;
00086 
00087   if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
00088     return CONTROLLER_SCSI;
00089 
00090   return CONTROLLER_UNKNOWN;
00091 }
00092 
00093 int
00094 get_dev_names(char ***names, const char *prefix)
00095 {
00096   char *disknames, *p, **mp;
00097   int n = 0;
00098   int sysctl_mib[2];
00099   size_t sysctl_len;
00100 
00101   *names = NULL;
00102 
00103   sysctl_mib[0] = CTL_HW;
00104   sysctl_mib[1] = HW_DISKNAMES;
00105   if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
00106     pout("Failed to get value of sysctl `hw.disknames'\n");
00107     return -1;
00108   }
00109   if (!(disknames = (char *)malloc(sysctl_len))) {
00110     pout("Out of memory constructing scan device list\n");
00111     return -1;
00112   }
00113   if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
00114     pout("Failed to get value of sysctl `hw.disknames'\n");
00115     return -1;
00116   }
00117   if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
00118     pout("Out of memory constructing scan device list\n");
00119     return -1;
00120   }
00121   for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
00122     if (strncmp(p, prefix, strlen(prefix))) {
00123       continue;
00124     }
00125     mp[n] = (char *)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
00126     if (!mp[n]) {
00127       pout("Out of memory constructing scan device list\n");
00128       return -1;
00129     }
00130     sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
00131     bytes += strlen(mp[n]) + 1;
00132     n++;
00133   }
00134 
00135   mp = (char **)realloc(mp, n * (sizeof(char *)));
00136   bytes += (n) * (sizeof(char *));
00137   *names = mp;
00138   return n;
00139 }
00140 
00141 int
00142 make_device_names(char ***devlist, const char *name)
00143 {
00144   if (!strcmp(name, "SCSI"))
00145     return get_dev_names(devlist, net_dev_scsi_disk);
00146   else if (!strcmp(name, "ATA"))
00147     return get_dev_names(devlist, net_dev_ata_disk);
00148   else
00149     return 0;
00150 }
00151 
00152 int
00153 deviceopen(const char *pathname, char *type)
00154 {
00155   if (!strcmp(type, "SCSI")) {
00156     int fd = open(pathname, O_RDWR | O_NONBLOCK);
00157     if (fd < 0 && errno == EROFS)
00158       fd = open(pathname, O_RDONLY | O_NONBLOCK);
00159     return fd;
00160   } else if (!strcmp(type, "ATA"))
00161     return open(pathname, O_RDWR | O_NONBLOCK);
00162   else
00163     return -1;
00164 }
00165 
00166 int
00167 deviceclose(int fd)
00168 {
00169   return close(fd);
00170 }
00171 
00172 int
00173 ata_command_interface(int fd, smart_command_set command, int select, char *data)
00174 {
00175   struct atareq req;
00176   unsigned char inbuf[DEV_BSIZE];
00177   int retval, copydata = 0;
00178 
00179   memset(&req, 0, sizeof(req));
00180   req.timeout = 1000;
00181 
00182   memset(&inbuf, 0, sizeof(inbuf));
00183 
00184   switch (command) {
00185   case READ_VALUES:
00186     req.flags = ATACMD_READ;
00187     req.features = WDSM_RD_DATA;
00188     req.command = WDCC_SMART;
00189     req.databuf = (char *)inbuf;
00190     req.datalen = sizeof(inbuf);
00191     req.cylinder = WDSMART_CYL;
00192     copydata = 1;
00193     break;
00194   case READ_THRESHOLDS:
00195     req.flags = ATACMD_READ;
00196     req.features = WDSM_RD_THRESHOLDS;
00197     req.command = WDCC_SMART;
00198     req.databuf = (char *)inbuf;
00199     req.datalen = sizeof(inbuf);
00200     req.cylinder = WDSMART_CYL;
00201     copydata = 1;
00202     break;
00203   case READ_LOG:
00204     req.flags = ATACMD_READ;
00205     req.features = ATA_SMART_READ_LOG_SECTOR;   /* XXX missing from wdcreg.h */
00206     req.command = WDCC_SMART;
00207     req.databuf = (char *)inbuf;
00208     req.datalen = sizeof(inbuf);
00209     req.cylinder = WDSMART_CYL;
00210     req.sec_num = select;
00211     req.sec_count = 1;
00212     copydata = 1;
00213     break;
00214   case WRITE_LOG:
00215     memcpy(inbuf, data, 512);
00216     req.flags = ATACMD_WRITE;
00217     req.features = ATA_SMART_WRITE_LOG_SECTOR;  /* XXX missing from wdcreg.h */
00218     req.command = WDCC_SMART;
00219     req.databuf = (char *)inbuf;
00220     req.datalen = sizeof(inbuf);
00221     req.cylinder = WDSMART_CYL;
00222     req.sec_num = select;
00223     req.sec_count = 1;
00224     break;
00225   case IDENTIFY:
00226     req.flags = ATACMD_READ;
00227     req.command = WDCC_IDENTIFY;
00228     req.databuf = (char *)inbuf;
00229     req.datalen = sizeof(inbuf);
00230     copydata = 1;
00231     break;
00232   case PIDENTIFY:
00233     req.flags = ATACMD_READ;
00234     req.command = ATAPI_IDENTIFY_DEVICE;
00235     req.databuf = (char *)inbuf;
00236     req.datalen = sizeof(inbuf);
00237     copydata = 1;
00238     break;
00239   case ENABLE:
00240     req.flags = ATACMD_READREG;
00241     req.features = WDSM_ENABLE_OPS;
00242     req.command = WDCC_SMART;
00243     req.cylinder = WDSMART_CYL;
00244     break;
00245   case DISABLE:
00246     req.flags = ATACMD_READREG;
00247     req.features = WDSM_DISABLE_OPS;
00248     req.command = WDCC_SMART;
00249     req.cylinder = WDSMART_CYL;
00250     break;
00251   case AUTO_OFFLINE:
00252     /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
00253     req.flags = ATACMD_READREG;
00254     req.features = ATA_SMART_AUTO_OFFLINE;      /* XXX missing from wdcreg.h */
00255     req.command = WDCC_SMART;
00256     req.cylinder = WDSMART_CYL;
00257     req.sec_count = select;
00258     break;
00259   case AUTOSAVE:
00260     req.flags = ATACMD_READREG;
00261     req.features = ATA_SMART_AUTOSAVE;  /* XXX missing from wdcreg.h */
00262     req.command = WDCC_SMART;
00263     req.cylinder = WDSMART_CYL;
00264     req.sec_count = select;
00265     break;
00266   case IMMEDIATE_OFFLINE:
00267     /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
00268     req.flags = ATACMD_READREG;
00269     req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */
00270     req.command = WDCC_SMART;
00271     req.cylinder = WDSMART_CYL;
00272     req.sec_num = select;
00273     req.sec_count = 1;
00274     break;
00275   case STATUS:          /* should return 0 if SMART is enabled at all */
00276   case STATUS_CHECK:    /* should return 0 if disk's health is ok */
00277     req.flags = ATACMD_READREG;
00278     req.features = WDSM_STATUS;
00279     req.command = WDCC_SMART;
00280     req.cylinder = WDSMART_CYL;
00281     break;
00282   case CHECK_POWER_MODE:
00283     req.flags = ATACMD_READREG;
00284     req.command = WDCC_CHECK_PWR;
00285     break;
00286   default:
00287     pout("Unrecognized command %d in ata_command_interface()\n", command);
00288     errno = ENOSYS;
00289     return -1;
00290   }
00291 
00292   if (command == STATUS_CHECK || command == AUTOSAVE || command == AUTO_OFFLINE) {
00293     char buf[512];
00294 
00295     unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
00296 
00297     if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
00298       perror("Failed command");
00299       return -1;
00300     }
00301     if (req.retsts != ATACMD_OK) {
00302       return -1;
00303     }
00304     /* Cyl low and Cyl high unchanged means "Good SMART status" */
00305     if (req.cylinder == normal)
00306       return 0;
00307 
00308     /* These values mean "Bad SMART status" */
00309     if (req.cylinder == failed)
00310       return 1;
00311 
00312     /* We haven't gotten output that makes sense; 
00313      * print out some debugging info */
00314     snprintf(buf, sizeof(buf),
00315       "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
00316       (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
00317       (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
00318       (int) req.error);
00319     printwarning(BAD_SMART, buf);
00320     return 0;
00321   }
00322 
00323   if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
00324     perror("Failed command");
00325     return -1;
00326   }
00327   if (req.retsts != ATACMD_OK) {
00328     return -1;
00329   }
00330 
00331   if (command == CHECK_POWER_MODE)
00332     data[0] = req.sec_count;
00333 
00334   if (copydata)
00335     memcpy(data, inbuf, 512);
00336 
00337   return 0;
00338 }
00339 
00340 int
00341 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
00342 {
00343   struct scsireq sc;
00344 
00345   if (report > 0) {
00346     size_t k;
00347 
00348     const unsigned char *ucp = iop->cmnd;
00349     const char *np;
00350 
00351     np = scsi_get_opcode_name(ucp[0]);
00352     pout(" [%s: ", np ? np : "<unknown opcode>");
00353     for (k = 0; k < iop->cmnd_len; ++k)
00354       pout("%02x ", ucp[k]);
00355     if ((report > 1) &&
00356       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
00357       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
00358 
00359       pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
00360         (trunc ? " [only first 256 bytes shown]" : ""));
00361       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
00362     } else
00363       pout("]");
00364   }
00365   memset(&sc, 0, sizeof(sc));
00366   memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
00367   sc.cmdlen = iop->cmnd_len;
00368   sc.databuf = (char *)iop->dxferp;
00369   sc.datalen = iop->dxfer_len;
00370   sc.senselen = iop->max_sense_len;
00371   sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
00372   sc.flags =
00373     (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
00374     (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
00375 
00376   if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
00377     warn("error sending SCSI ccb");
00378     return -1;
00379   }
00380   iop->resid = sc.datalen - sc.datalen_used;
00381   iop->scsi_status = sc.status;
00382   if (iop->sensep) {
00383     memcpy(iop->sensep, sc.sense, sc.senselen_used);
00384     iop->resp_sense_len = sc.senselen_used;
00385   }
00386   if (report > 0) {
00387     int trunc;
00388 
00389     pout("  status=0\n");
00390     trunc = (iop->dxfer_len > 256) ? 1 : 0;
00391 
00392     pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
00393       (trunc ? " [only first 256 bytes shown]" : ""));
00394     dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
00395   }
00396   switch (sc.retsts) {
00397     case SCCMD_OK:
00398       return 0;
00399     case SCCMD_TIMEOUT:
00400       return -ETIMEDOUT;
00401     case SCCMD_BUSY:
00402       return -EBUSY;
00403     default:
00404       return -EIO;
00405   }
00406 }
00407 
00408 /* print examples for smartctl */
00409 void 
00410 print_smartctl_examples()
00411 {
00412   char p;
00413 
00414   p = 'a' + getrawpartition();
00415   printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
00416 #ifdef HAVE_GETOPT_LONG
00417   printf(
00418     "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
00419     "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
00420     "                                              (Enables SMART on first disk)\n\n"
00421     "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
00422     "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
00423     "                                      (Prints Self-Test & Attribute errors)\n",
00424     p, p, p, p
00425     );
00426 #else
00427   printf(
00428     "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
00429     "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
00430     "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
00431     "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
00432     "                                      (Prints Self-Test & Attribute errors)\n",
00433     p, p, p, p
00434     );
00435 #endif
00436   return;
00437 }