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