|
smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
|
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 }
1.7.4