/* * atacmds.c * * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net> * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org> * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> * * 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 <string.h> #include <errno.h> #include <stdlib.h> #include "atacmds.h" const char *CVSid1="$Id: atacmds.cpp,v 1.39 2002/11/11 10:50:08 ballen4705 Exp $" CVSID1; // These Drive Identity tables are taken from hdparm 5.2, and are also // given in the ATA/ATAPI specs for the IDENTIFY DEVICE command. Note // that SMART was first added into the ATA/ATAPI-3 Standard with // Revision 3 of the document, July 25, 1995. Look at the "Document // Status" revision commands at the beginning of // http://www.t13.org/project/d2008r6.pdf to see this. #define NOVAL_0 0x0000 #define NOVAL_1 0xffff /* word 81: minor version number */ #define MINOR_MAX 0x1C const char *minor_str[] = { /* word 81 value: */ "Device does not report version", /* 0x0000 */ "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */ "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */ "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */ "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */ "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ /* SMART NOT INCLUDED */ "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ "ATA-2 X3T10 948D revision 3", /* 0x0009 */ "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ "ATA-3 X3T10 2008D revision 6", /* 0x000b */ /* 1st VERSION WITH SMART */ "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */ "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */ "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */ "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */ "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */ "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */ "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012 */ "ATA/ATAPI-5 T13 1321D revision 3", /* 0x0013 */ "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */ "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */ "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016 */ "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */ "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */ "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */ "Reserved", /* 0x001a */ "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ "reserved" /* 0x001d */ "reserved" /* 0x001e */ "reserved" /* 0x001f-0xfffe*/ }; // NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device // attribute structures were NOT completely vendor specific. So any // disk that is ATA/ATAPI-4 or above can not be trusted to show the // vendor values in sensible format. // Negative values below are because it doesn't support SMART const int actual_ver[] = { /* word 81 value: */ 0, /* 0x0000 WARNING: */ 1, /* 0x0001 WARNING: */ 1, /* 0x0002 WARNING: */ 1, /* 0x0003 WARNING: */ 2, /* 0x0004 WARNING: This array */ 2, /* 0x0005 WARNING: corresponds */ -3, /*<== */ /* 0x0006 WARNING: *exactly* */ 2, /* 0x0007 WARNING: to the ATA/ */ -3, /*<== */ /* 0x0008 WARNING: ATAPI version */ 2, /* 0x0009 WARNING: listed in */ 3, /* 0x000a WARNING: the */ 3, /* 0x000b WARNING: minor_str */ 3, /* 0x000c WARNING: array */ 4, /* 0x000d WARNING: above. */ 4, /* 0x000e WARNING: */ 4, /* 0x000f WARNING: If you change */ 4, /* 0x0010 WARNING: that one, */ 4, /* 0x0011 WARNING: change this one */ 4, /* 0x0012 WARNING: too!!! */ 5, /* 0x0013 WARNING: */ 4, /* 0x0014 WARNING: */ 5, /* 0x0015 WARNING: */ 5, /* 0x0016 WARNING: */ 4, /* 0x0017 WARNING: */ 6, /* 0x0018 WARNING: */ 6, /* 0x0019 WARNING: */ 0, /* 0x001a WARNING: */ 6, /* 0x001b WARNING: */ 6, /* 0x001c WARNING: */ 0 /* 0x001d-0xfffe */ }; // A replacement for perror() that sends output to our choice of // printing. void syserror(const char *message){ const char *errormessage; // Get the correct system error message: if (errno<sys_nerr) errormessage=sys_errlist[errno]; else errormessage="unrecognized system error"; // Check that caller has handed a sensible string, and provide // appropriate output. See perrror(3) man page to understand better. if (message && *message) pout("%s: %s\n",message, errormessage); else pout("%s\n",errormessage); return; } // We no longer use this function, because the IOCTL appears to return // only the drive identity at the time that the system was booted // (perhaps from the BIOS. It doesn't correctly reflect the current // state information, and for example the checksum is usually // wrong. The replacement function follows afterwards #if (0) int ataReadHDIdentity (int device, struct hd_driveid *buf){ if (ioctl(device, HDIO_GET_IDENTITY, buf)){ perror ("Error ATA GET HD Identity Failed"); return -1; } return 0; } #endif // Reads current Device Identity info (512 bytes) into buf int ataReadHDIdentity (int device, struct hd_driveid *buf){ unsigned short driveidchecksum; unsigned char parms[HDIO_DRIVE_CMD_HDR_SIZE+sizeof(*buf)]= {WIN_IDENTIFY, 0, 0, 1,}; if (ioctl(device ,HDIO_DRIVE_CMD,parms)){ // See if device responds to packet command... parms[0]=WIN_PIDENTIFY; if (ioctl(device ,HDIO_DRIVE_CMD,parms)){ perror ("Error ATA GET HD Identity Failed"); return -1; } } // copy data into driveid structure memcpy(buf,parms+HDIO_DRIVE_CMD_HDR_SIZE,sizeof(*buf)); #if 0 // The following ifdef is a HACK to distinguish different versions // of the header file defining hd_driveid #ifdef CFA_REQ_EXT_ERROR_CODE driveidchecksum=buf->integrity_word; #else // Note -- the declaration that appears in // /usr/include/linux/hdreg.h: short words160_255[95], is WRONG. // It should say: short words160_255[96]. I have written to Andre // Hedrick about this on Oct 17 2002. Please remove this comment // once the fix has made it into the stock kernel tree. driveidchecksum=buf->words160_255[95]; #endif #else // This way is ugly and you may feel ill -- but it always works... { unsigned short *rawstructure= (unsigned short *)buf; driveidchecksum=rawstructure[255]; } #endif if ((driveidchecksum & 0x00ff) == 0x00a5){ // Device identity structure contains a checksum unsigned char cksum=0; int i; for (i=0;i<sizeof(*buf);i++) cksum+=parms[i+HDIO_DRIVE_CMD_HDR_SIZE]; if (cksum) checksumwarning("Drive Identity Structure"); } return 0; } // Returns ATA version as an integer, and a pointer to a string // describing which revision. Note that Revision 0 of ATA-3 does NOT // support SMART. For this one case we return -3 rather than +3 as // the version number. See notes above. int ataVersionInfo (const char** description, struct hd_driveid *drive, unsigned short *minor){ unsigned short major; int i; // get major and minor ATA revision numbers #ifdef __NEW_HD_DRIVE_ID major=drive->major_rev_num; *minor=drive->minor_rev_num; #else major=drive->word80; *minor=drive->word81; #endif // First check if device has ANY ATA version information in it if (major==NOVAL_0 || major==NOVAL_1) { *description=NULL; return -1; } // The minor revision number has more information - try there first if (*minor && (*minor<=MINOR_MAX)){ int std = actual_ver[*minor]; if (std) { *description=minor_str[*minor]; return std; } } // HDPARM has a very complicated algorithm from here on. Since SMART only // exists on ATA-3 and later standards, let's punt on this. If you don't // like it, please fix it. The code's in CVS. for (i=15; i>0; i--) if (major & (0x1<<i)) break; *description=NULL; if (i==0) return 1; else return i;; } // returns 1 if SMART supported, 0 if not supported or can't tell int ataSmartSupport(struct hd_driveid *drive){ unsigned short word82,word83; // get correct bits of IDENTIFY DEVICE structure #ifdef __NEW_HD_DRIVE_ID word82=drive->command_set_1; word83=drive->command_set_2; #else word82=drive->command_sets; word83=drive->word83; #endif // Note this does not work for ATA3 < Revision 6, when word82 and word83 were added // we should check for ATA3 Rev 0 in minor identity code... return (word83 & 0x0001<<14) && !(word83 & 0x0001<<15) && (word82 & 0x0001); } // returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell int ataIsSmartEnabled(struct hd_driveid *drive){ unsigned short word85,word87; // Get correct bits of IDENTIFY DRIVE structure #ifdef __NEW_HD_DRIVE_ID word85=drive->cfs_enable_1; word87=drive->csf_default; #else word85=drive->word85; word87=drive->word87; #endif if ((word87 & 0x0001<<14) && !(word87 & 0x0001<<15)) // word85 contains valid information, so return word85 & 0x0001; // Since we can't rely word85, we don't know if SMART is enabled. return -1; } // Reads SMART attributes into *data int ataReadSmartValues(int device, struct ata_smart_values *data){ int i; unsigned char chksum=0; unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE]= {WIN_SMART, 0, SMART_READ_VALUES, 1, }; if (ioctl(device,HDIO_DRIVE_CMD,buf)){ syserror("Error SMART Values Read failed"); return -1; } // compute checksum for (i=0;i<ATA_SMART_SEC_SIZE;i++) chksum+=buf[i+HDIO_DRIVE_CMD_HDR_SIZE]; // verify that checksum vanishes if (chksum) checksumwarning("SMART Attribute Data Structure"); // copy data and return memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE,ATA_SMART_SEC_SIZE); return 0; } // Reads the Self Test Log (log #6) int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){ int i; unsigned char chksum=0; unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = {WIN_SMART, 0x06, SMART_READ_LOG_SECTOR, 1,}; // get data from device if (ioctl(device, HDIO_DRIVE_CMD, buf)){ syserror("Error SMART Error Self-Test Log Read failed"); return -1; } // compute its checksum, and issue a warning if needed for (i=0;i<ATA_SMART_SEC_SIZE;i++) chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; if (chksum) checksumwarning("SMART Self-Test Log Structure"); // copy data back to the user and return memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); return 0; } // Reads the Error Log (log #1) int ataReadErrorLog (int device, struct ata_smart_errorlog *data){ int i; unsigned char chksum=0; unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = {WIN_SMART, 0x01, SMART_READ_LOG_SECTOR, 1,}; // get data from device if (ioctl(device,HDIO_DRIVE_CMD,buf)) { syserror("Error SMART Error Log Read failed"); return -1; } // compute checksum and issue warning if needed for (i=0;i<ATA_SMART_SEC_SIZE;i++) chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; if (chksum) checksumwarning("SMART ATA Error Log Structure"); //copy data back to user and return memcpy(data, buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); return 0; } int ataReadSmartThresholds (int device, struct ata_smart_thresholds *data){ int i; unsigned char chksum=0; unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = {WIN_SMART, 1, SMART_READ_THRESHOLDS, 1,}; // get data from device if (ioctl(device ,HDIO_DRIVE_CMD, buf)){ syserror("Error SMART Thresholds Read failed"); return -1; } // compute checksum and issue warning if needed for (i=0;i<ATA_SMART_SEC_SIZE;i++) chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; if (chksum) checksumwarning("SMART Attribute Thresholds Structure"); // copy data back to user and return memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); return 0; } // This routine is not currently in use, and it's been marked as // "Obsolete" in the ANSI ATA-5 spec. So it should probably be left // alone and unused. If you do modify the thresholds, be sure to set // the checksum correctly before putting the structure back! int ataSetSmartThresholds ( int device, struct ata_smart_thresholds *data){ unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = {WIN_SMART, 1, 0xD7, 1,}; memcpy(buf+HDIO_DRIVE_CMD_HDR_SIZE, data, ATA_SMART_SEC_SIZE); if (ioctl(device, HDIO_DRIVE_CMD, buf)){ syserror("Error SMART Thresholds Write failed"); return -1; } return 0; } int ataEnableSmart (int device ){ unsigned char parms[4] = {WIN_SMART, 1, SMART_ENABLE, 0}; if (ioctl (device, HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Enable failed"); return -1; } return 0; } int ataDisableSmart (int device ){ unsigned char parms[4] = {WIN_SMART, 1, SMART_DISABLE, 0}; if (ioctl(device, HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Disable failed"); return -1; } return 0; } int ataEnableAutoSave(int device){ unsigned char parms[4] = {WIN_SMART, 241, SMART_AUTOSAVE, 0}; if (ioctl(device, HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Enable Auto-save failed"); return -1; } return 0; } int ataDisableAutoSave(int device){ unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTOSAVE, 0}; if (ioctl(device, HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Disable Auto-save failed"); return -1; } return 0; } // Note that in the ATA-5 standard this command is marked "OBSOLETE" int ataEnableAutoOffline (int device ){ /* timer hard coded to 4 hours */ unsigned char parms[4] = {WIN_SMART, 248, SMART_AUTO_OFFLINE, 0}; if (ioctl(device , HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Enable Automatic Offline failed"); return -1; } return 0; } // Another Obsolete Command! int ataDisableAutoOffline (int device ){ unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTO_OFFLINE, 0}; if (ioctl(device , HDIO_DRIVE_CMD, parms)){ syserror("Error SMART Disable Automatic Offline failed"); return -1; } return 0; } // This function does NOTHING except tell us if SMART is working & // enabled on the device. See ataSmartStatus2() for one that actually // returns SMART status. int ataSmartStatus (int device ){ unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0}; if (ioctl(device, HDIO_DRIVE_CMD, parms)){ syserror("Error Return SMART Status via HDIO_DRIVE_CMD failed"); return -1; } return 0; } // If SMART is enabled, supported, and working, then this call is // guaranteed to return 1, else zero. Silent inverse of // ataSmartStatus() int ataDoesSmartWork(int device){ unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0}; return !ioctl(device, HDIO_DRIVE_CMD, parms); } #ifdef HDIO_DRIVE_TASK // This function uses a different interface (DRIVE_TASK) than the // other commands in this file. int ataSmartStatus2(int device){ unsigned char normal_cyl_lo=0x4f, normal_cyl_hi=0xc2; unsigned char failed_cyl_lo=0xf4, failed_cyl_hi=0x2c; unsigned char parms[HDIO_DRIVE_TASK_HDR_SIZE]= {WIN_SMART, SMART_STATUS, 0, 0, 0, 0, 0}; // load CL and CH values parms[4]=normal_cyl_lo; parms[5]=normal_cyl_hi; if (ioctl(device,HDIO_DRIVE_TASK,parms)){ syserror("Error SMART Status command via HDIO_DRIVE_TASK failed"); return -1; } // Cyl low and Cyl high unchanged means "Good SMART status" if (parms[4]==normal_cyl_lo && parms[5]==normal_cyl_hi) return 0; // These values mean "Bad SMART status" if (parms[4]==failed_cyl_lo && parms[5]==failed_cyl_hi) return 1; // We haven't gotten output that makes sense; print out some debugging info syserror("Error SMART Status command failed"); pout("Please get assistance from %s\n",PROJECTHOME); pout("Register values returned from SMART Status command are:\n"); pout("CMD=0x%02x\n",(int)parms[0]); pout("FR =0x%02x\n",(int)parms[1]); pout("NS =0x%02x\n",(int)parms[2]); pout("SC =0x%02x\n",(int)parms[3]); pout("CL =0x%02x\n",(int)parms[4]); pout("CH =0x%02x\n",(int)parms[5]); pout("SEL=0x%02x\n",(int)parms[6]); return -1; } #else // Just a hack so that the code compiles on // 2.2 kernels without HDIO_DRIVE TASK support. // Should be fixed by putting in a call to code // that compares smart data to thresholds. int ataSmartStatus2(int device){ return ataSmartStatus(device); } #endif // This is the way to execute ALL tests: offline, short self-test, // extended self test, with and without captive mode, etc. int ataSmartTest(int device, int testtype){ unsigned char parms[4] = {WIN_SMART, 0, SMART_IMMEDIATE_OFFLINE}; char cmdmsg[128],*type,*captive; int errornum; parms[1]=testtype; // Set up strings that describe the type of test if (testtype==SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) captive="captive"; else captive="off-line"; if (testtype==OFFLINE_FULL_SCAN) type="off-line"; else if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST) type="Short self-test"; else type="Extended self-test"; // Print ouf message that we are sending the command to test if (testtype==ABORT_SELF_TEST) sprintf(cmdmsg,"Abort SMART off-line mode self-test routine"); else sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive); pout("Sending command: \"%s\".\n",cmdmsg); // Now send the command to test errornum=ioctl(device, HDIO_DRIVE_CMD, parms); if (errornum && !((testtype=SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) && errno==EIO)){ char errormsg[128]; sprintf(errormsg,"Command \"%s\" failed",cmdmsg); syserror(errormsg); fprintf(stderr,"\n"); return -1; } // Since the command succeeded, tell user if (testtype==ABORT_SELF_TEST) pout("Self-testing aborted!\n"); else pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg); return 0; } /* Test Time Functions */ int TestTime(struct ata_smart_values *data,int testtype){ switch (testtype){ case OFFLINE_FULL_SCAN: return (int) data->total_time_to_complete_off_line; case SHORT_SELF_TEST: case SHORT_CAPTIVE_SELF_TEST: return (int) data->short_test_completion_time; case EXTEND_SELF_TEST: case EXTEND_CAPTIVE_SELF_TEST: return (int) data->extend_test_completion_time; default: return 0; } } // This function tells you both about the ATA error log and the // self-test error log capability. The bit is poorly documented in // the ATA/ATAPI standard. int isSmartErrorLogCapable ( struct ata_smart_values *data){ return data->errorlog_capability & 0x01; } int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data){ return data->offline_data_collection_capability & 0x01; } int isSupportAutomaticTimer ( struct ata_smart_values *data){ return data->offline_data_collection_capability & 0x02; } int isSupportOfflineAbort ( struct ata_smart_values *data){ return data->offline_data_collection_capability & 0x04; } int isSupportOfflineSurfaceScan ( struct ata_smart_values *data){ return data->offline_data_collection_capability & 0x08; } int isSupportSelfTest (struct ata_smart_values *data){ return data->offline_data_collection_capability & 0x10; } // Loop over all valid attributes. If they are prefailure attributes // and are at or below the threshold value, then return the ID of the // first failing attribute found. Return 0 if all prefailure // attributes are in bounds. The spec says "Bit 0 // -Pre-failure/advisory - If the value of this bit equals zero, an // attribute value less than or equal to its corresponding attribute // threshold indicates an advisory condition where the usage or age of // the device has exceeded its intended design life period. If the // value of this bit equals one, an atribute value less than or equal // to its corresponding attribute threshold indicates a pre-failure // condition where imminent loss of data is being predicted." // onlyfailed=0 : are or were any age or prefailure attributes <= threshold // onlyfailed=1: are any prefailure attributes <= threshold now int ataCheckSmart(struct ata_smart_values *data, struct ata_smart_thresholds *thresholds, int onlyfailed){ int i; // loop over all attributes for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){ // pointers to disk's values and vendor's thresholds struct ata_smart_attribute *disk=data->vendor_attributes+i; struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i; // consider only valid attributes if (disk->id && thre->id){ int failednow,failedever; failednow =disk->current <= thre->threshold; failedever=disk->worst <= thre->threshold; if (!onlyfailed && failedever) return disk->id; if (onlyfailed && failednow && disk->status.flag.prefailure) return disk->id; } } return 0; } // This checks the n'th attribute in the attribute list, NOT the // attribute with id==n. If the attribute does not exist, or the // attribute is > threshold, then returns zero. If the attribute is // <= threshold (failing) then we the attribute number if it is a // prefail attribute. Else we return minus the attribute number if it // is a usage attribute. int ataCheckAttribute(struct ata_smart_values *data, struct ata_smart_thresholds *thresholds, int n){ struct ata_smart_attribute *disk; struct ata_smart_threshold_entry *thre; if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds) return 0; // pointers to disk's values and vendor's thresholds disk=data->vendor_attributes+n; thre=thresholds->thres_entries+n; if (!disk || !thre) return 0; // consider only valid attributes, check for failure if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold) return 0; // We have found a failed attribute. Return positive or negative? if (disk->status.flag.prefailure) return disk->id; else return -1*(disk->id); } // Note some attribute names appear redundant because different // manufacturers use different attribute IDs for an attribute with the // same name. void ataPrintSmartAttribName(char *out, unsigned char id){ char *name; switch (id){ case 1: name="Raw_Read_Error_Rate"; break; case 2: name="Throughput_Performance"; break; case 3: name="Spin_Up_Time"; break; case 4: name="Start_Stop_Count"; break; case 5: name="Reallocated_Sector_Ct"; break; case 6: name="Read_Channel_Margin"; break; case 7: name="Seek_Error_Rate"; break; case 8: name="Seek_Time_Performance"; break; case 9: name="Power_On_Hours"; break; case 10: name="Spin_Retry_Count"; break; case 11: name="Calibration_Retry_Count"; break; case 12: name="Power_Cycle_Count"; break; case 13: name="Read_Soft_Error_Rate"; break; case 191: name="G-Sense_Error_Rate"; break; case 192: name="Power-Off_Retract_Count"; break; case 193: name="Load_Cycle_Count"; break; case 194: name="Temperature_Centigrade"; break; case 195: name="Hardware_ECC_Recovered"; break; case 196: name="Reallocated_Event_Count"; break; case 197: name="Current_Pending_Sector"; break; case 198: name="Offline_Uncorrectable"; break; case 199: name="UDMA_CRC_Error_Count"; break; case 220: // Note -- this is also apparently used for temperature. name="Disk_Shift"; break; case 221: name="G-Sense_Error_Rate"; break; case 222: name="Loaded_Hours"; break; case 223: name="Load_Retry_Count"; break; case 224: name="Load_Friction"; break; case 225: name="Load_Cycle_Count"; break; case 226: name="Load-in_Time"; break; case 227: name="Torq-amp_Count"; break; case 228: name="Power-off_Retract_Count"; break; case 231: name="Temperature_Centigrade"; break; default: name="Unknown_Attribute"; break; } sprintf(out,"%3hhu %s",id,name); return; } // These are two utility functions for printing CVS IDs. They don't // really belong here. But it's the only common source file included // in both smartd and smartctl. returns distance that it has moved // ahead in the input string int massagecvs(char *out, const char *cvsid){ char *copy,*filename,*date,*version; const char delimiters[] = " ,$"; // make a copy on stack, go to first token, if (!(copy=strdup(cvsid)) || !(filename=strtok(copy, delimiters))) return 0; // move to first instance of "Id:" while (strcmp(filename,"Id:")) if (!(filename=strtok(NULL, delimiters))) return 0; // get filename, skip "v", get version and date if (!( filename=strtok(NULL, delimiters) ) || !( strtok(NULL, delimiters) ) || !( version=strtok(NULL, delimiters) ) || !( date=strtok(NULL, delimiters) ) ) return 0; sprintf(out,"%-13s revision: %-6s date: %-15s", filename, version, date); free(copy); return (date-copy)+strlen(date); } // prints a single set of CVS ids void printone(char *block, const char *cvsid){ char strings[CVSMAXLEN]; const char *here=cvsid; int line=1,len=strlen(cvsid)+1; // check that the size of the output block is sufficient if (len>=CVSMAXLEN) { fprintf(stderr,"CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1); exit(1); } // loop through the different strings while ((len=massagecvs(strings,here))){ switch (line++){ case 1: block+=snprintf(block,CVSMAXLEN,"Module:"); break; default: block+=snprintf(block,CVSMAXLEN," uses:"); } block+=snprintf(block,CVSMAXLEN," %s\n",strings); here+=len; } return; }