Select Git revision
generate_sample.py
scsiprint.cpp 24.21 KiB
/*
* scsiprint.c
*
* Home page of code is: http://smartmontools.sourceforge.net
*
* Copyright (C) 2002-3 Bruce Allen <smartmontools-support@lists.sourceforge.net>
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
*
* Additional SCSI work:
* Copyright (C) 2003 Douglas Gilbert <dougg@torque.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* You should have received a copy of the GNU General Public License
* (for example COPYING); if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This code was originally developed as a Senior Thesis by Michael Cornwell
* at the Concurrent Systems Laboratory (now part of the Storage Systems
* Research Center), Jack Baskin School of Engineering, University of
* California, Santa Cruz. http://ssrc.soe.ucsc.edu/
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "extern.h"
#include "scsicmds.h"
#include "scsiprint.h"
#include "smartctl.h"
#include "utility.h"
#define GBUF_SIZE 65535
const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.49 2003/06/17 06:10:08 dpgilbert Exp $"
EXTERN_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
// control block which points to external global control variables
extern smartmonctrl *con;
UINT8 gBuf[GBUF_SIZE];
#define LOG_RESP_LEN 252
#define LOG_RESP_TAPE_ALERT_LEN 0x144
#define LOG_RESP_SELF_TEST_LEN 0x194
/* Log pages supported */
static int gSmartLPage = 0; /* Informational Exceptions log page */
static int gTempLPage = 0;
static int gSelfTestLPage = 0;
static int gStartStopLPage = 0;
static int gTapeAlertsLPage = 0;
/* Mode pages supported */
static int gIecMPage = 1; /* N.B. assume it until we know otherwise */
static void scsiGetSupportedLogPages(int device)
{
int i, err;
if ((err = scsiLogSense(device, SUPPORTED_LOG_PAGES, gBuf,
LOG_RESP_LEN, 0))) {
if (con->reportscsiioctl > 0)
pout("Log Sense for supported pages failed [%s]\n",
scsiErrString(err));
return;
}
for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
switch (gBuf[i])
{
case TEMPERATURE_PAGE:
gTempLPage = 1;
break;
case STARTSTOP_CYCLE_COUNTER_PAGE:
gStartStopLPage = 1;
break;
case SELFTEST_RESULTS_PAGE:
gSelfTestLPage = 1;
break;
case IE_LOG_PAGE:
gSmartLPage = 1;
break;
case TAPE_ALERTS_PAGE:
gTapeAlertsLPage = 1;
break;
default:
break;
}
}
}
void scsiGetSmartData(int device)
{
UINT8 asc;
UINT8 ascq;
UINT8 currenttemp = 0;
const char * cp;
int err;
QUIETON(con);
if ((err = scsiCheckIE(device, gSmartLPage, gTempLPage,
&asc, &ascq, ¤ttemp))) {
/* error message already announced */
QUIETOFF(con);
return;
}
QUIETOFF(con);
cp = scsiGetIEString(asc, ascq);
if (cp) {
QUIETON(con);
pout("SMART Health Status: %s [asc=%x,ascq=%x]\n", cp, asc, ascq);
QUIETOFF(con);
} else if (gIecMPage)
pout("SMART Health Status: OK\n");
if (currenttemp && !gTempLPage) {
if (255 != currenttemp)
pout("Current Drive Temperature: %d C\n", currenttemp);
else
pout("Current Drive Temperature: <not available>\n");
}
}
// Returns number of logged errors or zero if none or -1 if fetching
// TapeAlerts fails
static char *severities = "CWI";
static int scsiGetTapeAlertsData(int device, int peripheral_type)
{
unsigned short pagelength;
unsigned short parametercode;
int i, err;
char *s;
const char *ts;
int failures = 0;
QUIETON(con);
if ((err = scsiLogSense(device, TAPE_ALERTS_PAGE, gBuf,
LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
QUIETOFF(con);
return -1;
}
if (gBuf[0] != 0x2e) {
pout("TapeAlerts Log Sense Failed\n");
QUIETOFF(con);
return -1;
}
pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
for (s=severities; *s; s++) {
for (i = 4; i < pagelength; i += 5) {
parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
if (gBuf[i + 4]) {
ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
scsiTapeAlertsChangerDevice(parametercode) :
scsiTapeAlertsTapeDevice(parametercode);
if (*ts == *s) {
if (!failures)
pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
pout("[0x%02x] %s\n", parametercode, ts);
failures += 1;
}
}
}
}
QUIETOFF(con);
if (! failures)
pout("TapeAlert: OK\n");
return failures;
}
void scsiGetStartStopData(int device)
{
UINT32 currentStartStop;
UINT32 recommendedStartStop;
int err, len, k;
char str[6];
if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_PAGE, gBuf,
LOG_RESP_LEN, 0))) {
QUIETON(con);
pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
QUIETOFF(con);
return;
}
if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_PAGE) {
QUIETON(con);
pout("StartStop Log Sense Failed, page mismatch\n");
QUIETOFF(con);
return;
}
len = ((gBuf[2] << 8) | gBuf[3]) + 4;
if (len > 13) {
for (k = 0; k < 2; ++k)
str[k] = gBuf[12 + k];
str[k] = '\0';
pout("Manufactured in week %s of year ", str);
for (k = 0; k < 4; ++k)
str[k] = gBuf[8 + k];
str[k] = '\0';
pout("%s\n", str);
}
if (len > 39) {
recommendedStartStop = (gBuf[28] << 24) | (gBuf[29] << 16) |
(gBuf[30] << 8) | gBuf[31];
currentStartStop = (gBuf[36] << 24) | (gBuf[37] << 16) |
(gBuf[38] << 8) | gBuf[39];
pout("Current start stop count: %u times\n", currentStartStop);
pout("Recommended start stop count: %u times\n",
recommendedStartStop);
}
}
static void scsiPrintErrorCounterLog(int device)
{
struct scsiErrorCounter errCounterArr[3];
struct scsiErrorCounter * ecp;
struct scsiNonMediumError nme;
int found[3] = {0, 0, 0};
const char * pageNames[3] = {"read: ", "write: ", "verify: "};
int k;
double processed_gb;
if (0 == scsiLogSense(device, READ_ERROR_COUNTER_PAGE, gBuf,
LOG_RESP_LEN, 0)) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
found[0] = 1;
}
if (0 == scsiLogSense(device, WRITE_ERROR_COUNTER_PAGE, gBuf,
LOG_RESP_LEN, 0)) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
found[1] = 1;
}
if (0 == scsiLogSense(device, VERIFY_ERROR_COUNTER_PAGE, gBuf,
LOG_RESP_LEN, 0)) {
scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
ecp = &errCounterArr[2];
for (k = 0; k < 7; ++k) {
if (ecp->gotPC[k] && ecp->counter[k]) {
found[2] = 1;
break;
}
}
}
if (found[0] || found[1] || found[2]) {
pout("\nError counter log:\n");
pout(" Errors Corrected Total Total "
"Correction Gigabytes Total\n");
pout(" delay: [rereads/ errors "
"algorithm processed uncorrected\n");
pout(" minor | major rewrites] corrected "
"invocations [10^9 bytes] errors\n");
for (k = 0; k < 3; ++k) {
if (! found[k])
continue;
ecp = &errCounterArr[k];
pout("%s%8llu %8llu %8llu %8llu %8llu",
pageNames[k], ecp->counter[0], ecp->counter[1],
ecp->counter[2], ecp->counter[3], ecp->counter[4]);
processed_gb = ecp->counter[5] / 1000000000.0;
pout(" %12.3f %8llu\n", processed_gb, ecp->counter[6]);
}
}
else
pout("\nDevice does not support Error Counter logging\n");
if (0 == scsiLogSense(device, NON_MEDIUM_ERROR_PAGE, gBuf,
LOG_RESP_LEN, 0)) {
scsiDecodeNonMediumErrPage(gBuf, &nme);
if (nme.gotPC0)
pout("\nNon-medium error count: %8llu\n", nme.counterPC0);
}
}
const char * self_test_code[] = {
"Default ",
"Background short",
"Background long ",
"Reserved(3) ",
"Abort background",
"Foreground short",
"Foreground long ",
"Reserved(7) "
};
const char * self_test_result[] = {
"Completed ",
"Interrupted ('-X' switch)",
"Interrupted (bus reset ?)",
"Unknown error, incomplete",
"Completed, segment failed",
"Failed in first segment ",
"Failed in second segment ",
"Failed in segment --> ",
"Reserved(8) ",
"Reserved(9) ",
"Reserved(10) ",
"Reserved(11) ",
"Reserved(12) ",
"Reserved(13) ",
"Reserved(14) ",
"Self test in progress ..."
};
// See Working Draft SCSI Primary Commands - 3 (SPC-3) pages 231-233
// T10/1416-D Rev 10
static int scsiPrintSelfTest(int device)
{
int num, k, n, res, err, durationSec;
int noheader = 1;
UINT8 * ucp;
unsigned long long ull=0;
if ((err = scsiLogSense(device, SELFTEST_RESULTS_PAGE, gBuf,
LOG_RESP_SELF_TEST_LEN, 0))) {
QUIETON(con);
pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
QUIETOFF(con);
return 1;
}
if (gBuf[0] != SELFTEST_RESULTS_PAGE) {
QUIETON(con);
pout("Self-test Log Sense Failed, page mismatch\n");
QUIETOFF(con);
return 1;
}
// compute page length
num = (gBuf[2] << 8) + gBuf[3];
// Log sense page length 0x190 bytes
if (num != 0x190) {
QUIETON(con);
pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
QUIETOFF(con);
return 1;
}
// loop through the twenty possible entries
for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
int i;
// timestamp in power-on hours (or zero if test in progress)
n = (ucp[6] << 8) | ucp[7];
// The spec says "all 20 bytes will be zero if no test" but
// DG has found otherwise. So this is a heuristic.
if ((0 == n) && (0 == ucp[4]))
break;
// only print header if needed
if (noheader) {
pout("\nSMART Self-test log\n");
pout("Num Test Status segment "
"LifeTime LBA_first_err [SK ASC ASQ]\n");
pout(" Description number "
"(hours)\n");
noheader=0;
}
// print parameter code (test number) & self-test code text
pout("#%2d %s", (ucp[0] << 8) | ucp[1],
self_test_code[(ucp[4] >> 5) & 0x7]);
// self-test result
res = ucp[4] & 0xf;
pout(" %s", self_test_result[res]);
// self-test number identifies test that failed and consists
// of either the number of the segment that failed during
// the test, or the number of the test that failed and the
// number of the segment in which the test was run, using a
// vendor-specific method of putting both numbers into a
// single byte.
if (ucp[5])
pout(" %3d", (int)ucp[5]);
else
pout(" -");
// print time that the self-test was completed
if (n==0 && res==0xf)
// self-test in progress
pout(" NOW");
else
pout(" %5d", n);
// construct 8-byte integer address of first failure
for (i = 0; i < 8; i++) {
ull <<= 8;
ull |= ucp[i+8];
}
// print Address of First Failure, if sensible
if ((0xffffffffffffffffULL != ull) && (res > 0) && ( res < 0xf))
pout(" 0x%16llx", ull);
else
pout(" -");
// if sense key nonzero, then print it, along with
// additional sense code and additional sense code qualifier
if (ucp[16] & 0xf)
pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
else
pout(" [- - -]\n");
}
// if header never printed, then there was no output
if (noheader)
pout("No self-tests have been logged\n");
else
pout("\n");
if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec)) &&
(durationSec > 0))
pout("Long (extended) Self Test duration: %d seconds "
"[%.1f minutes]\n", durationSec, durationSec / 60.0);
return 0;
}
static const char * peripheral_dt_arr[] = {
"disk",
"tape",
"printer",
"processor",
"optical disk(4)",
"CD/DVD",
"scanner",
"optical disk(7)",
"medium changer",
"communications",
"graphics(10)",
"graphics(11)",
"storage array",
"enclosure",
"simplified disk",
"optical card reader"
};
/* Returns 0 on success */
static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
{
char manufacturer[9];
char product[17];
char revision[5];
char timedatetz[64];
struct scsi_iec_mode_page iec;
int err, len;
int is_tape = 0;
int peri_dt = 0;
memset(gBuf, 0, 36);
if ((err = scsiStdInquiry(device, gBuf, 36))) {
QUIETON(con);
pout("Standard Inquiry failed [%s]\n", scsiErrString(err));
QUIETOFF(con);
return 1;
}
len = gBuf[4] + 5;
peri_dt = gBuf[0] & 0x1f;
if (peripheral_type)
*peripheral_type = peri_dt;
if (! all)
return 0;
if (len >= 36) {
memset(manufacturer, 0, sizeof(manufacturer));
strncpy(manufacturer, &gBuf[8], 8);
memset(product, 0, sizeof(product));
strncpy(product, &gBuf[16], 16);
memset(revision, 0, sizeof(revision));
strncpy(revision, &gBuf[32], 4);
pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
/* should use VPD page 0x83 and fall back to this page (0x80)
* if 0x83 not supported. NAA requires a lot of decoding code */
len = gBuf[3];
gBuf[4 + len] = '\0';
pout("Serial number: %s\n", &gBuf[4]);
}
else if (con->reportscsiioctl > 0) {
QUIETON(con);
if (SIMPLE_ERR_BAD_RESP == err)
pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
else
pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
QUIETOFF(con);
}
} else {
QUIETON(con);
pout("Short INQUIRY response, skip product id\n");
QUIETOFF(con);
}
// print SCSI peripheral device type
if (peri_dt < (sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0])))
pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
else
pout("Device type: <%d>\n", peri_dt);
// print current time and date and timezone
dateandtimezone(timedatetz);
pout("Local Time is: %s\n", timedatetz);
if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
(SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
is_tape = 1;
// See if unit accepts SCSI commmands from us
if ((err = scsiTestUnitReady(device))) {
if (SIMPLE_ERR_NOT_READY == err) {
QUIETON(con);
pout("device is NOT READY (media absent, spun down, etc)\n");
QUIETOFF(con);
} else {
QUIETON(con);
pout("device Test Unit Ready [%s]\n", scsiErrString(err));
QUIETOFF(con);
}
return 0;
}
if ((err = scsiFetchIECmpage(device, &iec))) {
if (!is_tape) {
QUIETON(con);
pout("Device does not support SMART");
if (con->reportscsiioctl > 0)
pout(" [%s]\n", scsiErrString(err));
else
pout("\n");
if (SIMPLE_ERR_BAD_RESP == err) {
pout(">> Terminate command early due to bad response to IEC "
"mode page\n");
QUIETOFF(con);
gIecMPage = 0;
return 1;
}
QUIETOFF(con);
}
gIecMPage = 0;
return 0;
}
if (!is_tape)
pout("Device supports SMART and is %s\n",
(scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
pout("%s\n", (scsi_IsWarningEnabled(&iec)) ?
"Temperature Warning Enabled" :
"Temperature Warning Disabled or Not Supported");
return 0;
}
static int scsiSmartEnable(int device)
{
struct scsi_iec_mode_page iec;
int err;
if ((err = scsiFetchIECmpage(device, &iec))) {
QUIETON(con);
pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
QUIETOFF(con);
return 1;
}
if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
QUIETON(con);
pout("unable to enable Exception control and warning [%s]\n",
scsiErrString(err));
QUIETOFF(con);
return 1;
}
/* Need to refetch 'iec' since could be modified by previous call */
if ((err = scsiFetchIECmpage(device, &iec))) {
pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
return 1;
}
pout("Informational Exceptions (SMART) %s\n",
scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
pout("Temperature warning %s\n",
scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
return 0;
}
static int scsiSmartDisable(int device)
{
struct scsi_iec_mode_page iec;
int err;
if ((err = scsiFetchIECmpage(device, &iec))) {
QUIETON(con);
pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
QUIETOFF(con);
return 1;
}
if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
QUIETON(con);
pout("unable to disable Exception control and warning [%s]\n",
scsiErrString(err));
QUIETOFF(con);
return 1;
}
/* Need to refetch 'iec' since could be modified by previous call */
if ((err = scsiFetchIECmpage(device, &iec))) {
pout("unable to fetch IEC (SMART) mode page [%s]\n",
scsiErrString(err));
return 1;
}
pout("Informational Exceptions (SMART) %s\n",
scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
pout("Temperature warning %s\n",
scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
return 0;
}
void scsiPrintTemp(int device)
{
UINT8 temp = 0;
UINT8 trip = 0;
if (scsiGetTemp(device, &temp, &trip))
return;
if (temp) {
if (255 != temp)
pout("Current Drive Temperature: %d C\n", temp);
else
pout("Current Drive Temperature: <not available>\n");
}
if (trip)
pout("Drive Trip Temperature: %d C\n", trip);
}
// Compares failure type to policy in effect, and either exits or
// simply returns to the calling routine.
static void failuretest(int type, int returnvalue)
{
// If this is an error in an "optional" SMART command
if (type == OPTIONAL_CMD) {
if (con->conservative) {
pout("An optional SMART command has failed: exiting.\n"
"To continue, set the tolerance level to something other "
"than 'conservative'\n");
exit(returnvalue);
}
return;
}
// If this is an error in a "mandatory" SMART command
if (type==MANDATORY_CMD) {
if (con->permissive)
return;
pout("A mandatory SMART command has failed: exiting. To continue, "
"use the -T option to set the tolerance level to 'permissive'\n");
exit(returnvalue);
}
pout("Smartctl internal error in failuretest(type=%d). Please contact "
"%s\n",type,PROJECTHOME);
exit(returnvalue|FAILCMD);
}
/* Main entry point used by smartctl command. Return 0 for success */
int scsiPrintMain(const char *dev_name, int fd)
{
int checkedSupportedLogPages = 0;
UINT8 peripheral_type = 0;
int returnval=0;
int res;
if (scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo)) {
failuretest(MANDATORY_CMD, returnval |= FAILID);
}
if (con->smartenable) {
if (scsiSmartEnable(fd))
failuretest(MANDATORY_CMD, returnval |= FAILSMART);
}
if (con->smartdisable) {
if (scsiSmartDisable(fd))
failuretest(MANDATORY_CMD,returnval |= FAILSMART);
}
if (con->checksmart) {
scsiGetSupportedLogPages(fd);
checkedSupportedLogPages = 1;
if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
(SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
if (gTapeAlertsLPage) {
if (con->driveinfo)
pout("TapeAlert Supported\n");
if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
}
else
pout("TapeAlert Not Supported\n");
if (gTempLPage)
scsiPrintTemp(fd);
if (gStartStopLPage)
scsiGetStartStopData(fd);
} else { /* disk, cd/dvd, enclosure, etc */
scsiGetSmartData(fd);
if (gTempLPage)
scsiPrintTemp(fd);
if (gStartStopLPage)
scsiGetStartStopData(fd);
}
}
if (con->smarterrorlog)
scsiPrintErrorCounterLog(fd);
if (con->smartselftestlog) {
if (! checkedSupportedLogPages)
scsiGetSupportedLogPages(fd);
res = 0;
if (gSelfTestLPage)
res = scsiPrintSelfTest(fd);
else {
pout("Device does not support Self Test logging\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
if (0 != res)
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
if (con->smartexeoffimmediate) {
if (scsiSmartDefaultSelfTest(fd)) {
pout( "Default Self Test Failed\n");
return returnval;
}
pout("Default Self Test Successful\n");
}
if (con->smartshortcapselftest) {
if (scsiSmartShortCapSelfTest(fd)) {
pout("Short Foreground Self Test Failed\n");
return returnval;
}
pout("Short Foreground Self Test Successful\n");
}
if (con->smartshortselftest ) {
if ( scsiSmartShortSelfTest(fd)) {
pout("Short Background Self Test Failed\n");
return returnval;
}
pout("Short Background Self Test has begun\n");
pout("Use smartctl -X to abort test\n");
}
if (con->smartextendselftest) {
if (scsiSmartExtendSelfTest(fd)) {
pout("Extended Background Self Test Failed\n");
return returnval;
}
pout("Extended Background Self Test has begun\n");
pout("Use smartctl -X to abort test\n");
}
if (con->smartextendcapselftest) {
if (scsiSmartExtendCapSelfTest(fd)) {
pout("Extended Foreground Self Test Failed\n");
return returnval;
}
pout("Extended Foreground Self Test Successful\n");
}
if (con->smartselftestabort) {
if (scsiSmartSelfTestAbort(fd)) {
pout("Self Test Abort Failed\n");
return returnval;
}
pout("Self Test returned without error\n");
}
return returnval;
}