diff --git a/CVSROOT/checkoutlist b/CVSROOT/checkoutlist deleted file mode 100644 index b04b3501f5efd94313942eb7439457bc82f5a2f5..0000000000000000000000000000000000000000 --- a/CVSROOT/checkoutlist +++ /dev/null @@ -1,13 +0,0 @@ -# The "checkoutlist" file is used to support additional version controlled -# administrative files in $CVSROOT/CVSROOT, such as template files. -# -# The first entry on a line is a filename which will be checked out from -# the corresponding RCS file in the $CVSROOT/CVSROOT directory. -# The remainder of the line is an error message to use if the file cannot -# be checked out. -# -# File format: -# -# [<whitespace>]<filename><whitespace><error message><end-of-line> -# -# comment lines begin with '#' diff --git a/CVSROOT/commitinfo b/CVSROOT/commitinfo deleted file mode 100644 index b19e7b7a63e8e90cdb49c43f02035646c4a76e0a..0000000000000000000000000000000000000000 --- a/CVSROOT/commitinfo +++ /dev/null @@ -1,15 +0,0 @@ -# The "commitinfo" file is used to control pre-commit checks. -# The filter on the right is invoked with the repository and a list -# of files to check. A non-zero exit of the filter program will -# cause the commit to be aborted. -# -# The first entry on a line is a regular expression which is tested -# against the directory that the change is being committed to, relative -# to the $CVSROOT. For the first match that is found, then the remainder -# of the line is the name of the filter to run. -# -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. -# -# If the name "ALL" appears as a regular expression it is always used -# in addition to the first matching regex or "DEFAULT". diff --git a/CVSROOT/config b/CVSROOT/config deleted file mode 100644 index ff43ec005ab332bc2aa7e1378754180e75a4b049..0000000000000000000000000000000000000000 --- a/CVSROOT/config +++ /dev/null @@ -1,14 +0,0 @@ -# Set this to "no" if pserver shouldn't check system users/passwords -#SystemAuth=no - -# Put CVS lock files in this directory rather than directly in the repository. -#LockDir=/var/lock/cvs - -# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top -# level of the new working directory when using the `cvs checkout' -# command. -#TopLevelAdmin=no - -# Set `LogHistory' to `all' or `TOFEWGCMAR' to log all transactions to the -# history file, or a subset as needed (ie `TMAR' logs all write operations) -#LogHistory=TOFEWGCMAR diff --git a/CVSROOT/cvswrappers b/CVSROOT/cvswrappers deleted file mode 100644 index 0accaf1b1532448d633d8a183cd8e3a5dd3b4a75..0000000000000000000000000000000000000000 --- a/CVSROOT/cvswrappers +++ /dev/null @@ -1,23 +0,0 @@ -# This file affects handling of files based on their names. -# -# The -t/-f options allow one to treat directories of files -# as a single file, or to transform a file in other ways on -# its way in and out of CVS. -# -# The -m option specifies whether CVS attempts to merge files. -# -# The -k option specifies keyword expansion (e.g. -kb for binary). -# -# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) -# -# wildcard [option value][option value]... -# -# where option is one of -# -f from cvs filter value: path to filter -# -t to cvs filter value: path to filter -# -m update methodology value: MERGE or COPY -# -k expansion mode value: b, o, kkv, &c -# -# and value is a single-quote delimited value. -# For example: -#*.gif -k 'b' diff --git a/CVSROOT/editinfo b/CVSROOT/editinfo deleted file mode 100644 index d78886c1522b6eae3470c13da218c3d8e197cf71..0000000000000000000000000000000000000000 --- a/CVSROOT/editinfo +++ /dev/null @@ -1,21 +0,0 @@ -# The "editinfo" file is used to allow verification of logging -# information. It works best when a template (as specified in the -# rcsinfo file) is provided for the logging procedure. Given a -# template with locations for, a bug-id number, a list of people who -# reviewed the code before it can be checked in, and an external -# process to catalog the differences that were code reviewed, the -# following test can be applied to the code: -# -# Making sure that the entered bug-id number is correct. -# Validating that the code that was reviewed is indeed the code being -# checked in (using the bug-id number or a seperate review -# number to identify this particular code set.). -# -# If any of the above test failed, then the commit would be aborted. -# -# Actions such as mailing a copy of the report to each reviewer are -# better handled by an entry in the loginfo file. -# -# One thing that should be noted is the the ALL keyword is not -# supported. There can be only one entry that matches a given -# repository. diff --git a/CVSROOT/loginfo b/CVSROOT/loginfo deleted file mode 100644 index 20957ed843e3834a67a2a71a54b202355d7a23a3..0000000000000000000000000000000000000000 --- a/CVSROOT/loginfo +++ /dev/null @@ -1,29 +0,0 @@ -# The "loginfo" file controls where "cvs commit" log information -# is sent. The first entry on a line is a regular expression which must match -# the directory that the change is being made to, relative to the -# $CVSROOT. If a match is found, then the remainder of the line is a filter -# program that should expect log information on its standard input. -# -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. -# -# If the name ALL appears as a regular expression it is always used -# in addition to the first matching regex or DEFAULT. -# -# You may specify a format string as part of the -# filter. The string is composed of a `%' followed -# by a single format character, or followed by a set of format -# characters surrounded by `{' and `}' as separators. The format -# characters are: -# -# s = file name -# V = old version number (pre-checkin) -# v = new version number (post-checkin) -# -# For example: -#DEFAULT (echo ""; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog -# or -#DEFAULT (echo ""; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog -^sm5$ /cvsroot/sitedocs/CVSROOT/cvstools/syncmail -u %{sVv} smartmontools-cvs@lists.sourceforge.net -^sm5/[_a-zA-Z0-9]*$ /cvsroot/sitedocs/CVSROOT/cvstools/syncmail -u %{sVv} smartmontools-cvs@lists.sourceforge.net -^www$ /cvsroot/sitedocs/CVSROOT/cvstools/syncmail -u %{sVv} smartmontools-cvs@lists.sourceforge.net diff --git a/CVSROOT/modules b/CVSROOT/modules deleted file mode 100644 index cb9e9efc94b342879a5fff24b425473fc11edd01..0000000000000000000000000000000000000000 --- a/CVSROOT/modules +++ /dev/null @@ -1,26 +0,0 @@ -# Three different line formats are valid: -# key -a aliases... -# key [options] directory -# key [options] directory files... -# -# Where "options" are composed of: -# -i prog Run "prog" on "cvs commit" from top-level of module. -# -o prog Run "prog" on "cvs checkout" of module. -# -e prog Run "prog" on "cvs export" of module. -# -t prog Run "prog" on "cvs rtag" of module. -# -u prog Run "prog" on "cvs update" of module. -# -d dir Place module in directory "dir" instead of module name. -# -l Top-level directory only -- do not recurse. -# -# NOTE: If you change any of the "Run" options above, you'll have to -# release and re-checkout any working directories of these modules. -# -# And "directory" is a path to a directory relative to $CVSROOT. -# -# The "-a" option specifies an alias. An alias is interpreted as if -# everything on the right of the "-a" had been typed on the command line. -# -# You can encode a module within a module by using the special '&' -# character to interpose another module into the current module. This -# can be useful for creating a module that consists of many directories -# spread out over the entire source repository. diff --git a/CVSROOT/notify b/CVSROOT/notify deleted file mode 100644 index 34f0bc288808e56e499d0852a9bfc9a3214b02d9..0000000000000000000000000000000000000000 --- a/CVSROOT/notify +++ /dev/null @@ -1,12 +0,0 @@ -# The "notify" file controls where notifications from watches set by -# "cvs watch add" or "cvs edit" are sent. The first entry on a line is -# a regular expression which is tested against the directory that the -# change is being made to, relative to the $CVSROOT. If it matches, -# then the remainder of the line is a filter program that should contain -# one occurrence of %s for the user to notify, and information on its -# standard input. -# -# "ALL" or "DEFAULT" can be used in place of the regular expression. -# -# For example: -#ALL mail %s -s "CVS notification" diff --git a/CVSROOT/rcsinfo b/CVSROOT/rcsinfo deleted file mode 100644 index 49e59f4d0df9b432c5b99c0b806378a77c9cd870..0000000000000000000000000000000000000000 --- a/CVSROOT/rcsinfo +++ /dev/null @@ -1,13 +0,0 @@ -# The "rcsinfo" file is used to control templates with which the editor -# is invoked on commit and import. -# -# The first entry on a line is a regular expression which is tested -# against the directory that the change is being made to, relative to the -# $CVSROOT. For the first match that is found, then the remainder of the -# line is the name of the file that contains the template. -# -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. -# -# If the name "ALL" appears as a regular expression it is always used -# in addition to the first matching regex or "DEFAULT". diff --git a/CVSROOT/taginfo b/CVSROOT/taginfo deleted file mode 100644 index 274a46dd5b61069f1cea62395178b09aa3120248..0000000000000000000000000000000000000000 --- a/CVSROOT/taginfo +++ /dev/null @@ -1,20 +0,0 @@ -# The "taginfo" file is used to control pre-tag checks. -# The filter on the right is invoked with the following arguments: -# -# $1 -- tagname -# $2 -- operation "add" for tag, "mov" for tag -F, and "del" for tag -d -# $3 -- repository -# $4-> file revision [file revision ...] -# -# A non-zero exit of the filter program will cause the tag to be aborted. -# -# The first entry on a line is a regular expression which is tested -# against the directory that the change is being committed to, relative -# to the $CVSROOT. For the first match that is found, then the remainder -# of the line is the name of the filter to run. -# -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. -# -# If the name "ALL" appears as a regular expression it is always used -# in addition to the first matching regex or "DEFAULT". diff --git a/CVSROOT/verifymsg b/CVSROOT/verifymsg deleted file mode 100644 index 86f747ce222390e6aa7a488074e372030d57a479..0000000000000000000000000000000000000000 --- a/CVSROOT/verifymsg +++ /dev/null @@ -1,21 +0,0 @@ -# The "verifymsg" file is used to allow verification of logging -# information. It works best when a template (as specified in the -# rcsinfo file) is provided for the logging procedure. Given a -# template with locations for, a bug-id number, a list of people who -# reviewed the code before it can be checked in, and an external -# process to catalog the differences that were code reviewed, the -# following test can be applied to the code: -# -# Making sure that the entered bug-id number is correct. -# Validating that the code that was reviewed is indeed the code being -# checked in (using the bug-id number or a seperate review -# number to identify this particular code set.). -# -# If any of the above test failed, then the commit would be aborted. -# -# Actions such as mailing a copy of the report to each reviewer are -# better handled by an entry in the loginfo file. -# -# One thing that should be noted is the the ALL keyword is not -# supported. There can be only one entry that matches a given -# repository. diff --git a/sm5/atacmdnames.c b/sm5/atacmdnames.c new file mode 100644 index 0000000000000000000000000000000000000000..05d6f408dbdce3883d7be78d7f69bdf270eb5670 --- /dev/null +++ b/sm5/atacmdnames.c @@ -0,0 +1,520 @@ +/* + * atacmdnames.c + * + * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7) + * specification, which is available from http://www.t13.org/#FTP_site + * + * Home page of code is: http://smartmontools.sourceforge.net + * Address of support mailing list: smartmontools-support@lists.sourceforge.net + * + * Copyright (C) 2003-4 Philip Williams + * + * 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. + * + */ + +#include "atacmdnames.h" +#include <stdlib.h> +#include <stdio.h> + +#define COMMAND_TABLE_SIZE 256 + +const char *atacmdnames_c_cvsid="$Id: atacmdnames.c,v 1.11 2004/01/02 16:05:24 ballen4705 Exp $" ATACMDNAMES_H_CVSID; + +const char cmd_reserved[] = "[RESERVED]"; +const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]"; +const char cmd_reserved_sa[] = "[RESERVED FOR SERIAL ATA]"; +const char cmd_reserved_cf[] = "[RESERVED FOR COMPACTFLASH ASSOCIATION]"; +const char cmd_reserved_mcpt[] = "[RESERVED FOR MEDIA CARD PASS THROUGH]"; +const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]"; +const char cmd_seek_ret4[] = "SEEK [RET-4]"; + +const char *command_table[COMMAND_TABLE_SIZE] = { +/*-------------------------------------------------- 00h-0Fh -----*/ + "NOP", + cmd_reserved, + cmd_reserved, + "CFA REQUEST EXTENDED ERROR CODE", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + "DEVICE RESET", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 10h-1Fh -----*/ + "RECALIBRATE [OBS-4]", + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, +/*-------------------------------------------------- 20h-2Fh -----*/ + "READ SECTOR(S)", + "READ SECTOR(S) [OBS-5]", + "READ LONG (w/ retry) [OBS-4]", + "READ LONG (w/o retry) [OBS-4]", + "READ SECTOR(S) EXT", + "READ DMA EXT", + "READ DMA QUEUED EXT", + "READ NATIVE MAX ADDRESS EXT", + cmd_reserved, + "READ MULTIPLE EXT", + "READ STREAM DMA", + "READ STREAM PIO", + cmd_reserved, + cmd_reserved, + cmd_reserved, + "READ LOG EXT", +/*-------------------------------------------------- 30h-3Fh -----*/ + "WRITE SECTOR(S)", + "WRITE SECTOR(S) [OBS-5]", + "WRITE LONG(w/ retry) [OBS-4]", + "WRITE LONG(w/o retry) [OBS-4]", + "WRITE SECTORS(S) EXT", + "WRITE DMA EXT", + "WRITE DMA QUEUED EXT", + "SET MAX ADDRESS EXT", + "CFA WRITE SECTORS WITHOUT ERASE", + "WRITE MULTIPLE EXT", + "WRITE STREAM DMA", + "WRITE STREAM PIO", + "WRITE VERIFY [OBS-4]", + "WRITE DMA FUA EXT", + "WRITE DMA QUEUED FUA EXT", + "WRITE LOG EXT", +/*-------------------------------------------------- 40h-4Fh -----*/ + "READ VERIFY SECTOR(S)", + "READ VERIFY SECTOR(S) [OBS-5]", + "READ VERIFY SECTOR(S) EXT", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 50h-5Fh -----*/ + "FORMAT TRACK [OBS-4]", + "CONFIGURE STREAM", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 60h-6Fh -----*/ + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 70h-7Fh -----*/ + "SEEK [OBS-7]", + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, +/*-------------------------------------------------- 80h-8Fh -----*/ + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + "CFA TRANSLATE SECTOR [VS IF NO CFA]", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, +/*-------------------------------------------------- 90h-9Fh -----*/ + "EXECUTE DEVICE DIAGNOSTIC", + "INITIALIZE DEVICE PARAMETERS [OBS-6]", + "DOWNLOAD MICROCODE", + cmd_reserved, + "STANDBY IMMEDIATE [RET-4]", + "IDLE IMMEDIATE [RET-4]", + "STANDBY [RET-4]", + "IDLE [RET-4]", + "CHECK POWER MODE [RET-4]", + "SLEEP [RET-4]", + cmd_vendor_specific, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- A0h-AFh -----*/ + "PACKET", + "IDENTIFY PACKET DEVICE", + "SERVICE", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- B0h-BFh -----*/ + "SMART", + "DEVICE CONFIGURATION", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, +/*-------------------------------------------------- C0h-CFh -----*/ + "CFA ERASE SECTORS [VS IF NO CFA]", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + "READ MULTIPLE", + "WRITE MULTIPLE", + "SET MULTIPLE MODE", + "READ DMA QUEUED", + "READ DMA", + "READ DMA [OBS-5]", + "WRITE DMA", + "WRITE DMA [OBS-5]", + "WRITE DMA QUEUED", + "CFA WRITE MULTIPLE WITHOUT ERASE", + "WRITE MULTIPLE FUA EXT", + cmd_reserved, +/*-------------------------------------------------- D0h-DFh -----*/ + cmd_reserved, + "CHECK MEDIA CARD TYPE", + cmd_reserved_mcpt, + cmd_reserved_mcpt, + cmd_reserved_mcpt, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + "GET MEDIA STATUS", + "ACKNOWLEDGE MEDIA CHANGE [RET-4]", + "BOOT POST-BOOT [RET-4]", + "BOOT PRE-BOOT [RET-4]", + "MEDIA LOCK", + "MEDIA UNLOCK", +/*-------------------------------------------------- E0h-EFh -----*/ + "STANDBY IMMEDIATE", + "IDLE IMMEDIATE", + "STANDBY", + "IDLE", + "READ BUFFER", + "CHECK POWER MODE", + "SLEEP", + "FLUSH CACHE", + "WRITE BUFFER", + "WRITE SAME [RET-4]", /* Warning! This command is retired but the value of + f_reg is used in look_up_ata_command(). If this + command code is reclaimed in a future standard then + be sure to update look_up_ata_command(). */ + "FLUSH CACHE EXIT", + cmd_reserved, + "IDENTIFY DEVICE", + "MEDIA EJECT", + "IDENTIFY DEVICE DMA [OBS-4]", + "SET FEATURES", +/*-------------------------------------------------- F0h-FFh -----*/ + cmd_vendor_specific, + "SECURITY SET PASSWORD", + "SECURITY UNLOCK", + "SECURITY ERASE PREPARE", + "SECURITY ERASE UNIT", + "SECURITY FREEZE LOCK", + "SECURITY DISABLE PASSWORD", + cmd_vendor_specific, + "READ NATIVE MAX ADDRESS", + "SET MAX", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific +}; + +/* Returns the name of the command (and possibly sub-command) with the given + command code and feature register values. For most command codes this + simply returns the corresponding entry in the command_table array, but for + others the value of the feature register specifies a subcommand or + distinguishes commands. */ +const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { + + // check that command table not messed up. The compiler will issue + // warnings if there are too many array elements, but won't issue + // warnings if there are not enough of them. + if (sizeof(command_table) != sizeof(char *)*COMMAND_TABLE_SIZE){ + fprintf(stderr, + "Problem in atacmdnames.c. Command Table command_table[] does\n" + "not have %d entries! It has %d entries. Please fix it.\n", + COMMAND_TABLE_SIZE, (int)(sizeof(command_table)/sizeof(char *))); + abort(); + } + + switch (c_code) { + case 0x00: /* NOP */ + switch (f_reg) { + case 0x00: + return "NOP [Abort queued commands]"; + case 0x01: + return "NOP [Don't abort queued commands]"; + default: + return "NOP [Reserved subcommand]"; + } + case 0x92: /* DOWNLOAD MICROCODE */ + switch (f_reg) { + case 0x01: + return "DOWNLOAD MICROCODE [Temporary]"; + case 0x07: + return "DOWNLOAD MICROCODE [Save]"; + default: + return "DOWNLOAD MICROCODE [Reserved subcommand]"; + } + case 0xB0: /* SMART */ + switch (f_reg) { + case 0xD0: + return "SMART READ DATA"; + case 0xD1: + return "SMART READ ATTRIBUTE THRESHOLDS [OBS-4]"; + case 0xD2: + return "SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE"; + case 0xD3: + return "SMART SAVE ATTRIBUTE VALUES [OBS-6]"; + case 0xD4: + return "SMART EXECUTE OFF-LINE IMMEDIATE"; + case 0xD5: + return "SMART READ LOG"; + case 0xD6: + return "SMART WRITE LOG"; + case 0xD7: + return "SMART WRITE ATTRIBUTE THRESHOLDS [NS, OBS-4]"; + case 0xD8: + return "SMART ENABLE OPERATIONS"; + case 0xD9: + return "SMART DISABLE OPERATIONS"; + case 0xDA: + return "SMART RETURN STATUS"; + case 0xDB: + return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]"; + default: + if (f_reg >= 0xE0) + return "[Vendor specific SMART command]"; + else + return "[Reserved SMART command]"; + } + case 0xB1: /* DEVICE CONFIGURATION */ + switch (f_reg) { + case 0xC0: + return "DEVICE CONFIGURATION RESTORE"; + case 0xC1: + return "DEVICE CONFIGURATION FREEZE LOCK"; + case 0xC2: + return "DEVICE CONFIGURATION IDENTIFY"; + case 0xC3: + return "DEVICE CONFIGURATION SET"; + default: + return "DEVICE CONFIGURATION [Reserved command]"; + } + case 0xE9: /* WRITE SAME */ + switch (f_reg) { + case 0x22: + return "WRITE SAME [Start specified] [RET-4]"; + case 0xDD: + return "WRITE SAME [Start unspecified] [RET-4]"; + default: + return "WRITE SAME [Invalid subcommand] [RET-4]"; + } + case 0xEF: /* SET FEATURES */ + switch (f_reg) { + case 0x01: + return "SET FEATURES [Enable 8-bit PIO]"; + case 0x02: + return "SET FEATURES [Enable write cache]"; + case 0x03: + return "SET FEATURES [Set transfer mode]"; + case 0x04: + return "SET FEATURES [Enable auto DR] [OBS-4]"; + case 0x05: + return "SET FEATURES [Enable APM]"; + case 0x06: + return "SET FEATURES [Enable Pwr-Up In Standby]"; + case 0x07: + return "SET FEATURES [Set device spin-up]"; + case 0x09: + return "SET FEATURES [Reserved (address offset)]"; + case 0x0A: + return "SET FEATURES [Enable CFA power mode 1]"; + case 0x10: + return "SET FEATURES [Reserved for Serial ATA]"; + case 0x20: + return "SET FEATURES [Set Time-ltd R/W WCT]"; + case 0x21: + return "SET FEATURES [Set Time-ltd R/W EH]"; + case 0x31: + return "SET FEATURES [Disable Media Status Notf]"; + case 0x33: + return "SET FEATURES [Disable retry] [OBS-4]"; + case 0x42: + return "SET FEATURES [Enable AAM]"; + case 0x43: + return "SET FEATURES [Set Max Host I/F S Times]"; + case 0x44: + return "SET FEATURES [Length of VS data] [OBS-4]"; + case 0x54: + return "SET FEATURES [Set cache segs] [OBS-4]"; + case 0x55: + return "SET FEATURES [Disable read look-ahead]"; + case 0x5D: + return "SET FEATURES [Enable release interrupt]"; + case 0x5E: + return "SET FEATURES [Enable SERVICE interrupt]"; + case 0x66: + return "SET FEATURES [Disable revert defaults]"; + case 0x77: + return "SET FEATURES [Disable ECC] [OBS-4]"; + case 0x81: + return "SET FEATURES [Disable 8-bit PIO]"; + case 0x82: + return "SET FEATURES [Disable write cache]"; + case 0x84: + return "SET FEATURES [Disable auto DR] [OBS-4]"; + case 0x85: + return "SET FEATURES [Disable APM]"; + case 0x86: + return "SET FEATURES [Disable Pwr-Up In Standby]"; + case 0x88: + return "SET FEATURES [Disable ECC] [OBS-4]"; + case 0x89: + return "SET FEATURES [Reserved (address offset)]"; + case 0x8A: + return "SET FEATURES [Disable CFA power mode 1]"; + case 0x90: + return "SET FEATURES [Reserved for Serial ATA]"; + case 0x95: + return "SET FEATURES [Enable Media Status Notf]"; + case 0x99: + return "SET FEATURES [Enable retries] [OBS-4]"; + case 0x9A: + return "SET FEATURES [Set max avg curr] [OBS-4]"; + case 0xAA: + return "SET FEATURES [Enable read look-ahead]"; + case 0xAB: + return "SET FEATURES [Set max prefetch] [OBS-4]"; + case 0xBB: + return "SET FEATURES [4 bytes VS data] [OBS-4]"; + case 0xC2: + return "SET FEATURES [Disable AAM]"; + case 0xCC: + return "SET FEATURES [Enable revert to defaults]"; + case 0xDD: + return "SET FEATURES [Disable release interrupt]"; + case 0xDE: + return "SET FEATURES [Disable SERVICE interrupt]"; + case 0xE0: + return "SET FEATURES [Obsolete subcommand]"; + default: + if (f_reg >= 0xF0) + return "SET FEATURES [Reserved for CFA]"; + else + return "SET FEATURES [Reserved subcommand]"; + } + case 0xF9: /* SET MAX */ + switch (f_reg) { + case 0x00: + return "SET MAX ADDRESS [OBS-6]"; + case 0x01: + return "SET MAX SET PASSWORD"; + case 0x02: + return "SET MAX LOCK"; + case 0x03: + return "SET MAX UNLOCK"; + case 0x04: + return "SET MAX FREEZE LOCK"; + default: + return "[Reserved SET MAX command]"; + } + default: + return command_table[c_code]; + } +} diff --git a/sm5/atacmds.c b/sm5/atacmds.c new file mode 100644 index 0000000000000000000000000000000000000000..fca2f62385bc18aeb69df4c08826379df0bd0256 --- /dev/null +++ b/sm5/atacmds.c @@ -0,0 +1,1876 @@ +/* + * atacmds.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 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 <ctype.h> + +#include "atacmds.h" +#include "config.h" +#include "int64.h" +#include "extern.h" +#include "utility.h" + +const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.161 2004/08/18 19:27:44 likewise Exp $" +ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// for passing global control variables +extern smartmonctrl *con; + +// 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 0x22 +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 */ + "ATA/ATAPI-7 T13 1532D revision 1", /* 0x001a */ + "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ + "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ + "reserved", /* 0x001d */ + "ATA/ATAPI-7 T13 1532D revision 0", /* 0x001e */ + "reserved", /* 0x001f */ + "reserved", /* 0x0020 */ + "ATA/ATAPI-7 T13 1532D revision 4a", /* 0x0021 */ + "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022 */ +}; + +// 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: */ + 7, /* 0x001a WARNING: */ + 6, /* 0x001b WARNING: */ + 6, /* 0x001c WARNING: */ + 0, /* 0x001d WARNING: */ + 7, /* 0x001e WARNING: */ + 0, /* 0x001f WARNING: */ + 0, /* 0x0020 WARNING: */ + 7, /* 0x0021 WARNING: */ + 6 /* 0x0022 WARNING: */ +}; + +// When you add additional items to this list, you should then: +// 0 -- update this list +// 1 -- modify the following function parse_attribute_def() +// 2 -- if needed, modify ataPrintSmartAttribRawValue() +// 3 - if needed, modify ataPrintSmartAttribName() +// 4 -- add #define PRESET_N_DESCRIPTION at top of knowndrives.c +// 5 -- add drive in question into knowndrives[] table in knowndrives.c +// 6 -- update smartctl.8 +// 7 -- update smartd.8 +// 8 -- do "make smartd.conf.5" to update smartd.conf.5 +// 9 -- update CHANGELOG file +const char *vendorattributeargs[] = { + // 0 defs[9]=1 + "9,minutes", + // 1 defs[9]=3 + "9,seconds", + // 2 defs[9]=2 + "9,temp", + // 3 defs[220]=1 + "220,temp", + // 4 defs[*]=253 + "N,raw8", + // 5 defs[*]=254 + "N,raw16", + // 6 defs[*]=255 + "N,raw48", + // 7 defs[200]=1 + "200,writeerrorcount", + // 8 defs[9]=4 + "9,halfminutes", + // 9 defs[194]=1 + "194,10xCelsius", + // 10 defs[194]=2 + "194,unknown", + // 11 defs[193]=1 + "193,loadunload", + // 12 defs[201]=1 + "201,detectedtacount", + // 13 defs[192]=1 + "192,emergencyretractcyclect", + // 14 defs[198]=1 + "198,offlinescanuncsectorct", + // NULL should always terminate the array + NULL +}; + +// This are the meanings of the Self-test failure checkpoint byte. +// This is in the self-test log at offset 4 bytes into the self-test +// descriptor and in the SMART READ DATA structure at byte offset +// 371. These codes are not well documented. The meanings returned by +// this routine are used (at least) by Maxtor and IBM. Returns NULL if +// not recognized. Currently the maximum length is 15 bytes. +const char *SelfTestFailureCodeName(unsigned char which){ + + switch (which) { + case 0: + return "Write_Test"; + case 1: + return "Servo_Basic"; + case 2: + return "Servo_Random"; + case 3: + return "G-list_Scan"; + case 4: + return "Handling_Damage"; + case 5: + return "Read_Scan"; + default: + return NULL; + } +} + +// This is a utility function for parsing pairs like "9,minutes" or +// "220,temp", and putting the correct flag into the attributedefs +// array. Returns 1 if problem, 0 if pair has been recongized. +int parse_attribute_def(char *pair, unsigned char **defsptr){ + int i,j; + char temp[32]; + unsigned char *defs; + + // If array does not exist, allocate it + if (!*defsptr && !(*defsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM, 1))){ + pout("Out of memory in parse_attribute_def\n"); + EXIT(1); + } + + defs=*defsptr; + + // look along list and see if we find the pair + for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++); + + switch (i) { + case 0: + // attribute 9 is power on time in minutes + defs[9]=1; + return 0; + case 1: + // attribute 9 is power-on-time in seconds + defs[9]=3; + return 0; + case 2: + // attribute 9 is temperature in celsius + defs[9]=2; + return 0; + case 3: + // attribute 220 is temperature in celsius + defs[220]=1; + return 0; + case 4: + // print all attributes in raw 8-bit form + for (j=0; j<MAX_ATTRIBUTE_NUM; j++) + defs[j]=253; + return 0; + case 5: + // print all attributes in raw 16-bit form + for (j=0; j<MAX_ATTRIBUTE_NUM; j++) + defs[j]=254; + return 0; + case 6: + // print all attributes in raw 48-bit form + for (j=0; j<MAX_ATTRIBUTE_NUM; j++) + defs[j]=255; + return 0; + case 7: + // attribute 200 is write error count + defs[200]=1; + return 0; + case 8: + // attribute 9 increments once every 30 seconds (power on time + // measure) + defs[9]=4; + return 0; + case 9: + // attribute 194 is ten times disk temp in Celsius + defs[194]=1; + return 0; + case 10: + // attribute 194 is unknown + defs[194]=2; + return 0; + case 11: + // Hitachi : Attributes 193 has 2 values : 1 load, 1 normal unload + defs[193]=1; + return 0; + case 12: + // Fujitsu + defs[201]=1; + return 0; + case 13: + // Fujitsu + defs[192]=1; + return 0; + case 14: + // Fujitsu + defs[198]=1; + return 0; + default: + // pair not found + break; + } + // At this point, either the pair was not found, or it is of the + // form N,uninterpreted, in which case we need to parse N + j=sscanf(pair,"%d,%14s", &i, temp); + + // if no match to pattern, unrecognized + if (j!=2 || i<0 || i >255) + return 1; + + // check for recognized strings + if (!strcmp(temp, "raw8")) { + defs[i]=253; + return 0; + } + + // check for recognized strings + if (!strcmp(temp, "raw16")) { + defs[i]=254; + return 0; + } + + // check for recognized strings + if (!strcmp(temp, "raw48")) { + defs[i]=255; + return 0; + } + + // didn't recognize the string + return 1; +} + +// Structure used in sorting the array vendorattributeargs[]. +typedef struct vaa_pair_s { + const char *vaa; + const char *padded_vaa; +} vaa_pair; + +// Returns a copy of s with all numbers of less than three digits padded with +// leading zeros. Returns NULL if there isn't enough memory available. The +// memory for the string is dynamically allocated and should be freed by the +// caller. +char *pad_numbers(const char *s) +{ + char c, *t, *u; + const char *r; + int i, len, ndigits = 0; + + // Allocate the maximum possible amount of memory needed. + if (!(t = (char *)malloc(strlen(s)*2+2))) + return NULL; + + // Copy the string s to t, padding any numbers of less than three digits + // with leading zeros. The string is copied backwards to simplify the code. + r = s + strlen(s); + u = t; + while (( r-- >= s)) { + if (isdigit((int)*r)) + ndigits++; + else if (ndigits > 0) { + while (ndigits++ < 3) + *u++ = '0'; + ndigits = 0; + } + *u++ = *r; + } + *u = '\0'; + + // Reverse the string in t. + len = strlen(t); + for (i = 0; i < len/2; i++) { + c = t[i]; + t[i] = t[len-1-i]; + t[len-1-i] = c; + } + + return t; +} + +// Comparison function for qsort(). Used by sort_vendorattributeargs(). +int compare_vaa_pairs(const void *a, const void *b) +{ + vaa_pair *p = (vaa_pair *)a; + vaa_pair *q = (vaa_pair *)b; + + return strcmp(p->padded_vaa, q->padded_vaa); +} + +// Returns a sorted list of vendorattributeargs or NULL if there is not enough +// memory available. The memory for the list is allocated dynamically and +// should be freed by the caller. +// To perform the sort, any numbers in the strings are padded out to three +// digits by adding leading zeros. For example, +// +// "9,minutes" becomes "009,minutes" +// "N,raw16" becomes "N,raw016" +// +// and the original strings are paired with the padded strings. The list of +// pairs is then sorted by comparing the padded strings (using strcmp) and the +// result is then the list of unpadded strings. +// +const char **sort_vendorattributeargs(void) { + const char **ps, **sorted_list = NULL; + vaa_pair *pairs, *pp; + int count, i; + + // Figure out how many strings are in vendorattributeargs[] (not including + // the terminating NULL). + count = (sizeof vendorattributeargs) / sizeof(char *) - 1; + + // Construct a list of pairs of strings from vendorattributeargs[] and their + // padded equivalents. + if (!(pairs = (vaa_pair *)malloc(sizeof(vaa_pair) * count))) + goto END; + for (ps = vendorattributeargs, pp = pairs; *ps; ps++, pp++) { + pp->vaa = *ps; + if (!(pp->padded_vaa = pad_numbers(*ps))) + goto END; + } + + // Sort the array of vaa_pair structures by comparing the padded strings + // using strcmp(). + qsort(pairs, count, sizeof(vaa_pair), compare_vaa_pairs); + + // Construct the sorted list of strings. + if (!(sorted_list = (const char **)malloc(sizeof vendorattributeargs))) + goto END; + for (ps = sorted_list, pp = pairs, i = 0; i < count; ps++, pp++, i++) + *ps = pp->vaa; + *ps = NULL; + +END: + if (pairs) { + for (i = 0; i < count; i++) + if (pairs[i].padded_vaa) + free((void *)pairs[i].padded_vaa); + free((void *)pairs); + } + + // If there was a problem creating the list then sorted_list should now + // contain NULL. + return sorted_list; +} + +// Function to return a multiline string containing a list of the arguments in +// vendorattributeargs[]. The strings are preceeded by tabs and followed +// (except for the last) by newlines. +// This function allocates the required memory for the string and the caller +// must use free() to free it. It returns NULL if the required memory can't +// be allocated. +char *create_vendor_attribute_arg_list(void){ + const char **ps, **sorted; + char *s; + int len; + + // Get a sorted list of vendor attribute arguments. If the sort fails + // (which should only happen if the system is really low on memory) then just + // use the unordered list. + if (!(sorted = (const char **) sort_vendorattributeargs())) + sorted = vendorattributeargs; + + // Calculate the required number of characters + len = 1; // At least one char ('\0') + for (ps = sorted; *ps != NULL; ps++) { + len += 1; // For the tab + len += strlen(*ps); // For the actual argument string + if (*(ps+1)) + len++; // For the newline if required + } + + // Attempt to allocate memory for the string + if (!(s = (char *)malloc(len))) + return NULL; + + // Construct the string + *s = '\0'; + for (ps = sorted; *ps != NULL; ps++) { + strcat(s, "\t"); + strcat(s, *ps); + if (*(ps+1)) + strcat(s, "\n"); + } + + free((char **)sorted); + + // Return a pointer to the string + return s; +} + +// swap two bytes. Point to low address +void swap2(char *location){ + char tmp=*location; + *location=*(location+1); + *(location+1)=tmp; + return; +} + +// swap four bytes. Point to low address +void swap4(char *location){ + char tmp=*location; + *location=*(location+3); + *(location+3)=tmp; + swap2(location+1); + return; +} + +// swap eight bytes. Points to low address +void swap8(char *location){ + char tmp=*location; + *location=*(location+7); + *(location+7)=tmp; + tmp=*(location+1); + *(location+1)=*(location+6); + *(location+6)=tmp; + swap4(location+2); + return; +} + +static char *commandstrings[]={ + "SMART ENABLE", + "SMART DISABLE", + "SMART AUTOMATIC ATTRIBUTE SAVE", + "SMART IMMEDIATE OFFLINE", + "SMART AUTO OFFLINE", + "SMART STATUS", + "SMART STATUS CHECK", + "SMART READ ATTRIBUTE VALUES", + "SMART READ ATTRIBUTE THRESHOLDS", + "SMART READ LOG", + "IDENTIFY DEVICE", + "IDENTIFY PACKET DEVICE", + "CHECK POWER MODE", + "SMART WRITE LOG", + "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n" +}; + +void prettyprint(unsigned char *stuff, char *name){ + int i,j; + pout("\n===== [%s] DATA START (BASE-16) =====\n", name); + for (i=0; i<32; i++){ + pout("%03d-%03d: ", 16*i, 16*(i+1)-1); + for (j=0; j<15; j++) + pout("%02x ",*stuff++); + pout("%02x\n",*stuff++); + } + pout("===== [%s] DATA END (512 Bytes) =====\n\n", name); +} + +// This function provides the pretty-print reporting for SMART +// commands: it implements the various -r "reporting" options for ATA +// ioctls. +int smartcommandhandler(int device, smart_command_set command, int select, char *data){ + int retval; + + // This conditional is true for commands that return data + int getsdata=(command==PIDENTIFY || + command==IDENTIFY || + command==READ_LOG || + command==READ_THRESHOLDS || + command==READ_VALUES || + command==CHECK_POWER_MODE); + + int sendsdata=(command==WRITE_LOG); + + // If reporting is enabled, say what the command will be before it's executed + if (con->reportataioctl){ + // conditional is true for commands that use parameters + int usesparam=(command==READ_LOG || + command==AUTO_OFFLINE || + command==AUTOSAVE || + command==IMMEDIATE_OFFLINE || + command==WRITE_LOG); + + pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]); + if (usesparam) + pout(" InputParameter=%d\n", select); + else + pout("\n"); + } + + if ((getsdata || sendsdata) && !data){ + pout("REPORT-IOCTL: Unable to execute command %s : data destination address is NULL\n", commandstrings[command]); + return -1; + } + + // The reporting is cleaner, and we will find coding bugs faster, if + // the commands that failed clearly return empty (zeroed) data + // structures + if (getsdata) { + if (command==CHECK_POWER_MODE) + data[0]=0; + else + memset(data, '\0', 512); + } + + + // If reporting is enabled, say what input was sent to the command + if (con->reportataioctl && sendsdata){ + pout("REPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]); + // if requested, pretty-print the output data structure + if (con->reportataioctl>1) + prettyprint((unsigned char *)data, commandstrings[command]); + } + + // In case the command produces an error, we'll want to know what it is: + errno=0; + + // now execute the command + switch (con->controller_type) { + case CONTROLLER_3WARE_678K: + case CONTROLLER_3WARE_678K_CHAR: + case CONTROLLER_3WARE_9000_CHAR: + retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data); + break; + case CONTROLLER_MARVELL_SATA: + retval=marvell_command_interface(device, command, select, data); + break; + default: + retval=ata_command_interface(device, command, select, data); + } + + // If reporting is enabled, say what output was produced by the command + if (con->reportataioctl){ + if (errno) + pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", + device, commandstrings[command], retval, errno, strerror(errno)); + else + pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n", + device, commandstrings[command], retval); + + // if requested, pretty-print the output data structure + if (con->reportataioctl>1 && getsdata) { + if (command==CHECK_POWER_MODE) + pout("Sector Count Register (BASE-16): %02x\n", *data); + else + prettyprint((unsigned char *)data, commandstrings[command]); + } + } + return retval; +} + + +// This function computes the checksum of a single disk sector (512 +// bytes). Returns zero if checksum is OK, nonzero if the checksum is +// incorrect. The size (512) is correct for all SMART structures. +unsigned char checksum(unsigned char *buffer){ + unsigned char sum=0; + int i; + + for (i=0; i<512; i++) + sum+=buffer[i]; + + return sum; +} + +// returns -1 if command fails or the device is in Sleep mode, else +// value of Sector Count register. Sector Count result values: +// 00h device is in Standby mode. +// 80h device is in Idle mode. +// FFh device is in Active mode or Idle mode. + +int ataCheckPowerMode(int device) { + unsigned char result; + + if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result))) + return -1; + + if (result!=0 && result!=0x80 && result!=0xff) + pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result); + + return (int)result; +} + + + + +// Reads current Device Identity info (512 bytes) into buf. Returns 0 +// if all OK. Returns -1 if no ATA Device identity can be +// established. Returns >0 if Device is ATA Packet Device (not SMART +// capable). The value of the integer helps identify the type of +// Packet device, which is useful so that the user can connect the +// formal device number with whatever object is inside their computer. +int ataReadHDIdentity (int device, struct ata_identify_device *buf){ + unsigned short *rawshort=(unsigned short *)buf; + unsigned char *rawbyte =(unsigned char *)buf; + + // See if device responds either to IDENTIFY DEVICE or IDENTIFY + // PACKET DEVICE + if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){ + if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){ + return -1; + } + } + + // if machine is big-endian, swap byte order as needed + if (isbigendian()){ + int i; + + // swap various capability words that are needed + for (i=0; i<33; i++) + swap2((char *)(buf->words047_079+i)); + + for (i=80; i<=87; i++) + swap2((char *)(rawshort+i)); + + for (i=0; i<168; i++) + swap2((char *)(buf->words088_255+i)); + } + + // If there is a checksum there, validate it + if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte)) + checksumwarning("Drive Identity Structure"); + + // If this is a PACKET DEVICE, return device type + if (rawbyte[1] & 0x80) + return 1+(rawbyte[1] & 0x1f); + + // Not a PACKET DEVICE + 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 ata_identify_device *drive, unsigned short *minor){ + unsigned short major; + int i; + + // check that arrays at the top of this file are defined + // consistently + if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){ + pout("Internal error in ataVersionInfo(). minor_str[] size %d\n" + "is not consistent with value of MINOR_MAX+1 = %d\n", + (int)(sizeof(minor_str)/sizeof(char *)), MINOR_MAX+1); + fflush(NULL); + abort(); + } + if (sizeof(actual_ver) != sizeof(int)*(1+MINOR_MAX)){ + pout("Internal error in ataVersionInfo(). actual_ver[] size %d\n" + "is not consistent with value of MINOR_MAX = %d\n", + (int)(sizeof(actual_ver)/sizeof(int)), MINOR_MAX+1); + fflush(NULL); + abort(); + } + + // get major and minor ATA revision numbers + major=drive->major_rev_num; + *minor=drive->minor_rev_num; + + // 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 SMART unsupported, -1 if can't tell +int ataSmartSupport(struct ata_identify_device *drive){ + unsigned short word82=drive->command_set_1; + unsigned short word83=drive->command_set_2; + + // check if words 82/83 contain valid info + if ((word83>>14) == 0x01) + // return value of SMART support bit + return word82 & 0x0001; + + // since we can're rely on word 82, we don't know if SMART supported + return -1; +} + +// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell +int ataIsSmartEnabled(struct ata_identify_device *drive){ + unsigned short word85=drive->cfs_enable_1; + unsigned short word87=drive->csf_default; + + // check if words 85/86/87 contain valid info + if ((word87>>14) == 0x01) + // return value of SMART enabled bit + 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){ + + if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){ + syserror("Error SMART Values Read failed"); + return -1; + } + + // compute checksum + if (checksum((unsigned char *)data)) + checksumwarning("SMART Attribute Data Structure"); + + // byte swap if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->revnumber)); + swap2((char *)&(data->total_time_to_complete_off_line)); + swap2((char *)&(data->smart_capability)); + for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){ + struct ata_smart_attribute *x=data->vendor_attributes+i; + swap2((char *)&(x->flags)); + } + } + + return 0; +} + + +// This corrects some quantities that are byte reversed in the SMART +// SELF TEST LOG +void fixsamsungselftestlog(struct ata_smart_selftestlog *data){ + int i; + + // bytes 508/509 (numbered from 0) swapped (swap of self-test index + // with one byte of reserved. + swap2((char *)&(data->mostrecenttest)); + + // LBA low register (here called 'selftestnumber", containing + // information about the TYPE of the self-test) is byte swapped with + // Self-test execution status byte. These are bytes N, N+1 in the + // entries. + for (i=0; i<21; i++) + swap2((char *)&(data->selftest_struct[i].selftestnumber)); + + return; +} + +// Reads the Self Test Log (log #6) +int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){ + syserror("Error SMART Error Self-Test Log Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Self-Test Log Structure"); + + // fix firmware bugs in self-test log + if (con->fixfirmwarebug == FIX_SAMSUNG) + fixsamsungselftestlog(data); + + // fix endian order, if needed + if (isbigendian()){ + int i; + swap2((char*)&(data->revnumber)); + for (i=0; i<21; i++){ + struct ata_smart_selftestlog_struct *x=data->selftest_struct+i; + swap2((char *)&(x->timestamp)); + swap4((char *)&(x->lbafirstfailure)); + } + } + + return 0; +} + + +// Reads the Log Directory (log #0). Note: NO CHECKSUM!! +int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){ + return -1; + } + + // swap endian order if needed + if (isbigendian()){ + swap2((char *)&(data->logversion)); + } + + return 0; +} + + +// Reads the selective self-test log (log #9) +int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){ + syserror("Error SMART Read Selective Self-Test Log failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Selective Self-Test Log Structure"); + + // swap endian order if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->logversion)); + for (i=0;i<5;i++){ + swap8((char *)&(data->span[i].start)); + swap8((char *)&(data->span[i].end)); + } + swap8((char *)&(data->currentlba)); + swap2((char *)&(data->currentspan)); + swap2((char *)&(data->flags)); + swap2((char *)&(data->pendingtime)); + } + + if (data->logversion != 1) + pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion); + + return 0; +} + +// Writes the selective self-test log (log #9) +int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv){ + int i; + struct ata_selective_self_test_log sstlog, *data=&sstlog; + unsigned char cksum=0; + unsigned char *ptr=(unsigned char *)data; + + // Read log + if (ataReadSelectiveSelfTestLog(device, data)) { + pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n"); + return -1; + } + + // Fix logversion if needed + if (data->logversion !=1) { + pout("Error SMART Selective Self-Test Log Data Structure Revision not recognized\n" + "Revision number should be 1 but is %d. To be safe, aborting WRITE LOG\n", data->logversion); + return -2; + } + + // Host is NOT allowed to write selective self-test log if a selective + // self-test is in progress. + if (0<data->currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) { + pout("Error SMART Selective or other Self-Test in progress.\n"); + return -4; + } + + // Clear spans + for (i=0; i<5; i++) + memset(data->span+i, 0, sizeof(struct test_span)); + + // Set spans for testing + for (i=0; i<con->smartselectivenumspans; i++){ + data->span[i].start = con->smartselectivespan[i][0]; + data->span[i].end = con->smartselectivespan[i][1]; + } + + // host must initialize to zero before initiating selective self-test + data->currentlba=0; + data->currentspan=0; + + // Perform off-line scan after selective test? + if (1 == con->scanafterselect) + // NO + data->flags &= ~SELECTIVE_FLAG_DOSCAN; + else if (2 == con->scanafterselect) + // YES + data->flags |= SELECTIVE_FLAG_DOSCAN; + + // Must clear active and pending flags before writing + data->flags &= ~(SELECTIVE_FLAG_ACTIVE); + data->flags &= ~(SELECTIVE_FLAG_PENDING); + + // modify pending time? + if (con->pendingtime) + data->pendingtime=(unsigned short)(con->pendingtime-1); + + // Set checksum to zero, then compute checksum + data->checksum=0; + for (i=0; i<512; i++) + cksum+=ptr[i]; + cksum=~cksum; + cksum+=1; + data->checksum=cksum; + + // swap endian order if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->logversion)); + for (i=0;i<5;i++){ + swap8((char *)&(data->span[i].start)); + swap8((char *)&(data->span[i].end)); + } + swap8((char *)&(data->currentlba)); + swap2((char *)&(data->currentspan)); + swap2((char *)&(data->flags)); + swap2((char *)&(data->pendingtime)); + } + + // write new selective self-test log + if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){ + syserror("Error Write Selective Self-Test Log failed"); + return -3; + } + + return 0; +} + +// This corrects some quantities that are byte reversed in the SMART +// ATA ERROR LOG. +void fixsamsungerrorlog(struct ata_smart_errorlog *data){ + int i,j; + + // FIXED IN SAMSUNG -25 FIRMWARE??? + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + + // FIXED IN SAMSUNG -22a FIRMWARE + // step through 5 error log data structures + for (i=0; i<5; i++){ + // step through 5 command data structures + for (j=0; j<5; j++) + // Command data structure 4-byte millisec timestamp. These are + // bytes (N+8, N+9, N+10, N+11). + swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp)); + // Error data structure two-byte hour life timestamp. These are + // bytes (N+28, N+29). + swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp)); + } + return; +} + +// NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE +void fixsamsungerrorlog2(struct ata_smart_errorlog *data){ + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + return; +} + +// Reads the Summary SMART Error Log (log #1). The Comprehensive SMART +// Error Log is #2, and the Extended Comprehensive SMART Error log is +// #3 +int ataReadErrorLog (int device, struct ata_smart_errorlog *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){ + syserror("Error SMART Error Log Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART ATA Error Log Structure"); + + // Some disks have the byte order reversed in some SMART Summary + // Error log entries + if (con->fixfirmwarebug == FIX_SAMSUNG) + fixsamsungerrorlog(data); + else if (con->fixfirmwarebug == FIX_SAMSUNG2) + fixsamsungerrorlog2(data); + + // Correct endian order if necessary + if (isbigendian()){ + int i,j; + + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + + // step through 5 error log data structures + for (i=0; i<5; i++){ + // step through 5 command data structures + for (j=0; j<5; j++) + // Command data structure 4-byte millisec timestamp + swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp)); + // Error data structure life timestamp + swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp)); + } + } + + return 0; +} + +int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){ + + // get data from device + if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){ + syserror("Error SMART Thresholds Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Attribute Thresholds Structure"); + + // byte swap if needed + if (isbigendian()) + swap2((char *)&(data->revnumber)); + + return 0; +} + +int ataEnableSmart (int device ){ + if (smartcommandhandler(device, ENABLE, 0, NULL)){ + syserror("Error SMART Enable failed"); + return -1; + } + return 0; +} + +int ataDisableSmart (int device ){ + + if (smartcommandhandler(device, DISABLE, 0, NULL)){ + syserror("Error SMART Disable failed"); + return -1; + } + return 0; +} + +int ataEnableAutoSave(int device){ + if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){ + syserror("Error SMART Enable Auto-save failed"); + return -1; + } + return 0; +} + +int ataDisableAutoSave(int device){ + + if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){ + syserror("Error SMART Disable Auto-save failed"); + return -1; + } + return 0; +} + +// In *ALL* ATA standards the Enable/Disable AutoOffline command is +// marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most +// vendors still support it for backwards compatibility. IBM documents +// it for some drives. +int ataEnableAutoOffline (int device ){ + + /* timer hard coded to 4 hours */ + if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ + syserror("Error SMART Enable Automatic Offline failed"); + return -1; + } + return 0; +} + +// Another Obsolete Command. See comments directly above, associated +// with the corresponding Enable command. +int ataDisableAutoOffline (int device ){ + + if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){ + syserror("Error SMART Disable Automatic Offline 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){ + return !smartcommandhandler(device, STATUS, 0, NULL); +} + +// This function uses a different interface (DRIVE_TASK) than the +// other commands in this file. +int ataSmartStatus2(int device){ + return smartcommandhandler(device, STATUS_CHECK, 0, NULL); +} + +// 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, struct ata_smart_values *sv) { + char cmdmsg[128],*type,*captive; + int errornum, cap, retval, select=0; + + // Boolean, if set, says test is captive + cap=testtype & CAPTIVE_MASK; + + // Set up strings that describe the type of test + if (cap) + 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 if (testtype==EXTEND_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) + type="Extended self-test"; + else if (testtype==CONVEYANCE_SELF_TEST || testtype==CONVEYANCE_CAPTIVE_SELF_TEST) + type="Conveyance self-test"; + else if ((select=(testtype==SELECTIVE_SELF_TEST || testtype==SELECTIVE_CAPTIVE_SELF_TEST))) + type="Selective self-test"; + else + type="[Unrecognized] self-test"; + + // If doing a selective self-test, first use WRITE_LOG to write the + // selective self-test log. + if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv))) { + if (retval==-4) + pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n"); + return retval; + } + + // 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); + + if (select) { + int i; + pout("SPAN STARTING_LBA ENDING_LBA\n"); + for (i = 0; i < con->smartselectivenumspans; i++) + pout(" %d %20"PRId64" %20"PRId64"\n", i, + con->smartselectivespan[i][0], + con->smartselectivespan[i][1]); + } + + // Now send the command to test + errornum=smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL); + + if (errornum && !(cap && errno==EIO)){ + char errormsg[128]; + sprintf(errormsg,"Command \"%s\" failed",cmdmsg); + syserror(errormsg); + pout("\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; + case CONVEYANCE_SELF_TEST: + case CONVEYANCE_CAPTIVE_SELF_TEST: + return (int) data->conveyance_test_completion_time; + default: + return 0; + } +} + +// This function tells you both about the ATA error log and the +// self-test error log capability (introduced in ATA-5). The bit is +// poorly documented in the ATA/ATAPI standard. Starting with ATA-6, +// SMART error logging is also indicated in bit 0 of DEVICE IDENTIFY +// word 84 and 87. Top two bits must match the pattern 01. BEFORE +// ATA-6 these top two bits still had to match the pattern 01, but the +// remaining bits were reserved (==0). +int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){ + + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + int isata6=identity->major_rev_num & (0x01<<6); + int isata7=identity->major_rev_num & (0x01<<7); + + if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01)) + return 1; + + if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01)) + return 1; + + // otherwise we'll use the poorly documented capability bit + return data->errorlog_capability & 0x01; +} + +// See previous function. If the error log exists then the self-test +// log should (must?) also exist. +int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){ + + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + int isata6=identity->major_rev_num & (0x01<<6); + int isata7=identity->major_rev_num & (0x01<<7); + + if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02)) + return 1; + + if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02)) + return 1; + + + // otherwise we'll use the poorly documented capability bit + return data->errorlog_capability & 0x01; +} + + +int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){ + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + + // If bit 14 of word 84 is set to one and bit 15 of word 84 is + // cleared to zero, the contents of word 84 contains valid support + // information. If not, support information is not valid in this + // word. + if ((word84>>14) == 0x01) + // If bit 5 of word 84 is set to one, the device supports the + // General Purpose Logging feature set. + return (word84 & (0x01 << 5)); + + // If bit 14 of word 87 is set to one and bit 15 of word 87 is + // cleared to zero, the contents of words (87:85) contain valid + // information. If not, information is not valid in these words. + if ((word87>>14) == 0x01) + // If bit 5 of word 87 is set to one, the device supports + // the General Purpose Logging feature set. + return (word87 & (0x01 << 5)); + + // not capable + return 0; +} + + +// SMART self-test capability is also indicated in bit 1 of DEVICE +// IDENTIFY word 87 (if top two bits of word 87 match pattern 01). +// However this was only introduced in ATA-6 (but self-test log was in +// ATA-5). +int isSupportExecuteOfflineImmediate(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x01; +} +// Note in the ATA-5 standard, the following bit is listed as "Vendor +// Specific". So it may not be reliable. The only use of this that I +// have found is in IBM drives, where it is well-documented. See for +// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX +// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002. +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; +} +int isSupportConveyanceSelfTest(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x20; +} +int isSupportSelectiveSelfTest(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x40; +} + + + +// 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_pvt *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 && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) + 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_pvt *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 (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) + return disk->id; + else + return -1*(disk->id); +} + + +// This routine prints the raw value of an attribute as a text string +// into out. It also returns this 48-bit number as a long long. The +// array defs[] contains non-zero values if particular attributes have +// non-default interpretations. + +int64_t ataPrintSmartAttribRawValue(char *out, + struct ata_smart_attribute *attribute, + unsigned char *defs){ + int64_t rawvalue; + unsigned word[3]; + int j; + unsigned char select; + + // convert the six individual bytes to a long long (8 byte) integer. + // This is the value that we'll eventually return. + rawvalue = 0; + for (j=0; j<6; j++) { + // This looks a bit roundabout, but is necessary. Don't + // succumb to the temptation to use raw[j]<<(8*j) since under + // the normal rules this will be promoted to the native type. + // On a 32 bit machine this might then overflow. + int64_t temp; + temp = attribute->raw[j]; + temp <<= 8*j; + rawvalue |= temp; + } + + // convert quantities to three two-byte words + for (j=0; j<3; j++){ + word[j] = attribute->raw[2*j+1]; + word[j] <<= 8; + word[j] |= attribute->raw[2*j]; + } + + // if no data array, Attributes have default interpretations + if (defs) + select=defs[attribute->id]; + else + select=0; + + // Print six one-byte quantities. + if (select==253){ + for (j=0; j<5; j++) + out+=sprintf(out, "%d ", attribute->raw[5-j]); + out+=sprintf(out, "%d ", attribute->raw[0]); + return rawvalue; + } + + // Print three two-byte quantities + if (select==254){ + out+=sprintf(out, "%d %d %d", word[2], word[1], word[0]); + return rawvalue; + } + + // Print one six-byte quantity + if (select==255){ + out+=sprintf(out, "%"PRIu64, rawvalue); + return rawvalue; + } + + // This switch statement is where we handle Raw attributes + // that are stored in an unusual vendor-specific format, + switch (attribute->id){ + // Spin-up time + case 3: + out+=sprintf(out, "%d", word[0]); + // if second nonzero then it stores the average spin-up time + if (word[1]) + out+=sprintf(out, " (Average %d)", word[1]); + break; + // Power on time + case 9: + if (select==1){ + // minutes + int64_t tmp1=rawvalue/60; + int64_t tmp2=rawvalue%60; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2); + } + else if (select==3){ + // seconds + int64_t hours=rawvalue/3600; + int64_t minutes=(rawvalue-3600*hours)/60; + int64_t seconds=rawvalue%60; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m+%02"PRIu64"s", hours, minutes, seconds); + } + else if (select==4){ + // 30-second counter + int64_t tmp1=rawvalue/120; + int64_t tmp2=(rawvalue-120*tmp1)/2; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2); + } + else + // hours + out+=sprintf(out, "%"PRIu64, rawvalue); //stored in hours + break; + // Load unload cycles + case 193: + if (select==1){ + // loadunload + long load =attribute->raw[0] + (attribute->raw[1]<<8) + (attribute->raw[2]<<16); + long unload=attribute->raw[3] + (attribute->raw[4]<<8) + (attribute->raw[5]<<16); + out+=sprintf(out, "%lu/%lu", load, unload); + } + else + // associated + out+=sprintf(out, "%"PRIu64, rawvalue); + break; + // Temperature + case 194: + if (select==1){ + // ten times temperature in Celsius + int deg=word[0]/10; + int tenths=word[0]%10; + out+=sprintf(out, "%d.%d", deg, tenths); + } + else if (select==2) + // unknown attribute + out+=sprintf(out, "%"PRIu64, rawvalue); + else { + out+=sprintf(out, "%d", word[0]); + if (!(rawvalue==word[0])) { + int min=word[1]<word[2]?word[1]:word[2]; + int max=word[1]>word[2]?word[1]:word[2]; + // The other bytes are in use. Try IBM's model + out+=sprintf(out, " (Lifetime Min/Max %d/%d)", min, max); + } + } + break; + default: + out+=sprintf(out, "%"PRIu64, rawvalue); + } + + // Return the full value + return rawvalue; +} + + +// Note some attribute names appear redundant because different +// manufacturers use different attribute IDs for an attribute with the +// same name. The variable val should contain a non-zero value if a particular +// attributes has a non-default interpretation. +void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definitions){ + char *name; + unsigned char val; + + // If no data array, use default interpretations + if (definitions) + val=definitions[id]; + else + val=0; + + 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: + switch (val) { + case 1: + name="Power_On_Minutes"; + break; + case 2: + name="Temperature_Celsius"; + break; + case 3: + name="Power_On_Seconds"; + break; + case 4: + name="Power_On_Half_Minutes"; + break; + default: + name="Power_On_Hours"; + break; + } + 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: + switch (val) { + case 1: + // Fujitsu + name="Emergency_Retract_Cycle_Ct"; + break; + default: + name="Power-Off_Retract_Count"; + break; + } + break; + case 193: + name="Load_Cycle_Count"; + break; + case 194: + switch (val){ + case 1: + // Samsung SV1204H with RK100-13 firmware + name="Temperature_Celsius_x10"; + break; + case 2: + // for disks with no temperature Attribute + name="Unknown_Attribute"; + break; + default: + name="Temperature_Celsius"; + break; + } + break; + case 195: + // Fujitsu name="ECC_On_The_Fly_Count"; + name="Hardware_ECC_Recovered"; + break; + case 196: + name="Reallocated_Event_Count"; + break; + case 197: + name="Current_Pending_Sector"; + break; + case 198: + switch (val){ + case 1: + // Fujitsu + name="Off-line_Scan_UNC_Sector_Ct"; + break; + default: + name="Offline_Uncorrectable"; + break; + } + break; + case 199: + name="UDMA_CRC_Error_Count"; + break; + case 200: + switch (val) { + case 1: + // Fujitsu MHS2020AT + name="Write_Error_Count"; + break; + default: + // Western Digital + name="Multi_Zone_Error_Rate"; + break; + } + break; + case 201: + switch (val) { + case 1: + // Fujitsu + name="Detected_TA_Count"; + break; + default: + name="Soft_Read_Error_Rate"; + break; + } + break; + case 202: + // Fujitsu + name="TA_Increase_Count"; + // Maxtor: Data Address Mark Errors + break; + case 203: + // Fujitsu + name="Run_Out_Cancel"; + // Maxtor: ECC Errors + break; + case 204: + // Fujitsu + name="Shock_Count_Write_Opern"; + // Maxtor: Soft ECC Correction + break; + case 205: + // Fujitsu + name="Shock_Rate_Write_Opern"; + // Maxtor: Thermal Aspirates + break; + case 206: + // Fujitsu + name="Flying_Height"; + break; + case 207: + // Maxtor + name="Spin_High_Current"; + break; + case 208: + // Maxtor + name="Spin_Buzz"; + break; + case 209: + // Maxtor + name="Offline_Seek_Performnce"; + break; + case 220: + switch (val) { + case 1: + name="Temperature_Celsius"; + break; + default: + name="Disk_Shift"; + break; + } + 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 230: + // seen in IBM DTPA-353750 + name="Head_Amplitude"; + break; + case 231: + name="Temperature_Celsius"; + break; + case 240: + name="Head_Flying_Hours"; + break; + case 250: + name="Read_Error_Retry_Rate"; + break; + default: + name="Unknown_Attribute"; + break; + } + sprintf(out,"%3hu %s",(short int)id,name); + return; +} + +// Returns raw value of Attribute with ID==id. This will be in the +// range 0 to 2^48-1 inclusive. If the Attribute does not exist, +// return -1. +int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data) { + int i; + + // valid Attribute IDs are in the range 1 to 255 inclusive. + if (!id || !data) + return -1; + + // loop over Attributes to see if there is one with the desired ID + for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) { + struct ata_smart_attribute *this = data->vendor_attributes + i; + if (this->id == id) { + // we've found the desired Attribute. Return its value + int64_t rawvalue=0; + int j; + + for (j=0; j<6; j++) { + // This looks a bit roundabout, but is necessary. Don't + // succumb to the temptation to use raw[j]<<(8*j) since under + // the normal rules this will be promoted to the native type. + // On a 32 bit machine this might then overflow. + int64_t temp; + temp = this->raw[j]; + temp <<= 8*j; + rawvalue |= temp; + } // loop over j + return rawvalue; + } // found desired Attribute + } // loop over Attributes + + // fall-through: no such Attribute found + return -1; +} + + diff --git a/sm5/ataprint.c b/sm5/ataprint.c new file mode 100644 index 0000000000000000000000000000000000000000..21e87e7c5d3f01ed52b26d082c47c5714a514659 --- /dev/null +++ b/sm5/ataprint.c @@ -0,0 +1,1834 @@ +/* + * ataprint.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.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 <ctype.h> +#include <stdio.h> +#include <string.h> +#include "atacmdnames.h" +#include "atacmds.h" +#include "ataprint.h" +#include "smartctl.h" +#include "int64.h" +#include "extern.h" +#include "utility.h" +#include "knowndrives.h" +#include "config.h" + +const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.156 2004/09/10 04:13:41 ballen4705 Exp $" +ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; + +// for passing global control variables +extern smartmonctrl *con; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents +// bytes. +void swapbytes(char *out, const char *in, size_t n) +{ + size_t i; + + for (i = 0; i < n; i += 2) { + out[i] = in[i+1]; + out[i+1] = in[i]; + } +} + +// Copies in to out, but removes leading and trailing whitespace. +void trim(char *out, const char *in) +{ + int i, first, last; + + // Find the first non-space character (maybe none). + first = -1; + for (i = 0; in[i]; i++) + if (!isspace((int)in[i])) { + first = i; + break; + } + + if (first == -1) { + // There are no non-space characters. + out[0] = '\0'; + return; + } + + // Find the last non-space character. + for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--) + ; + last = i; + + strncpy(out, in+first, last-first+1); + out[last-first+1] = '\0'; +} + +// Convenience function for formatting strings from ata_identify_device +void formatdriveidstring(char *out, const char *in, int n) +{ + char tmp[65]; + + n = n > 64 ? 64 : n; + swapbytes(tmp, in, n); + tmp[n] = '\0'; + trim(out, tmp); +} + +// Function for printing ASCII byte-swapped strings, skipping white +// space. Please note that this is needed on both big- and +// little-endian hardware. +void printswap(char *output, char *in, unsigned int n){ + formatdriveidstring(output, in, n); + if (*output) + pout("%s\n", output); + else + pout("[No Information Found]\n"); +} + +/* For the given Command Register (CR) and Features Register (FR), attempts + * to construct a string that describes the contents of the Status + * Register (ST) and Error Register (ER). The string is dynamically allocated + * memory and the return value is a pointer to this string. It is up to the + * caller to free this memory. If there is insufficient memory or if the + * meanings of the flags of the error register are not known for the given + * command then it returns NULL. + * + * The meanings of the flags of the error register for all commands are + * described in the ATA spec and could all be supported here in theory. + * Currently, only a few commands are supported (those that have been seen + * to produce errors). If many more are to be added then this function + * should probably be redesigned. + */ +char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) { + unsigned char CR=data->commands[4].commandreg; + unsigned char FR=data->commands[4].featuresreg; + unsigned char ST=data->error_struct.status; + unsigned char ER=data->error_struct.error_register; + char *s; + const char *error_flag[8]; + int i, print_lba=0, print_sector=0; + + // Set of character strings corresponding to different error codes. + // Please keep in alphabetic order if you add more. + const char *abrt = "ABRT"; // ABORTED + const char *amnf = "AMNF"; // ADDRESS MARK NOT FOUND + const char *ccto = "CCTO"; // COMMAND COMPLETTION TIMED OUT + const char *eom = "EOM"; // END OF MEDIA + const char *icrc = "ICRC"; // INTERFACE CRC ERROR + const char *idnf = "IDNF"; // ID NOT FOUND + const char *ili = "ILI"; // MEANING OF THIS BIT IS COMMAND-SET SPECIFIC + const char *mc = "MC"; // MEDIA CHANGED + const char *mcr = "MCR"; // MEDIA CHANGE REQUEST + const char *nm = "NM"; // NO MEDIA + const char *obs = "obs"; // OBSOLETE + const char *tk0nf = "TK0NF"; // TRACK 0 NOT FOUND + const char *unc = "UNC"; // UNCORRECTABLE + const char *wp = "WP"; // WRITE PROTECTED + + /* If for any command the Device Fault flag of the status register is + * not used then used_device_fault should be set to 0 (in the CR switch + * below) + */ + int uses_device_fault = 1; + + /* A value of NULL means that the error flag isn't used */ + for (i = 0; i < 8; i++) + error_flag[i] = NULL; + + switch (CR) { + case 0x10: // RECALIBRATE + error_flag[2] = abrt; + error_flag[1] = tk0nf; + break; + case 0x20: /* READ SECTOR(S) */ + case 0x21: // READ SECTOR(S) + case 0x24: // READ SECTOR(S) EXT + case 0xC4: /* READ MULTIPLE */ + case 0x29: // READ MULTIPLE EXT + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x22: // READ LONG (with retries) + case 0x23: // READ LONG (without retries) + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x2a: // READ STREAM DMA + case 0x2b: // READ STREAM PIO + if (CR==0x2a) + error_flag[7] = icrc; + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = ccto; + print_lba=1; + print_sector=(int)data->error_struct.sector_count; + break; + case 0x3A: // WRITE STREAM DMA + case 0x3B: // WRITE STREAM PIO + if (CR==0x3A) + error_flag[7] = icrc; + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = ccto; + print_lba=1; + print_sector=(int)data->error_struct.sector_count; + break; + case 0x25: /* READ DMA EXT */ + case 0x26: // READ DMA QUEUED EXT + case 0xC7: // READ DMA QUEUED + case 0xC8: /* READ DMA */ + case 0xC9: + error_flag[7] = icrc; + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + if (CR==0x25 || CR==0xC8) + print_sector=(int)data->error_struct.sector_count; + break; + case 0x30: /* WRITE SECTOR(S) */ + case 0x31: // WRITE SECTOR(S) + case 0x34: // WRITE SECTOR(S) EXT + case 0xC5: /* WRITE MULTIPLE */ + case 0x39: // WRITE MULTIPLE EXT + case 0xCE: // WRITE MULTIPLE FUA EXT + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + print_lba=1; + break; + case 0x32: // WRITE LONG (with retries) + case 0x33: // WRITE LONG (without retries) + error_flag[4] = idnf; + error_flag[2] = abrt; + print_lba=1; + break; + case 0x3C: // WRITE VERIFY + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x40: // READ VERIFY SECTOR(S) with retries + case 0x41: // READ VERIFY SECTOR(S) without retries + case 0x42: // READ VERIFY SECTOR(S) EXT + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + break; + case 0xA0: /* PACKET */ + /* Bits 4-7 are all used for sense key (a 'command packet set specific error + * indication' according to the ATA/ATAPI-7 standard), so "Sense key" will + * be repeated in the error description string if more than one of those + * bits is set. + */ + error_flag[7] = "Sense key (bit 3)", + error_flag[6] = "Sense key (bit 2)", + error_flag[5] = "Sense key (bit 1)", + error_flag[4] = "Sense key (bit 0)", + error_flag[2] = abrt; + error_flag[1] = eom; + error_flag[0] = ili; + break; + case 0xA1: /* IDENTIFY PACKET DEVICE */ + case 0xEF: /* SET FEATURES */ + case 0x00: /* NOP */ + case 0xC6: /* SET MULTIPLE MODE */ + error_flag[2] = abrt; + break; + case 0x2F: // READ LOG EXT + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0x3F: // WRITE LOG EXT + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xB0: /* SMART */ + switch(FR) { + case 0xD0: // SMART READ DATA + case 0xD1: // SMART READ ATTRIBUTE THRESHOLDS + case 0xD5: /* SMART READ LOG */ + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xD6: /* SMART WRITE LOG */ + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xD2: // Enable/Disable Attribute Autosave + case 0xD3: // SMART SAVE ATTRIBUTE VALUES (ATA-3) + case 0xD8: // SMART ENABLE OPERATIONS + case 0xD9: /* SMART DISABLE OPERATIONS */ + case 0xDA: /* SMART RETURN STATUS */ + case 0xDB: // Enable/Disable Auto Offline (SFF) + error_flag[2] = abrt; + break; + case 0xD4: // SMART EXECUTE IMMEDIATE OFFLINE + error_flag[4] = idnf; + error_flag[2] = abrt; + break; + default: + return NULL; + break; + } + break; + case 0xB1: /* DEVICE CONFIGURATION */ + switch (FR) { + case 0xC0: /* DEVICE CONFIGURATION RESTORE */ + error_flag[2] = abrt; + break; + default: + return NULL; + break; + } + break; + case 0xCA: /* WRITE DMA */ + case 0xCB: + case 0x35: // WRITE DMA EXT + case 0x3D: // WRITE DMA FUA EXT + case 0xCC: // WRITE DMA QUEUED + case 0x36: // WRITE DMA QUEUED EXT + case 0x3E: // WRITE DMA QUEUED FUA EXT + error_flag[7] = icrc; + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + if (CR==0x35) + print_sector=(int)data->error_struct.sector_count; + break; + case 0xE4: // READ BUFFER + case 0xE8: // WRITE BUFFER + error_flag[2] = abrt; + break; + default: + return NULL; + } + + /* 256 bytes -- that'll be plenty (OK, this is lazy!) */ + if (!(s = (char *)malloc(256))) + return s; + + s[0] = '\0'; + + /* We ignore any status flags other than Device Fault and Error */ + + if (uses_device_fault && (ST & (1 << 5))) { + strcat(s, "Device Fault"); + if (ST & 1) // Error flag + strcat(s, "; "); + } + if (ST & 1) { // Error flag + int count = 0; + + strcat(s, "Error: "); + for (i = 7; i >= 0; i--) + if ((ER & (1 << i)) && (error_flag[i])) { + if (count++ > 0) + strcat(s, ", "); + strcat(s, error_flag[i]); + } + } + + // If the error was a READ or WRITE error, print the Logical Block + // Address (LBA) at which the read or write failed. + if (print_lba) { + char tmp[128]; + int lba; + + // bits 24-27: bits 0-3 of DH + lba = 0xf & data->error_struct.drive_head; + lba <<= 8; + // bits 16-23: CH + lba |= data->error_struct.cylinder_high; + lba <<= 8; + // bits 8-15: CL + lba |= data->error_struct.cylinder_low; + lba <<= 8; + // bits 0-7: SN + lba |= data->error_struct.sector_number; + + // print number of sectors, if known, and append to print string + if (print_sector) { + snprintf(tmp, 128, " %d sectors", print_sector); + strcat(s, tmp); + } + + // print LBA, and append to print string + snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba); + strcat(s, tmp); + } + + return s; +} + +// This returns the capacity of a disk drive and also prints this into +// a string, using comma separators to make it easier to read. If the +// drive doesn't support LBA addressing or has no user writable +// sectors (eg, CDROM or DVD) then routine returns zero. +uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){ + + unsigned short command_set_2 = drive->command_set_2; + unsigned short capabilities_0 = drive->words047_079[49-47]; + unsigned short sects_16 = drive->words047_079[60-47]; + unsigned short sects_32 = drive->words047_079[61-47]; + unsigned short lba_16 = drive->words088_255[100-88]; + unsigned short lba_32 = drive->words088_255[101-88]; + unsigned short lba_48 = drive->words088_255[102-88]; + unsigned short lba_64 = drive->words088_255[103-88]; + uint64_t capacity_short=0, capacity=0, threedigits, power_of_ten; + int started=0,k=1000000000; + + // if drive supports LBA addressing, determine 32-bit LBA capacity + if (capabilities_0 & 0x0200) { + capacity_short = (unsigned int)sects_32 << 16 | + (unsigned int)sects_16 << 0 ; + + // if drive supports 48-bit addressing, determine THAT capacity + if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400)) + capacity = (uint64_t)lba_64 << 48 | + (uint64_t)lba_48 << 32 | + (uint64_t)lba_32 << 16 | + (uint64_t)lba_16 << 0 ; + + // choose the larger of the two possible capacities + if (capacity_short>capacity) + capacity=capacity_short; + } + + // turn sectors into bytes + capacity_short = (capacity *= 512); + + // print with comma separators. I know this is anglo-centric: + // tell me what to change to use LOCALE if you want. + power_of_ten = k; + power_of_ten *= k; + + for (k=0; k<7; k++) { + threedigits = capacity/power_of_ten; + capacity -= threedigits*power_of_ten; + if (started) + // we have already printed some digits + pstring += sprintf(pstring, ",%03"PRIu64, threedigits); + else if (threedigits || k==6) { + // these are the first digits that we are printing + pstring += sprintf(pstring, "%"PRIu64, threedigits); + started = 1; + } + if (k!=6) + power_of_ten /= 1000; + } + + return capacity_short; +} + +void ataPrintDriveInfo (struct ata_identify_device *drive){ + int version, drivetype; + const char *description; + char unknown[64], timedatetz[DATEANDEPOCHLEN]; + unsigned short minorrev; + char model[64], serial[64], firm[64], capacity[64]; + + + // print out model, serial # and firmware versions (byte-swap ASCI strings) + pout("Device Model: "); + printswap(model, (char *)drive->model,40); + + pout("Serial Number: "); + printswap(serial, (char *)drive->serial_no,20); + + pout("Firmware Version: "); + printswap(firm, (char *)drive->fw_rev,8); + + if (determine_capacity(drive, capacity)) + pout("User Capacity: %s bytes\n", capacity); + + // See if drive is recognized + drivetype=lookupdrive(model, firm); + pout("Device is: %s\n", drivetype<0? + "Not in smartctl database [for details use: -P showall]": + "In smartctl database [for details use: -P show]"); + + // now get ATA version info + version=ataVersionInfo(&description,drive, &minorrev); + + // unrecognized minor revision code + if (!description){ + if (!minorrev) + sprintf(unknown, "Exact ATA specification draft version not indicated"); + else + sprintf(unknown,"Not recognized. Minor revision code: 0x%02hx", minorrev); + description=unknown; + } + + + // SMART Support 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. So it's not + // enough to check if we are ATA-3. Version=-3 indicates ATA-3 + // BEFORE Revision 3. + pout("ATA Version is: %d\n",(int)abs(version)); + pout("ATA Standard is: %s\n",description); + + // print current time and date and timezone + dateandtimezone(timedatetz); + pout("Local Time is: %s\n", timedatetz); + + // Print warning message, if there is one + if (drivetype>=0 && knowndrives[drivetype].warningmsg) + pout("\n==> WARNING: %s\n\n", knowndrives[drivetype].warningmsg); + + if (version>=3) + return; + + pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n"); + pout("We will try to proceed in spite of this.\n"); + return; +} + + +const char *OfflineDataCollectionStatus(unsigned char status_byte){ + unsigned char stat=status_byte & 0x7f; + + switch(stat){ + case 0x00: + return "was never started"; + case 0x02: + return "was completed without error"; + case 0x03: + if (status_byte == 0x03) + return "is in progress"; + else + return "is in a Reserved state"; + case 0x04: + return "was suspended by an interrupting command from host"; + case 0x05: + return "was aborted by an interrupting command from host"; + case 0x06: + return "was aborted by the device with a fatal error"; + default: + if (stat >= 0x40) + return "is in a Vendor Specific state\n"; + else + return "is in a Reserved state\n"; + } +} + + + /* prints verbose value Off-line data collection status byte */ + void PrintSmartOfflineStatus(struct ata_smart_values *data){ + + pout("Offline data collection status: (0x%02x)\t", + (int)data->offline_data_collection_status); + + // Off-line data collection status byte is not a reserved + // or vendor specific value + pout("Offline data collection activity\n" + "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status)); + + // Report on Automatic Data Collection Status. Only IBM documents + // this bit. See SFF 8035i Revision 2 for details. + if (data->offline_data_collection_status & 0x80) + pout("\t\t\t\t\tAuto Offline Data Collection: Enabled.\n"); + else + pout("\t\t\t\t\tAuto Offline Data Collection: Disabled.\n"); + + return; +} + +void PrintSmartSelfExecStatus(struct ata_smart_values *data) +{ + pout("Self-test execution status: "); + + switch (data->self_test_exec_status >> 4) + { + case 0: + pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n"); + break; + case 1: + pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the host.\n"); + break; + case 2: + pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("by the host with a hard or soft reset.\n"); + break; + case 3: + pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("occurred while the device was executing\n\t\t\t\t\t"); + pout("its self-test routine and the device \n\t\t\t\t\t"); + pout("was unable to complete the self-test \n\t\t\t\t\t"); + pout("routine.\n"); + break; + case 4: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("a test element that failed and the test\n\t\t\t\t\t"); + pout("element that failed is not known.\n"); + break; + case 5: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the electrical element of the test\n\t\t\t\t\t"); + pout("failed.\n"); + break; + case 6: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the servo (and/or seek) element of the \n\t\t\t\t\t"); + pout("test failed.\n"); + break; + case 7: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the read element of the test failed.\n"); + break; + case 15: + pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("%1d0%% of test remaining.\n", + (int)(data->self_test_exec_status & 0x0f)); + break; + default: + pout("(%4d)\tReserved.\n", + (int)data->self_test_exec_status); + break; + } + +} + + + +void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){ + pout("Total time to complete Offline \n"); + pout("data collection: \t\t (%4d) seconds.\n", + (int)data->total_time_to_complete_off_line); +} + + + +void PrintSmartOfflineCollectCap(struct ata_smart_values *data){ + pout("Offline data collection\n"); + pout("capabilities: \t\t\t (0x%02x) ", + (int)data->offline_data_collection_capability); + + if (data->offline_data_collection_capability == 0x00){ + pout("\tOffline data collection not supported.\n"); + } + else { + pout( "%s\n", isSupportExecuteOfflineImmediate(data)? + "SMART execute Offline immediate." : + "No SMART execute Offline immediate."); + + pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? + "Auto Offline data collection on/off support.": + "No Auto Offline data collection support."); + + pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? + "Abort Offline collection upon new\n\t\t\t\t\tcommand.": + "Suspend Offline collection upon new\n\t\t\t\t\tcommand."); + + pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? + "Offline surface scan supported.": + "No Offline surface scan supported."); + + pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? + "Self-test supported.": + "No Self-test supported."); + + pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? + "Conveyance Self-test supported.": + "No Conveyance Self-test supported."); + + pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? + "Selective Self-test supported.": + "No Selective Self-test supported."); + } +} + + + +void PrintSmartCapability ( struct ata_smart_values *data) +{ + pout("SMART capabilities: "); + pout("(0x%04x)\t", (int)data->smart_capability); + + if (data->smart_capability == 0x00) + { + pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n"); + } + else + { + + pout( "%s\n", (data->smart_capability & 0x01)? + "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.": + "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode."); + + if ( data->smart_capability & 0x02 ) + { + pout("\t\t\t\t\tSupports SMART auto save timer.\n"); + } + } +} + +void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity) +{ + + pout("Error logging capability: "); + + if ( isSmartErrorLogCapable(data, identity) ) + { + pout(" (0x%02x)\tError logging supported.\n", + (int)data->errorlog_capability); + } + else { + pout(" (0x%02x)\tError logging NOT supported.\n", + (int)data->errorlog_capability); + } +} + +void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){ + pout("Short self-test routine \n"); + if (isSupportSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->short_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){ + pout("Extended self-test routine\n"); + if (isSupportSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->extend_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){ + pout("Conveyance self-test routine\n"); + if (isSupportConveyanceSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->conveyance_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +// onlyfailed=0 : print all attribute values +// onlyfailed=1: just ones that are currently failed and have prefailure bit set +// onlyfailed=2: ones that are failed, or have failed with or without prefailure bit set +void PrintSmartAttribWithThres (struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds, + int onlyfailed){ + int i; + int needheader=1; + char rawstring[64]; + + // step through all vendor attributes + for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){ + char *status; + struct ata_smart_attribute *disk=data->vendor_attributes+i; + struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i; + + // consider only valid attributes (allowing some screw-ups in the + // thresholds page data to slip by) + if (disk->id){ + char *type, *update; + int failednow,failedever; + char attributename[64]; + + failednow = (disk->current <= thre->threshold); + failedever= (disk->worst <= thre->threshold); + + // These break out of the loop if we are only printing certain entries... + if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow)) + continue; + + if (onlyfailed==2 && !failedever) + continue; + + // print header only if needed + if (needheader){ + if (!onlyfailed){ + pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber); + pout("Vendor Specific SMART Attributes with Thresholds:\n"); + } + pout("ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n"); + needheader=0; + } + + // is this Attribute currently failed, or has it ever failed? + if (failednow) + status="FAILING_NOW"; + else if (failedever) + status="In_the_past"; + else + status=" -"; + + // Print name of attribute + ataPrintSmartAttribName(attributename,disk->id, con->attributedefs); + pout("%-28s",attributename); + + // printing line for each valid attribute + type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age"; + update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline"; + + pout("0x%04x %.3d %.3d %.3d %-10s%-9s%-12s", + (int)disk->flags, (int)disk->current, (int)disk->worst, + (int)thre->threshold, type, update, status); + + // print raw value of attribute + ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs); + pout("%s\n", rawstring); + + // print a warning if there is inconsistency here! + if (disk->id != thre->id){ + char atdat[64],atthr[64]; + ataPrintSmartAttribName(atdat, disk->id, con->attributedefs); + ataPrintSmartAttribName(atthr, thre->id, con->attributedefs); + pout("%-28s<== Data Page | WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat); + pout("%-28s<== Threshold Page | INCONSISTENT IDENTITIES IN THE DATA\n",atthr); + } + } + } + if (!needheader) pout("\n"); +} + +void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){ + pout("General SMART Values:\n"); + + PrintSmartOfflineStatus(data); + + if (isSupportSelfTest(data)){ + PrintSmartSelfExecStatus (data); + } + + PrintSmartTotalTimeCompleteOffline(data); + PrintSmartOfflineCollectCap(data); + PrintSmartCapability(data); + + PrintSmartErrorLogCapability(data, drive); + + pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)? + "General Purpose Logging supported.": + "No General Purpose Logging support."); + + if (isSupportSelfTest(data)){ + PrintSmartShortSelfTestPollingTime (data); + PrintSmartExtendedSelfTestPollingTime (data); + } + if (isSupportConveyanceSelfTest(data)) + PrintSmartConveyanceSelfTestPollingTime (data); + + pout("\n"); +} + +int ataPrintLogDirectory(struct ata_smart_log_directory *data){ + int i; + char *name; + + pout("SMART Log Directory Logging Version %d%s\n", + data->logversion, data->logversion==1?" [multi-sector log support]":""); + for (i=0; i<=255; i++){ + int numsect; + + // Directory log length + numsect = i? data->entry[i-1].numsectors : 1; + + // If the log is not empty, what is it's name + if (numsect){ + switch (i) { + case 0: + name="Log Directory"; break; + case 1: + name="Summary SMART error log"; break; + case 2: + name="Comprehensive SMART error log"; break; + case 3: + name="Extended Comprehensive SMART error log"; break; + case 6: + name="SMART self-test log"; break; + case 7: + name="Extended self-test log"; break; + case 9: + name="Selective self-test log"; break; + case 0x20: + name="Streaming performance log"; break; + case 0x21: + name="Write stream error log"; break; + case 0x22: + name="Read stream error log"; break; + case 0x23: + name="Delayed sector log"; break; + default: + if (0xa0<=i && i<=0xbf) + name="Device vendor specific log"; + else if (0x80<=i && i<=0x9f) + name="Host vendor specific log"; + else + name="Reserved log"; + break; + } + + // print name and length of log + pout("Log at address 0x%02x has %03d sectors [%s]\n", + i, numsect, name); + } + } + return 0; +} + +// returns number of errors +int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){ + int k; + + pout("SMART Error Log Version: %d\n", (int)data->revnumber); + + // if no errors logged, return + if (!data->error_log_pointer){ + pout("No Errors Logged\n\n"); + return 0; + } + PRINT_ON(con); + // If log pointer out of range, return + if (data->error_log_pointer>5){ + pout("Invalid Error Log index = 0x%02x (T13/1321D rev 1c " + "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n", + (int)data->error_log_pointer); + return 0; + } + + // Some internal consistency checking of the data structures + if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) { + pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n", + data->ata_error_count,data->error_log_pointer); + } + + // starting printing error log info + if (data->ata_error_count<=5) + pout( "ATA Error Count: %d\n", (int)data->ata_error_count); + else + pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n", + (int)data->ata_error_count); + PRINT_OFF(con); + pout("\tCR = Command Register [HEX]\n" + "\tFR = Features Register [HEX]\n" + "\tSC = Sector Count Register [HEX]\n" + "\tSN = Sector Number Register [HEX]\n" + "\tCL = Cylinder Low Register [HEX]\n" + "\tCH = Cylinder High Register [HEX]\n" + "\tDH = Device/Head Register [HEX]\n" + "\tDC = Device Command Register [HEX]\n" + "\tER = Error register [HEX]\n" + "\tST = Status register [HEX]\n" + "Powered_Up_Time is measured from power on, and printed as\n" + "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n" + "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n"); + + // now step through the five error log data structures (table 39 of spec) + for (k = 4; k >= 0; k-- ) { + char *st_er_desc; + + // The error log data structure entries are a circular buffer + int j, i=(data->error_log_pointer+k)%5; + struct ata_smart_errorlog_struct *elog=data->errorlog_struct+i; + struct ata_smart_errorlog_error_struct *summary=&(elog->error_struct); + + // Spec says: unused error log structures shall be zero filled + if (nonempty((unsigned char*)elog,sizeof(*elog))){ + // Table 57 of T13/1532D Volume 1 Revision 3 + char *msgstate; + int bits=summary->state & 0x0f; + int days = (int)summary->timestamp/24; + + switch (bits){ + case 0x00: msgstate="in an unknown state";break; + case 0x01: msgstate="sleeping"; break; + case 0x02: msgstate="in standby mode"; break; + case 0x03: msgstate="active or idle"; break; + case 0x04: msgstate="doing SMART Offline or Self-test"; break; + default: + if (bits<0x0b) + msgstate="in a reserved state"; + else + msgstate="in a vendor specific state"; + } + + // See table 42 of ATA5 spec + PRINT_ON(con); + pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n", + (int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days)); + PRINT_OFF(con); + pout(" When the command that caused the error occurred, the device was %s.\n\n",msgstate); + pout(" After command completion occurred, registers were:\n" + " ER ST SC SN CL CH DH\n" + " -- -- -- -- -- -- --\n" + " %02x %02x %02x %02x %02x %02x %02x", + (int)summary->error_register, + (int)summary->status, + (int)summary->sector_count, + (int)summary->sector_number, + (int)summary->cylinder_low, + (int)summary->cylinder_high, + (int)summary->drive_head); + // Add a description of the contents of the status and error registers + // if possible + st_er_desc = construct_st_er_desc(elog); + if (st_er_desc) { + pout(" %s", st_er_desc); + free(st_er_desc); + } + pout("\n\n"); + pout(" Commands leading to the command that caused the error were:\n" + " CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name\n" + " -- -- -- -- -- -- -- -- ---------------- --------------------\n"); + for ( j = 4; j >= 0; j--){ + struct ata_smart_errorlog_command_struct *thiscommand=elog->commands+j; + + // Spec says: unused data command structures shall be zero filled + if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand))) { + char timestring[32]; + + // Convert integer milliseconds to a text-format string + MsecToText(thiscommand->timestamp, timestring); + + pout(" %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n", + (int)thiscommand->commandreg, + (int)thiscommand->featuresreg, + (int)thiscommand->sector_count, + (int)thiscommand->sector_number, + (int)thiscommand->cylinder_low, + (int)thiscommand->cylinder_high, + (int)thiscommand->drive_head, + (int)thiscommand->devicecontrolreg, + timestring, + look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg)); + } + } + pout("\n"); + } + } + PRINT_ON(con); + if (con->printing_switchable) + pout("\n"); + PRINT_OFF(con); + return data->ata_error_count; +} + +void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) { + int i,field1,field2; + char *msg; + char tmp[64]; + uint64_t maxl=0,maxr=0; + uint64_t current=log->currentlba; + uint64_t currentend=current+65535; + + // print data structure revision number + pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion); + if (1 != log->logversion) + pout("Warning: ATA Specification requires selective self-test log data structure revision number = 1\n"); + + switch((sv->self_test_exec_status)>>4){ + case 0:msg="Completed"; + break; + case 1:msg="Aborted_by_host"; + break; + case 2:msg="Interrupted"; + break; + case 3:msg="Fatal_error"; + break; + case 4:msg="Completed_unknown_failure"; + break; + case 5:msg="Completed_electrical_failure"; + break; + case 6:msg="Completed_servo/seek_failure"; + break; + case 7:msg="Completed_read_failure"; + break; + case 8:msg="Completed_handling_damage??"; + break; + case 15:msg="Self_test_in_progress"; + break; + default:msg="Unknown_status "; + break; + } + + // find the number of columns needed for printing. If in use, the + // start/end of span being read-scanned... + if (log->currentspan>5) { + maxl=current; + maxr=currentend; + } + for (i=0; i<5; i++) { + uint64_t start=log->span[i].start; + uint64_t end =log->span[i].end; + // ... plus max start/end of each of the five test spans. + if (start>maxl) + maxl=start; + if (end > maxr) + maxr=end; + } + + // we need at least 7 characters wide fields to accomodate the + // labels + if ((field1=snprintf(tmp,64, "%"PRIu64, maxl))<7) + field1=7; + if ((field2=snprintf(tmp,64, "%"PRIu64, maxr))<7) + field2=7; + + // now print the five test spans + pout(" SPAN %*s %*s CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA"); + + for (i=0; i<5; i++) { + uint64_t start=log->span[i].start; + uint64_t end=log->span[i].end; + + if ((i+1)==(int)log->currentspan) + // this span is currently under test + pout(" %d %*"PRIu64" %*"PRIu64" %s [%01d0%% left] (%"PRIu64"-%"PRIu64")\n", + i+1, field1, start, field2, end, msg, + (int)(sv->self_test_exec_status & 0x7), current, currentend); + else + // this span is not currently under test + pout(" %d %*"PRIu64" %*"PRIu64" Not_testing\n", + i+1, field1, start, field2, end); + } + + // if we are currently read-scanning, print LBAs and the status of + // the read scan + if (log->currentspan>5) + pout("%5d %*"PRIu64" %*"PRIu64" Read_scanning %s\n", + (int)log->currentspan, field1, current, field2, currentend, + OfflineDataCollectionStatus(sv->offline_data_collection_status)); + + /* Print selective self-test flags. Possible flag combinations are + (numbering bits from 0-15): + Bit-1 Bit-3 Bit-4 + Scan Pending Active + 0 * * Don't scan + 1 0 0 Will carry out scan after selective test + 1 1 0 Waiting to carry out scan after powerup + 1 0 1 Currently scanning + 1 1 1 Currently scanning + */ + + pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags); + if (log->flags & SELECTIVE_FLAG_DOSCAN) { + if (log->flags & SELECTIVE_FLAG_ACTIVE) + pout(" Currently read-scanning the remainder of the disk.\n"); + else if (log->flags & SELECTIVE_FLAG_PENDING) + pout(" Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n", + (int)log->pendingtime); + else + pout(" After scanning selected spans, read-scan remainder of disk.\n"); + } + else + pout(" After scanning selected spans, do NOT read-scan remainder of disk.\n"); + + // print pending time + pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n", + (int)log->pendingtime); + + return; +} + +// return value is: +// bottom 8 bits: number of entries found where self-test showed an error +// remaining bits: if nonzero, power on hours of last self-test where error was found +int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){ + int i,j,noheaderprinted=1; + int retval=0, hours=0, testno=0; + + if (allentries) + pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber); + if ((data->revnumber!=0x0001) && allentries && con->fixfirmwarebug != FIX_SAMSUNG) + pout("Warning: ATA Specification requires self-test log structure revision number = 1\n"); + if (data->mostrecenttest==0){ + if (allentries) + pout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n\n"); + return 0; + } + + // print log + for (i=20;i>=0;i--){ + struct ata_smart_selftestlog_struct *log; + + // log is a circular buffer + j=(i+data->mostrecenttest)%21; + log=data->selftest_struct+j; + + if (nonempty((unsigned char*)log,sizeof(*log))){ + char *msgtest,*msgstat,percent[64],firstlba[64]; + int errorfound=0; + + // count entry based on non-empty structures -- needed for + // Seagate only -- other vendors don't have blank entries 'in + // the middle' + testno++; + + // test name + switch(log->selftestnumber){ + case 0: msgtest="Offline "; break; + case 1: msgtest="Short offline "; break; + case 2: msgtest="Extended offline "; break; + case 3: msgtest="Conveyance offline "; break; + case 4: msgtest="Selective offline "; break; + case 127: msgtest="Abort offline test "; break; + case 129: msgtest="Short captive "; break; + case 130: msgtest="Extended captive "; break; + case 131: msgtest="Conveyance captive "; break; + case 132: msgtest="Selective captive "; break; + default: + if ( log->selftestnumber>=192 || + (log->selftestnumber>= 64 && log->selftestnumber<=126)) + msgtest="Vendor offline "; + else + msgtest="Reserved offline "; + } + + // test status + switch((log->selfteststatus)>>4){ + case 0:msgstat="Completed without error "; break; + case 1:msgstat="Aborted by host "; break; + case 2:msgstat="Interrupted (host reset) "; break; + case 3:msgstat="Fatal or unknown error "; errorfound=1; break; + case 4:msgstat="Completed: unknown failure "; errorfound=1; break; + case 5:msgstat="Completed: electrical failure"; errorfound=1; break; + case 6:msgstat="Completed: servo/seek failure"; errorfound=1; break; + case 7:msgstat="Completed: read failure "; errorfound=1; break; + case 8:msgstat="Completed: handling damage?? "; errorfound=1; break; + case 15:msgstat="Self-test routine in progress"; break; + default:msgstat="Unknown/reserved test status "; + } + + retval+=errorfound; + sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf); + + // T13/1321D revision 1c: (Data structure Rev #1) + + //The failing LBA shall be the LBA of the uncorrectable sector + //that caused the test to fail. If the device encountered more + //than one uncorrectable sector during the test, this field + //shall indicate the LBA of the first uncorrectable sector + //encountered. If the test passed or the test failed for some + //reason other than an uncorrectable sector, the value of this + //field is undefined. + + // This is true in ALL ATA-5 specs + + if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000) + sprintf(firstlba,"%s","-"); + else + sprintf(firstlba,"%u",log->lbafirstfailure); + + // print out a header if needed + if (noheaderprinted && (allentries || errorfound)){ + pout("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); + noheaderprinted=0; + } + + // print out an entry, either if we are printing all entries OR + // if an error was found + if (allentries || errorfound) + pout("#%2d %s %s %s %8d %s\n", testno, msgtest, msgstat, percent, (int)log->timestamp, firstlba); + + // keep track of time of most recent error + if (errorfound && !hours) + hours=log->timestamp; + } + } + if (!allentries && retval) + pout("\n"); + + hours = hours << 8; + return (retval | hours); +} + +void ataPseudoCheckSmart ( struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds) { + int i; + int failed = 0; + for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) { + if (data->vendor_attributes[i].id && + thresholds->thres_entries[i].id && + ATTRIBUTE_FLAGS_PREFAILURE(data->vendor_attributes[i].flags) && + (data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) && + (thresholds->thres_entries[i].threshold != 0xFE)){ + pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id); + failed = 1; + } + } + pout("%s\n", ( failed )? + "SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA": + "SMART overall-health self-assessment test result: PASSED"); +} + + +// Compares failure type to policy in effect, and either exits or +// simply returns to the calling routine. +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 failed: exiting. Remove '-T conservative' option to continue.\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 failed: exiting. To continue, add one or more '-T permissive' options.\n"); + EXIT(returnvalue); + } + + pout("Smartctl internal error in failuretest(type=%d). Please contact developers at " PACKAGE_HOMEPAGE "\n",type); + EXIT(returnvalue|FAILCMD); +} + +// Used to warn users about invalid checksums. Action to be taken may be +// altered by the user. +void checksumwarning(const char *string){ + // user has asked us to ignore checksum errors + if (con->checksumignore) + return; + + pout("Warning! %s error: invalid SMART checksum.\n",string); + + // user has asked us to fail on checksum errors + if (con->checksumfail) + EXIT(FAILSMART); + + return; +} + +// Initialize to zero just in case some SMART routines don't work +struct ata_identify_device drive; +struct ata_smart_values smartval; +struct ata_smart_thresholds_pvt smartthres; +struct ata_smart_errorlog smarterror; +struct ata_smart_selftestlog smartselftest; + +int ataPrintMain (int fd){ + int timewait,code; + int returnval=0, retid=0, supported=0, needupdate=0; + + // Start by getting Drive ID information. We need this, to know if SMART is supported. + if ((retid=ataReadHDIdentity(fd,&drive))<0){ + pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILID); + } + + // If requested, show which presets would be used for this drive and exit. + if (con->showpresets) { + showpresets(&drive); + EXIT(0); + } + + // Use preset vendor attribute options unless user has requested otherwise. + if (!con->ignorepresets){ + unsigned char *charptr; + if ((charptr=con->attributedefs)) + applypresets(&drive, &charptr, con); + else { + pout("Fatal internal error in ataPrintMain()\n"); + EXIT(returnval|=FAILCMD); + } + } + + // Print most drive identity information if requested + if (con->driveinfo){ + pout("=== START OF INFORMATION SECTION ===\n"); + ataPrintDriveInfo(&drive); + } + + // Was this a packet device? + if (retid>0){ + pout("SMART support is: Unavailable - Packet Interface Devices [this device: %s] don't support ATA SMART\n", packetdevicetype(retid-1)); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + + // if drive does not supports SMART it's time to exit + supported=ataSmartSupport(&drive); + if (supported != 1){ + if (supported==0) { + pout("SMART support is: Unavailable - device lacks SMART capability.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + pout(" Checking to be sure by trying SMART ENABLE command.\n"); + } + else { + pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + pout(" Checking for SMART support by trying SMART ENABLE command.\n"); + } + + if (ataEnableSmart(fd)){ + pout(" SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + supported=0; + } + else { + pout(" SMART ENABLE appeared to work! Continuing.\n"); + supported=1; + } + if (!con->driveinfo) pout("\n"); + } + + // Now print remaining drive info: is SMART enabled? + if (con->driveinfo){ + int ison=ataIsSmartEnabled(&drive),isenabled=ison; + + if (ison==-1) { + pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + // check SMART support by trying a command + pout(" Checking to be sure by trying SMART RETURN STATUS command.\n"); + isenabled=ataDoesSmartWork(fd); + } + else + pout("SMART support is: Available - device has SMART capability.\n"); + + if (isenabled) + pout("SMART support is: Enabled\n"); + else { + if (ison==-1) + pout("SMART support is: Unavailable\n"); + else + pout("SMART support is: Disabled\n"); + } + pout("\n"); + } + + // START OF THE ENABLE/DISABLE SECTION OF THE CODE + if (con->smartenable || con->smartdisable || + con->smartautosaveenable || con->smartautosavedisable || + con->smartautoofflineenable || con->smartautoofflinedisable) + pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); + + // Enable/Disable SMART commands + if (con->smartenable){ + if (ataEnableSmart(fd)) { + pout("Smartctl: SMART Enable Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Enabled.\n"); + } + + // From here on, every command requires that SMART be enabled... + if (!ataDoesSmartWork(fd)) { + pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); + return returnval; + } + + // Turn off SMART on device + if (con->smartdisable){ + if (ataDisableSmart(fd)) { + pout( "Smartctl: SMART Disable Failed.\n\n"); + failuretest(MANDATORY_CMD,returnval|=FAILSMART); + } + pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); + return returnval; + } + + // Let's ALWAYS issue this command to get the SMART status + code=ataSmartStatus2(fd); + if (code==-1) + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + + // Enable/Disable Auto-save attributes + if (con->smartautosaveenable){ + if (ataEnableAutoSave(fd)){ + pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Attribute Autosave Enabled.\n"); + } + if (con->smartautosavedisable){ + if (ataDisableAutoSave(fd)){ + pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Attribute Autosave Disabled.\n"); + } + + // for everything else read values and thresholds are needed + if (ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if (ataReadSmartThresholds(fd, &smartthres)){ + pout("Smartctl: SMART Read Thresholds failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + + // Enable/Disable Off-line testing + if (con->smartautoofflineenable){ + if (!isSupportAutomaticTimer(&smartval)){ + pout("Warning: device does not support SMART Automatic Timers.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + needupdate=1; + if (ataEnableAutoOffline(fd)){ + pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + pout("SMART Automatic Offline Testing Enabled every four hours.\n"); + } + if (con->smartautoofflinedisable){ + if (!isSupportAutomaticTimer(&smartval)){ + pout("Warning: device does not support SMART Automatic Timers.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + needupdate=1; + if (ataDisableAutoOffline(fd)){ + pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + pout("SMART Automatic Offline Testing Disabled.\n"); + } + + if (needupdate && ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + + // all this for a newline! + if (con->smartenable || con->smartdisable || + con->smartautosaveenable || con->smartautosavedisable || + con->smartautoofflineenable || con->smartautoofflinedisable) + pout("\n"); + + // START OF READ-ONLY OPTIONS APART FROM -V and -i + if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog) + pout("=== START OF READ SMART DATA SECTION ===\n"); + + // Check SMART status (use previously returned value) + if (con->checksmart){ + switch (code) { + + case 0: + // The case where the disk health is OK + pout("SMART overall-health self-assessment test result: PASSED\n"); + if (ataCheckSmart(&smartval, &smartthres,0)){ + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Please note the following marginal Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,2); + } + returnval|=FAILAGE; + } + else + pout("\n"); + break; + + case 1: + // The case where the disk health is NOT OK + PRINT_ON(con); + pout("SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); + PRINT_OFF(con); + if (ataCheckSmart(&smartval, &smartthres,1)){ + returnval|=FAILATTR; + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for failed Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Failed Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,1); + } + } + else + pout("No failed Attributes found.\n\n"); + returnval|=FAILSTATUS; + PRINT_OFF(con); + break; + + case -1: + default: + // The case where something went wrong with HDIO_DRIVE_TASK ioctl() + if (ataCheckSmart(&smartval, &smartthres,1)){ + PRINT_ON(con); + pout("SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); + PRINT_OFF(con); + returnval|=FAILATTR; + returnval|=FAILSTATUS; + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for failed Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Failed Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,1); + } + } + else { + pout("SMART overall-health self-assessment test result: PASSED\n"); + if (ataCheckSmart(&smartval, &smartthres,0)){ + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Please note the following marginal Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,2); + } + returnval|=FAILAGE; + } + else + pout("\n"); + } + PRINT_OFF(con); + break; + } // end of switch statement + + PRINT_OFF(con); + } // end of checking SMART Status + + // Print general SMART values + if (con->generalsmartvalues) + ataPrintGeneralSmartValues(&smartval, &drive); + + // Print vendor-specific attributes + if (con->smartvendorattrib){ + PRINT_ON(con); + PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0); + PRINT_OFF(con); + } + + // Print SMART log Directory + if (con->smartlogdirectory){ + struct ata_smart_log_directory smartlogdirectory; + if (!isGeneralPurposeLoggingCapable(&drive)){ + pout("Warning: device does not support General Purpose Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + pout("Log Directory Supported\n"); + if (ataReadLogDirectory(fd, &smartlogdirectory)){ + PRINT_OFF(con); + pout("Read Log Directory failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + ataPrintLogDirectory( &smartlogdirectory); + } + PRINT_OFF(con); + } + + // Print SMART error log + if (con->smarterrorlog){ + if (!isSmartErrorLogCapable(&smartval, &drive)){ + pout("Warning: device does not support Error Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if (ataReadErrorLog(fd, &smarterror)){ + pout("Smartctl: SMART Error Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + // quiet mode is turned on inside ataPrintSmartErrorLog() + if (ataPrintSmartErrorlog(&smarterror)) + returnval|=FAILERR; + PRINT_OFF(con); + } + } + + // Print SMART self-test log + if (con->smartselftestlog){ + if (!isSmartTestLogCapable(&smartval, &drive)){ + pout("Warning: device does not support Self Test Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if(ataReadSelfTestLog(fd, &smartselftest)){ + pout("Smartctl: SMART Self Test Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable)) + returnval|=FAILLOG; + PRINT_OFF(con); + pout("\n"); + } + } + + // Print SMART selective self-test log + if (con->selectivetestlog){ + struct ata_selective_self_test_log log; + + if (!isSupportSelectiveSelfTest(&smartval)) + pout("Device does not support Selective Self Tests/Logging\n"); + else if(ataReadSelectiveSelfTestLog(fd, &log)) { + pout("Smartctl: SMART Selective Self Test Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + ataPrintSelectiveSelfTestLog(&log, &smartval); + PRINT_OFF(con); + pout("\n"); + } + } + + // START OF THE TESTING SECTION OF THE CODE. IF NO TESTING, RETURN + if (con->testcase==-1) + return returnval; + + pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n"); + // if doing a self-test, be sure it's supported by the hardware + switch (con->testcase){ + case OFFLINE_FULL_SCAN: + if (!isSupportExecuteOfflineImmediate(&smartval)){ + pout("Warning: device does not support Execute Offline Immediate function.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case ABORT_SELF_TEST: + case SHORT_SELF_TEST: + case EXTEND_SELF_TEST: + case SHORT_CAPTIVE_SELF_TEST: + case EXTEND_CAPTIVE_SELF_TEST: + if (!isSupportSelfTest(&smartval)){ + pout("Warning: device does not support Self-Test functions.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case CONVEYANCE_SELF_TEST: + case CONVEYANCE_CAPTIVE_SELF_TEST: + if (!isSupportConveyanceSelfTest(&smartval)){ + pout("Warning: device does not support Conveyance Self-Test functions.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case SELECTIVE_SELF_TEST: + case SELECTIVE_CAPTIVE_SELF_TEST: + if (!isSupportSelectiveSelfTest(&smartval)){ + pout("Warning: device does not support Selective Self-Test functions.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + break; + default: + pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase); + pout("Please contact smartmontools developers at %s.\n", PACKAGE_BUGREPORT); + EXIT(returnval|=FAILCMD); + } + + // Now do the test. Note ataSmartTest prints its own error/success + // messages + if (ataSmartTest(fd, con->testcase, &smartval)) + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + else { + // Tell user how long test will take to complete. This is tricky + // because in the case of an Offline Full Scan, the completion + // timer is volatile, and needs to be read AFTER the command is + // given. If this will interrupt the Offline Full Scan, we don't + // do it, just warn user. + if (con->testcase==OFFLINE_FULL_SCAN){ + if (isSupportOfflineAbort(&smartval)) + pout("Note: giving further SMART commands will abort Offline testing\n"); + else if (ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + } + + // Now say how long the test will take to complete + if ((timewait=TestTime(&smartval,con->testcase))){ + time_t t=time(NULL); + if (con->testcase==OFFLINE_FULL_SCAN) { + t+=timewait; + pout("Please wait %d seconds for test to complete.\n", (int)timewait); + } else { + t+=timewait*60; + pout("Please wait %d minutes for test to complete.\n", (int)timewait); + } + pout("Test will complete after %s\n", ctime(&t)); + + if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && + con->testcase!=EXTEND_CAPTIVE_SELF_TEST && + con->testcase!=CONVEYANCE_CAPTIVE_SELF_TEST && + con->testcase!=SELECTIVE_CAPTIVE_SELF_TEST) + pout("Use smartctl -X to abort test.\n"); + } + } + + return returnval; +} diff --git a/sm5/knowndrives.c b/sm5/knowndrives.c new file mode 100644 index 0000000000000000000000000000000000000000..a0861ef7c141a41166eb7ac7d881918f3fd3bd58 --- /dev/null +++ b/sm5/knowndrives.c @@ -0,0 +1,1048 @@ +/* + * knowndrives.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * Address of support mailing list: smartmontools-support@lists.sourceforge.net + * + * Copyright (C) 2003-4 Philip Williams, Bruce Allen + * + * 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. + * + */ + +#include <stdio.h> +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "int64.h" +#include "knowndrives.h" +#include "utility.h" // includes <regex.h> +#include "config.h" + +const char *knowndrives_c_cvsid="$Id: knowndrives.c,v 1.118 2004/08/21 15:20:39 pjwilliams Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID; + +#define MODEL_STRING_LENGTH 40 +#define FIRMWARE_STRING_LENGTH 8 +#define TABLEPRINTWIDTH 19 + +// See vendorattributeargs[] array in atacmds.c for definitions. +#define PRESET_9_MINUTES { 9, 1 } +#define PRESET_9_TEMP { 9, 2 } +#define PRESET_9_SECONDS { 9, 3 } +#define PRESET_9_HALFMINUTES { 9, 4 } +#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192, 1 } +#define PRESET_193_LOADUNLOAD { 193, 1 } +#define PRESET_194_10XCELSIUS { 194, 1 } +#define PRESET_194_UNKNOWN { 194, 2 } +#define PRESET_198_OFFLINESCANUNCSECTORCT { 198, 1 } +#define PRESET_200_WRITEERRORCOUNT { 200, 1 } +#define PRESET_201_DETECTEDTACOUNT { 201, 1 } +#define PRESET_220_TEMP { 220, 1 } + +/* Arrays of preset vendor-specific attribute options for use in + * knowndrives[]. */ + +extern int64_t bytes; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// These three are common to several models. +const unsigned char vendoropts_9_minutes[][2] = { + PRESET_9_MINUTES, + {0,0} +}; +const unsigned char vendoropts_9_halfminutes[][2] = { + PRESET_9_HALFMINUTES, + {0,0} +}; +const unsigned char vendoropts_9_seconds[][2] = { + PRESET_9_SECONDS, + {0,0} +}; + +const unsigned char vendoropts_Maxtor_4D080H4[][2] = { + PRESET_9_MINUTES, + PRESET_194_UNKNOWN, + {0,0} +}; + +const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = { + PRESET_9_SECONDS, + PRESET_192_EMERGENCYRETRACTCYCLECT, + PRESET_198_OFFLINESCANUNCSECTORCT, + PRESET_200_WRITEERRORCOUNT, + PRESET_201_DETECTEDTACOUNT, + {0,0} +}; + +const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = { + PRESET_9_SECONDS, + PRESET_192_EMERGENCYRETRACTCYCLECT, + PRESET_198_OFFLINESCANUNCSECTORCT, + PRESET_200_WRITEERRORCOUNT, + {0,0} +}; + +const unsigned char vendoropts_Samsung_SV4012H[][2] = { + PRESET_9_HALFMINUTES, + {0,0} +}; + +const unsigned char vendoropts_Samsung_SV1204H[][2] = { + PRESET_9_HALFMINUTES, + PRESET_194_10XCELSIUS, + {0,0} +}; + +const unsigned char vendoropts_Hitachi_DK23XX[][2] = { + PRESET_9_MINUTES, + PRESET_193_LOADUNLOAD, + {0,0} +}; + +const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)"; +const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)"; + +const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details."; +const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details."; +const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details."; +const char may_need_minus_F_enabled[] ="May need -F samsung or -F samsung2 enabled; see manual for details."; + +/* Special-purpose functions for use in knowndrives[]. */ +void specialpurpose_reverse_samsung(smartmonctrl *con) +{ + if (con->fixfirmwarebug==FIX_NOTSPECIFIED) + con->fixfirmwarebug = FIX_SAMSUNG; +} +void specialpurpose_reverse_samsung2(smartmonctrl *con) +{ + if (con->fixfirmwarebug==FIX_NOTSPECIFIED) + con->fixfirmwarebug = FIX_SAMSUNG2; +} + +/* Table of settings for known drives terminated by an element containing all + * zeros. The drivesettings structure is described in knowndrives.h. Note + * that lookupdrive() will search knowndrives[] from the start to end or + * until it finds the first match, so the order in knowndrives[] is important + * for distinct entries that could match the same drive. */ + +// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS. +// A good on-line reference for these is: +// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html + +const drivesettings knowndrives[] = { + { // IBM Deskstar 60GXP series + "IC35L0[12346]0AVER07", + ".*", + "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n" + "Please see http://www.geocities.com/dtla_update/index.html#rel and\n" + "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" + "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", + NULL, NULL, NULL + }, + { // IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware) + "(IBM-)?DTLA-30[57]0[123467][05]", + "^T[WX][123468A]OA[56]AA$", + NULL, NULL, NULL, NULL + }, + { // IBM Deskstar 40GV & 75GXP series (all other firmware) + "(IBM-)?DTLA-30[57]0[123467][05]", + ".*", + "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n" + "Please see http://www.geocities.com/dtla_update/ and\n" + "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" + "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", + NULL, NULL, NULL + }, + { // ExcelStor J240 + "^ExcelStor Technology J240$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Fujitsu MPB series + "^FUJITSU MPB....ATU?$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MPD and MPE series + "^FUJITSU MP[DE]....A[HTE]$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MPF series + "^FUJITSU MPF3(102A[HT]|153A[HT]|204A[HT])$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MPG series + "^FUJITSU MPG3(102A(H|T E)|204A(H|[HT] E)|307A(H E|T)|409A[HT] E)$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MPC series + "^FUJITSU MPC3(032AT|043AT|045AH|064A[HT]|084AT|096AT|102AT)$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MHN2300AT + "^FUJITSU MHN2300AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MHR2040AT + "^FUJITSU MHR2040AT$", + ".*", // Tested on 40BA + NULL, + vendoropts_Fujitsu_MHR2040AT, + NULL, NULL + }, + { // Fujitsu MHR2020AT + "^FUJITSU MHR2020AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MHSxxxxAT family + "^FUJITSU MHS20[6432]0AT( .)?$", + ".*", + NULL, + vendoropts_Fujitsu_MHS2020AT, + NULL, NULL + }, + { // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT + "^FUJITSU MH(L230|M2(20|10|15))0AT$", + ".*", + "This drive's firmware has a harmless Drive Identity Structure\n" + "checksum error bug.", + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MHTxxxxAT family + "^FUJITSU MHT20[23468]0AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Fujitsu MHTxxxxAH family + "^FUJITSU MHT20[468]0AH$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { // Samsung SV4012H (known firmware) + "^SAMSUNG SV4012H$", + "^RM100-08$", + NULL, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { // Samsung SV4012H (all other firmware) + "^SAMSUNG SV4012H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { // Samsung SV0412H (known firmware) + "^SAMSUNG SV0412H$", + "^SK100-01$", + NULL, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { // Samsung SV0412H (all other firmware) + "^SAMSUNG SV0412H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { // Samsung SV1204H (known firmware) + "^SAMSUNG SV1204H$", + "^RK100-1[3-5]$", + NULL, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { //Samsung SV1204H (all other firmware) + "^SAMSUNG SV1204H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { //SAMSUNG SV0322A tested with FW JK200-35 + "^SAMSUNG SV0322A$", + ".*", + NULL, + NULL, + NULL, + NULL + }, + { // SAMSUNG SP40A2H with RR100-07 firmware + "^SAMSUNG SP40A2H$", + "^RR100-07$", + NULL, + vendoropts_9_halfminutes, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { + // Any other Samsung disk with *-23 *-24 firmware + // SAMSUNG SP1213N (TL100-23 firmware) + // SAMSUNG SP0802N (TK100-23 firmware) + // Samsung SP1604N, tested with FW TM100-23 and TM100-24 + "^SAMSUNG .*$", + ".*-2[34]$", + NULL, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung2, + same_as_minus_F2 + }, + { // All Samsung drives with '.*-25' firmware + "^SAMSUNG.*", + ".*-25$", + may_need_minus_F2_disabled, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung2, + same_as_minus_F2 + }, + { // All Samsung drives with '.*-26 or later (currently to -39)' firmware + "^SAMSUNG.*", + ".*-(2[6789]|3[0-9])$", + NULL, + vendoropts_Samsung_SV4012H, + NULL, + NULL + }, + { // Samsung ALL OTHER DRIVES + "^SAMSUNG.*", + ".*", + may_need_minus_F_enabled, + NULL, NULL, NULL + }, + { // Maxtor Fireball 541DX family + "^Maxtor 2B0(0[468]|1[05]|20)H1$", + ".*", + NULL, + vendoropts_Maxtor_4D080H4, + NULL, NULL + }, + { // Maxtor DiamondMax 3400 Ultra ATA family + "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax D540X-4G family + "^Maxtor 4G(120J6|160J[68])$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Maxtor DiamondMax D540X-4K family + "^MAXTOR 4K(020H1|040H2|060H3|080H4)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Maxtor DiamondMax Plus D740X family + "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Maxtor DiamondMax Plus 5120 Ultra ATA 33 family + "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 6800 Ultra ATA 66 family + "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax D540X-4D and Maxtor 4G120J6 + "^Maxtor (4D0(20H1|40H2|60H3|80H4)|4G120J6)$", + ".*", + NULL, + vendoropts_Maxtor_4D080H4, + NULL, NULL + }, + { // Maxtor DiamondMax 16 family + "^Maxtor 4(R0[68]0[JL]|R1[26]0L|A160J)0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 4320 family + "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 20 VL family + "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax VL 30 family + "^Maxtor (33073U4|32049U3|31536U2|30768U1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 36 family + "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 40 ATA 66 series + "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100) + "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 40 VL Ultra ATA 100 series + "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 45 Ulta ATA 100 family + "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 60 family + "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 80 family + "^Maxtor (98196H8|96147H6)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax 536DX family + "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 8 family + "^Maxtor 6E0[234]0L0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor DiamondMax Plus 9 family + "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor MaXLine Plus II + "^Maxtor 7Y250[PM]0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // Maxtor MaXLine II family + "^Maxtor [45]A(25|32)0[JN]0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { // HITACHI_DK14FA-20B + "^HITACHI_DK14FA-20B$", + ".*", + NULL, + vendoropts_Hitachi_DK23XX, + NULL, NULL + }, + { // HITACHI Travelstar DK23XX/DK23XXB series + "^HITACHI_DK23..-..B?$", + ".*", + NULL, + vendoropts_Hitachi_DK23XX, + NULL, NULL + }, + { // IBM Deskstar 14GXP and 16GP series + "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Deskstar 25GP and 22GXP family + "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Travelstar 25GS, 18GT, and 12GN family + "^IBM-DARA-2(25|18|15|12|09|06)000$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Travelstar 48GH, 30GN, and 15GN family + "^IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Travelstar 32GH, 30GT, and 20GN family + "^IBM-DJSA-2(32|30|20|10|05)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Deskstar 37GP and 34GXP family + "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM/Hitachi Travelstar 60GH and 40GN family + "^IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM/Hitachi Travelstar 40GNX family + "^IC25N0[42]0ATC[SX]05-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Hitachi Travelstar 80GN family + "^(Hitachi )?IC25N0[23468]0ATMR04-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Hitachi Travelstar 5K80 family + "^HTS5480[8642]0M9AT00$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Hitachi Travelstar 7K60 + "^HTS726060M9AT00$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM/Hitachi Deskstar 120GXP family + "^IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM/Hitachi Deskstar GXP-180 family + "^IC35L(030|060|090|120|180)AVV207-[01]$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Travelstar 14GS + "^IBM-DCYA-214000$", + ".*", + NULL, NULL, NULL, NULL + }, + { // IBM Travelstar 4LP + "^IBM-DTNA-2(180|216)0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Hitachi Deskstar 7K250 series + "^HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK4025GAS + "^TOSHIBA MK4025GAS$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop] + "^TOSHIBA MK6021GAS$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK4019GAX/MK4019GAXB + "^TOSHIBA MK4019GAXB?$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK6409MAV + "^TOSHIBA MK6409MAV$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOS MK3019GAXB SUN30G + "^TOS MK3019GAXB SUN30G$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS + "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK4018GAS + "^TOSHIBA MK4018GAS$", + ".*", + NULL, NULL, NULL, NULL + }, + { // TOSHIBA MK3017GAP + "^TOSHIBA MK3017GAP$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Medalist 8641 family + "^ST3(2110|3221|4312|6531|8641)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate U Series X family + "^ST3(10014A(CE)?|20014A)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate U Series 6 family + "^ST3(8002|6002|4081|3061|2041)0A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate U Series 5 family + "^ST3(40823|30621|20413|15311|10211)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate U8 family + "^ST3(8410|4313|17221|13021)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate U10 family + "^ST3(20423|15323|10212)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Barracuda ATA II family + "^ST3(3063|2042|1532|1021)0A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Barracuda ATA III family + "^ST3(40824|30620|20414|15310|10215)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Barracuda ATA IV family + "^ST3(20011|40016|60021|80021)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Barracuda ATA V family + "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Barracuda 7200.7 and 7200.7 Plus family + "^ST3(200822AS?|16002[13]AS?|12002[26]AS?|8001[13]AS?|60014A|40014AS?)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Seagate Medalist 17242, 13032, 10232, 8422, and 4312 + "^ST3(1724|1303|1023|842|431)2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Protege + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD([2468]00E|1[26]00A)B-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar family + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar WDxxxAB series + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(3|4|6)00AB-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar WDxxxAA series + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(64|84|102|136|205|272|307)AA(-.*)?$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar WDxxxBA series + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(102|136|153|205)BA$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar AC12500, AC24300, AC25100, AC36400, AC38400 + "^WDC AC(125|243|251|364|384)00", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar SE family + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD((4|6|8|10|12|16|18|20|25)00JB|(12|20|25)00PB)-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar SE (Serial ATA) family + "^WDC WD(4|8|12|16|20|25)00JD-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar AC38400 + "^WDC AC38400L$", + ".*", + NULL, NULL, NULL, NULL + }, + { // Western Digital Caviar AC23200L + "^WDC AC23200L$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30 + "^QUANTUM FIREBALLlct15 [23]0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALLlct20 series + "^QUANTUM FIREBALLlct20 [234]0$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALL CX10.2A + "^QUANTUM FIREBALL CX10.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALLP LM15 and LM30 + "^QUANTUM FIREBALLP LM(15|30)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALL CR4.3A + "^QUANTUM FIREBALL CR4.3A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0 + "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALL EX6.4A + "^QUANTUM FIREBALL EX6.4A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALL ST3.2A + "^QUANTUM FIREBALL ST3.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { // QUANTUM FIREBALL EX3.2A + "^QUANTUM FIREBALL EX3.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + /*------------------------------------------------------------ + * End of table. Do not add entries below this marker. + *------------------------------------------------------------ */ + {NULL, NULL, NULL, NULL, NULL, NULL} +}; + +// Searches knowndrives[] for a drive with the given model number and firmware +// string. If either the drive's model or firmware strings are not set by the +// manufacturer then values of NULL may be used. Returns the index of the +// first match in knowndrives[] or -1 if no match if found. +int lookupdrive(const char *model, const char *firmware) +{ + regex_t regex; + int i, index; + const char *empty = ""; + + model = model ? model : empty; + firmware = firmware ? firmware : empty; + + for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) { + // Attempt to compile regular expression. + if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) + goto CONTINUE; + + // Check whether model matches the regular expression in knowndrives[i]. + if (!regexec(®ex, model, 0, NULL, 0)) { + // model matches, now check firmware. + if (!knowndrives[i].firmwareregexp) + // The firmware regular expression in knowndrives[i] is NULL, which is + // considered a match. + index = i; + else { + // Compare firmware against the regular expression in knowndrives[i]. + regfree(®ex); // Recycle regex. + if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) + goto CONTINUE; + if (!regexec(®ex, firmware, 0, NULL, 0)) + index = i; + } + } + CONTINUE: + regfree(®ex); + } + + return index; +} + + +// Shows all presets for drives in knowndrives[]. +void showonepreset(const drivesettings *drivetable){ + + const unsigned char (* presets)[2] = drivetable->vendoropts; + int first_preset = 1; + + // Basic error check + if (!drivetable || !drivetable->modelregexp){ + pout("Null known drive table pointer. Please report\n" + "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n"); + return; + } + + // print model and firmware regular expressions + pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp); + pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ? + drivetable->firmwareregexp : ""); + + // if there are any presets, then show them + if (presets && (*presets)[0]) while (1) { + char out[256]; + const int attr = (*presets)[0], val = (*presets)[1]; + unsigned char fakearray[MAX_ATTRIBUTE_NUM]; + + // if we are at the end of the attribute list, break out + if (!attr) + break; + + // This is a hack. ataPrintSmartAttribName() needs a pointer to an + // "array" to dereference, so we provide such a pointer. + fakearray[attr]=val; + ataPrintSmartAttribName(out, attr, fakearray); + + // Use leading zeros instead of spaces so that everything lines up. + out[0] = (out[0] == ' ') ? '0' : out[0]; + out[1] = (out[1] == ' ') ? '0' : out[1]; + pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out); + first_preset = 0; + presets++; + } + else + pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); + + + // Is a special purpose function defined? If so, describe it + if (drivetable->specialpurpose){ + pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:"); + pout("%s\n", drivetable->functiondesc ? + drivetable->functiondesc : "A special purpose function " + "is defined for this drive"); + } + + // Print any special warnings + if (drivetable->warningmsg){ + pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:"); + pout("%s\n", drivetable->warningmsg); + } + + return; +} + +void showallpresets(void){ + int i; + + // loop over all entries in the knowndrives[] table, printing them + // out in a nice format + for (i=0; knowndrives[i].modelregexp; i++){ + showonepreset(&knowndrives[i]); + pout("\n"); + } + pout("For information about adding a drive to the database see the FAQ on the\n"); + pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n"); + return; +} + +// Shows the presets (if any) that are available for the given drive. +void showpresets(const struct ata_identify_device *drive){ + int i; + char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; + + // get the drive's model/firmware strings + formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); + formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); + + // and search to see if they match values in the table + if ((i = lookupdrive(model, firmware)) < 0) { + // no matches found + pout("No presets are defined for this drive. Its identity strings:\n" + "MODEL: %s\n" + "FIRMWARE: %s\n" + "do not match any of the known regular expressions.\n" + "Use -P showall to list all known regular expressions.\n", + model, firmware); + return; + } + + // We found a matching drive. Print out all information about it. + pout("Drive found in smartmontools Database. Drive identity strings:\n" + "%-*s %s\n" + "%-*s %s\n" + "match smartmontools Drive Database entry:\n", + TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware); + showonepreset(&knowndrives[i]); + return; +} + +// Sets preset vendor attribute options in opts by finding the entry +// (if any) for the given drive in knowndrives[]. Values that have +// already been set in opts will not be changed. Returns <0 if drive +// not recognized else index >=0 into drive database. +int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr, + smartmonctrl *con) { + int i; + unsigned char *opts; + char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; + + if (*optsptr==NULL) + bytes+=MAX_ATTRIBUTE_NUM; + + if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){ + pout("Unable to allocate memory in applypresets()"); + bytes-=MAX_ATTRIBUTE_NUM; + EXIT(1); + } + + opts=*optsptr; + + // get the drive's model/firmware strings + formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); + formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); + + // Look up the drive in knowndrives[]. + if ((i = lookupdrive(model, firmware)) >= 0) { + + // if vendoropts is non-NULL then Attribute interpretation presets + if (knowndrives[i].vendoropts) { + const unsigned char (* presets)[2]; + + // For each attribute in list of attribute/val pairs... + presets = knowndrives[i].vendoropts; + while (1) { + const int attr = (*presets)[0]; + const int val = (*presets)[1]; + + if (!attr) + break; + + // ... set attribute if user hasn't already done so. + if (!opts[attr]) + opts[attr] = val; + presets++; + } + } + + // If a special-purpose function is defined for this drive then + // call it. Note that if command line arguments or Directives + // over-ride this choice, then the specialpurpose function that is + // called must deal with this. + if (knowndrives[i].specialpurpose) + (*knowndrives[i].specialpurpose)(con); + } + + // return <0 if drive wasn't recognized, or index>=0 into database + // if it was + return i; +} diff --git a/sm5/os_darwin.c b/sm5/os_darwin.c new file mode 100644 index 0000000000000000000000000000000000000000..81353e3ab5201b80ab2999ffe107ce6dd72671fc --- /dev/null +++ b/sm5/os_darwin.c @@ -0,0 +1,403 @@ +/* + * os_darwin.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 Geoffrey Keating <geoffk@geoffk.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. + */ + +#include <stdbool.h> +#include <errno.h> +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <mach/mach_init.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/IOBSD.h> +#include <IOKit/storage/ata/IOATAStorageDefines.h> +#include <IOKit/storage/ata/ATASMARTLib.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> +#include <IOKit/storage/IOMedia.h> +#include <CoreFoundation/CoreFoundation.h> + + // No, I don't know why there isn't a header for this. +#define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice" + +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +#include "os_darwin.h" + +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_XXXX_c_cvsid="$Id: os_darwin.c,v 1.8 2004/08/18 19:27:44 likewise Exp $" \ +ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + + +// Print examples for smartctl. +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); + printf( + " smartctl -a disk0 (Prints all SMART information)\n\n" + " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n" +#ifdef HAVE_GETOPT_LONG + " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n" + " (Prints Self-Test & Attribute errors)\n\n" +#else + " smartctl -s on -S on /dev/rdisk0 (Enables SMART on first disk)\n\n" + " smartctl -A -l selftest -q errorsonly /dev/disk0\n" + " (Prints Self-Test & Attribute errors)\n\n" +#endif + " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n" + " (You can use IOService: ...)\n\n" + " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n" + " (... Or IODeviceTree:)\n" + ); + return; +} + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char* dev_name) { + // Only ATA is supported right now, so that's what it'd better be. + dev_name = dev_name; // suppress unused warning. + return CONTROLLER_ATA; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist); the +// other N arrays each contain null-terminated character strings. In +// the case N==0, no arrays are allocated because the array of 0 +// pointers has zero length, equivalent to calling malloc(0). +int make_device_names (char*** devlist, const char* name) { + IOReturn err; + io_iterator_t i; + io_object_t device; + int result; + int index; + const char * cls; + + if (strcmp (name, "ATA") == 0) + cls = kIOATABlockStorageDeviceClass; + else // only ATA supported right now. + return 0; + + err = IOServiceGetMatchingServices (kIOMasterPortDefault, + IOServiceMatching (cls), + &i); + if (err != kIOReturnSuccess) + return -1; + + // Count the devices. + for (result = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; result++) + IOObjectRelease (device); + + // Create an array of service names. + IOIteratorReset (i); + *devlist = Calloc (result, sizeof (char *)); + if (! *devlist) + goto error; + for (index = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; index++) + { + io_string_t devName; + IORegistryEntryGetPath(device, kIOServicePlane, devName); + IOObjectRelease (device); + + (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__); + if (! (*devlist)[index]) + goto error; + } + IOObjectRelease (i); + + return result; + + error: + IOObjectRelease (i); + if (*devlist) + { + for (index = 0; index < result; index++) + if ((*devlist)[index]) + FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__); + FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__); + } + return -1; +} + +// Information that we keep about each device. + +static struct { + io_object_t ioob; + bool hassmart; + IOCFPlugInInterface **plugin; + IOATASMARTInterface **smartIf; +} devices[20]; + +// Like open(). Return non-negative integer handle, only used by the +// functions below. type=="ATA" or "SCSI". The return value is +// an index into the devices[] array. If the device can't be opened, +// sets errno and returns -1. +// Acceptable device names are: +// /dev/disk* +// /dev/rdisk* +// disk* +// IOService:* +// IODeviceTree:* +int deviceopen(const char *pathname, char *type){ + size_t devnum; + const char *devname; + io_object_t disk; + + if (strcmp (type, "ATA") != 0) + { + errno = EINVAL; + return -1; + } + + // Find a free device number. + for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++) + if (! devices[devnum].ioob) + break; + if (devnum == sizeof (devices) / sizeof (devices[0])) + { + errno = EMFILE; + return -1; + } + + devname = NULL; + if (strncmp (pathname, "/dev/rdisk", 10) == 0) + devname = pathname + 6; + else if (strncmp (pathname, "/dev/disk", 9) == 0) + devname = pathname + 5; + else if (strncmp (pathname, "disk", 4) == 0) + // allow user to just say 'disk0' + devname = pathname; + + // Find the device. + if (devname) + { + CFMutableDictionaryRef matcher; + matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); + disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); + } + else + { + disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname); + } + + if (! disk) + { + errno = ENOENT; + return -1; + } + + // Find the ATA block storage driver that is the parent of this device + while (! IOObjectConformsTo (disk, kIOATABlockStorageDeviceClass)) + { + IOReturn err; + io_object_t notdisk = disk; + + err = IORegistryEntryGetParentEntry (notdisk, kIOServicePlane, &disk); + if (err != kIOReturnSuccess || ! disk) + { + errno = ENODEV; + IOObjectRelease (notdisk); + return -1; + } + } + + devices[devnum].ioob = disk; + + { + CFDictionaryRef diskChars = NULL; + CFNumberRef diskFeatures = NULL; + UInt32 ataFeatures; + + // Determine whether the drive actually supports SMART. + if ((diskChars = IORegistryEntryCreateCFProperty (disk, + CFSTR (kIOPropertyDeviceCharacteristicsKey), + kCFAllocatorDefault, + kNilOptions)) != NULL + && CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"), + (const void **)&diskFeatures) + && CFNumberGetValue (diskFeatures, kCFNumberLongType, &ataFeatures) + && (ataFeatures & kIOATAFeatureSMART)) + devices[devnum].hassmart = true; + else + devices[devnum].hassmart = false; + if (diskChars) + CFRelease (diskChars); + } + + { + SInt32 dummy; + + devices[devnum].plugin = NULL; + devices[devnum].smartIf = NULL; + + // Create an interface to the ATA SMART library. + if (devices[devnum].hassmart + && IOCreatePlugInInterfaceForService (disk, + kIOATASMARTUserClientTypeID, + kIOCFPlugInInterfaceID, + &devices[devnum].plugin, + &dummy) == kIOReturnSuccess) + (*devices[devnum].plugin)->QueryInterface + (devices[devnum].plugin, + CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID), + (LPVOID) &devices[devnum].smartIf); + } + + return devnum; +} + +// Like close(). Acts only on integer handles returned by +// deviceopen() above. +int deviceclose(int fd){ + if (devices[fd].smartIf) + (*devices[fd].smartIf)->Release (devices[fd].smartIf); + if (devices[fd].plugin) + IODestroyPlugInInterface (devices[fd].plugin); + IOObjectRelease (devices[fd].ioob); + devices[fd].ioob = MACH_PORT_NULL; + return 0; +} + +// Interface to ATA devices. See os_linux.c for the cannonical example. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the integer handle provided by deviceopen() +// command: defines the different operations, see atacmds.h +// select: additional input data IF NEEDED (which log, which type of +// self-test). +// data: location to write output data, IF NEEDED (1 or 512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES (for all commands BUT command==STATUS_CHECK) +// -1 if the command failed +// 0 if the command succeeded, +// RETURN VALUES if command==STATUS_CHECK +// -1 if the command failed OR the disk SMART status can't be determined +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + +// Things that aren't available in the Darwin interfaces: +// - Tests other than short and extended (in particular, can't run +// an immediate offline test) +// - Captive-mode tests, aborting tests +// - ability to switch automatic offline testing on or off + +// Note that some versions of Darwin, at least 7H63 and earlier, +// have a buggy library that treats the boolean value in +// SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and +// SMARTExecuteOffLineImmediate as always being true. +int marvell_command_interface(int fd, smart_command_set command, + int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, + int select, char *data) +{ + IOATASMARTInterface **ifp = devices[fd].smartIf; + IOATASMARTInterface *smartIf; + IOReturn err; + + if (! ifp) + return -1; + smartIf = *ifp; + + switch (command) + { + case STATUS: + return 0; + case STATUS_CHECK: + { + Boolean is_failing; + err = smartIf->SMARTReturnStatus (ifp, &is_failing); + if (err == kIOReturnSuccess && is_failing) + return 1; + break; + } + case ENABLE: + case DISABLE: + err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE); + break; + case AUTOSAVE: + err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0); + break; + case IMMEDIATE_OFFLINE: + if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST) + { + errno = EINVAL; + return -1; + } + err = smartIf->SMARTExecuteOffLineImmediate (ifp, + select == EXTEND_SELF_TEST); + break; + case READ_VALUES: + err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data); + break; + case READ_THRESHOLDS: + err = smartIf->SMARTReadDataThresholds (ifp, + (ATASMARTDataThresholds *)data); + break; + case READ_LOG: + err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512); + break; + case WRITE_LOG: + err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512); + break; + case IDENTIFY: + { + UInt32 dummy; + err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy); + if (err == kIOReturnSuccess && isbigendian()) + { + int i; + /* The system has already byte-swapped, undo it. */ + for (i = 0; i < 256; i+=2) + swap2 (data + i); + } + } + break; + case CHECK_POWER_MODE: + // The information is right there in the device registry, but how + // to get to it portably? + default: + errno = ENOTSUP; + return -1; + } + if (err == kIOReturnExclusiveAccess) + errno = EBUSY; + return err == kIOReturnSuccess ? 0 : -1; +} + +// There's no special handling needed for hidden devices, the kernel +// must deal with them. +int escalade_command_interface(int fd, int escalade_port, int escalade_type, + smart_command_set command, int select, + char *data) +{ + fd = fd; + escalade_port = escalade_port; + escalade_type = escalade_type; + command = command; + select = select; + data = data; + return -1; +} + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + return -ENOSYS; +} diff --git a/sm5/os_freebsd.c b/sm5/os_freebsd.c new file mode 100644 index 0000000000000000000000000000000000000000..1a7e9edf9feb879f626e653db8e8550105e70e4b --- /dev/null +++ b/sm5/os_freebsd.c @@ -0,0 +1,946 @@ +/* + * os_freebsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-4 Eduard Martinescu <smartmontools-support@lists.sourceforge.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. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <err.h> +#include <camlib.h> +#include <cam/scsi/scsi_message.h> +#include <sys/ata.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <glob.h> +#include <fcntl.h> + + +#include "config.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_freebsd.h" + +static const char *filenameandversion="$Id: os_freebsd.c,v 1.43 2004/09/04 22:11:55 arvoreen Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_freebsd.c,v 1.43 2004/09/04 22:11:55 arvoreen Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Private table of open devices: guaranteed zero on startup since +// part of static data. +struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; + +// forward declaration +static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch); + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n" + " (Prints Self-Test & Attribute errors)\n" +// " smartctl -a --device=3ware,2 /dev/sda\n" +// " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/ad0 (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/ad0 (Enables SMART on first disk)\n" + " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/ad0\n" + " (Prints Self-Test & Attribute errors)\n" +// " smartctl -a -d 3ware,2 /dev/sda\n" +// " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#endif + return; +} + +// Like open(). Return positive integer handle, used by functions below only. mode=="ATA" or "SCSI". +int deviceopen (const char* dev, char* mode) { + struct freebsd_dev_channel *fdchan; + int parse_ok, i; + + // Search table for a free entry + for (i=0; i<FREEBSD_MAXDEV; i++) + if (!devicetable[i]) + break; + + // If no free entry found, return error. We have max allowed number + // of "file descriptors" already allocated. + if (i==FREEBSD_MAXDEV) { + errno=EMFILE; + return -1; + } + + fdchan = calloc(1,sizeof(struct freebsd_dev_channel)); + if (fdchan == NULL) { + // errno already set by call to malloc() + return -1; + } + + parse_ok = parse_ata_chan_dev (dev,fdchan); + if (parse_ok == CONTROLLER_UNKNOWN) { + free(fdchan); + errno = ENOTTY; + return -1; // can't handle what we don't know + } + + if (parse_ok == CONTROLLER_ATA) { + if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) { + int myerror = errno; //preserve across free call + free (fdchan); + errno = myerror; + return -1; + } + } + + if (parse_ok == CONTROLLER_3WARE_678K_CHAR) { + char buf[512]; + sprintf(buf,"/dev/twe%d",fdchan->device); + if ((fdchan->atacommand = open(buf,O_RDWR))<0) { + int myerror = errno; // preserver across free call + free(fdchan); + errno=myerror; + return -1; + } + } + + if (parse_ok == CONTROLLER_SCSI) { + // this is really a NO-OP, as the parse takes care + // of filling in correct details + } + + // return pointer to "file descriptor" table entry, properly offset. + devicetable[i]=fdchan; + return i+FREEBSD_FDOFFSET; +} + +// Returns 1 if device not available/open/found else 0. Also shifts fd into valid range. +static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) { + // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1 + *fd -= FREEBSD_FDOFFSET; + + // check for validity of "file descriptor". + if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) { + errno = ENODEV; + return 1; + } + + return 0; +} + +// Like close(). Acts on handles returned by above function. +int deviceclose (int fd) { + struct freebsd_dev_channel *fdchan; + int failed = 0; + + // check for valid file descriptor + if (isnotopen(&fd, &fdchan)) + return -1; + + + // did we allocate a SCSI device name? + if (fdchan->devname) + free(fdchan->devname); + + // close device, if open + if (fdchan->atacommand) + failed=close(fdchan->atacommand); + + if (fdchan->scsicontrol) + failed=close(fdchan->scsicontrol); + + // if close succeeded, then remove from device list + // Eduard, should we also remove it from list if close() fails? I'm + // not sure. Here I only remove it from list if close() worked. + if (!failed) { + free(fdchan); + devicetable[fd]=NULL; + } + + return failed; +} + +#define NO_RETURN 0 +#define BAD_SMART 1 +#define NO_DISK_3WARE 2 +#define BAD_KERNEL 3 +#define MAX_MSG 3 + +// Utility function for printing warnings +void printwarning(int msgNo, const char* extra) { + static int printed[] = {0,0,0,0}; + static const char* message[]={ + "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n", + + "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + + "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n", + + "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s",extra); + } + } + return; +} + + +// Interface to ATA devices. See os_linux.c +int marvell_command_interface(int fd, smart_command_set command, int select, char *data) { + return -1; +} + +int ata_command_interface(int fd, smart_command_set command, int select, char *data) { +#ifndef ATAREQUEST + // sorry, but without ATAng, we can't do anything here + printwarning(BAD_KERNEL,NULL); + errno = ENOSYS; + return -1; +#else + struct freebsd_dev_channel* con; + int retval, copydata=0; + struct ata_cmd iocmd; + unsigned char buff[512]; + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -1; + + bzero(buff,512); + + bzero(&iocmd,sizeof(struct ata_cmd)); + bzero(buff,512); + iocmd.cmd=ATAREQUEST; + iocmd.channel=con->channel; + iocmd.device=con->device; + + iocmd.u.request.u.ata.command=ATA_SMART_CMD; + iocmd.u.request.timeout=600; + switch (command){ + case READ_VALUES: + iocmd.u.request.u.ata.feature=ATA_SMART_READ_VALUES; + iocmd.u.request.u.ata.lba=0xc24f<<8; + iocmd.u.request.flags=ATA_CMD_READ; + iocmd.u.request.data=buff; + iocmd.u.request.count=512; + copydata=1; + break; + case READ_THRESHOLDS: + iocmd.u.request.u.ata.feature=ATA_SMART_READ_THRESHOLDS; + iocmd.u.request.u.ata.count=1; + iocmd.u.request.u.ata.lba=1|(0xc24f<<8); + iocmd.u.request.flags=ATA_CMD_READ; + iocmd.u.request.data=buff; + iocmd.u.request.count=512; + copydata=1; + break; + case READ_LOG: + iocmd.u.request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR; + iocmd.u.request.u.ata.lba=select|(0xc24f<<8); + iocmd.u.request.u.ata.count=1; + iocmd.u.request.flags=ATA_CMD_READ; + iocmd.u.request.data=buff; + iocmd.u.request.count=512; + copydata=1; + break; + case IDENTIFY: + iocmd.u.request.u.ata.command=ATA_IDENTIFY_DEVICE; + iocmd.u.request.flags=ATA_CMD_READ; + iocmd.u.request.data=buff; + iocmd.u.request.count=512; + copydata=1; + break; + case PIDENTIFY: + iocmd.u.request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE; + iocmd.u.request.flags=ATA_CMD_READ; + iocmd.u.request.data=buff; + iocmd.u.request.count=512; + copydata=1; + break; + case ENABLE: + iocmd.u.request.u.ata.feature=ATA_SMART_ENABLE; + iocmd.u.request.u.ata.lba=0xc24f<<8; + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + case DISABLE: + iocmd.u.request.u.ata.feature=ATA_SMART_DISABLE; + iocmd.u.request.u.ata.lba=0xc24f<<8; + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + case AUTO_OFFLINE: + // NOTE: According to ATAPI 4 and UP, this command is obsolete + iocmd.u.request.u.ata.feature=ATA_SMART_AUTO_OFFLINE; + iocmd.u.request.u.ata.lba=select|(0xc24f<<8); + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + case AUTOSAVE: + iocmd.u.request.u.ata.feature=ATA_SMART_AUTOSAVE; + iocmd.u.request.u.ata.count=0xf1; // to enable autosave + iocmd.u.request.u.ata.lba=0xc24f<<8; + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + case IMMEDIATE_OFFLINE: + iocmd.u.request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE; + iocmd.u.request.u.ata.lba = select|(0xc24f<<8); // put test in sector + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + case STATUS_CHECK: // same command, no HDIO in FreeBSD + case STATUS: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + iocmd.u.request.u.ata.feature=ATA_SMART_STATUS; + iocmd.u.request.u.ata.lba=0xc24f<<8; + iocmd.u.request.flags=ATA_CMD_CONTROL; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno=ENOSYS; + return -1; + } + + if (command==STATUS_CHECK){ + unsigned const char normal_lo=0x4f, normal_hi=0xc2; + unsigned const char failed_lo=0xf4, failed_hi=0x2c; + unsigned char low,high; + + if ((retval=ioctl(con->atacommand, IOCATA, &iocmd))) + return -1; + +#if __FreeBSD_version < 502000 + printwarning(NO_RETURN,NULL); +#endif + + high = (iocmd.u.request.u.ata.lba >> 16) & 0xff; + low = (iocmd.u.request.u.ata.lba >> 8) & 0xff; + + // Cyl low and Cyl high unchanged means "Good SMART status" + if (low==normal_lo && high==normal_hi) + return 0; + + // These values mean "Bad SMART status" + if (low==failed_lo && high==failed_hi) + return 1; + + // We haven't gotten output that makes sense; print out some debugging info + char buf[512]; + sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int)iocmd.u.request.u.ata.command, + (int)iocmd.u.request.u.ata.feature, + (int)iocmd.u.request.u.ata.count, + (int)((iocmd.u.request.u.ata.lba) & 0xff), + (int)((iocmd.u.request.u.ata.lba>>8) & 0xff), + (int)((iocmd.u.request.u.ata.lba>>16) & 0xff), + (int)iocmd.u.request.error); + printwarning(BAD_SMART,buf); + return 0; + } + + if ((retval=ioctl(con->atacommand, IOCATA, &iocmd))) { + perror("Failed command: "); + return -1; + } + // + if (copydata) + memcpy(data, buff, 512); + + return 0; +#endif +} + + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct freebsd_dev_channel* con = NULL; + struct cam_device* cam_dev = NULL; + union ccb *ccb; + + + if (report > 0) { + unsigned int k; + const unsigned char * ucp = iop->cmnd; + const char * np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + pout("]"); + } + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -ENOTTY; + + + if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) { + warnx("%s",cam_errbuf); + return -1; + } + + if (!(ccb = cam_getccb(cam_dev))) { + warnx("error allocating ccb"); + return -ENOMEM; + } + + // clear out structure, except for header that was filled in for us + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + cam_fill_csio(&ccb->csio, + /*retrires*/ 1, + /*cbfcnp*/ NULL, + /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), + /* tagaction */ MSG_SIMPLE_Q_TAG, + /* dataptr */ iop->dxferp, + /* datalen */ iop->dxfer_len, + /* senselen */ iop->max_sense_len, + /* cdblen */ iop->cmnd_len, + /* timout (converted to seconds) */ iop->timeout*1000); + memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); + + if (cam_send_ccb(cam_dev,ccb) < 0) { + warn("error sending SCSI ccb"); + #if __FreeBSD_version > 500000 + cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + #endif + cam_freeccb(ccb); + return -1; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + #if __FreeBSD_version > 500000 + cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + #endif + cam_freeccb(ccb); + return -1; + } + + if (iop->sensep) { + memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); + iop->resp_sense_len = sizeof(struct scsi_sense_data); + } + + iop->scsi_status = ccb->csio.scsi_status; + + cam_freeccb(ccb); + + if (cam_dev) + cam_close_device(cam_dev); + + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + return 0; +} + +// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c + +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) { + // to hold true file descriptor + struct freebsd_dev_channel* con; + + // return value and buffer for ioctl() + int ioctlreturn, readdata=0; + struct twe_usercommand* cmd = NULL; + + // Used by both the SCSI and char interfaces + char ioctl_buffer[sizeof(struct twe_usercommand)]; + + if (disknum < 0) { + printwarning(NO_DISK_3WARE,NULL); + return -1; + } + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -1; + + memset(ioctl_buffer, 0, sizeof(struct twe_usercommand)); + + cmd = (struct twe_usercommand*)ioctl_buffer; + cmd->tu_command.ata.opcode = TWE_OP_ATA_PASSTHROUGH; + + // Same for (almost) all commands - but some reset below + cmd->tu_command.ata.request_id = 0xFF; + cmd->tu_command.ata.unit = disknum; + cmd->tu_command.ata.host_id = 0; + cmd->tu_command.ata.status = 0; + cmd->tu_command.ata.flags = 0x1; + cmd->tu_command.ata.drive_head = 0x0; + cmd->tu_command.ata.sector_num = 0; + + // All SMART commands use this CL/CH signature. These are magic + // values from the ATA specifications. + cmd->tu_command.ata.cylinder_lo = 0x4F; + cmd->tu_command.ata.cylinder_hi = 0xC2; + + // SMART ATA COMMAND REGISTER value + cmd->tu_command.ata.command = ATA_SMART_CMD; + + // Is this a command that reads or returns 512 bytes? + // passthru->param values are: + // 0x0 - non data command without TFR write check, + // 0x8 - non data command with TFR write check, + // 0xD - data command that returns data to host from device + // 0xF - data command that writes data from host to device + // passthru->size values are 0x5 for non-data and 0x07 for data + if (command == READ_VALUES || + command == READ_THRESHOLDS || + command == READ_LOG || + command == IDENTIFY || + command == WRITE_LOG ) { + readdata=1; + cmd->tu_size = 512; + cmd->tu_data = data; + cmd->tu_command.ata.sgl_offset = 0x5; + cmd->tu_command.ata.size = 0x5; + cmd->tu_command.ata.param = 0xD; + cmd->tu_command.ata.sector_count = 0x1; + // For 64-bit to work correctly, up the size of the command packet + // in dwords by 1 to account for the 64-bit single sgl 'address' + // field. Note that this doesn't agree with the typedefs but it's + // right (agree with kernel driver behavior/typedefs). + //if (sizeof(long)==8) + // cmd->tu_command.ata.size++; + } + else { + // Non data command -- but doesn't use large sector + // count register values. + cmd->tu_command.ata.sgl_offset = 0x0; + cmd->tu_command.ata.size = 0x5; + cmd->tu_command.ata.param = 0x8; + cmd->tu_command.ata.sector_count = 0x0; + } + + // Now set ATA registers depending upon command + switch (command){ + case CHECK_POWER_MODE: + cmd->tu_command.ata.command = ATA_CHECK_POWER_MODE; + cmd->tu_command.ata.features = 0; + cmd->tu_command.ata.cylinder_lo = 0; + cmd->tu_command.ata.cylinder_hi = 0; + break; + case READ_VALUES: + cmd->tu_command.ata.features = ATA_SMART_READ_VALUES; + break; + case READ_THRESHOLDS: + cmd->tu_command.ata.features = ATA_SMART_READ_THRESHOLDS; + break; + case READ_LOG: + cmd->tu_command.ata.features = ATA_SMART_READ_LOG_SECTOR; + // log number to return + cmd->tu_command.ata.sector_num = select; + break; + case WRITE_LOG: + cmd->tu_data = data; + readdata=0; + cmd->tu_command.ata.features = ATA_SMART_WRITE_LOG_SECTOR; + cmd->tu_command.ata.sector_count = 1; + cmd->tu_command.ata.sector_num = select; + cmd->tu_command.ata.param = 0xF; // PIO data write + break; + case IDENTIFY: + // ATA IDENTIFY DEVICE + cmd->tu_command.ata.command = ATA_IDENTIFY_DEVICE; + cmd->tu_command.ata.features = 0; + cmd->tu_command.ata.cylinder_lo = 0; + cmd->tu_command.ata.cylinder_hi = 0; + break; + case PIDENTIFY: + // 3WARE controller can NOT have packet device internally + pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum); + errno=ENODEV; + return -1; + case ENABLE: + cmd->tu_command.ata.features = ATA_SMART_ENABLE; + break; + case DISABLE: + cmd->tu_command.ata.features = ATA_SMART_DISABLE; + break; + case AUTO_OFFLINE: + cmd->tu_command.ata.features = ATA_SMART_AUTO_OFFLINE; + // Enable or disable? + cmd->tu_command.ata.sector_count = select; + break; + case AUTOSAVE: + cmd->tu_command.ata.features = ATA_SMART_AUTOSAVE; + // Enable or disable? + cmd->tu_command.ata.sector_count = select; + break; + case IMMEDIATE_OFFLINE: + cmd->tu_command.ata.features = ATA_SMART_IMMEDIATE_OFFLINE; + // What test type to run? + cmd->tu_command.ata.sector_num = select; + break; + case STATUS_CHECK: + cmd->tu_command.ata.features = ATA_SMART_STATUS; + break; + case STATUS: + // This is JUST to see if SMART is enabled, by giving SMART status + // command. But it doesn't say if status was good, or failing. + // See below for the difference. + cmd->tu_command.ata.features = ATA_SMART_STATUS; + break; + default: + pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", command, disknum); + errno=ENOSYS; + return -1; + } + + // Now send the command down through an ioctl() + ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd); + + // Deal with the different error cases + if (ioctlreturn) { + if (!errno) + errno=EIO; + return -1; + } + + // See if the ATA command failed. Now that we have returned from + // the ioctl() call, if passthru is valid, then: + // - cmd->tu_command.ata.status contains the 3ware controller STATUS + // - cmd->tu_command.ata.command contains the ATA STATUS register + // - cmd->tu_command.ata.features contains the ATA ERROR register + // + // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS + // If bit 0 (error bit) is set, then ATA ERROR register is valid. + // While we *might* decode the ATA ERROR register, at the moment it + // doesn't make much sense: we don't care in detail why the error + // happened. + + if (cmd->tu_command.ata.status || (cmd->tu_command.ata.command & 0x21)) { + pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",cmd->tu_command.ata.status,cmd->tu_command.ata.command,cmd->tu_command.ata.flags); + errno=EIO; + return -1; + } + + // For STATUS_CHECK, we need to check register values + if (command==STATUS_CHECK) { + + // To find out if the SMART RETURN STATUS is good or failing, we + // need to examine the values of the Cylinder Low and Cylinder + // High Registers. + + unsigned short cyl_lo=cmd->tu_command.ata.cylinder_lo; + unsigned short cyl_hi=cmd->tu_command.ata.cylinder_hi; + + // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. + if (cyl_lo==0x4F && cyl_hi==0xC2) + return 0; + + // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL + if (cyl_lo==0xF4 && cyl_hi==0x2C) + return 1; + + errno=EIO; + return -1; + } + + // copy sector count register (one byte!) to return data + if (command==CHECK_POWER_MODE) + *data=*(char *)&(cmd->tu_command.ata.sector_count); + + // look for nonexistent devices/ports + if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { + errno=ENODEV; + return -1; + } + + return 0; +} + +static int get_twe_channel_unit (const char* name, int* unit, int* dev) { + // at some point, we need to figure out which TWE controller any + // given disk belongs to..... + // at this point, I have no clue how to do this...so for now, it is + // always going to be controller 0 + *dev=0; + *unit=0; // not really needed for TWE drives, as we handle that seperately + return 0; +} + +static int get_ata_channel_unit ( const char* name, int* unit, int* dev) { +#ifndef ATAREQUEST + *dev=0; + *unit=0; +return 0; +#else + // there is no direct correlation between name 'ad0, ad1, ...' and + // channel/unit number. So we need to iterate through the possible + // channels and check each unit to see if we match names + struct ata_cmd iocmd; + int fd,maxunit; + + bzero(&iocmd, sizeof(struct ata_cmd)); + + if ((fd = open("/dev/ata", O_RDWR)) < 0) + return -errno; + + iocmd.cmd = ATAGMAXCHANNEL; + if (ioctl(fd, IOCATA, &iocmd) < 0) { + return -errno; + close(fd); + } + maxunit = iocmd.u.maxchan; + for (*unit = 0; *unit < maxunit; (*unit)++) { + iocmd.channel = *unit; + iocmd.device = -1; + iocmd.cmd = ATAGPARM; + if (ioctl(fd, IOCATA, &iocmd) < 0) { + close(fd); + return -errno; + } + if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) { + *dev = 0; + break; + } + if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) { + *dev = 1; + break; + } + } + close(fd); + if (*unit == maxunit) + return -1; + else + return 0; +#endif +} + + +// Guess device type (ata or scsi) based on device name (FreeBSD +// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst, +// osst, nosst and sg. +static const char * fbsd_dev_prefix = "/dev/"; +static const char * fbsd_dev_ata_disk_prefix = "ad"; +static const char * fbsd_dev_scsi_disk_plus = "da"; +static const char * fbsd_dev_scsi_tape1 = "sa"; +static const char * fbsd_dev_scsi_tape2 = "nsa"; +static const char * fbsd_dev_scsi_tape3 = "esa"; +static const char * fbsd_dev_twe_disk = "twed"; + +static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) { + int len; + int dev_prefix_len = strlen(fbsd_dev_prefix); + + // if dev_name null, or string length zero + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + // Remove the leading /dev/... if it's there + if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + // if nothing else in the string, unrecognized + return CONTROLLER_UNKNOWN; + // else advance pointer to following characters + dev_name += dev_prefix_len; + } + // form /dev/ad* or ad* + if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name, + strlen(fbsd_dev_ata_disk_prefix))) { + if (chan != NULL) { + if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { + return CONTROLLER_UNKNOWN; + } + } + return CONTROLLER_ATA; + } + + // form /dev/da* or da* + if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name, + strlen(fbsd_dev_scsi_disk_plus))) + goto handlescsi; + + // form /dev/sa* or sa* + if (!strncmp(fbsd_dev_scsi_tape1, dev_name, + strlen(fbsd_dev_scsi_tape1))) + goto handlescsi; + + // form /dev/nsa* or nsa* + if (!strncmp(fbsd_dev_scsi_tape2, dev_name, + strlen(fbsd_dev_scsi_tape2))) + goto handlescsi; + + // form /dev/esa* or esa* + if (!strncmp(fbsd_dev_scsi_tape3, dev_name, + strlen(fbsd_dev_scsi_tape3))) + goto handlescsi; + + if (!strncmp(fbsd_dev_twe_disk,dev_name, + strlen(fbsd_dev_twe_disk))) { + if (chan != NULL) { + if (get_twe_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { + return CONTROLLER_UNKNOWN; + } + } + return CONTROLLER_3WARE_678K_CHAR; + } + + // we failed to recognize any of the forms + return CONTROLLER_UNKNOWN; + + handlescsi: + if (chan != NULL) { + if (!(chan->devname = calloc(1,DEV_IDLEN+1))) + return CONTROLLER_UNKNOWN; + + if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1) + return CONTROLLER_UNKNOWN; + } + return CONTROLLER_SCSI; + +} + +int guess_device_type (const char* dev_name) { + return parse_ata_chan_dev(dev_name,NULL); +} + +// global variable holding byte count of allocated memory +extern long long bytes; + +// we are going to take advantage of the fact that FreeBSD's devfs will only +// have device entries for devices that exist. So if we get the equivilent of +// ls /dev/ad?, we have all the ATA devices on the system +// +// If any errors occur, leave errno set as it was returned by the +// system call, and return <0. + +// Return values: +// -1 out of memory +// -2 to -5 errors in glob + +int get_dev_names(char*** names, const char* prefix) { + int n = 0; + char** mp; + int retglob,lim; + glob_t globbuf; + int i; + char pattern1[128],pattern2[128]; + + bzero(&globbuf,sizeof(globbuf)); + // in case of non-clean exit + *names=NULL; + + // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV + sprintf(pattern1,"/dev/%s[0-9]",prefix); + sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix); + + // Use glob to look for any directory entries matching the patterns + // first call inits with first pattern match, second call appends + // to first list. Turn on NOCHECK for second call. This results in no + // error if no more matches found, however it does append the actual + // pattern to the list of paths.... + if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) || + (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) { + int retval = -1; + // glob failed + if (retglob==GLOB_NOSPACE) + pout("glob(3) ran out of memory matching patterns (%s),(%s)\n", + pattern1, pattern2); + else if (retglob==GLOB_ABORTED) + pout("glob(3) aborted matching patterns (%s),(%s)\n", + pattern1, pattern2); + else if (retglob==GLOB_NOMATCH) { + pout("glob(3) found no matches for patterns (%s),(%s)\n", + pattern1, pattern2); + retval = 0; + } + else if (retglob) + pout("Unexplained error in glob(3) of patterns (%s),(%s)\n", + pattern1, pattern2); + + // Free memory and return + globfree(&globbuf); + + return retval; + } + + // did we find too many paths? + // did we find too many paths? + lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV; + if (lim < globbuf.gl_pathc) + pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", + globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2, + globbuf.gl_pathc-MAX_NUM_DEV); + + // allocate space for up to lim number of ATA devices + if (!(mp = (char **)calloc(lim, sizeof(char*)))){ + pout("Out of memory constructing scan device list\n"); + return -1; + } + + // now step through the list returned by glob. No link checking needed + // in FreeBSD + for (i=0; i<globbuf.gl_pathc; i++){ + // becuase of the NO_CHECK on second call to glob, + // the pattern itself will be added to path list.. + // so ignore any paths that have the ']' from pattern + if (strchr(globbuf.gl_pathv[i],']') == NULL) + mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); + } + + globfree(&globbuf); + mp = realloc(mp,n*(sizeof(char*))); // shrink to correct size + bytes += (n)*(sizeof(char*)); // and set allocated byte count + *names=mp; + return n; +} + +int make_device_names (char*** devlist, const char* name) { + if (!strcmp(name,"SCSI")) + return get_dev_names(devlist,"da"); + else if (!strcmp(name,"ATA")) + return get_dev_names(devlist,"ad"); + else + return 0; +} diff --git a/sm5/os_generic.c b/sm5/os_generic.c new file mode 100644 index 0000000000000000000000000000000000000000..a62c9ab4ea6e656046f1d402eed9e7f69ff3e2ad --- /dev/null +++ b/sm5/os_generic.c @@ -0,0 +1,211 @@ +/* + * os_generic.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2003-4 Bruce Allen <smartmontools-support@lists.sourceforge.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. + */ + +/* PORTING NOTES AND COMMENTS + + To port smartmontools to the OS of your choice, please: + + [0] Contact smartmontools-support@lists.sourceforge.net to check + that it's not already been done. + + [1] Make copies of os_generic.[hc] called os_myOS.[hc]. + + [2] Modify configure.in so that case "${host}" includes myOS. + + [3] Verify that ./autogen.sh && ./configure && make compiles the + code. If not, fix any compilation problems. If your OS lacks + some function that is used elsewhere in the code, then add a + AC_CHECK_FUNCS([missingfunction]) line to configure.in, and + surround uses of the function with: + #ifdef HAVE_MISSINGFUNCTION + ... + #endif + where the macro HAVE_MISSINGFUNCTION is (or is not) defined in + config.h. + + [4] Provide the functions defined in this file by fleshing out the + skeletons below. You can entirely eliminate the function + 'unsupported()'. + + [5] Contact smartmontools-support@lists.sourceforge.net to see + about checking your code into the smartmontools CVS archive. +*/ + +// These are needed to define prototypes for the functions defined below +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +// This is to include whatever prototypes you define in os_generic.h +#include "os_generic.h" + +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_XXXX_c_cvsid="$Id: os_generic.c,v 1.17 2004/08/18 21:27:18 ballen4705 Exp $" \ +ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + + +// Please eliminate the following block: both the two #includes and +// the 'unsupported()' function. They are only here to warn +// unsuspecting users that their Operating System is not supported! If +// you wish, you can use a similar warning mechanism for any of the +// functions in this file that you can not (or choose not to) +// implement. + +#include "config.h" +#ifdef HAVE_UNAME +#include <sys/utsname.h> +#endif + +static void unsupported(){ + static int warninggiven; + + if (!warninggiven) { + char *osname; + extern unsigned char debugmode; + unsigned char savedebugmode=debugmode; + +#ifdef HAVE_UNAME + struct utsname ostype; + uname(&ostype); + osname=ostype.sysname; +#else + osname="host's"; +#endif + + debugmode=1; + pout("\n" + "############################################################################\n" + "WARNING: smartmontools has not been ported to the %s Operating System.\n" + "Please see the files os_generic.c and os_generic.h for porting instructions.\n" + "############################################################################\n\n", + osname); + debugmode=savedebugmode; + warninggiven=1; + } + + return; +} +// End of the 'unsupported()' block that you should eliminate. + + +// print examples for smartctl. You should modify this function so +// that the device paths are sensible for your OS, and to eliminate +// unsupported commands (eg, 3ware controllers). +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a --device=3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a -d 3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#endif + return; +} + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char* dev_name) { + unsupported(); + return CONTROLLER_UNKNOWN; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist); the +// other N arrays each contain null-terminated character strings. In +// the case N==0, no arrays are allocated because the array of 0 +// pointers has zero length, equivalent to calling malloc(0). +int make_device_names (char*** devlist, const char* name) { + unsupported(); + return 0; +} + +// Like open(). Return non-negative integer handle, only used by the +// functions below. type=="ATA" or "SCSI". If you need to store +// extra information about your devices, create a private internal +// array within this file (see os_freebsd.c for an example). If you +// can not open the device (permission denied, does not exist, etc) +// set errno as open() does and return <0. +int deviceopen(const char *pathname, char *type){ + unsupported(); + return -1; +} + +// Like close(). Acts only on integer handles returned by +// deviceopen() above. +int deviceclose(int fd){ + unsupported(); + return 0; +} + +// Interface to ATA devices. See os_linux.c for the cannonical example. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the integer handle provided by deviceopen() +// command: defines the different operations, see atacmds.h +// select: additional input data IF NEEDED (which log, which type of +// self-test). +// data: location to write output data, IF NEEDED (1 or 512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES (for all commands BUT command==STATUS_CHECK) +// -1 if the command failed +// 0 if the command succeeded, +// RETURN VALUES if command==STATUS_CHECK +// -1 if the command failed OR the disk SMART status can't be determined +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" +int ata_command_interface(int fd, smart_command_set command, int select, char *data){ + unsupported(); + return -1; +} + +int marvell_command_interface(int fd, smart_command_set command, int select, char *data){ + unsupported(); + return -1; +} + +// Interface to ATA devices behind 3ware escalade RAID controller +// cards. Same description as ata_command_interface() above except +// that 0 <= disknum <= 15 specifies the ATA disk attached to the +// controller. +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ + unsupported(); + return -1; +} + +#include <errno.h> +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + unsupported(); + return -ENOSYS; +} diff --git a/sm5/os_linux.c b/sm5/os_linux.c new file mode 100644 index 0000000000000000000000000000000000000000..ebe0e6eebc7d4feffd088850a679098fff40eb66 --- /dev/null +++ b/sm5/os_linux.c @@ -0,0 +1,1514 @@ +/* + * os_linux.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2003-4 Doug Gilbert <dougg@torque.net> + * + * Parts of this file are derived from code that was + * + * Written By: Adam Radford <linux@3ware.com> + * Modifications By: Joel Jacobson <linux@3ware.com> + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * Brad Strand <linux@3ware.com> + * + * Copyright (C) 1999-2003 3ware Inc. + * + * Kernel compatablity By: Andre Hedrick <andre@suse.com> + * Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> + * + * Other ars of this file are derived from code that was + * + * 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/ + * + */ + +// This file contains the linux-specific IOCTL parts of +// smartmontools. It includes one interface routine for ATA devices, +// one for SCSI devices, and one for ATA devices behind escalade +// controllers. + +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <scsi/scsi_ioctl.h> +#include <scsi/sg.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> +#ifndef makedev // old versions of types.h do not include sysmacros.h +#include <sys/sysmacros.h> +#endif + +#include "atacmds.h" +#include "os_linux.h" +#include "scsicmds.h" +#include "utility.h" + +#ifndef ENOTSUP +#define ENOTSUP ENOSYS +#endif +typedef unsigned long long u8; + +#define ARGUSED(x) ((void)(x)) + +static const char *filenameandversion="$Id: os_linux.c,v 1.72 2004/08/25 03:12:40 ballen4705 Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_linux.c,v 1.72 2004/08/25 03:12:40 ballen4705 Exp $" \ +ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// global variable holding byte count of allocated memory +extern long long bytes; + + + +/* This function will setup and fix device nodes for a 3ware controller. */ +#define MAJOR_STRING_LENGTH 3 +#define DEVICE_STRING_LENGTH 32 +#define NODE_STRING_LENGTH 16 +int setup_3ware_nodes(char *nodename, char *driver_name) { + int tw_major = 0; + int index = 0; + char majorstring[MAJOR_STRING_LENGTH+1]; + char device_name[DEVICE_STRING_LENGTH+1]; + char nodestring[NODE_STRING_LENGTH]; + struct stat stat_buf; + FILE *file; + + /* First try to open up /proc/devices */ + if (!(file = fopen("/proc/devices", "r"))) { + pout("Error opening /proc/devices to check/create 3ware device nodes\n"); + syserror("fopen"); + return 0; // don't fail here: user might not have /proc ! + } + + /* Attempt to get device major number */ + while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) { + majorstring[MAJOR_STRING_LENGTH]='\0'; + device_name[DEVICE_STRING_LENGTH]='\0'; + if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) { + tw_major = atoi(majorstring); + break; + } + } + fclose(file); + + /* See if we found a major device number */ + if (!tw_major) { + pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name); + return 2; + } + + /* Now check if nodes are correct */ + for (index=0; index<16; index++) { + sprintf(nodestring, "/dev/%s%d", nodename, index); + + /* Try to stat the node */ + if ((stat(nodestring, &stat_buf))) { + /* Create a new node if it doesn't exist */ + if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) { + pout("problem creating 3ware device nodes %s", nodestring); + syserror("mknod"); + return 3; + } + } + + /* See if nodes major and minor numbers are correct */ + if ((tw_major != (int)(major(stat_buf.st_rdev))) || + (index != (int)(minor(stat_buf.st_rdev))) || + (!S_ISCHR(stat_buf.st_mode))) { + + /* Delete the old node */ + if (unlink(nodestring)) { + pout("problem unlinking stale 3ware device node %s", nodestring); + syserror("unlink"); + return 4; + } + + /* Make a new node */ + if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) { + pout("problem creating 3ware device nodes %s", nodestring); + syserror("mknod"); + return 5; + } + } + } + return 0; +} + +// equivalent to open(path, flags) +int deviceopen(const char *pathname, char *type){ + if (!strcmp(type,"SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } + else if (!strcmp(type,"ATA")) + return open(pathname, O_RDONLY | O_NONBLOCK); + else if (!strcmp(type,"ATA_3WARE_9000")) { + // the device nodes for this controller are dynamically assigned, + // so we need to check that they exist with the correct major + // numbers and if not, create them + if (setup_3ware_nodes("twa", "3w-9xxx")) { + if (!errno) + errno=ENXIO; + return -1; + } + return open(pathname, O_RDONLY | O_NONBLOCK); + } + else if (!strcmp(type,"ATA_3WARE_678K")) { + // the device nodes for this controller are dynamically assigned, + // so we need to check that they exist with the correct major + // numbers and if not, create them + if (setup_3ware_nodes("twe", "3w-xxxx")) { + if (!errno) + errno=ENXIO; + return -1; + } + return open(pathname, O_RDONLY | O_NONBLOCK); + } + else + return -1; +} + +// equivalent to close(file descriptor) +int deviceclose(int fd){ + return close(fd); +} + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a --device=3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a -d 3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#endif + return; +} + + +// we are going to take advantage of the fact that Linux's devfs will only +// have device entries for devices that exist. So if we get the equivalent of +// ls /dev/hd[a-t], we have all the ATA devices on the system +// +// If any errors occur, leave errno set as it was returned by the +// system call, and return <0. +int get_dev_names(char*** names, const char* pattern, const char* name, int max) { + int n = 0, retglob, i, lim; + char** mp; + glob_t globbuf; + + memset(&globbuf, 0, sizeof(globbuf)); + + // in case of non-clean exit + *names=NULL; + + // Use glob to look for any directory entries matching the pattern + if ((retglob=glob(pattern, GLOB_ERR, NULL, &globbuf))) { + + // glob failed: free memory and return + globfree(&globbuf); + + if (retglob==GLOB_NOMATCH){ + pout("glob(3) found no matches for pattern %s\n", pattern); + return 0; + } + + if (retglob==GLOB_NOSPACE) + pout("glob(3) ran out of memory matching pattern %s\n", pattern); +#ifdef GLOB_ABORTED // missing in old versions of glob.h + else if (retglob==GLOB_ABORTED) + pout("glob(3) aborted matching pattern %s\n", pattern); +#endif + else + pout("Unexplained error in glob(3) of pattern %s\n", pattern); + + return -1; + } + + // did we find too many paths? + lim = ((int)globbuf.gl_pathc < max) ? (int)globbuf.gl_pathc : max; + if (lim < (int)globbuf.gl_pathc) + pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", + (int)globbuf.gl_pathc, max, pattern, (int)(globbuf.gl_pathc-max)); + + // allocate space for up to lim number of ATA devices + if (!(mp = (char **)calloc(lim, sizeof(char*)))){ + pout("Out of memory constructing scan device list\n"); + return -1; + } + + // now step through the list returned by glob. If not a link, copy + // to list. If it is a link, evaluate it and see if the path ends + // in "disc". + for (i=0; i<lim; i++){ + int retlink; + + // prepare a buffer for storing the link + char linkbuf[1024]; + + // see if path is a link + retlink=readlink(globbuf.gl_pathv[i], linkbuf, 1023); + + // if not a link (or a strange link), keep it + if (retlink<=0 || retlink>1023) + mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); + else { + // or if it's a link that points to a disc, follow it + char *p; + linkbuf[retlink]='\0'; + if ((p=strrchr(linkbuf,'/')) && !strcmp(p+1, "disc")) + // This is the branch of the code that gets followed if we are + // using devfs WITH traditional compatibility links. In this + // case, we add the traditional device name to the list that + // is returned. + mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); + else { + // This is the branch of the code that gets followed if we are + // using devfs WITHOUT traditional compatibility links. In + // this case, we check that the link to the directory is of + // the correct type, and then append "disc" to it. + char tmpname[1024]={0}; + char *type=strcmp(name,"ATA")?"scsi":"ide"; + if (strstr(linkbuf, type)){ + snprintf(tmpname, 1024, "%s/disc", globbuf.gl_pathv[i]); + mp[n++] = CustomStrDup(tmpname, 1, __LINE__, filenameandversion); + } + } + } + } + + // free memory, track memory usage + globfree(&globbuf); + mp = realloc(mp,n*(sizeof(char*))); + bytes += n*(sizeof(char*)); + + // and set up return values + *names=mp; + return n; +} + +// makes a list of device names to scan, for either ATA or SCSI +// devices. Return -1 if no memory remaining, else the number of +// devices on the list, which can be >=0. +int make_device_names (char*** devlist, const char* name) { + int retval, maxdev; + +#if 0 + // for testing case where no device names are found + return 0; +#endif + + if (!strcmp(name,"SCSI")) + retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26); + else if (!strcmp(name,"ATA")) + retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20); + else + // don't recognize disk type! + return 0; + + // if we found traditional links, we are done + if (retval>0) + return retval; + + // else look for devfs entries without traditional links + return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev); +} + + +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux one. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the file descriptor provided by open() +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + + +// huge value of buffer size needed because HDIO_DRIVE_CMD assumes +// that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and +// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space. +// Otherwise a 4+512 byte buffer would be enough. +#define STRANGE_BUFFER_LENGTH (4+512*0xf8) + +int ata_command_interface(int device, smart_command_set command, int select, char *data){ + unsigned char buff[STRANGE_BUFFER_LENGTH]; + // positive: bytes to write to caller. negative: bytes to READ from + // caller. zero: non-data command + int copydata=0; + + const int HDIO_DRIVE_CMD_OFFSET = 4; + + // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl() + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER + // buff[2]: ATA FEATURES REGISTER + // buff[3]: ATA SECTOR COUNT REGISTER + + // Note that on return: + // buff[2] contains the ATA SECTOR COUNT REGISTER + + // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) + memset(buff, 0, STRANGE_BUFFER_LENGTH); + + buff[0]=ATA_SMART_CMD; + switch (command){ + case CHECK_POWER_MODE: + buff[0]=ATA_CHECK_POWER_MODE; + copydata=1; + break; + case READ_VALUES: + buff[2]=ATA_SMART_READ_VALUES; + buff[3]=1; + copydata=512; + break; + case READ_THRESHOLDS: + buff[2]=ATA_SMART_READ_THRESHOLDS; + buff[1]=buff[3]=1; + copydata=512; + break; + case READ_LOG: + buff[2]=ATA_SMART_READ_LOG_SECTOR; + buff[1]=select; + buff[3]=1; + copydata=512; + break; + case WRITE_LOG: + break; + case IDENTIFY: + buff[0]=ATA_IDENTIFY_DEVICE; + buff[3]=1; + copydata=512; + break; + case PIDENTIFY: + buff[0]=ATA_IDENTIFY_PACKET_DEVICE; + buff[3]=1; + copydata=512; + break; + case ENABLE: + buff[2]=ATA_SMART_ENABLE; + buff[1]=1; + break; + case DISABLE: + buff[2]=ATA_SMART_DISABLE; + buff[1]=1; + break; + case STATUS: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + buff[2]=ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + buff[2]=ATA_SMART_AUTO_OFFLINE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + buff[2]=ATA_SMART_AUTOSAVE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; + buff[1]=select; + break; + case STATUS_CHECK: + // This command uses HDIO_DRIVE_TASK and has different syntax than + // the other commands. + buff[1]=ATA_SMART_STATUS; + break; + default: + pout("Unrecognized command %d in linux_ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno=ENOSYS; + return -1; + } + + // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the + // only ioctl() that can be used to WRITE data to the disk. + if (command==WRITE_LOG) { + unsigned char task[sizeof(ide_task_request_t)+512]; + ide_task_request_t *reqtask=(ide_task_request_t *) task; + task_struct_t *taskfile=(task_struct_t *) reqtask->io_ports; + int retval; + + memset(task, 0, sizeof(task)); + + taskfile->data = 0; + taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; + taskfile->sector_count = 1; + taskfile->sector_number = select; + taskfile->low_cylinder = 0x4f; + taskfile->high_cylinder = 0xc2; + taskfile->device_head = 0; + taskfile->command = ATA_SMART_CMD; + + reqtask->data_phase = TASKFILE_OUT; + reqtask->req_cmd = IDE_DRIVE_TASK_OUT; + reqtask->out_size = 512; + reqtask->in_size = 0; + + // copy user data into the task request structure + memcpy(task+sizeof(ide_task_request_t), data, 512); + + if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) { + if (retval==-EINVAL) + pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); + return -1; + } + return 0; + } + + // There are two different types of ioctls(). The HDIO_DRIVE_TASK + // one is this: + if (command==STATUS_CHECK){ + int retval; + + // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You + // have to read the IDE driver source code. Sigh. + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA FEATURES REGISTER + // buff[2]: ATA SECTOR_COUNT + // buff[3]: ATA SECTOR NUMBER + // buff[4]: ATA CYL LO REGISTER + // buff[5]: ATA CYL HI REGISTER + // buff[6]: ATA DEVICE HEAD + + unsigned const char normal_lo=0x4f, normal_hi=0xc2; + unsigned const char failed_lo=0xf4, failed_hi=0x2c; + buff[4]=normal_lo; + buff[5]=normal_hi; + + if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) { + if (retval==-EINVAL) { + pout("Error SMART Status command via HDIO_DRIVE_TASK failed"); + pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n"); + } + else + syserror("Error SMART Status command failed"); + return -1; + } + + // Cyl low and Cyl high unchanged means "Good SMART status" + if (buff[4]==normal_lo && buff[5]==normal_hi) + return 0; + + // These values mean "Bad SMART status" + if (buff[4]==failed_lo && buff[5]==failed_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 " PACKAGE_HOMEPAGE "\n"); + pout("Register values returned from SMART Status command are:\n"); + pout("CMD=0x%02x\n",(int)buff[0]); + pout("FR =0x%02x\n",(int)buff[1]); + pout("NS =0x%02x\n",(int)buff[2]); + pout("SC =0x%02x\n",(int)buff[3]); + pout("CL =0x%02x\n",(int)buff[4]); + pout("CH =0x%02x\n",(int)buff[5]); + pout("SEL=0x%02x\n",(int)buff[6]); + return -1; + } + +#if 1 + // Note to people doing ports to other OSes -- don't worry about + // this block -- you can safely ignore it. I have put it here + // because under linux when you do IDENTIFY DEVICE to a packet + // device, it generates an ugly kernel syslog error message. This + // is harmless but frightens users. So this block detects packet + // devices and make IDENTIFY DEVICE fail "nicely" without a syslog + // error message. + // + // If you read only the ATA specs, it appears as if a packet device + // *might* respond to the IDENTIFY DEVICE command. This is + // misleading - it's because around the time that SFF-8020 was + // incorporated into the ATA-3/4 standard, the ATA authors were + // sloppy. See SFF-8020 and you will see that ATAPI devices have + // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their + // command set, and return 'Command Aborted' to IDENTIFY DEVICE. + if (command==IDENTIFY || command==PIDENTIFY){ + unsigned short deviceid[256]; + // check the device identity, as seen when the system was booted + // or the device was FIRST registered. This will not be current + // if the user has subsequently changed some of the parameters. If + // device is a packet device, swap the command interpretations. + if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000)) + buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE; + } +#endif + + // We are now doing the HDIO_DRIVE_CMD type ioctl. + if ((ioctl(device, HDIO_DRIVE_CMD, buff))) + return -1; + + // CHECK POWER MODE command returns information in the Sector Count + // register (buff[3]). Copy to return data buffer. + if (command==CHECK_POWER_MODE) + buff[HDIO_DRIVE_CMD_OFFSET]=buff[2]; + + // if the command returns data then copy it back + if (copydata) + memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata); + + return 0; +} + +// >>>>>> Start of general SCSI specific linux code + +/* Linux specific code. + * Historically smartmontools (and smartsuite before it) used the + * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device + * nodes that use the SCSI subsystem. A better interface has been available + * via the SCSI generic (sg) driver but this involves the extra step of + * mapping disk devices (e.g. /dev/sda) to the corresponding sg device + * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of + * the sg driver have become available via the SG_IO ioctl which is available + * on all SCSI devices (on SCSI tape devices from lk 2.6.6). + * So the strategy below is to find out if the SG_IO ioctl is available and + * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl. + * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */ + +#define MAX_DXFER_LEN 1024 /* can be increased if necessary */ +#define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */ +#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */ +#define LSCSI_DRIVER_MASK 0xf /* mask out "suggestions" */ +#define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */ +#define LSCSI_DRIVER_TIMEOUT 0x6 +#define LSCSI_DID_TIME_OUT 0x3 +#define LSCSI_DID_BUS_BUSY 0x2 +#define LSCSI_DID_NO_CONNECT 0x1 + +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +#define SG_IO_PRESENT_UNKNOWN 0 +#define SG_IO_PRESENT_YES 1 +#define SG_IO_PRESENT_NO 2 + +static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); +static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); + +static int sg_io_state = SG_IO_PRESENT_UNKNOWN; + +/* Preferred implementation for issuing SCSI commands in linux. This + * function uses the SG_IO ioctl. Return 0 if command issued successfully + * (various status values should still be checked). If the SCSI command + * cannot be issued then a negative errno value is returned. */ +static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +{ +#ifndef SG_IO + ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report); + return -ENOTTY; +#else + struct sg_io_hdr io_hdr; + + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char *)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = iop->cmnd_len; + io_hdr.mx_sb_len = iop->max_sense_len; + io_hdr.dxfer_len = iop->dxfer_len; + io_hdr.dxferp = iop->dxferp; + io_hdr.cmdp = iop->cmnd; + io_hdr.sbp = iop->sensep; + /* sg_io_hdr interface timeout has millisecond units. Timeout of 0 + defaults to 60 seconds. */ + io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; + switch (iop->dxfer_dir) { + case DXFER_NONE: + io_hdr.dxfer_direction = SG_DXFER_NONE; + break; + case DXFER_FROM_DEVICE: + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case DXFER_TO_DEVICE: + io_hdr.dxfer_direction = SG_DXFER_TO_DEV; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) { + if (report) + pout(" SG_IO ioctl failed, errno=%d [%s]\n", errno, + strerror(errno)); + return -errno; + } + if (report > 0) { + pout(" scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n" + " info=0x%x duration=%d milliseconds\n", io_hdr.status, + io_hdr.host_status, io_hdr.driver_status, io_hdr.info, + io_hdr.duration); + if (report > 1) { + if (DXFER_FROM_DEVICE == iop->dxfer_dir) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char*)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + } + } + iop->resid = io_hdr.resid; + iop->scsi_status = io_hdr.status; + + if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */ + int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status); + + if (0 != io_hdr.host_status) { + if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) || + (LSCSI_DID_BUS_BUSY == io_hdr.host_status) || + (LSCSI_DID_TIME_OUT == io_hdr.host_status)) + return -ETIMEDOUT; + else + return -EIO; /* catch all */ + } + if (0 != masked_driver_status) { + if (LSCSI_DRIVER_TIMEOUT == masked_driver_status) + return -ETIMEDOUT; + else + return -EIO; /* catch all */ + } + if (LSCSI_DRIVER_SENSE == (io_hdr.driver_status & 0xf)) + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + iop->resp_sense_len = io_hdr.sb_len_wr; + if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && + iop->sensep && (iop->resp_sense_len > 0)) { + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", + (int)iop->resp_sense_len); + dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1); + } + } + if (report) { + if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { + pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", + iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12], + iop->sensep[13]); + } + else + pout(" status=0x%x\n", iop->scsi_status); + } + } + return 0; +#endif +} + +struct linux_ioctl_send_command +{ + int inbufsize; + int outbufsize; + UINT8 buff[MAX_DXFER_LEN + 16]; +}; + +/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't + * support: CDB length (guesses it from opcode), resid and timeout. + * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout + * to 2 hours in order to allow long foreground extended self tests. */ +static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +{ + struct linux_ioctl_send_command wrk; + int status, buff_offset; + size_t len; + + memcpy(wrk.buff, iop->cmnd, iop->cmnd_len); + buff_offset = iop->cmnd_len; + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char *)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + switch (iop->dxfer_dir) { + case DXFER_NONE: + wrk.inbufsize = 0; + wrk.outbufsize = 0; + break; + case DXFER_FROM_DEVICE: + wrk.inbufsize = 0; + if (iop->dxfer_len > MAX_DXFER_LEN) + return -EINVAL; + wrk.outbufsize = iop->dxfer_len; + break; + case DXFER_TO_DEVICE: + if (iop->dxfer_len > MAX_DXFER_LEN) + return -EINVAL; + memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len); + wrk.inbufsize = iop->dxfer_len; + wrk.outbufsize = 0; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk); + if (-1 == status) { + if (report) + pout(" SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n", + errno, strerror(errno)); + return -errno; + } + if (0 == status) { + if (report > 0) + pout(" status=0\n"); + if (DXFER_FROM_DEVICE == iop->dxfer_dir) { + memcpy(iop->dxferp, wrk.buff, iop->dxfer_len); + if (report > 1) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char*)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + } + return 0; + } + iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */ + if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf)) + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ? + SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len; + if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && + iop->sensep && (len > 0)) { + memcpy(iop->sensep, wrk.buff, len); + iop->resp_sense_len = len; + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", (int)len); + dStrHex((const char *)wrk.buff, len , 1); + } + } + if (report) { + if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { + pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff, + wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]); + } + else + pout(" status=0x%x\n", status); + } + if (iop->scsi_status > 0) + return 0; + else { + if (report > 0) + pout(" ioctl status=0x%x but scsi status=0, fail with EIO\n", + status); + return -EIO; /* give up, assume no device there */ + } +} + +/* SCSI command transmission interface function, linux version. + * Returns 0 if SCSI command successfully launched and response + * received. Even when 0 is returned the caller should check + * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings + * (e.g. CHECK CONDITION). If the SCSI command could not be issued + * (e.g. device not present or timeout) or some other problem + * (e.g. timeout) then returns a negative errno value */ +int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +{ + int res; + + /* implementation relies on static sg_io_state variable. If not + * previously set tries the SG_IO ioctl. If that succeeds assume + * that SG_IO ioctl functional. If it fails with an errno value + * other than ENODEV (no device) or permission then assume + * SCSI_IOCTL_SEND_COMMAND is the only option. */ + switch (sg_io_state) { + case SG_IO_PRESENT_UNKNOWN: + /* ignore report argument */ + if (0 == (res = sg_io_cmnd_io(dev_fd, iop, 0))) { + sg_io_state = SG_IO_PRESENT_YES; + return 0; + } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res)) + return res; /* wait until we see a device */ + sg_io_state = SG_IO_PRESENT_NO; + /* drop through by design */ + case SG_IO_PRESENT_NO: + return sisc_cmnd_io(dev_fd, iop, report); + case SG_IO_PRESENT_YES: + return sg_io_cmnd_io(dev_fd, iop, report); + default: + pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); + sg_io_state = SG_IO_PRESENT_UNKNOWN; + return -EIO; /* report error and reset state */ + } +} + +// >>>>>> End of general SCSI specific linux code + + +// prototype +void printwarning(smart_command_set command); + +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux interface to the 3ware 3w-xxxx driver. It allows ATA +// commands to be passed through the SCSI driver. +// DETAILED DESCRIPTION OF ARGUMENTS +// fd: is the file descriptor provided by open() +// disknum is the disk number (0 to 15) in the RAID array +// escalade_type indicates the type of controller type, and if scsi or char interface is used +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + + +/* 512 is the max payload size: increase if needed */ +#define BUFFER_LEN_678K ( sizeof(TW_Ioctl) ) // 1044 unpacked, 1041 packed +#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1 ) // 1539 unpacked, 1536 packed +#define BUFFER_LEN_9000 ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed +#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) ) + +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ + + // return value and buffer for ioctl() + int ioctlreturn, readdata=0; + + // Used by both the SCSI and char interfaces + TW_Passthru *passthru=NULL; + char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; + + // only used for SCSI device interface + TW_Ioctl *tw_ioctl=NULL; + TW_Output *tw_output=NULL; + + // only used for 6000/7000/8000 char device interface + TW_New_Ioctl *tw_ioctl_char=NULL; + + // only used for 9000 character device interface + TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL; + + memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); + + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { + tw_ioctl_apache = (TW_Ioctl_Buf_Apache *)ioctl_buffer; + tw_ioctl_apache->driver_command.control_code = TW_IOCTL_FIRMWARE_PASS_THROUGH; + tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */ + passthru = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand); + } + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { + tw_ioctl_char = (TW_New_Ioctl *)ioctl_buffer; + tw_ioctl_char->data_buffer_length = 512; + passthru = (TW_Passthru *)&(tw_ioctl_char->firmware_command); + } + else if (escalade_type==CONTROLLER_3WARE_678K) { + tw_ioctl = (TW_Ioctl *)ioctl_buffer; + tw_ioctl->cdb[0] = TW_IOCTL; + tw_ioctl->opcode = TW_ATA_PASSTHRU; + tw_ioctl->input_length = 512; // correct even for non-data commands + tw_ioctl->output_length = 512; // correct even for non-data commands + tw_output = (TW_Output *)tw_ioctl; + passthru = (TW_Passthru *)&(tw_ioctl->input_data); + } + else { + pout("Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum); + errno=ENOSYS; + return -1; + } + + // Same for (almost) all commands - but some reset below + passthru->byte0.opcode = TW_OP_ATA_PASSTHRU; + passthru->request_id = 0xFF; + passthru->byte3.aport = disknum; + passthru->byte3.host_id = 0; + passthru->status = 0; + passthru->flags = 0x1; + passthru->drive_head = 0x0; + passthru->sector_num = 0; + + // All SMART commands use this CL/CH signature. These are magic + // values from the ATA specifications. + passthru->cylinder_lo = 0x4F; + passthru->cylinder_hi = 0xC2; + + // SMART ATA COMMAND REGISTER value + passthru->command = ATA_SMART_CMD; + + // Is this a command that reads or returns 512 bytes? + // passthru->param values are: + // 0x0 - non data command without TFR write check, + // 0x8 - non data command with TFR write check, + // 0xD - data command that returns data to host from device + // 0xF - data command that writes data from host to device + // passthru->size values are 0x5 for non-data and 0x07 for data + if (command == READ_VALUES || + command == READ_THRESHOLDS || + command == READ_LOG || + command == IDENTIFY || + command == WRITE_LOG ) { + readdata=1; + passthru->byte0.sgloff = 0x5; + passthru->size = 0x7; + passthru->param = 0xD; + passthru->sector_count = 0x1; + // For 64-bit to work correctly, up the size of the command packet + // in dwords by 1 to account for the 64-bit single sgl 'address' + // field. Note that this doesn't agree with the typedefs but it's + // right (agree with kernel driver behavior/typedefs). + if (escalade_type==CONTROLLER_3WARE_9000_CHAR && sizeof(long)==8) + passthru->size++; + } + else { + // Non data command -- but doesn't use large sector + // count register values. + passthru->byte0.sgloff = 0x0; + passthru->size = 0x5; + passthru->param = 0x8; + passthru->sector_count = 0x0; + } + + // Now set ATA registers depending upon command + switch (command){ + case CHECK_POWER_MODE: + passthru->command = ATA_CHECK_POWER_MODE; + passthru->features = 0; + passthru->cylinder_lo = 0; + passthru->cylinder_hi = 0; + break; + case READ_VALUES: + passthru->features = ATA_SMART_READ_VALUES; + break; + case READ_THRESHOLDS: + passthru->features = ATA_SMART_READ_THRESHOLDS; + break; + case READ_LOG: + passthru->features = ATA_SMART_READ_LOG_SECTOR; + // log number to return + passthru->sector_num = select; + break; + case WRITE_LOG: + if (escalade_type == CONTROLLER_3WARE_9000_CHAR) + memcpy((unsigned char *)tw_ioctl_apache->data_buffer, data, 512); + else if (escalade_type == CONTROLLER_3WARE_678K_CHAR) + memcpy((unsigned char *)tw_ioctl_char->data_buffer, data, 512); + else { + // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE + // memcpy(tw_output->output_data, data, 512); + printwarning(command); + errno=ENOTSUP; + return -1; + } + readdata=0; + passthru->features = ATA_SMART_WRITE_LOG_SECTOR; + passthru->sector_count = 1; + passthru->sector_num = select; + passthru->param = 0xF; // PIO data write + break; + case IDENTIFY: + // ATA IDENTIFY DEVICE + passthru->command = ATA_IDENTIFY_DEVICE; + passthru->features = 0; + passthru->cylinder_lo = 0; + passthru->cylinder_hi = 0; + break; + case PIDENTIFY: + // 3WARE controller can NOT have packet device internally + pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum); + errno=ENODEV; + return -1; + case ENABLE: + passthru->features = ATA_SMART_ENABLE; + break; + case DISABLE: + passthru->features = ATA_SMART_DISABLE; + break; + case AUTO_OFFLINE: + passthru->features = ATA_SMART_AUTO_OFFLINE; + // Enable or disable? + passthru->sector_count = select; + break; + case AUTOSAVE: + passthru->features = ATA_SMART_AUTOSAVE; + // Enable or disable? + passthru->sector_count = select; + break; + case IMMEDIATE_OFFLINE: + passthru->features = ATA_SMART_IMMEDIATE_OFFLINE; + // What test type to run? + passthru->sector_num = select; + break; + case STATUS_CHECK: + passthru->features = ATA_SMART_STATUS; + break; + case STATUS: + // This is JUST to see if SMART is enabled, by giving SMART status + // command. But it doesn't say if status was good, or failing. + // See below for the difference. + passthru->features = ATA_SMART_STATUS; + break; + default: + pout("Unrecognized command %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", command, disknum); + errno=ENOSYS; + return -1; + } + + // Now send the command down through an ioctl() + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) + ioctlreturn=ioctl(fd, TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache); + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) + ioctlreturn=ioctl(fd, TW_CMD_PACKET_WITH_DATA, tw_ioctl_char); + else + ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl); + + // Deal with the different error cases + if (ioctlreturn) { + if (CONTROLLER_3WARE_678K==escalade_type && ((command==AUTO_OFFLINE || command==AUTOSAVE) && select)){ + // error here is probably a kernel driver whose version is too old + printwarning(command); + errno=ENOTSUP; + } + if (!errno) + errno=EIO; + return -1; + } + + // The passthru structure is valid after return from an ioctl if: + // - we are using the character interface OR + // - we are using the SCSI interface and this is a NON-READ-DATA command + // For SCSI interface, note that we set passthru to a different + // value after ioctl(). + if (CONTROLLER_3WARE_678K==escalade_type) { + if (readdata) + passthru=NULL; + else + passthru=(TW_Passthru *)&(tw_output->output_data); + } + + // See if the ATA command failed. Now that we have returned from + // the ioctl() call, if passthru is valid, then: + // - passthru->status contains the 3ware controller STATUS + // - passthru->command contains the ATA STATUS register + // - passthru->features contains the ATA ERROR register + // + // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS + // If bit 0 (error bit) is set, then ATA ERROR register is valid. + // While we *might* decode the ATA ERROR register, at the moment it + // doesn't make much sense: we don't care in detail why the error + // happened. + + if (passthru && (passthru->status || (passthru->command & 0x21))) { + errno=EIO; + return -1; + } + + // If this is a read data command, copy data to output buffer + if (readdata) { + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) + memcpy(data, (unsigned char *)tw_ioctl_apache->data_buffer, 512); + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) + memcpy(data, (unsigned char *)tw_ioctl_char->data_buffer, 512); + else + memcpy(data, tw_output->output_data, 512); + } + + // For STATUS_CHECK, we need to check register values + if (command==STATUS_CHECK) { + + // To find out if the SMART RETURN STATUS is good or failing, we + // need to examine the values of the Cylinder Low and Cylinder + // High Registers. + + unsigned short cyl_lo=passthru->cylinder_lo; + unsigned short cyl_hi=passthru->cylinder_hi; + + // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. + if (cyl_lo==0x4F && cyl_hi==0xC2) + return 0; + + // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL + if (cyl_lo==0xF4 && cyl_hi==0x2C) + return 1; + + // Any other values mean that something has gone wrong with the command + if (CONTROLLER_3WARE_678K==escalade_type) { + printwarning(command); + errno=ENOSYS; + return 0; + } + else { + errno=EIO; + return -1; + } + } + + // copy sector count register (one byte!) to return data + if (command==CHECK_POWER_MODE) + *data=*(char *)&(passthru->sector_count); + + // look for nonexistent devices/ports + if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { + errno=ENODEV; + return -1; + } + + return 0; +} + + + +int marvell_command_interface(int device, + smart_command_set command, + int select, + char *data) { + typedef struct { + int inlen; + int outlen; + char cmd[540]; + } mvsata_scsi_cmd; + + int copydata = 0; + mvsata_scsi_cmd smart_command; + unsigned char *buff = &smart_command.cmd[6]; + // See struct hd_drive_cmd_hdr in hdreg.h + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA SECTOR NUMBER REGISTER + // buff[2]: ATA FEATURES REGISTER + // buff[3]: ATA SECTOR COUNT REGISTER + + // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) + memset(&smart_command, 0, sizeof(smart_command)); + smart_command.inlen = 540; + smart_command.outlen = 540; + smart_command.cmd[0] = 0xC; //Vendor-specific code + smart_command.cmd[4] = 6; //command length + + buff[0] = ATA_SMART_CMD; + switch (command){ + case CHECK_POWER_MODE: + buff[0]=ATA_CHECK_POWER_MODE; + break; + case READ_VALUES: + buff[2]=ATA_SMART_READ_VALUES; + copydata=buff[3]=1; + break; + case READ_THRESHOLDS: + buff[2]=ATA_SMART_READ_THRESHOLDS; + copydata=buff[1]=buff[3]=1; + break; + case READ_LOG: + buff[2]=ATA_SMART_READ_LOG_SECTOR; + buff[1]=select; + copydata=buff[3]=1; + break; + case IDENTIFY: + buff[0]=ATA_IDENTIFY_DEVICE; + copydata=buff[3]=1; + break; + case PIDENTIFY: + buff[0]=ATA_IDENTIFY_PACKET_DEVICE; + copydata=buff[3]=1; + break; + case ENABLE: + buff[2]=ATA_SMART_ENABLE; + buff[1]=1; + break; + case DISABLE: + buff[2]=ATA_SMART_DISABLE; + buff[1]=1; + break; + case STATUS: + case STATUS_CHECK: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + buff[2] = ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + buff[2]=ATA_SMART_AUTO_OFFLINE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + buff[2]=ATA_SMART_AUTOSAVE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; + buff[1]=select; + break; + default: + pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command); + exit(1); + break; + } + // There are two different types of ioctls(). The HDIO_DRIVE_TASK + // one is this: + // We are now doing the HDIO_DRIVE_CMD type ioctl. + if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command)) + return -1; + + if (command==CHECK_POWER_MODE) { + // LEON -- CHECK THIS PLEASE. THIS SHOULD BE THE SECTOR COUNT + // REGISTER, AND IT MIGHT BE buff[2] NOT buff[3]. Bruce + data[0]=buff[3]; + return 0; + } + + //Data returned is starting from 0 offset + if (command == STATUS || command == STATUS_CHECK) + { + // Cyl low and Cyl high unchanged means "Good SMART status" + if (buff[4] == 0x4F && buff[5] == 0xC2) + return 0; + // These values mean "Bad SMART status" + if (buff[4] == 0xF4 && buff[5] == 0x2C) + 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",PACKAGE_BUGREPORT); + pout("Register values returned from SMART Status command are:\n"); + pout("CMD =0x%02x\n",(int)buff[0]); + pout("FR =0x%02x\n",(int)buff[1]); + pout("NS =0x%02x\n",(int)buff[2]); + pout("SC =0x%02x\n",(int)buff[3]); + pout("CL =0x%02x\n",(int)buff[4]); + pout("CH =0x%02x\n",(int)buff[5]); + pout("SEL=0x%02x\n",(int)buff[6]); + return -1; + } + + if (copydata) + memcpy(data, buff, 512); + return 0; +} + + +// Utility function for printing warnings +void printwarning(smart_command_set command){ + static int printed[4]={0,0,0,0}; + const char* message= + "can not be passed through the 3ware 3w-xxxx driver. This can be fixed by\n" + "applying a simple 3w-xxxx driver patch that can be found here:\n" + PACKAGE_HOMEPAGE "\n" + "Alternatively, upgrade your 3w-xxxx driver to version 1.02.00.037 or greater.\n\n"; + + if (command==AUTO_OFFLINE && !printed[0]) { + printed[0]=1; + pout("The SMART AUTO-OFFLINE ENABLE command (smartmontools -o on option/Directive)\n%s", message); + } + else if (command==AUTOSAVE && !printed[1]) { + printed[1]=1; + pout("The SMART AUTOSAVE ENABLE command (smartmontools -S on option/Directive)\n%s", message); + } + else if (command==STATUS_CHECK && !printed[2]) { + printed[2]=1; + pout("The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n%s", message); + } + else if (command==WRITE_LOG && !printed[3]) { + printed[3]=1; + pout("The SMART WRITE LOG command (smartmontools -t selective) only supported via char /dev/tw[ae] interface\n"); + } + + return; +} + +// Guess device type (ata or scsi) based on device name (Linux +// specific) SCSI device name in linux can be sd, sr, scd, st, nst, +// osst, nosst and sg. +static const char * lin_dev_prefix = "/dev/"; +static const char * lin_dev_ata_disk_plus = "h"; +static const char * lin_dev_ata_devfs_disk_plus = "ide/"; +static const char * lin_dev_scsi_devfs_disk_plus = "scsi/"; +static const char * lin_dev_scsi_disk_plus = "s"; +static const char * lin_dev_scsi_tape1 = "ns"; +static const char * lin_dev_scsi_tape2 = "os"; +static const char * lin_dev_scsi_tape3 = "nos"; +static const char * lin_dev_3ware_9000_char = "twa"; +static const char * lin_dev_3ware_678k_char = "twe"; + +int guess_device_type(const char * dev_name) { + int len; + int dev_prefix_len = strlen(lin_dev_prefix); + + // if dev_name null, or string length zero + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + // Remove the leading /dev/... if it's there + if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + // if nothing else in the string, unrecognized + return CONTROLLER_UNKNOWN; + // else advance pointer to following characters + dev_name += dev_prefix_len; + } + + // form /dev/h* or h* + if (!strncmp(lin_dev_ata_disk_plus, dev_name, + strlen(lin_dev_ata_disk_plus))) + return CONTROLLER_ATA; + + // form /dev/ide/* or ide/* + if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name, + strlen(lin_dev_ata_devfs_disk_plus))) + return CONTROLLER_ATA; + + // form /dev/s* or s* + if (!strncmp(lin_dev_scsi_disk_plus, dev_name, + strlen(lin_dev_scsi_disk_plus))) + return CONTROLLER_SCSI; + + // form /dev/scsi/* or scsi/* + if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name, + strlen(lin_dev_scsi_devfs_disk_plus))) + return CONTROLLER_SCSI; + + // form /dev/ns* or ns* + if (!strncmp(lin_dev_scsi_tape1, dev_name, + strlen(lin_dev_scsi_tape1))) + return CONTROLLER_SCSI; + + // form /dev/os* or os* + if (!strncmp(lin_dev_scsi_tape2, dev_name, + strlen(lin_dev_scsi_tape2))) + return CONTROLLER_SCSI; + + // form /dev/nos* or nos* + if (!strncmp(lin_dev_scsi_tape3, dev_name, + strlen(lin_dev_scsi_tape3))) + return CONTROLLER_SCSI; + + // form /dev/twa* + if (!strncmp(lin_dev_3ware_9000_char, dev_name, + strlen(lin_dev_3ware_9000_char))) + return CONTROLLER_3WARE_9000_CHAR; + + // form /dev/twe* + if (!strncmp(lin_dev_3ware_678k_char, dev_name, + strlen(lin_dev_3ware_678k_char))) + return CONTROLLER_3WARE_678K_CHAR; + + // we failed to recognize any of the forms + return CONTROLLER_UNKNOWN; +} + + +#if 0 + +[ed@firestorm ed]$ ls -l /dev/discs +total 0 +lr-xr-xr-x 1 root root 30 Dec 31 1969 disc0 -> ../ide/host2/bus0/target0/lun0/ +lr-xr-xr-x 1 root root 30 Dec 31 1969 disc1 -> ../ide/host2/bus1/target0/lun0/ +[ed@firestorm ed]$ ls -l dev/ide/host*/bus*/target*/lun*/disc +ls: dev/ide/host*/bus*/target*/lun*/disc: No such file or directory +[ed@firestorm ed]$ ls -l /dev/ide/host*/bus*/target*/lun*/disc +brw------- 1 root root 33, 0 Dec 31 1969 /dev/ide/host2/bus0/target0/lun0/disc +brw------- 1 root root 34, 0 Dec 31 1969 /dev/ide/host2/bus1/target0/lun0/disc +[ed@firestorm ed]$ ls -l /dev/ide/c*b*t*u* +ls: /dev/ide/c*b*t*u*: No such file or directory +[ed@firestorm ed]$ +Script done on Fri Nov 7 13:46:28 2003 + +#endif diff --git a/sm5/os_netbsd.c b/sm5/os_netbsd.c new file mode 100644 index 0000000000000000000000000000000000000000..4ccb43d512b25cb542447213eef4c39a63069991 --- /dev/null +++ b/sm5/os_netbsd.c @@ -0,0 +1,445 @@ +/* + * os_netbsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-4 Sergey Svishchev <smartmontools-support@lists.sourceforge.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. + * + */ + +#include "config.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_netbsd.h" + +const char *os_XXXX_c_cvsid = "$Id: os_netbsd.c,v 1.9 2004/08/18 19:27:36 likewise Exp $" \ +ATACMDS_H_CVSID OS_NETBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* global variable holding byte count of allocated memory */ +extern long long bytes; + +enum warnings { + BAD_SMART, NO_3WARE, MAX_MSG +}; + +/* Utility function for printing warnings */ +void +printwarning(int msgNo, const char *extra) +{ + static int printed[] = {0, 0}; + static const char *message[] = { + "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n", + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s", extra); + } + } + return; +} + +static const char *net_dev_prefix = "/dev/"; +static const char *net_dev_ata_disk = "wd"; +static const char *net_dev_scsi_disk = "sd"; +static const char *net_dev_scsi_tape = "enrst"; + +/* Guess device type(ata or scsi) based on device name */ +int +guess_device_type(const char *dev_name) +{ + int len; + int dev_prefix_len = strlen(net_dev_prefix); + + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + return CONTROLLER_UNKNOWN; + else + dev_name += dev_prefix_len; + } + if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk))) + return CONTROLLER_ATA; + + if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk))) + return CONTROLLER_SCSI; + + if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape))) + return CONTROLLER_SCSI; + + return CONTROLLER_UNKNOWN; +} + +int +get_dev_names(char ***names, const char *prefix) +{ + char *disknames, *p, **mp; + int n = 0; + int sysctl_mib[2]; + size_t sysctl_len; + + *names = NULL; + + sysctl_mib[0] = CTL_HW; + sysctl_mib[1] = HW_DISKNAMES; + if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(disknames = malloc(sysctl_len))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) { + if (strncmp(p, prefix, strlen(prefix))) { + continue; + } + mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2); + if (!mp[n]) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition()); + bytes += strlen(mp[n]) + 1; + n++; + } + + mp = realloc(mp, n * (sizeof(char *))); + bytes += (n) * (sizeof(char *)); + *names = mp; + return n; +} + +int +make_device_names(char ***devlist, const char *name) +{ + if (!strcmp(name, "SCSI")) + return get_dev_names(devlist, net_dev_scsi_disk); + else if (!strcmp(name, "ATA")) + return get_dev_names(devlist, net_dev_ata_disk); + else + return 0; +} + +int +deviceopen(const char *pathname, char *type) +{ + if (!strcmp(type, "SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } else if (!strcmp(type, "ATA")) + return open(pathname, O_RDWR | O_NONBLOCK); + else + return -1; +} + +int +deviceclose(int fd) +{ + return close(fd); +} + +int +marvell_command_interface(int fd, smart_command_set command, int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, int select, char *data) +{ + struct atareq req; + unsigned char inbuf[DEV_BSIZE]; + int retval, copydata = 0; + + memset(&req, 0, sizeof(req)); + memset(&inbuf, 0, sizeof(inbuf)); + + switch (command) { + case READ_VALUES: + req.flags = ATACMD_READ; + req.features = WDSM_RD_DATA; + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_THRESHOLDS: + req.flags = ATACMD_READ; + req.features = WDSM_RD_THRESHOLDS; + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_LOG: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + copydata = 1; + break; + case WRITE_LOG: + memcpy(inbuf, data, 512); + req.flags = ATACMD_WRITE; + req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case IDENTIFY: + req.flags = ATACMD_READ; + req.command = WDCC_IDENTIFY; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case PIDENTIFY: + req.flags = ATACMD_READ; + req.command = ATAPI_IDENTIFY_DEVICE; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case ENABLE: + req.flags = ATACMD_READ; + req.features = WDSM_ENABLE_OPS; + req.command = WDCC_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case DISABLE: + req.flags = ATACMD_READ; + req.features = WDSM_DISABLE_OPS; + req.command = WDCC_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case AUTO_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case AUTOSAVE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.sec_count = 0xf1; + /* to enable autosave */ + req.timeout = 1000; + break; + case IMMEDIATE_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case STATUS_CHECK: + /* same command, no HDIO in NetBSD */ + case STATUS: + req.flags = ATACMD_READ; + req.features = WDSM_STATUS; + req.command = WDCC_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case CHECK_POWER_MODE: + req.flags = ATACMD_READREG; + req.command = WDCC_CHECK_PWR; + req.timeout = 1000; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n", command); + errno = ENOSYS; + return -1; + } + + if (command == STATUS_CHECK) { + char buf[512]; + + unsigned const short normal = WDSMART_CYL, failed = 0x2cf4; + + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + /* Cyl low and Cyl high unchanged means "Good SMART status" */ + if (le16toh(req.cylinder) == normal) + return 0; + + /* These values mean "Bad SMART status" */ + if (le16toh(req.cylinder) == failed) + return 1; + + /* We haven't gotten output that makes sense; + * print out some debugging info */ + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num, + (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff), + (int) req.error); + printwarning(BAD_SMART, buf); + return 0; + } + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + if (command == CHECK_POWER_MODE) + data[0] = req.sec_count; + + if (copydata) + memcpy(data, inbuf, 512); + + return 0; +} + +int +escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) +{ + printwarning(NO_3WARE, NULL); + return -1; +} + +int +do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct scsireq sc; + + if (report > 0) { + size_t k; + + const unsigned char *ucp = iop->cmnd; + const char *np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } else + pout("]"); + } + memset(&sc, 0, sizeof(sc)); + memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); + sc.cmdlen = iop->cmnd_len; + sc.databuf = iop->dxferp; + sc.datalen = iop->dxfer_len; + sc.senselen = iop->max_sense_len; + sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout; /* XXX */ + sc.flags = + (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ : /* XXX */ + (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); + + if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) { + warn("error sending SCSI ccb"); + return -1; + } + iop->resid = sc.datalen - sc.datalen_used; + iop->scsi_status = sc.status; + if (iop->sensep) { + memcpy(iop->sensep, sc.sense, sc.senselen_used); + iop->resp_sense_len = sc.senselen_used; + } + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } + return 0; +} + +/* print examples for smartctl */ +void +print_smartctl_examples() +{ + char p; + + p = 'a' + getrawpartition(); + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#else + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/wd0%c" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#endif + return; +} diff --git a/sm5/os_openbsd.c b/sm5/os_openbsd.c new file mode 100644 index 0000000000000000000000000000000000000000..75c4da7ccec93acf96f79be636edf9f5c82cbf8d --- /dev/null +++ b/sm5/os_openbsd.c @@ -0,0 +1,446 @@ +/* + * os_openbsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 David Snyder <smartmontools-support@lists.sourceforge.net> + * + * Derived from os_netbsd.c by Sergey Svishchev <smartmontools-support@lists.sourceforge.net>, Copyright (C) 2003-4 + * + * 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. + * + */ + +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_openbsd.h" + +const char *os_XXXX_c_cvsid = "$Id: os_openbsd.c,v 1.6 2004/09/12 10:21:09 snyderx Exp $" \ +ATACMDS_H_CVSID OS_OPENBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* global variable holding byte count of allocated memory */ +extern long long bytes; + +enum warnings { + BAD_SMART, NO_3WARE, MAX_MSG +}; + +/* Utility function for printing warnings */ +void +printwarning(int msgNo, const char *extra) +{ + static int printed[] = {0, 0}; + static const char *message[] = { + "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n", + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s", extra); + } + } + return; +} + +static const char *net_dev_prefix = "/dev/"; +static const char *net_dev_ata_disk = "wd"; +static const char *net_dev_scsi_disk = "sd"; +static const char *net_dev_scsi_tape = "st"; + +/* Guess device type(ata or scsi) based on device name */ +int +guess_device_type(const char *dev_name) +{ + int len; + int dev_prefix_len = strlen(net_dev_prefix); + + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + return CONTROLLER_UNKNOWN; + else + dev_name += dev_prefix_len; + } + if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk))) + return CONTROLLER_ATA; + + if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk))) + return CONTROLLER_SCSI; + + if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape))) + return CONTROLLER_SCSI; + + return CONTROLLER_UNKNOWN; +} + +int +get_dev_names(char ***names, const char *prefix) +{ + char *disknames, *p, **mp; + int n = 0; + int sysctl_mib[2]; + size_t sysctl_len; + + *names = NULL; + + sysctl_mib[0] = CTL_HW; + sysctl_mib[1] = HW_DISKNAMES; + if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(disknames = malloc(sysctl_len))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + for (p = strtok(disknames, ","); p; p = strtok(NULL, ",")) { + if (strncmp(p, prefix, strlen(prefix))) { + continue; + } + mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2); + if (!mp[n]) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition()); + bytes += strlen(mp[n]) + 1; + n++; + } + + mp = realloc(mp, n * (sizeof(char *))); + bytes += (n) * (sizeof(char *)); + *names = mp; + return n; +} + +int +make_device_names(char ***devlist, const char *name) +{ + if (!strcmp(name, "SCSI")) + return get_dev_names(devlist, net_dev_scsi_disk); + else if (!strcmp(name, "ATA")) + return get_dev_names(devlist, net_dev_ata_disk); + else + return 0; +} + +int +deviceopen(const char *pathname, char *type) +{ + if (!strcmp(type, "SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } else if (!strcmp(type, "ATA")) + return open(pathname, O_RDWR | O_NONBLOCK); + else + return -1; +} + +int +deviceclose(int fd) +{ + return close(fd); +} + +int +marvell_command_interface(int fd, smart_command_set command, int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, int select, char *data) +{ + struct atareq req; + unsigned char inbuf[DEV_BSIZE]; + int retval, copydata = 0; + + memset(&req, 0, sizeof(req)); + memset(&inbuf, 0, sizeof(inbuf)); + + switch (command) { + case READ_VALUES: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_VALUES; + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_THRESHOLDS: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_THRESHOLDS; + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_LOG: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + copydata = 1; + break; + case WRITE_LOG: + memcpy(inbuf, data, 512); + req.flags = ATACMD_WRITE; + req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case IDENTIFY: + req.flags = ATACMD_READ; + req.command = WDCC_IDENTIFY; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case PIDENTIFY: + req.flags = ATACMD_READ; + req.command = ATAPI_IDENTIFY_DEVICE; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case ENABLE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_ENABLE; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case DISABLE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_DISABLE; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case AUTO_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case AUTOSAVE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.sec_count = 0xf1; + /* to enable autosave */ + req.timeout = 1000; + break; + case IMMEDIATE_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case STATUS_CHECK: + /* same command, no HDIO in NetBSD */ + case STATUS: + req.flags = ATACMD_READ; + req.features = ATA_SMART_STATUS; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case CHECK_POWER_MODE: + req.flags = ATACMD_READREG; + req.command = WDCC_CHECK_PWR; + req.timeout = 1000; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n", command); + errno = ENOSYS; + return -1; + } + + if (command == STATUS_CHECK) { + char buf[512]; + + unsigned const short normal = WDSMART_CYL, failed = 0x2cf4; + + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + /* Cyl low and Cyl high unchanged means "Good SMART status" */ + if (letoh16(req.cylinder) == normal) + return 0; + + /* These values mean "Bad SMART status" */ + if (letoh16(req.cylinder) == failed) + return 1; + + /* We haven't gotten output that makes sense; + * print out some debugging info */ + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num, + (int) (letoh16(req.cylinder) & 0xff), (int) ((letoh16(req.cylinder) >> 8) & 0xff), + (int) req.error); + printwarning(BAD_SMART, buf); + return 0; + } + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + if (command == CHECK_POWER_MODE) + data[0] = req.sec_count; + + if (copydata) + memcpy(data, inbuf, 512); + + return 0; +} + +int +escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) +{ + printwarning(NO_3WARE, NULL); + return -1; +} + +int +do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct scsireq sc; + + if (report > 0) { + size_t k; + + const unsigned char *ucp = iop->cmnd; + const char *np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } else + pout("]"); + } + memset(&sc, 0, sizeof(sc)); + memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); + sc.cmdlen = iop->cmnd_len; + sc.databuf = iop->dxferp; + sc.datalen = iop->dxfer_len; + sc.senselen = iop->max_sense_len; + sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout; /* XXX */ + sc.flags = + (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ : /* XXX */ + (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); + + if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) { + warn("error sending SCSI ccb"); + return -1; + } + iop->resid = sc.datalen - sc.datalen_used; + iop->scsi_status = sc.status; + if (iop->sensep) { + memcpy(iop->sensep, sc.sense, sc.senselen_used); + iop->resp_sense_len = sc.senselen_used; + } + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } + return 0; +} + +/* print examples for smartctl */ +void +print_smartctl_examples() +{ + char p; + + p = 'a' + getrawpartition(); + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#else + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/wd0%c" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#endif + return; +} diff --git a/sm5/os_solaris.c b/sm5/os_solaris.c new file mode 100644 index 0000000000000000000000000000000000000000..3de0d1abc91d2591b93dbafc2386dc99f6c26190 --- /dev/null +++ b/sm5/os_solaris.c @@ -0,0 +1,431 @@ +/* + * os_solaris.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-4 Casper Dik <smartmontools-support@lists.sourceforge.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. + * + */ + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <dirent.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/param.h> + +// These are needed to define prototypes for the functions defined below +#include "config.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +// This is to include whatever prototypes you define in os_solaris.h +#include "os_solaris.h" + +#define ARGUSED(x) ((void)(x)) + +extern long long bytes; + +static const char *filenameandversion="$Id: os_solaris.c,v 1.21 2004/08/28 23:23:46 card_captor Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_solaris.c,v 1.21 2004/08/28 23:23:46 card_captor Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// The printwarning() function warns about unimplemented functions +int printedout[2]; +char *unimplemented[2]={ + "ATA command routine ata_command_interface()", + "3ware Escalade Controller command routine escalade_command_interface()", +}; + +int printwarning(int which){ + if (!unimplemented[which]) + return 0; + + if (printedout[which]) + return 1; + + printedout[which]=1; + + pout("\n" + "#######################################################################\n" + "%s NOT IMPLEMENTED under Solaris.\n" + "Please contact " PACKAGE_BUGREPORT " if\n" + "you want to help in porting smartmontools to Solaris.\n" + "#######################################################################\n" + "\n", + unimplemented[which]); + + return 1; +} + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n" + " (Prints Self-Test & Attribute errors)\n" + ); +#else + printf( + " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n" + " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n" + " (Prints Self-Test & Attribute errors)\n" + ); +#endif + return; +} + +static const char *uscsidrvrs[] = { + "sd", + "ssd", + "st" +}; + +static const char *atadrvrs[] = { + "cmdk", + "dad", +}; + +static int +isdevtype(const char *dev_name, const char *table[], int tsize) +{ + char devpath[MAXPATHLEN]; + int i; + char *basename; + + if (realpath(dev_name, devpath) == NULL) + return 0; + + if ((basename = strrchr(devpath, '/')) == NULL) + return 0; + + basename++; + + for (i = 0; i < tsize; i++) { + int l = strlen(table[i]); + if (strncmp(basename, table[i], l) == 0 && basename[l] == '@') + return 1; + } + return 0; +} + +static int +isscsidev(const char *path) +{ + return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *)); +} + +static int +isatadev(const char *path) +{ + return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *)); +} + +// tries to guess device type given the name (a path) +int guess_device_type (const char* dev_name) { + if (isscsidev(dev_name)) + return CONTROLLER_SCSI; + else if (isatadev(dev_name)) + return CONTROLLER_ATA; + else + return CONTROLLER_UNKNOWN; +} + +struct pathlist { + char **names; + int nnames; + int maxnames; +}; + +static int +addpath(const char *path, struct pathlist *res) +{ + if (++res->nnames > res->maxnames) { + res->maxnames += 16; + res->names = realloc(res->names, res->maxnames * sizeof (char *)); + if (res->names == NULL) + return -1; + bytes += 16*sizeof(char *); + } + if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion))) + return -1; + return 0; +} + +static int +grokdir(const char *dir, struct pathlist *res, int testfun(const char *)) +{ + char pathbuf[MAXPATHLEN]; + size_t len; + DIR *dp; + struct dirent *de; + int isdisk = strstr(dir, "dsk") != NULL; + char *p; + + len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir); + if (len >= sizeof (pathbuf)) + return -1; + + dp = opendir(dir); + if (dp == NULL) + return 0; + + while ((de = readdir(dp)) != NULL) { + if (de->d_name[0] == '.') + continue; + + if (strlen(de->d_name) + len >= sizeof (pathbuf)) + continue; + + if (isdisk) { + /* Disk represented by slice 0 */ + p = strstr(de->d_name, "s0"); + /* String doesn't end in "s0\0" */ + if (p == NULL || p[2] != '\0') + continue; + } else { + /* Tape drive represented by the all-digit device */ + for (p = de->d_name; *p; p++) + if (!isdigit((int)(*p))) + break; + if (*p != '\0') + continue; + } + strcpy(&pathbuf[len], de->d_name); + if (testfun(pathbuf)) { + if (addpath(pathbuf, res) == -1) { + closedir(dp); + return -1; + } + } + } + closedir(dp); + + return 0; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number of devices, or -1 if out of memory. +int make_device_names (char*** devlist, const char* name) { + struct pathlist res; + + res.nnames = res.maxnames = 0; + res.names = NULL; + if (strcmp(name, "SCSI") == 0) { + if (grokdir("/dev/rdsk", &res, isscsidev) == -1) + return -1; + if (grokdir("/dev/rmt", &res, isscsidev) == -1) + return -1; + } else if (strcmp(name, "ATA") == 0) { + if (grokdir("/dev/rdsk", &res, isatadev) == -1) + return -1; + } else { + // non-SCSI and non-ATA case not implemented + *devlist=NULL; + return 0; + } + + // shrink array to min possible size + res.names = realloc(res.names, res.nnames * sizeof (char *)); + bytes -= sizeof(char *)*(res.maxnames-res.nnames); + + // pass list back + *devlist = res.names; + return res.nnames; +} + +// Like open(). Return integer handle, used by functions below only. +// type="ATA" or "SCSI". +int deviceopen(const char *pathname, char *type){ + if (!strcmp(type,"SCSI")) + return open(pathname, O_RDWR | O_NONBLOCK); + else if (!strcmp(type,"ATA")) + return open(pathname, O_RDONLY | O_NONBLOCK); + else + return -1; +} + +// Like close(). Acts on handles returned by above function. +int deviceclose(int fd){ + return close(fd); +} + +static void swap_sector(void *p) +{ + int i; + unsigned char t, *cp = p; + for(i = 0; i < 256; i++) { + t = cp[0]; cp[0] = cp[1]; cp[1] = t; + cp += 2; + } +} + +// Interface to ATA devices. See os_linux.c +int marvell_command_interface(int fd, smart_command_set command, int select, char *data){ + ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data); + return -1; +} + +int ata_command_interface(int fd, smart_command_set command, int select, char *data){ +#if defined(__sparc) + int err; + + switch (command){ + case CHECK_POWER_MODE: + /* currently not recognized */ + return -1; + case READ_VALUES: + return smart_read_data(fd, data); + case READ_THRESHOLDS: + return smart_read_thresholds(fd, data); + case READ_LOG: + return smart_read_log(fd, select, 1, data); + case IDENTIFY: + err = ata_identify(fd, data); + if(err) return err; + swap_sector(data); + return 0; + case PIDENTIFY: + err = ata_pidentify(fd, data); + if(err) return err; + swap_sector(data); + return 0; + case ENABLE: + return smart_enable(fd); + case DISABLE: + return smart_disable(fd); + case STATUS: + return smart_status(fd); + case AUTO_OFFLINE: + return smart_auto_offline(fd, select); + case AUTOSAVE: + return smart_auto_save(fd, select); + case IMMEDIATE_OFFLINE: + return smart_immediate_offline(fd, select); + case STATUS_CHECK: + return smart_status_check(fd); + default: + pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command); + exit(1); + break; + } +#else /* __sparc */ + // avoid gcc warnings// + fd=command=select=0; + data=NULL; + + /* Above smart_* routines uses undocumented ioctls of "dada" + * driver, which is specific to SPARC Solaris. x86 Solaris seems + * not to provide similar or alternative interface... */ + if (printwarning(0)) + return -1; +#endif + return -1; +} + +// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ + // avoid gcc warnings// + fd=disknum=escalade_type=command=select=0; + data=NULL; + + if (printwarning(1)) + return -1; + return -1; +} + +#include <errno.h> +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/generic/status.h> +#include <sys/scsi/impl/types.h> +#include <sys/scsi/impl/uscsi.h> + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + struct uscsi_cmd uscsi; + + if (report > 0) { + int k; + const unsigned char * ucp = iop->cmnd; + const char * np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < (int)iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + pout("]"); + } + + + memset(&uscsi, 0, sizeof (uscsi)); + + uscsi.uscsi_cdb = (void *)iop->cmnd; + uscsi.uscsi_cdblen = iop->cmnd_len; + if (iop->timeout == 0) + uscsi.uscsi_timeout = 60; /* XXX */ + else + uscsi.uscsi_timeout = iop->timeout; + uscsi.uscsi_bufaddr = (void *)iop->dxferp; + uscsi.uscsi_buflen = iop->dxfer_len; + uscsi.uscsi_rqbuf = (void *)iop->sensep; + uscsi.uscsi_rqlen = iop->max_sense_len; + + switch (iop->dxfer_dir) { + case DXFER_NONE: + case DXFER_FROM_DEVICE: + uscsi.uscsi_flags = USCSI_READ; + break; + case DXFER_TO_DEVICE: + uscsi.uscsi_flags = USCSI_WRITE; + break; + default: + return -EINVAL; + } + uscsi.uscsi_flags |= USCSI_ISOLATE; + + if (ioctl(fd, USCSICMD, &uscsi)) + return -errno; + + iop->scsi_status = uscsi.uscsi_status; + iop->resid = uscsi.uscsi_resid; + iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid; + + if (report > 0) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + pout(" status=0\n"); + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + + return (0); +} diff --git a/sm5/os_win32.c b/sm5/os_win32.c new file mode 100644 index 0000000000000000000000000000000000000000..96ef1f962dd29a5178a1e2c12229c1e3e1145b71 --- /dev/null +++ b/sm5/os_win32.c @@ -0,0 +1,1279 @@ +/* + * os_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 Christian Franke <smartmontools-support@lists.sourceforge.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. + * + */ + +#include "config.h" +#include "atacmds.h" +#include "extern.h" +extern smartmonctrl * con; // con->permissive +#include "int64.h" +#include "scsicmds.h" +#include "utility.h" +extern int64_t bytes; // malloc() byte count + +#include <errno.h> +#ifdef _DEBUG +#include <assert.h> +#else +#define assert(x) /**/ +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stddef.h> // offsetof() +#include <io.h> // access() + +#define ARGUSED(x) ((void)(x)) + +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_XXXX_c_cvsid="$Id: os_win32.c,v 1.17 2004/08/18 19:27:44 likewise Exp $" +ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + + +static int ata_open(int drive); +static void ata_close(int fd); +static unsigned ata_scan(void); + +static int aspi_open(unsigned adapter, unsigned id); +static void aspi_close(int fd); +static unsigned long aspi_scan(void); + + +static int is_permissive() +{ + if (con->permissive <= 0) { + pout("To continue, add one or more '-T permissive' options.\n"); + return 0; + } + con->permissive--; + return 1; +} + +static const char * skipdev(const char * s) +{ + return (!strncmp(s, "/dev/", 5) ? s + 5 : s); +} + + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char * dev_name) +{ + dev_name = skipdev(dev_name); + if (!strncmp(dev_name, "hd", 2)) + return CONTROLLER_ATA; + if (!strncmp(dev_name, "scsi", 4)) + return CONTROLLER_SCSI; + return CONTROLLER_UNKNOWN; +} + + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist), the +// others each contain null-terminated character strings. +int make_device_names (char*** devlist, const char* type) +{ + unsigned long drives; + int i, j, n, sz, scsi; + const char * path; + + if (!strcmp(type, "ATA")) { + // bit i set => drive i present + drives = ata_scan(); + path = "/dev/hda"; + scsi = 0; + } + else if (!strcmp(type, "SCSI")) { + // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present + drives = aspi_scan(); + path = "/dev/scsi00"; + scsi = 1; + } + else + return -1; + + if (!drives) + return 0; + + // Count #drives + n = 0; + for (i = 0; i < 32; i++) { + if (drives & (1 << i)) + n++; + } + assert(n > 0); + if (n == 0) + return 0; + + // Alloc devlist + assert(scsi || n <= 9); + sz = n * sizeof(char **); + *devlist = (char **)malloc(sz); bytes += sz; + + // Add devices + for (i = j = 0; i < n; i++) { + char * s; + sz = strlen(path)+1; + s = (char *)malloc(sz); bytes += sz; + strcpy(s, path); + while (j < 32 && !(drives & (1 << j))) + j++; + assert(j < 32); + if (!scsi) { + assert(j <= 9); + s[sz-2] += j; // /dev/hd[a-j] + } + else { + s[sz-3] += (j >> 3); // /dev/scsi[0-3]. + s[sz-2] += (j & 0x7); // .....[0-7] + } + (*devlist)[i] = s; + j++; + } + return n; +} + + +// Like open(). Return positive integer handle, only used by +// functions below. type="ATA" or "SCSI". If you need to store extra +// information about your devices, create a private internal array +// within this file (see os_freebsd.c for an example). +int deviceopen(const char * pathname, char *type) +{ + int len; + pathname = skipdev(pathname); + len = strlen(pathname); + + if (!strcmp(type, "ATA")) { + // hd[a-z] => ATA 0-9 + if (!(len == 3 && pathname[0] == 'h' && pathname[1] == 'd' + && 'a' <= pathname[2] && pathname[2] <= 'j')) { + errno = ENOENT; + return -1; + } + return ata_open(pathname[2] - 'a'); + } + + if (!strcmp(type, "SCSI")) { + // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0 + unsigned adapter = ~0, id = ~0; int n = -1; + if (!(sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len)) { + errno = ENOENT; + return -1; + } + return aspi_open(adapter, id); + } + errno = ENOENT; + return -1; +} + + +// Like close(). Acts only on handles returned by above function. +// (Never called in smartctl!) +int deviceclose(int fd) +{ + if (fd < 0x100) { + ata_close(fd); + } + else { + aspi_close(fd); + } + return 0; +} + + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n" + " smartctl -a /dev/hda (Prints all SMART information)\n\n" +#ifdef HAVE_GETOPT_LONG + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" +#else + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" +#endif + " smartctl -a /dev/scsi21\n" + " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n" + ); +} + + +///////////////////////////////////////////////////////////////////////////// +// ATA Interface +///////////////////////////////////////////////////////////////////////////// + +// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) + +// Deklarations from: +// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3 + +#define FILE_READ_ACCESS 0x0001 +#define FILE_WRITE_ACCESS 0x0002 +#define METHOD_BUFFERED 0 +#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) + +#define FILE_DEVICE_DISK 7 +#define IOCTL_DISK_BASE FILE_DEVICE_DISK + +#define SMART_GET_VERSION \ + CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define SMART_RCV_DRIVE_DATA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define SMART_SEND_DRIVE_COMMAND \ + CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define SMART_CYL_LOW 0x4F +#define SMART_CYL_HI 0xC2 + +#pragma pack(1) + +typedef struct _GETVERSIONOUTPARAMS { + UCHAR bVersion; + UCHAR bRevision; + UCHAR bReserved; + UCHAR bIDEDeviceMap; + ULONG fCapabilities; + ULONG dwReserved[4]; +} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS; + +typedef struct _IDEREGS { + UCHAR bFeaturesReg; + UCHAR bSectorCountReg; + UCHAR bSectorNumberReg; + UCHAR bCylLowReg; + UCHAR bCylHighReg; + UCHAR bDriveHeadReg; + UCHAR bCommandReg; + UCHAR bReserved; +} IDEREGS, *PIDEREGS, *LPIDEREGS; + +typedef struct _SENDCMDINPARAMS { + ULONG cBufferSize; + IDEREGS irDriveRegs; + UCHAR bDriveNumber; + UCHAR bReserved[3]; + ULONG dwReserved[4]; + UCHAR bBuffer[1]; +} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS; + +/* DRIVERSTATUS.bDriverError constants (just for info, not used) +#define SMART_NO_ERROR 0 +#define SMART_IDE_ERROR 1 +#define SMART_INVALID_FLAG 2 +#define SMART_INVALID_COMMAND 3 +#define SMART_INVALID_BUFFER 4 +#define SMART_INVALID_DRIVE 5 +#define SMART_INVALID_IOCTL 6 +#define SMART_ERROR_NO_MEM 7 +#define SMART_INVALID_REGISTER 8 +#define SMART_NOT_SUPPORTED 9 +#define SMART_NO_IDE_DEVICE 10 +*/ + +typedef struct _DRIVERSTATUS { + UCHAR bDriverError; + UCHAR bIDEError; + UCHAR bReserved[2]; + ULONG dwReserved[2]; +} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS; + +typedef struct _SENDCMDOUTPARAMS { + ULONG cBufferSize; + DRIVERSTATUS DriverStatus; + UCHAR bBuffer[1]; +} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS; + +#pragma pack() + + +///////////////////////////////////////////////////////////////////////////// + +static void print_ide_regs(const IDEREGS * r, int out) +{ + pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", + (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, + r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); +} + + +///////////////////////////////////////////////////////////////////////////// + +// call SMART_* ioctl + +static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize) +{ + SENDCMDINPARAMS inpar; + unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512]; + const SENDCMDOUTPARAMS * outpar; + DWORD code, num_out; + unsigned int size_out; + + assert(SMART_SEND_DRIVE_COMMAND == 0x07c084); + assert(SMART_RCV_DRIVE_DATA == 0x07c088); + assert(sizeof(SENDCMDINPARAMS)-1 == 32); + assert(sizeof(SENDCMDOUTPARAMS)-1 == 16); + + memset(&inpar, 0, sizeof(inpar)); + inpar.irDriveRegs = *regs; + // drive is set to 0-3 on Win9x only + inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4); + inpar.bDriveNumber = drive; + + assert(datasize == 0 || datasize == 512); + if (datasize) { + inpar.cBufferSize = size_out = 512; + code = SMART_RCV_DRIVE_DATA; + } + else if (regs->bFeaturesReg == ATA_SMART_STATUS) { + size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data + code = SMART_SEND_DRIVE_COMMAND; + } + else { + size_out = 0; + code = SMART_SEND_DRIVE_COMMAND; + } + + memset(&outbuf, 0, sizeof(outbuf)); + +#ifdef _DEBUG + pout("DeviceIoControl(.,0x%lx,.,%lu,.,%lu,.,NULL)\n", + code, sizeof(SENDCMDINPARAMS)-1, sizeof(SENDCMDOUTPARAMS)-1 + size_out); + print_ide_regs(&inpar.irDriveRegs, 0); +#endif + if (!DeviceIoControl(hdevice, code, + &inpar, sizeof(SENDCMDINPARAMS)-1, + outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, + &num_out, NULL)) { + // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface() + long err = GetLastError(); +#ifdef _DEBUG + pout("DeviceIoControl failed, Error=%ld\n", err); +#endif + errno = ( err == ERROR_INVALID_FUNCTION /*9x*/ + || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO); + return -1; + } + // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs + + outpar = (const SENDCMDOUTPARAMS *)outbuf; +#ifdef _DEBUG + pout("DeviceIoControl returns %lu (%lu) bytes\n", num_out, outpar->cBufferSize); +#endif + + if (outpar->DriverStatus.bDriverError) { + pout("Error SMART IOCTL DriverError=0x%02x, IDEError=0x%02x\n", + outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError); + errno = EIO; + return -1; + } + + if (datasize) + memcpy(data, outpar->bBuffer, 512); + else if (regs->bFeaturesReg == ATA_SMART_STATUS) { + *regs = *(const IDEREGS *)(outpar->bBuffer); +#ifdef _DEBUG + print_ide_regs(regs, 1); +#endif + } + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +// IDE PASS THROUGH for W2K/XP (does not work on W9x/NT4) +// Only used for SMART commands not supported by SMART_* IOCTLs +// +// Based on WinATA.cpp, 2002 c't/Matthias Withopf +// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip + +#define FILE_DEVICE_CONTROLLER 4 +#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER + +#define IOCTL_IDE_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#pragma pack(1) + +typedef struct { + IDEREGS IdeReg; + ULONG DataBufferSize; + UCHAR DataBuffer[1]; +} ATA_PASS_THROUGH; + +#pragma pack() + + +///////////////////////////////////////////////////////////////////////////// + +static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) +{ + unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize; + ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); + DWORD num_out; + assert(sizeof(ATA_PASS_THROUGH)-1 == 12); + assert(IOCTL_IDE_PASS_THROUGH == 0x04d028); + + buf->IdeReg = *regs; + buf->DataBufferSize = datasize; + +#ifdef _DEBUG + pout("DeviceIoControl(.,0x%x,.,%u,.,%u,.,NULL)\n", + IOCTL_IDE_PASS_THROUGH, size, size); + print_ide_regs(&buf->IdeReg, 0); +#endif + + if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH, + buf, size, buf, size, &num_out, NULL)) { + long err = GetLastError(); +#ifdef _DEBUG + pout("DeviceIoControl failed, Error=%ld\n", err); +#endif + VirtualFree(buf, size, MEM_RELEASE); + errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); + return -1; + } + +#ifdef _DEBUG + pout("DeviceIoControl returns %lu (%lu) bytes\n", num_out, buf->DataBufferSize); + print_ide_regs(&buf->IdeReg, 1); +#endif + + if (datasize) + memcpy(data, buf->DataBuffer, datasize); + VirtualFree(buf, size, MEM_RELEASE); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static HANDLE h_ata_ioctl = 0; +static int ide_pass_through_broken = 0; + + +// Print SMARTVSD error message, return errno + +static int smartvsd_error() +{ + char path[MAX_PATH]; + unsigned len; + if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2)) + return ENOENT; + strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD"); + if (!access(path, 0)) { +#ifdef _DEBUG + pout("Driver \"%s\" not loaded,\n" + " possibly no IDE/ATA devices present.\n", path); +#endif + return ENOENT; + } + // Some Windows versions install SMARTVSD.VXD in SYSTEM directory + // http://support.microsoft.com/default.aspx?scid=kb;en-us;265854 + strcpy(path+len, "\\SMARTVSD.VXD"); + if (!access(path, 0)) { + path[len] = 0; + pout("SMART driver is not properly installed,\n" + " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n" + " and reboot Windows.\n", path, path); + return ENOSYS; + } + path[len] = 0; + pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path); + return ENOENT; +} + + +static int ata_open(int drive) +{ + int win9x; + char devpath[30]; + GETVERSIONOUTPARAMS vers; + DWORD num_out; + + assert(SMART_GET_VERSION == 0x074080); + assert(sizeof(GETVERSIONOUTPARAMS) == 24); + + // TODO: This version does not allow to open more than 1 ATA devices + if (h_ata_ioctl) { + errno = ENFILE; + return -1; + } + + win9x = ((GetVersion() & 0x80000000) != 0); + + if (!(0 <= drive && drive <= (win9x ? 3 : 9))) { + errno = ENOENT; + return -1; + } + // path depends on Windows Version + if (win9x) + strcpy(devpath, "\\\\.\\SMARTVSD"); + else + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive); + + // Open device + if ((h_ata_ioctl = CreateFileA(devpath, + GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { + long err = GetLastError(); + pout("Cannot open device %s, Error=%ld\n", devpath, err); + if (win9x && err == ERROR_FILE_NOT_FOUND) + errno = smartvsd_error(); + else + errno = (err == ERROR_FILE_NOT_FOUND ? ENOENT : EPERM); + h_ata_ioctl = 0; + return -1; + } + + // Get drive map + memset(&vers, 0, sizeof(vers)); + if (!DeviceIoControl(h_ata_ioctl, SMART_GET_VERSION, + NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { + pout("%s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError()); + if (!win9x) + pout("If this is a SCSI disk, try \"scsi<adapter><id>\".\n"); + if (!is_permissive()) { + CloseHandle(h_ata_ioctl); h_ata_ioctl = 0; + errno = ENOSYS; + return -1; + } + } + +#ifdef _DEBUG + pout("SMART_GET_VERSION (%ld bytes): Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n", + num_out, vers.bVersion, vers.bRevision, vers.fCapabilities, vers.bIDEDeviceMap); +#endif + + // TODO: Check vers.fCapabilities here? + + if (!win9x) + // NT4/2K/XP: Drive exists, Drive number not necessary for ioctl + return 0; + + // Win9x/ME: Check device presence & type + if (((vers.bIDEDeviceMap >> drive) & 0x11) != 0x01) { + unsigned char atapi = (vers.bIDEDeviceMap >> drive) & 0x10; + pout(( atapi + ? "Drive %d is an ATAPI device (IDEDeviceMap=0x%02x).\n" + : "Drive %d does not exist (IDEDeviceMap=0x%02x).\n"), + drive, vers.bIDEDeviceMap); + // Win9x Drive existence check may not work as expected + // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01 + // http://support.microsoft.com/support/kb/articles/Q196/1/20.ASP + if (!is_permissive()) { + CloseHandle(h_ata_ioctl); h_ata_ioctl = 0; + errno = (atapi ? ENOSYS : ENOENT); + return -1; + } + } + // Use drive number as fd for ioctl + return drive; +} + + +static void ata_close(int fd) +{ + ARGUSED(fd); + CloseHandle(h_ata_ioctl); + h_ata_ioctl = 0; +} + + +// Scan for ATA drives, return bitmask of drives present + +static unsigned ata_scan() +{ + unsigned drives = 0; + int win9x = ((GetVersion() & 0x80000000) != 0); + int i; + + for (i = 0; i <= 9; i++) { + char devpath[30]; + GETVERSIONOUTPARAMS vers; + DWORD num_out; + HANDLE h; + if (win9x) + strcpy(devpath, "\\\\.\\SMARTVSD"); + else + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i); + + // Open device + if ((h = CreateFileA(devpath, + GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { + if (win9x) + break; // SMARTVSD.VXD missing or no ATA devices + continue; // Disk not found or access denied (break;?) + } + + // Get drive map + memset(&vers, 0, sizeof(vers)); + if (!DeviceIoControl(h, SMART_GET_VERSION, + NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { + CloseHandle(h); + if (win9x) + break; // Should not happen + continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk) + } + CloseHandle(h); + + if (win9x) { + // Check ATA device presence, remove ATAPI devices + drives = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf); + break; + } + + // ATA drive exists and driver supports SMART ioctl + drives |= 1 << i; + } + + return drives; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Interface to ATA devices. See os_linux.c +int marvell_command_interface(int fd, smart_command_set command, int select, char * data) +{ return -1; } + +int ata_command_interface(int fd, smart_command_set command, int select, char * data) +{ + IDEREGS regs; + int copydata; + + if (!(0 <= fd && fd <= 3)) { + errno = EBADF; + return -1; + } + + // CMD,CYL default to SMART, changed by P?IDENTIFY + memset(®s, 0, sizeof(regs)); + regs.bCommandReg = ATA_SMART_CMD; + regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW; + copydata = 0; + + switch (command) { + case CHECK_POWER_MODE: + case WRITE_LOG: + // TODO. Not supported by SMART IOCTL + errno = ENOSYS; + return -1; + case READ_VALUES: + regs.bFeaturesReg = ATA_SMART_READ_VALUES; + regs.bSectorNumberReg = regs.bSectorCountReg = 1; + copydata = 1; + break; + case READ_THRESHOLDS: + regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS; + regs.bSectorNumberReg = regs.bSectorCountReg = 1; + copydata = 1; + break; + case READ_LOG: + regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR; + regs.bSectorNumberReg = select; + regs.bSectorCountReg = 1; + copydata = 1; + break; + case IDENTIFY: + regs.bCommandReg = ATA_IDENTIFY_DEVICE; + regs.bCylLowReg = regs.bCylHighReg = 0; + regs.bSectorCountReg = 1; + copydata = 1; + break; + case PIDENTIFY: + regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE; + regs.bCylLowReg = regs.bCylHighReg = 0; + regs.bSectorCountReg = 1; + copydata = 1; + break; + case ENABLE: + regs.bFeaturesReg = ATA_SMART_ENABLE; + regs.bSectorNumberReg = 1; + break; + case DISABLE: + regs.bFeaturesReg = ATA_SMART_DISABLE; + regs.bSectorNumberReg = 1; + break; + case STATUS: + case STATUS_CHECK: + regs.bFeaturesReg = ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE; + regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + regs.bFeaturesReg = ATA_SMART_AUTOSAVE; + regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE; + regs.bSectorNumberReg = select; + break; + default: + pout("Unrecognized command %d in win32_ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno = ENOSYS; + return -1; + } + + if (smart_ioctl(h_ata_ioctl, fd, ®s, data, (copydata?512:0))) { + // Read log only supported on Win9x, retry with pass through command + // CAUTION: smart_ioctl() MUST NOT change "regs" Parameter in this case + if (errno == ENOSYS && command == READ_LOG && !ide_pass_through_broken) { + errno = 0; + memset(data, 0, 512); + if (ide_pass_through_ioctl(h_ata_ioctl, ®s, data, 512)) + return -1; + if (!nonempty(data, 512)) { + // Nothing useful returned => ioctl probably broken + pout("IOCTL_IDE_PASS_THROUGH does not work on your version of Windows\n"); + ide_pass_through_broken = 1; // Do not retry (smartd) + errno = ENOSYS; + return -1; + } + return 0; + } + return -1; + } + + if (command == STATUS_CHECK) { + // Cyl low and Cyl high unchanged means "Good SMART status" + if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW) + return 0; + + // These values mean "Bad SMART status" + if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4) + 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", PACKAGE_HOMEPAGE); + print_ide_regs(®s, 1); + errno = EIO; + return -1; + } + + return 0; +} + + +// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) +{ + static int warned = 0; + ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data); + if (!warned) { + pout( + "#######################################################################\n" + "3ware Escalade Controller command routine escalade_command_interface()\n" + "NOT IMPLEMENTED under Win32.\n" + "Please contact " PACKAGE_BUGREPORT " if\n" + "you want to help in porting smartmontools to Win32.\n" + "#######################################################################\n" + "\n"); + warned = 1; + } + errno = ENOSYS; + return -1; +} + + +///////////////////////////////////////////////////////////////////////////// +// ASPI Interface +///////////////////////////////////////////////////////////////////////////// + +#pragma pack(1) + +#define ASPI_SENSE_SIZE 18 + +// ASPI SCSI Request block header + +typedef struct { + unsigned char cmd; // 00: Command code + unsigned char status; // 01: ASPI status + unsigned char adapter; // 02: Host adapter number + unsigned char flags; // 03: Request flags + unsigned char reserved[4]; // 04: 0 +} ASPI_SRB_HEAD; + +// SRB for host adapter inquiry + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char adapters; // 08: Number of adapters + unsigned char target_id; // 09: Target ID ? + char manager_id[16]; // 10: SCSI manager ID + char adapter_id[16]; // 26: Host adapter ID + unsigned char parameters[16]; // 42: Host adapter unique parmameters +} ASPI_SRB_INQUIRY; + +// SRB for get device type + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char target_id; // 08: Target ID + unsigned char lun; // 09: LUN + unsigned char devtype; // 10: Device type + unsigned char reserved; // 11: Reserved +} ASPI_SRB_DEVTYPE; + +// SRB for SCSI I/O + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char target_id; // 08: Target ID + unsigned char lun; // 09: LUN + unsigned char reserved[2]; // 10: Reserved + unsigned long data_size; // 12: Data alloc. lenght + void * data_addr; // 16: Data buffer pointer + unsigned char sense_size; // 20: Sense alloc. length + unsigned char cdb_size; // 21: CDB length + unsigned char host_status; // 22: Host status + unsigned char target_status; // 23: Target status + void * event_handle; // 24: Event handle + unsigned char workspace[20]; // 28: ASPI workspace + unsigned char cdb[16+ASPI_SENSE_SIZE]; +} ASPI_SRB_IO; + +// Macro to retrieve start of sense information +#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16) + +// SRB union + +typedef union { + ASPI_SRB_HEAD h; // Common header + ASPI_SRB_INQUIRY q; // Inquiry + ASPI_SRB_DEVTYPE t; // Device type + ASPI_SRB_IO i; // I/O +} ASPI_SRB; + +#pragma pack() + +// ASPI commands +#define ASPI_CMD_ADAPTER_INQUIRE 0x00 +#define ASPI_CMD_GET_DEVICE_TYPE 0x01 +#define ASPI_CMD_EXECUTE_IO 0x02 +#define ASPI_CMD_ABORT_IO 0x03 + +// Request flags +#define ASPI_REQFLAG_DIR_TO_HOST 0x08 +#define ASPI_REQFLAG_DIR_TO_TARGET 0x10 +#define ASPI_REQFLAG_DIR_NO_XFER 0x18 +#define ASPI_REQFLAG_EVENT_NOTIFY 0x40 + +// ASPI status +#define ASPI_STATUS_IN_PROGRESS 0x00 +#define ASPI_STATUS_NO_ERROR 0x01 +#define ASPI_STATUS_ABORTED 0x02 +#define ASPI_STATUS_ABORT_ERR 0x03 +#define ASPI_STATUS_ERROR 0x04 +#define ASPI_STATUS_INVALID_COMMAND 0x80 +#define ASPI_STATUS_INVALID_ADAPTER 0x81 +#define ASPI_STATUS_INVALID_TARGET 0x82 +#define ASPI_STATUS_NO_ADAPTERS 0xE8 + +// Adapter (host) status +#define ASPI_HSTATUS_NO_ERROR 0x00 +#define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11 +#define ASPI_HSTATUS_DATA_OVERRUN 0x12 +#define ASPI_HSTATUS_BUS_FREE 0x13 +#define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14 +#define ASPI_HSTATUS_BAD_SGLIST 0x1A + +// Target status +#define ASPI_TSTATUS_NO_ERROR 0x00 +#define ASPI_TSTATUS_CHECK_CONDITION 0x02 +#define ASPI_TSTATUS_BUSY 0x08 +#define ASPI_TSTATUS_RESERV_CONFLICT 0x18 + + +static HINSTANCE h_aspi_dll; // DLL handle +static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint +static unsigned num_aspi_adapters; + +#ifdef __CYGWIN__ +// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork() +static DWORD aspi_dll_pid; // PID of DLL owner to detect fork() +#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId())) +#else +#define aspi_entry_valid() (!!aspi_entry) +#endif + + +static int aspi_call(ASPI_SRB * srb) +{ + int i; + aspi_entry(srb); + i = 0; + while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) { + if (++i > 100/*10sek*/) { + pout("ASPI Adapter %u: Timeout\n", srb->h.adapter); + aspi_entry = 0; + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = EIO; + return -1; + } +#ifdef _DEBUG + pout("ASPI Wait %d\n", i); +#endif + Sleep(100); + } + return 0; +} + + +// Get ASPI entrypoint from wnaspi32.dll + +static FARPROC aspi_get_address(const char * name, int verbose) +{ + FARPROC addr; + assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE); + + if (!(addr = GetProcAddress(h_aspi_dll, name))) { + if (verbose) + pout("Missing %s() in WNASPI32.DLL\n", name); + aspi_entry = 0; + FreeLibrary(h_aspi_dll); + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = ENOSYS; + return 0; + } + return addr; +} + + +static int aspi_open_dll(int verbose) +{ + UINT (*aspi_info)(void); + UINT info, rc; + + assert(!aspi_entry_valid()); + + // Check structure layout + assert(sizeof(ASPI_SRB_HEAD) == 8); + assert(sizeof(ASPI_SRB_INQUIRY) == 58); + assert(sizeof(ASPI_SRB_DEVTYPE) == 12); + assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE); + assert(offsetof(ASPI_SRB,h.cmd) == 0); + assert(offsetof(ASPI_SRB,h.flags) == 3); + assert(offsetof(ASPI_SRB_IO,lun) == 9); + assert(offsetof(ASPI_SRB_IO,data_addr) == 16); + assert(offsetof(ASPI_SRB_IO,workspace) == 28); + assert(offsetof(ASPI_SRB_IO,cdb) == 48); + + if (h_aspi_dll == INVALID_HANDLE_VALUE) { + // do not retry + errno = ENOENT; + return -1; + } + + // Load ASPI DLL + if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) { + if (verbose) + pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError()); + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = ENOENT; + return -1; + } + + // Get ASPI entrypoints + if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose))) + return -1; + if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose))) + return -1; + + // Init ASPI manager and get number of adapters + info = (aspi_info)(); +#ifdef _DEBUG + pout("GetASPI32SupportInfo() returns 0x%04x\n", info); +#endif + rc = (info >> 8) & 0xff; + if (rc == ASPI_STATUS_NO_ADAPTERS) { + num_aspi_adapters = 0; + } + else if (rc == ASPI_STATUS_NO_ERROR) { + num_aspi_adapters = info & 0xff; + } + else { + if (verbose) + pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info); + aspi_entry = 0; + FreeLibrary(h_aspi_dll); + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = ENOENT; + return -1; + } + +#ifdef __CYGWIN__ + // save PID to detect fork() in aspi_entry_valid() + aspi_dll_pid = GetCurrentProcessId(); +#endif + assert(aspi_entry_valid()); + return 0; +} + + +static int aspi_io_call(ASPI_SRB * srb) +{ + HANDLE event; + // Create event + if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) { + pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO; + } + srb->i.event_handle = event; + srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY; + // Start ASPI request + aspi_entry(srb); + if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) { + // Wait for event + DWORD rc = WaitForSingleObject(event, 30*1000L); + if (rc != WAIT_OBJECT_0) { + if (rc == WAIT_TIMEOUT) { + pout("ASPI Timeout\n"); + } + else { + pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n", + (unsigned long)event, rc, rc, GetLastError()); + } + // TODO: ASPI_ABORT_IO command + aspi_entry = 0; + h_aspi_dll = INVALID_HANDLE_VALUE; + return -EIO; + } + } + CloseHandle(event); + return 0; +} + + +static int aspi_open(unsigned adapter, unsigned id) +{ + if (!(adapter <= 9 && id < 16)) { + errno = ENOENT; + return -1; + } + + if (!aspi_entry_valid()) { + if (aspi_open_dll(1/*verbose*/)) + return -1; + } + + // Adapter OK? + if (adapter >= num_aspi_adapters) { + pout("ASPI Adapter %d does not exist (%d Adapter(s) detected).\n", adapter, num_aspi_adapters); + if (!is_permissive()) { + errno = ENOENT; + return -1; + } + } + + return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf)); +} + + +static void aspi_close(int fd) +{ + // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads + ARGUSED(fd); +} + + +// Scan for SCSI drives, return bitmask [adapter:0-3][id:0-7] of drives present + +static unsigned long aspi_scan() +{ + unsigned long drives = 0; + unsigned ad, nad; + + if (!aspi_entry_valid()) { + if (aspi_open_dll(0/*quiet*/)) + return 0; + } + + nad = num_aspi_adapters; + if (nad >= 4) + nad = 4; + for (ad = 0; ad < nad; ad++) { + ASPI_SRB srb; int id; + // Get adapter name + memset(&srb, 0, sizeof(srb)); + srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE; + srb.h.adapter = ad; + if (aspi_call(&srb)) + return 0; +#ifdef _DEBUG + pout("ASPI Adapter %u: %02x,\"%.16s\"\n", ad, srb.h.status, srb.q.adapter_id); +#endif + if (srb.h.status != ASPI_STATUS_NO_ERROR) + continue; + + // Skip ATA/ATAPI devices + srb.q.adapter_id[sizeof(srb.q.adapter_id)-1] = 0; + if (strstr(srb.q.adapter_id, "ATAPI")) + continue; + + for (id = 0; id <= 7; id++) { + // Get device type + memset(&srb, 0, sizeof(srb)); + srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE; + srb.h.adapter = ad; srb.i.target_id = id; + if (aspi_call(&srb)) + return 0; +#ifdef _DEBUG + pout("Device type for scsi%u%x: %02x,%02x\n", ad, id, srb.h.status, srb.t.devtype); +#endif + if (srb.h.status == ASPI_STATUS_NO_ERROR && srb.t.devtype == 0x00/*HDD*/) + drives |= 1 << ((ad<<3)+id); + } + } + return drives; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + ASPI_SRB srb; + + if (!aspi_entry_valid()) + return -EBADF; + if (!((fd & ~0xff) == 0x100)) + return -EBADF; + + if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12)) { + pout("do_scsi_cmnd_io: bad CDB length\n"); + return -EINVAL; + } + + if (report > 0) { + // From os_linux.c + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + + memset(&srb, 0, sizeof(srb)); + srb.h.cmd = ASPI_CMD_EXECUTE_IO; + srb.h.adapter = ((fd >> 4) & 0xf); + srb.i.target_id = (fd & 0xf); + //srb.i.lun = 0; + srb.i.sense_size = ASPI_SENSE_SIZE; + srb.i.cdb_size = iop->cmnd_len; + memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len); + + switch (iop->dxfer_dir) { + case DXFER_NONE: + srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER; + break; + case DXFER_FROM_DEVICE: + srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST; + srb.i.data_size = iop->dxfer_len; + srb.i.data_addr = iop->dxferp; + break; + case DXFER_TO_DEVICE: + srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET; + srb.i.data_size = iop->dxfer_len; + srb.i.data_addr = iop->dxferp; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + + if (aspi_io_call(&srb)) { + // Timeout + return -EIO; + } + + if (srb.h.status != ASPI_STATUS_NO_ERROR) { + if ( srb.h.status == ASPI_STATUS_ERROR + && srb.i.host_status == ASPI_HSTATUS_NO_ERROR + && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) { + // Sense valid + const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len); + int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len); + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + if (len > 0 && iop->sensep) { + memcpy(iop->sensep, sense, len); + iop->resp_sense_len = len; + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", (int)len); + dStrHex(iop->sensep, len , 1); + } + } + if (report) { + pout(" sense_key=%x asc=%x ascq=%x\n", + sense[2] & 0xf, sense[12], sense[13]); + } + return 0; + } + else { + if (report) + pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status); + return -EIO; + } + } + + if (report > 0) + pout(" OK\n"); + + if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + + return 0; +} diff --git a/sm5/os_win32/daemon_win32.c b/sm5/os_win32/daemon_win32.c new file mode 100644 index 0000000000000000000000000000000000000000..c54caf75094c8c0c44956b167ba0c1049731eb39 --- /dev/null +++ b/sm5/os_win32/daemon_win32.c @@ -0,0 +1,1168 @@ +/* + * os_win32/daemon_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 Christian Franke <smartmontools-support@lists.sourceforge.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. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <io.h> + +#define WIN32_LEAN_AND_MEAN +// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP) +#define _WIN32_WINNT 0x0400 +#include <windows.h> +#ifdef _DEBUG +#include <crtdbg.h> +#endif + +#include "daemon_win32.h" + +const char *daemon_win32_c_cvsid = "$Id: daemon_win32.c,v 1.5 2004/08/09 14:35:59 chrfranke Exp $" +DAEMON_WIN32_H_CVSID; + + +///////////////////////////////////////////////////////////////////////////// + +#define ARGUSED(x) ((void)(x)) + +// Prevent spawning of child process if debugging +#ifdef _DEBUG +#define debugging() IsDebuggerPresent() +#else +#define debugging() FALSE +#endif + + +#define EVT_NAME_LEN 260 + +// Internal events (must be > SIGUSRn) +#define EVT_RUNNING 100 // Exists when running, signaled on creation +#define EVT_DETACHED 101 // Signaled when child detaches from console +#define EVT_RESTART 102 // Signaled when child should restart + +static void make_name(char * name, int sig) +{ + int i; + if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10)) + strcpy(name, "DaemonEvent"); + for (i = 0; name[i]; i++) { + char c = name[i]; + if (!( ('0' <= c && c <= '9') + || ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z'))) + name[i] = '_'; + } + sprintf(name+strlen(name), "-%d", sig); +} + + +static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + make_name(name, sig); + if (exists) + *exists = FALSE; + if (!(h = CreateEventA(NULL, FALSE, initial, name))) { + if (errmsg) + fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError()); + return 0; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) { + if (!exists) { + if (errmsg) + fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name); + CloseHandle(h); + return 0; + } + *exists = TRUE; + } + return h; +} + + +static HANDLE open_event(int sig) +{ + char name[EVT_NAME_LEN]; + make_name(name, sig); + return OpenEventA(EVENT_MODIFY_STATE, FALSE, name); +} + + +static int event_exists(int sig) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + make_name(name, sig); + if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) + return 0; + CloseHandle(h); + return 1; +} + + +static int sig_event(int sig) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + make_name(name, sig); + if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) { + make_name(name, EVT_RUNNING); + if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name))) + return -1; + CloseHandle(h); + return 0; + } + SetEvent(h); + CloseHandle(h); + return 1; +} + + +static void daemon_help(FILE * f, const char * ident, const char * message) +{ + fprintf(f, + "%s: %s.\n" + "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", + ident, message, ident); + fflush(f); +} + + +///////////////////////////////////////////////////////////////////////////// +// Parent Process + + +static BOOL WINAPI parent_console_handler(DWORD event) +{ + switch (event) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TRUE; // Ignore + } + return FALSE; // continue with next handler ... +} + + +static int parent_main(HANDLE rev) +{ + HANDLE dev; + HANDLE ht[2]; + char * cmdline; + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD rc, exitcode; + + // Ignore ^C, ^BREAK in parent + SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/); + + // Create event used by child to signal daemon_detach() + if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) { + CloseHandle(rev); + return 101; + } + + // Restart process with same args + cmdline = GetCommandLineA(); + memset(&si, 0, sizeof(si)); si.cb = sizeof(si); + + if (!CreateProcessA( + NULL, cmdline, + NULL, NULL, TRUE/*inherit*/, + 0, NULL, NULL, &si, &pi)) { + fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); + CloseHandle(rev); CloseHandle(dev); + return 101; + } + CloseHandle(pi.hThread); + + // Wait for daemon_detach() or exit() + ht[0] = dev; ht[1] = pi.hProcess; + rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE); + if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) { + fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc); + TerminateProcess(pi.hProcess, 200); + } + CloseHandle(rev); CloseHandle(dev); + + // Get exit code + if (!GetExitCodeProcess(pi.hProcess, &exitcode)) + exitcode = 201; + else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK + exitcode = 0; + + CloseHandle(pi.hProcess); + return exitcode; +} + + +///////////////////////////////////////////////////////////////////////////// +// Child Process + + +static int svc_mode; // Running as service? +static int svc_paused; // Service paused? + +static void service_report_status(int state, int waithint); + + +// Tables of signal handler and corresponding events +typedef void (*sigfunc_t)(int); + +#define MAX_SIG_HANDLERS 8 + +static int num_sig_handlers = 0; +static sigfunc_t sig_handlers[MAX_SIG_HANDLERS]; +static int sig_numbers[MAX_SIG_HANDLERS]; +static HANDLE sig_events[MAX_SIG_HANDLERS]; + +static HANDLE sigint_handle, sigbreak_handle, sigterm_handle; + +static HANDLE running_event; + +static int reopen_stdin, reopen_stdout, reopen_stderr; + + +// Handler for windows console events + +static BOOL WINAPI child_console_handler(DWORD event) +{ + // Caution: runs in a new thread + // TODO: Guard with a mutex + HANDLE h = 0; + switch (event) { + case CTRL_C_EVENT: // <CONTROL-C> (SIGINT) + h = sigint_handle; break; + case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT) + case CTRL_CLOSE_EVENT: // User closed console or abort via task manager + h = sigbreak_handle; break; + case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM) + case CTRL_SHUTDOWN_EVENT: + h = sigterm_handle; break; + } + if (!h) + return FALSE; // continue with next handler + // Signal event + if (!SetEvent(h)) + return FALSE; + return TRUE; +} + + +static void child_exit(void) +{ + int i; + char * cmdline; + HANDLE rst; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + for (i = 0; i < num_sig_handlers; i++) + CloseHandle(sig_events[i]); + num_sig_handlers = 0; + CloseHandle(running_event); running_event = 0; + + // Restart? + if (!(rst = open_event(EVT_RESTART))) + return; // No => normal exit + + // Yes => Signal exit and restart process + Sleep(500); + SetEvent(rst); + CloseHandle(rst); + Sleep(500); + + cmdline = GetCommandLineA(); + memset(&si, 0, sizeof(si)); si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; + + if (!CreateProcessA( + NULL, cmdline, + NULL, NULL, TRUE/*inherit*/, + 0, NULL, NULL, &si, &pi)) { + fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); + } + CloseHandle(pi.hThread); CloseHandle(pi.hProcess); +} + +static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv) +{ + // Keep EVT_RUNNING open until exit + running_event = hev; + + // Install console handler + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); + + // Install restart handler + atexit(child_exit); + + // Continue in main_func() to do the real work + return main_func(argc, argv); +} + + +// Simulate signal() + +sigfunc_t daemon_signal(int sig, sigfunc_t func) +{ + int i; + HANDLE h; + if (func == SIG_DFL || func == SIG_IGN) + return func; // TODO + for (i = 0; i < num_sig_handlers; i++) { + if (sig_numbers[i] == sig) { + sigfunc_t old = sig_handlers[i]; + sig_handlers[i] = func; + return old; + } + } + if (num_sig_handlers >= MAX_SIG_HANDLERS) + return SIG_ERR; + if (!(h = create_event(sig, FALSE, TRUE, NULL))) + return SIG_ERR; + sig_events[num_sig_handlers] = h; + sig_numbers[num_sig_handlers] = sig; + sig_handlers[num_sig_handlers] = func; + switch (sig) { + case SIGINT: sigint_handle = h; break; + case SIGTERM: sigterm_handle = h; break; + case SIGBREAK: sigbreak_handle = h; break; + } + num_sig_handlers++; + return SIG_DFL; +} + + +// strsignal() + +const char * daemon_strsignal(int sig) +{ + switch (sig) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGTERM: return "SIGTERM"; + case SIGBREAK:return "SIGBREAK"; + case SIGUSR1: return "SIGUSR1"; + case SIGUSR2: return "SIGUSR2"; + default: return "*UNKNOWN*"; + } +} + + +// Simulate sleep() + +void daemon_sleep(int seconds) +{ + do { + if (num_sig_handlers <= 0) { + Sleep(seconds*1000L); + } + else { + // Wait for any signal or timeout + DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events, + FALSE/*OR*/, seconds*1000L); + if (rc != WAIT_TIMEOUT) { + if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) { + fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc); + Sleep(seconds*1000L); + return; + } + // Call Handler + sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]); + break; + } + } + } while (svc_paused); +} + + +// Disable/Enable console + +void daemon_disable_console() +{ + SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); + reopen_stdin = reopen_stdout = reopen_stderr = 0; + if (isatty(fileno(stdin))) { + fclose(stdin); reopen_stdin = 1; + } + if (isatty(fileno(stdout))) { + fclose(stdout); reopen_stdout = 1; + } + if (isatty(fileno(stderr))) { + fclose(stderr); reopen_stderr = 1; + } + FreeConsole(); + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); +} + +int daemon_enable_console(const char * title) +{ + BOOL ok; + SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); + ok = AllocConsole(); + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); + if (!ok) + return -1; + if (title) + SetConsoleTitleA(title); + if (reopen_stdin) + freopen("conin$", "r", stdin); + if (reopen_stdout) + freopen("conout$", "w", stdout); + if (reopen_stderr) + freopen("conout$", "w", stderr); + reopen_stdin = reopen_stdout = reopen_stderr = 0; + return 0; +} + + +// Detach daemon from console & parent + +int daemon_detach(const char * ident) +{ + if (!svc_mode) { + if (ident) { + // Print help + FILE * f = ( isatty(fileno(stdout)) ? stdout + : isatty(fileno(stderr)) ? stderr : NULL); + if (f) + daemon_help(f, ident, "now detaches from console into background mode"); + } + // Signal detach to parent + if (sig_event(EVT_DETACHED) != 1) { + if (!debugging()) + return -1; + } + daemon_disable_console(); + } + else { + // Signal end of initialization to service control manager + service_report_status(SERVICE_RUNNING, 0); + } + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// +// MessageBox + +#ifndef _MT +//MT runtime not necessary, because mbox_thread uses no unsafe lib functions +//#error Program must be linked with multithreaded runtime library +#endif + +static LONG mbox_count; // # mbox_thread()s +static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service) + +typedef struct mbox_args_s { + HANDLE taken; const char * title, * text; int mode; +} mbox_args; + + +// Thread to display one message box + +static ULONG WINAPI mbox_thread(LPVOID arg) +{ + // Take args + mbox_args * mb = (mbox_args *)arg; + char title[100]; char text[1000]; int mode; + strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0; + strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0; + mode = mb->mode; + SetEvent(mb->taken); + + // Show only one box at a time + WaitForSingleObject(mbox_mutex, INFINITE); + MessageBoxA(NULL, text, title, mode); + ReleaseMutex(mbox_mutex); + + InterlockedDecrement(&mbox_count); + return 0; +} + + +// Display a message box +int daemon_messagebox(int system, const char * title, const char * text) +{ + mbox_args mb; + HANDLE ht; DWORD tid; + + // Create mutex during first call + if (!mbox_mutex) + mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/); + + // Allow at most 10 threads + if (InterlockedIncrement(&mbox_count) > 10) { + InterlockedDecrement(&mbox_count); + return -1; + } + + // Create thread + mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/); + mb.mode = MB_OK|MB_ICONWARNING + |(svc_mode?MB_SERVICE_NOTIFICATION:0) + |(system?MB_SYSTEMMODAL:MB_APPLMODAL); + mb.title = title; mb.text = text; + mb.text = text; + if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid))) + return -1; + + // Wait for args taken + if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0) + TerminateThread(ht, 0); + CloseHandle(mb.taken); + CloseHandle(ht); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Spawn a command and redirect <inpbuf >outbuf +// return command's exitcode or -1 on error + +int daemon_spawn(const char * cmd, + const char * inpbuf, int inpsize, + char * outbuf, int outsize ) +{ + HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h; + DWORD num_io, exitcode; int i; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; PROCESS_INFORMATION pi; + HANDLE self = GetCurrentProcess(); + + // Create stdin pipe with inheritable read side + memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + if (!CreatePipe(&pipe_inp_r, &h, &sa, inpsize*2+13)) + return -1; + if (!DuplicateHandle(self, h, self, &pipe_inp_w, + 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS)) { + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + CloseHandle(h); + + // Create stdout pipe with inheritable write side + memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + if (!CreatePipe(&h, &pipe_out_w, &sa, outsize)) { + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + if (!DuplicateHandle(self, h, self, &pipe_out_r, + 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS)) { + CloseHandle(h); CloseHandle(pipe_out_w); + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + CloseHandle(h); + + // Create stderr handle as dup of stdout write side + if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w, + 0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) { + CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + + // Create process with pipes as stdio + memset(&si, 0, sizeof(si)); si.cb = sizeof(si); + si.hStdInput = pipe_inp_r; + si.hStdOutput = pipe_out_w; + si.hStdError = pipe_err_w; + si.dwFlags = STARTF_USESTDHANDLES; + if (!CreateProcessA( + NULL, (char*)cmd, + NULL, NULL, TRUE/*inherit*/, + DETACHED_PROCESS/*no new console*/, + NULL, NULL, &si, &pi)) { + CloseHandle(pipe_err_w); + CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + CloseHandle(pi.hThread); + // Close inherited handles + CloseHandle(pipe_inp_r); + CloseHandle(pipe_out_w); + CloseHandle(pipe_err_w); + + // Copy inpbuf to stdin + // convert \n => \r\n + for (i = 0; i < inpsize; ) { + int len = 0; + while (i+len < inpsize && inpbuf[i+len] != '\n') + len++; + if (len > 0) + WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL); + i += len; + if (i < inpsize) { + WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL); + i++; + } + } + CloseHandle(pipe_inp_w); + + // Copy stdout to output buffer until full, rest to /dev/null + // convert \r\n => \n + for (i = 0; ; ) { + char buf[256]; + int j; + if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0) + break; + for (j = 0; i < outsize-1 && j < (int)num_io; j++) { + if (buf[j] != '\r') + outbuf[i++] = buf[j]; + } + } + outbuf[i] = 0; + CloseHandle(pipe_out_r); + + // Wait for process exitcode + WaitForSingleObject(pi.hProcess, INFINITE); + exitcode = 42; + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pi.hProcess); + return exitcode; +} + + +///////////////////////////////////////////////////////////////////////////// +// Initd Functions + +static int wait_signaled(HANDLE h, int seconds) +{ + int i; + for (i = 0; ; ) { + if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) + return 0; + if (++i >= seconds) + return -1; + fputchar('.'); fflush(stdout); + } +} + + +static int wait_evt_running(int seconds, int exists) +{ + int i; + if (event_exists(EVT_RUNNING) == exists) + return 0; + for (i = 0; ; ) { + Sleep(1000); + if (event_exists(EVT_RUNNING) == exists) + return 0; + if (++i >= seconds) + return -1; + fputchar('.'); fflush(stdout); + } +} + + +static int is_initd_command(char * s) +{ + if (!strcmp(s, "status")) + return EVT_RUNNING; + if (!strcmp(s, "stop")) + return SIGTERM; + if (!strcmp(s, "reload")) + return SIGHUP; + if (!strcmp(s, "sigusr1")) + return SIGUSR1; + if (!strcmp(s, "sigusr2")) + return SIGUSR2; + if (!strcmp(s, "restart")) + return EVT_RESTART; + return -1; +} + + +static int initd_main(const char * ident, int argc, char **argv) +{ + int rc; + if (argc < 2) + return -1; + if ((rc = is_initd_command(argv[1])) < 0) + return -1; + if (argc != 2) { + printf("%s: no arguments allowed for command %s\n", ident, argv[1]); + return 1; + } + + switch (rc) { + default: + case EVT_RUNNING: + printf("Checking for %s:", ident); fflush(stdout); + rc = event_exists(EVT_RUNNING); + puts(rc ? " running" : " not running"); + return (rc ? 0 : 1); + + case SIGTERM: + printf("Stopping %s:", ident); fflush(stdout); + rc = sig_event(SIGTERM); + if (rc <= 0) { + puts(rc < 0 ? " not running" : " error"); + return (rc < 0 ? 0 : 1); + } + rc = wait_evt_running(10, 0); + puts(!rc ? " done" : " timeout"); + return (!rc ? 0 : 1); + + case SIGHUP: + printf("Reloading %s:", ident); fflush(stdout); + rc = sig_event(SIGHUP); + puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); + return (rc > 0 ? 0 : 1); + + case SIGUSR1: + case SIGUSR2: + printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); + rc = sig_event(rc); + puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); + return (rc > 0 ? 0 : 1); + + case EVT_RESTART: + { + HANDLE rst; + printf("Stopping %s:", ident); fflush(stdout); + if (event_exists(EVT_DETACHED)) { + puts(" not detached, cannot restart"); + return 1; + } + if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { + puts(" error"); + return 1; + } + rc = sig_event(SIGTERM); + if (rc <= 0) { + puts(rc < 0 ? " not running" : " error"); + CloseHandle(rst); + return 1; + } + rc = wait_signaled(rst, 10); + CloseHandle(rst); + if (rc) { + puts(" timeout"); + return 1; + } + puts(" done"); + Sleep(100); + + printf("Starting %s:", ident); fflush(stdout); + rc = wait_evt_running(10, 1); + puts(!rc ? " done" : " error"); + return (!rc ? 0 : 1); + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// Windows Service Functions + +int daemon_winsvc_exitcode; // Set by app to exit(code) + +static SERVICE_STATUS_HANDLE svc_handle; +static SERVICE_STATUS svc_status; + + +// Report status to SCM + +static void service_report_status(int state, int seconds) +{ + // TODO: Avoid race + static DWORD checkpoint = 1; + svc_status.dwCurrentState = state; + svc_status.dwWaitHint = seconds*1000; + switch (state) { + default: + svc_status.dwCheckPoint = checkpoint++; + break; + case SERVICE_RUNNING: + case SERVICE_STOPPED: + svc_status.dwCheckPoint = 0; + } + switch (state) { + case SERVICE_START_PENDING: + case SERVICE_STOP_PENDING: + svc_status.dwControlsAccepted = 0; + break; + default: + svc_status.dwControlsAccepted = + SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN| + SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE; + break; + } + SetServiceStatus(svc_handle, &svc_status); +} + + +// Control the service, called by SCM + +static void WINAPI service_control(DWORD ctrlcode) +{ + switch (ctrlcode) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + service_report_status(SERVICE_STOP_PENDING, 30); + svc_paused = 0; + sig_event(SIGTERM); + break; + case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP + service_report_status(svc_status.dwCurrentState, 0); + svc_paused = 0; + sig_event(SIGHUP); // reload + break; + case SERVICE_CONTROL_PAUSE: + service_report_status(SERVICE_PAUSED, 0); + svc_paused = 1; + break; + case SERVICE_CONTROL_CONTINUE: + service_report_status(SERVICE_RUNNING, 0); + { + int was_paused = svc_paused; + svc_paused = 0; + sig_event(was_paused ? SIGHUP : SIGUSR1); // reload:recheck + } + break; + case SERVICE_CONTROL_INTERROGATE: + default: // unknown + service_report_status(svc_status.dwCurrentState, 0); + break; + } +} + + +// Exit handler for service + +static void service_exit(void) +{ + // Close signal events + int i; + for (i = 0; i < num_sig_handlers; i++) + CloseHandle(sig_events[i]); + num_sig_handlers = 0; + + // Set exitcode + if (daemon_winsvc_exitcode) { + svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode; + } + // Report stopped + service_report_status(SERVICE_STOPPED, 0); +} + + +// Variables for passing main(argc, argv) from daemon_main to service_main() +static int (*svc_main_func)(int, char **); +static int svc_main_argc; +static char ** svc_main_argv; + +// Main function for service, called by service dispatcher + +static void WINAPI service_main(DWORD argc, LPSTR * argv) +{ + char path[MAX_PATH], *p; + ARGUSED(argc); + + // Register control handler + svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); + + // Init service status + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS; + service_report_status(SERVICE_START_PENDING, 10); + + // Service started in \windows\system32, change to .exe directory + if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) { + *p = 0; SetCurrentDirectoryA(path); + } + + // Install exit handler + atexit(service_exit); + + // Do the real work, service status later updated by daemon_detach() + daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv); + + exit(daemon_winsvc_exitcode); + // ... continued in service_exit() +} + + +///////////////////////////////////////////////////////////////////////////// +// Windows Service Admin Functions + +// Set Service description (Win2000/XP) + +static int svcadm_setdesc(SC_HANDLE hs, const char * desc) +{ + HANDLE hdll; + BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID); + BOOL ret; + if (!(hdll = LoadLibraryA("ADVAPI32.DLL"))) + return FALSE; + if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A")))) + ret = FALSE; + else { + SERVICE_DESCRIPTIONA sd = { (char *)desc }; + ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd); + } + FreeLibrary(hdll); + return ret; +} + + +// Service install/remove commands + +static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, + int argc, char **argv ) +{ + int remove; + SC_HANDLE hm, hs; + + if (argc < 2) + return -1; + if (!strcmp(argv[1], "install")) + remove = 0; + else if (!strcmp(argv[1], "remove")) { + if (argc != 2) { + printf("%s: no arguments allowed for command remove\n", ident); + return 1; + } + remove = 1; + } + else + return -1; + + printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout); + + // Open SCM + if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) { + printf(" cannot open SCManager, Error=%ld\n", GetLastError()); + return 1; + } + + if (!remove) { + char path[MAX_PATH+100]; + int i; + // Get program path + if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { + printf(" unknown program path, Error=%ld\n", GetLastError()); + CloseServiceHandle(hm); + return 1; + } + // Append options + strcat(path, " "); strcat(path, svc_opts->cmd_opt); + for (i = 2; i < argc; i++) { + const char * s = argv[i]; + if (strlen(path)+strlen(s)+1 >= sizeof(path)) + break; + strcat(path, " "); strcat(path, s); + } + // Create + if (!(hs = CreateService(hm, + svc_opts->svcname, svc_opts->dispname, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, + NULL/*no load ordering*/, NULL/*no tag id*/, + ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) { + printf(" failed, Error=%ld\n", GetLastError()); + CloseServiceHandle(hm); + return 1; + } + // Set optional description + if (svc_opts->descript) + svcadm_setdesc(hs, svc_opts->descript); + } + else { + // Open + if (!(hs = OpenService(hm, ident, SERVICE_ALL_ACCESS))) { + puts(" not found"); + CloseServiceHandle(hm); + return 1; + } + // TODO: Stop service if running + // Remove + if (!DeleteService(hs)) { + printf(" failed, Error=%ld\n", GetLastError()); + CloseServiceHandle(hs); CloseServiceHandle(hm); + return 1; + } + } + puts(" done"); + CloseServiceHandle(hs); CloseServiceHandle(hm); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// +// Main Function + +// This function must be called from main() +// main_func is the function doing the real work + +int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, + int (*main_func)(int, char **), int argc, char **argv ) +{ + int rc; +#ifdef _DEBUG + // Enable Debug heap checks + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) + |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); +#endif + + // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters + if ((rc = initd_main(ident, argc, argv)) >= 0) + return rc; + // Check for [install|remove] parameters + if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0) + return rc; + + // Run as service if svc_opts.cmd_opt is given as first(!) argument + svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt)); + + if (!svc_mode) { + // Daemon: Try to simulate a Unix-like daemon + HANDLE rev; + BOOL exists; + + // Create main event to detect process type: + // 1. new: parent process => start child and wait for detach() or exit() of child. + // 2. exists && signaled: child process => do the real work, signal detach() to parent + // 3. exists && !signaled: already running => exit() + if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists))) + return 100; + + if (!exists && !debugging()) { + // Event new => parent process + return parent_main(rev); + } + + if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) { + // Event was signaled => In child process + return child_main(rev, main_func, argc, argv); + } + + // Event no longer signaled => Already running! + daemon_help(stdout, ident, "already running"); + CloseHandle(rev); + return 1; + } + else { + // Service: Start service_main() via SCM + SERVICE_TABLE_ENTRY service_table[] = { + { (char*)svc_opts->svcname, service_main }, { NULL, NULL } + }; + + svc_main_func = main_func; + svc_main_argc = argc; + svc_main_argv = argv; + if (!StartServiceCtrlDispatcher(service_table)) { + fprintf(stderr, "%s: cannot dispatch service, Error=%ld\n", ident, GetLastError()); +#ifdef _DEBUG + if (debugging()) + service_main(argc, argv); +#endif + return 100; + } + Sleep(1000); + ExitThread(0); // Do not redo exit() processing + /*NOTREACHED*/ + return 0; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// Test Program + +#ifdef TEST + +static volatile sig_atomic_t caughtsig = 0; + +static void sig_handler(int sig) +{ + caughtsig = sig; +} + +static void test_exit(void) +{ + printf("Main exit\n"); +} + +int test_main(int argc, char **argv) +{ + int i; + int debug = 0; + + printf("PID=%ld\n", GetCurrentProcessId()); + for (i = 0; i < argc; i++) { + printf("%d: \"%s\"\n", i, argv[i]); + if (!strcmp(argv[i],"-d")) + debug = 1; + } + + daemon_signal(SIGINT, sig_handler); + daemon_signal(SIGBREAK, sig_handler); + daemon_signal(SIGTERM, sig_handler); + daemon_signal(SIGHUP, sig_handler); + daemon_signal(SIGUSR2, sig_handler); + + atexit(test_exit); + + if (!debug) { + printf("Preparing to detach...\n"); + Sleep(2000); + daemon_detach("test"); + printf("Detached!\n"); + } + + for (;;) { + daemon_sleep(1); + printf("."); fflush(stdout); + if (caughtsig) { + if (caughtsig == SIGUSR2) { + debug ^= 1; + if (debug) + daemon_enable_console("Daemon[Debug]"); + else + daemon_disable_console(); + } + printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout); + if (caughtsig == SIGTERM || caughtsig == SIGBREAK) + break; + caughtsig = 0; + } + } + printf("\nExiting on signal %d\n", caughtsig); + return 0; +} + + +int main(int argc, char **argv) +{ + static const daemon_winsvc_options svc_opts = { + "-s", "test", "Test Service", "Service to test daemon_win32.c Module" + }; + + return daemon_main("testd", &svc_opts, test_main, argc, argv); +} + +#endif diff --git a/sm5/os_win32/hostname_win32.c b/sm5/os_win32/hostname_win32.c new file mode 100644 index 0000000000000000000000000000000000000000..21928adf4da274e13cfc960b27f81150233b5bd7 --- /dev/null +++ b/sm5/os_win32/hostname_win32.c @@ -0,0 +1,186 @@ +/* + * os_win32/hostname_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 Christian Franke <smartmontools-support@lists.sourceforge.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. + * + */ + +#include "hostname_win32.h" + +const char * hostname_win32_c_cvsid = "$Id: hostname_win32.c,v 1.1 2004/07/31 19:18:54 chrfranke Exp $" HOSTNAME_WIN32_H_CVSID; + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <string.h> + +#ifndef MAX_HOSTNAME_LEN + +// From IPHlpApi.dll: + +#define MAX_HOSTNAME_LEN 132 +#define MAX_DOMAIN_NAME_LEN 132 +#define MAX_SCOPE_ID_LEN 260 + +typedef struct { + char String[4 * 4]; +} IP_ADDRESS_STRING, +*PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; + +typedef struct _IP_ADDR_STRING { + struct _IP_ADDR_STRING* Next; + IP_ADDRESS_STRING IpAddress; + IP_MASK_STRING IpMask; + DWORD Context; +} IP_ADDR_STRING, +*PIP_ADDR_STRING; + +typedef struct { + char HostName[MAX_HOSTNAME_LEN]; + char DomainName[MAX_DOMAIN_NAME_LEN]; + PIP_ADDR_STRING CurrentDnsServer; + IP_ADDR_STRING DnsServerList; + UINT NodeType; + char ScopeId[MAX_SCOPE_ID_LEN]; + UINT EnableRouting; + UINT EnableProxy; + UINT EnableDns; +} FIXED_INFO, +*PFIXED_INFO; + +DWORD WINAPI GetNetworkParams(PFIXED_INFO info, PULONG size); + +#endif // MAX_HOSTNAME_LEN + + +// Call GetComputerNameEx() if available (Win2000/XP) + +static BOOL CallGetComputerNameExA(int type, LPSTR name, LPDWORD size) +{ + HANDLE hdll; + BOOL (WINAPI * GetComputerNameExA_p)(int/*enum COMPUTER_NAME_FORMAT*/, LPSTR, LPDWORD); + BOOL ret; + if (!(hdll = LoadLibraryA("KERNEL32.DLL"))) + return FALSE; + if (!((FARPROC)GetComputerNameExA_p = GetProcAddress(hdll, "GetComputerNameExA"))) + ret = FALSE; + else + ret = GetComputerNameExA_p(type, name, size); + FreeLibrary(hdll); + return ret; +} + + +// Call GetNetworkParams() if available (Win98/ME/2000/XP) + +static DWORD CallGetNetworkParams(PFIXED_INFO info, PULONG size) +{ + HANDLE hdll; + DWORD (WINAPI * GetNetworkParams_p)(PFIXED_INFO info, PULONG size); + DWORD ret; + if (!(hdll = LoadLibraryA("IPHlpApi.dll"))) + return ERROR_NOT_SUPPORTED; + if (!((FARPROC)GetNetworkParams_p = GetProcAddress(hdll, "GetNetworkParams"))) + ret = ERROR_NOT_SUPPORTED; + else + ret = GetNetworkParams_p(info, size); + FreeLibrary(hdll); + return ret; +} + + +// Get host/domainname from registry (Win98/ME/NT4/2000/XP) + +static DWORD GetNamesFromRegistry(BOOL domain, char * name, int len) +{ + HKEY hk; DWORD size, type; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + (GetVersion() & 0x80000000 + ? "System\\CurrentControlSet\\Services\\VxD\\MSTCP" //Win9x/ME + : "System\\CurrentControlSet\\Services\\Tcpip\\Parameters"), + 0, KEY_READ, &hk) != ERROR_SUCCESS) + return 0; + size = len-1; + if (!(RegQueryValueExA(hk, (!domain?"HostName":"Domain"), 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ)) + size = 0; + if (size == 0 && domain) { + size = len-1; + if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ)) + size = 0; + } + RegCloseKey(hk); + return size; +} + + +static int gethostdomname(int domain, char * name, int len) +{ + DWORD size; FIXED_INFO info; + + // try KERNEL32.dll::GetComputerNameEx() + size = len - 1; + if (CallGetComputerNameExA((!domain ? 1:2/*ComputerNameDnsHost:Domain*/), name, &size)) + return 0; + + // try IPHlpApi.dll::GetNetworkParams() + size = sizeof(info); + if (CallGetNetworkParams(&info, &size) == ERROR_SUCCESS) { + strncpy(name, (!domain?info.HostName:info.DomainName), len-1); name[len-1] = 0; + return 0; + } + + // try registry + if (GetNamesFromRegistry(domain, name, len)) + return 0; + + if (domain) + return -1; + + // last resort: get NETBIOS name + size = len - 1; + if (GetComputerNameA(name, &size)) + return 0; + + return -1; +} + + +int gethostname(char * name, int len) +{ + return gethostdomname(0, name, len); +} + + +int getdomainname(char * name, int len) +{ + return gethostdomname(1, name, len); +} + + +#ifdef TEST + +#include <stdio.h> + +main() +{ + char name[256]; + if (gethostname(name, sizeof(name))) + strcpy(name, "Error"); + printf("hostname=\"%s\"\n", name); + if (getdomainname(name, sizeof(name))) + strcpy(name, "Error"); + printf("domainname=\"%s\"\n", name); + return 0; +} + +#endif diff --git a/sm5/os_win32/syslog_win32.c b/sm5/os_win32/syslog_win32.c new file mode 100644 index 0000000000000000000000000000000000000000..832854f8a9df1ffa14e9cce040a605f1232caaed --- /dev/null +++ b/sm5/os_win32/syslog_win32.c @@ -0,0 +1,435 @@ +/* + * os_win32/syslog_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004 Christian Franke <smartmontools-support@lists.sourceforge.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. + * + */ + +// Win32 Emulation of syslog() for smartd +// Writes to windows event log on NT4/2000/XP +// (Register syslogevt.exe as event message file) +// Writes to file "<ident>.log" on 9x/ME. +// If facility is set to LOG_LOCAL[0-7], log is written to +// file "<ident>.log", stdout, stderr, "<ident>[1-5].log". + + +#include "syslog.h" + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <errno.h> +#include <process.h> // getpid() + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ... + +const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.4 2004/04/07 11:17:08 chrfranke Exp $" +SYSLOG_H_CVSID; + +#ifdef _MSC_VER +// MSVC +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +#define ARGUSED(x) ((void)(x)) + + +#ifndef _MT +//MT runtime not necessary, because thread uses no unsafe lib functions +//#error Program must be linked with multithreaded runtime library +#endif + + +#ifdef TESTEVT +// Redirect event log to stdout for testing + +static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid, + WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data) +{ + int i; + printf("%u %lu:%s", type, id, nstrings != 1?"\n":""); + for (i = 0; i < nstrings; i++) + printf(" \"%s\"\n", strings[i]); + fflush(stdout); + return TRUE; +} + +HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source) +{ + return (HANDLE)42; +} + +#define ReportEventA Test_ReportEventA +#define RegisterEventSourceA Test_RegisterEventSourceA +#endif // TESTEVT + + +// Event message ids, +// should be identical to MSG_SYSLOG* in "syslogevt.h" +// (generated by "mc" from "syslogevt.mc") +#define MSG_SYSLOG 0x00000000L +#define MSG_SYSLOG_01 0x00000001L +// ... +#define MSG_SYSLOG_10 0x0000000AL + +static char sl_ident[100]; +static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1]; +static FILE * sl_logfile; +static char sl_pidstr[16]; +static HANDLE sl_hevtsrc; + + +// Ring buffer for event log output via thread +#define MAXLINES 10 +#define LINELEN 200 + +static HANDLE evt_hthread; +static char evt_lines[MAXLINES][LINELEN+1]; +static int evt_priorities[MAXLINES]; +static volatile int evt_timeout; +static int evt_index_in, evt_index_out; +static HANDLE evt_wait_in, evt_wait_out; + + +// Map syslog priority to event type + +static WORD pri2evtype(int priority) +{ + switch (priority) { + default: + case LOG_EMERG: case LOG_ALERT: + case LOG_CRIT: case LOG_ERR: + return EVENTLOG_ERROR_TYPE; + case LOG_WARNING: + return EVENTLOG_WARNING_TYPE; + case LOG_NOTICE: case LOG_INFO: + case LOG_DEBUG: + return EVENTLOG_INFORMATION_TYPE; + } +} + + +// Map syslog priority to string + +static const char * pri2text(int priority) +{ + switch (priority) { + case LOG_EMERG: return "EMERG"; + case LOG_ALERT: return "ALERT"; + case LOG_CRIT: return "CRIT"; + default: + case LOG_ERR: return "ERROR"; + case LOG_WARNING: return "Warn"; + case LOG_NOTICE: return "Note"; + case LOG_INFO: return "Info"; + case LOG_DEBUG: return "Debug"; + } +} + + +// Output cnt events from ring buffer + +static void report_events(int cnt) +{ + const char * msgs[3+MAXLINES]; + + int i, pri; + if (cnt <= 0) + return; + if (cnt > MAXLINES) + cnt = MAXLINES; + + pri = evt_priorities[evt_index_out]; + + msgs[0] = sl_ident; + msgs[1] = sl_pidstr; + msgs[2] = pri2text(pri); + for (i = 0; i < cnt; i++) { + //assert(evt_priorities[evt_index_out] == pri); + msgs[3+i] = evt_lines[evt_index_out]; + if (++evt_index_out >= MAXLINES) + evt_index_out = 0; + } + ReportEventA(sl_hevtsrc, + pri2evtype(pri), // type + 0, MSG_SYSLOG+cnt, // category, message id + NULL, // no security id + (WORD)(3+cnt), 0, // 3+cnt strings, ... + msgs, NULL); // ... , no data +} + + +// Thread to combine several syslog lines into one event log entry + +static ULONG WINAPI event_logger_thread(LPVOID arg) +{ + int cnt; + ARGUSED(arg); + + cnt = 0; + for (;;) { + // Wait for first line ... + int prior, i, rest; + if (cnt == 0) { + if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0) + break; + cnt = 1; + } + + // ... wait some time for more lines with same prior + i = evt_index_out; + prior = evt_priorities[i]; + rest = 0; + while (cnt < MAXLINES) { + long timeout = + evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES); + if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0) + break; + if (++i >= MAXLINES) + i = 0; + if (evt_priorities[i] != prior) { + rest = 1; + break; + } + cnt++; + } + + // Output all in one event log entry + report_events(cnt); + + // Signal space + if (!ReleaseSemaphore(evt_wait_in, cnt, NULL)) + break; + cnt = rest; + } + return 0; +} + + +static void on_exit_event_logger(void) +{ + // Output lines immediate if exiting + evt_timeout = 0; + // Wait for thread to finish + WaitForSingleObject(evt_hthread, 1000L); + CloseHandle(evt_hthread); +#if 0 + if (sl_hevtsrc) { + DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0; + } +#else + // Leave event message source open to prevent losing messages during shutdown +#endif +} + + +static int start_event_logger() +{ + DWORD tid; + evt_timeout = 1; + if ( !(evt_wait_in = CreateSemaphore(NULL, MAXLINES, MAXLINES, NULL)) + || !(evt_wait_out = CreateSemaphore(NULL, 0, MAXLINES, NULL))) { + fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError()); + return -1; + } + if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) { + fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError()); + return -1; + } + atexit(on_exit_event_logger); + return 0; +} + + +// Write lines to event log ring buffer + +static void write_event_log(int priority, const char * lines) +{ + int cnt = 0; + int i; + for (i = 0; lines[i]; i++) { + int len = 0; + while (lines[i+len] && lines[i+len] != '\n') + len++; + ; + if (len > 0) { + // Wait for space + if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0) + return; + // Copy line + evt_priorities[evt_index_in] = priority; + memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN)); + if (len < LINELEN) + evt_lines[evt_index_in][len] = 0; + if (++evt_index_in >= MAXLINES) + evt_index_in = 0; + // Signal avail if ring buffer full + if (++cnt >= MAXLINES) { + ReleaseSemaphore(evt_wait_out, cnt, NULL); + cnt = 0; + } + i += len; + } + if (!lines[i]) + break; + } + + // Signal avail + if (cnt > 0) + ReleaseSemaphore(evt_wait_out, cnt, NULL); + Sleep(1); +} + + +// Write lines to logfile + +static void write_logfile(FILE * f, int priority, const char * lines) +{ + time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13]; + int i; + + now = time((time_t*)0); + if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now))) + strcpy(stamp,"?"); + + for (i = 0; lines[i]; i++) { + int len = 0; + while (lines[i+len] && lines[i+len] != '\n') + len++; + if (len > 0) { + fprintf(f, "%s %s[%s]: %-5s: ", + stamp, sl_ident, sl_pidstr, pri2text(priority)); + fwrite(lines+i, len, 1, f); + fputc('\n', f); + i += len; + } + if (!lines[i]) + break; + } +} + + +void openlog(const char *ident, int logopt, int facility) +{ + int pid; + if (sl_logpath[0] || sl_logfile || sl_hevtsrc) + return; // Already open + + strncpy(sl_ident, ident, sizeof(sl_ident)-1); + // logopt==LOG_PID assumed + ARGUSED(logopt); + pid = getpid(); + if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0) + strcpy(sl_pidstr,"?"); + + if (facility == LOG_LOCAL0) // "ident.log" + strcat(strcpy(sl_logpath, sl_ident), ".log"); + else if (facility == LOG_LOCAL1) // stdout + sl_logfile = stdout; + else if (facility == LOG_LOCAL2) // stderr + sl_logfile = stderr; + else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log" + snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log", + sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2)); + } + else // Assume LOG_DAEMON, use event log if possible, else "ident.log" + if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) { + // Cannot open => Use logfile + long err = GetLastError(); + strcat(strcpy(sl_logpath, sl_ident), ".log"); + if (GetVersion() & 0x80000000) + fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n", + sl_ident, sl_logpath); + else + fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n", + sl_ident, err, sl_logpath); + } + else { + // Start event log thread + start_event_logger(); + } + //assert(sl_logpath[0] || sl_logfile || sl_hevtsrc); + +} + + +void closelog() +{ +} + + +void vsyslog(int priority, const char * message, va_list args) +{ + char buffer[1000]; + + // Translation of %m to error text not supported yet + if (strstr(message, "%m")) + message = "Internal error: \"%%m\" in log message"; + + // Format message + if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0) + strcpy(buffer, "Internal Error: buffer overflow"); + + if (sl_hevtsrc) { + // Write to event log + write_event_log(priority, buffer); + } + else if (sl_logfile) { + // Write to stdout/err + write_logfile(sl_logfile, priority, buffer); + fflush(sl_logfile); + } + else if (sl_logpath[0]) { + // Append to logfile + FILE * f; + if (!(f = fopen(sl_logpath, "a"))) + return; + write_logfile(f, priority, buffer); + fclose(f); + } +} + + +#ifdef TEST +// Test program + +void syslog(int priority, const char *message, ...) +{ + va_list args; + va_start(args, message); + vsyslog(priority, message, args); + va_end(args); +} + +int main(int argc, char* argv[]) +{ + int i; + openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1)); + syslog(LOG_INFO, "Info\n"); + syslog(LOG_WARNING, "Warning %d\n\n", 42); + syslog(LOG_ERR, "Error %s", "Fatal"); + for (i = 0; i < 100; i++) { + char buf[LINELEN]; + if (i % 13 == 0) + Sleep(1000L); + sprintf(buf, "Log Line %d\n", i); + syslog(i % 17 ? LOG_INFO : LOG_ERR, buf); + } + closelog(); + return 0; +} + +#endif diff --git a/sm5/scsicmds.c b/sm5/scsicmds.c new file mode 100644 index 0000000000000000000000000000000000000000..71c94a5a5ce3d48851409dc3f3fa18a2cfe6992e --- /dev/null +++ b/sm5/scsicmds.c @@ -0,0 +1,2007 @@ +/* + * scsicmds.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org> + * + * Additional SCSI work: + * Copyright (C) 2003-4 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/ + * + * + * In the SCSI world "SMART" is a dead or withdrawn standard. In recent + * SCSI standards (since SCSI-3) it goes under the awkward name of + * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")]. + * The relevant information is spread around several SCSI draft + * standards available at http://www.t10.org . Reference is made in the + * code to the following acronyms: + * - SAM [SCSI Architectural model, versions 2 or 3] + * - SPC [SCSI Primary commands, versions 2 or 3] + * - SBC [SCSI Block commands, versions 2] + * + * Some SCSI disk vendors have snippets of "SMART" information in their + * product manuals. + */ + +#include <stdio.h> +#include <string.h> + +#include "config.h" +#include "int64.h" +#include "extern.h" +#include "scsicmds.h" +#include "utility.h" + +const char *scsicmds_c_cvsid="$Id: scsicmds.c,v 1.78 2004/09/05 13:53:14 dpgilbert Exp $" +CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* for passing global control variables */ +extern smartmonctrl *con; + +/* output binary in hex and optionally ascii */ +void dStrHex(const char* str, int len, int no_ascii) +{ + const char* p = str; + unsigned char c; + char buff[82]; + int a = 0; + const int bpstart = 5; + const int cpstart = 60; + int cpos = cpstart; + int bpos = bpstart; + int i, k; + + if (len <= 0) return; + memset(buff,' ',80); + buff[80]='\0'; + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + if (bpos >= ((bpstart + (9 * 3)))) + bpos++; + + for(i = 0; i < len; i++) + { + c = *p++; + bpos += 3; + if (bpos == (bpstart + (9 * 3))) + bpos++; + sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); + buff[bpos + 2] = ' '; + if (no_ascii) + buff[cpos++] = ' '; + else { + if ((c < ' ') || (c >= 0x7f)) + c='.'; + buff[cpos++] = c; + } + if (cpos > (cpstart+15)) + { + pout("%s\n", buff); + bpos = bpstart; + cpos = cpstart; + a += 16; + memset(buff,' ',80); + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + } + } + if (cpos > cpstart) + { + pout("%s\n", buff); + } +} + +struct scsi_opcode_name { + UINT8 opcode; + const char * name; +}; + +static struct scsi_opcode_name opcode_name_arr[] = { + /* in ascending opcode order */ + {TEST_UNIT_READY, "test unit ready"}, /* 0x00 */ + {REQUEST_SENSE, "request sense"}, /* 0x03 */ + {INQUIRY, "inquiry"}, /* 0x12 */ + {MODE_SELECT, "mode select"}, /* 0x15 */ + {MODE_SENSE, "mode sense"}, /* 0x1a */ + {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */ + {SEND_DIAGNOSTIC, "send diagnostic"}, /* 0x1d */ + {LOG_SENSE, "log sense"}, /* 0x4d */ + {MODE_SELECT_10, "mode select(10)"}, /* 0x55 */ + {MODE_SENSE_10, "mode sense(10)"}, /* 0x5a */ +}; + +const char * scsi_get_opcode_name(UINT8 opcode) +{ + int k; + int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]); + struct scsi_opcode_name * onp; + + for (k = 0; k < len; ++k) { + onp = &opcode_name_arr[k]; + if (opcode == onp->opcode) + return onp->name; + else if (opcode < onp->opcode) + return NULL; + } + return NULL; +} + + +void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf, + struct scsi_sense_disect * out) +{ + memset(out, 0, sizeof(struct scsi_sense_disect)); + if ((SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) && + (io_buf->resp_sense_len > 7)) { + out->error_code = (io_buf->sensep[0] & 0x7f); + out->sense_key = (io_buf->sensep[2] & 0xf); + if (io_buf->resp_sense_len > 13) { + out->asc = io_buf->sensep[12]; + out->ascq = io_buf->sensep[13]; + } + } +} + +static int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) +{ + if (SCSI_SK_NOT_READY == sinfo->sense_key) { + if (SCSI_ASC_NO_MEDIUM == sinfo->asc) + return SIMPLE_ERR_NO_MEDIUM; + else if (SCSI_ASC_NOT_READY == sinfo->asc) { + if (0x1 == sinfo->ascq) + return SIMPLE_ERR_BECOMING_READY; + else + return SIMPLE_ERR_NOT_READY; + } else + return SIMPLE_ERR_NOT_READY; + } else if (SCSI_SK_ILLEGAL_REQUEST == sinfo->sense_key) { + if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc) + return SIMPLE_ERR_BAD_OPCODE; + else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc) + return SIMPLE_ERR_BAD_FIELD; + else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc) + return SIMPLE_ERR_BAD_PARAM; + } else if (SCSI_SK_UNIT_ATTENTION == sinfo->sense_key) + return SIMPLE_ERR_TRY_AGAIN; + return SIMPLE_NO_ERROR; +} + +const char * scsiErrString(int scsiErr) +{ + if (scsiErr < 0) + return strerror(-scsiErr); + switch (scsiErr) { + case SIMPLE_NO_ERROR: + return "no error"; + case SIMPLE_ERR_NOT_READY: + return "device not ready"; + case SIMPLE_ERR_BAD_OPCODE: + return "unsupported scsi opcode"; + case SIMPLE_ERR_BAD_FIELD: + return "unsupported field in scsi command"; + case SIMPLE_ERR_BAD_PARAM: + return "badly formed scsi parameters"; + case SIMPLE_ERR_BAD_RESP: + return "scsi response fails sanity test"; + case SIMPLE_ERR_NO_MEDIUM: + return "no medium present"; + case SIMPLE_ERR_BECOMING_READY: + return "device will be ready soon"; + case SIMPLE_ERR_TRY_AGAIN: + return "unit attention reported, try again"; + default: + return "unknown error"; + } +} + +/* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if + command not supported, 3 if field (within command) not supported or + returns negated errno. SPC sections 7.6 and 8.2 N.B. Sets PC==1 + to fetch "current cumulative" log pages. + If known_resp_len > 0 then a single fetch is done for this response + length. If known_resp_len == 0 then twin fetches are performed, the + first to deduce the response length, then send the same command again + requesting the deduced response length. This protects certain fragile + HBAs. The twin fetch technique should not be used with the TapeAlert + log page since it clears its state flags after each fetch. */ +int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen, + int known_resp_len) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int pageLen; + int status, res; + + if (known_resp_len > bufLen) + return -EIO; + if (known_resp_len > 0) + pageLen = known_resp_len; + else { + /* Starting twin fetch strategy: first fetch to find respone length */ + pageLen = 4; + if (pageLen > bufLen) + return -EIO; + else + memset(pBuf, 0, pageLen); + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = pageLen; + io_hdr.dxferp = pBuf; + cdb[0] = LOG_SENSE; + cdb[2] = 0x40 | (pagenum & 0x3f); /* Page control (PC)==1 */ + cdb[7] = (pageLen >> 8) & 0xff; + cdb[8] = pageLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + if ((res = scsiSimpleSenseFilter(&sinfo))) + return res; + /* sanity check on response */ + if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum)) + return SIMPLE_ERR_BAD_RESP; + if (0 == ((pBuf[2] << 8) + pBuf[3])) + return SIMPLE_ERR_BAD_RESP; + pageLen = (pBuf[2] << 8) + pBuf[3] + 4; + /* some SCSI HBA don't like "odd" length transfers */ + if (pageLen % 2) + pageLen += 1; + if (pageLen > bufLen) + pageLen = bufLen; + } + memset(pBuf, 0, 4); + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = pageLen; + io_hdr.dxferp = pBuf; + cdb[0] = LOG_SENSE; + cdb[2] = 0x40 | (pagenum & 0x3f); /* Page control (PC)==1 */ + cdb[7] = (pageLen >> 8) & 0xff; + cdb[8] = pageLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (0 != status) + return status; + /* sanity check on response */ + if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum)) + return SIMPLE_ERR_BAD_RESP; + if (0 == ((pBuf[2] << 8) + pBuf[3])) + return SIMPLE_ERR_BAD_RESP; + return 0; +} + +/* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY, + * 2 if command not supported (then MODE SENSE(10) should be supported), + * 3 if field in command not supported or returns negated errno. + * SPC sections 7.9 and 8.4 [mode subpage==0] */ +int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SENSE; + cdb[2] = (pc << 6) | (pagenum & 0x3f); + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + } + if ((0 == status) && (ALL_MODE_PAGES != pagenum)) { + int offset; + + offset = scsiModePageOffset(pBuf, bufLen, 0); + if (offset < 0) + return SIMPLE_ERR_BAD_RESP; + else if (pagenum != (pBuf[offset] & 0x3f)) + return SIMPLE_ERR_BAD_RESP; + } + return status; +} + +/* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response + * from a corresponding 6 byte MODE SENSE command. Such a response should + * have a 4 byte header followed by 0 or more 8 byte block descriptors + * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY, + * 2 if command not supported (then MODE SELECT(10) may be supported), + * 3 if field in command not supported, 4 if bad parameter to command + * or returns negated errno. SPC sections 7.7 and 8.4 */ +int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status, pg_offset, pg_len, hdr_plus_1_pg; + + pg_offset = 4 + pBuf[3]; + if (pg_offset + 2 >= bufLen) + return -EINVAL; + pg_len = pBuf[pg_offset + 1] + 2; + hdr_plus_1_pg = pg_offset + pg_len; + if (hdr_plus_1_pg > bufLen) + return -EINVAL; + pBuf[0] = 0; /* Length of returned mode sense data reserved for SELECT */ + pBuf[pg_offset] &= 0x3f; /* Mask of PS bit from byte 0 of page data */ + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_TO_DEVICE; + io_hdr.dxfer_len = hdr_plus_1_pg; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SELECT; + cdb[1] = 0x10 | (sp & 1); /* set PF (page format) bit always */ + cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */ + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command + * not supported (then MODE SENSE(6) might be supported), 3 if field in + * command not supported or returns negated errno. + * SPC sections 7.10 and 8.4 [mode subpage==0] */ +int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SENSE_10; + cdb[2] = (pc << 6) | (pagenum & 0x3f); + cdb[7] = (bufLen >> 8) & 0xff; + cdb[8] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + } + if ((0 == status) && (ALL_MODE_PAGES != pagenum)) { + int offset; + + offset = scsiModePageOffset(pBuf, bufLen, 1); + if (offset < 0) + return SIMPLE_ERR_BAD_RESP; + else if (pagenum != (pBuf[offset] & 0x3f)) + return SIMPLE_ERR_BAD_RESP; + } + return status; +} + +/* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response + * from a corresponding 10 byte MODE SENSE command. Such a response should + * have a 8 byte header followed by 0 or more 8 byte block descriptors + * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if + * command not supported (then MODE SELECT(6) may be supported), 3 if field + * in command not supported, 4 if bad parameter to command or returns + * negated errno. SAM sections 7.8 and 8.4 */ +int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int status, pg_offset, pg_len, hdr_plus_1_pg; + + pg_offset = 8 + (pBuf[6] << 8) + pBuf[7]; + if (pg_offset + 2 >= bufLen) + return -EINVAL; + pg_len = pBuf[pg_offset + 1] + 2; + hdr_plus_1_pg = pg_offset + pg_len; + if (hdr_plus_1_pg > bufLen) + return -EINVAL; + pBuf[0] = 0; + pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */ + pBuf[pg_offset] &= 0x3f; /* Mask of PS bit from byte 0 of page data */ + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_TO_DEVICE; + io_hdr.dxfer_len = hdr_plus_1_pg; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SELECT_10; + cdb[1] = 0x10 | (sp & 1); /* set PF (page format) bit always */ + cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */ + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* Standard INQUIRY returns 0 for ok, anything else is a major problem. + * bufLen should be 36 for unsafe devices (like USB mass storage stuff) + * otherwise they can lock up! SPC sections 7.4 and 8.6 */ +int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen) +{ + struct scsi_sense_disect sinfo; + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + int status; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = INQUIRY; + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* INQUIRY to fetch Vital Page Data. Returns 0 if ok, 1 if NOT READY + * (unlikely), 2 if command not supported, 3 if field in command not + * supported, 5 if response indicates that EVPD bit ignored or returns + * negated errno. SPC section 7.4 and 8.6 */ +int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status, res; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + if (bufLen > 1) + pBuf[1] = 0x0; + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = INQUIRY; + cdb[1] = 0x1; /* set EVPD bit (enable Vital Product Data) */ + cdb[2] = vpd_page; + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + if ((res = scsiSimpleSenseFilter(&sinfo))) + return res; + /* Guard against devices that ignore EVPD bit and do standard INQUIRY */ + if (bufLen > 1) { + if (vpd_page == pBuf[1]) { + if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2])) + return SIMPLE_ERR_BAD_RESP; + } else + return SIMPLE_ERR_BAD_RESP; + } + return 0; +} + +/* REQUEST SENSE command. Returns 0 if ok, anything else major problem. + * SPC section 7.24 */ +int scsiRequestSense(int device, struct scsi_sense_disect * sense_info) +{ + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + UINT8 buff[18]; + int status, len; + UINT8 ecode; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = sizeof(buff); + io_hdr.dxferp = buff; + cdb[0] = REQUEST_SENSE; + cdb[4] = sizeof(buff); + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if ((0 == status) && (sense_info)) { + ecode = buff[0] & 0x7f; + sense_info->error_code = ecode; + sense_info->sense_key = buff[2] & 0xf; + sense_info->asc = 0; + sense_info->ascq = 0; + if ((0x70 == ecode) || (0x71 == ecode)) { + len = buff[7] + 8; + if (len > 13) { + sense_info->asc = buff[12]; + sense_info->ascq = buff[13]; + } + } + } + return status; +} + +/* SEND DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if command + * not supported, 3 if field in command not supported or returns negated + * errno. SPC section 7.25 */ +int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = SEND_DIAGNOSTIC; + if (SCSI_DIAG_DEF_SELF_TEST == functioncode) + cdb[1] = 0x4; /* SelfTest bit */ + else if (SCSI_DIAG_NO_SELF_TEST != functioncode) + cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */ + else /* SCSI_DIAG_NO_SELF_TEST == functioncode */ + cdb[1] = 0x10; /* PF bit */ + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST; + /* worst case is an extended foreground self test on a big disk */ + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if + * command not supported, 3 if field in command not supported or returns + * negated errno. SPC section 7.17 */ +int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, + int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = RECEIVE_DIAGNOSTIC; + cdb[1] = pcv; + cdb[2] = pagenum; + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* TEST UNIT READY command. SPC section 7.28 (probably in SBC as well) */ +static int _testunitready(int device, struct scsi_sense_disect * sinfo) +{ + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_NONE; + io_hdr.dxfer_len = 0; + io_hdr.dxferp = NULL; + cdb[0] = TEST_UNIT_READY; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, sinfo); + return 0; +} + +/* Returns 0 for device responds and media ready, 1 for device responds and + media not ready, or returns a negated errno value */ +int scsiTestUnitReady(int device) +{ + struct scsi_sense_disect sinfo; + int status; + + status = _testunitready(device, &sinfo); + if (0 != status) + return status; + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + /* power on reset, media changed, ok ... try again */ + status = _testunitready(device, &sinfo); + if (0 != status) + return status; + status = scsiSimpleSenseFilter(&sinfo); + } + return status; +} + +/* Offset into mode sense (6 or 10 byte) response that actual mode page + * starts at (relative to resp[0]). Returns -1 if problem */ +int scsiModePageOffset(const UINT8 * resp, int len, int modese_len) +{ + int resp_len, bd_len; + int offset = -1; + + if (resp) { + if (10 == modese_len) { + resp_len = (resp[0] << 8) + resp[1] + 2; + bd_len = (resp[6] << 8) + resp[7]; + offset = bd_len + 8; + } else { + resp_len = resp[0] + 1; + bd_len = resp[3]; + offset = bd_len + 4; + } + if ((offset + 2) > len) { + pout("scsiModePageOffset: raw_curr too small, offset=%d " + "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len); + offset = -1; + } else if ((offset + 2) > resp_len) { + pout("scsiModePageOffset: response length too short, resp_len=%d" + " offset=%d bd_len=%d\n", resp_len, offset, bd_len); + offset = -1; + } + } + return offset; +} + +/* IEC mode page byte 2 bit masks */ +#define DEXCPT_ENABLE 0x08 +#define EWASC_ENABLE 0x10 +#define DEXCPT_DISABLE 0xf7 +#define EWASC_DISABLE 0xef +#define TEST_DISABLE 0xfb + +/* Fetches the Informational Exceptions Control mode page. First tries + * the 6 byte MODE SENSE command and if that fails with an illegal opcode + * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive + * number if a known error (see SIMPLE_ERR_ ...) or a negative errno + * value. */ +int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len) +{ + int err = 0; + + memset(iecp, 0, sizeof(*iecp)); + iecp->modese_len = modese_len; + iecp->requestedCurrent = 1; + if (iecp->modese_len <= 6) { + if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CURRENT, + iecp->raw_curr, sizeof(iecp->raw_curr)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + iecp->modese_len = 10; + else { + iecp->modese_len = 0; + return err; + } + } else if (0 == iecp->modese_len) + iecp->modese_len = 6; + } + if (10 == iecp->modese_len) { + err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CURRENT, + iecp->raw_curr, sizeof(iecp->raw_curr)); + if (err) { + iecp->modese_len = 0; + return err; + } + } + iecp->gotCurrent = 1; + iecp->requestedChangeable = 1; + if (10 == iecp->modese_len) + err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CHANGEABLE, + iecp->raw_chg, sizeof(iecp->raw_chg)); + else if (6 == iecp->modese_len) + err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CHANGEABLE, + iecp->raw_chg, sizeof(iecp->raw_chg)); + if (err) + return err; + iecp->gotChangeable = 1; + return 0; +} + +int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp) +{ + int offset; + + if (iecp && iecp->gotCurrent) { + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset >= 0) + return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1; + else + return 0; + } else + return 0; +} + +int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) +{ + int offset; + + if (iecp && iecp->gotCurrent) { + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset >= 0) + return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0; + else + return 0; + } else + return 0; +} + +/* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */ +#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 +#define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4 +/* exception/warning via an unrequested REQUEST SENSE command */ +#define SCSI_IEC_MP_MRIE 6 +#define SCSI_IEC_MP_INTERVAL_T 0 +#define SCSI_IEC_MP_REPORT_COUNT 1 + +/* Try to set (or clear) both Exception Control and Warning in the IE + * mode page subject to the "changeable" mask. The object pointed to + * by iecp is (possibly) inaccurate after this call, therefore + * scsiFetchIECmpage() should be called again if the IEC mode page + * is to be re-examined. + * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...' + * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */ +int scsiSetExceptionControlAndWarning(int device, int enabled, + const struct scsi_iec_mode_page *iecp) +{ + int k, offset, resp_len; + int err = 0; + UINT8 rout[SCSI_IECMP_RAW_LEN]; + int sp, eCEnabled, wEnabled; + + if ((! iecp) || (! iecp->gotCurrent)) + return -EINVAL; + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset < 0) + return -EINVAL; + memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN); + if (10 == iecp->modese_len) { + resp_len = (rout[0] << 8) + rout[1] + 2; + memset(rout, 0, 2); /* mode data length==0 for mode select */ + } else { + resp_len = rout[0] + 1; + memset(rout, 0, 1); /* mode data length==0 for mode select */ + } + sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */ + rout[offset] &= 0x7f; /* mask off PS bit */ + if (enabled) { + rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED; + if (con->reportscsiioctl > 2) + rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK; + rout[offset + 3] = SCSI_IEC_MP_MRIE; + rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff; + rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff; + rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff; + rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff; + rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff; + rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff; + rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff; + rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff; + if (iecp->gotChangeable) { + UINT8 chg2 = iecp->raw_chg[offset + 2]; + + rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) : + iecp->raw_curr[offset + 2]; + for (k = 3; k < 12; ++k) { + if (0 == iecp->raw_chg[offset + k]) + rout[offset + k] = iecp->raw_curr[offset + k]; + } + } + if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) { + if (con->reportscsiioctl > 0) + pout("scsiSetExceptionControlAndWarning: already enabled\n"); + return 0; + } + } else { /* disabling Exception Control and (temperature) Warnings */ + eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1; + wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0; + if ((! eCEnabled) && (! wEnabled)) { + if (con->reportscsiioctl > 0) + pout("scsiSetExceptionControlAndWarning: already disabled\n"); + return 0; /* nothing to do, leave other setting alone */ + } + if (wEnabled) + rout[offset + 2] &= EWASC_DISABLE; + if (eCEnabled) { + if (iecp->gotChangeable && + (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE)) + rout[offset + 2] |= DEXCPT_ENABLE; + rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */ + } + } + if (10 == iecp->modese_len) + err = scsiModeSelect10(device, sp, rout, resp_len); + else if (6 == iecp->modese_len) + err = scsiModeSelect(device, sp, rout, resp_len); + return err; +} + +int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp) +{ + UINT8 tBuf[252]; + int err; + + memset(tBuf, 0, sizeof(tBuf)); + if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, tBuf, + sizeof(tBuf), 0))) { + *currenttemp = 0; + *triptemp = 0; + pout("Log Sense for temperature failed [%s]\n", scsiErrString(err)); + return err; + } + *currenttemp = tBuf[9]; + *triptemp = tBuf[15]; + return 0; +} + +/* Read informational exception log page or Request Sense response. + * Fetching asc/ascq code potentially flagging an exception or warning. + * Returns 0 if ok, else error number. A current temperature of 255 + * (Celsius) implies that the temperature not available. */ +int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage, + UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, + UINT8 *triptemp) +{ + UINT8 tBuf[252]; + struct scsi_sense_disect sense_info; + int err; + int temperatureSet = 0; + unsigned short pagesize; + UINT8 currTemp, trTemp; + + *asc = 0; + *ascq = 0; + *currenttemp = 0; + *triptemp = 0; + memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk + memset(&sense_info, 0, sizeof(sense_info)); + if (hasIELogPage) { + if ((err = scsiLogSense(device, IE_LPAGE, tBuf, + sizeof(tBuf), 0))) { + pout("Log Sense failed, IE page [%s]\n", scsiErrString(err)); + return err; + } + // pull out page size from response, don't forget to add 4 + pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4; + if ((pagesize < 4) || tBuf[4] || tBuf[5]) { + pout("Log Sense failed, IE page, bad parameter code or length\n"); + return SIMPLE_ERR_BAD_PARAM; + } + if (tBuf[7] > 1) { + sense_info.asc = tBuf[8]; + sense_info.ascq = tBuf[9]; + if (! hasTempLogPage) { + if (tBuf[7] > 2) + *currenttemp = tBuf[10]; + if (tBuf[7] > 3) /* IBM extension in SMART (IE) lpage */ + *triptemp = tBuf[11]; + } + } + } + if (0 == sense_info.asc) { + /* ties in with MRIE field of 6 in IEC mode page (0x1c) */ + if ((err = scsiRequestSense(device, &sense_info))) { + pout("Request Sense failed, [%s]\n", scsiErrString(err)); + return err; + } + } + *asc = sense_info.asc; + *ascq = sense_info.ascq; + if ((! temperatureSet) && hasTempLogPage) { + if (0 == scsiGetTemp(device, &currTemp, &trTemp)) { + *currenttemp = currTemp; + *triptemp = trTemp; + } + } + return 0; +} + +// The first character (W, C, I) tells the severity +static const char * TapeAlertsMessageTable[]= { + " ", + /* 0x01 */ + "W: The tape drive is having problems reading data. No data has been lost,\n" + " but there has been a reduction in the performance of the tape.", + /* 0x02 */ + "W: The tape drive is having problems writing data. No data has been lost,\n" + " but there has been a reduction in the capacity of the tape.", + /* 0x03 */ + "W: The operation has stopped because an error has occurred while reading\n" + " or writing data that the drive cannot correct.", + /* 0x04 */ + "C: Your data is at risk:\n" + " 1. Copy any data you require from this tape. \n" + " 2. Do not use this tape again.\n" + " 3. Restart the operation with a different tape.", + /* 0x05 */ + "C: The tape is damaged or the drive is faulty. Call the tape drive\n" + " supplier helpline.", + /* 0x06 */ + "C: The tape is from a faulty batch or the tape drive is faulty:\n" + " 1. Use a good tape to test the drive.\n" + " 2. If problem persists, call the tape drive supplier helpline.", + /* 0x07 */ + "W: The tape cartridge has reached the end of its calculated useful life:\n" + " 1. Copy data you need to another tape.\n" + " 2. Discard the old tape.", + /* 0x08 */ + "W: The tape cartridge is not data-grade. Any data you back up to the tape\n" + " is at risk. Replace the cartridge with a data-grade tape.", + /* 0x09 */ + "C: You are trying to write to a write-protected cartridge. Remove the\n" + " write-protection or use another tape.", + /* 0x0a */ + "I: You cannot eject the cartridge because the tape drive is in use. Wait\n" + " until the operation is complete before ejecting the cartridge.", + /* 0x0b */ + "I: The tape in the drive is a cleaning cartridge.", + /* 0x0c */ + "I: You have tried to load a cartridge of a type which is not supported\n" + " by this drive.", + /* 0x0d */ + "C: The operation has failed because the tape in the drive has experienced\n" + " a mechanical failure:\n" + " 1. Discard the old tape.\n" + " 2. Restart the operation with a different tape.", + /* 0x0e */ + "C: The operation has failed because the tape in the drive has experienced\n" + " a mechanical failure:\n" + " 1. Do not attempt to extract the tape cartridge\n" + " 2. Call the tape drive supplier helpline.", + /* 0x0f */ + "W: The memory in the tape cartridge has failed, which reduces\n" + " performance. Do not use the cartridge for further write operations.", + /* 0x10 */ + "C: The operation has failed because the tape cartridge was manually\n" + " de-mounted while the tape drive was actively writing or reading.", + /* 0x11 */ + "W: You have loaded a cartridge of a type that is read-only in this drive.\n" + " The cartridge will appear as write-protected.", + /* 0x12 */ + "W: The tape directory on the tape cartridge has been corrupted. File\n" + " search performance will be degraded. The tape directory can be rebuilt\n" + " by reading all the data on the cartridge.", + /* 0x13 */ + "I: The tape cartridge is nearing the end of its calculated life. It is\n" + " recommended that you:\n" + " 1. Use another tape cartridge for your next backup.\n" + " 2. Store this tape in a safe place in case you need to restore " + " data from it.", + /* 0x14 */ + "C: The tape drive needs cleaning:\n" + " 1. If the operation has stopped, eject the tape and clean the drive.\n" + " 2. If the operation has not stopped, wait for it to finish and then\n" + " clean the drive.\n" + " Check the tape drive users manual for device specific cleaning instructions.", + /* 0x15 */ + "W: The tape drive is due for routine cleaning:\n" + " 1. Wait for the current operation to finish.\n" + " 2. The use a cleaning cartridge.\n" + " Check the tape drive users manual for device specific cleaning instructions.", + /* 0x16 */ + "C: The last cleaning cartridge used in the tape drive has worn out:\n" + " 1. Discard the worn out cleaning cartridge.\n" + " 2. Wait for the current operation to finish.\n" + " 3. Then use a new cleaning cartridge.", + /* 0x17 */ + "C: The last cleaning cartridge used in the tape drive was an invalid\n" + " type:\n" + " 1. Do not use this cleaning cartridge in this drive.\n" + " 2. Wait for the current operation to finish.\n" + " 3. Then use a new cleaning cartridge.", + /* 0x18 */ + "W: The tape drive has requested a retention operation", + /* 0x19 */ + "W: A redundant interface port on the tape drive has failed", + /* 0x1a */ + "W: A tape drive cooling fan has failed", + /* 0x1b */ + "W: A redundant power supply has failed inside the tape drive enclosure.\n" + " Check the enclosure users manual for instructions on replacing the\n" + " failed power supply.", + /* 0x1c */ + "W: The tape drive power consumption is outside the specified range.", + /* 0x1d */ + "W: Preventive maintenance of the tape drive is required. Check the tape\n" + " drive users manual for device specific preventive maintenance\n" + " tasks or call the tape drive supplier helpline.", + /* 0x1e */ + "C: The tape drive has a hardware fault:\n" + " 1. Eject the tape or magazine.\n" + " 2. Reset the drive.\n" + " 3. Restart the operation.", + /* 0x1f */ + "C: The tape drive has a hardware fault:\n" + " 1. Turn the tape drive off and then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the tape drive supplier helpline.", + /* 0x20 */ + "W: The tape drive has a problem with the application client interface:\n" + " 1. Check the cables and cable connections.\n" + " 2. Restart the operation.", + /* 0x21 */ + "C: The operation has failed:\n" + " 1. Eject the tape or magazine.\n" + " 2. Insert the tape or magazine again.\n" + " 3. Restart the operation.", + /* 0x22 */ + "W: The firmware download has failed because you have tried to use the\n" + " incorrect firmware for this tape drive. Obtain the correct\n" + " firmware and try again.", + /* 0x23 */ + "W: Environmental conditions inside the tape drive are outside the\n" + " specified humidity range.", + /* 0x24 */ + "W: Environmental conditions inside the tape drive are outside the\n" + " specified temperature range.", + /* 0x25 */ + "W: The voltage supply to the tape drive is outside the specified range.", + /* 0x26 */ + "C: A hardware failure of the tape drive is predicted. Call the tape\n" + " drive supplier helpline.", + /* 0x27 */ + "W: The tape drive may have a hardware fault. Run extended diagnostics to\n" + " verify and diagnose the problem. Check the tape drive users manual for\n" + " device specific instructions on running extended diagnostic tests.", + /* 0x28 */ + "C: The changer mechanism is having difficulty communicating with the tape\n" + " drive:\n" + " 1. Turn the autoloader off then on.\n" + " 2. Restart the operation.\n" + " 3. If problem persists, call the tape drive supplier helpline.", + /* 0x29 */ + "C: A tape has been left in the autoloader by a previous hardware fault:\n" + " 1. Insert an empty magazine to clear the fault.\n" + " 2. If the fault does not clear, turn the autoloader off and then\n" + " on again.\n" + " 3. If the problem persists, call the tape drive supplier helpline.", + /* 0x2a */ + "W: There is a problem with the autoloader mechanism.", + /* 0x2b */ + "C: The operation has failed because the autoloader door is open:\n" + " 1. Clear any obstructions from the autoloader door.\n" + " 2. Eject the magazine and then insert it again.\n" + " 3. If the fault does not clear, turn the autoloader off and then\n" + " on again.\n" + " 4. If the problem persists, call the tape drive supplier helpline.", + /* 0x2c */ + "C: The autoloader has a hardware fault:\n" + " 1. Turn the autoloader off and then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the tape drive supplier helpline.\n" + " Check the autoloader users manual for device specific instructions\n" + " on turning the device power on and off.", + /* 0x2d */ + "C: The autoloader cannot operate without the magazine,\n" + " 1. Insert the magazine into the autoloader.\n" + " 2. Restart the operation.", + /* 0x2e */ + "W: A hardware failure of the changer mechanism is predicted. Call the\n" + " tape drive supplier helpline.", + /* 0x2f */ + "I: Reserved.", + /* 0x30 */ + "I: Reserved.", + /* 0x31 */ + "I: Reserved.", + /* 0x32 */ + "W: Media statistics have been lost at some time in the past", + /* 0x33 */ + "W: The tape directory on the tape cartridge just unloaded has been\n" + " corrupted. File search performance will be degraded. The tape\n" + " directory can be rebuilt by reading all the data.", + /* 0x34 */ + "C: The tape just unloaded could not write its system area successfully:\n" + " 1. Copy data to another tape cartridge.\n" + " 2. Discard the old cartridge.", + /* 0x35 */ + "C: The tape system are could not be read successfully at load time:\n" + " 1. Copy data to another tape cartridge.\n", + /* 0x36 */ + "C: The start or data could not be found on the tape:\n" + " 1. Check you are using the correct format tape.\n" + " 2. Discard the tape or return the tape to your supplier", + /* 0x37 */ + "C: The operation has failed because the media cannot be loaded\n" + " and threaded.\n" + " 1. Remove the cartridge, inspect it as specified in the product\n" + " manual, and retry the operation.\n" + " 2. If the problem persists, call the tape drive supplier help line.", + /* 0x38 */ + "C: The operation has failed because the medium cannot be unloaded:\n" + " 1. Do not attempt to extract the tape cartridge.\n" + " 2. Call the tape driver supplier help line.", + /* 0x39 */ + "C: The tape drive has a problem with the automation interface:\n" + " 1. Check the power to the automation system.\n" + " 2. Check the cables and cable connections.\n" + " 3. Call the supplier help line if problem persists.", + /* 0x3a */ + "W: The tape drive has reset itself due to a detected firmware\n" + " fault. If problem persists, call the supplier help line.", + }; + +const char * scsiTapeAlertsTapeDevice(unsigned short code) +{ + const int num = sizeof(TapeAlertsMessageTable) / + sizeof(TapeAlertsMessageTable[0]); + + return (code < num) ? TapeAlertsMessageTable[code] : "Unknown Alert"; +} + +// The first character (W, C, I) tells the severity +static const char * ChangerTapeAlertsMessageTable[]= { + " ", + /* 0x01 */ + "C: The library mechanism is having difficulty communicating with the\n" + " drive:\n" + " 1. Turn the library off then on.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x02 */ + "W: There is a problem with the library mechanism. If problem persists,\n" + " call the library supplier help line.", + /* 0x03 */ + "C: The library has a hardware fault:\n" + " 1. Reset the library.\n" + " 2. Restart the operation.\n" + " Check the library users manual for device specific instructions on resetting\n" + " the device.", + /* 0x04 */ + "C: The library has a hardware fault:\n" + " 1. Turn the library off then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the library supplier help line.\n" + " Check the library users manual for device specific instructions on turning the\n" + " device power on and off.", + /* 0x05 */ + "W: The library mechanism may have a hardware fault.\n" + " Run extended diagnostics to verify and diagnose the problem. Check the library\n" + " users manual for device specific instructions on running extended diagnostic\n" + " tests.", + /* 0x06 */ + "C: The library has a problem with the host interface:\n" + " 1. Check the cables and connections.\n" + " 2. Restart the operation.", + /* 0x07 */ + "W: A hardware failure of the library is predicted. Call the library\n" + " supplier help line.", + /* 0x08 */ + "W: Preventive maintenance of the library is required.\n" + " Check the library users manual for device specific preventative maintenance\n" + " tasks, or call your library supplier help line.", + /* 0x09 */ + "C: General environmental conditions inside the library are outside the\n" + " specified humidity range.", + /* 0x0a */ + "C: General environmental conditions inside the library are outside the\n" + " specified temperature range.", + /* 0x0b */ + "C: The voltage supply to the library is outside the specified range.\n" + " There is a potential problem with the power supply or failure of\n" + " a redundant power supply.", + /* 0x0c */ + "C: A cartridge has been left inside the library by a previous hardware\n" + " fault:\n" + " 1. Insert an empty magazine to clear the fault.\n" + " 2. If the fault does not clear, turn the library off and then on again.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x0d */ + "W: There is a potential problem with the drive ejecting cartridges or\n" + " with the library mechanism picking a cartridge from a slot.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x0e */ + "W: There is a potential problem with the library mechanism placing a\n" + " cartridge into a slot.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x0f */ + "W: There is a potential problem with the drive or the library mechanism\n" + " loading cartridges, or an incompatible cartridge.", + /* 0x10 */ + "C: The library has failed because the door is open:\n" + " 1. Clear any obstructions from the library door.\n" + " 2. Close the library door.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x11 */ + "C: There is a mechanical problem with the library media import/export\n" + " mailslot.", + /* 0x12 */ + "C: The library cannot operate without the magazine.\n" + " 1. Insert the magazine into the library.\n" + " 2. Restart the operation.", + /* 0x13 */ + "W: Library security has been compromised.", + /* 0x14 */ + "I: The library security mode has been changed.\n" + " The library has either been put into secure mode, or the library has exited\n" + " the secure mode.\n" + " This is for information purposes only. No action is required.", + /* 0x15 */ + "I: The library has been manually turned offline and is unavailable for use.", + /* 0x16 */ + "I: A drive inside the library has been taken offline.\n" + " This is for information purposes only. No action is required.", + /* 0x17 */ + "W: There is a potential problem with the bar code label or the scanner\n" + " hardware in the library mechanism.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x18 */ + "C: The library has detected an inconsistency in its inventory.\n" + " 1. Redo the library inventory to correct inconsistency.\n" + " 2. Restart the operation.\n" + " Check the applications users manual or the hardware users manual for\n" + " specific instructions on redoing the library inventory.", + /* 0x19 */ + "W: A library operation has been attempted that is invalid at this time.", + /* 0x1a */ + "W: A redundant interface port on the library has failed.", + /* 0x1b */ + "W: A library cooling fan has failed.", + /* 0x1c */ + "W: A redundant power supply has failed inside the library. Check the\n" + " library users manual for instructions on replacing the failed power supply.", + /* 0x1d */ + "W: The library power consumption is outside the specified range.", + /* 0x1e */ + "C: A failure has occurred in the cartridge pass-through mechanism between\n" + " two library modules.", + /* 0x1f */ + "C: A cartridge has been left in the pass-through mechanism from a\n" + " previous hardware fault. Check the library users guide for instructions on\n" + " clearing this fault.", + /* 0x20 */ + "I: The library was unable to read the bar code on a cartridge.", +}; + +const char * scsiTapeAlertsChangerDevice(unsigned short code) +{ + const int num = sizeof(ChangerTapeAlertsMessageTable) / + sizeof(ChangerTapeAlertsMessageTable[0]); + + return (code < num) ? ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; +} + + +/* this is a subset of the SCSI additional sense code strings indexed + * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d) + */ +static const char * strs_for_asc_5d[] = { + /* 0x00 */ "FAILURE PREDICTION THRESHOLD EXCEEDED", + "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED", + "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED", + "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 0x10 */ "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS", + "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED", + "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT", + "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x20 */ "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS", + "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED", + "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE", + "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT", + "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x30 */ "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS", + "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED", + "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE", + "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT", + "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x40 */ "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS", + "SERVO IMPENDING FAILURE CONTROLLER DETECTED", + "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE", + "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT", + "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x50 */ "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS", + "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED", + "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT", + "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x60 */ "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS", + "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED", + "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT", + /* 0x6c */ "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"}; + + +/* this is a subset of the SCSI additional sense code strings indexed + * * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb) + * */ +static const char * strs_for_asc_b[] = { + /* 0x00 */ "WARNING", + "WARNING - SPECIFIED TEMPERATURE EXCEEDED", + "WARNING - ENCLOSURE DEGRADED"}; + +static char spare_buff[128]; + +const char * scsiGetIEString(UINT8 asc, UINT8 ascq) +{ + const char * rp; + + if (SCSI_ASC_IMPENDING_FAILURE == asc) { + if (ascq == 0xff) + return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)"; + else if (ascq < + (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) { + rp = strs_for_asc_5d[ascq]; + if (strlen(rp) > 0) + return rp; + } + snprintf(spare_buff, sizeof(spare_buff), + "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq); + return spare_buff; + } else if (SCSI_ASC_WARNING == asc) { + if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) { + rp = strs_for_asc_b[ascq]; + if (strlen(rp) > 0) + return rp; + } + snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq); + return spare_buff; + } + return NULL; /* not a IE additional sense code */ +} + + +/* This is not documented in t10.org, page 0x80 is vendor specific */ +/* Some IBM disks do an offline read-scan when they get this command. */ +int scsiSmartIBMOfflineTest(int device) +{ + UINT8 tBuf[256]; + + memset(tBuf, 0, sizeof(tBuf)); + /* Build SMART Off-line Immediate Diag Header */ + tBuf[0] = 0x80; /* Page Code */ + tBuf[1] = 0x00; /* Reserved */ + tBuf[2] = 0x00; /* Page Length MSB */ + tBuf[3] = 0x04; /* Page Length LSB */ + tBuf[4] = 0x03; /* SMART Revision */ + tBuf[5] = 0x00; /* Reserved */ + tBuf[6] = 0x00; /* Off-line Immediate Time MSB */ + tBuf[7] = 0x00; /* Off-line Immediate Time LSB */ + return scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8); +} + +int scsiSmartDefaultSelfTest(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0); +} + +int scsiSmartShortSelfTest(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0); +} + +int scsiSmartExtendSelfTest(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, + NULL, 0); +} + +int scsiSmartShortCapSelfTest(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0); +} + +int scsiSmartExtendCapSelfTest(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, + NULL, 0); +} + +int scsiSmartSelfTestAbort(int device) +{ + return scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0); +} + +/* Returns 0 and the expected duration of an extended self test (in seconds) + if successful; any other return value indicates a failure. */ +int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len) +{ + int err, offset, res; + UINT8 buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return err; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return err; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if (offset < 0) + return -EINVAL; + if (buff[offset + 1] >= 0xa) { + res = (buff[offset + 10] << 8) | buff[offset + 11]; + *durationSec = res; + return 0; + } + else + return -EINVAL; +} + +void scsiDecodeErrCounterPage(unsigned char * resp, + struct scsiErrorCounter *ecp) +{ + int k, j, num, pl, pc; + unsigned char * ucp; + unsigned char * xp; + uint64_t * ullp; + + memset(ecp, 0, sizeof(*ecp)); + num = (resp[2] << 8) | resp[3]; + ucp = &resp[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + ecp->gotPC[pc] = 1; + ullp = &ecp->counter[pc]; + break; + default: + ecp->gotExtraPC = 1; + ullp = &ecp->counter[7]; + break; + } + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(*ullp)) { + xp += (k - sizeof(*ullp)); + k = sizeof(*ullp); + } + *ullp = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + *ullp <<= 8; + *ullp |= xp[j]; + } + num -= pl; + ucp += pl; + } +} + +void scsiDecodeNonMediumErrPage(unsigned char *resp, + struct scsiNonMediumError *nmep) +{ + int k, j, num, pl, pc, szof; + unsigned char * ucp; + unsigned char * xp; + + memset(nmep, 0, sizeof(*nmep)); + num = (resp[2] << 8) | resp[3]; + ucp = &resp[0] + 4; + szof = sizeof(nmep->counterPC0); + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: + nmep->gotPC0 = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterPC0 = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterPC0 <<= 8; + nmep->counterPC0 |= xp[j]; + } + break; + case 0x8009: + nmep->gotTFE_H = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterTFE_H = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterTFE_H <<= 8; + nmep->counterTFE_H |= xp[j]; + } + break; + case 0x8015: + nmep->gotPE_H = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterPE_H = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterPE_H <<= 8; + nmep->counterPE_H |= xp[j]; + } + break; + default: + nmep->gotExtraPC = 1; + break; + } + num -= pl; + ucp += pl; + } +} + +/* Counts number of failed self-tests. Also encodes the poweron_hour + of the most recent failed self-test. Return value is negative if + this function has a problem (typically -1), otherwise the bottom 8 + bits are the number of failed self tests and the 16 bits above that + are the poweron hour of the most recent failure. Note: aborted self + tests (typically by the user) and self tests in progress are not + considered failures. See Working Draft SCSI Primary Commands - 3 + (SPC-3) section 7.2.10 T10/1416-D Rev 15 */ +int scsiCountFailedSelfTests(int fd, int noisy) +{ + int num, k, n, err, res, fails, fail_hour; + UINT8 * ucp; + unsigned char resp[LOG_RESP_SELF_TEST_LEN]; + + if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, + LOG_RESP_SELF_TEST_LEN, 0))) { + if (noisy) + pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err)); + return -1; + } + if (resp[0] != SELFTEST_RESULTS_LPAGE) { + if (noisy) + pout("Self-test Log Sense Failed, page mismatch\n"); + return -1; + } + // compute page length + num = (resp[2] << 8) + resp[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + if (noisy) + pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num); + return -1; + } + fails = 0; + fail_hour = 0; + // loop through the twenty possible entries + for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) { + + // 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; + res = ucp[4] & 0xf; + if ((res > 2) && (res < 8)) { + fails++; + if (1 == fails) + fail_hour = (ucp[6] << 8) + ucp[7]; + } + } + return (fail_hour << 8) + fails; +} + +/* Returns 0 if able to read self test log page; then outputs 1 into + *inProgress if self test still in progress, else outputs 0. */ +int scsiSelfTestInProgress(int fd, int * inProgress) +{ + int num; + UINT8 * ucp; + unsigned char resp[LOG_RESP_SELF_TEST_LEN]; + + if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, + LOG_RESP_SELF_TEST_LEN, 0)) + return -1; + if (resp[0] != SELFTEST_RESULTS_LPAGE) + return -1; + // compute page length + num = (resp[2] << 8) + resp[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + return -1; + } + ucp = resp + 4; + if (inProgress) + *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0; + return 0; +} + +/* Returns a negative value if failed to fetch Contol mode page or it was + malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD + bit is set. Examines default mode page when current==0 else examines + current mode page. */ +int scsiFetchControlGLTSD(int device, int modese_len, int current) +{ + int err, offset; + UINT8 buff[64]; + int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, pc, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return -EINVAL; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, pc, + buff, sizeof(buff)); + if (err) + return -EINVAL; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset >= 0) && (buff[offset + 1] >= 0xa)) + return (buff[offset + 2] & 2) ? 1 : 0; + return -EINVAL; +} + +/* Attempts to set or clear GLTSD bit in Control mode page. If enabled is + 0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if + successful, negative if low level error, > 0 if higher level error (e.g. + SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */ +int scsiSetControlGLTSD(int device, int enabled, int modese_len) +{ + int err, offset, resp_len, sp; + UINT8 buff[64]; + UINT8 ch_buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return err; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return err; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset < 0) || (buff[offset + 1] < 0xa)) + return SIMPLE_ERR_BAD_RESP; + + if (enabled) + enabled = 2; + if (enabled == (buff[offset + 2] & 2)) + return 0; /* GLTSD already in wanted state so nothing to do */ + + if (modese_len == 6) + err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + else + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + if (err) + return err; + if (0 == (ch_buff[offset + 2] & 2)) + return SIMPLE_ERR_BAD_PARAM; /* GLTSD bit not chageable */ + + if (10 == modese_len) { + resp_len = (buff[0] << 8) + buff[1] + 2; + memset(buff, 0, 2); + } else { + resp_len = buff[0] + 1; + memset(buff, 0, 1); + } + sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */ + buff[offset] &= 0x7f; /* mask off PS bit */ + if (enabled) + buff[offset + 2] |= 0x2; /* set GLTSD bit */ + else + buff[offset + 2] &= 0xfd; /* clear GLTSD bit */ + if (10 == modese_len) + err = scsiModeSelect10(device, sp, buff, resp_len); + else if (6 == modese_len) + err = scsiModeSelect(device, sp, buff, resp_len); + return err; +} + +/* Returns a negative value if failed to fetch Protocol specific port mode + page or it was malformed. Returns transport protocol identifier when + value >= 0 . */ +int scsiFetchTransportProtocol(int device, int modese_len) +{ + int err, offset; + UINT8 buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return -EINVAL; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return -EINVAL; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset >= 0) && (buff[offset + 1] > 1)) { + if ((0 == (buff[offset] & 0x40)) && /* SPF==0 */ + (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) + return (buff[offset + 2] & 0xf); + } + return -EINVAL; +} + + +/* This is Linux specific code to look for the libata ATA-SCSI + simulator in the vendor device identification page. + Returns 1 if found else 0. */ +int isLinuxLibAta(unsigned char * vpd_di_buff, int len) +{ + int k, id_len, c_set, assoc, id_type, i_len; + unsigned char * ucp; + unsigned char * ip; + + if (len < 4) { + /* Device identification VPD page length too short */ + return 0; + } + len -= 4; + ucp = vpd_di_buff + 4; + for (k = 0; k < len; k += id_len, ucp += id_len) { + i_len = ucp[3]; + id_len = i_len + 4; + if ((k + id_len) > len) { + /* short descriptor, badly formed */ + return 0; + } + ip = ucp + 4; + c_set = (ucp[0] & 0xf); + assoc = ((ucp[1] >> 4) & 0x3); + id_type = (ucp[1] & 0xf); + if ((0 == id_type) && (2 == c_set) && (0 == assoc) && + (0 == strncmp((const char *)ip, + "Linux ATA-SCSI simulator", i_len))) { + return 1; + } + } + return 0; +} diff --git a/sm5/scsiprint.c b/sm5/scsiprint.c new file mode 100644 index 0000000000000000000000000000000000000000..85f65b2d445db99443d8126354aaf63ede8e0383 --- /dev/null +++ b/sm5/scsiprint.c @@ -0,0 +1,1095 @@ +/* + * scsiprint.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> + * + * Additional SCSI work: + * Copyright (C) 2003-4 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 <string.h> +#include <fcntl.h> +#include <errno.h> + +#include "config.h" +#include "int64.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.c,v 1.89 2004/09/06 03:41:13 dpgilbert Exp $" +CONFIG_H_CVSID EXTERN_H_CVSID INT64_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; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +UINT8 gBuf[GBUF_SIZE]; +#define LOG_RESP_LEN 252 +#define LOG_RESP_LONG_LEN 8192 +#define LOG_RESP_TAPE_ALERT_LEN 0x144 + +/* 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; +static int gSeagateCacheLPage = 0; +static int gSeagateFactoryLPage = 0; + +/* Mode pages supported */ +static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ + +/* Remember last successful mode sense/select command */ +static int modese_len = 0; + +// Compares failure type to policy in effect, and either exits or +// simply returns to the calling routine. +extern void failuretest(int type, int returnvalue); + +static void scsiGetSupportedLogPages(int device) +{ + int i, err; + + if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 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_LPAGE: + gTempLPage = 1; + break; + case STARTSTOP_CYCLE_COUNTER_LPAGE: + gStartStopLPage = 1; + break; + case SELFTEST_RESULTS_LPAGE: + gSelfTestLPage = 1; + break; + case IE_LPAGE: + gSmartLPage = 1; + break; + case TAPE_ALERTS_LPAGE: + gTapeAlertsLPage = 1; + break; + case SEAGATE_CACHE_LPAGE: + gSeagateCacheLPage = 1; + break; + case SEAGATE_FACTORY_LPAGE: + gSeagateFactoryLPage = 1; + break; + default: + break; + } + } +} + +void scsiGetSmartData(int device, int attribs) +{ + UINT8 asc; + UINT8 ascq; + UINT8 currenttemp = 0; + UINT8 triptemp = 0; + const char * cp; + int err; + + PRINT_ON(con); + if ((err = scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, + &ascq, ¤ttemp, &triptemp))) { + /* error message already announced */ + PRINT_OFF(con); + return; + } + PRINT_OFF(con); + cp = scsiGetIEString(asc, ascq); + if (cp) { + PRINT_ON(con); + pout("SMART Health Status: %s [asc=%x,ascq=%x]\n", cp, asc, ascq); + PRINT_OFF(con); + } else if (gIecMPage) + pout("SMART Health Status: OK\n"); + + if (attribs && !gTempLPage) { + if (currenttemp || triptemp) + pout("\n"); + if (currenttemp) { + if (255 != currenttemp) + pout("Current Drive Temperature: %d C\n", currenttemp); + else + pout("Current Drive Temperature: <not available>\n"); + } + if (triptemp) + pout("Drive Trip Temperature: %d C\n", triptemp); + } +} + + +// 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; + + PRINT_ON(con); + if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, gBuf, + LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { + pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return -1; + } + if (gBuf[0] != 0x2e) { + pout("TapeAlerts Log Sense Failed\n"); + PRINT_OFF(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; + } + } + } + } + PRINT_OFF(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_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_LPAGE) { + PRINT_ON(con); + pout("StartStop Log Sense Failed, page mismatch\n"); + PRINT_OFF(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 maximum start stop count: %u times\n", + recommendedStartStop); + } +} + +static void scsiPrintSeagateCacheLPage(int device) +{ + int k, j, num, pl, pc, err, len; + unsigned char * ucp; + unsigned char * xp; + uint64_t ull; + + if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != SEAGATE_CACHE_LPAGE) { + PRINT_ON(con); + pout("Seagate Cache Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return; + } + len = ((gBuf[2] << 8) | gBuf[3]) + 4; + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: case 1: case 2: case 3: case 4: + break; + default: + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("Vendor (Seagate) cache lpage has unexpected parameter" + ", skip\n"); + PRINT_OFF(con); + } + return; + } + num -= pl; + ucp += pl; + } + pout("Vendor (Seagate) cache information\n"); + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: pout(" Blocks sent to initiator"); break; + case 1: pout(" Blocks received from initiator"); break; + case 2: pout(" Blocks read from cache and sent to initiator"); break; + case 3: pout(" Number of read and write commands whose size " + "<= segment size"); break; + case 4: pout(" Number of read and write commands whose size " + "> segment size"); break; + default: pout(" Unknown Seagate parameter code [0x%x]", pc); break; + } + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(ull)) { + xp += (k - (int)sizeof(ull)); + k = (int)sizeof(ull); + } + ull = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + ull <<= 8; + ull |= xp[j]; + } + pout(" = %"PRIu64"\n", ull); + num -= pl; + ucp += pl; + } +} + +static void scsiPrintSeagateFactoryLPage(int device) +{ + int k, j, num, pl, pc, len, err; + unsigned char * ucp; + unsigned char * xp; + uint64_t ull; + + if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != SEAGATE_FACTORY_LPAGE) { + PRINT_ON(con); + pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return; + } + len = ((gBuf[2] << 8) | gBuf[3]) + 4; + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: case 8: + break; + default: + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("\nVendor (Seagate/Hitachi) factory lpage has unexpected" + " parameter, skip\n"); + PRINT_OFF(con); + } + return; + } + num -= pl; + ucp += pl; + } + pout("Vendor (Seagate/Hitachi) factory information\n"); + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: pout(" number of hours powered up"); break; + case 8: pout(" number of minutes until next internal SMART test"); + break; + default: pout(" Unknown parameter code [0x%x]", pc); break; + } + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(ull)) { + xp += (k - (int)sizeof(ull)); + k = (int)sizeof(ull); + } + ull = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + ull <<= 8; + ull |= xp[j]; + } + if (0 == pc) + pout(" = %.2f\n", uint64_to_double(ull) / 60.0 ); + else + pout(" = %"PRIu64"\n", ull); + num -= pl; + ucp += pl; + } +} + +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_LPAGE, gBuf, + LOG_RESP_LEN, 0)) { + scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); + found[0] = 1; + } + if (0 == scsiLogSense(device, WRITE_ERROR_COUNTER_LPAGE, gBuf, + LOG_RESP_LEN, 0)) { + scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); + found[1] = 1; + } + if (0 == scsiLogSense(device, VERIFY_ERROR_COUNTER_LPAGE, 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 by Total " + "Correction Gigabytes Total\n"); + pout(" EEC rereads/ errors " + "algorithm processed uncorrected\n"); + pout(" fast | delayed rewrites corrected " + "invocations [10^9 bytes] errors\n"); + for (k = 0; k < 3; ++k) { + if (! found[k]) + continue; + ecp = &errCounterArr[k]; + pout("%s%8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64, + pageNames[k], ecp->counter[0], ecp->counter[1], + ecp->counter[2], ecp->counter[3], ecp->counter[4]); + processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0; + pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]); + } + } + else + pout("\nError Counter logging not supported\n"); + if (0 == scsiLogSense(device, NON_MEDIUM_ERROR_LPAGE, gBuf, + LOG_RESP_LEN, 0)) { + scsiDecodeNonMediumErrPage(gBuf, &nme); + if (nme.gotPC0) + pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0); + if (nme.gotTFE_H) + pout("Track following error count [Hitachi]: %8"PRIu64"\n", + nme.counterTFE_H); + if (nme.gotPE_H) + pout("Positioning error count [Hitachi]: %8"PRIu64"\n", + nme.counterPE_H); + } + if (0 == scsiLogSense(device, LAST_N_ERROR_LPAGE, gBuf, + LOG_RESP_LONG_LEN, 0)) { + unsigned char * ucp; + int num, k, pc, pl; + + num = (gBuf[2] << 8) + gBuf[3]; + num = (num < LOG_RESP_LONG_LEN) ? num : LOG_RESP_LONG_LEN; + ucp = gBuf + 4; + if (num < 4) + pout("\nNo error events logged\n"); + else { + pout("Last n error events log page\n"); + for (k = num; k > 0; k -= pl, ucp += pl) { + if (k < 3) { + pout(" <<short Last n error events log page>>\n"); + break; + } + pl = ucp[3] + 4; + pc = (ucp[0] << 8) + ucp[1]; + pout(" Error event %d:\n", pc); + if (ucp[2] & 0x2) { + pout(" [binary]:\n"); + dStrHex((const char *)ucp + 4, pl - 4, 1); + } else + pout(" %.*s\n", pl - 4, (const char *)(ucp + 4)); + num -= pl; + ucp += pl; + } + } + } else + pout("\nError Events logging not supported\n"); +} + +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; + uint64_t ull=0; + + if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, gBuf, + LOG_RESP_SELF_TEST_LEN, 0))) { + PRINT_ON(con); + pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + if (gBuf[0] != SELFTEST_RESULTS_LPAGE) { + PRINT_ON(con); + pout("Self-test Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return 1; + } + // compute page length + num = (gBuf[2] << 8) + gBuf[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + PRINT_ON(con); + pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num); + PRINT_OFF(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 ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) + pout(" 0x%16"PRIx64, 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, + modese_len)) && (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" +}; + +static const char * transport_proto_arr[] = { + "Fibre channel (FCP-2)", + "Parallel SCSI (SPI-4)", + "SSA", + "IEEE 1394 (SBP-2)", + "RDMA (SRP)", + "iSCSI", + "SAS", + "ADT", + "0x8", + "0x9", + "0xa", + "0xb", + "0xc", + "0xd", + "0xe", + "0xf" +}; + +/* Returns 0 on success, 1 on general error and 2 for early, clean exit */ +static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) +{ + char manufacturer[9]; + char product[17]; + char revision[5]; + char timedatetz[DATEANDEPOCHLEN]; + struct scsi_iec_mode_page iec; + int err, iec_err, len, req_len, avail_len, val; + int is_tape = 0; + int peri_dt = 0; + int returnval=0; + + memset(gBuf, 0, 96); + req_len = 36; + if ((err = scsiStdInquiry(device, gBuf, req_len))) { + PRINT_ON(con); + pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); + pout("Retrying with a 64 byte Standard Inquiry\n"); + PRINT_OFF(con); + /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ + req_len = 64; + if ((err = scsiStdInquiry(device, gBuf, req_len))) { + PRINT_ON(con); + pout("Standard Inquiry (64 bytes) failed [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + } + avail_len = gBuf[4] + 5; + len = (avail_len < req_len) ? avail_len : req_len; + peri_dt = gBuf[0] & 0x1f; + if (peripheral_type) + *peripheral_type = peri_dt; + if (! all) + return 0; + + if (len < 36) { + PRINT_ON(con); + pout("Short INQUIRY response, skip product id\n"); + PRINT_OFF(con); + return 1; + } + memset(manufacturer, 0, sizeof(manufacturer)); + strncpy(manufacturer, (char *)&gBuf[8], 8); + + memset(product, 0, sizeof(product)); + strncpy(product, (char *)&gBuf[16], 16); + + memset(revision, 0, sizeof(revision)); + strncpy(revision, (char *)&gBuf[32], 4); + pout("Device: %s %s Version: %s\n", manufacturer, product, revision); + + if (0 == strncmp(manufacturer, "3ware", 5)) { + pout("please try '-d 3ware,N'\n"); + return 2; + } else if ((len >= 42) && (0 == strncmp(&gBuf[36], "MVSATA", 6))) { + pout("please try '-d marvell'\n"); + return 2; + } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) { + /* <<<< This is Linux specific code to detect SATA disks using a + SCSI-ATA command translation layer. This may be generalized + later when the t10.org SAT project matures. >>>> */ + req_len = 96; + memset(gBuf, 0, req_len); + if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) { + PRINT_ON(con); + pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4; + len = (avail_len < req_len) ? avail_len : req_len; + if (isLinuxLibAta(gBuf, len)) { + pout("\nSATA disks accessed via libata are not currently " + "supported by\nsmartmontools. When libata is given " + "an ATA pass-thru ioctl() then an\nadditional '-d libata'" + " device type will be added to smartmontools.\n"); + return 2; + } + } + + /* Do this here to try and detect badly conforming devices (some USB + keys) that will lock up on a InquiryVpd or log sense or ... */ + if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) { + if (SIMPLE_ERR_BAD_RESP == iec_err) { + pout(">> Terminate command early due to bad response to IEC " + "mode page\n"); + PRINT_OFF(con); + gIecMPage = 0; + return 1; + } + } else + modese_len = iec.modese_len; + + 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) { + PRINT_ON(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); + PRINT_OFF(con); + } + + // print SCSI peripheral device type + if (peri_dt < (int)(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); + + // See if transport protocol is known + val = scsiFetchTransportProtocol(device, modese_len); + if ((val >= 0) && (val <= 0xf)) + pout("Transport protocol: %s\n", transport_proto_arr[val]); + + // 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) { + PRINT_ON(con); + if (!is_tape) + pout("device is NOT READY (e.g. spun down, busy)\n"); + else + pout("device is NOT READY (e.g. no tape)\n"); + PRINT_OFF(con); + } else if (SIMPLE_ERR_NO_MEDIUM == err) { + PRINT_ON(con); + pout("NO MEDIUM present on device\n"); + PRINT_OFF(con); + } else if (SIMPLE_ERR_BECOMING_READY == err) { + PRINT_ON(con); + pout("device becoming ready (wait)\n"); + PRINT_OFF(con); + } else { + PRINT_ON(con); + pout("device Test Unit Ready [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + } + failuretest(MANDATORY_CMD, returnval|=FAILID); + } + + if (iec_err) { + if (!is_tape) { + PRINT_ON(con); + pout("Device does not support SMART"); + if (con->reportscsiioctl > 0) + pout(" [%s]\n", scsiErrString(iec_err)); + else + pout("\n"); + PRINT_OFF(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, modese_len))) { + PRINT_ON(con); + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } else + modese_len = iec.modese_len; + + if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { + PRINT_ON(con); + pout("unable to enable Exception control and warning [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + /* Need to refetch 'iec' since could be modified by previous call */ + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + return 1; + } else + modese_len = iec.modese_len; + + 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, modese_len))) { + PRINT_ON(con); + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } else + modese_len = iec.modese_len; + + if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { + PRINT_ON(con); + pout("unable to disable Exception control and warning [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + /* Need to refetch 'iec' since could be modified by previous call */ + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + return 1; + } else + modese_len = iec.modese_len; + + 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); +} + +/* Main entry point used by smartctl command. Return 0 for success */ +int scsiPrintMain(int fd) +{ + int checkedSupportedLogPages = 0; + UINT8 peripheral_type = 0; + int returnval = 0; + int res, durationSec; + + res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo); + if (res) { + if (2 == res) + return 0; + else + 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->smartautosaveenable) { + if (scsiSetControlGLTSD(fd, 0, modese_len)) { + pout("Enable autosave (clear GLTSD bit) failed\n"); + failuretest(OPTIONAL_CMD,returnval |= FAILSMART); + } + } + + if (con->smartautosavedisable) { + if (scsiSetControlGLTSD(fd, 1, modese_len)) { + pout("Disable autosave (set GLTSD bit) failed\n"); + failuretest(OPTIONAL_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"); + } else { /* disk, cd/dvd, enclosure, etc */ + scsiGetSmartData(fd, con->smartvendorattrib); + } + } + if (con->smartvendorattrib) { + if (! checkedSupportedLogPages) + scsiGetSupportedLogPages(fd); + if (gTempLPage) { + if (con->checksmart) + pout("\n"); + scsiPrintTemp(fd); + } + if (gStartStopLPage) + scsiGetStartStopData(fd); + if (SCSI_PT_DIRECT_ACCESS == peripheral_type) { + if (gSeagateCacheLPage) + scsiPrintSeagateCacheLPage(fd); + if (gSeagateFactoryLPage) + scsiPrintSeagateFactoryLPage(fd); + } + } + if (con->smarterrorlog) { + scsiPrintErrorCounterLog(fd); + if (1 == scsiFetchControlGLTSD(fd, modese_len, 1)) + pout("\n[GLTSD (Global Logging Target Save Disable) set. " + "Enable Save with '-S on']\n"); + } + 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"); + if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, + modese_len)) && (durationSec > 0)) { + time_t t = time(NULL); + + t += durationSec; + pout("Please wait %d minutes for test to complete.\n", + durationSec / 60); + pout("Estimated completion time: %s\n", ctime(&t)); + } + 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; +} diff --git a/sm5/smartctl.c b/sm5/smartctl.c new file mode 100644 index 0000000000000000000000000000000000000000..6f23577e84da9ce8ac32d63d39e1fc1938303505 --- /dev/null +++ b/sm5/smartctl.c @@ -0,0 +1,890 @@ +/* + * smartctl.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2000 Michael Cornwell <cornwell@acm.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 "config.h" +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <string.h> +#include <stdarg.h> +#ifdef HAVE_GETOPT_LONG +#include <getopt.h> +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) +#include <unistd.h> +#endif +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "int64.h" +#include "knowndrives.h" +#include "scsicmds.h" +#include "scsiprint.h" +#include "smartctl.h" +#include "utility.h" + +#ifdef NEED_SOLARIS_ATA_CODE +extern const char *os_solaris_ata_s_cvsid; +#endif +#if defined(_WIN32) && defined(_MSC_VER) +extern const char *int64_vc6_c_cvsid; +#endif +extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid; +const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.135 2004/08/18 19:27:45 likewise Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; + +// This is a block containing all the "control variables". We declare +// this globally in this file, and externally in other files. +smartmonctrl *con=NULL; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Track memory use +extern int64_t bytes; + +void printslogan(){ + pout("smartctl version %s [%s] Copyright (C) 2002-4 Bruce Allen\n", PACKAGE_VERSION, SMARTMONTOOLS_BUILD_HOST); + pout("Home page is " PACKAGE_HOMEPAGE "\n\n"); + return; +} + +void PrintOneCVS(const char *a_cvs_id){ + char out[CVSMAXLEN]; + printone(out,a_cvs_id); + pout("%s",out); + return; +} + +void printcopy(){ + char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]"; + + pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n"); + pout("is free software, and you are welcome to redistribute it\n"); + pout("under the terms of the GNU General Public License Version 2.\n"); + pout("See http://www.gnu.org for further details.\n\n"); + pout("CVS version IDs of files used to build this code are:\n"); + PrintOneCVS(atacmdnames_c_cvsid); + PrintOneCVS(atacmds_c_cvsid); + PrintOneCVS(ataprint_c_cvsid); +#if defined(_WIN32) && defined(_MSC_VER) + PrintOneCVS(int64_vc6_c_cvsid); +#endif + PrintOneCVS(knowndrives_c_cvsid); + PrintOneCVS(os_XXXX_c_cvsid); +#ifdef NEED_SOLARIS_ATA_CODE + PrintOneCVS(os_solaris_ata_s_cvsid); +#endif + PrintOneCVS(scsicmds_c_cvsid); + PrintOneCVS(scsiprint_c_cvsid); + PrintOneCVS(smartctl_c_cvsid); + PrintOneCVS(utility_c_cvsid); + pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"); + pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"); + pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n"); + pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n"); + pout("smartmontools configure arguments: %s\n", configargs); + return; +} + +void UsageSummary(){ + pout("\nUse smartctl -h to get a usage summary\n\n"); + return; +} + +/* void prints help information for command syntax */ +void Usage (void){ + printf("Usage: smartctl [options] device\n\n"); + printf("============================================ SHOW INFORMATION OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -h, --help, --usage\n" +" Display this help and exit\n\n" +" -V, --version, --copyright, --license\n" +" Print license, copyright, and version information and exit\n\n" +" -i, --info \n" +" Show identity information for device\n\n" +" -a, --all \n" +" Show all SMART information for device\n\n" + ); +#else + printf( +" -h Display this help and exit\n" +" -V Print license, copyright, and version information\n" +" -i Show identity information for device\n" +" -a Show all SMART information for device\n\n" + ); +#endif + printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -q TYPE, --quietmode=TYPE (ATA)\n" +" Set smartctl quiet mode to one of: errorsonly, silent\n\n" +" -d TYPE, --device=TYPE\n" +" Specify device type to one of: ata, scsi, marvell, 3ware,N\n\n" +" -T TYPE, --tolerance=TYPE (ATA)\n" +" Tolerance: normal, conservative, permissive, verypermissive\n\n" +" -b TYPE, --badsum=TYPE (ATA)\n" +" Set action on bad checksum to one of: warn, exit, ignore\n\n" +" -r TYPE, --report=TYPE\n" +" Report transactions (see man page)\n\n" + ); +#else + printf( +" -q TYPE Set smartctl quiet mode to one of: errorsonly, silent (ATA)\n" +" -d TYPE Specify device type to one of: ata, scsi, 3ware,N\n" +" -T TYPE Tolerance: normal, conservative,permissive,verypermissive (ATA\n" +" -b TYPE Set action on bad checksum to one of: warn, exit, ignore (ATA)\n" +" -r TYPE Report transactions (see man page)\n\n" + ); +#endif + printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -s VALUE, --smart=VALUE\n" +" Enable/disable SMART on device (on/off)\n\n" +" -o VALUE, --offlineauto=VALUE (ATA)\n" +" Enable/disable automatic offline testing on device (on/off)\n\n" +" -S VALUE, --saveauto=VALUE (ATA)\n" +" Enable/disable Attribute autosave on device (on/off)\n\n" + ); +#else + printf( +" -s VALUE Enable/disable SMART on device (on/off)\n" +" -o VALUE Enable/disable device automatic offline testing (on/off) (ATA)\n" +" -S VALUE Enable/disable device Attribute autosave (on/off) (ATA)\n\n" + ); +#endif + printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -H, --health\n" +" Show device SMART health status\n\n" +" -c, --capabilities (ATA)\n" +" Show device SMART capabilities\n\n" +" -A, --attributes \n" +" Show device SMART vendor-specific Attributes and values\n\n" +" -l TYPE, --log=TYPE\n" +" Show device log. TYPE: error, selftest, selective, directory\n\n" +" -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" +" Set display OPTION for vendor Attribute N (see man page)\n\n" +" -F TYPE, --firmwarebug=TYPE (ATA)\n" +" Use firmware bug workaround: none, samsung, samsung2\n\n" +" -P TYPE, --presets=TYPE (ATA)\n" +" Drive-specific presets: use, ignore, show, showall\n\n" + ); +#else + printf( +" -H Show device SMART health status\n" +" -c Show device SMART capabilities (ATA)\n" +" -A Show device SMART vendor-specific Attributes and values (ATA)\n" +" -l TYPE Show device log. TYPE: error,selftest,selective,directory\n" +" -v N,OPT Set display OPTion for vendor Attribute N (see man page) (ATA)\n" +" -F TYPE Use firmware bug workaround: none, samsung, samsung2 (ATA)\n" +" -P TYPE Drive-specific presets: use, ignore, show, showall (ATA)\n\n" + ); +#endif + printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -t TEST, --test=TEST\n" +" Run test. TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n\n" +" -C, --captive\n" +" Do test in captive mode (along with -t)\n\n" +" -X, --abort\n" +" Abort any non-captive test on device\n\n" +); +#else + printf( +" -t TEST Run test. TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n" +" -C Do test in captive mode (along with -t)\n" +" -X Abort any non-captive test\n\n" + ); +#endif + print_smartctl_examples(); + return; +} + +/* Returns a pointer to a static string containing a formatted list of the valid + arguments to the option opt or NULL on failure. Note 'v' case different */ +const char *getvalidarglist(char opt) { + switch (opt) { + case 'q': + return "errorsonly, silent"; + case 'd': + return "ata, scsi, 3ware,N"; + case 'T': + return "normal, conservative, permissive, verypermissive"; + case 'b': + return "warn, exit, ignore"; + case 'r': + return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; + case 's': + case 'o': + case 'S': + return "on, off"; + case 'l': + return "error, selftest, selective, directory"; + case 'P': + return "use, ignore, show, showall"; + case 't': + return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,on, afterselect,off"; + case 'F': + return "none, samsung, samsung2"; + case 'v': + default: + return NULL; + } +} + +/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where + <LIST> is the list of valid arguments for option opt. */ +void printvalidarglistmessage(char opt) { + char *s; + + if (opt=='v') + s=create_vendor_attribute_arg_list(); + else + s=(char *)getvalidarglist(opt); + + if (!s) { + pout("Error whilst constructing argument list for option %c", opt); + return; + } + + if (opt=='v'){ + pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s); + free(s); + } + else { + // getvalidarglist() might produce a multiline or single line string. We + // need to figure out which to get the formatting right. + char separator = strchr(s, '\n') ? '\n' : ' '; + pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator); + } + + return; +} + +/* Takes command options and sets features to be run */ +void ParseOpts (int argc, char** argv){ + int optchar; + int badarg; + int captive; + unsigned char *charp; + extern char *optarg; + extern int optopt, optind, opterr; + char extraerror[256]; + // Please update getvalidarglist() if you edit shortopts + const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:"; +#ifdef HAVE_GETOPT_LONG + char *arg; + // Please update getvalidarglist() if you edit longopts + struct option longopts[] = { + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "copyright", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'V' }, + { "quietmode", required_argument, 0, 'q' }, + { "device", required_argument, 0, 'd' }, + { "tolerance", required_argument, 0, 'T' }, + { "badsum", required_argument, 0, 'b' }, + { "report", required_argument, 0, 'r' }, + { "smart", required_argument, 0, 's' }, + { "offlineauto", required_argument, 0, 'o' }, + { "saveauto", required_argument, 0, 'S' }, + { "health", no_argument, 0, 'H' }, + { "capabilities", no_argument, 0, 'c' }, + { "attributes", no_argument, 0, 'A' }, + { "log", required_argument, 0, 'l' }, + { "info", no_argument, 0, 'i' }, + { "all", no_argument, 0, 'a' }, + { "vendorattribute", required_argument, 0, 'v' }, + { "presets", required_argument, 0, 'P' }, + { "test", required_argument, 0, 't' }, + { "captive", no_argument, 0, 'C' }, + { "abort", no_argument, 0, 'X' }, + { "firmwarebug", required_argument, 0, 'F' }, + { 0, 0, 0, 0 } + }; +#endif + + memset(extraerror, 0, sizeof(extraerror)); + memset(con,0,sizeof(*con)); + con->testcase=-1; + opterr=optopt=0; + badarg = captive = FALSE; + + // This miserable construction is needed to get emacs to do proper indenting. Sorry! + while (-1 != (optchar = +#ifdef HAVE_GETOPT_LONG + getopt_long(argc, argv, shortopts, longopts, NULL) +#else + getopt(argc, argv, shortopts) +#endif + )){ + switch (optchar){ + case 'V': + con->dont_print=FALSE; + printslogan(); + printcopy(); + exit(0); + break; + case 'q': + if (!strcmp(optarg,"errorsonly")) { + con->printing_switchable = TRUE; + con->dont_print = FALSE; + } else if (!strcmp(optarg,"silent")) { + con->printing_switchable = FALSE; + con->dont_print = TRUE; + } else { + badarg = TRUE; + } + break; + case 'd': + if (!strcmp(optarg,"ata")) { + con->controller_type = CONTROLLER_ATA; + con->controller_port = 0; + } else if (!strcmp(optarg,"scsi")) { + con->controller_type = CONTROLLER_SCSI; + con->controller_port = 0; + } else if (!strcmp(optarg,"marvell")) { + con->controller_type = CONTROLLER_MARVELL_SATA; + con->controller_port = 0; + } else { + // look for RAID-type device + int i; + char *s; + + // make a copy of the string to mess with + if (!(s = strdup(optarg))) { + con->dont_print = FALSE; + pout("No memory for argument of -d. Exiting...\n"); + exit(FAILCMD); + } else if (strncmp(s,"3ware,",6)) { + badarg = TRUE; + } else if (split_report_arg2(s, &i)) { + sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n"); + badarg = TRUE; + } else if (i<0 || i>15) { + sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15\n", i); + badarg = TRUE; + } else { + // NOTE: controller_port == disk number + 1 + con->controller_type = CONTROLLER_3WARE; + con->controller_port = i+1; + } + free(s); + } + break; + case 'T': + if (!strcmp(optarg,"normal")) { + con->conservative = FALSE; + con->permissive = 0; + } else if (!strcmp(optarg,"conservative")) { + con->conservative = TRUE; + } else if (!strcmp(optarg,"permissive")) { + if (con->permissive<0xff) + con->permissive++; + } else if (!strcmp(optarg,"verypermissive")) { + con->permissive=0xff; + } else { + badarg = TRUE; + } + break; + case 'b': + if (!strcmp(optarg,"warn")) { + con->checksumfail = FALSE; + con->checksumignore = FALSE; + } else if (!strcmp(optarg,"exit")) { + con->checksumfail = TRUE; + con->checksumignore = FALSE; + } else if (!strcmp(optarg,"ignore")) { + con->checksumignore = TRUE; + con->checksumfail = FALSE; + } else { + badarg = TRUE; + } + break; + case 'r': + { + int i; + char *s; + + // split_report_arg() may modify its first argument string, so use a + // copy of optarg in case we want optarg for an error message. + if (!(s = strdup(optarg))) { + con->dont_print = FALSE; + pout("Can't allocate memory to copy argument to -r option" + " - exiting\n"); + EXIT(FAILCMD); + } + if (split_report_arg(s, &i)) { + badarg = TRUE; + } else if (!strcmp(s,"ioctl")) { + con->reportataioctl = con->reportscsiioctl = i; + } else if (!strcmp(s,"ataioctl")) { + con->reportataioctl = i; + } else if (!strcmp(s,"scsiioctl")) { + con->reportscsiioctl = i; + } else { + badarg = TRUE; + } + free(s); + } + break; + case 's': + if (!strcmp(optarg,"on")) { + con->smartenable = TRUE; + con->smartdisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartdisable = TRUE; + con->smartenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'o': + if (!strcmp(optarg,"on")) { + con->smartautoofflineenable = TRUE; + con->smartautoofflinedisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartautoofflinedisable = TRUE; + con->smartautoofflineenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'S': + if (!strcmp(optarg,"on")) { + con->smartautosaveenable = TRUE; + con->smartautosavedisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartautosavedisable = TRUE; + con->smartautosaveenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'H': + con->checksmart = TRUE; + break; + case 'F': + if (!strcmp(optarg,"none")) { + con->fixfirmwarebug = FIX_NONE; + } else if (!strcmp(optarg,"samsung")) { + con->fixfirmwarebug = FIX_SAMSUNG; + } else if (!strcmp(optarg,"samsung2")) { + con->fixfirmwarebug = FIX_SAMSUNG2; + } else { + badarg = TRUE; + } + break; + case 'c': + con->generalsmartvalues = TRUE; + break; + case 'A': + con->smartvendorattrib = TRUE; + break; + case 'l': + if (!strcmp(optarg,"error")) { + con->smarterrorlog = TRUE; + } else if (!strcmp(optarg,"selftest")) { + con->smartselftestlog = TRUE; + } else if (!strcmp(optarg, "selective")) { + con->selectivetestlog = TRUE; + } else if (!strcmp(optarg,"directory")) { + con->smartlogdirectory = TRUE; + } else { + badarg = TRUE; + } + break; + case 'i': + con->driveinfo = TRUE; + break; + case 'a': + con->driveinfo = TRUE; + con->checksmart = TRUE; + con->generalsmartvalues = TRUE; + con->smartvendorattrib = TRUE; + con->smarterrorlog = TRUE; + con->smartselftestlog = TRUE; + con->selectivetestlog = TRUE; + break; + case 'v': + // parse vendor-specific definitions of attributes + if (!strcmp(optarg,"help")) { + char *s; + con->dont_print=FALSE; + printslogan(); + if (!(s = create_vendor_attribute_arg_list())) { + pout("Insufficient memory to construct argument list\n"); + EXIT(FAILCMD); + } + pout("The valid arguments to -v are:\n\thelp\n%s\n", s); + free(s); + EXIT(0); + } + charp=con->attributedefs; + if (!charp){ + pout("Fatal internal error in ParseOpts()\n"); + EXIT(FAILCMD); + } + if (parse_attribute_def(optarg, &charp)) + badarg = TRUE; + break; + case 'P': + if (!strcmp(optarg, "use")) { + con->ignorepresets = FALSE; + } else if (!strcmp(optarg, "ignore")) { + con->ignorepresets = TRUE; + } else if (!strcmp(optarg, "show")) { + con->showpresets = TRUE; + } else if (!strcmp(optarg, "showall")) { + showallpresets(); + EXIT(0); + } else { + badarg = TRUE; + } + break; + case 't': + if (!strcmp(optarg,"offline")) { + con->smartexeoffimmediate = TRUE; + con->testcase = OFFLINE_FULL_SCAN; + } else if (!strcmp(optarg,"short")) { + con->smartshortselftest = TRUE; + con->testcase = SHORT_SELF_TEST; + } else if (!strcmp(optarg,"long")) { + con->smartextendselftest = TRUE; + con->testcase = EXTEND_SELF_TEST; + } else if (!strcmp(optarg,"conveyance")) { + con->smartconveyanceselftest = TRUE; + con->testcase = CONVEYANCE_SELF_TEST; + } else if (!strcmp(optarg,"afterselect,on")) { + // scan remainder of disk after doing selected segments + con->scanafterselect=2; + } else if (!strcmp(optarg,"afterselect,off")) { + // don't scan remainder of disk after doing selected segments + con->scanafterselect=1; + } else if (!strncmp(optarg,"pending,",strlen("pending,"))) { + // parse number of minutes that test should be pending + int i; + char *tailptr=NULL; + errno=0; + i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10); + if (errno || *tailptr != '\0') { + sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n"); + badarg = TRUE; + } else if (i<0 || i>65535) { + sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); + badarg = TRUE; + } else { + con->pendingtime=i+1; + } + } else if (!strncmp(optarg,"select",strlen("select"))) { + // parse range of LBAs to test + uint64_t start, stop; + + if (split_selective_arg(optarg, &start, &stop)) { + sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n"); + badarg = TRUE; + } else { + if (con->smartselectivenumspans >= 5 || start > stop) { + if (start > stop) { + sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n", + start, stop, optarg); + } else { + sprintf(extraerror,"ERROR: No more than five selective self-test spans may be" + " defined\n"); + } + badarg = TRUE; + } + con->smartselectivespan[con->smartselectivenumspans][0] = start; + con->smartselectivespan[con->smartselectivenumspans][1] = stop; + con->smartselectivenumspans++; + con->testcase = SELECTIVE_SELF_TEST; + } + } else { + badarg = TRUE; + } + break; + case 'C': + captive = TRUE; + break; + case 'X': + con->smartselftestabort = TRUE; + con->testcase = ABORT_SELF_TEST; + break; + case 'h': + con->dont_print=FALSE; + printslogan(); + Usage(); + EXIT(0); + break; + case '?': + default: + con->dont_print=FALSE; + printslogan(); +#ifdef HAVE_GETOPT_LONG + // Point arg to the argument in which this option was found. + arg = argv[optind-1]; + // Check whether the option is a long option that doesn't map to -h. + if (arg[1] == '-' && optchar != 'h') { + // Iff optopt holds a valid option then argument must be missing. + if (optopt && (strchr(shortopts, optopt) != NULL)) { + pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); + printvalidarglistmessage(optopt); + } else + pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } +#endif + if (optopt) { + // Iff optopt holds a valid option then argument must be + // missing. Note (BA) this logic seems to fail using Solaris + // getopt! + if (strchr(shortopts, optopt) != NULL) { + pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt); + printvalidarglistmessage(optopt); + } else + pout("=======> UNRECOGNIZED OPTION: %c\n",optopt); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } + Usage(); + EXIT(0); + } // closes switch statement to process command-line options + + // Check to see if option had an unrecognized or incorrect argument. + if (badarg) { + printslogan(); + // It would be nice to print the actual option name given by the user + // here, but we just print the short form. Please fix this if you know + // a clean way to do it. + pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg); + printvalidarglistmessage(optchar); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } + } + // At this point we have processed all command-line options. If the + // print output is switchable, then start with the print output + // turned off + if (con->printing_switchable) + con->dont_print=TRUE; + + // error message if user has asked for more than one test + if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+ + con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort + (con->smartselectivenumspans>0?1:0))){ + con->dont_print=FALSE; + printslogan(); + pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // error message if user has set selective self-test options without + // asking for a selective self-test + if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){ + con->dont_print=FALSE; + printslogan(); + if (con->pendingtime) + pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); + else + pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // If captive option was used, change test type if appropriate. + if (captive && con->smartshortselftest) { + con->smartshortselftest = FALSE; + con->smartshortcapselftest = TRUE; + con->testcase = SHORT_CAPTIVE_SELF_TEST; + } else if (captive && con->smartextendselftest) { + con->smartextendselftest = FALSE; + con->smartextendcapselftest = TRUE; + con->testcase = EXTEND_CAPTIVE_SELF_TEST; + } + else if (captive && con->smartconveyanceselftest) { + con->smartconveyanceselftest = FALSE; + con->smartconveyancecapselftest = TRUE; + con->testcase = CONVEYANCE_CAPTIVE_SELF_TEST; + } + else if (captive && con->smartselectiveselftest) { + con->smartselectiveselftest = FALSE; + con->smartselectivecapselftest = TRUE; + con->testcase = SELECTIVE_CAPTIVE_SELF_TEST; + } + + // From here on, normal operations... + printslogan(); + + // Warn if the user has provided no device name + if (argc-optind<1){ + pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // Warn if the user has provided more than one device name + if (argc-optind>1){ + int i; + pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); + pout("You have provided %d device names:\n",argc-optind); + for (i=0; i<argc-optind; i++) + pout("%s\n",argv[optind+i]); + UsageSummary(); + EXIT(FAILCMD); + } +} + +// Printing function (controlled by global con->dont_print) +// [From GLIBC Manual: Since the prototype doesn't specify types for +// optional arguments, in a call to a variadic function the default +// argument promotions are performed on the optional argument +// values. This means the objects of type char or short int (whether +// signed or not) are promoted to either int or unsigned int, as +// appropriate.] +void pout(char *fmt, ...){ + va_list ap; + + // initialize variable argument list + va_start(ap,fmt); + if (con->dont_print){ + va_end(ap); + return; + } + + // print out + vprintf(fmt,ap); + va_end(ap); + fflush(stdout); + return; +} + +// This function is used by utility.c to report LOG_CRIT errors. +// The smartctl version prints to stdout instead of syslog(). +void PrintOut(int priority, char *fmt, ...) { + va_list ap; + + // avoid warning message about unused variable from gcc -W: just + // change value of local copy. + priority=0; + + va_start(ap,fmt); + vprintf(fmt,ap); + va_end(ap); + return; +} + + +/* Main Program */ +int main (int argc, char **argv){ + int fd,retval=0; + char *device; + smartmonctrl control; + char *mode=NULL; + + // define control block for external functions + con=&control; + + // Part input arguments + ParseOpts(argc,argv); + + device = argv[argc-1]; + + // If use has specified 3ware controller, determine which interface + if (con->controller_type == CONTROLLER_3WARE) { + con->controller_type=guess_device_type(device); + if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR) + con->controller_type = CONTROLLER_3WARE_678K; + } + + if (con->controller_type == CONTROLLER_UNKNOWN) + con->controller_type=guess_device_type(device); + + if (con->controller_type == CONTROLLER_UNKNOWN) { + pout("Smartctl: please specify device type with the -d option.\n"); + UsageSummary(); + return FAILCMD; + } + + // set up mode for open() call. SCSI case is: + switch (con->controller_type) { + case CONTROLLER_SCSI: + mode="SCSI"; + break; + case CONTROLLER_3WARE_9000_CHAR: + mode="ATA_3WARE_9000"; + break; + case CONTROLLER_3WARE_678K_CHAR: + mode="ATA_3WARE_678K"; + break; + default: + mode="ATA"; + break; + } + + // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the + // scsi generic device can be used (needs write permission for MODE + // SELECT command) plus O_NONBLOCK to stop open hanging if media not + // present (e.g. with st). Opening is retried O_RDONLY if read-only + // media prevents opening O_RDWR (it cannot happen for scsi generic + // devices, but it can for the others). + fd = deviceopen(device, mode); + if (fd<0) { + char errmsg[256]; + snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]); + errmsg[255]='\0'; + syserror(errmsg); + return FAILDEV; + } + + // now call appropriate ATA or SCSI routine + switch (con->controller_type) { + case CONTROLLER_UNKNOWN: + // we should never fall into this branch! + pout("Smartctl: please specify device type with the -d option.\n"); + UsageSummary(); + retval = FAILCMD; + break; + case CONTROLLER_SCSI: + retval = scsiPrintMain(fd); + break; + default: + retval = ataPrintMain(fd); + break; + } + + return retval; +} diff --git a/sm5/smartd.c b/sm5/smartd.c new file mode 100644 index 0000000000000000000000000000000000000000..22f5c4ec1ffbbc9a91281d277599afbf559dd92b --- /dev/null +++ b/sm5/smartd.c @@ -0,0 +1,4011 @@ +/* + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2000 Michael Cornwell <cornwell@acm.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/ + * + */ + +// unconditionally included files +#define _GNU_SOURCE +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> // umask +#ifndef _WIN32 +#include <sys/wait.h> +#include <unistd.h> +#endif +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <limits.h> + +#if SCSITIMEOUT +#include <setjmp.h> +#endif + +// see which system files to conditionally include +#include "config.h" + +// conditionally included files +#ifdef HAVE_GETOPT_LONG +#include <getopt.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4761) // "conversion supplied" +typedef unsigned short mode_t; +typedef int pid_t; +#endif +#include <io.h> // umask() +#include <process.h> // getpid() +#endif // _WIN32 + +// locally included files +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "int64.h" +#include "knowndrives.h" +#include "scsicmds.h" +#include "smartd.h" +#include "utility.h" + +#ifdef _WIN32 +#include "hostname_win32.h" // gethost/domainname() +#define HAVE_GETHOSTNAME 1 +#define HAVE_GETDOMAINNAME 1 +// fork()/signal()/initd simulation for native Windows +#include "daemon_win32.h" // daemon_main/detach/signal() +#undef SIGNALFN +#define SIGNALFN daemon_signal +#define strsignal daemon_strsignal +#define sleep daemon_sleep +#undef EXIT // see utility.h +#define EXIT(x) { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); } +// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK. +#define SIGQUIT SIGBREAK +#define SIGQUIT_KEYNAME "CONTROL-Break" +#else // _WIN32 +#ifdef __CYGWIN__ +// 2x CONTROL-C simulates missing SIGQUIT via keyboard +#define SIGQUIT_KEYNAME "2x CONTROL-C" +#else // __CYGWIN__ +#define SIGQUIT_KEYNAME "CONTROL-\\" +#endif // __CYGWIN__ +#endif // _WIN32 + +#if defined (__SVR4) && defined (__sun) +int getdomainname(char *, int); /* no declaration in header files! */ +#endif + +#define ARGUSED(x) ((void)(x)) + +// These are CVS identification information for *.c and *.h files +extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, + *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; + +static const char *filenameandversion="$Id: smartd.c,v 1.341 2004/09/06 04:30:22 dpgilbert Exp $"; +#ifdef NEED_SOLARIS_ATA_CODE +extern const char *os_solaris_ata_s_cvsid; +#endif +#ifdef _WIN32 +extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid; +#ifdef _MSC_VER +extern const char *int64_vc6_c_cvsid; +#endif +#endif +const char *smartd_c_cvsid="$Id: smartd.c,v 1.341 2004/09/06 04:30:22 dpgilbert Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID +#ifdef DAEMON_WIN32_H_CVSID +DAEMON_WIN32_H_CVSID +#endif +EXTERN_H_CVSID INT64_H_CVSID +#ifdef HOSTNAME_WIN32_H_CVSID +HOSTNAME_WIN32_H_CVSID +#endif +KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID +#ifdef SYSLOG_H_CVSID +SYSLOG_H_CVSID +#endif +UTILITY_H_CVSID; + +extern const char *reportbug; + +// GNU copyleft statement. Needed for GPL purposes. +const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n" + "free software, and you are welcome to redistribute it\n" + "under the terms of the GNU General Public License\n" + "Version 2. See http://www.gnu.org for further details.\n\n"; + +extern unsigned char debugmode; + +// command-line: how long to sleep between checks +static int checktime=CHECKTIME; + +// command-line: name of PID file (NULL for no pid file) +static char* pid_file=NULL; + +// configuration file name +#ifndef _WIN32 +static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ; +#else +static char* configfile = "./" CONFIGFILENAME ; +#endif +// configuration file "name" if read from stdin +static /*const*/ char * const configfile_stdin = "<stdin>"; +// allocated memory for alternate configuration file name +static char* configfile_alt = NULL; + +// command-line: when should we exit? +static int quit=0; + +// command-line; this is the default syslog(3) log facility to use. +static int facility=LOG_DAEMON; + +// used for control of printing, passing arguments to atacmds.c +smartmonctrl *con=NULL; + +// pointers to (real or simulated) entries in configuration file, and +// maximum space currently allocated for these entries. +cfgfile **cfgentries=NULL; +int cfgentries_max=0; + +// pointers to ATA and SCSI devices being monitored, maximum and +// actual numbers +cfgfile **atadevlist=NULL, **scsidevlist=NULL; +int atadevlist_max=0, scsidevlist_max=0; +int numdevata=0, numdevscsi=0; + +// track memory usage +extern int64_t bytes; + +// exit status +extern int exitstatus; + +// set to one if we catch a USR1 (check devices now) +volatile int caughtsigUSR1=0; + +#ifdef _WIN32 +// set to one if we catch a USR2 (toggle debug mode) +volatile int caughtsigUSR2=0; +#endif + +// set to one if we catch a HUP (reload config file). In debug mode, +// set to two, if we catch INT (also reload config file). +volatile int caughtsigHUP=0; + +// set to signal value if we catch INT, QUIT, or TERM +volatile int caughtsigEXIT=0; + +#if SCSITIMEOUT +// stack environment if we time out during SCSI access (USB devices) +jmp_buf registerscsienv; +#endif + +// tranlate cfg->pending into the correct Attribute numbers +void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) { + + unsigned char curr = CURR_PEND(pending); + unsigned char off = OFF_PEND(pending); + + // look for special value of CUR_UNC_DEFAULT that means DONT + // monitor. 0 means DO test. + if (curr==CUR_UNC_DEFAULT) + curr=0; + else if (curr==0) + curr=CUR_UNC_DEFAULT; + + // look for special value of OFF_UNC_DEFAULT that means DONT + // monitor. 0 means DO TEST. + if (off==OFF_UNC_DEFAULT) + off=0; + else if (off==0) + off=OFF_UNC_DEFAULT; + + *current=curr; + *offline=off; + + return; +} + + +// free all memory associated with selftest part of configfile entry. Return NULL +testinfo* FreeTestData(testinfo *data){ + + // make sure we have something to do. + if (!data) + return NULL; + + // free space for text pattern + data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion); + + // free compiled expression + regfree(&(data->cregex)); + + // make sure that no sign of the compiled expression is left behind + // (just in case, to help detect bugs if we ever try and refer to + // that again). + memset(&(data->cregex), '0', sizeof(regex_t)); + + // free remaining memory space + data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion); + + return NULL; +} + +cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){ + // for now keep BLOCKSIZE small to help detect coding problems. + // Perhaps increase in the future. + const int BLOCKSIZE=8; + int i; + int old = *oldsize; + int new = old + BLOCKSIZE; + cfgfile **newptr=realloc(oldarray, new*sizeof(cfgfile *)); + + // did we get more space? + if (newptr) { + + // clear remaining entries ala calloc() + for (i=old; i<new; i++) + newptr[i]=NULL; + + bytes += BLOCKSIZE*sizeof(cfgfile *); + + *oldsize=new; + +#if 0 + PrintOut(LOG_INFO, "allocating %d slots for %s\n", BLOCKSIZE, listname); +#endif + + return newptr; + } + + PrintOut(LOG_CRIT, "out of memory for allocating %s list\n", listname); + EXIT(EXIT_NOMEM); +} + +void PrintOneCVS(const char *a_cvs_id){ + char out[CVSMAXLEN]; + printone(out,a_cvs_id); + PrintOut(LOG_INFO,"%s",out); + return; +} + +// prints CVS identity information for the executable +void PrintCVS(void){ + char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]"; + + PrintOut(LOG_INFO,(char *)copyleftstring); + PrintOut(LOG_INFO,"CVS version IDs of files used to build this code are:\n"); + PrintOneCVS(atacmdnames_c_cvsid); + PrintOneCVS(atacmds_c_cvsid); + PrintOneCVS(ataprint_c_cvsid); +#ifdef _WIN32 + PrintOneCVS(daemon_win32_c_cvsid); +#endif +#if defined(_WIN32) && defined(_MSC_VER) + PrintOneCVS(int64_vc6_c_cvsid); +#endif +#ifdef _WIN32 + PrintOneCVS(hostname_win32_c_cvsid); +#endif + PrintOneCVS(knowndrives_c_cvsid); + PrintOneCVS(os_XXXX_c_cvsid); +#ifdef NEED_SOLARIS_ATA_CODE + PrintOneCVS( os_solaris_ata_s_cvsid); +#endif + PrintOneCVS(scsicmds_c_cvsid); + PrintOneCVS(smartd_c_cvsid); +#ifdef _WIN32 + PrintOneCVS(syslog_win32_c_cvsid); +#endif + PrintOneCVS(utility_c_cvsid); + PrintOut(LOG_INFO, "\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"); + PrintOut(LOG_INFO, "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"); + PrintOut(LOG_INFO, "smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n"); + PrintOut(LOG_INFO, "smartd compile dated " __DATE__ " at "__TIME__ "\n"); + PrintOut(LOG_INFO, "smartmontools configure arguments: %s\n", configargs); + return; +} + +// Removes config file entry, freeing all memory +void RmConfigEntry(cfgfile **anentry, int whatline){ + + cfgfile *cfg; + + // pointer should never be null! + if (!anentry){ + PrintOut(LOG_CRIT,"Internal error in RmConfigEntry() at line %d of file %s\n%s", + whatline, filenameandversion, reportbug); + EXIT(EXIT_BADCODE); + } + + // only remove entries that exist! + if (!(cfg=*anentry)) + return; + + // entry exists -- free all of its memory + cfg->name = FreeNonZero(cfg->name, -1,__LINE__,filenameandversion); + cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion); + cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion); + cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion); + cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion); + if (cfg->mailwarn){ + cfg->mailwarn->address = FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion); + cfg->mailwarn->emailcmdline = FreeNonZero(cfg->mailwarn->emailcmdline, -1,__LINE__,filenameandversion); + cfg->mailwarn = FreeNonZero(cfg->mailwarn, sizeof(maildata),__LINE__,filenameandversion); + } + cfg->testdata = FreeTestData(cfg->testdata); + *anentry = FreeNonZero(cfg, sizeof(cfgfile),__LINE__,filenameandversion); + + return; +} + +// deallocates all memory associated with cfgentries list +void RmAllConfigEntries(){ + int i; + + for (i=0; i<cfgentries_max; i++) + RmConfigEntry(cfgentries+i, __LINE__); + + cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion); + cfgentries_max=0; + + return; +} + +// deallocates all memory associated with ATA/SCSI device lists +void RmAllDevEntries(){ + int i; + + for (i=0; i<atadevlist_max; i++) + RmConfigEntry(atadevlist+i, __LINE__); + + atadevlist=FreeNonZero(atadevlist, sizeof(cfgfile *)*atadevlist_max, __LINE__, filenameandversion); + atadevlist_max=0; + + for (i=0; i<scsidevlist_max; i++) + RmConfigEntry(scsidevlist+i, __LINE__); + + scsidevlist=FreeNonZero(scsidevlist, sizeof(cfgfile *)*scsidevlist_max, __LINE__, filenameandversion); + scsidevlist_max=0; + + return; +} + +// remove the PID file +void RemovePidFile(){ + if (pid_file) { + if ( -1==unlink(pid_file) ) + PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", + pid_file, strerror(errno)); + pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion); + } + return; +} + + +// Note if we catch a SIGUSR1 +void USR1handler(int sig){ + if (SIGUSR1==sig) + caughtsigUSR1=1; + return; +} + +#ifdef _WIN32 +// Note if we catch a SIGUSR2 +void USR2handler(int sig){ + if (SIGUSR2==sig) + caughtsigUSR2=1; + return; +} +#endif + +// Note if we catch a HUP (or INT in debug mode) +void HUPhandler(int sig){ + if (sig==SIGHUP) + caughtsigHUP=1; + else + caughtsigHUP=2; + return; +} + +// signal handler for TERM, QUIT, and INT (if not in debug mode) +void sighandler(int sig){ + if (!caughtsigEXIT) + caughtsigEXIT=sig; + return; +} + + +// signal handler that prints Goodbye message and removes pidfile +void Goodbye(void){ + + // clean up memory -- useful for debugging + RmAllConfigEntries(); + RmAllDevEntries(); + + // delete PID file, if one was created + RemovePidFile(); + + // remove alternate configfile name + configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion); + + // useful for debugging -- have we managed memory correctly? + if (debugmode || (bytes && exitstatus!=EXIT_NOMEM)) + PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes); + + // if we are exiting because of a code bug, tell user + if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM)) + PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n"); + + if (exitstatus==0 && bytes) + exitstatus=EXIT_BADCODE; + + // and this should be the final output from smartd before it exits + PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus); + + return; +} + +#define ENVLENGTH 1024 + +// a replacement for setenv() which is not available on all platforms. +// Note that the string passed to putenv must not be freed or made +// invalid, since a pointer to it is kept by putenv(). This means that +// it must either be a static buffer or allocated off the heap. The +// string can be freed if the environment variable is redefined or +// deleted via another call to putenv(). So we keep these on the stack +// as long as the popen() call is underway. +int exportenv(char* stackspace, const char *name, const char *value){ + snprintf(stackspace,ENVLENGTH, "%s=%s", name, value); + return putenv(stackspace); +} + +char* dnsdomain(const char* hostname) { + char *p = NULL; +#ifdef HAVE_GETHOSTBYNAME + struct hostent *hp; + + if ((hp = gethostbyname(hostname))) { + // Does this work if gethostbyname() returns an IPv6 name in + // colon/dot notation? [BA] + if ((p = strchr(hp->h_name, '.'))) + p++; // skip "." + } +#else + ARGUSED(hostname); +#endif + return p; +} + +#define EBUFLEN 1024 + +// If either address or executable path is non-null then send and log +// a warning email, or execute executable +void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ + char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024]; + char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN]; + char environ_strings[11][ENVLENGTH]; + time_t epoch; + va_list ap; + const int day=24*3600; + int days=0; + char *whichfail[]={ + "EmailTest", // 0 + "Health", // 1 + "Usage", // 2 + "SelfTest", // 3 + "ErrorCount", // 4 + "FailedHealthCheck", // 5 + "FailedReadSmartData", // 6 + "FailedReadSmartErrorLog", // 7 + "FailedReadSmartSelfTestLog", // 8 + "FailedOpenDevice", // 9 + "CurrentPendingSector", // 10 + "OfflineUncorrectableSector" // 11 + }; + + char *comma, *address, *executable; + mailinfo *mail; + maildata* data=cfg->mailwarn; +#ifndef _WIN32 + FILE *pfp=NULL; +#else + char stdinbuf[1024]; int boxmsgoffs, boxtype; +#endif + char *newadd=NULL, *newwarn=NULL; + const char *unknown="[Unknown]"; + + // See if user wants us to send mail + if(!data) + return; + + address=data->address; + executable=data->emailcmdline; + + if (!address && !executable) + return; + + // which type of mail are we sending? + mail=(data->maillog)+which; + + // checks for sanity + if (data->emailfreq<1 || data->emailfreq>3) { + PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq); + return; + } + if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) { + PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n", + which, (int)sizeof(whichfail)); + return; + } + + // Return if a single warning mail has been sent. + if ((data->emailfreq==1) && mail->logged) + return; + + // Return if this is an email test and one has already been sent. + if (which == 0 && mail->logged) + return; + + // To decide if to send mail, we need to know what time it is. + epoch=time(NULL); + + // Return if less than one day has gone by + if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) + return; + + // Return if less than 2^(logged-1) days have gone by + if (data->emailfreq==3 && mail->logged){ + days=0x01<<(mail->logged-1); + days*=day; + if (epoch<(mail->lastsent+days)) + return; + } + + // record the time of this mail message, and the first mail message + if (!mail->logged) + mail->firstsent=epoch; + mail->lastsent=epoch; + + // get system host & domain names (not null terminated if length=MAX) +#ifdef HAVE_GETHOSTNAME + if (gethostname(hostname, 256)) + strcpy(hostname, unknown); + else { + char *p=NULL; + hostname[255]='\0'; + p = dnsdomain(hostname); + if (p && *p) { + strncpy(domainname, p, 255); + domainname[255]='\0'; + } else + strcpy(domainname, unknown); + } +#else + strcpy(hostname, unknown); + strcpy(domainname, unknown); +#endif + +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(nisdomain, 256)) + strcpy(nisdomain, unknown); + else + nisdomain[255]='\0'; +#else + strcpy(nisdomain, unknown); +#endif + + // print warning string into message + va_start(ap, fmt); + vsnprintf(message, 256, fmt, ap); + va_end(ap); + + // appropriate message about further information + additional[0]=original[0]=further[0]='\0'; + if (which) { + sprintf(further,"You can also use the smartctl utility for further investigation.\n"); + + switch (data->emailfreq){ + case 1: + sprintf(additional,"No additional email messages about this problem will be sent.\n"); + break; + case 2: + sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n"); + break; + case 3: + sprintf(additional,"Another email message will be sent in %d days if the problem persists\n", + (0x01)<<mail->logged); + break; + } + if (data->emailfreq>1 && mail->logged){ + dateandtimezoneepoch(dates, mail->firstsent); + sprintf(original,"The original email about this issue was sent at %s\n", dates); + } + } + + snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname); + + // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx". + if (!executable) +#ifdef DEFAULT_MAILER + executable = DEFAULT_MAILER ; +#else +#ifndef _WIN32 + executable = "mail"; +#else + executable = "blat"; // http://blat.sourceforge.net/ +#endif +#endif + + // make a private copy of address with commas replaced by spaces + // to separate recipients + if (address) { + address=CustomStrDup(data->address, 1, __LINE__, filenameandversion); +#ifndef _WIN32 // blat mailer needs comma + comma=address; + while ((comma=strchr(comma, ','))) + *comma=' '; +#endif + } + + // Export information in environment variables that will be useful + // for user scripts + exportenv(environ_strings[0], "SMARTD_MAILER", executable); + exportenv(environ_strings[1], "SMARTD_MESSAGE", message); + exportenv(environ_strings[2], "SMARTD_SUBJECT", subject); + dateandtimezoneepoch(dates, mail->firstsent); + exportenv(environ_strings[3], "SMARTD_TFIRST", dates); + snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); + exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates); + exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]); + if (address) + exportenv(environ_strings[6], "SMARTD_ADDRESS", address); + exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name); + + switch (cfg->controller_type) { + case CONTROLLER_3WARE_678K: + case CONTROLLER_3WARE_9000_CHAR: + case CONTROLLER_3WARE_678K_CHAR: + { + char *s,devicetype[16]; + sprintf(devicetype, "3ware,%d", cfg->controller_port-1); + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype); + if ((s=strchr(cfg->name, ' '))) + *s='\0'; + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + if (s) + *s=' '; + } + break; + case CONTROLLER_ATA: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + break; + case CONTROLLER_MARVELL_SATA: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + break; + case CONTROLLER_SCSI: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + } + + snprintf(fullmessage, 1024, + "This email was generated by the smartd daemon running on:\n\n" + " host name: %s\n" + " DNS domain: %s\n" + " NIS domain: %s\n\n" + "The following warning/error was logged by the smartd daemon:\n\n" + "%s\n\n" + "For details see host's SYSLOG (default: /var/log/messages).\n\n" + "%s%s%s", + hostname, domainname, nisdomain, message, further, original, additional); + exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage); + + // now construct a command to send this as EMAIL +#ifndef _WIN32 + if (address) + snprintf(command, 2048, + "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n" + "%sENDMAIL\n", subject, address, fullmessage); + else + snprintf(command, 2048, "%s 2>&1", executable); + + // tell SYSLOG what we are about to do... + newadd=address?address:"<nomailer>"; + newwarn=which?"Warning via":"Test of"; + + PrintOut(LOG_INFO,"%s %s to %s ...\n", + which?"Sending warning via":"Executing test of", executable, newadd); + + // issue the command to send mail or to run the user's executable + errno=0; + if (!(pfp=popen(command, "r"))) + // failed to popen() mail process + PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", + newwarn, executable, newadd, errno?strerror(errno):""); + else { + // pipe suceeded! + int len, status; + char buffer[EBUFLEN]; + + // if unexpected output on stdout/stderr, null terminate, print, and flush + if ((len=fread(buffer, 1, EBUFLEN, pfp))) { + int count=0; + int newlen = len<EBUFLEN ? len : EBUFLEN-1; + buffer[newlen]='\0'; + PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", + newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer); + + // flush pipe if needed + while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN) + count++; + + // tell user that pipe was flushed, or that something is really wrong + if (count && count<EBUFLEN) + PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", + newwarn, executable, newadd); + else if (count) + PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", + newwarn, executable, newadd); + } + + // if something went wrong with mail process, print warning + errno=0; + if (-1==(status=pclose(pfp))) + PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd, + errno?strerror(errno):""); + else { + // mail process apparently succeeded. Check and report exit status + int status8; + + if (WIFEXITED(status)) { + // exited 'normally' (but perhaps with nonzero status) + status8=WEXITSTATUS(status); + + if (status8>128) + PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", + newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128)); + else if (status8) + PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", + newwarn, executable, newadd, status, status8); + else + PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); + } + + if (WIFSIGNALED(status)) + PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", + newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status))); + + // this branch is probably not possible. If subprocess is + // stopped then pclose() should not return. + if (WIFSTOPPED(status)) + PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n", + newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status))); + + } + } + +#else // _WIN32 + + // No "here-documents" on Windows, so must use separate commandline and stdin + command[0] = stdinbuf[0] = 0; + boxtype = -1; boxmsgoffs = 0; + newadd = "<nomailer>"; + if (address) { + // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox + int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0); + if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) { + boxtype = (addroffs > 0 ? 1 : 0); + addroffs += 6; + if (address[addroffs]) + addroffs++; + } + else + addroffs = 0; + + if (address[addroffs]) { + // Use "blat" parameter syntax (TODO: configure via -M for other mailers) + snprintf(command, sizeof(command), + "%s - -q -subject \"%s\" -to \"%s\"", + executable, subject, address+addroffs); + newadd = address+addroffs; + } + // Message for mail [0...] and messagebox [boxmsgoffs...] + snprintf(stdinbuf, sizeof(stdinbuf), + "This email was generated by the smartd daemon running on:\n\n" + " host name: %s\n" + " DNS domain: %s\n" +// " NIS domain: %s\n" + "\n%n" + "The following warning/error was logged by the smartd daemon:\n\n" + "%s\n\n" + "For details see the event log or log file of smartd.\n\n" + "%s%s%s" + "\n", + hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional); + } + else + snprintf(command, sizeof(command), "%s", executable); + + newwarn=which?"Warning via":"Test of"; + if (boxtype >= 0) { + // show message box + daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs); + PrintOut(LOG_INFO,"%s message box\n", newwarn); + } + if (command[0]) { + char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog() + int rc; + // run command + PrintOut(LOG_INFO,"%s %s to %s ...\n", + (which?"Sending warning via":"Executing test of"), executable, newadd); + rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf)); + if (rc >= 0 && stdoutbuf[0]) + PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n", + newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf); + if (rc != 0) + PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n", + newwarn, executable, newadd, rc); + else + PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); + } + +#endif // _WIN32 + + // increment mail sent counter + mail->logged++; + + // free copy of address (without commas) + address=FreeNonZero(address, -1, __LINE__, filenameandversion); + + return; +} + +// Printing function for watching ataprint commands, or losing them +// [From GLIBC Manual: Since the prototype doesn't specify types for +// optional arguments, in a call to a variadic function the default +// argument promotions are performed on the optional argument +// values. This means the objects of type char or short int (whether +// signed or not) are promoted to either int or unsigned int, as +// appropriate.] +void pout(char *fmt, ...){ + va_list ap; + + // get the correct time in syslog() + FixGlibcTimeZoneBug(); + // initialize variable argument list + va_start(ap,fmt); + // in debug==1 mode we will print the output from the ataprint.o functions! + if (debugmode && debugmode!=2) +#ifdef _WIN32 + if (facility == LOG_LOCAL1) // logging to stdout + vfprintf(stderr,fmt,ap); + else +#endif + vprintf(fmt,ap); + // in debug==2 mode we print output from knowndrives.o functions + else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) { + openlog("smartd", LOG_PID, facility); + vsyslog(LOG_INFO, fmt, ap); + closelog(); + } + va_end(ap); + fflush(NULL); + return; +} + +// This function prints either to stdout or to the syslog as needed. +// This function is also used by utility.c to report LOG_CRIT errors. +void PrintOut(int priority,char *fmt, ...){ + va_list ap; + + // get the correct time in syslog() + FixGlibcTimeZoneBug(); + // initialize variable argument list + va_start(ap,fmt); + if (debugmode) +#ifdef _WIN32 + if (facility == LOG_LOCAL1) // logging to stdout + vfprintf(stderr,fmt,ap); + else +#endif + vprintf(fmt,ap); + else { + openlog("smartd", LOG_PID, facility); + vsyslog(priority,fmt,ap); + closelog(); + } + va_end(ap); + return; +} + +// Forks new process, closes ALL file descriptors, redirects stdin, +// stdout, and stderr. Not quite daemon(). See +// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html +// for a good description of why we do things this way. +void DaemonInit(){ +#ifndef _WIN32 + pid_t pid; + int i; + + // flush all buffered streams. Else we might get two copies of open + // streams since both parent and child get copies of the buffers. + fflush(NULL); + + if ((pid=fork()) < 0) { + // unable to fork! + PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); + EXIT(EXIT_STARTUP); + } + else if (pid) + // we are the parent process -- exit cleanly + EXIT(0); + + // from here on, we are the child process. + setsid(); + + // Fork one more time to avoid any possibility of having terminals + if ((pid=fork()) < 0) { + // unable to fork! + PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); + EXIT(EXIT_STARTUP); + } + else if (pid) + // we are the parent process -- exit cleanly + EXIT(0); + + // Now we are the child's child... + + // close any open file descriptors + for (i=getdtablesize();i>=0;--i) + close(i); + + // redirect any IO attempts to /dev/null for stdin + i=open("/dev/null",O_RDWR); + // stdout + dup(i); + // stderr + dup(i); + umask(0); + chdir("/"); + + PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid()); + +#else // _WIN32 + + // No fork() on native Win32 + // Detach this process from console + fflush(NULL); + if (daemon_detach("smartd")) { + PrintOut(LOG_CRIT,"smartd unable to detach from console!\n"); + EXIT(EXIT_STARTUP); + } + // stdin/out/err now closed if not redirected + +#endif // _WIN32 + return; +} + +// create a PID file containing the current process id +void WritePidFile() { + if (pid_file) { + int error = 0; + pid_t pid = getpid(); + mode_t old_umask; + FILE* fp; + + old_umask = umask(0077); + fp = fopen(pid_file, "w"); + umask(old_umask); + if (fp == NULL) { + error = 1; + } else if (fprintf(fp, "%d\n", (int)pid) <= 0) { + error = 1; + } else if (fclose(fp) != 0) { + error = 1; + } + if (error) { + PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file); + EXIT(EXIT_PID); + } + PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid); + } + return; +} + +// Prints header identifying version of code and home +void PrintHead(){ + PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-4 Bruce Allen\n", PACKAGE_VERSION, SMARTMONTOOLS_BUILD_HOST); + PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n"); + return; +} + +// prints help info for configuration file Directives +void Directives() { + PrintOut(LOG_INFO, + "Configuration file (%s) Directives (after device name):\n" + " -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N\n" + " -T TYPE Set the tolerance to one of: normal, permissive\n" + " -o VAL Enable/disable automatic offline tests (on/off)\n" + " -S VAL Enable/disable attribute autosave (on/off)\n" + " -n MODE No check if: never, sleep, standby, idle\n" + " -H Monitor SMART Health Status, report if failed\n" + " -s REG Do Self-Test at time(s) given by regular expression REG\n" + " -l TYPE Monitor SMART log. Type is one of: error, selftest\n" + " -f Monitor 'Usage' Attributes, report failures\n" + " -m ADD Send email warning to address ADD\n" + " -M TYPE Modify email warning behavior (see man page)\n" + " -p Report changes in 'Prefailure' Attributes\n" + " -u Report changes in 'Usage' Attributes\n" + " -t Equivalent to -p and -u Directives\n" + " -r ID Also report Raw values of Attribute ID with -p, -u or -t\n" + " -R ID Track changes in Attribute ID Raw value with -p, -u or -t\n" + " -i ID Ignore Attribute ID for -f Directive\n" + " -I ID Ignore Attribute ID for -p, -u or -t Directive\n" + " -C ID Monitor Current Pending Sectors in Attribute ID\n" + " -U ID Monitor Offline Uncorrectable Sectors in Attribute ID\n" + " -v N,ST Modifies labeling of Attribute N (see man page) \n" + " -P TYPE Drive-specific presets: use, ignore, show, showall\n" + " -a Default: -H -f -t -l error -l selftest -C 197 -U 198\n" + " -F TYPE Firmware bug workaround: none, samsung, samsung2\n" + " # Comment: text after a hash sign is ignored\n" + " \\ Line continuation character\n" + "Attribute ID is a decimal integer 1 <= ID <= 255\n" + "Use ID = 0 to turn off -C and/or -U Directives\n" + "Example: /dev/hda -a\n", + configfile); + return; +} + +/* Returns a pointer to a static string containing a formatted list of the valid + arguments to the option opt or NULL on failure. */ +const char *GetValidArgList(char opt) { + switch (opt) { + case 'c': + return "<FILE_NAME>, -"; + case 's': + return "valid_regular_expression"; + case 'l': + return "daemon, local0, local1, local2, local3, local4, local5, local6, local7"; + case 'q': + return "nodev, errors, nodevstartup, never, onecheck"; + case 'r': + return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; + case 'p': + return "<FILE_NAME>"; + case 'i': + return "<INTEGER_SECONDS>"; + default: + return NULL; + } +} + +/* prints help information for command syntax */ +void Usage (void){ + PrintOut(LOG_INFO,"Usage: smartd [options]\n\n"); +#ifdef HAVE_GETOPT_LONG + PrintOut(LOG_INFO," -c NAME|-, --configfile=NAME|-\n"); + PrintOut(LOG_INFO," Read configuration file NAME or stdin [default is %s]\n\n", configfile); + PrintOut(LOG_INFO," -d, --debug\n"); + PrintOut(LOG_INFO," Start smartd in debug mode\n\n"); + PrintOut(LOG_INFO," -D, --showdirectives\n"); + PrintOut(LOG_INFO," Print the configuration file Directives and exit\n\n"); + PrintOut(LOG_INFO," -h, --help, --usage\n"); + PrintOut(LOG_INFO," Display this help and exit\n\n"); + PrintOut(LOG_INFO," -i N, --interval=N\n"); + PrintOut(LOG_INFO," Set interval between disk checks to N seconds, where N >= 10\n\n"); + PrintOut(LOG_INFO," -l local[0-7], --logfacility=local[0-7]\n"); +#if !(defined(_WIN32) || defined(__CYGWIN__)) + PrintOut(LOG_INFO," Use syslog facility local0 - local7 or daemon [default]\n\n"); +#else +#ifdef _WIN32 + PrintOut(LOG_INFO," Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n"); +#else + PrintOut(LOG_INFO," Use syslog facility local0 - local7 (ignored on Cygwin)\n\n"); +#endif +#endif // _WIN32 || __CYGWIN__ + PrintOut(LOG_INFO," -p NAME, --pidfile=NAME\n"); + PrintOut(LOG_INFO," Write PID file NAME\n\n"); + PrintOut(LOG_INFO," -q WHEN, --quit=WHEN\n"); + PrintOut(LOG_INFO," Quit on one of: %s\n\n", GetValidArgList('q')); + PrintOut(LOG_INFO," -r, --report=TYPE\n"); + PrintOut(LOG_INFO," Report transactions for one of: %s\n\n", GetValidArgList('r')); +#ifdef _WIN32 + PrintOut(LOG_INFO," --service\n"); + PrintOut(LOG_INFO," Running as windows service (must be first arg, do not use from console)\n\n"); +#endif // _WIN32 + PrintOut(LOG_INFO," -V, --version, --license, --copyright\n"); + PrintOut(LOG_INFO," Print License, Copyright, and version information\n"); +#else + PrintOut(LOG_INFO," -c NAME|- Read configuration file NAME or stdin [default is %s]\n", configfile); + PrintOut(LOG_INFO," -d Start smartd in debug mode\n"); + PrintOut(LOG_INFO," -D Print the configuration file Directives and exit\n"); + PrintOut(LOG_INFO," -h Display this help and exit\n"); + PrintOut(LOG_INFO," -i N Set interval between disk checks to N seconds, where N >= 10\n"); + PrintOut(LOG_INFO," -l local? Use syslog facility local0 - local7, or daemon\n"); + PrintOut(LOG_INFO," -p NAME Write PID file NAME\n"); + PrintOut(LOG_INFO," -q WHEN Quit on one of: %s\n", GetValidArgList('q')); + PrintOut(LOG_INFO," -r TYPE Report transactions for one of: %s\n", GetValidArgList('r')); + PrintOut(LOG_INFO," -V Print License, Copyright, and version information\n"); +#endif +} + +// returns negative if problem, else fd>=0 +static int OpenDevice(char *device, char *mode, int scanning) { + int fd; + char *s=device; + + // If there is an ASCII "space" character in the device name, + // terminate string there. This is for 3ware devices only. + if ((s=strchr(device,' '))) + *s='\0'; + + // open the device + fd = deviceopen(device, mode); + + // if we removed a space, put it back in please + if (s) + *s=' '; + + // if we failed to open the device, complain! + if (fd < 0) { + + // For linux+devfs, a nonexistent device gives a strange error + // message. This makes the error message a bit more sensible. + // If no debug and scanning - don't print errors + if (debugmode || !scanning) { + if (errno==ENOENT || errno==ENOTDIR) + errno=ENODEV; + + PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n", + device, strerror(errno)); + } + return -1; + } + // device opened sucessfully + return fd; +} + +int CloseDevice(int fd, char *name){ + if (deviceclose(fd)){ + PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd); + return 1; + } + // device sucessfully closed + return 0; +} + +// returns <0 on failure +int ATAErrorCount(int fd, char *name){ + struct ata_smart_errorlog log; + + if (-1==ataReadErrorLog(fd,&log)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name); + return -1; + } + + // return current number of ATA errors + return log.error_log_pointer?log.ata_error_count:0; +} + +// returns <0 if problem. Otherwise, bottom 8 bits are the self test +// error count, and top bits are the power-on hours of the last error. +int SelfTestErrorCount(int fd, char *name){ + struct ata_smart_selftestlog log; + + if (-1==ataReadSelfTestLog(fd,&log)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name); + return -1; + } + + // return current number of self-test errors + return ataPrintSmartSelfTestlog(&log,0); +} + +// scan to see what ata devices there are, and if they support SMART +int ATADeviceScan(cfgfile *cfg, int scanning){ + int fd, supported=0; + struct ata_identify_device drive; + char *name=cfg->name; + int retainsmartdata=0; + int retid; + char *mode; + + // should we try to register this as an ATA device? + switch (cfg->controller_type) { + case CONTROLLER_ATA: + case CONTROLLER_3WARE_678K: + case CONTROLLER_MARVELL_SATA: + case CONTROLLER_UNKNOWN: + mode="ATA"; + break; + case CONTROLLER_3WARE_678K_CHAR: + mode="ATA_3WARE_678K"; + break; + case CONTROLLER_3WARE_9000_CHAR: + mode="ATA_3WARE_9000"; + break; + default: + // not a recognized ATA or SATA device. We should never enter + // this branch. + return 1; + } + + // open the device + if ((fd=OpenDevice(name, mode, scanning))<0) + // device open failed + return 1; + PrintOut(LOG_INFO,"Device: %s, opened\n", name); + + // pass user settings on to low-level ATA commands + con->controller_port=cfg->controller_port; + con->controller_type=cfg->controller_type; + con->fixfirmwarebug = cfg->fixfirmwarebug; + + // Get drive identity structure + if ((retid=ataReadHDIdentity (fd,&drive))){ + if (retid<0) + // Unable to read Identity structure + PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name); + else + PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n", + name, packetdevicetype(retid-1)); + CloseDevice(fd, name); + return 2; + } + + // Show if device in database, and use preset vendor attribute + // options unless user has requested otherwise. + if (cfg->ignorepresets) + PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name); + else { + // do whatever applypresets decides to do. Will allocate memory if + // cfg->attributedefs is needed. + if (applypresets(&drive, &cfg->attributedefs, con)<0) + PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); + else + PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name); + + // then save the correct state of the flag (applypresets may have changed it) + cfg->fixfirmwarebug = con->fixfirmwarebug; + } + + // If requested, show which presets would be used for this drive + if (cfg->showpresets) { + int savedebugmode=debugmode; + PrintOut(LOG_INFO, "Device %s: presets are:\n", name); + if (!debugmode) + debugmode=2; + showpresets(&drive); + debugmode=savedebugmode; + } + + // see if drive supports SMART + supported=ataSmartSupport(&drive); + if (supported!=1) { + if (supported==0) + // drive does NOT support SMART + PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name); + else + // can't tell if drive supports SMART + PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name); + + // should we proceed anyway? + if (cfg->permissive){ + PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name); + } + else { + PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name); + CloseDevice(fd, name); + return 2; + } + } + + if (ataEnableSmart(fd)){ + // Enable SMART command has failed + PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name); + CloseDevice(fd, name); + return 2; + } + + // disable device attribute autosave... + if (cfg->autosave==1){ + if (ataDisableAutoSave(fd)) + PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name); + else + PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name); + } + + // or enable device attribute autosave + if (cfg->autosave==2){ + if (ataEnableAutoSave(fd)) + PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name); + else + PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name); + } + + // capability check: SMART status + if (cfg->smartcheck && ataSmartStatus2(fd)==-1){ + PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name); + cfg->smartcheck=0; + } + + // capability check: Read smart values and thresholds. Note that + // smart values are ALSO needed even if we ONLY want to know if the + // device is self-test log or error-log capable! After ATA-5, this + // information was ALSO reproduced in the IDENTIFY DEVICE response, + // but sadly not for ATA-5. Sigh. + + // do we need to retain SMART data after returning from this routine? + retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage; + + // do we need to get SMART data? + if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) { + + unsigned char currentpending, offlinepending; + + cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)); + cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt)); + + if (!cfg->smartval || !cfg->smartthres){ + PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n"); + EXIT(EXIT_NOMEM); + } + + if (ataReadSmartValues(fd,cfg->smartval) || + ataReadSmartThresholds (fd,cfg->smartthres)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name); + retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0; + cfg->pending=DONT_MONITOR_UNC; + } + + // see if the necessary Attribute is there to monitor offline or + // current pending sectors + TranslatePending(cfg->pending, ¤tpending, &offlinepending); + + if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) { + PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n", + name, (int)currentpending); + cfg->pending &= 0xff00; + cfg->pending |= CUR_UNC_DEFAULT; + } + + if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) { + PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count - no Attribute %d\n", + name, (int)offlinepending); + cfg->pending &= 0x00ff; + cfg->pending |= OFF_UNC_DEFAULT<<8; + } + } + + // enable/disable automatic on-line testing + if (cfg->autoofflinetest){ + // is this an enable or disable request? + char *what=(cfg->autoofflinetest==1)?"disable":"enable"; + if (!cfg->smartval) + PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what); + else { + // if command appears unsupported, issue a warning... + if (!isSupportAutomaticTimer(cfg->smartval)) + PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name); + // ... but then try anyway + if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd)) + PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what); + else + PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what); + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int retval; + + // start with service disabled, and re-enable it if all works OK + cfg->selftest=0; + cfg->selflogcount=0; + cfg->selfloghour=0; + + if (!cfg->smartval) + PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name); + else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive)) + PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name); + else if ((retval=SelfTestErrorCount(fd, name))<0) + PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name); + else { + cfg->selftest=1; + cfg->selflogcount=SELFTEST_ERRORCOUNT(retval); + cfg->selfloghour =SELFTEST_ERRORHOURS(retval); + } + } + + // capability check: ATA error log + if (cfg->errorlog){ + int val; + + // start with service disabled, and re-enable it if all works OK + cfg->errorlog=0; + cfg->ataerrorcount=0; + + if (!cfg->smartval) + PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name); + else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive)) + PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name); + else if ((val=ATAErrorCount(fd, name))<0) + PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name); + else { + cfg->errorlog=1; + cfg->ataerrorcount=val; + } + } + + // If we don't need to save SMART data, get rid of it now + if (!retainsmartdata) { + if (cfg->smartval) { + cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion); + bytes-=sizeof(struct ata_smart_values); + } + if (cfg->smartthres) { + cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion); + bytes-=sizeof(struct ata_smart_thresholds_pvt); + } + } + + // capabilities check -- does it support powermode? + if (cfg->powermode) { + int powermode=ataCheckPowerMode(fd); + + if (-1 == powermode) { + PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name); + cfg->powermode=0; + } + else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) { + PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", + name, powermode); + cfg->powermode=0; + } + } + + // If no tests available or selected, return + if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || + cfg->usagefailed || cfg->prefail || cfg->usage)) { + CloseDevice(fd, name); + return 3; + } + + // Do we still have entries available? + while (numdevata>=atadevlist_max) + atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device"); + + // register device + PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name); + + // record number of device, type of device, increment device count + if (cfg->controller_type == CONTROLLER_UNKNOWN) + cfg->controller_type=CONTROLLER_ATA; + + // close file descriptor + CloseDevice(fd, name); + return 0; +} + +// Returns 1 if device recognised as one we do not want to treat as a general +// SCSI device. Also returns 1 if INQUIRY fails (all "SCSI" devices should +// respond to INQUIRY). Otherwise returns 0 (i.e. normal SCSI device). +static int SCSIFilterKnown(int fd, char * device) +{ + char req_buff[256]; + char di_buff[256]; + int req_len, avail_len, len; + + memset(req_buff, 0, 96); + req_len = 36; + if (scsiStdInquiry(fd, req_buff, req_len)) { + /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ + /* watch this spot ... other devices could lock up here */ + req_len = 64; + if (scsiStdInquiry(fd, req_buff, req_len)) { + PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device); + // device doesn't like INQUIRY commands + return 1; + } + } + avail_len = req_buff[4] + 5; + len = (avail_len < req_len) ? avail_len : req_len; + if (len >= 36) { + if (0 == strncmp(req_buff + 8, "3ware", 5)) { + PrintOut(LOG_INFO, "Device %s, please try '-d 3ware,N'\n", device); + return 1; + } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) { + PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device); + return 1; + } else if ((avail_len >= 96) && (0 == strncmp(req_buff + 8, "ATA", 3))) { + /* <<<< This is Linux specific code to detect SATA disks using a + SCSI-ATA command translation layer. This may be generalized + later when the t10.org SAT project matures. >>>> */ + req_len = 96; + memset(di_buff, 0, req_len); + if (scsiInquiryVpd(fd, 0x83, di_buff, req_len)) { + return 0; // guess it is normal device + } + avail_len = ((di_buff[2] << 8) + di_buff[3]) + 4; + len = (avail_len < req_len) ? avail_len : req_len; + if (isLinuxLibAta(di_buff, len)) { + PrintOut(LOG_INFO, "Device %s, SATA disks accessed via libata are not" + " currently supported by\nsmartmontools. When libata is given" + " an ATA pass-thru ioctl() then an\nadditional '-d libata'" + " device type will be added to smartmontools.\n", device); + return 1; + } + } + } + return 0; +} + +// on success, return 0. On failure, return >0. Never return <0, +// please. +static int SCSIDeviceScan(cfgfile *cfg, int scanning) { + int k, fd, err; + char *device = cfg->name; + struct scsi_iec_mode_page iec; + UINT8 tBuf[64]; + + // should we try to register this as a SCSI device? + switch (cfg->controller_type) { + case CONTROLLER_SCSI: + case CONTROLLER_UNKNOWN: + break; + default: + return 1; + } + + // open the device + if ((fd = OpenDevice(device, "SCSI", scanning)) < 0) + return 1; + PrintOut(LOG_INFO,"Device: %s, opened\n", device); + + // early skip if device known and needs to be handled by some other + // device type (e.g. '-d 3ware,<n>') + if (SCSIFilterKnown(fd, device)) { + CloseDevice(fd, device); + return 2; + } + + // check that device is ready for commands. IE stores its stuff on + // the media. + if ((err = scsiTestUnitReady(fd))) { + if (SIMPLE_ERR_NOT_READY == err) + PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device); + else if (SIMPLE_ERR_NO_MEDIUM == err) + PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device); + else if (SIMPLE_ERR_BECOMING_READY == err) + PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device); + else + PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err); + CloseDevice(fd, device); + return 2; + } + + // Badly-conforming USB storage devices may fail this check. + // The response to the following IE mode page fetch (current and + // changeable values) is carefully examined. It has been found + // that various USB devices that malform the response will lock up + // if asked for a log page (e.g. temperature) so it is best to + // bail out now. + if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len))) + cfg->modese_len = iec.modese_len; + else if (SIMPLE_ERR_BAD_FIELD == err) + ; /* continue since it is reasonable not to support IE mpage */ + else { /* any other error (including malformed response) unreasonable */ + PrintOut(LOG_INFO, + "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", + device, err); + CloseDevice(fd, device); + return 3; + } + + // N.B. The following is passive (i.e. it doesn't attempt to turn on + // smart if it is off). This may change to be the same as the ATA side. + if (!scsi_IsExceptionControlEnabled(&iec)) { + PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n" + "Try 'smartctl -s on %s' to turn on SMART features\n", + device, device); + CloseDevice(fd, device); + return 3; + } + + // Device exists, and does SMART. Add to list (allocating more space if needed) + while (numdevscsi >= scsidevlist_max) + scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device"); + + // Flag that certain log pages are supported (information may be + // available from other sources). + if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, tBuf, sizeof(tBuf), 0)) { + for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) { + switch (tBuf[k]) { + case TEMPERATURE_LPAGE: + cfg->TempPageSupported = 1; + break; + case IE_LPAGE: + cfg->SmartPageSupported = 1; + break; + default: + break; + } + } + } + + // record type of device + cfg->controller_type = CONTROLLER_SCSI; + + // get rid of allocated memory only needed for ATA devices. These + // might have been allocated if the user specified Ignore options or + // other ATA-only Attribute-specific options on the DEVICESCAN line. + cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion); + cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion); + cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion); + cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion); + + // Check if scsiCheckIE() is going to work + { + UINT8 asc = 0; + UINT8 ascq = 0; + UINT8 currenttemp = 0; + UINT8 triptemp = 0; + + if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported, + &asc, &ascq, ¤ttemp, &triptemp)) { + PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); + cfg->SuppressReport = 1; + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int retval=scsiCountFailedSelfTests(fd, 0); + if (retval<0) { + // no self-test log, turn off monitoring + PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device); + cfg->selftest=0; + cfg->selflogcount=0; + cfg->selfloghour=0; + } + else { + // register starting values to watch for changes + cfg->selflogcount=SELFTEST_ERRORCOUNT(retval); + cfg->selfloghour =SELFTEST_ERRORHOURS(retval); + } + } + + // disable autosave (set GLTSD bit) + if (cfg->autosave==1){ + if (scsiSetControlGLTSD(fd, 1, cfg->modese_len)) + PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device); + else + PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device); + } + + // or enable autosave (clear GLTSD bit) + if (cfg->autosave==2){ + if (scsiSetControlGLTSD(fd, 0, cfg->modese_len)) + PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device); + else + PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device); + } + + // tell user we are registering device + PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device); + + // close file descriptor + CloseDevice(fd, device); + return 0; +} + +// We compare old and new values of the n'th attribute. Note that n +// is NOT the attribute ID number.. If (Normalized & Raw) equal, +// then return 0, else nonzero. +int ATACompareValues(changedattribute_t *delta, + struct ata_smart_values *new, + struct ata_smart_values *old, + struct ata_smart_thresholds_pvt *thresholds, + int n, char *name){ + struct ata_smart_attribute *now,*was; + struct ata_smart_threshold_entry *thre; + unsigned char oldval,newval; + int sameraw; + + // check that attribute number in range, and no null pointers + if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds) + return 0; + + // pointers to disk's values and vendor's thresholds + now=new->vendor_attributes+n; + was=old->vendor_attributes+n; + thre=thresholds->thres_entries+n; + + // consider only valid attributes + if (!now->id || !was->id || !thre->id) + return 0; + + + // issue warning if they don't have the same ID in all structures: + if ( (now->id != was->id) || (now->id != thre->id) ){ + PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n", + name, (int)now->id, (int)was->id, (int)thre->id); + return 0; + } + + // new and old values of Normalized Attributes + newval=now->current; + oldval=was->current; + + // See if the RAW values are unchanged (ie, the same) + if (memcmp(now->raw, was->raw, 6)) + sameraw=0; + else + sameraw=1; + + // if any values out of the allowed range, or if the values haven't + // changed, return 0 + if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw)) + return 0; + + // values have changed. Construct output and return + delta->newval=newval; + delta->oldval=oldval; + delta->id=now->id; + delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags); + delta->sameraw=sameraw; + + return 1; +} + +// This looks to see if the corresponding bit of the 32 bytes is set. +// This wastes a few bytes of storage but eliminates all searching and +// sorting functions! Entry is ZERO <==> the attribute ON. Calling +// with set=0 tells you if the attribute is being tracked or not. +// Calling with set=1 turns the attribute OFF. +int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){ + unsigned char *data; + int loc=attr>>3; + int bit=attr & 0x07; + unsigned char mask=0x01<<bit; + + if (which>=NMONITOR || which < 0){ + PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s", + whatline, filenameandversion, which, reportbug); + EXIT(EXIT_BADCODE); + } + + if (*datap == NULL){ + // NULL data implies Attributes are ON... + if (!set) + return 0; + + // we are writing + if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){ + PrintOut(LOG_CRIT,"No memory to create monattflags\n"); + EXIT(EXIT_NOMEM); + } + } + + // pointer to the 256 bits that we need + data=*datap+which*32; + + // attribute zero is always OFF + if (!attr) + return 1; + + if (!set) + return (data[loc] & mask); + + data[loc]|=mask; + + // return value when setting has no sense + return 0; +} + +// If the self-test log has got more self-test errors (or more recent +// self-test errors) recorded, then notify user. +void CheckSelfTestLogs(cfgfile *cfg, int new){ + char *name=cfg->name; + + if (new<0) + // command failed + MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name); + else { + // old and new error counts + int oldc=cfg->selflogcount; + int newc=SELFTEST_ERRORCOUNT(new); + + // old and new error timestamps in hours + int oldh=cfg->selfloghour; + int newh=SELFTEST_ERRORHOURS(new); + + if (oldc<newc) { + // increase in error count + PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n", + name, oldc, newc); + MailWarning(cfg, 3, "Device: %s, Self-Test Log error count increased from %d to %d", + name, oldc, newc); + } else if (oldh<newh) { + // more recent error + PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n", + name, newh); + MailWarning(cfg, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n", + name, newh); + } + + // Needed since self-test error count may DECREASE. Hour should + // never decrease but this does no harm. + cfg->selflogcount= newc; + cfg->selfloghour = newh; + } + return; +} + +// returns 1 if time to do test of type testtype, 0 if not time to do +// test, < 0 if error +int DoTestNow(cfgfile *cfg, char testtype) { + // start by finding out the time: + struct tm *timenow; + time_t epochnow; + char matchpattern[16]; + regmatch_t substring; + int weekday, length; + unsigned short hours; + testinfo *dat=cfg->testdata; + + // check that self-testing has been requested + if (!dat) + return 0; + + // since we are about to call localtime(), be sure glibc is informed + // of any timezone changes we make. + FixGlibcTimeZoneBug(); + + // construct pattern containing the month, day of month, day of + // week, and hour + time(&epochnow); + timenow=localtime(&epochnow); + + // tm_wday is 0 (Sunday) to 6 (Saturday). We use 1 (Monday) to 7 + // (Sunday). + weekday=timenow->tm_wday?timenow->tm_wday:7; + sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1, + timenow->tm_mday, weekday, timenow->tm_hour); + + // if no match, we are done + if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0)) + return 0; + + // must match the ENTIRE type/date/time string + length=strlen(matchpattern); + if (substring.rm_so!=0 || substring.rm_eo!=length) + return 0; + + // never do a second test in the same hour as another test (the % 7 ensures + // that the RHS will never be greater than 65535 and so will always fit into + // an unsigned short) + hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7)); + if (hours==dat->hour) { + if (testtype!=dat->testtype) + PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n", + cfg->name, dat->testtype, testtype); + return 0; + } + + // save time and type of the current test; we are ready to do a test + dat->hour=hours; + dat->testtype=testtype; + return 1; +} + +// Return zero on success, nonzero on failure. Perform offline (background) +// short or long (extended) self test on given scsi device. +int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) { + int retval = 0; + char *testname = NULL; + char *name = cfg->name; + int inProgress; + + if (scsiSelfTestInProgress(fd, &inProgress)) { + PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name); + cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1; + return 1; + } + + if (1 == inProgress) { + PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in " + "progress.\n", name); + return 1; + } + + switch (testtype) { + case 'S': + testname = "Short Self"; + retval = scsiSmartShortSelfTest(fd); + break; + case 'L': + testname = "Long Self"; + retval = scsiSmartExtendSelfTest(fd); + break; + } + // If we can't do the test, exit + if (NULL == testname) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, + testtype); + return 1; + } + if (retval) { + if ((SIMPLE_ERR_BAD_OPCODE == retval) || + (SIMPLE_ERR_BAD_FIELD == retval)) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, + testname); + if ('L'==testtype) + cfg->testdata->not_cap_long=1; + else + cfg->testdata->not_cap_short=1; + + return 1; + } + PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, + testname, retval); + return 1; + } + + PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname); + + return 0; +} + +// Do an offline immediate or self-test. Return zero on success, +// nonzero on failure. +int DoATASelfTest(int fd, cfgfile *cfg, char testtype) { + + struct ata_smart_values data; + char *testname=NULL; + int retval, dotest=-1; + char *name=cfg->name; + + // Read current smart data and check status/capability + if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) { + PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name); + return 1; + } + + // Check for capability to do the test + switch (testtype) { + case 'O': + testname="Offline Immediate "; + if (isSupportExecuteOfflineImmediate(&data)) + dotest=OFFLINE_FULL_SCAN; + else + cfg->testdata->not_cap_offline=1; + break; + case 'C': + testname="Conveyance Self-"; + if (isSupportConveyanceSelfTest(&data)) + dotest=CONVEYANCE_SELF_TEST; + else + cfg->testdata->not_cap_conveyance=1; + break; + case 'S': + testname="Short Self-"; + if (isSupportSelfTest(&data)) + dotest=SHORT_SELF_TEST; + else + cfg->testdata->not_cap_short=1; + break; + case 'L': + testname="Long Self-"; + if (isSupportSelfTest(&data)) + dotest=EXTEND_SELF_TEST; + else + cfg->testdata->not_cap_long=1; + break; + } + + // If we can't do the test, exit + if (dotest<0) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname); + return 1; + } + + // If currently running a self-test, do not interrupt it to start another. + if (15==(data.self_test_exec_status >> 4)) { + PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n", + name, testname, (int)(data.self_test_exec_status & 0x0f)); + return 1; + } + + // else execute the test, and return status + if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL))) + PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname); + else + PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname); + + return retval; +} + + +int ATACheckDevice(cfgfile *cfg){ + int fd,i; + char *name=cfg->name; + char *mode="ATA"; + + // fix firmware bug if requested + con->fixfirmwarebug=cfg->fixfirmwarebug; + con->controller_port=cfg->controller_port; + con->controller_type=cfg->controller_type; + + // If user has asked, test the email warning system + if (cfg->mailwarn && cfg->mailwarn->emailtest) + MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); + + if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR) + mode="ATA_3WARE_9000"; + + if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR) + mode="ATA_3WARE_678K"; + + // if we can't open device, fail gracefully rather than hard -- + // perhaps the next time around we'll be able to open it. ATAPI + // cd/dvd devices will hang awaiting media if O_NONBLOCK is not + // given (see linux cdrom driver). + if ((fd=OpenDevice(name, mode, 0))<0){ + MailWarning(cfg, 9, "Device: %s, unable to open device", name); + return 1; + } + + // user may have requested (with the -n Directive) to leave the disk + // alone if it is in idle or sleeping mode. In this case check the + // power mode and exit without check if needed + if (cfg->powermode){ + int dontcheck=0, powermode=ataCheckPowerMode(fd); + char *mode=NULL; + + switch (powermode){ + case -1: + // SLEEP + mode="SLEEP"; + if (cfg->powermode>=1) + dontcheck=1; + break; + case 0: + // STANDBY + mode="STANDBY"; + if (cfg->powermode>=2) + dontcheck=1; + break; + case 0x80: + // IDLE + mode="IDLE"; + if (cfg->powermode>=3) + dontcheck=1; + break; + case 0xff: + // ACTIVE/IDLE + break; + default: + // UNKNOWN + PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", + name, powermode); + cfg->powermode=0; + break; + } + + // if we are going to skip a check, return now + if (dontcheck){ + CloseDevice(fd, name); + PrintOut(LOG_INFO, "Device: %s, is in %s mode, skipping checks\n", name, mode); + return 0; + } + } + + // check smart status + if (cfg->smartcheck){ + int status=ataSmartStatus2(fd); + if (status==-1){ + PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name); + MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name); + } + else if (status==1){ + PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name); + MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name); + } + } + + // Check everything that depends upon SMART Data (eg, Attribute values) + if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC){ + struct ata_smart_values curval; + struct ata_smart_thresholds_pvt *thresh=cfg->smartthres; + + // Read current attribute values. *drive contains old values and thresholds + if (ataReadSmartValues(fd,&curval)){ + PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name); + MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name); + } + else { + // look for current or offline pending sectors + if (cfg->pending != DONT_MONITOR_UNC) { + int64_t rawval; + unsigned char currentpending, offlinepending; + + TranslatePending(cfg->pending, ¤tpending, &offlinepending); + + if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) { + // Unreadable pending sectors!! + PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval); + MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval); + } + + if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) { + // Unreadable offline sectors!! + PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval); + MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval); + } + } + + if (cfg->usagefailed || cfg->prefail || cfg->usage) { + + // look for failed usage attributes, or track usage or prefail attributes + for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){ + int att; + changedattribute_t delta; + + // This block looks for usage attributes that have failed. + // Prefail attributes that have failed are returned with a + // positive sign. No failure returns 0. Usage attributes<0. + if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){ + + // are we ignoring failures of this attribute? + att *= -1; + if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){ + char attname[64], *loc=attname; + + // get attribute name & skip white space + ataPrintSmartAttribName(loc, att, cfg->attributedefs); + while (*loc && *loc==' ') loc++; + + // warning message + PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc); + MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc); + } + } + + // This block tracks usage or prefailure attributes to see if + // they are changing. It also looks for changes in RAW values + // if this has been requested by user. + if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){ + unsigned char id=delta.id; + + // if the only change is the raw value, and we're not + // tracking raw value, then continue loop over attributes + if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__)) + continue; + + // are we tracking this attribute? + if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){ + char newrawstring[64], oldrawstring[64], attname[64], *loc=attname; + + // get attribute name, skip spaces + ataPrintSmartAttribName(loc, id, cfg->attributedefs); + while (*loc && *loc==' ') loc++; + + // has the user asked for us to print raw values? + if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) { + // get raw values (as a string) and add to printout + char rawstring[64]; + ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs); + sprintf(newrawstring, " [Raw %s]", rawstring); + ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs); + sprintf(oldrawstring, " [Raw %s]", rawstring); + } + else + newrawstring[0]=oldrawstring[0]='\0'; + + // prefailure attribute + if (cfg->prefail && delta.prefail) + PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n", + name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring); + + // usage attribute + if (cfg->usage && !delta.prefail) + PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n", + name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring); + } + } // endof block tracking usage or prefailure + } // end of loop over attributes + + // Save the new values into *drive for the next time around + *(cfg->smartval)=curval; + } + } + } + + // check if number of selftest errors has increased (note: may also DECREASE) + if (cfg->selftest) + CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name)); + + // check if number of ATA errors has increased + if (cfg->errorlog){ + + int new,old=cfg->ataerrorcount; + + // new number of errors + new=ATAErrorCount(fd, name); + + // did command fail? + if (new<0) + // lack of PrintOut here is INTENTIONAL + MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name); + + // has error count increased? + if (new>old){ + PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n", + name, old, new); + MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d", + name, old, new); + } + + // this last line is probably not needed, count always increases + if (new>=0) + cfg->ataerrorcount=new; + } + + // if the user has asked, and device is capable (or we're not yet + // sure) carry out scheduled self-tests. + if (cfg->testdata) { + // long test + if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L')>0) + DoATASelfTest(fd, cfg, 'L'); + // short test + else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S')>0) + DoATASelfTest(fd, cfg, 'S'); + // conveyance test + else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C')>0) + DoATASelfTest(fd, cfg, 'C'); + // offline immediate + else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O')>0) + DoATASelfTest(fd, cfg, 'O'); + } + + // Don't leave device open -- the OS/user may want to access it + // before the next smartd cycle! + CloseDevice(fd, name); + return 0; +} + +#define DEF_SCSI_REPORT_TEMPERATURE_DELTA 2 +static int scsi_report_temperature_delta = DEF_SCSI_REPORT_TEMPERATURE_DELTA; + +int SCSICheckDevice(cfgfile *cfg) +{ + UINT8 asc, ascq; + UINT8 currenttemp; + UINT8 triptemp; + int fd; + char *name=cfg->name; + const char *cp; + + // If the user has asked for it, test the email warning system + if (cfg->mailwarn && cfg->mailwarn->emailtest) + MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); + + // if we can't open device, fail gracefully rather than hard -- + // perhaps the next time around we'll be able to open it + if ((fd=OpenDevice(name, "SCSI", 0))<0) { + // Lack of PrintOut() here is intentional! + MailWarning(cfg, 9, "Device: %s, unable to open device", name); + return 1; + } + currenttemp = 0; + asc = 0; + ascq = 0; + if (! cfg->SuppressReport) { + if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported, + &asc, &ascq, ¤ttemp, &triptemp)) { + PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n", + name); + MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name); + cfg->SuppressReport = 1; + } + } + if (asc > 0) { + cp = scsiGetIEString(asc, ascq); + if (cp) { + PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); + MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp); + } + } else if (debugmode) + PrintOut(LOG_INFO,"Device: %s, Acceptable asc,ascq: %d,%d\n", + name, (int)asc, (int)ascq); + + if (currenttemp && currenttemp!=255) { + if (cfg->Temperature) { + if (abs(((int)currenttemp - (int)cfg->Temperature)) >= + scsi_report_temperature_delta) { + PrintOut(LOG_INFO, "Device: %s, Temperature changed %d Celsius " + "to %d Celsius since last report\n", name, + (int)(currenttemp - cfg->Temperature), + (int)currenttemp); + cfg->Temperature = currenttemp; + } + } + else { + PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d " + "Celsius\n", name, (int)currenttemp); + if (triptemp) + PrintOut(LOG_INFO, " [trip Temperature is %d Celsius]\n", + (int)triptemp); + cfg->Temperature = currenttemp; + cfg->Temperature = currenttemp; + } + } + + // check if number of selftest errors has increased (note: may also DECREASE) + if (cfg->selftest) + CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0)); + + if (cfg->testdata) { + // long (extended) background test + if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L')>0) + DoSCSISelfTest(fd, cfg, 'L'); + // short background test + else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S')>0) + DoSCSISelfTest(fd, cfg, 'S'); + } + CloseDevice(fd, name); + return 0; +} + +// Checks the SMART status of all ATA and SCSI devices +void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){ + int i; + + for (i=0; i<numdevata; i++) + ATACheckDevice(atadevices[i]); + + for (i=0; i<numdevscsi; i++) + SCSICheckDevice(scsidevices[i]); + + return; +} + +#if SCSITIMEOUT +// This alarm means that a SCSI USB device was hanging +void AlarmHandler(int signal) { + longjmp(registerscsienv, 1); +} +#endif + +// Does initialization right after fork to daemon mode +void Initialize(time_t *wakeuptime){ + + // install goobye message and remove pidfile handler + atexit(Goodbye); + + // write PID file only after installing exit handler + if (!debugmode) + WritePidFile(); + + // install signal handlers. On Solaris, can't use signal() because + // it resets the handler to SIG_DFL after each call. So use sigset() + // instead. So SIGNALFN()==signal() or SIGNALFN()==sigset(). + + // normal and abnormal exit + if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN) + SIGNALFN(SIGTERM, SIG_IGN); + if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN) + SIGNALFN(SIGQUIT, SIG_IGN); + + // in debug mode, <CONTROL-C> ==> HUP + if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN) + SIGNALFN(SIGINT, SIG_IGN); + + // Catch HUP and USR1 + if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN) + SIGNALFN(SIGHUP, SIG_IGN); + if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN) + SIGNALFN(SIGUSR1, SIG_IGN); +#ifdef _WIN32 + if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN) + SIGNALFN(SIGUSR2, SIG_IGN); +#endif + + // initialize wakeup time to CURRENT time + *wakeuptime=time(NULL); + + return; +} + +#ifdef _WIN32 +// Toggle debug mode implemented for native windows only +// (there is no easy way to reopen tty on *nix) +static void ToggleDebugMode() +{ + if (!debugmode) { + PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n"); + if (!daemon_enable_console("smartd [Debug]")) { + debugmode = 1; + daemon_signal(SIGINT, HUPhandler); + PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid()); + } + else + PrintOut(LOG_INFO,"enable console failed\n"); + } + else if (debugmode == 1) { + daemon_disable_console(); + debugmode = 0; + daemon_signal(SIGINT, sighandler); + PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n"); + } + else + PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode); +} +#endif + +time_t dosleep(time_t wakeuptime){ + time_t timenow=0; + + // If past wake-up-time, compute next wake-up-time + timenow=time(NULL); + while (wakeuptime<=timenow){ + int intervals=1+(timenow-wakeuptime)/checktime; + wakeuptime+=intervals*checktime; + } + + // sleep until we catch SIGUSR1 or have completed sleeping + while (timenow<wakeuptime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT){ + + // protect user again system clock being adjusted backwards + if (wakeuptime>timenow+checktime){ + PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n"); + wakeuptime=timenow+checktime; + } + + // Exit sleep when time interval has expired or a signal is received + sleep(wakeuptime-timenow); + +#ifdef _WIN32 + // toggle debug mode? + if (caughtsigUSR2) { + ToggleDebugMode(); + caughtsigUSR2 = 0; + } +#endif + + timenow=time(NULL); + } + + // if we caught a SIGUSR1 then print message and clear signal + if (caughtsigUSR1){ + PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n", + wakeuptime-timenow>0?(int)(wakeuptime-timenow):0); + caughtsigUSR1=0; + } + + // return adjusted wakeuptime + return wakeuptime; +} + +// Print out a list of valid arguments for the Directive d +void printoutvaliddirectiveargs(int priority, char d) { + char *s=NULL; + + switch (d) { + case 'n': + PrintOut(priority, "never, sleep, standby, idle"); + break; + case 's': + PrintOut(priority, "valid_regular_expression"); + break; + case 'd': + PrintOut(priority, "ata, scsi, marvell, removable, 3ware,N"); + break; + case 'T': + PrintOut(priority, "normal, permissive"); + break; + case 'o': + case 'S': + PrintOut(priority, "on, off"); + break; + case 'l': + PrintOut(priority, "error, selftest"); + break; + case 'M': + PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\""); + break; + case 'v': + if (!(s = create_vendor_attribute_arg_list())) { + PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n"); + EXIT(EXIT_NOMEM); + } + PrintOut(priority, "\n%s\n", s); + s=CheckFree(s, __LINE__,filenameandversion); + break; + case 'P': + PrintOut(priority, "use, ignore, show, showall"); + break; + case 'F': + PrintOut(priority, "none, samsung, samsung2"); + break; + } +} + +// exits with an error message, or returns integer value of token +int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){ + char *endptr; + int val; + + // check input range + if (min<0){ + PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min); + return -1; + } + + // make sure argument is there + if (!arg) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n", + configfile, lineno, name, token, min, max); + return -1; + } + + // get argument value (base 10), check that it's integer, and in-range + val=strtol(arg,&endptr,10); + if (*endptr!='\0' || val<min || val>max ) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n", + configfile, lineno, name, token, arg, min, max); + return -1; + } + + // all is well; return value + return val; +} + +// This function returns 1 if it has correctly parsed one token (and +// any arguments), else zero if no tokens remain. It returns -1 if an +// error was encountered. +int ParseToken(char *token,cfgfile *cfg){ + char sym; + char *name=cfg->name; + int lineno=cfg->lineno; + char *delim = " \n\t"; + int badarg = 0; + int missingarg = 0; + char *arg = NULL; + int makemail=0; + maildata *mdat=NULL, tempmail; + + // is the rest of the line a comment + if (*token=='#') + return 1; + + // is the token not recognized? + if (*token!='-' || strlen(token)!=2) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", + configfile, lineno, name, token); + PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n"); + return -1; + } + + // token we will be parsing: + sym=token[1]; + + // create temporary maildata structure. This means we can postpone + // allocating space in the data segment until we are sure there are + // no errors. + if ('m'==sym || 'M'==sym){ + if (!cfg->mailwarn){ + memset(&tempmail, 0, sizeof(maildata)); + mdat=&tempmail; + makemail=1; + } + else + mdat=cfg->mailwarn; + } + + // parse the token and swallow its argument + switch (sym) { + int val; + + case 'C': + // monitor current pending sector count (default 197) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0) + return -1; + if (val==CUR_UNC_DEFAULT) + val=0; + else if (val==0) + val=CUR_UNC_DEFAULT; + // set bottom 8 bits to correct value + cfg->pending &= 0xff00; + cfg->pending |= val; + break; + case 'U': + // monitor offline uncorrectable sectors (default 198) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0) + return -1; + if (val==OFF_UNC_DEFAULT) + val=0; + else if (val==0) + val=OFF_UNC_DEFAULT; + // turn off top 8 bits, then set to correct value + cfg->pending &= 0xff; + cfg->pending |= (val<<8); + break; + case 'T': + // Set tolerance level for SMART command failures + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "normal")) { + // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but + // not on failure of an optional S.M.A.R.T. command. + // This is the default so we don't need to actually do anything here. + cfg->permissive=0; + } else if (!strcmp(arg, "permissive")) { + // Permissive mode; ignore errors from Mandatory SMART commands + cfg->permissive=1; + } else { + badarg = 1; + } + break; + case 'd': + // specify the device type + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "ata")) { + cfg->controller_port = 0; + cfg->controller_type = CONTROLLER_ATA; + } else if (!strcmp(arg, "scsi")) { + cfg->controller_port =0; + cfg->controller_type = CONTROLLER_SCSI; + } else if (!strcmp(arg, "marvell")) { + cfg->controller_port =0; + cfg->controller_type = CONTROLLER_MARVELL_SATA; + } else if (!strcmp(arg, "removable")) { + cfg->removable = 1; + } else { + // look 3ware,N RAID device + int i; + char *s; + + // make a copy of the string to mess with + if (!(s = strdup(arg))) { + PrintOut(LOG_CRIT, + "No memory to copy argument to -d option - exiting\n"); + EXIT(EXIT_NOMEM); + } else if (strncmp(s,"3ware,",6)) { + badarg=1; + } else if (split_report_arg2(s, &i)){ + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n", + configfile, lineno, name); + badarg=1; + } else if ( i<0 || i>15) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n", + configfile, lineno, name, i); + badarg=1; + } else { + // determine type of escalade device from name of device + cfg->controller_type = guess_device_type(name); + if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR) + cfg->controller_type=CONTROLLER_3WARE_678K; + + // NOTE: controller_port == disk number + 1 + cfg->controller_port = i+1; + } + s=CheckFree(s, __LINE__,filenameandversion); + } + break; + case 'F': + // fix firmware bug + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "none")) { + cfg->fixfirmwarebug = FIX_NONE; + } else if (!strcmp(arg, "samsung")) { + cfg->fixfirmwarebug = FIX_SAMSUNG; + } else if (!strcmp(arg, "samsung2")) { + cfg->fixfirmwarebug = FIX_SAMSUNG2; + } else { + badarg = 1; + } + break; + case 'H': + // check SMART status + cfg->smartcheck=1; + break; + case 'f': + // check for failure of usage attributes + cfg->usagefailed=1; + break; + case 't': + // track changes in all vendor attributes + cfg->prefail=1; + cfg->usage=1; + break; + case 'p': + // track changes in prefail vendor attributes + cfg->prefail=1; + break; + case 'u': + // track changes in usage vendor attributes + cfg->usage=1; + break; + case 'l': + // track changes in SMART logs + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "selftest")) { + // track changes in self-test log + cfg->selftest=1; + } else if (!strcmp(arg, "error")) { + // track changes in ATA error log + cfg->errorlog=1; + } else { + badarg = 1; + } + break; + case 'a': + // monitor everything + cfg->smartcheck=1; + cfg->prefail=1; + cfg->usagefailed=1; + cfg->usage=1; + cfg->selftest=1; + cfg->errorlog=1; + break; + case 'o': + // automatic offline testing enable/disable + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "on")) { + cfg->autoofflinetest = 2; + } else if (!strcmp(arg, "off")) { + cfg->autoofflinetest = 1; + } else { + badarg = 1; + } + break; + case 'n': + // skip disk check if in idle or standby mode + if (!(arg = strtok(NULL, delim))) + missingarg = 1; + else if (!strcmp(arg, "never")) + cfg->powermode = 0; + else if (!strcmp(arg, "sleep")) + cfg->powermode = 1; + else if (!strcmp(arg, "standby")) + cfg->powermode = 2; + else if (!strcmp(arg, "idle")) + cfg->powermode = 3; + else + badarg = 1; + break; + case 'S': + // automatic attribute autosave enable/disable + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "on")) { + cfg->autosave = 2; + } else if (!strcmp(arg, "off")) { + cfg->autosave = 1; + } else { + badarg = 1; + } + break; + case 's': + // warn user, and delete any previously given -s REGEXP Directives + if (cfg->testdata){ + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n", + configfile, lineno, name, cfg->testdata->regex); + cfg->testdata=FreeTestData(cfg->testdata); + } + // check for missing argument + if (!(arg = strtok(NULL, delim))) { + missingarg = 1; + } + // allocate space for structure and string + else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n", + configfile, lineno, name, arg); + EXIT(EXIT_NOMEM); + } + else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) { + char errormsg[512]; + // not a valid regular expression! + regerror(val, &(cfg->testdata->cregex), errormsg, 512); + PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n", + configfile, lineno, name, arg, errormsg); + cfg->testdata=FreeTestData(cfg->testdata); + return -1; + } + // Do a bit of sanity checking and warn user if we think that + // their regexp is "strange". User probably confused about shell + // glob(3) syntax versus regular expression syntax regexp(7). + if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.+*|()?^$[]SLCO"))) + PrintOut(LOG_INFO, "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n", + configfile, lineno, name, val+1, arg[val], arg); + break; + case 'm': + // send email to address that follows + if (!(arg = strtok(NULL,delim))) + missingarg = 1; + else { + if (mdat->address) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", + configfile, lineno, name, mdat->address); + mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion); + } + mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion); + } + break; + case 'M': + // email warning options + if (!(arg = strtok(NULL, delim))) + missingarg = 1; + else if (!strcmp(arg, "once")) + mdat->emailfreq = 1; + else if (!strcmp(arg, "daily")) + mdat->emailfreq = 2; + else if (!strcmp(arg, "diminishing")) + mdat->emailfreq = 3; + else if (!strcmp(arg, "test")) + mdat->emailtest = 1; + else if (!strcmp(arg, "exec")) { + // Get the next argument (the command line) + if (!(arg = strtok(NULL, delim))) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", + configfile, lineno, name, token); + return -1; + } + // Free the last cmd line given if any, and copy new one + if (mdat->emailcmdline) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n", + configfile, lineno, name, mdat->emailcmdline); + mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion); + } + mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion); + } + else + badarg = 1; + break; + case 'i': + // ignore failure of usage attribute + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__); + break; + case 'I': + // ignore attribute for tracking purposes + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__); + break; + case 'r': + // print raw value when tracking + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); + break; + case 'R': + // track changes in raw value (forces printing of raw value) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__); + break; + case 'v': + // non-default vendor-specific attribute meaning + if (!(arg=strtok(NULL,delim))) { + missingarg = 1; + } else if (parse_attribute_def(arg, &cfg->attributedefs)){ + badarg = 1; + } + break; + case 'P': + // Define use of drive-specific presets. + if (!(arg = strtok(NULL, delim))) { + missingarg = 1; + } else if (!strcmp(arg, "use")) { + cfg->ignorepresets = FALSE; + } else if (!strcmp(arg, "ignore")) { + cfg->ignorepresets = TRUE; + } else if (!strcmp(arg, "show")) { + cfg->showpresets = TRUE; + } else if (!strcmp(arg, "showall")) { + showallpresets(); + } else { + badarg = 1; + } + break; + default: + // Directive not recognized + PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", + configfile, lineno, name, token); + Directives(); + return -1; + } + if (missingarg) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n", + configfile, lineno, name, token); + } + if (badarg) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n", + configfile, lineno, name, token, arg); + } + if (missingarg || badarg) { + PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token); + printoutvaliddirectiveargs(LOG_CRIT, sym); + PrintOut(LOG_CRIT, "\n"); + return -1; + } + + // If this did something to fill the mail structure, and that didn't + // already exist, create it and copy. + if (makemail) { + if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n", + configfile, lineno, name); + EXIT(EXIT_NOMEM); + } + memcpy(cfg->mailwarn, mdat, sizeof(maildata)); + } + + return 1; +} + +// Allocate storage for a new cfgfile entry. If original!=NULL, it's +// a copy of the original, but with private data storage. Else all is +// zeroed. Returns address, and fails if non memory available. + +cfgfile *CreateConfigEntry(cfgfile *original){ + cfgfile *add; + + // allocate memory for new structure + if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile)))) + goto badexit; + + // if old structure was pointed to, copy it + if (original) + memcpy(add, original, sizeof(cfgfile)); + + // make private copies of data items ONLY if they are in use (non + // NULL) + add->name = CustomStrDup(add->name, 0, __LINE__,filenameandversion); + + if (add->testdata) { + int val; + if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo)))) + goto badexit; + memcpy(add->testdata, original->testdata, sizeof(testinfo)); + add->testdata->regex = CustomStrDup(add->testdata->regex, 1, __LINE__,filenameandversion); + // only POSIX-portable way to make fresh copy of compiled regex is + // to recompile it completely. There is no POSIX + // compiled-regex-copy command. + if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) { + char errormsg[512]; + regerror(val, &(add->testdata->cregex), errormsg, 512); + PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg); + goto badexit; + } + } + + if (add->mailwarn) { + if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata)))) + goto badexit; + memcpy(add->mailwarn, original->mailwarn, sizeof(maildata)); + add->mailwarn->address = CustomStrDup(add->mailwarn->address, 0, __LINE__,filenameandversion); + add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion); + } + + if (add->attributedefs) { + if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1))) + goto badexit; + memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM); + } + + if (add->monitorattflags) { + if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1))) + goto badexit; + memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32); + } + + if (add->smartval) { + if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)))) + goto badexit; + } + + if (add->smartthres) { + if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt)))) + goto badexit; + } + + return add; + + badexit: + PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n"); + EXIT(EXIT_NOMEM); +} + + + +// This is the routine that adds things to the cfgentries list. To +// prevent memory leaks when re-reading the configuration file many +// times, this routine MUST deallocate any memory other than that +// pointed to within cfg-> before it returns. +// +// Return values are: +// 1: parsed a normal line +// 0: found comment or blank line +// -1: found SCANDIRECTIVE line +// -2: found an error +// +// Note: this routine modifies *line from the caller! +int ParseConfigLine(int entry, int lineno,char *line){ + char *token=NULL; + char *name=NULL; + char *delim = " \n\t"; + cfgfile *cfg=NULL; + int devscan=0; + + // get first token: device name. If a comment, skip line + if (!(name=strtok(line,delim)) || *name=='#') { + return 0; + } + + // Have we detected the SCANDIRECTIVE directive? + if (!strcmp(SCANDIRECTIVE,name)){ + devscan=1; + if (entry) { + PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile); + return -2; + } + } + + // Is there space for another entry? If not, allocate more + while (entry>=cfgentries_max) + cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device"); + + // We've got a legit entry, make space to store it + cfg=cfgentries[entry]=CreateConfigEntry(NULL); + cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion); + + // Store line number, and by default check for both device types. + cfg->lineno=lineno; + + // Try and recognize if a IDE or SCSI device. These can be + // overwritten by configuration file directives. + if (cfg->controller_type==CONTROLLER_UNKNOWN) + cfg->controller_type = guess_device_type(cfg->name); + + // parse tokens one at a time from the file. + while ((token=strtok(NULL,delim))){ + int retval=ParseToken(token,cfg); + + if (retval==0) + // No tokens left: + break; + + if (retval>0) { + // Parsed token +#if (0) + PrintOut(LOG_INFO,"Parsed token %s\n",token); +#endif + continue; + } + + if (retval<0) { + // error found on the line + return -2; + } + } + + // If we found 3ware controller, then modify device name by adding a SPACE + if (cfg->controller_port){ + int len=17+strlen(cfg->name); + char *newname; + + if (devscan){ + PrintOut(LOG_CRIT, "smartd: can not scan for 3ware devices (line %d of file %s)\n", + lineno, configfile); + return -2; + } + + if (!(newname=(char *)calloc(len,1))) { + PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno)); + EXIT(EXIT_NOMEM); + } + + // Make new device name by adding a space then RAID disk number + snprintf(newname, len, "%s [3ware_disk_%02d]", cfg->name, cfg->controller_port-1); + cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion); + cfg->name=newname; + bytes+=16; + } + + // If NO monitoring directives are set, then set all of them. + if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail || + cfg->usage || cfg->selftest || cfg->errorlog )){ + + PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n", + cfg->name, cfg->lineno, configfile); + + cfg->smartcheck=1; + cfg->usagefailed=1; + cfg->prefail=1; + cfg->usage=1; + cfg->selftest=1; + cfg->errorlog=1; + } + + // additional sanity check. Has user set -M options without -m? + if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){ + PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n", + cfg->name, cfg->lineno, configfile); + return -2; + } + + // has the user has set <nomailer>? + if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){ + // check that -M exec is also set + if (!cfg->mailwarn->emailcmdline){ + PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n", + cfg->name, cfg->lineno, configfile); + return -2; + } + // now free memory. From here on the sign of <nomailer> is + // address==NULL and cfg->emailcmdline!=NULL + cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion); + } + + // set cfg->emailfreq to 1 (once) if user hasn't set it + if (cfg->mailwarn && !cfg->mailwarn->emailfreq) + cfg->mailwarn->emailfreq = 1; + + entry++; + + if (devscan) + return -1; + else + return 1; +} + +// clean up utility for ParseConfigFile() +void cleanup(FILE **fpp, int is_stdin){ + if (*fpp){ + // (*fpp != stdin) does not work here if stdin has been closed & reopened + if (!is_stdin) + fclose(*fpp); + *fpp=NULL; + } + + return; +} + + +// Parses a configuration file. Return values are: +// N=>0: found N entries +// -1: syntax error in config file +// -2: config file does not exist +// -3: config file exists but cannot be read +// +// In the case where the return value is 0, there are three +// possiblities: +// Empty configuration file ==> cfgentries==NULL +// No configuration file ==> cfgentries[0]->lineno == 0 +// SCANDIRECTIVE found ==> cfgentries[0]->lineno != 0 +int ParseConfigFile(){ + FILE *fp=NULL; + int entry=0,lineno=1,cont=0,contlineno=0; + char line[MAXLINELEN+2]; + char fullline[MAXCONTLINE+1]; + + int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here + + // Open config file, if it exists and is not <stdin> + if (!is_stdin) { + fp=fopen(configfile,"r"); + if (fp==NULL && (errno!=ENOENT || configfile_alt)) { + // file exists but we can't read it or it should exist due to '-c' option + int ret = (errno!=ENOENT ? -3 : -2); + PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n", + strerror(errno),configfile); + return ret; + } + } + else // read from stdin ('-c -' option) + fp = stdin; + + // No configuration file found -- use fake one + if (fp==NULL) { + int len=strlen(SCANDIRECTIVE)+4; + char *fakeconfig=(char *)calloc(len,1); + + if (!fakeconfig || + (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) || + -1 != ParseConfigLine(entry, 0, fakeconfig) + ) { + PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s", + __LINE__, filenameandversion, reportbug); + EXIT(EXIT_BADCODE); + } + fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion); + return 0; + } + + // configuration file exists + PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile); + + // parse config file line by line + while (1) { + int len=0,scandevice; + char *lastslash; + char *comment; + char *code; + + // make debugging simpler + memset(line,0,sizeof(line)); + + // get a line + code=fgets(line,MAXLINELEN+2,fp); + + // are we at the end of the file? + if (!code){ + if (cont) { + scandevice=ParseConfigLine(entry,contlineno,fullline); + // See if we found a SCANDIRECTIVE directive + if (scandevice==-1) { + cleanup(&fp, is_stdin); + return 0; + } + // did we find a syntax error + if (scandevice==-2) { + cleanup(&fp, is_stdin); + return -1; + } + // the final line is part of a continuation line + cont=0; + entry+=scandevice; + } + break; + } + + // input file line number + contlineno++; + + // See if line is too long + len=strlen(line); + if (len>MAXLINELEN){ + char *warn; + if (line[len-1]=='\n') + warn="(including newline!) "; + else + warn=""; + PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n", + (int)contlineno,configfile,warn,(int)MAXLINELEN); + cleanup(&fp, is_stdin); + return -1; + } + + // Ignore anything after comment symbol + if ((comment=strchr(line,'#'))){ + *comment='\0'; + len=strlen(line); + } + + // is the total line (made of all continuation lines) too long? + if (cont+len>MAXCONTLINE){ + PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n", + lineno, (int)contlineno, configfile, (int)MAXCONTLINE); + cleanup(&fp, is_stdin); + return -1; + } + + // copy string so far into fullline, and increment length + strcpy(fullline+cont,line); + cont+=len; + + // is this a continuation line. If so, replace \ by space and look at next line + if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){ + *(fullline+(cont-len)+(lastslash-line))=' '; + continue; + } + + // Not a continuation line. Parse it + scandevice=ParseConfigLine(entry,contlineno,fullline); + + // did we find a scandevice directive? + if (scandevice==-1) { + cleanup(&fp, is_stdin); + return 0; + } + // did we find a syntax error + if (scandevice==-2) { + cleanup(&fp, is_stdin); + return -1; + } + + entry+=scandevice; + lineno++; + cont=0; + } + cleanup(&fp, is_stdin); + + // note -- may be zero if syntax of file OK, but no valid entries! + return entry; +} + + +// Prints copyright, license and version information +void PrintCopyleft(void){ + debugmode=1; + PrintHead(); + PrintCVS(); + return; +} + +/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> <=======\n", where + <LIST> is the list of valid arguments for option opt. */ +void PrintValidArgs(char opt) { + const char *s; + + PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: "); + if (!(s = GetValidArgList(opt))) + PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt); + else + PrintOut(LOG_CRIT, (char *)s); + PrintOut(LOG_CRIT, " <=======\n"); +} + +// Parses input line, prints usage message and +// version/license/copyright messages +void ParseOpts(int argc, char **argv){ + extern char *optarg; + extern int optopt, optind, opterr; + int optchar; + int badarg; + char *tailptr; + long lchecktime; + // Please update GetValidArgList() if you edit shortopts + const char *shortopts = "c:l:q:dDi:p:r:Vh?"; +#ifdef HAVE_GETOPT_LONG + char *arg; + // Please update GetValidArgList() if you edit longopts + struct option longopts[] = { + { "configfile", required_argument, 0, 'c' }, + { "logfacility", required_argument, 0, 'l' }, + { "quit", required_argument, 0, 'q' }, + { "debug", no_argument, 0, 'd' }, + { "showdirectives", no_argument, 0, 'D' }, + { "interval", required_argument, 0, 'i' }, + { "pidfile", required_argument, 0, 'p' }, + { "report", required_argument, 0, 'r' }, +#ifdef _WIN32 + { "service", no_argument, 0, 'S' }, +#endif + { "version", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'V' }, + { "copyright", no_argument, 0, 'V' }, + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; +#endif + + opterr=optopt=0; + badarg=FALSE; + + // Parse input options. This horrible construction is so that emacs + // indents properly. Sorry. + while (-1 != (optchar = +#ifdef HAVE_GETOPT_LONG + getopt_long(argc, argv, shortopts, longopts, NULL) +#else + getopt(argc, argv, shortopts) +#endif + )) { + + switch(optchar) { + case 'q': + // when to quit + if (!(strcmp(optarg,"nodev"))) { + quit=0; + } else if (!(strcmp(optarg,"nodevstartup"))) { + quit=1; + } else if (!(strcmp(optarg,"never"))) { + quit=2; + } else if (!(strcmp(optarg,"onecheck"))) { + quit=3; + debugmode=1; + } else if (!(strcmp(optarg,"errors"))) { + quit=4; + } else { + badarg = TRUE; + } + break; + case 'l': + // set the log facility level + if (!strcmp(optarg, "daemon")) + facility=LOG_DAEMON; + else if (!strcmp(optarg, "local0")) + facility=LOG_LOCAL0; + else if (!strcmp(optarg, "local1")) + facility=LOG_LOCAL1; + else if (!strcmp(optarg, "local2")) + facility=LOG_LOCAL2; + else if (!strcmp(optarg, "local3")) + facility=LOG_LOCAL3; + else if (!strcmp(optarg, "local4")) + facility=LOG_LOCAL4; + else if (!strcmp(optarg, "local5")) + facility=LOG_LOCAL5; + else if (!strcmp(optarg, "local6")) + facility=LOG_LOCAL6; + else if (!strcmp(optarg, "local7")) + facility=LOG_LOCAL7; + else + badarg = TRUE; + break; + case 'd': + // enable debug mode + debugmode = TRUE; + break; + case 'D': + // print summary of all valid directives + debugmode = TRUE; + Directives(); + EXIT(0); + break; + case 'i': + // Period (time interval) for checking + // strtol will set errno in the event of overflow, so we'll check it. + errno = 0; + lchecktime = strtol(optarg, &tailptr, 10); + if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg); + PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + checktime = (int)lchecktime; + break; + case 'r': + // report IOCTL transactions + { + int i; + char *s; + + // split_report_arg() may modify its first argument string, so use a + // copy of optarg in case we want optarg for an error message. + if (!(s = strdup(optarg))) { + PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n"); + EXIT(EXIT_NOMEM); + } + if (split_report_arg(s, &i)) { + badarg = TRUE; + } else if (i<1 || i>3) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg); + PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n"); + EXIT(EXIT_BADCMD); + } else if (!strcmp(s,"ioctl")) { + con->reportataioctl = con->reportscsiioctl = i; + } else if (!strcmp(s,"ataioctl")) { + con->reportataioctl = i; + } else if (!strcmp(s,"scsiioctl")) { + con->reportscsiioctl = i; + } else { + badarg = TRUE; + } + s=CheckFree(s, __LINE__,filenameandversion); + } + break; + case 'c': + // alternate configuration file + if (strcmp(optarg,"-")) + configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion); + else // read from stdin + configfile=configfile_stdin; + break; + case 'p': + // output file with PID number + pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion); + break; +#ifdef _WIN32 + case 'S': + // running as service, option already handled by deamon_main(), so ignore it + break; +#endif + case 'V': + // print version and CVS info + PrintCopyleft(); + EXIT(0); + break; + case 'h': + // help: print summary of command-line options + debugmode=1; + PrintHead(); + Usage(); + EXIT(0); + break; + case '?': + default: + // unrecognized option + debugmode=1; + PrintHead(); +#ifdef HAVE_GETOPT_LONG + // Point arg to the argument in which this option was found. + arg = argv[optind-1]; + // Check whether the option is a long option that doesn't map to -h. + if (arg[1] == '-' && optchar != 'h') { + // Iff optopt holds a valid option then argument must be missing. + if (optopt && (strchr(shortopts, optopt) != NULL)) { + PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2); + PrintValidArgs(optopt); + } else { + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2); + } + PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } +#endif + if (optopt) { + // Iff optopt holds a valid option then argument must be missing. + if (strchr(shortopts, optopt) != NULL){ + PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt); + PrintValidArgs(optopt); + } else { + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt); + } + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + Usage(); + EXIT(0); + } + + // Check to see if option had an unrecognized or incorrect argument. + if (badarg) { + debugmode=1; + PrintHead(); + // It would be nice to print the actual option name given by the user + // here, but we just print the short form. Please fix this if you know + // a clean way to do it. + PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg); + PrintValidArgs(optchar); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + } + + // non-option arguments are not allowed + if (argc > optind) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + + // no pidfile in debug mode + if (debugmode && pid_file) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n"); + PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file); + pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion); + EXIT(EXIT_BADCMD); + } + + // print header + PrintHead(); + + return; +} + +// Function we call if no configuration file was found or if the +// SCANDIRECTIVE Directive was found. It makes entries for device +// names returned by make_device_names() in os_OSNAME.c +int MakeConfigEntries(const char *type, int start){ + int i; + int num; + char** devlist = NULL; + cfgfile *first=cfgentries[0],*cfg=first; + + // make list of devices + if ((num=make_device_names(&devlist,type))<0) + PrintOut(LOG_CRIT,"Problem creating device name scan list\n"); + + // if no devices, or error constructing list, return + if (num<=0) + return 0; + + // loop over entries to create + for (i=0; i<num; i++){ + + // make storage and copy for all but first entry + if (start+i) { + // allocate more storage if needed + while (cfgentries_max<=start+i) + cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "simulated configuration file device"); + cfg=cfgentries[start+i]=CreateConfigEntry(first); + } + + // ATA or SCSI? + if (!strcmp(type,"ATA") ) + cfg->controller_type = CONTROLLER_ATA; + if (!strcmp(type,"SCSI") ) + cfg->controller_type = CONTROLLER_SCSI; + + // remove device name, if it's there, and put in correct one + cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion); + // save pointer to the device name created within + // make_device_names + cfg->name=devlist[i]; + } + + // If needed, free memory used for devlist: pointers now in + // cfgentries[]->names. If num==0 we never get to this point, but + // that's OK. If we realloc()d the array length in + // make_device_names() that was ALREADY equivalent to calling + // free(). + devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion); + + return num; +} + +void CanNotRegister(char *name, char *type, int line, int scandirective){ + if( !debugmode && scandirective == 1 ) { return; } + if (line) + PrintOut(scandirective?LOG_INFO:LOG_CRIT, + "Unable to register %s device %s at line %d of file %s\n", + type, name, line, configfile); + else + PrintOut(LOG_INFO,"Unable to register %s device %s\n", + type, name); + return; +} + +// Returns negative value (see ParseConfigFile()) if config file +// had errors, else number of entries which may be zero or positive. +// If we found no configuration file, or it contained SCANDIRECTIVE, +// then *scanning is set to 1, else 0. +int ReadOrMakeConfigEntries(int *scanning){ + int entries; + + // deallocate any cfgfile data structures in memory + RmAllConfigEntries(); + + // parse configuration file configfile (normally /etc/smartd.conf) + if ((entries=ParseConfigFile())<0) { + + // There was an error reading the configuration file. + RmAllConfigEntries(); + if (entries == -1) + PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile); + return entries; + } + + // did we find entries or scan? + *scanning=0; + + // no error parsing config file. + if (entries) { + // we did not find a SCANDIRECTIVE and did find valid entries + PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile); + } + else if (cfgentries && cfgentries[0]) { + // we found a SCANDIRECTIVE or there was no configuration file so + // scan. Configuration file's first entry contains all options + // that were set + cfgfile *first=cfgentries[0]; + int doata = !(first->controller_type==CONTROLLER_SCSI); + int doscsi = !(first->controller_type==CONTROLLER_ATA); + + *scanning=1; + + if (first->lineno) + PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE); + else + PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile); + + // make config list of ATA devices to search for + if (doata) + entries+=MakeConfigEntries("ATA", entries); + // make config list of SCSI devices to search for + if (doscsi) + entries+=MakeConfigEntries("SCSI", entries); + + // warn user if scan table found no devices + if (!entries) { + PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n"); + // get rid of fake entry with SCANDIRECTIVE as name + RmConfigEntry(cfgentries, __LINE__); + } + } + else + PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile); + + return entries; +} + + +// This function tries devices from cfgentries. Each one that can be +// registered is moved onto the [ata|scsi]devices lists and removed +// from the cfgentries list, else it's memory is deallocated. +void RegisterDevices(int scanning){ + int i; + + // start by clearing lists/memory of ALL existing devices + RmAllDevEntries(); + numdevata=numdevscsi=0; + + // Register entries + for (i=0; i<cfgentries_max ; i++){ + + cfgfile *ent=cfgentries[i]; + + // skip any NULL entries (holes) + if (!ent) + continue; + + // register ATA devices + if (ent->controller_type!=CONTROLLER_SCSI){ + if (ATADeviceScan(ent, scanning)) + CanNotRegister(ent->name, "ATA", ent->lineno, scanning); + else { + // move onto the list of ata devices + cfgentries[i]=NULL; + while (numdevata>=atadevlist_max) + atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device"); + atadevlist[numdevata++]=ent; + } + } + + // then register SCSI devices + if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_UNKNOWN){ + int retscsi=0; + +#if SCSITIMEOUT + struct sigaction alarmAction, defaultaction; + + // Set up an alarm handler to catch USB devices that hang on + // SCSI scanning... + alarmAction.sa_handler= AlarmHandler; + alarmAction.sa_flags = SA_RESTART; + if (sigaction(SIGALRM, &alarmAction, &defaultaction)) { + // if we can't set timeout, just scan device + PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n"); + retscsi=SCSIDeviceScan(ent, scanning); + } + else { + // prepare return point in case of bad SCSI device + if (setjmp(registerscsienv)) + // SCSI device timed out! + retscsi=-1; + else { + // Set alarm, make SCSI call, reset alarm + alarm(SCSITIMEOUT); + retscsi=SCSIDeviceScan(ent, scanning); + alarm(0); + } + if (sigaction(SIGALRM, &defaultaction, NULL)){ + PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n"); + } + } +#else + retscsi=SCSIDeviceScan(ent, scanning); +#endif + + // Now scan SCSI device... + if (retscsi){ + if (retscsi<0) + PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name); + CanNotRegister(ent->name, "SCSI", ent->lineno, scanning); + } + else { + // move onto the list of scsi devices + cfgentries[i]=NULL; + while (numdevscsi>=scsidevlist_max) + scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device"); + scsidevlist[numdevscsi++]=ent; + } + } + + // if device is explictly listed and we can't register it, then + // exit unless the user has specified that the device is removable + if (cfgentries[i] && !scanning){ + if (ent->removable || quit==2) + PrintOut(LOG_INFO, "Device %s not available\n", ent->name); + else { + PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name); + EXIT(EXIT_BADDEV); + } + } + + // free up memory if device could not be registered + RmConfigEntry(cfgentries+i, __LINE__); + } + + return; +} + + +#ifndef _WIN32 +// Main function +int main(int argc, char **argv) +#else +// Windows: internal main function started direct or by service control manager +static int smartd_main(int argc, char **argv) +#endif +{ + // external control variables for ATA disks + smartmonctrl control; + + // is it our first pass through? + int firstpass=1; + + // next time to wake up + time_t wakeuptime; + + // for simplicity, null all global communications variables/lists + con=&control; + memset(con, 0,sizeof(control)); + + // parse input and print header and usage info if needed + ParseOpts(argc,argv); + + // do we mute printing from ataprint commands? + con->printing_switchable=0; + con->dont_print=debugmode?0:1; + + // don't exit on bad checksums + con->checksumfail=0; + + // the main loop of the code + while (1){ + + // are we exiting from a signal? + if (caughtsigEXIT) { + // are we exiting with SIGTERM? + int isterm=(caughtsigEXIT==SIGTERM); + int isquit=(caughtsigEXIT==SIGQUIT); + int isok=debugmode?isterm || isquit:isterm; + + PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n", + caughtsigEXIT, strsignal(caughtsigEXIT)); + + EXIT(isok?0:EXIT_SIGNAL); + } + + // Should we (re)read the config file? + if (firstpass || caughtsigHUP){ + int entries, scanning=0; + + if (!firstpass) { +#ifdef __CYGWIN__ + // Workaround for missing SIGQUIT via keyboard on Cygwin + if (caughtsigHUP==2) { + // Simulate SIGQUIT if another SIGINT arrives soon + caughtsigHUP=0; + sleep(1); + if (caughtsigHUP==2) { + caughtsigEXIT=SIGQUIT; + continue; + } + caughtsigHUP=2; + } +#endif + PrintOut(LOG_INFO, + caughtsigHUP==1? + "Signal HUP - rereading configuration file %s\n": + "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n", + configfile); + } + + // clears cfgentries, (re)reads config file, makes >=0 entries + entries=ReadOrMakeConfigEntries(&scanning); + + if (entries>=0) { + // checks devices, then moves onto ata/scsi list or deallocates. + RegisterDevices(scanning); + } + else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) { + // user has asked to continue on error in configuration file + if (!firstpass) + PrintOut(LOG_INFO,"Reusing previous configuration\n"); + } + else { + // exit with configuration file error status + int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF); + EXIT(status); + } + + // Log number of devices we are monitoring... + if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass)) + PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n", + numdevata, numdevscsi); + else { + PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n"); + EXIT(EXIT_NODEV); + } + + // reset signal + caughtsigHUP=0; + } + + // check all devices once + CheckDevicesOnce(atadevlist, scsidevlist); + + // user has asked us to exit after first check + if (quit==3) { + PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n" + "smartd is exiting (exit status 0)\n"); + EXIT(0); + } + + // fork into background if needed + if (firstpass && !debugmode) + DaemonInit(); + + // set exit and signal handlers, write PID file, set wake-up time + if (firstpass){ + Initialize(&wakeuptime); + firstpass=0; + } + + // sleep until next check time, or a signal arrives + wakeuptime=dosleep(wakeuptime); + } +} + + +#ifdef _WIN32 +// Main function for Windows +int main(int argc, char **argv){ + // Options for smartd windows service + static const daemon_winsvc_options svc_opts = { + "--service", // cmd_opt + "smartd", "SmartD Service", // servicename, displayname + // description + "Controls and monitors storage devices using the Self-Monitoring, " + "Analysis and Reporting Technology System (S.M.A.R.T.) " + "built into ATA and SCSI Hard Drives. " + PACKAGE_HOMEPAGE + }; + // daemon_main() handles daemon and service specific commands + // and starts smartd_main() direct, from a new process, + // or via service control manager + return daemon_main("smartd", &svc_opts , smartd_main, argc, argv); +} +#endif diff --git a/sm5/utility.c b/sm5/utility.c new file mode 100644 index 0000000000000000000000000000000000000000..da3f3177f5e02c62e768bbb36b5c03a8927ae1f7 --- /dev/null +++ b/sm5/utility.c @@ -0,0 +1,599 @@ +/* + * utility.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-4 Bruce Allen <smartmontools-support@lists.sourceforge.net> + * Copyright (C) 2000 Michael Cornwell <cornwell@acm.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/ + * + */ + +// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO +// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD, +// SMARTCTL, OR BOTH. + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <stdlib.h> +#include <ctype.h> +#include <syslog.h> +#include <stdarg.h> +#include <sys/stat.h> + +#include "config.h" +#include "int64.h" +#include "utility.h" + +// Any local header files should be represented by a CVSIDX just below. +const char* utility_c_cvsid="$Id: utility.c,v 1.54 2004/08/29 01:08:37 ballen4705 Exp $" +CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID; + +const char * packet_types[] = { + "Direct-access (disk)", + "Sequential-access (tape)", + "Printer", + "Processor", + "Write-once (optical disk)", + "CD/DVD", + "Scanner", + "Optical memory (optical disk)", + "Medium changer", + "Communications", + "Graphic arts pre-press (10)", + "Graphic arts pre-press (11)", + "Array controller", + "Enclosure services", + "Reduced block command (simplified disk)", + "Optical card reader/writer" +}; + +// Whenever exit() status is EXIT_BADCODE, please print this message +const char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n"; + + +// hang on to exit code, so we can make use of more generic 'atexit()' +// functionality and still check our exit code +int exitstatus = 0; + +// command-line argument: are we running in debug mode?. +unsigned char debugmode = 0; + + +// Solaris only: Get site-default timezone. This is called from +// UpdateTimezone() when TZ environment variable is unset at startup. +#if defined (__SVR4) && defined (__sun) +static const char *TIMEZONE_FILE = "/etc/TIMEZONE"; + +static char *ReadSiteDefaultTimezone(){ + FILE *fp; + char buf[512], *tz; + int n; + + tz = NULL; + fp = fopen(TIMEZONE_FILE, "r"); + if(fp == NULL) return NULL; + while(fgets(buf, sizeof(buf), fp)) { + if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line + continue; + n = strlen(buf) - 1; + if (buf[n] == '\n') buf[n] = 0; + if (tz) free(tz); + tz = strdup(buf); + } + fclose(fp); + return tz; +} +#endif + +// Make sure that this executable is aware if the user has changed the +// time-zone since the last time we polled devices. The cannonical +// example is a user who starts smartd on a laptop, then flies across +// time-zones with a laptop, and then changes the timezone, WITHOUT +// restarting smartd. This is a work-around for a bug in +// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and +// thanks to Ian Redfern for posting a workaround. + +// Please refer to the smartd manual page, in the section labeled LOG +// TIMESTAMP TIMEZONE. +void FixGlibcTimeZoneBug(){ +#if __GLIBC__ + if (!getenv("TZ")) { + putenv("TZ=GMT"); + tzset(); + putenv("TZ"); + tzset(); + } +#elif _WIN32 + if (!getenv("TZ")) { + putenv("TZ=GMT"); + tzset(); + putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing + tzset(); + } +#elif defined (__SVR4) && defined (__sun) + // In Solaris, putenv("TZ=") sets null string and invalid timezone. + // putenv("TZ") does nothing. With invalid TZ, tzset() do as if + // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at + // first tzset() call. Conclusion: Unlike glibc, dynamic + // configuration of timezone can be done only by changing actual + // value of TZ environment value. + enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; + static enum tzstate state = NOT_CALLED_YET; + + static struct stat prev_stat; + static char *prev_tz; + struct stat curr_stat; + char *curr_tz; + + if(state == NOT_CALLED_YET) { + if(getenv("TZ")) { + state = USER_TIMEZONE; // use supplied timezone + } else { + state = TRACK_TIMEZONE; + if(stat(TIMEZONE_FILE, &prev_stat)) { + state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever + } else { + prev_tz = ReadSiteDefaultTimezone(); // track timezone file change + if(prev_tz) putenv(prev_tz); + } + } + tzset(); + } else if(state == TRACK_TIMEZONE) { + if(stat(TIMEZONE_FILE, &curr_stat) == 0 + && (curr_stat.st_ctime != prev_stat.st_ctime + || curr_stat.st_mtime != prev_stat.st_mtime)) { + // timezone file changed + curr_tz = ReadSiteDefaultTimezone(); + if(curr_tz) { + putenv(curr_tz); + if(prev_tz) free(prev_tz); + prev_tz = curr_tz; prev_stat = curr_stat; + } + } + tzset(); + } +#endif + // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO + // KEEP THEM INDEPENDENT. + return; +} + +// This value follows the peripheral device type value as defined in +// SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in +// the ATA standard for packet devices to define the device type. +const char *packetdevicetype(int type){ + if (type<0x10) + return packet_types[type]; + + if (type<0x20) + return "Reserved"; + + return "Unknown"; +} + + +// Returns 1 if machine is big endian, else zero. This is a run-time +// rather than a compile-time function. We could do it at +// compile-time but in principle there are architectures that can run +// with either byte-ordering. +int isbigendian(){ + short i=0x0100; + char *tmp=(char *)&i; + return *tmp; +} + +// Utility function prints date and time and timezone into a character +// buffer of length>=64. All the fuss is needed to get the right +// timezone info (sigh). +void dateandtimezoneepoch(char *buffer, time_t tval){ + struct tm *tmval; + char *timezonename; + char datebuffer[DATEANDEPOCHLEN]; + int lenm1; + + FixGlibcTimeZoneBug(); + + // Get the time structure. We need this to determine if we are in + // daylight savings time or not. + tmval=localtime(&tval); + + // Convert to an ASCII string, put in datebuffer + // same as: asctime_r(tmval, datebuffer); + strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); + datebuffer[DATEANDEPOCHLEN-1]='\0'; + + // Remove newline + lenm1=strlen(datebuffer)-1; + datebuffer[lenm1>=0?lenm1:0]='\0'; + + // correct timezone name + if (tmval->tm_isdst==0) + // standard time zone + timezonename=tzname[0]; + else if (tmval->tm_isdst>0) + // daylight savings in effect + timezonename=tzname[1]; + else + // unable to determine if daylight savings in effect + timezonename=""; + + // Finally put the information into the buffer as needed. + snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename); + + return; +} + +// Date and timezone gets printed into string pointed to by buffer +void dateandtimezone(char *buffer){ + + // Get the epoch (time in seconds since Jan 1 1970) + time_t tval=time(NULL); + + dateandtimezoneepoch(buffer, tval); + return; +} + +// These are two utility functions for printing CVS IDs. Massagecvs() +// returns distance that it has moved ahead in the input string +int massagecvs(char *out, const char *cvsid){ + char *copy,*filename,*date,*version; + int retVal=0; + const char delimiters[] = " ,$"; + + // make a copy on the heap, go to first token, + if (!(copy=strdup(cvsid))) + return 0; + + if (!(filename=strtok(copy, delimiters))){ + free(copy); + return 0; + } + + // move to first instance of "Id:" + while (strcmp(filename,"Id:")) + if (!(filename=strtok(NULL, delimiters))){ + free(copy); + 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) ) ) { + free(copy); + return 0; + } + + sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date); + retVal = (date-copy)+strlen(date); + free(copy); + return retVal; +} + +// 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) { + pout("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; +} + + +// A replacement for perror() that sends output to our choice of +// printing. If errno not set then just print message. +void syserror(const char *message){ + + if (errno) { + // Get the correct system error message: + const char *errormessage=strerror(errno); + + // 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); + } + else if (message && *message) + pout("%s\n",message); + + return; +} + +// Prints a warning message for a failed regular expression compilation from +// regcomp(). +void printregexwarning(int errcode, regex_t *compiled){ + size_t length = regerror(errcode, compiled, NULL, 0); + char *buffer = malloc(length); + if (!buffer){ + pout("Out of memory in printregexwarning()\n"); + return; + } + regerror(errcode, compiled, buffer, length); + pout("%s\n", buffer); + free(buffer); + return; +} + +// A wrapper for regcomp(). Returns zero for success, non-zero otherwise. +int compileregex(regex_t *compiled, const char *pattern, int cflags) +{ + int errorcode; + + if ((errorcode = regcomp(compiled, pattern, cflags))) { + pout("Internal error: unable to compile regular expression %s", pattern); + printregexwarning(errorcode, compiled); + pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n"); + return 1; + } + return 0; +} + +// Splits an argument to the -r option into a name part and an (optional) +// positive integer part. s is a pointer to a string containing the +// argument. After the call, s will point to the name part and *i the +// integer part if there is one or 1 otherwise. Note that the string s may +// be changed by this function. Returns zero if successful and non-zero +// otherwise. +int split_report_arg(char *s, int *i) +{ + if ((s = strchr(s, ','))) { + // Looks like there's a name part and an integer part. + char *tailptr; + + *s++ = '\0'; + if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive + return 1; + errno = 0; + *i = (int) strtol(s, &tailptr, 10); + if (errno || *tailptr != '\0') + return 1; + } else { + // There's no integer part. + *i = 1; + } + + return 0; +} + +// same as above but sets *i to -1 if missing , argument +int split_report_arg2(char *s, int *i){ + char *tailptr; + s+=6; + + if (*s=='\0' || !isdigit((int)*s)) { + // What's left must be integer + *i=-1; + return 1; + } + + errno = 0; + *i = (int) strtol(s, &tailptr, 10); + if (errno || *tailptr != '\0') { + *i=-1; + return 1; + } + + return 0; +} + +#ifndef HAVE_STRTOULL +// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0) +// Functionality reduced to split_selective_arg()'s requirements. + +static uint64_t strtoull(const char * p, char * * endp, int base) +{ + uint64_t result, maxres; + int i = 0; + char c = p[i++]; + // assume base == 0 + if (c == '0') { + if (p[i] == 'x' || p[i] == 'X') { + base = 16; i++; + } + else + base = 8; + c = p[i++]; + } + else + base = 10; + + result = 0; + maxres = ~(uint64_t)0 / (unsigned)base; + for (;;) { + unsigned digit; + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('A' <= c && c <= 'Z') + digit = c - 'A' + 10; + else if ('a' <= c && c <= 'z') + digit = c - 'a' + 10; + else + break; + if (digit >= (unsigned)base) + break; + if (!( result < maxres + || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) { + result = ~(uint64_t)0; errno = ERANGE; // return on overflow + break; + } + result = result * (unsigned)base + digit; + c = p[i++]; + } + *endp = (char *)p + i - 1; + return result; +} +#endif // HAVE_STRTOLL + +// Splits an argument to the -t option that is assumed to be of the form +// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex) +// are allowed). The first long long int is assigned to *start and the second +// to *stop. Returns zero if successful and non-zero otherwise. +int split_selective_arg(char *s, uint64_t *start, + uint64_t *stop) +{ + char *tailptr; + + if (!(s = strchr(s, ','))) + return 1; + if (!isdigit((int)(*++s))) + return 1; + errno = 0; + // Last argument to strtoull (the base) is 0 meaning that decimal is assumed + // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used. + *start = strtoull(s, &tailptr, 0); + + s = tailptr; + if (errno || *s++ != '-') + return 1; + *stop = strtoull(s, &tailptr, 0); + if (errno || *tailptr != '\0') + return 1; + return 0; +} + +int64_t bytes = 0; +// Helps debugging. If the second argument is non-negative, then +// decrement bytes by that amount. Else decrement bytes by (one plus) +// length of null terminated string. +void *FreeNonZero(void *address, int size, int line, const char* file){ + if (address) { + if (size<0) + bytes-=1+strlen(address); + else + bytes-=size; + return CheckFree(address, line, file); + } + return NULL; +} + +// To help with memory checking. Use when it is known that address is +// NOT null. +void *CheckFree(void *address, int whatline, const char* file){ + if (address){ + free(address); + return NULL; + } + + PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", + whatline, file, reportbug); + EXIT(EXIT_BADCODE); +} + +// A custom version of calloc() that tracks memory use +void *Calloc(size_t nmemb, size_t size) { + void *ptr=calloc(nmemb, size); + + if (ptr) + bytes+=nmemb*size; + + return ptr; +} + +// A custom version of strdup() that keeps track of how much memory is +// being allocated. If mustexist is set, it also throws an error if we +// try to duplicate a NULL string. +char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file){ + char *tmp; + + // report error if ptr is NULL and mustexist is set + if (ptr==NULL){ + if (mustexist) { + PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", + whatline, file, reportbug); + EXIT(EXIT_BADCODE); + } + else + return NULL; + } + + // make a copy of the string... + tmp=strdup(ptr); + + if (!tmp) { + PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file); + EXIT(EXIT_NOMEM); + } + + // and track memory usage + bytes+=1+strlen(ptr); + + return tmp; +} + +// Returns nonzero if region of memory contains non-zero entries +int nonempty(unsigned char *testarea,int n){ + int i; + for (i=0;i<n;i++) + if (testarea[i]) + return 1; + return 0; +} + + +// This routine converts an integer number of milliseconds into a test +// string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is +// written to the array. +void MsecToText(unsigned int msec, char *txt){ + int start=0; + unsigned int days, hours, min, sec; + + days = msec/86400000U; + msec -= days*86400000U; + + hours = msec/3600000U; + msec -= hours*3600000U; + + min = msec/60000U; + msec -= min*60000U; + + sec = msec/1000U; + msec -= sec*1000U; + + if (days) { + txt += sprintf(txt, "%2dd+", (int)days); + start=1; + } + + sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec); + return; +} + + + diff --git a/www/3w-xxxx.txt b/www/3w-xxxx.txt deleted file mode 100644 index 5dde49c574c8ad84e69d89797ceeea17115f68e6..0000000000000000000000000000000000000000 --- a/www/3w-xxxx.txt +++ /dev/null @@ -1,157 +0,0 @@ ---------------------------------------------------------------------------- -# November 27, 2003 -# -# This patch is now against the official 3ware version 1.02.00.036 3w-xxxx.c driver -# dated Wed Jul 16 20:30:28 2003. Instructions for use: -# -# [1] download the 1.02.00.036 3w-xxxx.c driver from -# http://www.3ware.com/support/download.asp -# -# [2] Unpack it: -# tar zxvf rh7x_8x.tgz (or su7x_8x.tgz for SuSE) -# -# [3] Unpack the source code, and move to the right directory: -# cd src/2.4 -# tar zxvf 3w-xxxx.tgz -# cd driver -# -# [4] Copy THIS FILE (what you are reading!) into that -# directory and name it 3w-xxxx.txt -# -# [5] Patch the driver: -# patch < 3w-xxxx.txt -# You should get the response 'patching file 3w-xxxx.c'. -# -# [6] Build the driver with the command: -# make -# This will create the driver: a file named 3w-xxxx.o -# -# [7] Load the driver (you must be root to do this): -# /sbin/insmod ./3w-xxxx.o -# [Note: if '/sbin/lsmod' shows that the driver is loaded already, -# then unmount any file systems that use it, then unload the driver -# with '/sbin/rmmod 3w-xxxx' first!] -# -# [8] Copy the driver into place in the kernel tree: -# cp ./3w-xxxx.o /lib/modules/`uname -r`/kernel/drivers/scsi -# That's it! -# -# August 14, 2003 -# -# Adam Radford has incorporated a change that now allows the 3w-xxxx -# driver to return the Cylinder Low/High values. These are needed to -# get the SMART health status. This patch incorporates those changes -# as well. -# -# August 12, 2003 -# -# 3ware has incorporated a more general version of this fix into their latest -# 3w-xxxx driver release. Rather than using this patch, you can upgrade your -# 3w-xxxx driver to version 1.02.00.037 or greater. Or you can use this patch. -# -# August 8, 2003 -# PATCH FOR 3WARE 3w-xxxx DRIVER -# Bruce Allen ballen at gravity.phys.uwm.edu -# CVS ID of this file: $Id: 3w-xxxx.txt,v 1.5 2003/11/28 17:58:50 ballen4705 Exp $ -# -# To apply this patch, save this entire file to 3w-xxxx.txt in a -# directory containing the original unpatched 3w-xxxx.c file. Then -# given the command: -# patch < 3w-xxxx.txt -# That's it! -# -# TECHNICAL EXPLANATION OF THE PATCH FOLLOWS. SKIP IT IF YOU DON'T CARE. -# -# The 3w-xxxx SCSI RAID driver for 3ware Escalade controller cards has a bug -# in the "passthru" ioctl() which prevents two SMART commands from being -# passed to the ATA devices behind the controller. The commands are: -# -# SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE -# (Command Register=0xB0/Feature Register=0xD2) -# -# SMART ENABLE/DISABLE AUTOMATIC OFF-LINE -# (Command Register=0xB0/Feature Register=0xDB) -# -# [Note: the second of these commands is listed as "Obsolete" in the ATA -# specifications. It was originally defined in SFF-8035i. Most vendors -# (IBM/Hitachi, Maxtor, Samsung, WD, among others) still implement it for -# backwards compatibility.] -# -# The problem arises because in both cases (stupidly!) the ENABLE subcommand -# is indicated with a nonzero value of the Sector Count Register. For the -# AUTOSAVE command one uses Sector Count Register=0xF1 and for the AUTOMATIC -# OFF-LINE command one uses Sector Count Register=0xF8. -# -# This provokes the following error messages from the 3w-xxxx driver: -# 3w-xxxx: tw_ioctl(): Passthru size (123392) too big. -# 3w-xxxx: tw_ioctl(): Passthru size (126976) too big. -# and the driver doesn't pass the ATA command on. This is because the -# passthru part of the 3w-xxxx driver assumes that the value in the Sector -# Count Register is the number of 512-byte blocks to transfer, and these -# values exceed the internal buffer sizes. -# -# In fact both of these are non-data commands, and so this is trivial to -# fix. I am attaching an 8-line patch for this purpose. It looks for these -# particular commands and then treats them as non-data commands. It has been -# tested on both a 6800 and a 7500 controller, and should be endian-order -# and 32/64-bit clean. -# -# [Note: the normal linux ide drivers also assume that the Sector Count -# Register is the number of 512-byte sectors to transfer to user space. -# But in that case the user can simply allocate a userland buffer large -# enough to hold the 0xf1*0x200 or 0xf8*0x200 bytes, and then ignore the -# contents.] -# -# ----------------------------------------------------------------------- - ---- 3w-xxxx.c.orig Wed Jul 16 20:30:28 2003 -+++ 3w-xxxx.c Thu Nov 27 11:20:25 2003 -@@ -173,6 +173,9 @@ - 1.02.00.035 - Improve tw_allocate_memory() memory allocation. - Fix tw_chrdev_ioctl() to sleep correctly. - 1.02.00.036 - Increase character ioctl timeout to 60 seconds. -+ -+ This version 1.02.00.036 3w-xxxx.c driver has been patched for full smartmontools support. -+ - */ - - #include <linux/module.h> -@@ -1930,12 +1933,15 @@ - } - - passthru = (TW_Passthru *)tw_dev->command_packet_virtual_address[request_id]; -- passthru->sg_list[0].length = passthru->sector_count*512; -- if (passthru->sg_list[0].length > TW_MAX_PASSTHRU_BYTES) { -- printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%d) too big.\n", passthru->sg_list[0].length); -- return 1; -+ /* Don't load sg_list for non-data ATA cmds */ -+ if ((passthru->param != 0) && (passthru->param != 0x8)) { -+ passthru->sg_list[0].length = passthru->sector_count*512; -+ if (passthru->sg_list[0].length > TW_MAX_PASSTHRU_BYTES) { -+ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%d) too big.\n", passthru->sg_list[0].length); -+ return 1; -+ } -+ passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id]; - } -- passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id]; - tw_post_command_packet(tw_dev, request_id); - return 0; - case TW_CMD_PACKET: -@@ -2185,8 +2191,15 @@ - ioctl = (TW_Ioctl *)buff; - switch (ioctl->opcode) { - case TW_ATA_PASSTHRU: -- passthru = (TW_Passthru *)ioctl->data; -- memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512); -+ passthru = (TW_Passthru *)ioctl->data; -+ /* Don't return data for non-data ATA cmds */ -+ if ((passthru->param != 0) && (passthru->param != 0x8)) -+ memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512); -+ else { -+ /* For non-data cmds, return cmd pkt */ -+ if (tw_dev->srb[request_id]->request_bufflen >= sizeof(TW_Command)) -+ memcpy(buff, tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command)); -+ } - break; - case TW_CMD_PACKET_WITH_DATA: - dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n"); diff --git a/www/BadBlockHowTo.txt b/www/BadBlockHowTo.txt deleted file mode 100644 index f59086d1d80605a3d6575a25f0b1bc4a2566f4ed..0000000000000000000000000000000000000000 --- a/www/BadBlockHowTo.txt +++ /dev/null @@ -1,505 +0,0 @@ -THIS DOCUMENT SHOWS HOW TO IDENTIFY THE FILE ASSOCIATED WITH AN -UNREADABLE DISK SECTOR, AND HOW TO FORCE THAT SECTOR TO REALLOCATE. - -Assumptions: Linux OS, ext2 or ext3 file system. - -Bruce Allen <smartmontools-support@lists.sourceforge.net> - -Thanks to Sergey Vlasov, Theodore Ts'o, Michael Bendzick, and others -for explaining this to me. I would like to add text showing how to do -this for other file systems, in particular ReiserFS, XFS, and JFS: -please email me if you can provide this information. - -NOTE: Starting with GNU coreutils release 5.3.0, dd on Linux includes -options 'iflag=direct' and 'oflag=direct'. Using these with the dd commands -below should be helpful, because adding these flags should avoid any interaction -with the block buffering IO layer in Linux and permit direct reads/writes -from the raw device. Use 'dd --help' to see if your version of dd supports -these options. If not, build the latest code from -fttp://alpha.gnu.org/gnu/coreutils. - -In this example, the disk is failing self-tests at Logical Block -Address LBA = 0x016561e9 = 23421417. The LBA counts sectors in units -of 512 bytes, and starts at zero. - ------------------------------------------------------------------------------------------------ -root]# smartctl -l selftest /dev/hda: - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed: read failure 90% 217 0x016561e9 ------------------------------------------------------------------------------------------------ - -Note that other signs that there is a bad sector on the disk can be -found in the non-zero value of the Current Pending Sector count: ------------------------------------------------------------------------------------------------ -root]# smartctl -A /dev/hda -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 0 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 1 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 1 ------------------------------------------------------------------------------------------------ - -First Step: We need to locate the partition on which this sector of -the disk lives: ------------------------------------------------------------------------------------------------ -root]# fdisk -lu /dev/hda - -Disk /dev/hda: 123.5 GB, 123522416640 bytes -255 heads, 63 sectors/track, 15017 cylinders, total 241254720 sectors -Units = sectors of 1 * 512 = 512 bytes - - Device Boot Start End Blocks Id System -/dev/hda1 * 63 4209029 2104483+ 83 Linux -/dev/hda2 4209030 5269319 530145 82 Linux swap -/dev/hda3 5269320 238227884 116479282+ 83 Linux -/dev/hda4 238227885 241248104 1510110 83 Linux ------------------------------------------------------------------------------------------------ - -The partition /dev/hda3 starts at LBA 5269320 and extends past the -'problem' LBA. The 'problem' LBA is offset 23421417 - 5269320 = -18152097 sectors into the partition /dev/hda3. - -To verify the type of the file system and the mount point, look in -/etc/fstab: ------------------------------------------------------------------------------------------------ -root]# grep hda3 /etc/fstab -/dev/hda3 /data ext2 defaults 1 2 ------------------------------------------------------------------------------------------------ -You can see that this is an ext2 file system, mounted at /data. - -Second Step: we need to find the blocksize of the file system -(normally 4096 bytes for ext2): ------------------------------------------------------------------------------------------------ -root]# tune2fs -l /dev/hda3 | grep Block -Block count: 29119820 -Block size: 4096 ------------------------------------------------------------------------------------------------ -In this case the block size is 4096 bytes. - -Third Step: we need to determine which File System Block contains this -LBA. The formula is: - b = (int)((L-S)*512/B) -where: -b = File System block number -B = File system block size in bytes -L = LBA of bad sector -S = Starting sector of partition as shown by fdisk -lu -and (int) denotes the integer part. - -In our example, L=23421417, S=5269320, and B=4096. Hence the -'problem' LBA is in block number - b = (int)18152097*512/4096 = (int)2269012.125 -so b=2269012. - -Note: the fractional part of 0.125 indicates that this problem LBA is -actually the second of the eight sectors that make up this file system -block. - -Fourth Step: we use debugfs to locate the inode stored in this block, -and the file that contains that inode: ------------------------------------------------------------------------------------------------ -root]# debugfs -debugfs 1.32 (09-Nov-2002) -debugfs: open /dev/hda3 -debugfs: icheck 2269012 -Block Inode number -2269012 41032 -debugfs: ncheck 41032 -Inode Pathname -41032 /S1/R/H/714197568-714203359/H-R-714202192-16.gwf ------------------------------------------------------------------------------------------------ - -In this example, you can see that the problematic file (with the mount -point included in the path) is: -/data/S1/R/H/714197568-714203359/H-R-714202192-16.gwf - - -To force the disk to reallocate this bad block we'll write zeros to -the bad block, and sync the disk: ------------------------------------------------------------------------------------------------ -root]# dd if=/dev/zero of=/dev/hda3 bs=4096 count=1 seek=2269012 -root]# sync ------------------------------------------------------------------------------------------------ - -NOTE: THIS LAST STEP HAS PERMANENTLY AND IRRETREVIABLY DESTROYED SOME -OF THE DATA THAT WAS IN THIS FILE. DON'T DO THIS UNLESS YOU DON'T -NEED THE FILE OR YOU CAN REPLACE IT WITH A FRESH OR CORRECT VERSION. - - -Now everything is back to normal: the sector has been reallocated. -Compare the output just below to similar output near the top of this -article: ------------------------------------------------------------------------------------------------ -root]# smartctl -A /dev/hda -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 1 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 1 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 1 ------------------------------------------------------------------------------------------------ - -Note: for some disks it may be necessary to update the SMART Attribute values by using -smartctl -t offline /dev/hda - -The disk now passes its self-tests again: - ------------------------------------------------------------------------------------------------ -root]# smartctl -t long /dev/hda [wait until test completes, then] -root]# smartctl -l selftest /dev/hda - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed without error 00% 239 - -# 2 Extended offline Completed: read failure 90% 217 0x016561e9 -# 3 Extended offline Completed: read failure 90% 212 0x016561e9 -# 4 Extended offline Completed: read failure 90% 181 0x016561e9 -# 5 Extended offline Completed without error 00% 14 - -# 6 Extended offline Completed without error 00% 4 - ------------------------------------------------------------------------------------------------ - -and no longer shows any offline uncorrectable sectors: - ------------------------------------------------------------------------------------------------ -root]# smartctl -A /dev/hda -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 1 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 1 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 0 ------------------------------------------------------------------------------------------------ - - - -A SECOND EXAMPLE - -On this drive, the first sign of trouble was this email from smartd: - - To: ballen - Subject: SMART error (selftest) detected on host: medusa-slave166.medusa.phys.uwm.edu - - This email was generated by the smartd daemon running on host: - medusa-slave166.medusa.phys.uwm.edu in the domain: master001-nis - - The following warning/error was logged by the smartd daemon: - Device: /dev/hda, Self-Test Log error count increased from 0 to 1 - - -Running smartctl -a /dev/hda confirmed the problem: - -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed: read failure 80% 682 0x021d9f44 - -Note that the failing LBA reported is 0x021d9f44 (base 16) = 35495748 (base 10) - -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 0 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 3 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 3 - -and one can see above that there are 3 sectors on the list of pending -sectors that the disk can't read but would like to reallocate. - -The device also shows errors in the SMART error log: - -Error 212 occurred at disk power-on lifetime: 690 hours - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 12 46 9f 1d e2 Error: UNC 18 sectors at LBA = 0x021d9f46 = 35495750 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 25 00 12 46 9f 1d e0 00 2485545.000 READ DMA EXT - -Signs of trouble at this LBA may also be found in SYSLOG: - -[root]# grep LBA /var/log/messages | awk '{print $12}' | sort | uniq - LBAsect=35495748 - LBAsect=35495750 - -So I decide to do a quick check to see how many bad sectors there -really are. Using the bash shell I check 70 sectors around the trouble -area: - -[root]# export i=35495730 -[root]# while [ $i -lt 35495800 ] - > do echo $i - > dd if=/dev/hda of=/dev/null bs=512 count=1 skip=$i - > let i+=1 - > done - -<SNIP> - -35495734 -1+0 records in -1+0 records out -35495735 -dd: reading `/dev/hda': Input/output error -0+0 records in -0+0 records out - -<SNIP> - -35495751 -dd: reading `/dev/hda': Input/output error -0+0 records in -0+0 records out -35495752 -1+0 records in -1+0 records out - -<SNIP> - -which shows that the seventeen sectors 35495735-35495751 (inclusive) -are not readable. - -Next, we identify the files at those locations. The partitioning -information on this disk is identical to the first example above, and -as in that case the problem sectors are on the third partition -/dev/hda3. So we have: - L=35495735 to 35495751 - S=5269320 - B=4096 -so that b=3778301 to 3778303 are the three bad blocks in the file -system. - -[root]# debugfs -debugfs 1.32 (09-Nov-2002) -debugfs: open /dev/hda3 -debugfs: icheck 3778301 -Block Inode number -3778301 45192 -debugfs: icheck 3778302 -Block Inode number -3778302 45192 -debugfs: icheck 3778303 -Block Inode number -3778303 45192 -debugfs: ncheck 45192 -Inode Pathname -45192 /S1/R/H/714979488-714985279/H-R-714979984-16.gwf -debugfs: quit - -And finally, just to confirm that this is really the damaged file: - -[root]# md5sum /data/S1/R/H/714979488-714985279/H-R-714979984-16.gwf -md5sum: /data/S1/R/H/714979488-714985279/H-R-714979984-16.gwf: Input/output error - -Finally we force the disk to reallocate the three bad blocks: -[root]# dd if=/dev/zero of=/dev/hda3 bs=4096 count=3 seek=3778301 -[root]# sync - -We could also probably use: -[root]# dd if=/dev/zero of=/dev/hda bs=512 count=17 seek=35495735 - -At this point we now have: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail Always - 0 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 0 - -which is encouraging, since the pending sectors count is now zero. -Note that the drive reallocation count has not yet increased: the -drive may now have confidence in these sectors and have decided not to -reallocate them.. - -A device self test: - [root#] smartctl -t long /dev/hda -(then wait about an hour) shows no unreadable sectors or errors: - -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed without error 00% 692 - -# 2 Extended offline Completed: read failure 80% 682 0x021d9f44 - -[USEFUL HINTS ADDED BY OTHERS] - ---------------------------------------------------------------------------- - -From: Kay Diederichs - -I read your badblocks-howto at -http://smartmontools.sourceforge.net/BadBlockHowTo.txt and greatly -benefitted from it. One thing that's (maybe) missing is that often the -"smartctl -t long" scan finds a bad sector which is _not_ assigned to -any file. In that case it does not help to run debugfs, or rather -debugfs reports the fact that no file owns that sector. Furthermore, -it is somewhat laborious to come up with the correct numbers for -debugfs, and debugfs is slow ... - -So what I suggest in the case of presence of -Current_Pending_Sector/Offline_Uncorrectable errors is to create a -huge file on that filesystem. - dd if=/dev/zero of=/some/mount/point bs=4k -creates the file. Leave it running until the partition/filesystem is -full. This will make the disk reallocate those sectors which do not -belong to a file. Check the "smartctl -a" output after that and make -sure that the sectors are reallocated. If any remain, use the debugfs -method. Of course the usual caveats apply - back it up first, and so -on. - ---------------------------------------------------------------------------- - -From: Frederic BOITEUX - -HOW TO LOCATE AND REPAIR BAD BLOCKS ON AN LVM VOLUME - -* Smartd reports an error in a short test�: -------------------------------------------- - -# smartctl -a /dev/hdb -... -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short offline Completed: read failure 90% 66 37383668 - -So the disk has a bad block located in LBA block 37383668 - - -* In which physical partition is the bad block�? ------------------------------------------------- - -# sfdisk -lu /dev/hdb - -Disk /dev/hdb: 9729 cylinders, 255 heads, 63 sectors/track -Units = sectors of 512 bytes, counting from 0 - - Device Boot Start End #sectors Id System -/dev/hdb1 63 996029 995967 82 Linux swap / Solaris -/dev/hdb2 * 996030 1188809 192780 83 Linux -/dev/hdb3 1188810 156296384 155107575 8e Linux LVM -/dev/hdb4 0 - 0 0 Empty - -It's in the /dev/hdb3 partition, a LVM2 partition. -From the LVM2 partition beginning, the bad block has an offset of -(37383668 - 1188810) = 36194858 - -We have to find in which LVM2 logical partition the block belongs to. - - -* In which logical partition is the bad block�? ------------------------------------------------ - -*IMPORTANT*�: LVM2 can use different schemes dividing its physical - partitions to logical ones�: linear, striped, contiguous or - not... The following example assumes that allocation is linear�! - - -The physical partition used by LVM2 is divided in PE (Physical Extent) -units of the same size, starting at pe_start' 512 bytes blocks from -the beginning of the physical partition. - -The 'pvdisplay' command gives the size of the PE (in KB) of the -LVM partition�: -# part=/dev/hdb3�; pvdisplay -c $part | awk -F: '{print $8}' -4096 - -To get its size in LBA block size (512 bytes or 0.5 KB), we multiply this -number by 2�: 4096 * 2 = 8192 blocks for each PE. - - -To find the offset from the beginning of the physical partition is a -bit more difficult�: if you have a recent LVM2 version, try�: -# pvs -o+pe_start $part - -Either, you can look in /etc/lvm/backup�: -# grep pe_start $(grep -l $part /etc/lvm/backup/*) - pe_start = 384 - - -Then, we search in which PE is the badblock, calculating the PE rank -in which the faulty block of the partition is�: -physical partition's bad block number / sizeof(PE) = -36194858 / 8192 = 4418.3176 - -So we have to find in which LVM2 logical partition is used the PE -number 4418 (count starts from 0)�: -# lvdisplay --maps |egrep 'Physical|LV Name|Type' - LV Name /dev/WDC80Go/racine - Type linear - Physical volume /dev/hdb3 - Physical extents 0 to 127 - LV Name /dev/WDC80Go/usr - Type linear - Physical volume /dev/hdb3 - Physical extents 128 to 1407 - LV Name /dev/WDC80Go/var - Type linear - Physical volume /dev/hdb3 - Physical extents 1408 to 1663 - LV Name /dev/WDC80Go/tmp - Type linear - Physical volume /dev/hdb3 - Physical extents 1664 to 1791 - LV Name /dev/WDC80Go/home - Type linear - Physical volume /dev/hdb3 - Physical extents 1792 to 3071 - LV Name /dev/WDC80Go/ext1 - Type linear - Physical volume /dev/hdb3 - Physical extents 3072 to 10751 - LV Name /dev/WDC80Go/ext2 - Type linear - Physical volume /dev/hdb3 - Physical extents 10752 to 18932 - -So the PE #4418 is in the /dev/WDC80Go/ext1 LVM logical partition. - - -* Size of logical block of filesystem on /dev/WDC80Go/ext1�: ------------------------------------------------------------- - -It's a ext3 fs, so I get it like this�: -# dumpe2fs /dev/WDC80Go/ext1 | grep 'Block size' -dumpe2fs 1.37 (21-Mar-2005) -Block size: 4096 - - -* bad block number for the filesystem�: ---------------------------------------- - -The logical partition begins on PE 3072�: - (# PE's start of partition * sizeof(PE)) + parttion offset[pe_start] = - (3072 * 8192) + 384 = 25166208 -512b block of the physical partition, so the bad block number for the -filesystem� is�: -(36194858 - 25166208) / (sizeof(fs block) / 512) -= 11028650 / (4096 / 512) = 1378581.25 - - -* Test of the fs bad block�: - -dd if=/dev/WDC80Go/ext1 of=block1378581 bs=4096 count=1 skip=1378581 - -If this dd command succeeds, without any error message in console or -syslog, then the block number calculation is probably wrong�! *Don't* -go further, re-check it and if you don't find the error, please -renunce�! - - -* Search / correction follows the same scheme as for simple - partitions�: - -- find possible impacted files with debugfs (icheck <fs block nb>, - then ncheck <icheck nb>). - -- reallocate bad block writing zeros in it, *using the fs block size*�: - -dd if=/dev/zero of=/dev/WDC80Go/ext1 count=1 bs=4096 seek=1378581 - -Et voil�! - ---------------------------------------------------------------------------- - - - - -This document is version $Id: BadBlockHowTo.txt,v 1.9 2006/06/12 02:16:50 ballen4705 Exp $ -It is Copyright Bruce Allen (2004-6) and distributed under GPL2. - - diff --git a/www/BadBlockSCSIHowTo.txt b/www/BadBlockSCSIHowTo.txt deleted file mode 100644 index a146d0e689912de6e007026b16b6df87456626cf..0000000000000000000000000000000000000000 --- a/www/BadBlockSCSIHowTo.txt +++ /dev/null @@ -1,169 +0,0 @@ -Introduction -============ -This document supplies some extra information, mainly associated with -SCSI disks, to the http://smartmontools.sourceforge.net/BadBlockHowTo.txt -document which concentrates on ATA disks and recovery at the file -system level. - -As the name of the link suggests, the BadBlockHowTo.txt discusses what -can be done when smartmontools reports a bad block. The approach -taken is to use the facilities within the ext2 and ext3 file systems -in Linux to remap around the damaged section of the disk. While this -approach will work with SCSI disks as well, it does have some -disadvantages. - -SCSI disks have their own logical to physical mapping allowing -a damaged sector (usually 512 bytes long) to be remapped irrespective -of the operating system, file system or software RAID being used. -Also if the disk has been "ejected" from a RAID, after repairing -its bad block(s) (or simply reformatting it) the disk could be -used in other roles. - -Details -======= -The terms "block" and "sector" are used interchangeably, although -"block" tends to get used in higher level or more abstract contexts -such as a "logical block". - -When a SCSI disk is formatted, defective sectors identified during -the manufacturing process (the so called "primary" list: PLIST), -those found during the format itself (the "certification" list: CLIST), -those given explicitly to the format command (the DLIST) and optionally -the previous "grown" list (GLIST) are not used in the logical block -map. The number (and low level addresses) of the unmapped sectors can be -found with the READ DEFECT DATA SCSI command. - -SCSI disks tend to be divided into zones which have spare sectors and -perhaps spare tracks, to support the logical block address mapping -process. The idea is that if a logical block is remapped, the heads do not -have to move a long way to access the replacement sector. Note that spare -sectors are a scarce resource. - -Once a SCSI disk format has completed successfully, other problems -may appear over time. These fall into two categories: - - recoverable: the Error Correction Codes (ECC) detect a problem - but it is "small" enough to be corrected. Optionally other - strategies such as retrying the access may retriev the data. - - unrecoverable: try as it may, the disk logic and ECC algorithms - cannot recover the data. This is often reported as a "medium - error". -Other things can go wrong, typically associated with the transport and -they will be reported using a term other than "medium error". For example -a disk may decide a read operation was successful but a computer's host -bus adapter (HBA) checking the incoming data detects a CRC error due to -a bad cable or termination. - -Depending on the disk vendor, recoverable errors can be ignored. After all, -some disks have up to 68 bytes of ECC above the payload size of 512 bytes -so why use up spare sectors which are limited in number (see note A below)? -If the disk does decide to re-allocate (reassign) a sector, then whether it -tries or reports an error immediately depends on the settings of the ARRE -and AWRE bits in the read-write error recovery mode page. Usually these bits -are set enabling automatic (read or write) re-allocation. [It is possible -that disks inside a hardware RAID have those bits cleared (disabled) and the -RAID controller does things manually or flags the disk for replacement.] -The automatic re-allocation may also fail if the zone (or disk) has run out -of spare sectors. - -Another point about RAIDs, and applications that require a high data rate, -is that the controller logic may not want a disk to spend too long trying -to recover an error. - -Unrecoverable errors will cause a "medium error" sense key, perhaps with -some useful additional sense information. If the extended background self -test includes a full disk read scan, one would expect the self test log to -list the bad block, as shown in the BadBlockHowTo.txt document. Recent SCSI -disks with a periodic background scan should also list unrecoverable read -errors (and recoverable errors as well). The advantage of the background -scan is that it runs to completion while self tests will often terminate at -the first serious error. - -SCSI disks expect unrecoverable errors to be fixed manually using the -REASSIGN SCSI command since loss of data is involved. It is possible that an -operating system or a file system could issue the REASSIGN SCSI command -itself but the author is unaware of any examples. The REASSIGN SCSI command -will reassign one or more blocks, attempting to (partially ?) recover the -data (a forlorn hope at this stage), fetch an unused spare sector from the -current zone while adding the damaged old sector to the GLIST (hence the -name "grown" list). The contents of the GLIST may not be that interesting -but smartctl prints out the number of entries in the grown list and if that -number grows quickly, the disk may be approaching the end of its useful life. - -Here is an alternate brute force technique to consider: if the data on the -SCSI or ATA disk has all been backed up (e.g. is held on the other disks in -a RAID 5 enclosure), then simply reformatting the disk may be the least -fiddly approach. - -What to do -========== -Given a "bad block", it still may be useful to look at fdisk (if the disk -has multiple partitions) to find out which partition is involved, then use -debugfs (or a similar tool for the file system in question) to find out -which, if any, file or other part of the file system may have been damaged. -This is discussed in the BadBlockHowTo.txt document. - -Then a program that can execute the REASSIGN SCSI command is required. In -Linux (2.4 and 2.6 series), FreeBSD and Tru64 (osf) the author's sg_reassign -in the sg3_utils package can be used. Also found in that package is -sg_verify which can be used to check that a block is readable. - -Assuming logical block address 0x123456 has been reported by smartmontools -as bad block, then: - # sg_verify --lba=0x123456 /dev/sda - -should also report a problem. To check the number of elements in the -GLIST before the block reassignment, try: - # sg_reassign --grown /dev/sda - -To actually reassign that address try: - # sg_reassign --address=0x123456 /dev/sda - -If that succeeded then checking the GLIST length again should indicate -that it has grown by one element. If the disk was unable to recover -any data, then the "new" block at lba 0x123456 has vendor specific -data in it. The sg_reassign utility can also do bulk reassigns, see -'man sg_reassign' for more information. - -The dd command could be used to read the contents of the "new" block: - # dd if=/dev/sda iflag=direct skip=0x123456 of=blk.img bs=512 count=1 - -and a hex editor used to view and potentially change the 'blk.img' file. -An altered 'blk.img' file (or /dev/zero) could be written back with: - # dd if=blk.img of=/dev/sda seek=0x123456 oflag=direct bs=512 count=1 - -Notes: the 0x123456 is an arbitrary hexadecimal logical block address. -Recent versions of dd (e.g. those that support 'iflag=') support -hexadecimal addresses. Utilities in recent versions of the sg3_utils -package also accept the trailing 'h' notation for hexadecimal. -Alternatively decimal numbers could be used; most window managers have a -handy calculator that will do hex to decimal conversions. More work may -be needed at the file system level, especially if the reassigned block -held critical fs information such as a superblock or a directory. - -Even if a full backup of the disk is available, or the disk has been -"ejected" from a RAID, it may still be worthwhile to reassign the bad -block(s) that caused the problem (or simply format the disk (see sg_format -in the sg3_utils package)) and re-use the disk later (not unlike the -way a replacement disk from a manufacturer might be used). - -Conclusion -========== -This document contains some suggestions of what to do when smartmontools -reports a "bad block" on a SCSI disk. These suggestions are more general -in nature and lower level than those discussed in the BadBlockHowTo.txt -document. As always, there is no substitute for regular backups, even -high number RAIDs (e.g. 60) won't help when the user accidentally deletes -a directory. - - -Note A: Detecting and fixing an error with ECC "on the fly" and not going - the further step and reassigning the block in question may explain - why some disks have large numbers in their read error counter log. - Various worried users have reported large numbers in the "errors - corrected without substantial delay" counter field which is in the - "Errors corrected by ECC fast" column in the 'smartctl -l error' - output. - - -Douglas Gilbert -2006/9/17 diff --git a/www/FAQ.xml b/www/FAQ.xml deleted file mode 100644 index d46201b3c2802f63c5b4fcf591e70b4758639ca0..0000000000000000000000000000000000000000 --- a/www/FAQ.xml +++ /dev/null @@ -1,543 +0,0 @@ -<?xml version='1.0' encoding='ISO-8859-1'?> -<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" > - -<!-- -Layout borrowed from Doug's smartmontools_scsi.xml. The following text -is also from his file. - -This is DocBook XML that can be rendered into a single HTML page with a -command like 'xmlto html-nochunks <this_file_name>'. It can also be -rendered into multi-page HTML (drop the "-nochunks") or pdf, ps, txt, -etc. ---> - -<article id="index"> - <articleinfo> - <title>FAQ - Frequently Asked Questions</title> - <author> - <firstname>smartmontools</firstname> - <surname>developers</surname> - <affiliation> - <address> - <email>smartmontools-support@lists.sourceforge.net</email> - </address> - </affiliation> - </author> - <authorinitials>sd</authorinitials> - <pubdate>2003-09-24</pubdate> - - <revhistory> - <revision> - <revnumber>1.0</revnumber> - <date>2003-10-22</date> - <authorinitials>sd</authorinitials> - <revremark> - Moved from index.html to XML - </revremark> - </revision> - </revhistory> - - <copyright> - <year>2003</year> - <holder>Bruce Allen</holder> - </copyright> - - <legalnotice> - <para> - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU Free Documentation License, - Version 1.1 or any later version published by the Free Software - Foundation; with no Invariant Sections, with no Front-Cover Texts, - and with no Back-Cover Texts. - </para> - <para> - For an online copy of the license see - <ulink url="http://www.fsf.org/copyleft/fdl.html"> - <literal>http://www.fsf.org/copyleft/fdl.html</literal></ulink> . - </para> - - </legalnotice> - - <abstract> - <para> - FAQ - Frequently Asked Questions - </para> - </abstract> - </articleinfo> - -<!-- -<toc></toc> ---> - - -<sect1 id="a"> - -<title>What do I do if I have problems, or need support? Suppose I want -to become a developer, or suggest some new extensions?</title> - -<para>First, search the support mailing list archives to see if your -question has been answered. Instructions are in the following -paragraph. If you don't find an answer there, then please send an -e-mail to the smartmontools-support mailing list. Instructions are -available at <ulink url="http://lists.sourceforge.net/mailman/listinfo/smartmontools-support"> -<literal>http://lists.sourceforge.net/mailman/listinfo/smartmontools-support</literal></ulink> -. The list is moderated but you're not required to subscribe to it in -order to post your question.</para> - -<para>To search the archives, first go to <ulink url="http://sourceforge.net/mailarchive/forum.php?forum=smartmontools-support"> -<literal>http://sourceforge.net/mailarchive/forum.php?forum=smartmontools-support</literal></ulink> -. In the top left corner you will see a search box: use <emphasis -role="bold">Mailing List</emphasis> as the type of search. This tool -works very well.</para> - -<para>Note that from time to time SourceForge has mailing problems and -you'll get a message telling you that <emphasis role="italic">Either -your mailing list name was misspelled or your mailing list has not been -archived yet. If this list has just been created, please retry in 2-4 -hours</emphasis>. If this happens, you'll have to try again -later.</para> - -</sect1> - -<sect1 id="b"> - -<title>What are the future plans for smartmontools?</title> - -<para>My plan is that smartmontools-5.x will support ATA/ATAPI-5 disks. -Eventually, we'll do smartmontools-6.x to support ATA/ATAPI-6 disks, -smartmontools-7.x for the ATA/ATAPI-7 standard, and so on. The "x" will -denote revision level, as bugs get found and fixed, and as enhancements -get added. If it's possible to maintain backwards compatibility, that -would be nice, but I don't know if it will be possible or -practical.</para> - -</sect1> - -<sect1 id="c"> - -<title>Why are you doing this?</title> - -<para>My research group at U. Wisconsin - Milwaukee runs a beowulf -cluster - <ulink url="http://www.lsc-group.phys.uwm.edu/beowulf/medusa/"> -<literal>http://www.lsc-group.phys.uwm.edu/beowulf/medusa/</literal></ulink> -- with 600 ATA-5 and -6 disks (300 IBM and 300 Maxtor). We have more -than 50 TB of data stored on the system. I also help out with a cluster -- <ulink url="http://pandora.aei.mpg.de/merlin/"> -<literal>http://pandora.aei.mpg.de/merlin/</literal></ulink> - at the -Albert Einstein Institute that has another 300 IBM ATA-6 disks (36 TB -total). It's nice to have advanced warning when a disk is going to -fail.</para> - -</sect1> - -<sect1 id="d"> - -<title>I see some strange output from smartctl. What does it -mean?</title> - -<para>The raw S.M.A.R.T. attributes (temperature, power-on lifetime, and -so on) are stored in vendor-specific structures. Sometime these are -strange. Hitachi disks (at least some of them) store power-on lifetime -in minutes, rather than hours (see next question below). IBM disks (at -least some of them) have three temperatures stored in the raw structure, -not just one. And so on. If you find strange output, or unknown -attributes, please send an e-mail to the mailing list and we'll help you -try and figure it out.</para> - -</sect1> - -<sect1 id="e"> - -<title>What Kernel Version is needed? (Linux)</title> - -<para>Kernel versions 2.4.0 or later should work. We recommend the -latest 2.4 kernel.</para> - -<para>Vanilla kernel.org 2.2.X kernels do not support the -HDIO_DRIVE_TASK ioctl(), which is needed for the ATA drive to execute -the ATA SMART RETURN STATUS command. So these kernels will not -work.</para> - -<para>Vendor-supplied 2.2.X kernels, and vanilla 2.2.X kernels patched -with Andre Hedrick's IDE patches - <ulink url="http://www.funet.fi/pub/linux/kernel/people/hedrick/ide-2.2.20/"> -<literal>http://www.funet.fi/pub/linux/kernel/people/hedrick/ide-2.2.20/</literal></ulink> -(also available from your local kernel.org mirror, not updated for -2.2.21 or later, and probably still containing a few bugs) may support -the needed ioctl().</para> - -<para>If the configuration option CONFIG_IDE_TASK_IOCTL exists in your -2.2.X kernel source code tree, then your 2.2.X kernel will probably -support smartmontools. Note that this kernel configuration option does -<emphasis role="italic">not</emphasis> need to be enabled. Its presence -merely indicates that the required HDIO_DRIVE_TASK ioctl() is -supported.</para> - -</sect1> - -<sect1 id="f"> - -<title>What attributes does smartmontools not yet recognize?</title> - -<para>From Maxtor disks (99), (100), (101)</para> - -<para>If you can attach names/meanings to these attributes, please send -a note to the mailing list. If you have access to other SMART utilities -(especially manufacturer-specific ones, see below) and can send us -comparison output from smartctl and the other utility, that's especially -useful.</para> - -</sect1> - -<sect1 id="g"> - -<title>My Maxtor/Hitachi/Fujitsu disk is only a few days old, yet -smartctl reports its age (Attribute 9) as thousands of hours!</title> - -<para>On some recent disks, Maxtor has started to use Attribute 9 to -store the lifetime in minutes rather than hours. In this case, use the --m option (smartctl versions 5.0.X) or the --vendorattribute=9,minutes -(smartctl 5.1.X) option to correctly display hours and minutes.</para> - -<para>Some models of Fujitsu disks are known to use Attribute 9 for -lifetime in seconds. In that case, use the --vendorattribute=9,seconds -option to correctly display hours, minutes and seconds.</para> - -</sect1> - -<sect1 id="h"> - -<title>The power-on timer (Attribute 9 raw value) on my Maxtor disk acts -strange.</title> - -<para>There are three related problems with Maxtor's SMART -firmware:</para> - -<para><emphasis role="bold">1 - </emphasis>On some disks from 2001/2002, -the raw value of Attribute 9 (Power On Time) is <emphasis -role="italic">supposed</emphasis> to be minutes. But it advances at an -unpredictable rate, always more slowly than one count per minute. One -(unconfirmed) theory is that when the disk is in idle mode, the counter -stops advancing. This is only supposed to happen in standby -mode.</para> - -<para><emphasis role="bold">2 - </emphasis> In Maxtor disks that use the -raw value of Attribute 9 as a minutes counter, only two bytes (of the -six available) are used to store the raw value. So it resets to zero -once every 65536=2^16 minutes, or about once every 1092 hours. This is -fixed in all Maxtor disks manufactured after July 2003, where the raw -value was extended to four bytes.</para> - -<para><emphasis role="bold">3 - </emphasis> In Maxtor disks that use the -raw value of Attribute 9 as a minutes counter, the hour time-stamps in -the self-test and ATA error logs are calculated by right shifting 6 -bits. This is equivalent to dividing by 64 rather than by 60. As a -result, the hour time-stamps in these logs advance 7% more slowly than -they should. Thus, if you do self-tests once per week at the same time, -instead of the time-stamps being 168 hours apart, they are 157 hours -apart. This is also fixed in all Maxtor disks manufactured after July -2003.</para> - -</sect1> - -<sect1 id="i"> - -<title>Where can I find manufacturer-specific disk-testing -utilities?</title> - -<para>A good listing of such utilities can be found at <ulink url="http://www.benchmarkhq.ru/english.html?/be_hdd2.html"> -<literal>http://www.benchmarkhq.ru/english.html?/be_hdd2.html</literal></ulink> -. Unfortunately most of these are for MS operating systems, but most can -be run from an MS-DOS boot disk. Note: if you do run one of these -utilities, and it identifies the meanings of any SMART Attributes that -are not known to smartmontools, please report them to the mailing -list.</para> - -<para>These utilities have an important role to fill. If your disk has -bad sectors (for example, as revealed by running self-tests with -smartmontools) and the disk is not able to recover the data from those -sectors, then the disk will <emphasis role="italic">not</emphasis> -automatically reallocate those damaged sectors from its set of spare -sectors, because forcing the reallocation to take place may entail some -loss of data. Because the commands that force such reallocation are -<emphasis role="italic">Vendor Specific</emphasis>, most manufactuers -provide a utility for this purpose. It may cause data loss but can -repair damaged sectors (at least, until it runs out of replacement -sectors).</para> - -</sect1> - -<sect1 id="j"> - -<title>When I run <emphasis role="tt">smartd</emphasis>, -the SYSLOG <emphasis role="tt">/var/log/messages</emphasis> contains -messages like this:</title> - -<programlisting> -smartd: Reading Device /dev/sdv -modprobe: modprobe: Can't locate module block-major-65 -</programlisting> - -<para>This is because when <emphasis role="tt">smartd</emphasis> starts, -it looks for all ATA and SCSI devices to monitor (matching the pattern -<emphasis role="tt">/dev/hd[a-z]</emphasis> or <emphasis -role="tt">/dev/sd[a-z]</emphasis>). The log messages appear because -your system doesn't have most of these devices.</para> - -<para>Recent releases of smartd can use a configuration file <emphasis -role="tt">smartd.conf</emphasis> to specify which devices to include or -exclude from start-up search.</para> - -</sect1> - -<sect1 id="k"> - -<title>What's the story on IBM SMART disks?</title> - -<para>Apparently some of the older SMART firmware on IBM disks can -interfere with the regular operation of the disk. If you have this -problem, a firmware upgrade that fixes the problem is avaialable at -<ulink url="http://www.geocities.com/dtla_update/"> -<literal>http://www.geocities.com/dtla_update/</literal></ulink> -.</para> - -</sect1> - -<sect1 id="l"> - -<title>How can I check that the package hasn't been tampered -with?</title> - -<para>Since the <emphasis role="tt">smartmontools</emphasis> utilities -run as root, you might be concerned about something harmful being -embedded within them. Starting with release 5.19 of <emphasis -role="tt">smartmontools</emphasis>, the .rpm files and tarball have been -GPG signed. (The tarball's fingerprint is given in the SoureForge -Release Notes.) Please verify these using the GPG Signing Key available -at <ulink url="http://smartmontools.sourceforge.net/SmartmontoolsSigningKey.txt"> -<literal>http://smartmontools.sourceforge.net/SmartmontoolsSigningKey.txt</literal></ulink> -.</para> - -</sect1> - -<sect1 id="m"> - -<title>Is there a bootable standalone CD or floppy that contains -smartmontools?</title> - -<para>If you have a system that is showing signs of disk trouble (for -example, it's unbootable and the console is full of disk error messages) -it can be handy to have a version of smartmontools that can be run off -of a bootable CD or floppy to examine the disk's SMART data and run -self-tests. This is also useful if you want to run Captive Self-Tests -(the <emphasis role="bold"><emphasis role="tt">-C</emphasis></emphasis> -option of <emphasis role="bold"><emphasis -role="tt">smartctl</emphasis></emphasis> ) on disks that can not easily -be unmounted, such as those hosting the Operating System files. Or you -can use this to run <emphasis role="tt">smartctl</emphasis> on computers -that don't use Linux as the day-to-day Operating System.</para> - -<para>At present I am only aware of three such bootable disks:</para> - -<itemizedlist> -<listitem> -<para>LNX-BBC Bootable CD - <ulink url="http://www.lnx-bbc.org/"> -<literal>http://www.lnx-bbc.org/</literal></ulink></para> -</listitem> - -<listitem> -<para>Stresslinux Bootable CD - <ulink url="http://www.stresslinux.org/"> -<literal>http://www.stresslinux.org/</literal></ulink></para> -</listitem> - -<listitem> -<para>RIP (Recovery Is Possible) Bootable CD/Floppy - <ulink url="http://www.tux.org/pub/people/kent-robotti/looplinux/rip/"> -<literal>http://www.tux.org/pub/people/kent-robotti/looplinux/rip/</literal></ulink></para> -</listitem> -</itemizedlist> - -<para> Please let us know if there are others, and we'll add them to -this list.</para> - -</sect1> - -<sect1 id="n"> - -<title>Can I monitor ATA disks behind SCSI RAID controllers?</title> - -<para>From release 5.1-16, smartmontools supports 3ware SCSI RAID -controllers that use ATA disks internally. To pass commands through the -3ware controller, use the smartmontools <emphasis role="bold">-d -3ware,N</emphasis> option or Directive.</para> - -<para>In smartmontools release 5.1-16, the SMART HEALTH STATUS -(smartmontools <emphasis role="bold">-H</emphasis>) is not returned -correctly for 3ware devices. In this release, the ENABLE AUTOMATIC -OFFLINE and ENABLE ATTRIBUTE AUTOSAVE commands (smartmontools <emphasis -role="bold">-o on</emphasis> and <emphasis role="bold">-S on</emphasis>) -are <emphasis role="italic">disabled</emphasis> for 3ware devices, -because at the time 5.1-16 was released, the 3w-xxxx driver could not -pass these commands through to the ATA disks.</para> - -<para>Later smartmontools CVS code and releases <emphasis -role="italic">do</emphasis> correctly support <emphasis -role="italic">all</emphasis> of these commands. You may:</para> - -<itemizedlist> -<listitem> -<para>Use version <emphasis role="bold">1.02.00.037</emphasis> or -greater of the 3w-xxxx driver, or</para> -</listitem> - -<listitem> -<para>Patch earlier 3ware 3w-xxxx drivers with <ulink url="http://smartmontools.sourceforge.net/3w-xxxx.txt"> -<literal>http://smartmontools.sourceforge.net/3w-xxxx.txt</literal></ulink> -so that these commands reach the disks, or</para> -</listitem> - -<listitem> -<para>Use an <emphasis role="bold">unpatched</emphasis> earlier 3w-xxxx -driver (which won't pass these commands to the disks but will instead -print harmless warning messages to SYSLOG).</para> -</listitem> -</itemizedlist> - -<para>Since smartmontools 3ware support is new, please report positive -or negative experiences to the mailing list, particularly for 64-bit -and/or big-endian architectures.</para></sect1> - -<sect1 id="o"> - -<title>SCSI disks and tapes (TapeAlert)</title> - -<para>smartmontools for SCSI disks and tapes (including medium changers) -is discussed at <ulink url="http://smartmontools.sourceforge.net/smartmontools_scsi.html"> -<literal>http://smartmontools.sourceforge.net/smartmontools_scsi.html</literal></ulink> -.</para> - -</sect1> - -<sect1 id="p"> - -<title>FireWire, USB, and SATA disks/systems</title> - -<para>As for USB and FireWire (ieee1394) disks and tape drives, the news -isn't good. They appear to Linux as SCSI devices but their -implementations do not usually support those SCSI commands needed by -smartmontools. The ieee1394 consortium recently certified the first -external enclosure (containing a ATA disk and a protocol bridge) as -being compliant to the relevant standards. Such devices have already -been on the market for about 3 years and they tend to only support the -bare minimum of commands needed for device operation (i.e. S.M.A.R.T. -support is an unsupported extra).</para> - -<para>I'd be very grateful to find someone who could help me test the -smartmontools code on serial ATA (SATA) disks. They should appear as -normal ATA disks in Linux.</para> - -</sect1> - -<sect1 id="q"> - -<title>How does smartmontools differ from smartsuite?</title> - -<para>The smartsuite code was originally developed as a Senior Thesis by -Michael Cornwell at the Concurrent Systems Laboratory (now part of the -Storage Systems Research Center - <ulink url="http://ssrc.soe.ucsc.edu/"> -<literal>http://ssrc.soe.ucsc.edu/</literal></ulink>), Jack Baskin -School of Engineering, University of California, Santa Cruz. You can -find some information about the original smartsuite project here:</para> - -<itemizedlist> -<listitem> -<para>Press Release 1 - <ulink url="http://www.ucsc.edu/news_events/press_releases/archive/99-00/09-99/smart_software.htm"> -<literal>http://www.ucsc.edu/news_events/press_releases/archive/99-00/09-99/smart_software.htm</literal></ulink></para> -</listitem> - -<listitem> -<para>Press Release 2 - <ulink url="http://www.santa-cruz.com/archive/1999/September/22/local/stories/5local.htm"> -<literal>http://www.santa-cruz.com/archive/1999/September/22/local/stories/5local.htm</literal></ulink></para> -</listitem> - -<listitem> -<para>Press Release 3 - <ulink url="http://www.ucsc.edu/currents/99-00/09-27/smart.html"> -<literal>http://www.ucsc.edu/currents/99-00/09-27/smart.html</literal></ulink></para> -</listitem> -</itemizedlist> - -<para>smartmontools was derived directly from smartsuite. It differs -from smartsuite in that it supports the ATA/ATAPI-5 standard. So for -example <emphasis role="tt">smartctl</emphasis> from smartsuite has no -facility for printing the SMART self-test logs, and doesn't print -timestamp information in the most usable way.</para> - -<para>The <emphasis role="tt">smartctl</emphasis> utility in -smartmontools has added functionality for this (<emphasis role="tt">-q, --l selftest,-S, -T, -v and -m</emphasis> options), updated -documentation, and also fixes small technical bugs in smartsuite. [One -example: smartsuite does not actually use the ATA SMART RETURN STATUS -command to find out the health status of a disk. It instead tries to -infer this from the SMART Attribute values.] See <ulink url="http://smartmontools.cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?rev=HEAD&content-type=text/plain"> -<literal>http://smartmontools.cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?rev=HEAD&content-type=text/plain</literal></ulink> for a summary of what's been done.</para> - -<para>The <emphasis role="tt">smartd</emphasis> utility differs from the -smartsuite <emphasis role="tt">smartd</emphasis> in major ways. First, -it prints somewhat more informative error messages to the syslog. -Second, on startup it looks for a configuration file <emphasis -role="tt">smartd.conf</emphasis>, and if <emphasis -role="tt">smartd</emphasis> finds this file, it monitors the list of -devices therein, rather than querying all IDE and SCSI devices on your -system. (If the configuration file does not exist, then it does query -all IDE and SCSI devices.) Also, it's a well-behaved daemon and doesn't -leave open file descriptors and other detrius behind.</para> - -<para>In addition, the <emphasis role="tt">smartmontools</emphasis> -version of <emphasis role="tt">smartd</emphasis> can be instructed (via -Directives in the configuration file) to monitor for changes in a number -of different disk properties: the SMART status, failure or prefailure -attributes going below threshold, new errors appearing in the ATA Error -Log or the SMART Self-Test Log, and so on. <emphasis -role="tt">smartd</emphasis> can also send an e-mail warning or run a -user-specified executable if it detects a problem with the disk.</para> - -<para>The other principle difference is that smartmontools is an -OpenSource development project, meaning that we keep the files in CVS, -and that other developers who wish to contribute can commit changes to -the archive. If you would like to contribute, please write to the -mailing-list.</para> - -<para>But the bottom line is that the code in smartmontools is derived -directly from smartsuite and is similar. The smartsuite package can be -found at <ulink url="http://sourceforge.net/projects/smartsuite/"> -<literal>http://sourceforge.net/projects/smartsuite/</literal></ulink> -.</para></sect1> - -<sect1 id="r"> - -<title>Does it work on Windows?</title> - -<para>Currently not, but we consider Cygwin - <ulink url="http://www.cygwin.com/"> -<literal>http://www.cygwin.com/</literal></ulink> - the way to go, -where CVS compiles almost out of the box but still lacks any specific -code to make it work. Write to the mailing list if you're interested in -porting it. Someone already sent some S.M.A.R.T. code for Windows, -which may be of use. Porting to other platforms may be easier as well -now that any Linux specific code (like linux/hdreg.h) has been removed, -and more will be done soon in that direction.</para> - -<para>A Cygwin port would probably only require and additional DLL, -cygwin1.dll, to run on plain Windows.</para></sect1> - -<sect1 id="s"> - -<title>Why has the versioning scheme changed?</title> - -<para>With the move to GNU Autoconf and GNU Automake it changed from -5.X-Y (where X and Y are one or more numbers) to 5.Y. This had to be -done because the -Y extension is used by distributions to almost always -denote a new build of the same version. So, the first version with that -change will be 5.19 and not 5.1-19.</para> - -</sect1> - -</article> diff --git a/www/Makefile b/www/Makefile deleted file mode 100644 index 7af3daaef2868613ea340bf144be16aa79138be0..0000000000000000000000000000000000000000 --- a/www/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Manufactures HTML file from XML file. Note: do NOT edit the HTML -# file, only the XML file. - -all: smartmontools_scsi.html - -smartmontools_scsi.html: smartmontools_scsi.xml - xmlto html-nochunks smartmontools_scsi.xml - -upload: smartmontools_scsi.html index.html - scp smartmontools_scsi.html ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs - scp 3w-xxxx.txt ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs - scp index.html ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs - scp examples/*.html ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs/examples - scp examples/*.txt ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs/examples - scp BadBlockHowTo.txt ballen4705@smartmontools.sourceforge.net:/home/groups/s/sm/smartmontools/htdocs diff --git a/www/SmartmontoolsSigningKey.txt b/www/SmartmontoolsSigningKey.txt deleted file mode 100644 index 0ac19356f748d2b5f81b33962e588e8af28fb16d..0000000000000000000000000000000000000000 --- a/www/SmartmontoolsSigningKey.txt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.0.7 (GNU/Linux) - -mQGiBD9XAXIRBACgLEphBmhUKWE1mRKzjkq/8vZHtJsVUiFivcbxaSLa9jBbJoZV -sQk5fdcleVE6CcuodMetVE6Gl8uM4W4iymp0i35lwefdgmUzJYmza1ZD7Uk0x4zv -tKi9xZ9Hc+yrf4SHRwLTZxuUyLf9TURwGXfLd2bxP1USYJVL4vOYoiBwBwCgq/w3 -EyO5PhlGp5rfE+WIoyy9GHcEAIYP3ctigHu6tnSobIGA77BFOv+v7DbXRjbKhEz1 -s4lPGQQP5h2t4VFRiy9RlD4GlEXD51cRFMmtFk4cBbOuONQbNOJFQQ/9JpVBU6/O -CZrVMUqDnQMd2mdUU8pxM7cguaw5cPFxqqX5dkW1JikGrlG1QsH5UxuYhdadO+al -1fTnA/9RMRscXd6aAdN66pZ9mGoqIxVUO6N+icXO7DP+ArIt7gu4GLgvvARlgMiS -neRV4g7mvLm41kBDEv5gug+h2ha5ZI+P51oSRYs8yA5fVtl0GA2YRA2QercALv6C -CtAvnFXWFqSeyW1ESdd2zFKBjhXlBVkmujOyKDS6LXRpZjwJXrRWU21hcnRtb250 -b29scyBTaWduaW5nIEtleSAodGhyb3VnaCAyMDA0KSA8c21hcnRtb250b29scy1z -dXBwb3J0QGxpc3RzLnNvdXJjZWZvcmdlLm5ldD6IZAQTEQIAJAUCP1cBcgIbAwUJ -AnjQAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAjPh86m7GaIlTQAJ9IsqaxHbqX -BSd+xfJwUAZyKWqyLwCgoa2rMvHAHa/Pvpt4E0i0xF9NW3yITAQTEQIADAUCP1cB -+QWDAnjPeQAKCRARGZEEN/UVgWa7AJ95rEDeEw9G3uqAZO7L9u650QPX6wCgoHJ9 -Hq/akQJZhOgSKyrgVEyAc8S5AQ0EP1cBeBAEAMLXV8RwVFDs5EvfkQNwasoKNS+N -PvvhO/weED188XklZ3QTiToEp2b4JFaoUkTk1l2f9JxagDPaVHR6XU8H740x25LZ -gC6XObKMBxqJ9CrBLGcMt/bCugquDu18KFlL3Y1rq9uBxq9JS6CJthUzeaaFdFQS -V7tF2+3hBz/Okqo7AAMFA/9l0YcKnTKDy8jdOtNjky1NEbaF1LjyRc4laT6N4O6q -Xg2oGD6MgS7zSK/ORcT3B0T5kpTo6gnKLTYDxEAvpNjrOjlwn08Jtm3xrQZLId/W -RAo+Qqn5Or+sugZZpQPHrGGB9TTc0AL3MfCbK4mlssVhS0SAq35E/osCLQcor7Sx -sohPBBgRAgAPBQI/VwF4AhsMBQkCeNAAAAoJECM+HzqbsZoi4WUAn2IQhEtHY/48 -4rljbro8yUwYlrXzAJ44VfTwmjLlI9aoYdRW/cTtZ0tPgw== -=f2kQ ------END PGP PUBLIC KEY BLOCK----- diff --git a/www/SmartmontoolsSigningKey_2005.txt b/www/SmartmontoolsSigningKey_2005.txt deleted file mode 100644 index 82e20be2f4a9d09b3112588550505fbc24824b2b..0000000000000000000000000000000000000000 --- a/www/SmartmontoolsSigningKey_2005.txt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.2.3 (GNU/Linux) - -mQGiBEJl08ARBAC8HLnkjodFpCGWxIquBNqVNLbcJIi+4KuhD0o7Jn2njif31ZMu -useJAoKmlYD7wEShJemkTfdj76QEtv3DGAKXo3ylI50FzZwR8SflHvdM7D3ewk29 -hDGM7QqvezwG7nToz1IqmT6t6LUtp7FtqeLlJs+Ly9jzGIDlB1H3LIFsJwCgtkAY -VCv6cLuGnc2TE+elAi98KqUEAIORBntWHPtGb4uMXo0gAJzsu0m66OWosCNwT6Hb -EQZQc3fiW80Ph1tr9MHh24nyA4PaciKygrXt3GvIxlGocRB1YIk/BeDiGckMNn3L -/3R1KFPPbhCsuGcjOZ8hcwn/rVtSAYYptnotEftwdJc1iDtXogRXGs0RINqfL/NL -855SBACBUHQcQTHkj3nmciyh2gVBbbHC/NVDVbTxPLLH5MbKYd9cm/xAL+AztIsE -MgICJtSkYqg2QIN8VPRFMILdBa8DNYT03bVhlERf09AB9PrB9507QrEMFIoQjYF6 -41R74xj++3VNtbLSn2HFcY7E7hWmW9arYGka24jbrwCZnPqyvbRWU21hcnRtb250 -b29scyBTaWduaW5nIEtleSAodGhyb3VnaCAyMDA2KSA8c21hcnRtb250b29scy1z -dXBwb3J0QGxpc3RzLnNvdXJjZWZvcmdlLm5ldD6IZAQTEQIAJAUCQmXTwAIbAwUJ -Az6RAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAildgUhBq66DfgAJ9Ad+ywl9EA -TxbZarsWWb20AgsFJgCfRUjmcUMOoM2o6PJqxcn2rauOjrCITAQTEQIADAUCQmXU -FgWDAz6QqgAKCRARGZEEN/UVgeFJAJoDVs8e1QCs6WMljf0hXp5FEr/xIgCfUnbX -NKDft/y8GcktEJZVJrQ/Bcq5AQ0EQmXTwxAEAKtS+tILqIraz2vIpkI468o0ou+m -XsPg0Vjv1FX4iIotg9iqOaURITBJW1uyOWzwgIKQr9IV3KDjiViK/OeebGk++fA6 -F23k7UAePInOXp0f01xHwr2dkhoV07AFCjFrarsK5/aXz01lgg9vpgxuQ6Z78ci8 -MkX+NmcCxo+3Ilk/AAMFBACpXVsbcf4xxQd7IXc7bcwkAdnmB8NsTEm1L3fwk3Em -nW1UcQWzov2iJIBWOIbZDfRgTQC47cSIpE42ez6t8QFaG7zROBEHqBw4q1HA7sPT -3DU8mOMvU5hbitTE8N3J9vMxcCyzqKfSfYDnLsMNldV/QAJGhyX4LmTamx5p5Ks/ -+YhPBBgRAgAPBQJCZdPDAhsMBQkDPpEAAAoJECKV2BSEGrroVn8AnAhM11Nj07Jo -di8zFUMf0A962Yg7AJ4ptOiGVlWiLhJiSIfSWROPwweAlQ== -=5DeE ------END PGP PUBLIC KEY BLOCK----- diff --git a/www/cvs-script b/www/cvs-script deleted file mode 100755 index 52cbaea9b36a4f7048b65a3ea20700e378de7c47..0000000000000000000000000000000000000000 --- a/www/cvs-script +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# execute this script in the current shell, using for example -# . cvs_script -unset CVS_SERVER -export CVS_RSH=ssh -export CVSROOT=:ext:ballen4705@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools diff --git a/www/examples/FUJITSU1.txt b/www/examples/FUJITSU1.txt deleted file mode 100644 index b10abf7a3b232514e1133510003645544d3ad932..0000000000000000000000000000000000000000 --- a/www/examples/FUJITSU1.txt +++ /dev/null @@ -1,80 +0,0 @@ -[root/]# smartctl -v 9,seconds -v 200,writeerrorcount /dev/hda - -smartctl version 5.1-18 Copyright (C) 2002-3 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: FUJITSU MHR2040AT -Serial Number: NJ41T291391J -Firmware Version: 40BA -Device is: Not in smartctl database [for details use: -P showall] -ATA Version is: 6 -ATA Standard is: ATA/ATAPI-6 T13 1410D revision 1 -Local Time is: Thu Sep 4 22:18:48 2003 CEST -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. - Auto Off-line Data Collection: Disabled. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 468) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 60) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000f 100 100 046 Pre-fail Always - 52685626284 - 2 Throughput_Performance 0x0005 100 100 020 Pre-fail Offline - 0 - 3 Spin_Up_Time 0x0003 093 093 025 Pre-fail Always - 24065 - 4 Start_Stop_Count 0x0032 100 100 000 Old_age Always - 160 - 5 Reallocated_Sector_Ct 0x0033 099 099 024 Pre-fail Always - 31 - 7 Seek_Error_Rate 0x000f 100 100 047 Pre-fail Always - 131071 - 8 Seek_Time_Performance 0x0005 100 100 019 Pre-fail Offline - 0 - 9 Power_On_Seconds 0x0032 092 092 000 Old_age Always - 1335h+10m+34s - 10 Spin_Retry_Count 0x0013 100 100 020 Pre-fail Always - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age Always - 150 -192 Power-Off_Retract_Count 0x0032 099 099 000 Old_age Always - 7 -193 Load_Cycle_Count 0x0032 074 074 000 Old_age Always - 95890 -194 Temperature_Celsius 0x0022 090 100 000 Old_age Always - 57 -195 Hardware_ECC_Recovered 0x001a 100 100 000 Old_age Always - 10141348 -196 Reallocated_Event_Count 0x0032 099 099 000 Old_age Always - 30 -197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - 0 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0 -199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age Always - 0 -200 Write_Error_Count 0x000f 036 033 060 Pre-fail Always FAILING_NOW 2883583 -203 Run_Out_Cancel 0x0002 091 091 000 Old_age Always - 2589872160305 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log structure revision number 1 -No self-tests have been logged - - diff --git a/www/examples/FUJITSU_MHR2020AT.txt b/www/examples/FUJITSU_MHR2020AT.txt deleted file mode 100644 index 4f8dc82b9c5b6858e849cf8e682dbe855fdb0c53..0000000000000000000000000000000000000000 --- a/www/examples/FUJITSU_MHR2020AT.txt +++ /dev/null @@ -1,158 +0,0 @@ -smartctl version 5.32 Copyright (C) 2002-4 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: FUJITSU MHR2020AT -Serial Number: NJ13T2215TUH -Firmware Version: 30B8 -Device is: Not in smartctl database [for details use: -P showall] -ATA Version is: 6 -ATA Standard is: ATA/ATAPI-6 T13 1410D revision 1 -Local Time is: Sun Aug 15 13:21:33 2004 MEST -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Offline data collection status: (0x00) Offline data collection activity - was never started. - Auto Offline Data Collection: Disabled. -Self-test execution status: ( 105) The previous self-test completed having - the servo (and/or seek) element of the - test failed. -Total time to complete Offline -data collection: ( 234) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 30) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000f 007 001 046 Pre-fail Always FAILING_NOW 154618843423 - 2 Throughput_Performance 0x0005 100 100 020 Pre-fail Offline - 145 - 3 Spin_Up_Time 0x0003 094 083 025 Pre-fail Always - 24321 - 4 Start_Stop_Count 0x0032 097 097 000 Old_age Always - 1887 - 5 Reallocated_Sector_Ct 0x0033 099 099 024 Pre-fail Always - 1 - 7 Seek_Error_Rate 0x000f 100 100 047 Pre-fail Always - 458751 - 8 Seek_Time_Performance 0x0005 100 100 019 Pre-fail Offline - 0 - 9 Power_On_Seconds 0x0032 088 088 000 Old_age Always - 1819h+16m+52s - 10 Spin_Retry_Count 0x0013 100 100 020 Pre-fail Always - 0 - 12 Power_Cycle_Count 0x0032 091 091 000 Old_age Always - 1467 -192 Power-Off_Retract_Count 0x0032 099 099 000 Old_age Always - 44 -193 Load_Cycle_Count 0x0032 086 086 000 Old_age Always - 49810 -194 Temperature_Celsius 0x0022 100 100 000 Old_age Always - 32 -195 Hardware_ECC_Recovered 0x001a 100 100 000 Old_age Always - 1221 -196 Reallocated_Event_Count 0x0032 099 099 000 Old_age Always - 1 -197 Current_Pending_Sector 0x0012 001 001 000 Old_age Always - 10 -198 Offline_Uncorrectable 0x0010 092 092 000 Old_age Offline - 17 -199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age Always - 0 -200 Multi_Zone_Error_Rate 0x000f 100 100 060 Pre-fail Always - 393215 -203 Run_Out_Cancel 0x0002 100 100 000 Old_age Always - 429515210380 - -SMART Error Log Version: 1 -ATA Error Count: 6861 (device log contains only the most recent five errors) - CR = Command Register [HEX] - FR = Features Register [HEX] - SC = Sector Count Register [HEX] - SN = Sector Number Register [HEX] - CL = Cylinder Low Register [HEX] - CH = Cylinder High Register [HEX] - DH = Device/Head Register [HEX] - DC = Device Command Register [HEX] - ER = Error register [HEX] - ST = Status register [HEX] -Powered_Up_Time is measured from power on, and printed as -DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes, -SS=sec, and sss=millisec. It "wraps" after 49.710 days. - -Error 6861 occurred at disk power-on lifetime: 1818 hours (75 days + 18 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 8b 7d 16 51 f0 Error: UNC 139 sectors at LBA = 0x0051167d = 5314173 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 90 78 16 51 f0 00 08:22:32.857 READ DMA EXT - -Error 6860 occurred at disk power-on lifetime: 1818 hours (75 days + 18 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 8b 7d 16 51 f0 Error: UNC 139 sectors at LBA = 0x0051167d = 5314173 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 98 70 16 51 f0 00 08:22:27.700 READ DMA EXT - -Error 6859 occurred at disk power-on lifetime: 1818 hours (75 days + 18 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 8b 7d 16 51 f0 Error: UNC 139 sectors at LBA = 0x0051167d = 5314173 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 a0 68 16 51 f0 00 08:22:22.558 READ DMA EXT - -Error 6858 occurred at disk power-on lifetime: 1818 hours (75 days + 18 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 8b 7d 16 51 f0 Error: UNC 139 sectors at LBA = 0x0051167d = 5314173 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 a8 60 16 51 f0 00 08:22:17.458 READ DMA EXT - -Error 6857 occurred at disk power-on lifetime: 1818 hours (75 days + 18 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 8b 7d 16 51 f0 Error: UNC 139 sectors at LBA = 0x0051167d = 5314173 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 b0 58 16 51 f0 00 08:22:12.558 READ DMA EXT - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short offline Completed: servo/seek failure 90% 1819 - -# 2 Extended offline Completed: servo/seek failure 90% 1816 - - -Device does not support Selective Self Tests/Logging diff --git a/www/examples/HITACHI_DK23AA-12B.txt b/www/examples/HITACHI_DK23AA-12B.txt deleted file mode 100644 index 6f9b62e60cff0d5751ceceeae48873b665319356..0000000000000000000000000000000000000000 --- a/www/examples/HITACHI_DK23AA-12B.txt +++ /dev/null @@ -1,173 +0,0 @@ -smartctl version 5.30 Copyright (C) 2002-4 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: HITACHI_DK23AA-12B -Serial Number: xxxxxx -Firmware Version: 00XDA0B6 -Device is: Not in smartctl database [for details use: -P showall] -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -Local Time is: Sat Apr 24 17:19:58 2004 EST -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Offline data collection status: (0x00) Offline data collection activity was - never started. - Auto Offline Data Collection: Disabled. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete Offline -data collection: (1110) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 1) minutes. -Extended self-test routine -recommended polling time: ( 19) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000d 099 099 050 Pre-fail Offline - 68719476757 - 4 Start_Stop_Count 0x0032 097 097 050 Old_age Always - 3259 - 5 Reallocated_Sector_Ct 0x0033 001 001 010 Pre-fail Always FAILING_NOW 1876 - 7 Seek_Error_Rate 0x000b 100 100 050 Pre-fail Always - 760 - 9 Power_On_Hours 0x0032 090 090 060 Old_age Always - 21783 - 10 Spin_Retry_Count 0x0013 100 100 050 Pre-fail Always - 0 -196 Reallocated_Event_Count 0x0032 001 001 001 Old_age Always FAILING_NOW 254 -197 Current_Pending_Sector 0x0032 097 093 001 Old_age Always - 3 -198 Offline_Uncorrectable 0x0010 100 100 001 Old_age Offline - 0 -199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age Always - 0 -221 G-Sense_Error_Rate 0x000a 100 100 050 Old_age Always - 42 -223 Load_Retry_Count 0x0012 100 100 050 Old_age Always - 149 -225 Load_Cycle_Count 0x0032 050 050 050 Old_age Always FAILING_NOW 5110222858734 -250 Read_Error_Retry_Rate 0x000a 090 001 050 Old_age Always In_the_past 103 - -SMART Error Log Version: 1 -ATA Error Count: 1151 (device log contains only the most recent five errors) - CR = Command Register [HEX] - FR = Features Register [HEX] - SC = Sector Count Register [HEX] - SN = Sector Number Register [HEX] - CL = Cylinder Low Register [HEX] - CH = Cylinder High Register [HEX] - DH = Device/Head Register [HEX] - DC = Device Command Register [HEX] - ER = Error register [HEX] - ST = Status register [HEX] -Timestamp = decimal seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 1151 occurred at disk power-on lifetime: 5445 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 01 51 01 01 4f c2 e0 Error: obs - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - b0 d5 01 01 4f c2 e0 00 46273.155 SMART READ LOG - b0 d1 01 01 4f c2 e0 00 46273.138 SMART READ ATTRIBUTE THRESHOLDS [OBS-4] - b0 d0 01 00 4f c2 e0 00 46273.009 SMART READ DATA - b0 da 00 00 4f c2 a0 00 46272.881 SMART RETURN STATUS - b0 da 00 00 4f c2 e0 00 46272.760 SMART RETURN STATUS - -Error 1150 occurred at disk power-on lifetime: 5354 hours - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 01 51 01 56 36 54 e1 Error: AMNF 1 sectors at LBA = 0x01543656 = 22296150 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 80 d7 35 54 e1 00 1517.332 READ DMA - c8 00 80 57 35 54 e1 00 1517.329 READ DMA - ca 00 08 67 1d cb e0 00 1516.014 WRITE DMA - ca 00 30 37 1d cb e0 00 1515.992 WRITE DMA - ca 00 10 2f e0 ca e0 00 1515.874 WRITE DMA - -Error 1149 occurred at disk power-on lifetime: 5352 hours - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 01 51 0c bb c5 57 e1 Error: AMNF 12 sectors at LBA = 0x0157c5bb = 22529467 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 20 a7 c5 57 e1 00 380.146 READ DMA - 10 00 3f 00 00 00 e0 00 380.146 RECALIBRATE [OBS-4] - c8 00 20 a7 c5 57 e1 00 377.932 READ DMA - c8 00 20 a7 c5 57 e1 00 373.729 READ DMA - c8 00 80 6f 41 5f e1 00 371.776 READ DMA - -Error 1148 occurred at disk power-on lifetime: 5352 hours - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 01 51 0c bb c5 57 e1 Error: AMNF 12 sectors at LBA = 0x0157c5bb = 22529467 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 20 a7 c5 57 e1 00 377.932 READ DMA - c8 00 20 a7 c5 57 e1 00 373.729 READ DMA - c8 00 80 6f 41 5f e1 00 371.776 READ DMA - c8 00 80 ef 40 5f e1 00 371.742 READ DMA - ca 00 08 57 1f cb e0 00 371.291 WRITE DMA - -Error 1147 occurred at disk power-on lifetime: 5352 hours - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 01 51 0c bb c5 57 e1 Error: AMNF 12 sectors at LBA = 0x0157c5bb = 22529467 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 20 a7 c5 57 e1 00 373.729 READ DMA - c8 00 80 6f 41 5f e1 00 371.776 READ DMA - c8 00 80 ef 40 5f e1 00 371.742 READ DMA - ca 00 08 57 1f cb e0 00 371.291 WRITE DMA - ca 00 10 47 1f cb e0 00 371.262 WRITE DMA - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short offline Completed without error 00% 5445 - -# 2 Short offline Completed without error 00% 5445 - -# 3 Short offline Aborted by host 90% 5445 - -# 4 Short offline Completed without error 00% 5445 - -# 5 Short offline Completed without error 00% 5445 - - diff --git a/www/examples/HITACHI_DK23BA-20-0.txt b/www/examples/HITACHI_DK23BA-20-0.txt deleted file mode 100644 index 13e9cb0122e0c6e4e072ddfc91efba9d9a4a3d19..0000000000000000000000000000000000000000 --- a/www/examples/HITACHI_DK23BA-20-0.txt +++ /dev/null @@ -1,163 +0,0 @@ -[root@ballen www]# /usr/sbin/smartctl -am /dev/hda - -smartctl version 5.0-25 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: HITACHI_DK23BA-20 -Serial Number: 12H7M8 -Firmware Version: 00E0A0D2 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED -See vendor-specific Attribute list for marginal Attributes. - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: (1530) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 26) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000d 100 083 050 Pre-fail - 677 - 3 Spin_Up_Time 0x0007 100 100 050 Pre-fail - 0 - 4 Start_Stop_Count 0x0032 100 100 050 Old_age - 249 - 5 Reallocated_Sector_Ct 0x0033 099 099 010 Pre-fail - 30 - 7 Seek_Error_Rate 0x000f 100 100 050 Pre-fail - 319 - 9 Power_On_Hours 0x0032 099 099 060 Old_age - 701 h + 42 m - 10 Spin_Retry_Count 0x0013 100 100 050 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 050 Old_age - 249 -192 Power-Off_Retract_Count 0x0032 100 100 050 Old_age - 15 -195 Hardware_ECC_Recovered 0x001a 100 001 050 Old_age In_the_past 559 -196 Reallocated_Event_Count 0x0032 097 097 001 Old_age - 30 -197 Current_Pending_Sector 0x0032 095 095 001 Old_age - 5 -198 Offline_Uncorrectable 0x0010 095 095 001 Old_age - 31 -199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age - 0 -221 G-Sense_Error_Rate 0x000a 100 100 050 Old_age - 0 -223 Load_Retry_Count 0x0012 100 100 050 Old_age - 0 -225 Load_Cycle_Count 0x0032 095 095 050 Old_age - 18446744072753281791 -230 Unknown_Attribute 0x0032 100 100 060 Old_age - 18484 -250 Unknown_Attribute 0x000a 100 070 050 Old_age - 601 - -SMART Error Log Version: 1 -ATA Error Count: 9 (device log contains only the most recent five errors) - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 1 occurred at disk power-on lifetime: 458 hours -When the command that caused the error occurred, the device was active or idle. -After command completion occurred, registers were: -ER:40 SC:01 SN:15 CL:be CH:2e D/H:e0 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 00 01 15 be 2e e0 c8 831.599 - 00 00 01 14 be 2e e0 c8 831.594 - 00 00 01 13 be 2e e0 c8 831.594 - 00 00 01 12 be 2e e0 c8 831.594 - 00 00 01 11 be 2e e0 c8 831.594 - -Error 2 occurred at disk power-on lifetime: 458 hours -When the command that caused the error occurred, the device was active or idle. -After command completion occurred, registers were: -ER:40 SC:45 SN:15 CL:be CH:2e D/H:e0 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 00 80 da bd 2e e0 c8 829.680 - 00 00 80 5a bd 2e e0 c8 829.677 - 00 00 80 da bc 2e e0 c8 829.673 - 00 00 80 5a bc 2e e0 c8 829.671 - 00 00 01 58 bc 2e e0 c8 829.671 - -Error 3 occurred at disk power-on lifetime: 458 hours -When the command that caused the error occurred, the device was active or idle. -After command completion occurred, registers were: -ER:40 SC:01 SN:47 CL:bc CH:2e D/H:e0 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 00 01 47 bc 2e e0 c8 826.962 - 00 00 01 46 bc 2e e0 c8 826.961 - 00 00 01 45 bc 2e e0 c8 826.961 - 00 00 01 44 bc 2e e0 c8 826.961 - 00 00 01 43 bc 2e e0 c8 826.961 - -Error 4 occurred at disk power-on lifetime: 458 hours -When the command that caused the error occurred, the device was active or idle. -After command completion occurred, registers were: -ER:40 SC:13 SN:47 CL:bc CH:2e D/H:e0 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 00 80 da bb 2e e0 c8 825.038 - 00 00 80 5a bb 2e e0 c8 825.033 - 00 00 80 da ba 2e e0 c8 825.030 - 00 00 80 5a ba 2e e0 c8 824.940 - 00 00 80 da b9 2e e0 c8 824.937 - -Error 5 occurred at disk power-on lifetime: 458 hours -When the command that caused the error occurred, the device was active or idle. -After command completion occurred, registers were: -ER:40 SC:01 SN:85 CL:19 CH:2c D/H:e0 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 00 01 85 19 2c e0 c8 816.487 - 00 00 01 84 19 2c e0 c8 816.487 - 00 00 01 83 19 2c e0 c8 816.486 - 00 00 01 82 19 2c e0 c8 816.486 - 00 00 01 81 19 2c e0 c8 816.486 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short off-line Completed 00% 691 -# 2 Extended off-line Completed: read failure 40% 661 0x002c1985 -# 3 Extended off-line Completed: read failure 40% 661 0x002c1985 -# 4 Short off-line Completed 00% 660 -# 5 Extended off-line Completed: read failure 40% 658 0x002c1985 -# 6 Short off-line Completed 00% 658 -# 7 Short off-line Completed 00% 658 -# 8 Extended off-line Completed: read failure 40% 658 0x002c1985 -# 9 Extended off-line Completed: read failure 40% 657 0x002c1985 -#10 Short off-line Completed 00% 647 -#11 Short off-line Completed 00% 587 -#12 Short off-line Completed 00% 583 -#13 Short off-line Completed 00% 551 -#14 Short captive Interrupted (host reset) 40% 551 -#15 Short off-line Completed 00% 551 -#16 Extended off-line Completed: read failure 40% 550 0x002c1985 -#17 Extended off-line Aborted by host 50% 550 -#18 Short off-line Completed 00% 550 -#19 Short off-line Completed 00% 537 -#20 Extended off-line Completed: read failure 40% 536 0x002c1985 -#21 Short off-line Completed 00% 536 diff --git a/www/examples/IC35L120AVV207-0.txt b/www/examples/IC35L120AVV207-0.txt deleted file mode 100644 index 4e43e8c2d0976622c07e0e0cddc4507716f5cca6..0000000000000000000000000000000000000000 --- a/www/examples/IC35L120AVV207-0.txt +++ /dev/null @@ -1,67 +0,0 @@ -# smartctl -a /dev/hda -smartctl version 5.0-45 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: IC35L120AVV207-0 -Serial Number: VNVD02G4G4BDEG -Firmware Version: V24OA63A -ATA Version is: 6 -ATA Standard is: ATA/ATAPI-6 T13 1410D revision 3a -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: (2855) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 1) minutes. -Extended self-test routine -recommended polling time: ( 48) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 100 100 060 Pre-fail - 0 - 2 Throughput_Performance 0x0005 100 100 050 Pre-fail - 0 - 3 Spin_Up_Time 0x0007 102 102 024 Pre-fail - 16974103 - 4 Start_Stop_Count 0x0012 100 100 000 Old_age - 14 - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 067 Pre-fail - 0 - 8 Seek_Time_Performance 0x0005 100 100 020 Pre-fail - 0 - 9 Power_On_Hours 0x0012 100 100 000 Old_age - 242 - 10 Spin_Retry_Count 0x0013 100 100 060 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age - 14 -192 Power-Off_Retract_Count 0x0032 100 100 050 Old_age - 24 -193 Load_Cycle_Count 0x0012 100 100 050 Old_age - 24 -194 Temperature_Celsius 0x0002 203 203 000 Old_age - 27 (Lifetime Min/Max 20/37) -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 54 diff --git a/www/examples/IC35L120AVV207-1.txt b/www/examples/IC35L120AVV207-1.txt deleted file mode 100644 index 48a69910371771d2fdf0b10b19e3ce0ea3d4aff6..0000000000000000000000000000000000000000 --- a/www/examples/IC35L120AVV207-1.txt +++ /dev/null @@ -1,191 +0,0 @@ -smartctl version 5.31 Copyright (C) 2002-4 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: IC35L120AVV207-1 -Serial Number: VNVD09G4H3HPMT -Firmware Version: V24OA66A -Device is: In smartctl database [for details use: -P show] -ATA Version is: 6 -ATA Standard is: ATA/ATAPI-6 T13 1410D revision 3a -Local Time is: Tue Jun 15 23:38:56 2004 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Offline data collection status: (0x84) Offline data collection activity - was suspended by an interrupting command from host. - Auto Offline Data Collection: Enabled. -Self-test execution status: ( 121) The previous self-test completed having - the read element of the test failed. -Total time to complete Offline -data collection: (2855) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - General Purpose Logging supported. -Short self-test routine -recommended polling time: ( 1) minutes. -Extended self-test routine -recommended polling time: ( 48) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 089 089 060 Pre-fail Always - 1703986 - 2 Throughput_Performance 0x0005 015 015 050 Pre-fail Offline FAILING_NOW 5518 - 3 Spin_Up_Time 0x0007 100 100 024 Pre-fail Always - 278 - 4 Start_Stop_Count 0x0012 100 100 000 Old_age Always - 9 - 5 Reallocated_Sector_Ct 0x0033 091 091 005 Pre-fail Always - 277 - 7 Seek_Error_Rate 0x000b 100 100 067 Pre-fail Always - 0 - 8 Seek_Time_Performance 0x0005 123 123 020 Pre-fail Offline - 37 - 9 Power_On_Hours 0x0012 100 100 000 Old_age Always - 2759 - 10 Spin_Retry_Count 0x0013 100 100 060 Pre-fail Always - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age Always - 9 -192 Power-Off_Retract_Count 0x0032 100 100 050 Old_age Always - 45 -193 Load_Cycle_Count 0x0012 100 100 050 Old_age Always - 45 -194 Temperature_Celsius 0x0002 166 166 000 Old_age Always - 33 (Lifetime Min/Max 23/44) -196 Reallocated_Event_Count 0x0032 092 092 000 Old_age Always - 319 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age Always - 49 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age Offline - 20 -199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age Always - 0 - -SMART Error Log Version: 1 -ATA Error Count: 161 (device log contains only the most recent five errors) - CR = Command Register [HEX] - FR = Features Register [HEX] - SC = Sector Count Register [HEX] - SN = Sector Number Register [HEX] - CL = Cylinder Low Register [HEX] - CH = Cylinder High Register [HEX] - DH = Device/Head Register [HEX] - DC = Device Command Register [HEX] - ER = Error register [HEX] - ST = Status register [HEX] -Powered_Up_Time is measured from power on, and printed as -DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes, -SS=sec, and sss=millisec. It "wraps" after 49.710 days. - -Error 161 occurred at disk power-on lifetime: 2752 hours (114 days + 16 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 46 c2 a7 02 e0 Error: UNC 70 sectors at LBA = 0x0002a7c2 = 174018 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 46 c2 a7 02 e0 00 23d+03:19:20.800 READ DMA EXT - 25 00 48 c0 a7 02 e0 00 23d+03:19:16.000 READ DMA EXT - 25 00 1c a4 a7 02 e0 00 23d+03:19:15.200 READ DMA EXT - 25 00 1e a2 a7 02 e0 00 23d+03:19:10.400 READ DMA EXT - 25 00 20 a0 a7 02 e0 00 23d+03:19:02.200 READ DMA EXT - -Error 160 occurred at disk power-on lifetime: 2752 hours (114 days + 16 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 47 c1 a7 02 e0 Error: UNC 71 sectors at LBA = 0x0002a7c1 = 174017 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 48 c0 a7 02 e0 00 23d+03:19:16.000 READ DMA EXT - 25 00 1c a4 a7 02 e0 00 23d+03:19:15.200 READ DMA EXT - 25 00 1e a2 a7 02 e0 00 23d+03:19:10.400 READ DMA EXT - 25 00 20 a0 a7 02 e0 00 23d+03:19:02.200 READ DMA EXT - 25 00 22 9e a7 02 e0 00 23d+03:18:57.400 READ DMA EXT - -Error 159 occurred at disk power-on lifetime: 2752 hours (114 days + 16 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 02 be a7 02 e0 Error: UNC 2 sectors at LBA = 0x0002a7be = 174014 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 1e a2 a7 02 e0 00 23d+03:19:10.400 READ DMA EXT - 25 00 20 a0 a7 02 e0 00 23d+03:19:02.200 READ DMA EXT - 25 00 22 9e a7 02 e0 00 23d+03:18:57.400 READ DMA EXT - 25 00 24 9c a7 02 e0 00 23d+03:18:52.400 READ DMA EXT - 25 00 26 9a a7 02 e0 00 23d+03:18:40.200 READ DMA EXT - -Error 158 occurred at disk power-on lifetime: 2752 hours (114 days + 16 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 02 be a7 02 e0 Error: UNC 2 sectors at LBA = 0x0002a7be = 174014 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 20 a0 a7 02 e0 00 23d+03:19:02.200 READ DMA EXT - 25 00 22 9e a7 02 e0 00 23d+03:18:57.400 READ DMA EXT - 25 00 24 9c a7 02 e0 00 23d+03:18:52.400 READ DMA EXT - 25 00 26 9a a7 02 e0 00 23d+03:18:40.200 READ DMA EXT - 25 00 28 98 a7 02 e0 00 23d+03:18:32.100 READ DMA EXT - -Error 157 occurred at disk power-on lifetime: 2752 hours (114 days + 16 hours) - When the command that caused the error occurred, the device was active or idle. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 03 bd a7 02 e0 Error: UNC 3 sectors at LBA = 0x0002a7bd = 174013 - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name - -- -- -- -- -- -- -- -- ---------------- -------------------- - 25 00 22 9e a7 02 e0 00 23d+03:18:57.400 READ DMA EXT - 25 00 24 9c a7 02 e0 00 23d+03:18:52.400 READ DMA EXT - 25 00 26 9a a7 02 e0 00 23d+03:18:40.200 READ DMA EXT - 25 00 28 98 a7 02 e0 00 23d+03:18:32.100 READ DMA EXT - 25 00 2a 96 a7 02 e0 00 23d+03:18:26.400 READ DMA EXT - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed: read failure 90% 2692 173893 -# 2 Short offline Completed: read failure 10% 2654 173911 -# 3 Extended offline Completed: read failure 90% 2524 174132 -# 4 Extended offline Completed without error 00% 2358 - -# 5 Extended offline Completed without error 00% 2192 - -# 6 Extended offline Completed without error 00% 2023 - -# 7 Extended offline Completed without error 00% 1857 - -# 8 Extended offline Completed without error 00% 1689 - -# 9 Extended offline Completed without error 00% 1521 - -#10 Extended offline Completed without error 00% 1355 - -#11 Extended offline Completed without error 00% 1187 - -#12 Extended offline Completed without error 00% 1020 - -#13 Extended offline Completed without error 00% 854 - -#14 Extended offline Completed without error 00% 685 - -#15 Extended offline Completed without error 00% 517 - -#16 Extended offline Completed without error 00% 349 - -#17 Extended offline Completed without error 00% 181 - -#18 Extended offline Completed without error 00% 13 - -#19 Extended offline Completed without error 00% 4 - - -Device does not support Selective Self Tests/Logging diff --git a/www/examples/IC35L120AVVA07-0-0.txt b/www/examples/IC35L120AVVA07-0-0.txt deleted file mode 100644 index 57926ccfba480e32daeec54bd0011e23ffdfc5e3..0000000000000000000000000000000000000000 --- a/www/examples/IC35L120AVVA07-0-0.txt +++ /dev/null @@ -1,69 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: IC35L120AVVA07-0 -Serial Number: VNC605A6GG8W8A -Firmware Version: VA6OA52A -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: (3399) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 1) minutes. -Extended self-test routine -recommended polling time: ( 57) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 100 100 060 Pre-fail - 0 - 2 Throughput_Performance 0x0005 147 147 050 Pre-fail - 266 - 3 Spin_Up_Time 0x0007 093 093 024 Pre-fail - 23593335 - 4 Start_Stop_Count 0x0012 100 100 000 Old_age - 13 - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 067 Pre-fail - 0 - 8 Seek_Time_Performance 0x0005 138 138 020 Pre-fail - 30 - 9 Power_On_Hours 0x0012 100 100 000 Old_age - 554 - 10 Spin_Retry_Count 0x0013 100 100 060 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age - 13 -192 Power-Off_Retract_Count 0x0032 100 100 050 Old_age - 36 -193 Load_Cycle_Count 0x0012 100 100 050 Old_age - 36 -194 Temperature_Centigrade 0x0002 183 183 000 Old_age - 30 (Lifetime Min/Max 23/39) -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 492 -# 2 Short off-line Completed 00% 296 -# 3 Extended off-line Completed 00% 169 -# 4 Short off-line Completed 00% 168 diff --git a/www/examples/IC35L120AVVA07-0-1.txt b/www/examples/IC35L120AVVA07-0-1.txt deleted file mode 100644 index 992a620cec64e49651e672bbf8da46d9f6a57e77..0000000000000000000000000000000000000000 --- a/www/examples/IC35L120AVVA07-0-1.txt +++ /dev/null @@ -1,67 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: IC35L120AVVA07-0 -Serial Number: VNC605A6GEWZDA -Firmware Version: VA6OA52A -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: (3399) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 1) minutes. -Extended self-test routine -recommended polling time: ( 57) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 100 100 060 Pre-fail - 0 - 2 Throughput_Performance 0x0005 100 100 050 Pre-fail - 0 - 3 Spin_Up_Time 0x0007 098 098 024 Pre-fail - 22348126 - 4 Start_Stop_Count 0x0012 100 100 000 Old_age - 13 - 5 Reallocated_Sector_Ct 0x0033 100 100 005 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 067 Pre-fail - 0 - 8 Seek_Time_Performance 0x0005 100 100 020 Pre-fail - 0 - 9 Power_On_Hours 0x0012 100 100 000 Old_age - 554 - 10 Spin_Retry_Count 0x0013 100 100 060 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age - 13 -192 Power-Off_Retract_Count 0x0032 100 100 050 Old_age - 36 -193 Load_Cycle_Count 0x0012 100 100 050 Old_age - 36 -194 Temperature_Centigrade 0x0002 189 189 000 Old_age - 29 (Lifetime Min/Max 23/36) -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age - 0 -197 Current_Pending_Sector 0x0022 100 100 000 Old_age - 0 -198 Offline_Uncorrectable 0x0008 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x000a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 492 -# 2 Extended off-line Completed 00% 169 diff --git a/www/examples/MAXTOR-0.txt b/www/examples/MAXTOR-0.txt deleted file mode 100644 index 13e0eb28e95d5c7d703ac975814277c44d1c525c..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-0.txt +++ /dev/null @@ -1,139 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674205306226 -Firmware Version: A08.1500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x02) Offline data collection activity - completed without error. -Self-test execution status: ( 112) The previous self-test completed having - the read element of the test failed. -Total time to complete off-line -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 077 077 020 Pre-fail - 2909 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 29 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail - 1 - 7 Seek_Error_Rate 0x000b 100 100 023 Pre-fail - 0 - 9 Power_On_Hours 0x0012 097 097 001 Old_age - 1992 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 29 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail - 0 -194 Temperature_Centigrade 0x0022 093 090 042 Old_age - 19 -195 Hardware_ECC_Recovered 0x001a 100 006 000 Old_age - 7683906 -196 Reallocated_Event_Count 0x0010 099 099 020 Old_age - 1 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age - 1 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 198 198 000 Old_age - 2 - -SMART Error Log Version: 1 -ATA Error Count: 11 (device log contains only the most recent five errors) - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 1 occurred at disk power-on lifetime: 1029 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:02 SN:41 CL:2d CH:70 D/H:e8 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 70 08 02 41 2d 70 e8 c4 90.713 - 70 08 04 3f 2d 70 e8 c4 86.648 - 70 08 06 3d 2d 70 e8 c4 82.584 - 70 08 08 3b 2d 70 e8 c4 78.342 - 28 08 04 8b 2c 28 e8 c5 78.341 - -Error 2 occurred at disk power-on lifetime: 1029 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:04 SN:3f CL:2d CH:70 D/H:e8 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 70 08 04 3f 2d 70 e8 c4 86.648 - 70 08 06 3d 2d 70 e8 c4 82.584 - 70 08 08 3b 2d 70 e8 c4 78.342 - 28 08 04 8b 2c 28 e8 c5 78.341 - 28 08 08 4b 2c 28 e8 c5 78.339 - -Error 3 occurred at disk power-on lifetime: 1029 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:06 SN:3d CL:2d CH:70 D/H:e8 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 70 08 06 3d 2d 70 e8 c4 82.584 - 70 08 08 3b 2d 70 e8 c4 78.342 - 28 08 04 8b 2c 28 e8 c5 78.341 - 28 08 08 4b 2c 28 e8 c5 78.339 - 28 08 08 0b 2c 28 e8 c5 78.338 - -Error 4 occurred at disk power-on lifetime: 1029 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:08 SN:3b CL:2d CH:70 D/H:e8 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 70 08 08 3b 2d 70 e8 c4 78.342 - 28 08 04 8b 2c 28 e8 c5 78.341 - 28 08 08 4b 2c 28 e8 c5 78.339 - 28 08 08 0b 2c 28 e8 c5 78.338 - 28 08 08 cb 2b 28 e8 c5 78.337 - -Error 5 occurred at disk power-on lifetime: 1029 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:66 SN:41 CL:2d CH:70 D/H:e8 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 70 08 66 41 2d 70 e8 c4 69.020 - 70 08 68 3f 2d 70 e8 c4 64.956 - 70 08 6a 3d 2d 70 e8 c4 60.891 - 70 08 6c 3b 2d 70 e8 c4 56.826 - 70 08 fe a9 2c 70 e8 c4 52.713 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed: read failure 90% 1965 0x08702f11 -# 2 Short off-line Completed 00% 1800 -# 3 Short off-line Completed 00% 1778 -# 4 Short off-line Completed 00% 1777 diff --git a/www/examples/MAXTOR-1.txt b/www/examples/MAXTOR-1.txt deleted file mode 100644 index b1ed8ace35f6f00813087682bb8b1d9177b3ad2f..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-1.txt +++ /dev/null @@ -1,143 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119123435 -Firmware Version: A08.1500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Off-line data collection status: (0x04) Offline data collection activity was - suspended by an interrupting command from host. -Self-test execution status: ( 89) The previous self-test completed having - the electrical element of the test - failed. -Total time to complete off-line -data collection: (2536) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 074 074 020 Pre-fail - 3294 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 32 - 5 Reallocated_Sector_Ct 0x0033 001 001 020 Pre-fail FAILING_NOW 499 - 7 Seek_Error_Rate 0x000b 100 001 023 Pre-fail In_the_past 0 - 9 Power_On_Hours 0x0012 086 086 001 Old_age - 9812 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 32 - 13 Read_Soft_Error_Rate 0x000b 100 001 023 Pre-fail In_the_past 0 -194 Temperature_Centigrade 0x0022 091 086 042 Old_age - 24 -195 Hardware_ECC_Recovered 0x001a 006 004 000 Old_age - 417912090 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age - 0 -197 Current_Pending_Sector 0x0032 033 032 020 Old_age - 338 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -ATA Error Count: 20255 (device log contains only the most recent five errors) - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 1 occurred at disk power-on lifetime: 9574 hours -When the command that caused the error occurred, the device was in a vendor specific or reserved state. -After command completion occurred, registers were: -ER:40 SC:60 SN:b8 CL:c6 CH:02 D/H:e5 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 02 05 60 b8 c6 02 e5 c4 474.857 - 02 05 f8 20 c6 02 e5 c4 470.748 - 02 05 08 18 c6 02 e5 c4 470.746 - 00 00 f8 20 c5 02 e5 c4 470.732 - 00 00 08 18 c5 02 e5 c4 470.730 - -Error 2 occurred at disk power-on lifetime: 9574 hours -When the command that caused the error occurred, the device was in a vendor specific or reserved state. -After command completion occurred, registers were: -ER:40 SC:68 SN:b0 CL:c6 CH:02 D/H:e5 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 02 05 f8 20 c6 02 e5 c4 470.748 - 02 05 08 18 c6 02 e5 c4 470.746 - 00 00 f8 20 c5 02 e5 c4 470.732 - 00 00 08 18 c5 02 e5 c4 470.730 - 02 05 f8 20 c4 02 e5 c4 470.717 - -Error 3 occurred at disk power-on lifetime: 9574 hours -When the command that caused the error occurred, the device was in a vendor specific or reserved state. -After command completion occurred, registers were: -ER:40 SC:08 SN:c0 CL:3e CH:0e D/H:e5 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0e 05 08 c0 3e 0e e5 c4 181.677 - d0 04 08 b0 67 d0 e4 c5 181.651 - 0e 05 08 a0 45 0e e5 c4 181.646 - 0e 05 80 20 43 0e e5 c4 181.635 - 0e 05 80 20 41 0e e5 c4 181.622 - -Error 4 occurred at disk power-on lifetime: 9574 hours -When the command that caused the error occurred, the device was in a vendor specific or reserved state. -After command completion occurred, registers were: -ER:40 SC:60 SN:c0 CL:3e CH:0e D/H:e5 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0e 05 80 a0 3e 0e e5 c4 172.530 - 0e 05 80 20 3d 0e e5 c4 172.335 - 0e 05 80 20 3c 0e e5 c4 164.744 - 0e 05 10 10 3b 0e e5 c4 164.736 - 0e 05 f8 18 3a 0e e5 c4 157.202 - -Error 5 occurred at disk power-on lifetime: 9574 hours -When the command that caused the error occurred, the device was in a vendor specific or reserved state. -After command completion occurred, registers were: -ER:40 SC:c0 SN:18 CL:04 CH:d3 D/H:e4 ST:d1 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - d3 04 c0 18 04 d3 e4 c4 502.837 - d3 04 f8 e0 03 d3 e4 c4 498.739 - d3 04 f8 e8 02 d3 e4 c4 498.716 - 55 01 c8 90 70 55 e1 c4 498.705 - 55 01 38 48 70 55 e1 c4 498.680 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short off-line Completed: electrical failure 90% 9786 -# 2 Extended captive Completed: servo/seek failure 90% 9676 0x04b7ed3d -# 3 Extended captive Completed 00% 9575 -# 4 Extended off-line Completed 00% 9432 -# 5 Extended off-line Completed 00% 9415 diff --git a/www/examples/MAXTOR-10.txt b/www/examples/MAXTOR-10.txt deleted file mode 100644 index adb8beb9e75050ae872432eac6085f2878023613..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-10.txt +++ /dev/null @@ -1,188 +0,0 @@ -smartctl version 5.22 Copyright (C) 2002-3 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119014987 -Firmware Version: A08.1500 -Device is: In smartctl database [for details use: -P show] -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -Local Time is: Mon Oct 27 14:30:17 2003 CST -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Offline data collection status: (0x80) Offline data collection activity was - never started. - Auto Offline Data Collection: Enabled. -Self-test execution status: ( 112) The previous self-test completed having - the read element of the test failed. -Total time to complete Offline -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail Offline - 0 - 3 Spin_Up_Time 0x0027 075 074 020 Pre-fail Always - 3135 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age Always - 54 - 5 Reallocated_Sector_Ct 0x0033 098 097 020 Pre-fail Always - 14 - 7 Seek_Error_Rate 0x000b 100 100 023 Pre-fail Always - 0 - 9 Power_On_Hours 0x0012 072 072 001 Old_age Always - 18875 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age Always - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail Always - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age Always - 53 - 13 Read_Soft_Error_Rate 0x000b 100 085 023 Pre-fail Always - 0 -194 Temperature_Celsius 0x0022 093 088 042 Old_age Always - 20 -195 Hardware_ECC_Recovered 0x001a 028 002 000 Old_age Always - 1472864733 -196 Reallocated_Event_Count 0x0010 100 099 020 Old_age Offline - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age Always - 2 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age Always - 0 - -SMART Error Log Version: 1 -ATA Error Count: 28 (device log contains only the most recent five errors) - CR = Command Register [HEX] - FR = Features Register [HEX] - SC = Sector Count Register [HEX] - SN = Sector Number Register [HEX] - CL = Cylinder Low Register [HEX] - CH = Cylinder High Register [HEX] - DH = Device/Head Register [HEX] - DC = Device Command Register [HEX] - ER = Error register [HEX] - ST = Status register [HEX] -Timestamp = decimal seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 28 occurred at disk power-on lifetime: 18785 hours - When the command that caused the error occurred, the device was in an unknown state. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 d1 38 ce 8f 40 e0 Error: UNC - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 38 ce 8f 40 e0 40 315.769 READ DMA - c8 00 40 c6 8f 40 e0 40 311.634 READ DMA - b0 00 01 01 4f c2 e0 1f 284.485 [Reserved SMART command] - b0 00 01 06 4f c2 e0 34 284.470 [Reserved SMART command] - b0 00 01 00 4f c2 e0 34 284.399 [Reserved SMART command] - -Error 27 occurred at disk power-on lifetime: 18785 hours - When the command that caused the error occurred, the device was in an unknown state. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 38 ce 8f 40 e0 Error: UNC - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 40 c6 8f 40 e0 40 311.634 READ DMA - b0 00 01 01 4f c2 e0 1f 284.485 [Reserved SMART command] - b0 00 01 06 4f c2 e0 34 284.470 [Reserved SMART command] - b0 00 01 00 4f c2 e0 34 284.399 [Reserved SMART command] - b0 00 00 00 4f c2 00 34 284.328 [Reserved SMART command] - -Error 26 occurred at disk power-on lifetime: 18744 hours - When the command that caused the error occurred, the device was in an unknown state. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 d1 08 bf 00 30 e0 Error: UNC - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 00 08 bf 00 30 e0 30 134.258 READ DMA - c8 00 08 cf 3d 34 e0 34 134.247 READ DMA - c8 00 08 af 00 34 e0 34 134.215 READ DMA - c8 00 28 6f 3e 28 e0 28 134.213 READ DMA - c8 00 08 67 3e 28 e0 28 134.201 READ DMA - -Error 25 occurred at disk power-on lifetime: 18619 hours - When the command that caused the error occurred, the device was in an unknown state. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 07 89 67 10 e3 Error: UNC - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 03 08 88 67 10 e3 10 54.493 READ DMA - c8 03 08 88 69 08 e3 08 54.483 READ DMA - c8 03 08 88 67 08 e3 08 54.471 READ DMA - c8 02 08 88 69 ec e2 ec 54.464 READ DMA - c8 02 08 88 67 ec e2 ec 54.439 READ DMA - -Error 24 occurred at disk power-on lifetime: 18619 hours - When the command that caused the error occurred, the device was in an unknown state. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 59 06 8a 67 0c e2 Error: UNC - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - c8 02 08 88 67 0c e2 0c 49.281 READ DMA - c8 02 08 88 69 08 e2 08 49.270 READ DMA - c8 02 08 88 69 04 e2 04 49.259 READ DMA - c8 02 08 88 69 00 e2 00 49.257 READ DMA - c8 02 08 50 5c 00 e2 00 49.254 READ DMA - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed: read failure 80% 18841 0x001f807f -# 2 Extended offline Completed: read failure 80% 18673 0x020c678a -# 3 Extended offline Completed: read failure 80% 18505 0x020c678a -# 4 Extended offline Completed: read failure 80% 18338 0x020c678a -# 5 Extended offline Completed: read failure 90% 18229 0x00408f96 -# 6 Extended offline Completed: read failure 80% 18171 0x02386789 -# 7 Extended offline Completed without error 00% 18051 - -# 8 Short offline Completed without error 00% 18051 - -# 9 Extended offline Completed without error 00% 18003 - -#10 Extended offline Completed without error 00% 17836 - -#11 Extended offline Completed without error 00% 17668 - -#12 Extended offline Completed without error 00% 17501 - -#13 Extended offline Completed without error 00% 17334 - -#14 Extended offline Completed without error 00% 17166 - -#15 Extended offline Completed without error 00% 16999 - -#16 Extended offline Completed without error 00% 16831 - -#17 Extended offline Completed without error 00% 16664 - -#18 Extended offline Completed without error 00% 16603 - -#19 Extended offline Completed without error 00% 16497 - -#20 Extended offline Completed without error 00% 16329 - -#21 Extended offline Completed without error 00% 16163 - - diff --git a/www/examples/MAXTOR-2.txt b/www/examples/MAXTOR-2.txt deleted file mode 100644 index 9ff60ffcb3927d55bfa1c40ff5b803b9cc9ced87..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-2.txt +++ /dev/null @@ -1,79 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119113862 -Firmware Version: A08.1500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED -See vendor-specific Attribute list for marginal Attributes. - -General SMART Values: -Off-line data collection status: (0x05) Offline data collection activity was - aborted by an interrupting command from host. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 075 075 020 Pre-fail - 3249 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 31 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 001 023 Pre-fail In_the_past 0 - 9 Power_On_Hours 0x0012 086 086 001 Old_age - 9754 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 31 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail - 0 -194 Temperature_Centigrade 0x0022 035 032 042 Old_age FAILING_NOW 168 -195 Hardware_ECC_Recovered 0x001a 100 002 000 Old_age - 880099716 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age - 0 -198 Offline_Uncorrectable 0x0010 100 253 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short off-line Completed 00% 9691 -# 2 Short captive Completed 00% 9691 -# 3 Extended captive Completed 00% 9618 -# 4 Extended captive Interrupted (host reset) 90% 9563 -# 5 Short captive Completed 00% 9563 -# 6 Short off-line Completed 00% 9563 -# 7 Short captive Completed 00% 9545 -# 8 Extended off-line Completed 00% 9541 -# 9 Short captive Completed 00% 9541 -#10 Extended off-line Completed 00% 9537 -#11 Extended off-line Completed 00% 9536 -#12 Extended off-line Interrupted (host reset) 90% 9534 -#13 Extended off-line Completed 00% 9517 -#14 Extended off-line Completed 00% 9484 diff --git a/www/examples/MAXTOR-3.txt b/www/examples/MAXTOR-3.txt deleted file mode 100644 index 26df69e5977a8b4e46958290ac79b663d7246e4e..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-3.txt +++ /dev/null @@ -1,67 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 6L080J4 -Serial Number: 664201100034 -Firmware Version: A93.0500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 35) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 40) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 069 066 020 Pre-fail - 3984 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 110 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 023 Pre-fail - 0 - 9 Power_On_Hours 0x0012 099 099 001 Old_age - 1294 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 110 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail - 0 -194 Temperature_Centigrade 0x0022 092 087 042 Old_age - 22 -195 Hardware_ECC_Recovered 0x001a 100 100 000 Old_age - 1163 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age - 0 -198 Offline_Uncorrectable 0x0010 100 253 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 1038 -# 2 Extended off-line Aborted by host 00% 786 -# 3 Extended off-line Aborted by host 00% 786 diff --git a/www/examples/MAXTOR-4.txt b/www/examples/MAXTOR-4.txt deleted file mode 100644 index 65ae2850d953448898079f827870b697f26dc07d..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-4.txt +++ /dev/null @@ -1,65 +0,0 @@ -smartctl version 5.0-24 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 6L080J4 -Serial Number: 664205757172 -Firmware Version: A93.0500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x04) Offline data collection activity was - suspended by an interrupting command from host. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 35) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 40) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 065 064 020 Pre-fail - 4481 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 81 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 023 Pre-fail - 0 - 9 Power_On_Hours 0x0012 098 098 001 Old_age - 1767 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 100 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 81 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail - 0 -194 Temperature_Centigrade 0x0022 091 084 042 Old_age - 24 -195 Hardware_ECC_Recovered 0x001a 100 100 000 Old_age - 52795 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age - 0 -198 Offline_Uncorrectable 0x0010 100 253 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 1283 diff --git a/www/examples/MAXTOR-6.txt b/www/examples/MAXTOR-6.txt deleted file mode 100644 index fe37f6541cc9b0f487578982b94f6a4931f984fd..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-6.txt +++ /dev/null @@ -1,156 +0,0 @@ -# /usr/sbin/smartctl -a -m /dev/hda -smartctl version 5.0-49 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: Maxtor 4R080J0 -Serial Number: R20BZ3LE -Firmware Version: RAMB1TU0 -ATA Version is: 7 -ATA Standard is: Unrecognized. Minor revision code: 0x1e -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x82) Offline data collection activity - completed without error. -Self-test execution status: ( 33) The self-test routine was interrupted - by the host with a hard or soft reset. -Total time to complete off-line -data collection: ( 241) seconds. -Offline data collection -capabilities: (0x5b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 41) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 3 Spin_Up_Time 0x0027 252 252 063 Pre-fail - 1621 - 4 Start_Stop_Count 0x0032 253 253 000 Old_age - 18 - 5 Reallocated_Sector_Ct 0x0033 253 253 063 Pre-fail - 0 - 6 Read_Channel_Margin 0x0001 253 253 100 Pre-fail - 0 - 7 Seek_Error_Rate 0x000a 253 252 000 Old_age - 0 - 8 Seek_Time_Performance 0x0027 252 244 187 Pre-fail - 41642 - 9 Power_On_Hours 0x0032 253 253 000 Old_age - 27 h + 13 m - 10 Spin_Retry_Count 0x002b 252 252 157 Pre-fail - 0 - 11 Calibration_Retry_Count 0x002b 253 252 223 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 253 253 000 Old_age - 24 -192 Power-Off_Retract_Count 0x0032 253 253 000 Old_age - 0 -193 Load_Cycle_Count 0x0032 253 253 000 Old_age - 0 -194 Temperature_Celsius 0x0032 253 253 000 Old_age - 29 -195 Hardware_ECC_Recovered 0x000a 253 252 000 Old_age - 31004 -196 Reallocated_Event_Count 0x0008 253 253 000 Old_age - 0 -197 Current_Pending_Sector 0x0008 253 253 000 Old_age - 0 -198 Offline_Uncorrectable 0x0008 253 253 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x0008 199 199 000 Old_age - 0 -200 Multi_Zone_Error_Rate 0x000a 253 252 000 Old_age - 0 -201 Unknown_Attribute 0x000a 253 252 000 Old_age - 3 -202 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -203 Unknown_Attribute 0x000b 253 252 180 Pre-fail - 2 -204 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -205 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -207 Unknown_Attribute 0x002a 252 252 000 Old_age - 0 -208 Unknown_Attribute 0x002a 252 252 000 Old_age - 0 -209 Unknown_Attribute 0x0024 079 063 000 Old_age - 0 - 99 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 -100 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 -101 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 - -SMART Error Log Version: 1 -ATA Error Count: 5 - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 5 occurred at disk power-on lifetime: 4 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:00 SN:00 CL:f4 CH:2c D/H:00 ST:01 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0e d4 00 82 4f c2 f0 b0 8907.296 - 08 d4 00 82 4f c2 f0 b0 6260.832 - 08 d1 01 01 4f c2 f0 b0 6260.800 - 08 d0 01 00 4f c2 f0 b0 6260.768 - 08 da 00 00 4f c2 10 b0 6260.736 - -Error 4 occurred at disk power-on lifetime: 1 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:00 SN:00 CL:f4 CH:2c D/H:00 ST:01 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0e d4 00 82 4f c2 f0 b0 1944.192 - 08 d4 00 82 4f c2 f0 b0 1824.240 - 08 d1 01 01 4f c2 f0 b0 1824.224 - 08 d0 01 00 4f c2 f0 b0 1824.160 - 08 00 08 3f 00 00 f0 ca 1810.400 - -Error 3 occurred at disk power-on lifetime: 1 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:00 SN:00 CL:f4 CH:2c D/H:00 ST:01 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0e d4 00 82 4f c2 f0 b0 1456.432 - 08 d4 00 82 4f c2 f0 b0 1379.456 - 08 d1 01 01 4f c2 f0 b0 1379.440 - 08 d0 01 00 4f c2 f0 b0 1379.376 - 08 00 08 f7 01 54 f0 ca 1378.544 - -Error 2 occurred at disk power-on lifetime: 0 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:00 SN:00 CL:f4 CH:2c D/H:00 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 08 d4 00 82 4f c2 f0 b0 908.320 - 08 d1 01 01 4f c2 f0 b0 908.320 - 08 d0 01 00 4f c2 f0 b0 908.272 - 08 d4 00 7f 4f c2 f0 b0 905.696 - 08 d1 01 01 4f c2 f0 b0 905.664 - -Error 1 occurred at disk power-on lifetime: 0 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:50 SN:40 CL:97 CH:03 D/H:10 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 fe 00 00 00 00 10 ef 137.184 - 00 3d 00 00 00 00 10 c3 137.136 - 00 e4 00 00 00 00 10 c3 137.088 - 00 3d 00 00 00 00 10 c3 137.088 - 00 00 00 00 5e 20 10 70 137.040 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended captive Interrupted (host reset) 10% 4 -# 2 Short off-line Completed 00% 2 -# 3 Short captive Completed 00% 2 -# 4 Extended captive Interrupted (host reset) 40% 1 -# 5 Extended captive Interrupted (host reset) 70% 1 -# 6 Extended captive Interrupted (host reset) 40% 0 diff --git a/www/examples/MAXTOR-7.txt b/www/examples/MAXTOR-7.txt deleted file mode 100644 index 1d3fa00491946ea74388b37962bdac003ee8c829..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-7.txt +++ /dev/null @@ -1,157 +0,0 @@ -smartctl version 5.1-4 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119114160 -Firmware Version: A08.1500 -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -Local Time is: Tue May 13 08:59:49 2003 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Off-line data collection status: (0x82) Offline data collection activity - completed without error. -Self-test execution status: ( 112) The previous self-test completed having - the read element of the test failed. -Total time to complete off-line -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 075 075 020 Pre-fail - 3214 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age - 58 - 5 Reallocated_Sector_Ct 0x0033 098 098 020 Pre-fail - 12 - 7 Seek_Error_Rate 0x000b 001 001 023 Pre-fail FAILING_NOW 13 - 9 Power_On_Hours 0x0012 078 078 001 Old_age - 14851 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age - 0 - 11 Calibration_Retry_Count 0x0013 100 090 020 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age - 58 - 13 Read_Soft_Error_Rate 0x000b 100 085 023 Pre-fail - 0 -194 Temperature_Celsius 0x0022 091 087 042 Old_age - 24 -195 Hardware_ECC_Recovered 0x001a 004 003 000 Old_age - 513691822 -196 Reallocated_Event_Count 0x0010 099 099 020 Old_age - 1 -197 Current_Pending_Sector 0x0032 098 098 020 Old_age - 12 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age - 0 - -SMART Error Log Version: 1 -ATA Error Count: 25 (device log contains only the most recent five errors) - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 25 occurred at disk power-on lifetime: 14799 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:04 SN:53 CL:1d CH:0c D/H:e0 ST:59 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0c 00 08 4f 1d 0c e0 c8 0.523 - 0c 00 08 47 1d 0c e0 c8 510.677 - 04 00 08 6f 04 04 e0 ca 510.675 - 04 00 08 4f 1c 04 e0 ca 510.674 - 04 00 08 6f 04 04 e0 ca 510.674 - -Error 24 occurred at disk power-on lifetime: 14799 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:03 SN:44 CL:1d CH:0c D/H:e0 ST:59 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0c 00 08 3f 1d 0c e0 c8 495.294 - 0c 00 08 37 1d 0c e0 c8 491.239 - 0c 00 08 2f 1d 0c e0 c8 488.433 - 14 00 08 17 04 14 e0 ca 488.432 - 0c 00 08 b7 00 0c e0 ca 488.432 - -Error 23 occurred at disk power-on lifetime: 14799 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:06 SN:21 CL:18 CH:0c D/H:e0 ST:59 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0c 00 08 1f 18 0c e0 c8 392.659 - 00 00 08 17 18 0c e0 c8 392.654 - 0c 00 08 0f 18 0c e0 c8 392.624 - 00 00 08 07 18 0c e0 c8 392.620 - 00 00 08 ff 17 0c e0 c8 392.615 - -Error 22 occurred at disk power-on lifetime: 14799 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:05 SN:b2 CL:16 CH:0c D/H:e0 ST:59 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0c 00 08 af 16 0c e0 c8 385.889 - 04 00 08 6f 04 04 e0 ca 385.886 - 04 00 08 4f 1c 04 e0 ca 385.886 - 04 00 08 6f 04 04 e0 ca 385.886 - 04 00 08 4f 1c 04 e0 ca 385.885 - -Error 21 occurred at disk power-on lifetime: 14799 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:40 SC:06 SN:a9 CL:16 CH:0c D/H:e0 ST:59 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 0c 00 08 a7 16 0c e0 c8 381.795 - 04 00 08 6f 04 04 e0 ca 381.793 - 04 00 08 4f 1c 04 e0 ca 381.793 - 04 00 08 6f 04 04 e0 ca 381.792 - 04 00 08 4f 1c 04 e0 ca 381.792 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed: read failure 90% 14757 0x000409bb -# 2 Extended off-line Completed: read failure 90% 14755 0x000bf956 -# 3 Extended off-line Completed 00% 14305 - -# 4 Extended off-line Completed 00% 14100 - -# 5 Extended off-line Completed 00% 13721 - -# 6 Extended off-line Completed 00% 13636 - -# 7 Extended off-line Completed 00% 13233 - -# 8 Extended off-line Completed 00% 13078 - -# 9 Extended off-line Completed 00% 12093 - -#10 Extended off-line Completed 00% 11926 - -#11 Extended off-line Completed 00% 11428 - -#12 Extended off-line Completed 00% 11030 - -#13 Extended off-line Completed 00% 10888 - -#14 Extended off-line Completed 00% 10728 - -#15 Extended off-line Completed 00% 10435 - -#16 Extended off-line Completed 00% 10267 - -#17 Extended off-line Completed 00% 10098 - -#18 Extended off-line Completed 00% 9930 - -#19 Extended off-line Completed 00% 9599 - diff --git a/www/examples/MAXTOR-8.txt b/www/examples/MAXTOR-8.txt deleted file mode 100644 index 3ae4d87735563a46d403293ec5ce00b26d6bb3d0..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-8.txt +++ /dev/null @@ -1,82 +0,0 @@ -smartctl version 5.1-14 Copyright (C) 2002-3 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119116076 -Firmware Version: A08.1500 -Device is: In smartctl database [for details use: -P show] -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -Local Time is: Tue Jun 17 14:46:37 2003 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Off-line data collection status: (0x82) Offline data collection activity was - completed without error. - Auto Off-line Data Collection: Enabled. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail Offline - 0 - 3 Spin_Up_Time 0x0027 075 075 020 Pre-fail Always - 3249 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age Always - 45 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail Always - 0 - 7 Seek_Error_Rate 0x000b 100 001 023 Pre-fail Always In_the_past 0 - 9 Power_On_Hours 0x0012 082 082 001 Old_age Always - 12223 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age Always - 0 - 11 Calibration_Retry_Count 0x0013 020 020 020 Pre-fail Always FAILING_NOW 8 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age Always - 45 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail Always - 0 -194 Temperature_Celsius 0x0022 094 088 042 Old_age Always - 17 -195 Hardware_ECC_Recovered 0x001a 100 007 000 Old_age Always - 494134044 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age Offline - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age Always - 0 -198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age Always - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 11610 - -# 2 Extended off-line Completed 00% 11213 - -# 3 Extended off-line Completed 00% 11072 - -# 4 Extended off-line Completed 00% 10911 - -# 5 Extended off-line Completed 00% 10618 - -# 6 Extended off-line Completed 00% 10450 - -# 7 Extended off-line Completed 00% 10282 - -# 8 Extended off-line Completed 00% 10114 - -# 9 Extended off-line Completed 00% 9783 - - diff --git a/www/examples/MAXTOR-9.txt b/www/examples/MAXTOR-9.txt deleted file mode 100644 index 6a692423d766812c05a4c6f298bda322d780a655..0000000000000000000000000000000000000000 --- a/www/examples/MAXTOR-9.txt +++ /dev/null @@ -1,94 +0,0 @@ -smartctl version 5.1-14 Copyright (C) 2002-3 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: MAXTOR 4K080H4 -Serial Number: 674119123112 -Firmware Version: A08.1500 -Device is: In smartctl database [for details use: -P show] -ATA Version is: 5 -ATA Standard is: ATA/ATAPI-5 T13 1321D revision 1 -Local Time is: Tue Aug 19 02:06:11 2003 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Off-line data collection status: (0x80) Offline data collection activity was - never started. - Auto Off-line Data Collection: Enabled. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 44) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - No Conveyance Self-test supported. - No Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 50) minutes. - -SMART Attributes Data Structure revision number: 11 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x0029 100 253 020 Pre-fail Offline - 0 - 3 Spin_Up_Time 0x0027 075 074 020 Pre-fail Always - 3186 - 4 Start_Stop_Count 0x0032 100 100 008 Old_age Always - 68 - 5 Reallocated_Sector_Ct 0x0033 100 100 020 Pre-fail Always - 0 - 7 Seek_Error_Rate 0x000b 100 100 023 Pre-fail Always - 0 - 9 Power_On_Hours 0x0012 074 074 001 Old_age Always - 17202 - 10 Spin_Retry_Count 0x0026 100 100 000 Old_age Always - 0 - 11 Calibration_Retry_Count 0x0013 010 010 020 Pre-fail Always FAILING_NOW 9 - 12 Power_Cycle_Count 0x0032 100 100 008 Old_age Always - 68 - 13 Read_Soft_Error_Rate 0x000b 100 100 023 Pre-fail Always - 0 -194 Temperature_Celsius 0x0022 092 087 042 Old_age Always - 22 -195 Hardware_ECC_Recovered 0x001a 018 003 000 Old_age Always - 1082933060 -196 Reallocated_Event_Count 0x0010 100 100 020 Old_age Offline - 0 -197 Current_Pending_Sector 0x0032 100 100 020 Old_age Always - 0 -198 Offline_Uncorrectable 0x0010 100 253 000 Old_age Offline - 0 -199 UDMA_CRC_Error_Count 0x001a 200 200 000 Old_age Always - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 17157 - -# 2 Extended off-line Completed 00% 16990 - -# 3 Extended off-line Completed 00% 16823 - -# 4 Extended off-line Completed 00% 16657 - -# 5 Extended off-line Completed 00% 16598 - -# 6 Extended off-line Completed 00% 16490 - -# 7 Extended off-line Completed 00% 16323 - -# 8 Extended off-line Completed 00% 16157 - -# 9 Extended off-line Completed 00% 15991 - -#10 Extended off-line Completed 00% 15696 - -#11 Extended off-line Completed 00% 15614 - -#12 Extended off-line Completed 00% 15241 - -#13 Extended off-line Completed 00% 15026 - -#14 Extended off-line Completed 00% 14785 - -#15 Extended off-line Completed 00% 14334 - -#16 Extended off-line Completed 00% 14129 - -#17 Extended off-line Completed 00% 13750 - -#18 Extended off-line Completed 00% 13665 - -#19 Extended off-line Completed 00% 13262 - -#20 Extended off-line Completed 00% 13108 - -#21 Extended off-line Completed 00% 12125 - - diff --git a/www/examples/Maxtor-5.txt b/www/examples/Maxtor-5.txt deleted file mode 100644 index 3bb171eb6fd032d5ad56dea9925c85bec295c545..0000000000000000000000000000000000000000 --- a/www/examples/Maxtor-5.txt +++ /dev/null @@ -1,127 +0,0 @@ -smartctl version 5.0-36 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: Maxtor 98196H8 -Serial Number: V80HV6NC -Firmware Version: ZAH814Y0 -ATA Version is: 6 -ATA Standard is: ATA/ATAPI-6 T13 1410D revision 0 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 30) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 60) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000a 253 252 000 Old_age - 26 - 3 Spin_Up_Time 0x0027 208 206 063 Pre-fail - 11285 - 4 Start_Stop_Count 0x0032 253 253 000 Old_age - 62 - 5 Reallocated_Sector_Ct 0x0033 253 253 063 Pre-fail - 0 - 6 Read_Channel_Margin 0x0001 253 253 100 Pre-fail - 0 - 7 Seek_Error_Rate 0x000a 253 252 000 Old_age - 0 - 8 Seek_Time_Performance 0x0027 249 244 187 Pre-fail - 50271 - 9 Power_On_Hours 0x0032 236 236 000 Old_age - 32671 - 10 Spin_Retry_Count 0x002b 253 252 223 Pre-fail - 0 - 11 Calibration_Retry_Count 0x002b 253 252 223 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 253 253 000 Old_age - 67 -196 Reallocated_Event_Count 0x0008 253 253 000 Old_age - 0 -197 Current_Pending_Sector 0x0008 253 253 000 Old_age - 0 -198 Offline_Uncorrectable 0x0008 253 253 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x0008 199 199 000 Old_age - 0 -200 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -201 Unknown_Attribute 0x000a 253 252 000 Old_age - 1 -202 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -203 Unknown_Attribute 0x000b 253 252 180 Pre-fail - 2 -204 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -205 Unknown_Attribute 0x000a 253 252 000 Old_age - 0 -207 Unknown_Attribute 0x002a 253 252 000 Old_age - 0 -208 Unknown_Attribute 0x002a 253 252 000 Old_age - 0 -209 Unknown_Attribute 0x0024 253 253 000 Old_age - 0 - 96 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 - 97 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 - 98 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 - 99 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 -100 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 -101 Unknown_Attribute 0x0004 253 253 000 Old_age - 0 - -SMART Error Log Version: 1 -ATA Error Count: 3 - DCR = Device Control Register - FR = Features Register - SC = Sector Count Register - SN = Sector Number Register - CL = Cylinder Low Register - CH = Cylinder High Register - D/H = Device/Head Register - CR = Content written to Command Register - ER = Error register - STA = Status register -Timestamp is seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 1 occurred at disk power-on lifetime: 0 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:40 SN:42 CL:97 CH:23 D/H:00 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 fe 00 00 00 00 00 ef 137.440 - 00 d9 00 00 4f c2 00 b0 137.328 - 00 da 00 00 4f c2 00 b0 137.232 - 00 d8 00 00 4f c2 00 b0 137.152 - 00 db 00 00 4f c2 00 b0 136.976 - -Error 2 occurred at disk power-on lifetime: 0 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:40 SN:40 CL:97 CH:23 D/H:00 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 fe 00 00 00 00 00 ef 342.432 - 00 e4 00 00 00 00 00 c3 342.368 - 00 d0 00 00 0a 00 00 c3 342.368 - 00 fe 00 00 00 00 00 ef 342.304 - 00 3d 00 00 00 00 00 c3 342.256 - -Error 3 occurred at disk power-on lifetime: 0 hours -When the command that caused the error occurred, the device was in an unknown state. -After command completion occurred, registers were: -ER:04 SC:40 SN:40 CL:97 CH:03 D/H:00 ST:51 -Sequence of commands leading to the command that caused the error were: -DCR FR SC SN CL CH D/H CR Timestamp - 00 fe 00 00 00 00 00 ef 342.304 - 00 3d 00 00 00 00 00 c3 342.256 - 00 e4 00 00 00 00 00 c3 342.192 - 00 3d 00 00 00 00 00 c3 342.192 - 00 00 01 01 00 00 00 ec 342.144 - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short off-line Completed 00% 5255 diff --git a/www/examples/TOSHIBA-0.txt b/www/examples/TOSHIBA-0.txt deleted file mode 100644 index 965e55d99af8af42a5d6592c3159dd9fbb70312a..0000000000000000000000000000000000000000 --- a/www/examples/TOSHIBA-0.txt +++ /dev/null @@ -1,73 +0,0 @@ -smartctl version 5.0-31 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: TOSHIBA MK2018GAS -Serial Number: X22F7553T -Firmware Version: Q2.03 D -ATA Version is: 5 -ATA Standard is: Unrecognized. Minor revision code: 0x00 -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 212) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 23) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 100 100 050 Pre-fail - 0 - 2 Throughput_Performance 0x0005 100 100 050 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 100 100 001 Pre-fail - 910 - 4 Start_Stop_Count 0x0032 100 100 000 Old_age - 18 - 5 Reallocated_Sector_Ct 0x0033 100 100 050 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 050 Pre-fail - 0 - 8 Seek_Time_Performance 0x0005 100 100 050 Pre-fail - 0 - 9 Power_On_Hours 0x0032 100 100 000 Old_age - 9 - 10 Spin_Retry_Count 0x0033 100 100 030 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age - 18 -192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age - 6 -193 Load_Cycle_Count 0x0032 100 100 000 Old_age - 437 -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age - 0 -197 Current_Pending_Sector 0x0032 100 100 000 Old_age - 0 -198 Offline_Uncorrectable 0x0030 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x0032 200 200 000 Old_age - 0 -220 Disk_Shift 0x0002 100 100 000 Old_age - 4250 -222 Loaded_Hours 0x0032 100 100 000 Old_age - 4 -223 Load_Retry_Count 0x0032 100 100 000 Old_age - 0 -224 Load_Friction 0x0022 100 100 000 Old_age - 0 -226 Load-in_Time 0x0026 100 100 000 Old_age - 590 -240 Unknown_Attribute 0x0001 100 100 001 Pre-fail - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 4 -# 2 Short captive Completed 00% 0 -# 3 Short off-line Completed 00% 0 diff --git a/www/examples/TOSHIBA-MK6021GAS.txt b/www/examples/TOSHIBA-MK6021GAS.txt deleted file mode 100644 index 776cb3dcee4f794a875c763556a27b001893c597..0000000000000000000000000000000000000000 --- a/www/examples/TOSHIBA-MK6021GAS.txt +++ /dev/null @@ -1,74 +0,0 @@ -smartctl version 5.1-7 Copyright (C) 2002 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: TOSHIBA MK6021GAS -Serial Number: Y2MJ1530T -Firmware Version: GA023A -ATA Version is: 5 -ATA Standard is: Unrecognized. Minor revision code: 0x00 -Local Time is: Mon Feb 17 09:37:27 2003 CST -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: PASSED - -General SMART Values: -Off-line data collection status: (0x00) Offline data collection activity was - never started. -Self-test execution status: ( 0) The previous self-test routine completed - without error or no self-test has ever - been run. -Total time to complete off-line -data collection: ( 587) seconds. -Offline data collection -capabilities: (0x1b) SMART execute Offline immediate. - Automatic timer ON/OFF support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 65) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 100 100 050 Pre-fail - 0 - 2 Throughput_Performance 0x0005 100 100 050 Pre-fail - 0 - 3 Spin_Up_Time 0x0027 100 100 001 Pre-fail - 1267 - 4 Start_Stop_Count 0x0032 100 100 000 Old_age - 18 - 5 Reallocated_Sector_Ct 0x0033 100 100 050 Pre-fail - 0 - 7 Seek_Error_Rate 0x000b 100 100 050 Pre-fail - 0 - 8 Seek_Time_Performance 0x0005 100 100 050 Pre-fail - 0 - 9 Power_On_Hours 0x0032 100 100 000 Old_age - 39 - 10 Spin_Retry_Count 0x0033 100 100 030 Pre-fail - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age - 16 -192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age - 1 -193 Load_Cycle_Count 0x0032 100 100 000 Old_age - 460 -194 Temperature_Celsius 0x0022 100 100 000 Old_age - 40 (Lifetime Min/Max 17/51) -196 Reallocated_Event_Count 0x0032 100 100 000 Old_age - 0 -197 Current_Pending_Sector 0x0032 100 100 000 Old_age - 0 -198 Offline_Uncorrectable 0x0030 100 100 000 Old_age - 0 -199 UDMA_CRC_Error_Count 0x0032 200 200 000 Old_age - 0 -220 Disk_Shift 0x0002 100 100 000 Old_age - 8332 -222 Loaded_Hours 0x0032 100 100 000 Old_age - 30 -223 Load_Retry_Count 0x0032 100 100 000 Old_age - 0 -224 Load_Friction 0x0022 100 100 000 Old_age - 0 -226 Load-in_Time 0x0026 100 100 000 Old_age - 159 -240 Head flying hours 0x0001 100 100 001 Pre-fail - 0 - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log, version number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended off-line Completed 00% 4 - - diff --git a/www/examples/WD2500JB.txt b/www/examples/WD2500JB.txt deleted file mode 100644 index 04e963a8b2bbeff348eb643fcb2e4748c210356f..0000000000000000000000000000000000000000 --- a/www/examples/WD2500JB.txt +++ /dev/null @@ -1,192 +0,0 @@ -smartctl version 5.30 Copyright (C) 2002-4 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Device Model: WDC WD2500JB-32EVA0 -Serial Number: WD-WMAEH1156826 -Firmware Version: 15.05R15 -Device is: In smartctl database [for details use: -P show] -ATA Version is: 6 -ATA Standard is: Exact ATA specification draft version not indicated -Local Time is: Fri Jun 25 08:14:16 2004 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Offline data collection status: (0x84) Offline data collection activity was - suspended by an interrupting command from host. - Auto Offline Data Collection: Enabled. -Self-test execution status: ( 73) The previous self-test completed having - a test element that failed and the test - element that failed is not known. -Total time to complete Offline -data collection: (7608) seconds. -Offline data collection -capabilities: (0x7b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - Conveyance Self-test supported. - Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - No General Purpose Logging support. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 95) minutes. -Conveyance self-test routine -recommended polling time: ( 5) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate 0x000b 001 001 051 Pre-fail Always FAILING_NOW 2777 - 3 Spin_Up_Time 0x0007 125 120 021 Pre-fail Always - 4283 - 4 Start_Stop_Count 0x0032 100 100 040 Old_age Always - 133 - 5 Reallocated_Sector_Ct 0x0033 199 199 140 Pre-fail Always - 1 - 7 Seek_Error_Rate 0x000b 200 200 051 Pre-fail Always - 0 - 9 Power_On_Hours 0x0032 092 092 000 Old_age Always - 6545 - 10 Spin_Retry_Count 0x0013 100 100 051 Pre-fail Always - 0 - 11 Calibration_Retry_Count 0x0013 100 100 051 Pre-fail Always - 0 - 12 Power_Cycle_Count 0x0032 100 100 000 Old_age Always - 133 -194 Temperature_Celsius 0x0022 128 253 000 Old_age Always - 22 -196 Reallocated_Event_Count 0x0032 199 199 000 Old_age Always - 1 -197 Current_Pending_Sector 0x0012 200 200 000 Old_age Always - 13 -198 Offline_Uncorrectable 0x0012 200 200 000 Old_age Always - 0 -199 UDMA_CRC_Error_Count 0x000a 200 253 000 Old_age Always - 1 -200 Multi_Zone_Error_Rate 0x0009 200 155 051 Pre-fail Offline - 0 - -SMART Error Log Version: 1 -ATA Error Count: 50 (device log contains only the most recent five errors) - CR = Command Register [HEX] - FR = Features Register [HEX] - SC = Sector Count Register [HEX] - SN = Sector Number Register [HEX] - CL = Cylinder Low Register [HEX] - CH = Cylinder High Register [HEX] - DH = Device/Head Register [HEX] - DC = Device Command Register [HEX] - ER = Error register [HEX] - ST = Status register [HEX] -Timestamp = decimal seconds since the previous disk power-on. -Note: timestamp "wraps" after 2^32 msec = 49.710 days. - -Error 50 occurred at disk power-on lifetime: 1082 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 07 82 a9 ee e0 Error: - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 00 00 25 00 00 07 00 00 2825901.100 NOP [Abort queued commands] - 03 00 82 00 00 5f 67 00 2825901.100 CFA REQUEST EXTENDED ERROR CODE - 00 00 25 00 00 01 00 00 2825901.100 NOP [Abort queued commands] - 00 00 25 00 00 08 00 00 2825901.100 NOP [Abort queued commands] - 12 00 ee 00 00 5f a9 00 2825901.100 RECALIBRATE [RET-4] - -Error 49 occurred at disk power-on lifetime: 1082 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 07 82 a9 ee e0 Error: - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 00 00 25 00 00 07 00 00 2825899.350 NOP [Abort queued commands] - 12 00 ee 00 00 7f a9 00 2825899.350 RECALIBRATE [RET-4] - 00 00 25 00 00 08 00 00 2825899.350 NOP [Abort queued commands] - 00 00 25 00 00 08 00 00 2825899.350 NOP [Abort queued commands] - 12 00 ee 00 00 5f a9 00 2825899.350 RECALIBRATE [RET-4] - -Error 48 occurred at disk power-on lifetime: 1082 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 08 17 a9 ee e0 Error: - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 00 00 25 00 00 08 00 00 2825880.900 NOP [Abort queued commands] - 00 00 d6 00 00 77 ad 00 2825880.900 NOP [Abort queued commands] - 00 00 25 00 00 08 00 00 2825880.900 NOP [Abort queued commands] - 00 00 d0 00 00 5f 5c 00 2825880.900 NOP [Abort queued commands] - 00 00 35 00 00 08 00 00 2825880.900 NOP [Abort queued commands] - -Error 47 occurred at disk power-on lifetime: 1082 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 08 17 a9 ee e0 Error: - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 00 00 25 00 00 08 00 00 2825879.000 NOP [Abort queued commands] - 00 00 d6 00 00 77 ad 00 2825879.000 NOP [Abort queued commands] - 00 00 35 00 00 08 00 00 2825879.000 NOP [Abort queued commands] - 00 00 35 00 00 08 00 00 2825879.000 NOP [Abort queued commands] - 06 00 8a 00 00 4f 3b 00 2825879.000 [RESERVED] - -Error 46 occurred at disk power-on lifetime: 1082 hours - When the command that caused the error occurred, the device was doing SMART Offline or Self-test. - - After command completion occurred, registers were: - ER ST SC SN CL CH DH - -- -- -- -- -- -- -- - 40 51 08 c5 a8 ee e0 Error: - - Commands leading to the command that caused the error were: - CR FR SC SN CL CH DH DC Timestamp Command/Feature_Name - -- -- -- -- -- -- -- -- --------- -------------------- - 00 00 25 00 00 08 00 00 2825875.250 NOP [Abort queued commands] - 00 00 25 00 00 08 00 00 2825875.250 NOP [Abort queued commands] - 06 00 ba 00 00 f7 66 00 2825875.250 [RESERVED] - 00 00 35 00 00 10 00 00 2825875.250 NOP [Abort queued commands] - 06 00 ba 00 00 1f 66 00 2825875.250 [RESERVED] - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Short offline Completed: unknown failure 90% 1077 0xfff00000 -# 2 Short offline Completed without error 00% 1053 - -# 3 Short offline Completed without error 00% 1030 - -# 4 Short offline Completed without error 00% 1007 - -# 5 Short offline Completed without error 00% 983 - -# 6 Extended offline Completed without error 00% 961 - -# 7 Short offline Completed without error 00% 938 - -# 8 Short offline Completed without error 00% 914 - -# 9 Short offline Completed without error 00% 891 - -#10 Short offline Completed without error 00% 868 - -#11 Short offline Completed without error 00% 844 - -#12 Short offline Completed without error 00% 821 - -#13 Extended offline Completed without error 00% 799 - -#14 Short offline Completed without error 00% 775 - -#15 Short offline Completed without error 00% 752 - -#16 Short offline Completed without error 00% 728 - -#17 Short offline Completed without error 00% 705 - -#18 Short offline Completed without error 00% 682 - -#19 Short offline Completed without error 00% 659 - -#20 Extended offline Completed without error 00% 637 - -#21 Short offline Completed without error 00% 613 - - diff --git a/www/examples/WD800JD.txt b/www/examples/WD800JD.txt deleted file mode 100644 index 1c69e5f980f03c9217e799c74370fdd635ad720d..0000000000000000000000000000000000000000 --- a/www/examples/WD800JD.txt +++ /dev/null @@ -1,115 +0,0 @@ -[root@nemo-slave0560 ~]# /net/m0001/root/ballen/sm5/smartctl -a -d sat /dev/sda -smartctl version 5.37 [x86_64-unknown-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -=== START OF INFORMATION SECTION === -Model Family: Western Digital Caviar SE (Serial ATA) family -Device Model: WDC WD800JD-08LSA0 -Serial Number: WD-WMAM9H167762 -Firmware Version: 07.01D07 -User Capacity: 80,032,038,912 bytes -Device is: In smartctl database [for details use: -P show] -ATA Version is: 7 -ATA Standard is: Exact ATA specification draft version not indicated -Local Time is: Tue Jun 13 07:39:15 2006 CDT -SMART support is: Available - device has SMART capability. -SMART support is: Enabled - -=== START OF READ SMART DATA SECTION === -SMART overall-health self-assessment test result: FAILED! -Drive failure expected in less than 24 hours. SAVE ALL DATA. -See vendor-specific Attribute list for failed Attributes. - -General SMART Values: -Offline data collection status: (0x84) Offline data collection activity - was suspended by an interrupting command from host. - Auto Offline Data Collection: Enabled. -Self-test execution status: ( 73) The previous self-test completed having - a test element that failed and the test - element that failed is not known. -Total time to complete Offline -data collection: (2580) seconds. -Offline data collection -capabilities: (0x7b) SMART execute Offline immediate. - Auto Offline data collection on/off support. - Suspend Offline collection upon new - command. - Offline surface scan supported. - Self-test supported. - Conveyance Self-test supported. - Selective Self-test supported. -SMART capabilities: (0x0003) Saves SMART data before entering - power-saving mode. - Supports SMART auto save timer. -Error logging capability: (0x01) Error logging supported. - General Purpose Logging supported. -Short self-test routine -recommended polling time: ( 2) minutes. -Extended self-test routine -recommended polling time: ( 34) minutes. -Conveyance self-test routine -recommended polling time: ( 5) minutes. - -SMART Attributes Data Structure revision number: 16 -Vendor Specific SMART Attributes with Thresholds: -ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE - 1 Raw_Read_Error_Rate POSR-- 200 200 051 Pre-fail Always - 0 - 3 Spin_Up_Time PO---- 164 164 021 Pre-fail Always - 2775 - 4 Start_Stop_Count -O--CM 100 100 000 Old_age Always - 20 - 5 Reallocated_Sector_Ct PO--CM 129 129 140 Pre-fail Always FAILING_NOW 562 - 7 Seek_Error_Rate POSR-- 200 187 051 Pre-fail Always - 0 - 9 Power_On_Hours -O--CM 099 099 000 Old_age Always - 1211 - 10 Spin_Retry_Count PO--C- 100 253 051 Pre-fail Always - 0 - 11 Calibration_Retry_Count -O--C- 100 253 051 Old_age Always - 0 - 12 Power_Cycle_Count -O--CM 100 100 000 Old_age Always - 20 -190 Temperature_Celsius -O---M 082 037 045 Old_age Always In_the_past 18 -194 Temperature_Celsius -O---M 125 080 000 Old_age Always - 18 -196 Reallocated_Event_Count -O--CM 148 148 000 Old_age Always - 52 -197 Current_Pending_Sector -O--C- 200 200 000 Old_age Always - 0 -198 Offline_Uncorrectable ----C- 200 200 000 Old_age Offline - 0 -199 UDMA_CRC_Error_Count -OSRCM 200 200 000 Old_age Always - 0 -200 Multi_Zone_Error_Rate P--R-- 200 200 051 Pre-fail Offline - 0 - ||||||_ M self-preserving - |||||__ C event count - ||||___ R error rate - |||____ S speed/performance - ||_____ O updated online - |______ P prefailure warning - -SMART Error Log Version: 1 -No Errors Logged - -SMART Self-test log structure revision number 1 -Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error -# 1 Extended offline Completed: unknown failure 90% 1207 - -# 2 Extended offline Completed: unknown failure 90% 1187 - -# 3 Extended offline Completed: unknown failure 90% 1163 - -# 4 Extended offline Completed: unknown failure 90% 1139 - -# 5 Extended offline Completed: unknown failure 90% 1115 - -# 6 Extended offline Completed: unknown failure 90% 1091 - -# 7 Extended offline Completed: unknown failure 90% 1067 - -# 8 Extended offline Completed: unknown failure 90% 1043 - -# 9 Extended offline Completed: unknown failure 90% 1022 - -#10 Extended offline Completed: unknown failure 90% 998 - -#11 Extended offline Completed: unknown failure 90% 974 - -#12 Extended offline Completed without error 00% 943 - -#13 Extended offline Completed without error 00% 919 - -#14 Extended offline Completed without error 00% 895 - -#15 Extended offline Completed without error 00% 875 - -#16 Extended offline Completed without error 00% 857 - -#17 Extended offline Completed without error 00% 833 - -#18 Extended offline Completed without error 00% 809 - -#19 Extended offline Completed without error 00% 785 - -#20 Extended offline Completed without error 00% 761 - -#21 Extended offline Completed without error 00% 737 - - -SMART Selective self-test log data structure revision number 1 - SPAN MIN_LBA MAX_LBA CURRENT_TEST_STATUS - 1 0 0 Not_testing - 2 0 0 Not_testing - 3 0 0 Not_testing - 4 0 0 Not_testing - 5 0 0 Not_testing -Selective self-test flags (0x0): - After scanning selected spans, do NOT read-scan remainder of disk. -If Selective self-test is pending on power-up, resume after 0 minute delay. diff --git a/www/examples/atapi_cdrw_smt_a.html b/www/examples/atapi_cdrw_smt_a.html deleted file mode 100644 index 3e6cea3184ec067f6462097c24d5d00c78abf359..0000000000000000000000000000000000000000 --- a/www/examples/atapi_cdrw_smt_a.html +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>atapi_cdrw_smt_a</title> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-12 Copyright -(C) 2002-3 Bruce Allen</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is -http://smartmontools.sourceforge.net/</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device: ATAPI -CD-RW 48X16 Version: A.RZ</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device type: CD/DVD</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Thu May 15 -17:24:44 2003 EST</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device does not support SMART</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device does not support Error -Counter logging</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device does not support Self Test -logging</span> -</body> -</html> diff --git a/www/examples/ativ_36_smt_a.html b/www/examples/ativ_36_smt_a.html deleted file mode 100644 index c5dc6b2b326e1020fdd4e59f7aac524fb2b4c796..0000000000000000000000000000000000000000 --- a/www/examples/ativ_36_smt_a.html +++ /dev/null @@ -1,61 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>Atlas IV 36 WLS smartmontools output</title> -</head> -<body> -<span style="font-family: monospace;">Device: QUANTUM ATLAS IV 36 -WLS Version: 0A0A </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: 363930037828 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Sat May 3 -21:20:08 2003 EST </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device supports SMART and is -Disabled </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Enabled </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Sense: Ok! </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Current Drive -Temperature: 35 C </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log: </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -65535 -0 -0 -0 -0 -4.295 0 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -0 -0 -0 -0 -4.295 0 </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Non-medium error -count: 13</span> -</body> -</html> diff --git a/www/examples/bnch_DLT1.html b/www/examples/bnch_DLT1.html deleted file mode 100644 index efa6a371318df3b037a982309b3805b82d334752..0000000000000000000000000000000000000000 --- a/www/examples/bnch_DLT1.html +++ /dev/null @@ -1,67 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>benchmark tape systems DLT1</title> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-11 Copyright -(C) 2002-3 Bruce Allen </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is </span><a - class="moz-txt-link-freetext" - href="http://smartmontools.sourceforge.net/" - style="font-family: monospace;">http://smartmontools.sourceforge.net/</a><span - style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Device: BNCHMARK -DLT1 -Version: 391B </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: 0000052369<br> -Device type: tape</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Sun May 4 -11:53:27 2003 EST </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">device is NOT READY (media -absent, spun down, etc) </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">TapeAlert Supported </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">TapeAlert: Ok! </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log: </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -0 -0 -0 -0 -0 -0.000 0 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -0 -6 -6 -0 -5.920 0 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Warning: device does not support -Self Test Logging</span> -</body> -</html> diff --git a/www/examples/bnch_robot.html b/www/examples/bnch_robot.html deleted file mode 100644 index 5069b96191292ddc95c1c81cc18c20c685b3d7d5..0000000000000000000000000000000000000000 --- a/www/examples/bnch_robot.html +++ /dev/null @@ -1,64 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>Benchmark tape systems robot</title> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-11 Copyright -(C) 2002-3 Bruce Allen </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is </span><a - class="moz-txt-link-freetext" - href="http://smartmontools.sourceforge.net/" - style="font-family: monospace;">http://smartmontools.sourceforge.net/</a><span - style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Device: -STK -L20 -Version: 0207 </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: LLC02207812<br> -Device type: medium changer</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Sun May 4 -11:54:39 2003 EST </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Disabled or -Not Supported </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">TapeAlert Supported </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">TapeAlert Errors (C=Critical, -W=Warning, I=Informational): </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">[0x02] W: There is a problem with -the library mechanism. If problem persists, </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> call the library supplier -help line. </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">[0x0d] W: There is a potential -problem with the drive ejecting cartridges or </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> with the library mechanism -picking a cartridge from a slot. </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> 1. No action needs to be -taken at this time. </span><br style="font-family: monospace;"> -<span style="font-family: monospace;"> 2. If the problem persists, -call the library supplier help line. </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">[0x0e] W: There is a potential -problem with the library mechanism placing a </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> cartridge into a slot. </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> 1. No action needs to be -taken at this time. </span><br style="font-family: monospace;"> -<span style="font-family: monospace;"> 2. If the problem persists, -call the library supplier help line. </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">No Error counter log to report </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Warning: device does not support -Self Test Logging</span> -</body> -</html> diff --git a/www/examples/ddrs_39130_smt_a.html b/www/examples/ddrs_39130_smt_a.html deleted file mode 100644 index e920d71a1f8def59fe8d5930e2431dcfb3109511..0000000000000000000000000000000000000000 --- a/www/examples/ddrs_39130_smt_a.html +++ /dev/null @@ -1,82 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>ddrs_39130_smt_a.html</title> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-12 Copyright -(C) 2002-3 Bruce Allen</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is -http://smartmontools.sourceforge.net/</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> - </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device: -IBM -DDRS-39130D Version: DC1B</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: QE702689</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device type: disk</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Thu May 15 -16:51:27 2003 EST</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device supports SMART and is -Enabled</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Disabled or -Not Supported</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Health Status: OK</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> - </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log:</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -0 -0 -0 -0 -0 -4.295 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -0 -0 -4 -4 -4.295 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">verify: -0 -0 -0 -0 -0 -0.072 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> - </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Non-medium error -count: 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device does not support Self Test -logging</span> -</body> -</html> diff --git a/www/examples/hp_c5713a_smt_a.html b/www/examples/hp_c5713a_smt_a.html deleted file mode 100644 index 4c4beea98338e0e74f5eb441caec307f1bb102e2..0000000000000000000000000000000000000000 --- a/www/examples/hp_c5713a_smt_a.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>HP DDS-4 drive smartmontools output</title> -</head> -<body> -<pre wrap="">smartctl version 5.1-11 Copyright (C) 2002-3 Bruce Allen<br>Home page is <a - class="moz-txt-link-freetext" - href="http://smartmontools.sourceforge.net/">http://smartmontools.sourceforge.net/</a><br><br>Device: HP C5713A Version: H910<br>Local Time is: Thu May 1 23:26:38 2003 EEST<br>Temperature Warning Disabled or Not Supported<br>TapeAlert Supported<br>TapeAlert: Ok!<br><br>Error counter log:<br> Errors Corrected Total Total Correction Gigabytes Total<br> delay: [rereads/ errors algorithm processed uncorrected<br> minor | major rewrites] corrected invocations [10^9 bytes] errors<br>read: 0 0 0 2 0 0.000 0<br>write: 0 0 0 0 0 0.000 0<br>Warning: device does not support Self Test Logging</pre> -</body> -</html> diff --git a/www/examples/mam3184_smt_a.html b/www/examples/mam3184_smt_a.html deleted file mode 100644 index b2896462e43f6c797e65f83180f7db7b6debc1d4..0000000000000000000000000000000000000000 --- a/www/examples/mam3184_smt_a.html +++ /dev/null @@ -1,170 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <title>mam3184_smt_a.html</title> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-12 Copyright -(C) 2002-3 Bruce Allen</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is -http://smartmontools.sourceforge.net/</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device: FUJITSU -MAM3184MP Version: 0106</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: UKS0P2300CK0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device type: disk</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Thu May 15 -15:35:10 2003 EST</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device supports SMART and is -Enabled</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Enabled</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Health Status: OK</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Current Drive -Temperature: 42 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Drive Trip -Temperature: 65 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Manufactured in week 10 of year -2002</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Current start stop -count: 280 times</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Recommended start stop -count: 10000 times</span><br style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log:</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -0 -0 -0 -0 -0 -510.626 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -13 -0 -0 -0 -769.950 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Non-medium error -count: 855</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Self-test log</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Num -Test -Status -segment LifeTime LBA_first_err [SK ASC ASQ]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Description -number (hours)</span><br style="font-family: monospace;"> -<span style="font-family: monospace;"># 1 Background -long -Completed -- -980 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 2 Background short -Completed -- -788 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 3 Background -long -Completed -- -768 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 4 Background short -Completed -- -665 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 5 Background -long -Completed -- -635 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 6 Foreground -long -Completed -- -635 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 7 Foreground -long Interrupted (bus reset ?) - -634 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 8 Foreground -long Interrupted (bus reset ?) - -634 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 9 Foreground -long Interrupted (bus reset ?) -- -1 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#10 Foreground short -Completed -- -1 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#11 Background short -Completed -- -1 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Long (extended) Self Test -duration: 837 seconds [13.9 minutes]</span><br> -<tt><br> -</tt> -</body> -</html> diff --git a/www/examples/mam3184_smt_health.html b/www/examples/mam3184_smt_health.html deleted file mode 100644 index 37a41fb18685bd2b8a5c35ece46f9b56664d442a..0000000000000000000000000000000000000000 --- a/www/examples/mam3184_smt_health.html +++ /dev/null @@ -1,35 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <title>mam3184_smt_health.html</title> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-12 Copyright -(C) 2002-3 Bruce Allen</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is -http://smartmontools.sourceforge.net/</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Health Status: FAILURE -PREDICTION THRESHOLD EXCEEDED (FALSE) [asc=5d,ascq=ff]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Current Drive -Temperature: 42 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Drive Trip -Temperature: 65 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Manufactured in week 10 of year -2002</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Current start stop -count: 280 times</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Recommended start stop -count: 10000 times</span><br> -<tt><br> -</tt> -</body> -</html> diff --git a/www/examples/map3735_smt_a.html b/www/examples/map3735_smt_a.html deleted file mode 100644 index 928904f1bd0e06b1a78b85815f93ae34c24bff38..0000000000000000000000000000000000000000 --- a/www/examples/map3735_smt_a.html +++ /dev/null @@ -1,86 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> - <title>Fujitsu MAP 3735 smartmontools output</title> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-10 Copyright -(C) 2002-3 Bruce Allen </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is </span><a - class="moz-txt-link-freetext" - href="http://smartmontools.sourceforge.net/" - style="font-family: monospace;">http://smartmontools.sourceforge.net/</a><span - style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Device: FUJITSU -MAP3735NP Version: 0105 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: UPG0P2A00491 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Sat May 3 -21:22:09 2003 EST </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device supports SMART and is -Disabled </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Disabled or -Not Supported </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Sense: Ok! </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Current Drive -Temperature: 39 C </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Drive Trip -Temperature: 65 C </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Manufactured in week 40 of year -2002 </span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Current start stop -count: 14 times </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Recommended start stop -count: 10000 times </span><br style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log: </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -0 -0 -0 -0 -0 -810.959 0 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -0 -0 -0 -0 -72.300 0 </span><br - style="font-family: monospace;"> -<br style="font-family: monospace;"> -<span style="font-family: monospace;">Non-medium error -count: 27 </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">No self-tests have been logged </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Long (extended) Self Test -duration: 2872 seconds [47.9 minutes]</span> -</body> -</html> diff --git a/www/examples/st318451_smt_a.html b/www/examples/st318451_smt_a.html deleted file mode 100644 index fcbadaa880c2b67ad2e9431b3080bd83bb194176..0000000000000000000000000000000000000000 --- a/www/examples/st318451_smt_a.html +++ /dev/null @@ -1,185 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <title>st318451_smt_a</title> - <meta http-equiv="content-type" - content="text/html; charset=ISO-8859-1"> -</head> -<body> -<span style="font-family: monospace;">smartctl version 5.1-12 Copyright -(C) 2002-3 Bruce Allen</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Home page is -http://smartmontools.sourceforge.net/</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Device: SEAGATE -ST318451LW Version: 0003</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Serial number: -3CC01TTG000071033QEA</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device type: disk</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Local Time is: Thu May 15 -17:12:14 2003 EST</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Device supports SMART and is -Enabled</span><br style="font-family: monospace;"> -<span style="font-family: monospace;">Temperature Warning Enabled</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Health Status: OK</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Current Drive -Temperature: 34 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Drive Trip -Temperature: 65 C</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Error counter log:</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Errors Corrected Total -Total Correction -Gigabytes Total</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -delay: [rereads/ -errors algorithm -processed uncorrected</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -minor | major rewrites] corrected -invocations [10^9 bytes] errors</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">read: -21 -0 -0 -21 -21 -100.431 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">write: -0 -0 -0 -0 -0 -0.016 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">verify: -0 -0 -0 -0 -0 -0.010 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Non-medium error -count: 0</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">SMART Self-test log</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Num -Test -Status -segment LifeTime LBA_first_err [SK ASC ASQ]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> -Description -number (hours)</span><br style="font-family: monospace;"> -<span style="font-family: monospace;"># 1 Background -long -Completed -- -11 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 2 Background -long -Completed -- -11 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 3 Background short -Completed -- -11 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 4 Background short -Completed -- -10 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 5 Background short -Completed -- -6 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 6 Background -long -Completed -- -6 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 7 Background short -Completed -- -6 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 8 Background short -Completed -- -5 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"># 9 Background -long -Completed -- -3 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#10 Background short -Completed -- -3 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#11 Background short -Completed -- -2 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#12 Background short -Completed -- -0 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">#13 Background short -Completed -- -0 -- [- - -]</span><br - style="font-family: monospace;"> -<span style="font-family: monospace;"> </span><br - style="font-family: monospace;"> -<span style="font-family: monospace;">Long (extended) Self Test -duration: 587 seconds [9.8 minutes]</span><br> -<tt><br> -</tt> -</body> -</html> diff --git a/www/index.html b/www/index.html deleted file mode 100644 index c041dfc35e023eaba3feede6e7014b2bfb62f27c..0000000000000000000000000000000000000000 --- a/www/index.html +++ /dev/null @@ -1,1292 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> -<head> - <title>smartmontools Home Page (last updated $Date: 2006/09/01 17:12:49 $)</title> - <link rev="made" href="mailto:smartmontools-support@sourceforge.net" /> - <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> - <meta name="description" content="smartmontools Home Page" /> - <meta name="keywords" content="S.M.A.R.T., SMART, FreeBSD, Linux, NetBSD, Solaris, Windows, disk, monitor, monitoring" /> -</head> -<body> - -<!-- $Id: index.html,v 1.207 2006/09/01 17:12:49 guidog Exp $ --> - -<div align="center"> - <img src="smart_logo.gif" border="0" width="105" height="59" alt="SMART LOGO" /> - <br /> - <h1><font color="#3333ff">smartmontools Home Page</font></h1> -</div> - -<p>Welcome! This is the home page for the smartmontools package.</p> - -<font color="#ff0000"> -<b>NEWS:</b> -</font> -<ul> -<li><font color="#ff0000">Smartmontools 5.36 (stable) was released 2006-04-16, see -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/NEWS?revision=RELEASE_5_36&view=markup"> -NEWS</a> and -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?revision=RELEASE_5_36&view=markup"> -CHANGELOG</a> for details. -</font></li> -<li><font color="#ff0000">SourceForge CVS architecture has changed 2006-05-12, please read -notes <a href="#CVSInstall">below</a> if you install smartmontools from CVS.</font></li> -<li><font color="#ff0000">A Windows installer version of smartmontools 5.36 was released -2006-06-06, see <a href="#WindowsInstall">below</a>.</font></li> -</ul> -<font color="#ff0000"> -Please report problems (or success!) with the new release to the -<a href="http://lists.sourceforge.net/lists/listinfo/smartmontools-support"> -smartmontools-support mailing list</a>. -</font> - - -<p>The smartmontools package contains two utility programs -(<font color="#3333ff"><b>smartctl</b></font> and -<font color="#3333ff"><b>smartd</b></font>) to control and monitor storage -systems using the Self-Monitoring, Analysis and Reporting Technology -System (SMART) built into most modern ATA and SCSI hard -disks.  In many cases, these utilities will provide advanced warning -of disk degradation and failure.</p> - -<p>Smartmontools is originally derived from the Linux <a -href="http://sourceforge.net/projects/smartsuite/">smartsuite -package</a>, and includes support for ATA/ATAPI-3 to -7 disks and SCSI -disk and tape devices. It should run on any modern Darwin (Mac OSX), -Linux, FreeBSD, NetBSD, OpenBSD, Solaris, OS/2, eComStation or <a -href="#windows">Windows</a> system. Alternatively, it can also be run -from one of the <a href="#bootable">bootable CDs or floppies containing -smartmontools</a>.</p> - -<p>For printing convenience, everything except for the <a -href="#sampleoutput">example output</a> is on a single page.</p> - -<hr size="2" /> - -<ul> -<li><a href="http://www.linuxjournal.com/article.php?sid=6983"> -Monitoring Hard Disks with SMART (Linux Journal, January 2004, page 74)</a></li> -<li><a href="#howtodownload">How to download and install -smartmontools</a></li> -<li><a href="#PROBLEMS">Serious Problem Reports (system lockup, etc.)</a></li> -<li><a href="#FAQ">Frequently Asked Questions</a></li> -<li><a href="#scsi">SCSI disks and tapes (TapeAlert)</a></li> -<li><a href="#testinghelp">FireWire, USB, and SATA disks/tapes</a></li> -<li><a href="#differfromsmartsuite">How does smartmontools differ from -smartsuite?</a></li> -<li><a href="#references">Useful references on SMART and ATA/ATAPI-5, --6, and -7</a></li> -<li><a href="#sampleoutput">Example output from smartmontools</a> -<b>smartctl</b> utility</li> -<li><a href="http://smartmontools.cvs.sourceforge.net/smartmontools/">CVS -repository</a> and <a href="http://sourceforge.net/projects/smartmontools/">SourceForge's -Project Page</a></li> -<li>Mailing List <a href="http://lists.sourceforge.net/lists/listinfo/smartmontools-support">Information</a> -and <a href="http://sourceforge.net/mailarchive/forum.php?forum=smartmontools-support">Archives</a> -(Archive has <b>Search Box</b> in top left corner). <a href="#altmail">Alternative</a> (and usually up to date) archives.</li> -<li>Current <a href="man/smartctl.8.html">smartctl</a>, <a href="man/smartd.8.html">smartd</a>, and <a href="man/smartd.conf.5.html">smartd.conf</a> HTML man pages generated from CVS.</li> -</ul> - -<hr size="2" /> - -<b><a name="howtodownload"></a>How to download and install -smartmontools</b> - -<p>There are different ways to get and install -smartmontools.  You can use any of the procedures below -(the fourth is for Debian Linux only).  Just after "Method 6" below are -some instructions for trying out smartmontools once you have completed -the installation. The -<b><a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/INSTALL?view=markup"> -INSTALL</a></b> file contains additional information. -</p> -<b>First Method (Redhat/Fedora Linux) - Install from the RPM file</b> -<ul> -<li>Download the latest binary RPM file (<tt>*.rpm</tt>) from <a href="http://sourceforge.net/project/showfiles.php?group_id=64297">here</a>.  -Don't get the SRPM file (<tt>*.src.rpm</tt>).<br/> SuSE users: use one of the SuSE-specific RPM files.</li> -<li>Install it using RPM.  <i>You must be root to do this</i>: -<pre>su root (enter root password) -rpm -ivh smartmontools-5.32.i386.rpm</pre> -For most users, this is all that is needed.</li> -<li>If you receive an error message, you have probably previously -installed the <tt>smartsuite</tt> package, or RedHat's -<tt>kernel-utils</tt> package, which provide older versions of the -<tt>smartd</tt> and <tt>smartctl</tt> utilities.  In this case you -should use the <tt>--nodeps</tt> or <tt>--force</tt> arguments of rpm to -replace these two utilities: -<pre>rpm -ivh --nodeps --force smartmontools-5.32.i386.rpm</pre></li> -<li>If you want to remove the package (<tt>rpm -e smartmontools</tt>) -and your system does not have <tt>chkconfig</tt> installed, you may need -to use: -<pre>rpm -e --noscripts smartmontools</pre></li> -</ul> - -<b>Second Method (Linux/Solaris/FreeBSD/NetBSD/OpenBSD/Cygwin) - Install from the source tarball</b> -<ul> -<li>Download the latest source tarball from <a -href="http://sourceforge.net/project/showfiles.php?group_id=64297">here</a>. -Note: you probably want the most recent stable release. Stable releases have -even-numbered extensions, and unstable experimental releases have -odd-numbered extensions.</li> -<li>Uncompress the tarball: -<pre>tar zxvf smartmontools-5.32.tar.gz</pre></li> -<li>The previous step created a directory called <tt>smartmontools-5.32</tt> -containing the code.  Go to that directory, build, and install: -<pre>cd smartmontools-5.32 -./configure -make -make install -</pre></li> -<li> Note that the <tt>./configure</tt> step above is not possible for releases <=5.1-18, you -have to edit the Makefile by hand to change installation paths. For releases >=5.19, <tt>./configure</tt> -can take optional arguments. These optional arguments are fully explained in the -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/INSTALL?view=markup">INSTALL</a> -file. The most important one is <tt>--prefix</tt> to change the default installation directories.<p> -<i>Please note that the default installation location changed in versions >=5.31.</i> -If you don't pass any arguments to <tt>./configure</tt> all files will reside under -<b>/usr/local</b> to not interfere with files from your distribution. For more detailed -information please also refer to the -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/INSTALL?view=markup">INSTALL</a> -document. -</p> -</li> -<li>To compile from another directory (avoids overwriting virgin files from the smartmontools package) -replace <tt>./configure [options]</tt> by: -<pre> -mkdir objdir -cd objdir -../configure [options] -</pre></li> -<li>To install to another destination (useful for testing and to avoid overwriting an existing smartmontools installation) -replace <tt>make install</tt> by: -<pre> -make DESTDIR=/home/myself/smartmontools-test install -</pre> -Use a full path: <tt>~/smartmontools-test</tt> won't work. -</li> -<li>Unless the destination directory is your home directory (or a location that you have write permission) -only root can do <tt>make install</tt></li> -</ul> - -<b><a name="CVSInstall"></a> -Third Method (Darwin/FreeBSD/Linux/NetBSD/OpenBSD/Solaris/Cygwin) - Install from the CVS repository</b> -<ul> -<li><p><font color="#ff0000">Due to the new the SourceForge CVS -architecture, the hostname for CVS access has changed from -<tt>cvs.sourceforge.net</tt> to <tt>smartmontools.cvs.sourceforge.net</tt>. -To update a copy of smartmontools checked out before 2006-05-12, change all -the <tt>*/CVS/Root</tt> files accordingly. -</font></p></li> - -<li><p>One of the really cool things about CVS is that you can get -<i>any</i> version of the code you want, from the first release up the -the most current development version.  And it's trivial, because -each release is <u>tagged</u> with a name like -<tt>RELEASE_5_1_18</tt>.  You can see what the different names are -by looking at the <a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/"> -CVS repository</a>.  You'll see the tag names in the little scroll -window where it says "Show only files with tag".  All you need to -do to get the latest development code is -(but note that the development code may be unstable, and that the -documentation and code may be inconsistent):</p> - -<pre>cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools login (when prompted for a password, just press Enter) -cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools co sm5</pre></li> - -<li>To instead get the 5.1-16 release: - -<pre>cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools co -r RELEASE_5_1_16 sm5</pre></li> - -<li><p>This will create a subdirectory called <tt>sm5/</tt> containing the -code.  Go to that directory, build, and install:</p> -<pre>cd sm5 -./autogen.sh -./configure -make -make install -</pre> - -<ul> -<li>See notes under <b>Second method - install from source tarball</b> for different options to <tt>./configure</tt> -and other useful remarks.</li> -<li>Skip <tt>./autogen.sh</tt> and <tt>./configure</tt> for tagged releases -<= 5.1-18 (RELEASE_5_X_Y, where X = 0 or 1 and Y = 0 to 18).</li> -<li>If you get the current sources (<tt>cvs co</tt> with no arguments or do <tt>cvs up --A</tt>) then you <i>will</i> need those two additional steps.</li> -</ul></li> - -<li>To update your sources to the 5.1-18 release: -<pre>cd sm5 -cvs up -r RELEASE_5_1_18</pre></li> - -<li>To update any tagged release to the latest development code: - -<pre>cd sm5 -cvs up -A</pre></li> -</ul> - -<b>Fourth Method (Debian Linux) - Install the Debian package</b> -<ul> -<li> -The latest version of the smartmontools package in <i>.deb</i> format is -available at the <a href="http://packages.debian.org/unstable/utils/smartmontools.html">Debian smartmontools -package page</a>. -This package is for the (unreleased) <a href="http://www.debian.org/releases/">unstable</a> -distribution.</li> -<li>If you're running Debian <a -href="http://www.debian.org/releases/stable/">stable</a> please download a -backport to stable <a -href="http://www.backports.org/debian/pool/main/s/smartmontools/">here</a>. -These packages are provided by <a -href="http://www.backports.org">www.backports.org</a>.</li> -<li> -You can then install the package using: -<pre> -dpkg -i smartmontools_5.36-1_i386.deb -</pre> -If you prefer to fetch the packages using apt, please read the instructions at -<a href="http://www.backports.org/dokuwiki/doku.php?id=instructions">backports.org</a>. -</li> -</ul> - -<b><a name="CygwinInstall"></a> -Fifth Method (Windows with <a href="http://www.cygwin.com/">Cygwin</a> -installed) - Install the Cygwin package -</b> -<ul> -<li>Starting with CVS snapshot 2005-11-15, smartmontools is part of -the <a href="http://cygwin.com/packages/">Cygwin distribution</a>. -A list of available smartmontools packages and their contents is -<a href="http://cygwin.com/packages/smartmontools/">here</a>. -</li> -<li><p>To update your installation, click on the "Install or update now!" -link on the <a href="http://cygwin.com/">Cygwin web page</a>. -This downloads <tt>setup.exe</tt> to your system. -Then, run setup and answer all of the questions. -Select smartmontools package in the "Utils" category.</p> -</li> -<li><p>The optional source package (<tt>smartmontools-*-src.tar.bz2</tt>) -can be used to build both the Cygwin and the Windows binary packages -on Cygwin. -Refer to the file <tt>/usr/share/doc/Cygwin/smartmontools-*.README</tt> -for details.</p> -</li> -</ul> - -<b><a name="WindowsInstall"></a> -Sixth Method (Windows) - Install the Windows package -</b> -<ul> -<li>Download and run the latest smartmontools -<a href="http://nsis.sourceforge.net/Main_Page">NSIS</a>-installer -(<tt>*.win32-setup.exe</tt>) from -<a href="http://sourceforge.net/project/showfiles.php?group_id=64297">here</a>. -<ul> -<li>The default install type "Full" creates start menu shortcuts -including an uninstaller, and adds the install directory to the PATH variable. -</li> -<li>Select install type "Extract files only" to disable these extra -components. -</li> -<li><font color="#ff0000"> -Virus scanners occasionally produce false positive virus reports for -NSIS-installers, see the -<a href="http://nsis.sourceforge.net/NSIS_False_Positives">NSIS False Positives page</a>. -If this is the case for the smartmontools installer, please send a report to the -<a href="mailto:smartmontools-support@lists.sourceforge.net">smartmontools-support -mailing list</a>. -</font></li> -</ul> -</li> -<li><p>Or download and unzip the latest smartmontools Windows archive -(<tt>*.win32.zip</tt>) from -<a href="http://sourceforge.net/project/showfiles.php?group_id=64297">here</a>. -</p></li> -<li><p>More recent (and probably unstable) Windows test releases build from CVS -snapshots are available <a href="http://franke.dvrdns.org/smartmontools/">here</a>. -</p></li> -</ul> - - -<b>After installing it using Method 1, 2, 3, 4 or 5 above, you can read the -man pages, and try out the commands:</b> - -<pre> -man smartd.conf -man smartctl -man smartd -/usr/sbin/smartctl -s on -o on -S on /dev/hda (only root can do this) -/usr/sbin/smartctl -a /dev/hda (only root can do this)</pre> - -<p>Note that the default location for the manual pages are -<tt>/usr/share/man/man5</tt> and <tt>/usr/share/man/man8</tt>.  If -"<tt>man</tt>" doesn't find them, then you may need to add -<tt>/usr/share/man</tt> to your <tt>MANPATH</tt> environment -variable.</p> - -<p>The Windows package (see Method 6 above) provides preformatted man pages -in <tt>*.html</tt> and <tt>*.txt</tt> format.</p> - -<hr size="2" /> - -<a name="PROBLEMS"></a><b>Serious Problem Reports</b> -<p>If a serious problem gets reported to us, it gets added to the -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/WARNINGS?view=markup"> -WARNINGS</a> file in smartmontools. So far there are only a few problem systems listed.</p> - -<hr size="2" /> - -<a name="FAQ"></a><b>Frequently Asked Questions</b> - -<p>If your question is not here, please <a href="mailto:smartmontools-support@lists.sourceforge.net">email -me</a>.</p> - -<ul> -<li><b>Is the smartmontools File Download/Mail List/Mail Archive/CVS server broken?</b> - -<p>SourceForge is a free service, which supports a very large number of -users and projects. Please check the <a -href="http://sourceforge.net/docman/display_doc.php?docid=2352&group_id=1"> -SourceForge Site Status Page</a> to see the maintenance schedule and to -learn if SourceForge is experiencing unscheduled system outages or other -problems.</p> - -<p> -<a name="altmail"></a>Alternative mailing-list archives are provided by -<a href="http://gmane.org/find.php?list=smartmontools">Gmane</a> and MARC (<a -href="http://marc.theaimsgroup.com/?l=smartmontools-support">smartmontools-support</a> -and <a -href="http://marc.theaimsgroup.com/?l=smartmontools-database">smartmontools-database</a>).</p> - -</li> - -<li><b>What do I do if I have problems, or need support?  Suppose -I want to become a developer, or suggest some new extensions?</b> - -<p>First, search the support mailing list archives to see if your -question has been answered. Instructions are in the following -paragraph. If you don't find an answer there, then please send an -email to the <a -href="http://lists.sourceforge.net/mailman/listinfo/smartmontools-support">smartmontools-support -mailing list</a>. This is a moderated forum: you are not -required to subscribe to the list in order to post your question. -</p> - -<p>To search the email archives, first go to the <a -href="http://sourceforge.net/mailarchive/forum.php?forum=smartmontools-support"> -mailing list archive</a>. In the top left corner you will see a -search box: use <b>Mailing List</b> as the type of search. This tool -works very well.</p> - -<p>Note that from time to time SourceForge has mailing list problems -and you'll get a message telling you that <i>Either your mailing list -name was misspelled or your mailing list has not been archived yet. If -this list has just been created, please retry in 2-4 hours</i>. If -this happens, you'll have to try again later. Or use <a -href="#altmail">alternative</a> (and usually up to date) email -archives. -</p> - -</li> - -<li><b>What are the future plans for smartmontools?</b> - -<p>My plan is that smartmontools-5.x will support ATA/ATAPI-5 -disks.  Eventually, we'll do smartmontools-6.x to support -ATA/ATAPI-6 disks, smartmontools-7.x for the ATA/ATAPI-7 standard, and -so on.  The "x" will denote revision level, as bugs get found and -fixed, and as enhancements get added.  If it's possible to maintain -backwards compatibility, that would be nice, but I don't know if it will -be possible or practical.</p></li> - -<li><b>Why are you doing this?</b> - -<p>My research group at U. Wisconsin - Milwaukee runs a <a -href="http://www.lsc-group.phys.uwm.edu/beowulf/medusa/">beowulf -cluster</a> with 600 ATA-5 and -6 disks (300 IBM and 300 -Maxtor).  We have more than 50 TB of data stored on the -system.  I also help out with a <a -href="http://pandora.aei.mpg.de/merlin/"> cluster</a> at the Albert -Einstein Institute that has 540 IBM ATA-6 disks (65 TB -total). It's nice to have advanced warning when a disk is going to -fail.</p></li> - -<li><b>Where can I find distribution-specific bug reports?</b> -<p> -The smartmontools package supports a number of different operating -systems. Some of those operating systems are also distributed by -multiple sources, and some of these maintain a database of bug -reports. Here are links: -</p> - -<ul> -<li><a href="https://bugzilla.redhat.com/bugzilla/buglist.cgi?bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=NEEDINFO&bug_status=MODIFIED&bug_status=CLOSED&field0-0-0=product&type0-0-0=substring&value0-0-0=smartctl&field0-0-1=component&type0-0-1=substring&value0-0-1=smartctl&field0-0-2=short_desc&type0-0-2=substring&value0-0-2=smartctl&field0-0-3=status_whiteboard&type0-0-3=substring&value0-0-3=smartctl&field0-0-4=product&type0-0-4=substring&value0-0-4=smartd&field0-0-5=component&type0-0-5=substring&value0-0-5=smartd&field0-0-6=short_desc&type0-0-6=substring&value0-0-6=smartd&field0-0-7=status_whiteboard&type0-0-7=substring&value0-0-7=smartd&field0-0-8=product&type0-0-8=substring&value0-0-8=smartsuite&field0-0-9=component&type0-0-9=substring&value0-0-9=smartsuite&field0-0-10=short_desc&type0-0-10=substring&value0-0-10=smartsuite&field0-0-11=status_whiteboard&type0-0-11=substring&value0-0-11=smartsuite&field0-0-12=product&type0-0-12=substring&value0-0-12=smartmontools&field0-0-13=component&type0-0-13=substring&value0-0-13=smartmontools&field0-0-14=short_desc&type0-0-14=substring&value0-0-14=smartmontools&field0-0-15=status_whiteboard&type0-0-15=substring&value0-0-15=smartmontools">Redhat/Fedora Bugzilla Database (Linux)</a> </li> -<li><a href="http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=pkg&data=smartmontools&archive=no">Debian Bug Database (Linux)</a></li> -<li><a href="http://bugs.gentoo.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=product&type0-0-0=substring&value0-0-0=smartmontools&field0-0-1=component&type0-0-1=substring&value0-0-1=smartmontools&field0-0-2=short_desc&type0-0-2=substring&value0-0-2=smartmontools&field0-0-3=status_whiteboard&type0-0-3=substring&value0-0-3=smartmontools">Gentoo Bug Database (Linux)</a></li> -<li><a href="http://www.netbsd.org/cgi-bin/query-pr-list.pl?text=smartctl&state=open&state=feedback&state=analyzed&state=suspended">NetBSD smartctl bug database</a></li> -<li><a href="http://www.netbsd.org/cgi-bin/query-pr-list.pl?text=smartd&state=open&state=feedback&state=analyzed&state=suspended">NetBSD smartd bug database</a></li> -<li><a href="http://www.netbsd.org/cgi-bin/query-pr-list.pl?text=smartmontools&state=open&state=feedback&state=analyzed&state=suspended">NetBSD smartmontools bug database</a></li> -</ul> -<p> -If you can provide additional distribution or OS-specific bug-database links, please send an email to smartmontools-support. -</p></li> - -<li><b>I see some strange output from smartctl. What does it mean?</b> - -<p>The raw SMART attributes (temperature, power-on lifetime, and so -on) are stored in vendor-specific structures.  Sometime these are -strange.  Hitachi disks (at least some of them) store power-on -lifetime in minutes, rather than hours (see next question below).  IBM disks (at least some -of them) have three temperatures stored in the raw structure, not just -one.  And so on.  If you find strange output, or unknown -attributes, please send an email to <a href="http://lists.sourceforge.net/mailman/listinfo/smartmontools-support"> -smartmontools-support</a> and we'll help you try and figure it -out.</p></li> - - -<li><b>Why is my disk temperature s reported by smartd as 150 Celsius?</b> -<p> -It's not. Please read the end of the <b>smartd</b> man page (NOTES). -For example, in the message: <br/> -<b>'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93'</b><br/> -the value given is the 'Normalized' not the 'Raw' Attribute value (the -disk temperature in this case is about 22 Celsius). The -<b>'-R'</b> and <b>'-r'</b> Directives modify this behavior, so that -the information is printed with the Raw values as well, for example: -<br/> -<b>'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]'</b><br/> -Here the Raw values are the actual disk temperatures in Celsius. The -way in which the Raw values are printed, and the names under which the -Attributes are reported, is governed by the various -<b>'-v Num,Description'</b> Directives described in the <b>smartd</b> -man page. Please see the <b>smartctl</b> manual page for further -explanation of the differences between Normalized and Raw Attribute -values. -</p> -</li> - -<li><b>What are the operating system requirements?</b> -<p> -Please see the first section of the -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/INSTALL?view=markup"> -INSTALL</a> file. -</p> -</li> - -<li><b>What Attributes does smartmontools not yet recognize?</b> - -<p>From Maxtor disks (99), (100), and (101). These are not used by -Maxtor in SMART revision 5. They will be used in SMART revision 6, -but the engineering group has not yet decided what to monitor with -these Attributes. -</p> -</li> - -<li><b>My Maxtor/Hitachi/Fujitsu disk is only a few days old, yet smartctl reports its age (Attribute 9) as thousands of hours!</b> - -<p>On recent disks, Maxtor has started to use Attribute 9 to -store the power-on disk lifetime in minutes rather than hours. In this case, use -the:<br/> -<tt>-v 9,minutes</tt><br/> -option to correctly display hours and minutes. -</p> -<p>Some models of Fujitsu disks use Attribute 9 to store -the power-on disk lifetime in seconds. In that case, use the:<br/> -<tt>-v 9,seconds</tt><br/> -option to correctly display hours, minutes and seconds.</p> -</li> - -<li><b>The power-on timer (Attribute 9 raw value) on my Maxtor disk acts strange.</b> - -<p>There are three related problems with Maxtor's SMART firmware: -</p> - -<p> -<b>1 - </b> On some Maxtor disks, the raw value of Attribute 9 (Power -On Time) is <i>supposed</i> to be minutes. But it advances at an -unpredictable rate, always more slowly than one count per minute. -This is because when the disk is in idle mode, the counter stops -advancing. This is only supposed to happen in standby mode. This -will be corrected in Maxtor product lines released after October 2004. -</p> - -<p> -<b>2 - </b> In Maxtor disks that use the raw value of Attribute 9 as a -minutes counter, only two bytes (of the six available) are used to -store the raw value. So it resets to zero once every 65536=2^16 -minutes, or about once every 1092 hours. This is fixed in all Maxtor -disks manufactured after July 2003, where the raw value was extended -to four bytes. -</p> - -<p> -<b>3 - </b> In Maxtor disks that use the raw value of Attribute 9 as a -minutes counter, the hour time-stamps in the self-test and ATA error -logs are calculated by right shifting 6 bits. This is equivalent to -dividing by 64 rather than by 60. As a result, the hour time stamps -in these logs advance 7% more slowly than they should. Thus, if you -do self-tests once per week at the same time, instead of the -time-stamps being 168 hours apart, they are 157 hours apart. This is -also fixed in all Maxtor disks manufactured after July 2003. -</p> -</li> - -<li><b>The time stamps in the self-test log of my Western Digital (WD) disk -don't correspond to the power-on time when the test was run</b> - -<p> -The self-test log timestamps in many WD disks roll back to zero every -1092 hours (65536 minutes). This problem is due to a WD firmware bug. -The power-on lifetime in hours is correctly stored in Attribute -9. However when the power-on lifetime is calculated for self-test log -entries, the lifetime in minutes is put into a 16-bit register then -divided by 60. The 16-bit register overflows and wraps around every -1092 hours. -</p> - -<p>For WD drives that exhibit this firmware bug, the relationship between -Attribute 9's raw value (H) and the time-stamps in the self-test log (h) are given by:<br/> -Let H = power on hours as shown by Attribute 9 (correct)<br/> -Let M = 60*H (power on minutes, correct)<br/> -Let m = M mod 65536 (incorrect value of power on minutes)<br/> -Let h = m/60 (incorrect value of power on hours, shown in self-test log) -</p> -</li> - -<li><b>The (normalized) WORST Attribute values of my Western Digital -(WD) disk are <u>larger</u> than the (normalized) CURRENT Attribute -values</b> -<p>Western Digital firmware initializes SMART Attributes 10, 11, and -199 after either 120 spin-ups or 8 power-on hours. Until that time, -they have the uninitialized value 253. -</p> -</li> - -<li><b>Where can I find manufacturer-specific disk-testing -utilities?</b> - -<p>A good listing of such utilities can be found <a -href="http://www.benchmarkhq.ru/english.html?/be_hdd2.html">here</a>. -Unfortunately most of these are for MS operating systems, but most can -be run from an MS-DOS boot disk or from the UBCD (Ultimate Boot CD, -see <a href="#bootable">below</a>). -</p> - -<p>Note: if you do run one of these utilities, and it identifies the -meanings of any SMART Attributes that are not known to smartmontools, -please report them to the mailing list above. -</p> - -<p>These utilities have an important role to fill. If your disk has -bad sectors (for example, as revealed by running self-tests with -smartmontools) and the disk is not able to recover the data from those -sectors, then the disk will <i>not</i> automatically reallocate those -damaged sectors from its set of spare sectors, because -forcing the reallocation to take place may entail some loss of data. -Because the commands that force such reallocation are -<i>Vendor Specific</i>, most manufactuers provide a utility for this -purpose. It may cause data loss but can repair damaged sectors (at -least, until it runs out of replacement sectors). -</p> -</li> - -<li><b>When I run <tt>smartd</tt>, the SYSLOG <tt>/var/log/messages</tt> -contains messages like this:</b> -<pre>smartd: Reading Device /dev/sdv -modprobe: modprobe: Can't locate module block-major-65</pre> - -<p>This is because when <tt>smartd</tt> starts, if there is no -configuration file, it looks for all ATA and SCSI devices to monitor -(matching the pattern <tt>/dev/hd[a-t]</tt> or -<tt>/dev/sd[a-z]</tt>).  The log messages appear because your -system doesn't have most of these devices.</p> - -<p>The solution is simple: use the <tt>smartd</tt> configuration file -<tt>/etc/smartd.conf</tt> to specify which devices to monitor.</p></li> - -<li><b>What's the story on IBM SMART disks?</b> - -<p>Apparently some of the older SMART firmware on IBM disks can -interfere with the regular operation of the disk.  If you have this -problem, here are some links:<br/> -<a href="http://www.geocities.com/dtla_update/">Geocities Site</a>, -<a href="http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215">IBM Site #1</a>, -<a href="http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215">IBM Site #2</a><br/> -to an IBM Firmware Upgrade that fixes the problem. -</p></li> - -<li><b>How can I check that the package hasn't been tampered with?</b> - -<p>Since the <tt>smartmontools</tt> utilities run as root, you might -be concerned about something harmful being embedded within -them. Starting with release 5.19 of <tt>smartmontools</tt>, the .rpm -files and tarball have been GPG signed. The tarball's fingerprint is -given in a file on the release page with a name like -<tt>smartmontools-5.32.tar.gz.asc</tt>. Please verify these using -the -<a href="SmartmontoolsSigningKey_2005.txt">Smartmontools GPG Signing Key (current)</a> -<a href="SmartmontoolsSigningKey.txt">Smartmontools GPG Signing Key (before 2005)</a> -</p></li> - -<li><b>Is there a bootable standalone CD or floppy that contains smartmontools?</b> - -<p>If you have a system that is showing signs of disk trouble (for -example, it's unbootable and the console is full of disk error -messages) it can be handy to have a version of smartmontools that can -be run off of a bootable CD or floppy to examine the disk's SMART data and run -self-tests. This is also useful if you want to run Captive Self-Tests -(the <b><tt>-<font size="+2">C</font></tt></b> option of -<b><tt>smartctl</tt></b> ) on disks that can not easily be unmounted, -such as those hosting the Operating System files. Or you can use -this to run <tt>smartctl</tt> on computers that don't use Linux as the -day-to-day operating system.</p> - -<p><a name="bootable"></a>Here is a list of such bootable CDs:</p> -<ul> -<li><a href="http://www.lnx-bbc.org/">LNX-BBC Bootable CD</a> </li> -<li><a href="http://www.stresslinux.org/">Stresslinux Bootable CD</a></li> -<li><a href="http://www.tux.org/pub/people/kent-robotti/looplinux/rip/">RIP (Recovery Is Possible) Bootable CD</a></li> -<li><a href="http://www.sysresccd.org/Main_Page">SystemRescueCd</a></li> -<li><a href="http://www.gpstudio.com/stux/">STUX Bootable CD</a></li> -<li><a href="http://www.knopper.net/knoppix/index-en.html">Knoppix Bootable CD</a> -(Version 3.6 contains smartmontools 5.32, older versions contain -<a href="#differfromsmartsuite">smartsuite</a>)</li> -<li><a href="http://www.grml.org/">Grml Bootable CD</a> -(Knoppix and Debian based CD without KDE and OpenOffice but about 800 -<a href="http://www.grml.org/features/list.html">packages</a> added)</li> -<li><a href="http://smartlinux.sourceforge.net/">S.M.A.R.T. Linux</a> -(a bootable FLOPPY containing smartmontools!)</li> -<li><a href="http://ubcd.sourceforge.net/">Ultimate Boot CD</a> -(The "Full" version of UBCD 3.0 contains -<a href="http://www.inside-security.de/insert_en.html">INSERT</a> with smartmontools 5.32 -<a href="http://ubcd.sourceforge.net/insert/start.html#TOCChanges">added</a>)</li> -<li><a href="http://www.911cd.net/forums/lofiversion/index.php/t13459.html">Smartctl Plugin</a> -for <a href="http://www.nu2.nu/pebuilder/">BartPE bootable live windows CD</a></li> -</ul> -<p> -Please let me know if there are others, and I will add them to this -list. -</p> -</li> - -<li><b>Can I monitor ATA disks behind SCSI RAID controllers?</b> - -<p> -From release 5.1-18, smartmontools fully supports 3ware SCSI RAID -controllers that use ATA disks internally. To pass commands through -the 3ware controller, use the smartmontools <b>-d 3ware,N</b> option -or Directive. -</p> -<p> -3ware Linux device drivers (<tt>3w-xxxx</tt>) versions <b>1.02.00.036</b> -and earlier do not return the SMART HEALTH STATUS (smartmontools -<b>-H</b>) of the disks. In addition, the ENABLE AUTOMATIC OFFLINE and -ENABLE ATTRIBUTE AUTOSAVE commands (smartmontools <b>-o on</b> and -<b>-S on</b>) can not be passed through the driver to the disks. -Later driver versions support <i>all</i> of these commands. You may: -</p> -<ul> -<li>use version <b>1.02.00.037</b> or greater of the <tt>3w-xxxx</tt> driver, or</li> -<li><a href="3w-xxxx.txt">patch</a> earlier 3ware <tt>3w-xxxx</tt> drivers so that -these commands reach the disks, or</li> -<li> use an <b>unpatched</b> earlier <tt>3w-xxxx</tt> driver (which won't pass these -commands to the disks but will instead print -harmless warning messages to SYSLOG).</li> -</ul> -<p> -To see if your system's <tt>3w-xxxx</tt> driver has been patched, give the -command:<br/> -<tt>smartctl -H -S on -o on -d 3ware,? /dev/sd?</tt> -<br/> -If you -have an unpatched kernel, you'll see warning messages prompting you to -patch the kernel.</p> -<p> -The 3ware <tt>3w-xxxx</tt> version 1.02.00.037 driver first appeared -in kernel version 2.6.0-test5-bk11 on 24 September 2003 and in kernel -version 2.4.23-bk2 on 3 December 2003. It was officially released on -the 3ware web site on December 19, 2003 as part of their -driver/firmware/utility package version 7.7.0. -</p> - -<p> -Note (added 29 July 2004): starting with smartmontools (experimental) -release 5.33, one can also access SMART data from drives behind 3ware -controllers using the (character) devices /dev/twe0-15. This should -work correctly even with older versions of the 3w-xxxx driver. One can -also access SMART data from drives behind 3ware 9000-series -controllers (3w-9xxx driver) using the (character) devices -/dev/twa0-15. -</p> - -</li> - -<li><a name="windows"></a><b>Does it work on Windows?</b> - -<p>Yes, finally it does. A windows port of smartctl 5.26 by -<a href="http://sourceforge.net/users/chrfranke/">Christian Franke</a> -was first checked in 2004/02/23 on CVS branch -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/?pathrev=RELEASE_5_26_WIN32_BRANCH"> -RELEASE_5_26_WIN32_BRANCH</a> and has been merged to the CVS trunk later.</p> - -<p>The <a href="http://www.cygwin.com/">Cygwin</a> environment can be -used to built both Cygwin and Windows (using <a href="http://www.mingw.org/">MinGW</a>) -versions of smartctl and smartd. -Installation instructions for binary distributions can be found -<a href="#CygwinInstall">here</a> and <a href="#WindowsInstall">here</a>.</p> - -</li> - -<li><b>Why did the release version scheme change?</b> - -<p>It was non-standard. So with the move to GNU Autoconf and GNU -Automake it changed from 5.X-Y (where X and Y are one or more digits) -to 5.Y. Starting with the first release, and moving forward in time, the releases are -numbered as follows:<br/> -<tt> -5.0-1, -5.0-2, -..., -5.0-45, -5.1-1, -..., -5.1-18, -5.19, -5.20, -... -</tt> -</p> -</li> - -<li><a name="FAQ-database"></a><b>My ATA drive is not in the smartctl/smartd database. Does this break anything? How do I get it added?</b> -<p> - If your drive is not in the database, then the - <i>names</i> of the Attributes (displayed in the <tt>ATTRIBUTE_NAME</tt> column of - <tt>smartctl -A /dev/hd?</tt>) and the <i>format</i> of the the raw Attribute - values shown in the <tt>RAW_VALUE</tt> column may be incorrect. This - is mostly cosmetic: the essential drive health monitoring/testing - functionality of <b>smartmontools</b> does <i>not</i> depend upon the - database. -</p> - -<p> -<b> - If your drive is not in the database, pleaes check <a - href="http://sourceforge.net/project/showfiles.php?group_id=64297">here</a> - to be sure that you are using the latest smartmontools release. Each - new release has additional drives added to the database. Please do - not submit a new drive for the database without checking to see if it - is already in the database of the current smartmontools release - version. -</b> -</p> - -<p> -<b> If your drive is not in the database of the current release,</b> - to have it added to the database, first use the command:<br/> - <tt>smartctl -t short /dev/hd?</tt><br/> to run a short self-test on - the drive, and wait a few minutes for the test to complete. Then - email the entire output from:<br/> <tt>smartctl -a /dev/hd?</tt><br/> - to <a - href="https://lists.sourceforge.net/lists/listinfo/smartmontools-database">smartmontools-database</a> - as a plain-text ASCII email attachment (file type: ".txt"). The timestamp - in the self-test log will help us to determine whether Attribute 9 is - being used to store the lifetime in hours, minutes, or seconds. -</p> -<p> -If you need to use any of the vendor-specific display options - (<tt>-v</tt> options) with the drive, or if any of the Attributes are - behaving strangely, please include that information as well. -</p> -</li> - -<li><b>My ATA drive is failing its self-tests, but its SMART health status is 'PASS'. What's going on?</b> - -<p> -If your ATA drive supports self-tests, you should run them on a -regular basis, for example one per week: -<br/><tt>smartctl -t long /dev/hd?</tt><br/> -After the test has completed, you should examine the results with: -<br/><tt>smartctl -l selftest /dev/hd?</tt><br/> -</p> - -<p> -If the drive fails a self-test, but still has 'PASS' SMART health -status, this usually means that there is a corrupted sector on the -disk, which can not be read. If the disk were able to read that -sector of data, even once, then the disk firmware would mark the -sector as 'bad' and then allocate a spare sectors to replace it. But -if the disk can't read the sector even once, then it won't reallocate -the sector, in hopes of being able, at some time in the future, to -read the data from it. See <a -href="http://smartmontools.sourceforge.net/BadBlockHowTo.txt">BadBlockHowTo</a> -for instructions about how to force this sector to reallocate (Linux -only). -</p> -<p> -The disk still has passing health status because the firmware has not -found other signs of trouble, such as a failing servo. -</p> -<p> -Such disks can often be repaired by using the disk manufaturer's 'disk -evaluation and repair' utility. Beware: this may force reallocation -of the lost sector and thus corrupt or destroy any file system on the -disk. See <a -href="http://smartmontools.sourceforge.net/BadBlockHowTo.txt">BadBlockHowTo</a> -for generic Linux instructions. -</p> - -</li> - -<li><b>smartd is warning that my ATA disk has unreadable or uncorrectable or pending sectors. What's going on?</b> - -<p> -Disk drives store data in blocks (sectors) of 512 bytes. Each 512 -bytes has additional bytes appended to it (usually 40 to 60) which are -used internally by the disk firmware for error checking/detection and -correction. These are called ECC bytes. -</p> -<p> -Sometimes the data in a sector gets corrupted. This can happen -because a speck of dust scratched the disk, or because the disk was -powered down while writing data to that sector, or for other reasons. -Usually the ECC bytes can be used to correct the corrupted data. -However if the ECC bytes are inconsistent or can't be used to correct -the bad data, then the 512 bytes of data are lost. Such a sector is -called unreadable or uncorrectable. -</p> -<p> -If your disk has an unreadable sector, this means that some of your -data can't be retrieved. You can force the disk to replace the -unreadable sector with a spare good sector, but only at the price of -losing the 512 bytes of data forever. -</p> -<p> -Disks with uncorrectable sectors can often be repaired by using the -disk manufaturer's 'disk evaluation and repair' utility (see previous -FAQ entry). Beware: this may force reallocation of the lost sector -and thus corrupt or destroy any file system on the disk. See <a -href="http://smartmontools.sourceforge.net/BadBlockHowTo.txt">BadBlockHowTo</a> -for generic Linux instructions. -</p> -<p> -Normally when an uncorrectable sector is found, the disk puts this -onto a 'pending sector list' to indicate that it should be replaced -with a spare good sector. However this replacement won't take place -until either the disk can read the data on the bad sector, or is -commanded to write new data to that bad sector. -</p> - -</li> - - -<li><b>My computer's BIOS has a SMART enable/disable setting. What -does it do, and how should I set it?</b> -<p> -Some type of BIOS can check the SMART health status of a disk at -bootup: the equivalent of '<tt>smartctl -H /dev/hd?</tt>'. This one-time check on -bootup is done if the BIOS SMART setting is set to 'ENABLE', and is -not done if the setting is set to 'DISABLE'. -</p> - -<p> -If this one-time check is done, and the disk's health status is found -to be 'FAIL', then typically the BIOS will display an error message -and refuse to boot the machine. -</p> - -<p> -For the proper functioning of smartmontools, either BIOS setting may -be used. -</p> -</li> - - -<li><b>My Fedora Core Linux system displays the startup message: smartd [FAILED]</b> -<p> -Fedora Core is distributed with a smartd configuration file -/etc/smartd.conf that monitors the first IDE disk /dev/hda. If this -device does not exist (or lacks SMART capability) you will get the -error message above. Look in SYSLOG (/var/log/messages) for -additional details about what is going wrong. -</p> -<p> -The solution: If your system has only SCSI disks, or has IDE disk(s) -on a non-primary controller, just edit /etc/smartd.conf to reflect the -correct location of the drive(s). Please also read the 'smartd.conf' -man page for additional information. -</p> -</li> - -<li><b>Attribute 194 (Temperature Celsius) behaves strangely on my Seagate disk</b> -<p> -Some Seagate disks store the current temperature Celsius in both the -RAW and NORMALIZED Attribute 194 values, and the maximum lifetime -temperature in Celsius in the WORST value. Since cooler is better, -this means that in this case, <i>lower</i> NORMALIZED Attribute values -are farther from failure, and that over time the WORST Attribute -values get <i>larger</i>, not <i>smaller</i> (as with other -Attributes). -</p> -</li> - -<li><b>What's this smartctl message mean?: Warning: ATA error count 9 inconsistent with error log pointer 5</b> -<p> -The ATA error log is stored in a circular buffer, and the ATA -specifications are unambiguous about how the entries should be -ordered. This warning message means that the disk's firmware does not -strictly obey the ATA specification regarding the ordering of the -error log entries in the circular buffer. Smartmontools will correct -for this oversight, so this warning message can be safely ignored by -users. (On the other hand, firmware engineers: please read the ATA -specs more closely then fix your code!). -</p> -</li> - -<li><a name="FAQ-win-ata-as-scsi"></a><b>On Windows, smartctl aborts -with the message "...SMART_GET_VERSION failed". What is going wrong?</b> -<p> -A failing -<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Storage_r/hh/Storage_r/k307_2a8bcdd5-f85d-4228-b59c-bba2cc3211ee.xml.asp"> -SMART_GET_VERSION</a> call means that the device driver does not -implement the I/O controls (see <a href="#FAQ-win-ioctl">below</a>) -to access ATA SMART functionality. -</p> -<p> -Some Windows drivers for (S)ATA controllers are implemented as SCSI -class drivers. This is usually the case for drivers which support RAID. -Unfortunately, such drivers do not support the ATA specific SMART I/O -controls. -</p> -</li> - -<li><a name="FAQ-win-ioctl"></a><b>On Windows, smartctl prints the -message: "...Log Read failed: Function not implemented". What is -going wrong?</b> -<p> -This means that the device driver does not support the command -SMART READ LOG. -<u>The message does not indicate a hard disk problem!</u> -It does also not mean that the disk itself does not support SMART -logs. It may still be possible to read the logs with a Linux version -of smartmontools run from some <a href="#bootable">bootable CD</a>. -</p> -<p> -To access ATA SMART functionality on Windows, smartmontools uses the -I/O control calls -<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Storage_r/hh/Storage_r/k307_8c974d08-3752-4442-82a5-cc13835ba482.xml.asp"> -SMART_RCV_DRIVE_DATA</a> and -<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Storage_r/hh/Storage_r/k307_2b043284-934c-4440-a4a4-6078f1bc845d.xml.asp"> -SMART_SEND_DRIVE_CMD</a>. -These calls were available since Win95 OSR2. -An example program from Microsoft can be found -<a href="http://download.microsoft.com/download/winddk/sample3/9x/W9X/EN-US/SmartApp.exe"> -here</a> (the related KB article 208048 is no longer available). -</p> -<p> -Starting with NT4, these calls do more restrictive parameter checks. -In particular, the command codes for SMART READ LOG and ABORT SELF-TEST -are not accepted. To perform these functions, smartmontools uses the -undocumented functions SCSIOP_ATA_PASSTHROUGH (NT4) or -IOCTL_IDE_PASS_THROUGH (2000/XP) instead. -An example program using these calls can be found -<a href="ftp://ftp.heise.de/pub/ct/listings/0207-218.zip">here</a>, -a related newsgroup thread is -<a href="http://groups.google.com/group/microsoft.public.development.device.drivers/browse_frm/thread/e9763262823e11d1"> -here</a>. -</p> -Unfortunately, these undocumented functions are not implemented in -most vendor specific ATA device drivers. Smartctl prints a -"Function not implemented" message in this case. -<p> -A new I/O control call -<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/Storage_r/hh/Storage_r/k307_e93738e1-b773-452b-8776-854f9c616967.xml.asp"> -IOCTL_ATA_PASS_THROUGH</a> is available since Win2003 and XP SP2. -It should be supported by most new drivers. Experimental code using -this call was added 2006-04-27 and will appear in smartmontools -release 5.37. -</p> -</li> -</ul> - -<hr size="2" /><a name="scsi"></a><b>SCSI disks and tapes -(TapeAlert)</b> -<p>Smartmontools for SCSI disks and tapes (including medium changers) is -discussed on a separate <a href="smartmontools_scsi.html">page</a>. -</p> - -<hr size="2" /><a name="testinghelp"></a><b>FireWire, USB, and SATA -disks/systems</b> -<p>As for USB and FireWire (ieee1394) disks and tape drives, the news -is not good. They appear to Linux as SCSI devices but their -implementations do not usually support those SCSI commands needed by -smartmontools. The ieee1394 consortium recently certified the <span - style="font-style: italic;">first</span> external enclosure (containing -a ATA disk and a protocol bridge) as being compliant to the relevant -standards. Such devices have already been on the market for about 3 -years and they tend to only support the bare minimum of commands -needed for device operation (i.e. SMART support is an unsupported -extra).<br /> -</p> -<p>Smartmontools should work correctly with SATA drives under both -Linux 2.4 and 2.6 kernels, <i>if</i> you use the standard IDE drivers -in <tt>drivers/ide</tt>. If you use the new <tt>libata</tt> drivers, -it won't work correctly because <tt>libata</tt> doesn't yet support -the needed ATA-passthrough ioctl() calls. Jeff Garzik, the -<tt>libata</tt> developer, says that this support will be added to -libata in the future. When this happens, we'll add support to -smartmontools for a new SATA/libata device type <tt>'-d sata'</tt>. -Typically, to force an SATA disk to run using the standard -(non-libata) drivers, you must use the BIOS to select "legacy mode" -for the controller. If the IDE driver doesn't support your particular -SATA controller, or the controller doesn't have a legacy interface, -then only libata can be used. Unless the hard disk controller on the -system motherboard is Intel, VIA or nVidia, standard IDE drivers may -not work -</p> -<p>Note: an unofficial <a -href="http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&safe=off&threadm=2yY8S-4ps-33%40gated-at.bofh.it&rnum=1&prev=/groups%3Fq%3DLinville%2520ioctls%2520SMART%26num%3D100%26hl%3Den%26lr%3D%26ie%3DUTF-8%26safe%3Doff%26sa%3DN%26tab%3Dwg"> -patch to libata</a> that allows smartmontools to be used -with the standard '-d ata' device type was posted to the linux -kernel mailing list at the end of August 2004. The patch is included in the -<a href="http://www.kernel.org/pub/linux/kernel/people/jgarzik/libata/">libata-dev -patchset</a> that can be applied to a recent Linux kernel (>= 2.6.9). With a -SATA disk driven by a libata driver, smartmontools can now be used by specifying both the -device type 'ata' and the SCSI device corresponding to this disk, for example, -<tt>smartctl -i -d ata /dev/sda</tt>. -The patch is still under development and it is probably best to make sure that -the disk is idle before trying smartmontools. -</p> - - -<hr size="2" /><a name="differfromsmartsuite"></a><b>How does -smartmontools differ from smartsuite?</b> - -<p>The smartsuite code was originally developed as a Senior Thesis by -Michael Cornwell at the Concurrent Systems Laboratory (now part of the -<a href="http://ssrc.soe.ucsc.edu/">Storage Systems Research -Center</a>), Jack Baskin School of Engineering, University of -California, Santa Cruz. -You can find some information about the original smartsuite project here: -<a href="http://www.ucsc.edu/news_events/press_releases/archive/99-00/09-99/smart_software.htm">Press Release 1</a>, -<a href="http://www.santa-cruz.com/archive/1999/September/22/local/stories/5local.htm">Press Release 2</a>, -<a href="http://www.ucsc.edu/currents/99-00/09-27/smart.html">Press Release 3</a>. -</p> - -<p> -According to <a href="http://csl.cse.ucsc.edu/">SSRC</a> -smartsuite is no longer maintained; the last release was in 2001. -</p> - -<p>Smartmontools was derived directly from smartsuite.  It differs -from smartsuite in that it supports the ATA/ATAPI-5 standard.  So -for example <tt>smartctl</tt> from smartsuite has no facility for -printing the SMART self-test logs, and doesn't print timestamp -information in the most usable way.  The <tt>smartctl</tt> utility -in smartmontools has added functionality for this (<tt>-q, -l selftest,-S, --T, -v and -m</tt> options), updated documentation, and also fixes small -technical bugs in smartsuite. [One example: smartsuite does not actually use the -ATA SMART RETURN STATUS command to find out the health status of a disk. It instead tries to infer this from the -SMART Attribute values.]  See the -<a href="http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup">CHANGELOG</a> -file in CVS for a summary of what's been done.  The <tt>smartd</tt> -utility differs from the smartsuite <tt>smartd</tt> in major ways.  -First, it prints somewhat more informative error messages to the syslog. -  Second, on startup it looks for a configuration file -<tt>/etc/smartd.conf</tt>, and if <tt>smartd</tt> finds this file, it -monitors the list of devices therein, rather than querying all IDE and -SCSI devices on your system.  (If the configuration file does not -exist, then it does query all IDE and SCSI devices.)  Also, it's -a well-behaved daemon and doesn't leave open file descriptors and other -detrius behind.  In addition, the <tt>smartmontools</tt> version of -<tt>smartd</tt> can be instructed (via Directives in the configuration -file) to monitor for changes in a number of different disk properties: -the SMART status, failure or prefailure attributes going below -threshold, new errors appearing in the ATA Error Log or the SMART -Self-Test Log, and so on. <tt>smartd</tt> can also send an email warning or run a -user-specified executable if it detects a problem with the disk. -</p> - -<p>The other principle difference is that smartmontools is an -OpenSource development project, meaning that we keep the files in CVS, -and that other developers who wish to contribute can commit changes to -the archive. If you would like to contribute, please write to to <a -href="http://lists.sourceforge.net/mailman/listinfo/smartmontools-support">smartmontools-support</a>.</p> - -<p>But the bottom line is that the code in smartmontools is derived -directly from smartsuite and is similar.  The smartsuite package -can be found <a href="http://sourceforge.net/projects/smartsuite/">here</a>.</p> - -<hr size="2" /><a name="references"></a><b><big>Useful references on -SMART and the ATA/ATAPI standards</big></b> - -<p><big>If you are having trouble understanding the output of smartctl -or smartd, please first read the manual pages installed on your -system:</big></p> - -<pre> -man 8 smartctl -man 8 smartd -man 5 smartd.conf -</pre> - -<p> -Here are on-line versions of the smartmontools man pages:<br/> <a -href="man/smartctl.8.html">smartctl manual page</a><br/> <a -href="man/smartd.8.html">smartd manual page</a><br/> <a -href="man/smartd.conf.5.html">smartd.conf manual page</a><br/> Note -that these are the manual pages for the <b><i>current version</i></b> -of smartmontools in the developers CVS repository; they might not -correspond to the (possibly older) version of smartmontools installed -on <i>your</i> system. So the manual pages installed on your system -should be regarded as definitive for your installation.</p> - -<p><big>If you'd like to know more about SMART, then the following -references may be helpful:</big></p> - -<ul> -<li><a href="http://www.linuxjournal.com/article.php?sid=6983">Monitoring Hard Disks with SMART (Linux Journal, Jan 2004)</a></li> -<li><a href="http://lea-linux.org/cached/index/Hardware-hard_plus-smart.html">Soyez Smart (Francais) from GNU Linux Magazine France n�68,</a></li> -<li><a href="http://www.linux-user.de/ausgabe/2004/10/056-smartmontools/">Vorbeugen statt Crash (Deutsch)</a> -from <a href="http://www.linux-user.de/ausgabe/2004/10">LinuxUser 2004/10</a></li> -<li><a href="http://www.linux-magazine.com/issue/49/Monitoring_Hard_Disks_with_smartmontools.pdf">Crash Prevention -(English version of above)</a> from <a href="http://www.linux-magazine.com/issue/49">Linux Magazine Dec 2004</a></li> -<li><a href="http://www.wikipedia.org/">Wikipedia</a> articles about SMART: -<a href="http://en.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology">English</a>, -<a href="http://de.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology">Deutsch</a>, -<a href="http://es.wikipedia.org/wiki/SMART">Español</a>, -<a href="http://fr.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology">Français</a>, -<a href="http://it.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology">Italiano</a>, -<a href="http://ja.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology">Japanese</a>, -<a href="http://nl.wikipedia.org/wiki/S.M.A.R.T.">Nederlands</a>, -<a href="http://pl.wikipedia.org/wiki/S.M.A.R.T._%28informatyka%29">Polski</a>, -<a href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_SMART">Russian</a>, -<a href="http://sk.wikipedia.org/wiki/S.M.A.R.T">Slovenčina</a> -</li> - <li>The <a href="http://www.t13.org/project/d1321r3-ATA-ATAPI-5.pdf"> ATAPI/ATA-5 -Revision 3 specification</a> (start with Section 8.41)</li> - <li>The <a href="http://www.t13.org/docs2002/d1410r3b.pdf"> ATAPI/ATA-6 -Revision 3b specification</a></li> - <li>The ATAPI/ATA-7 specification (Draft 4b) - <a href="http://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf">Volume 1 (has SMART documentation)</a>, - <a href="http://www.t13.org/docs2004/d1532v2r4b-ATA-ATAPI-7.pdf">Volume 2</a>, - <a href="http://www.t13.org/docs2004/d1532v3r4b-ATA-ATAPI-7.pdf">Volume 3</a></li> - <li>The <a href="http://www.t13.org/docs2005/D1699r1f-ATA8-ACS.pdf">ATAPI/ATA-8 Command Set -specification (Draft 1f)</a></li> - <li><a href="http://www.t13.org/#FTP_site">Other revisions -of the ATAPI/ATA Specs</a></li> -<li>SCSI References: -<ul> - <li>The <a href="http://www.t10.org">homepage of the T10 project</a>.</li> - <li>The <a href="ftp://ftp.t10.org/t10/drafts/s2/">SCSI-2 draft</a> by the T10 project.</li> - <li>See also other subdirectories <a href="ftp://ftp.t10.org/t10/drafts/">here</a>.</li> -</ul> -</li> -<li> - The original SMART specification is SFF-8035i from the <a href="http://www.sffcommittee.com/ns/"> - Small Form Factors (SFF) Committee</a>.  - <ul> - <li> - Here is the SFF <a href="ftp://ftp.seagate.com/sff/INF-8035.TXT"> "link"</a> - (they have "expired" the document). - </li> - <li> - Version 1.0 of <a href="ftp://ftp3.ds.pg.gda.pl/people/macro/S.M.A.R.T./SFF-8035i.pdf"> - SFF-8035i "Self-Monitoring, Analysis and Reporting Technology (S.M.A.R.T.)". </a> - </li> - <li> - Revision 2.0 of <a href="ftp://ftp3.ds.pg.gda.pl/people/macro/S.M.A.R.T./8035R2_0.PDF"> - SFF-8035i "Self-Monitoring, Analysis and Reporting Technology (S.M.A.R.T.)". </a> - </li> - <li> - Revision 1.4 of <a href="ftp://ftp3.ds.pg.gda.pl/people/macro/S.M.A.R.T./8055.PDF"> - SFF-8055i "S.M.A.R.T. Applications Guide for the ATA and SCSI Interfaces" </a> - </li> - </ul> -</li> -<li>From the <a href="http://cmrr.ucsd.edu/smart/">UCSD SMART Project</a>: -<ul> - <li><a href="http://cmrr.ucsd.edu/smart/tech_papr/HamerlySmartPaper.pdf">Bayesian -Approaches to Failure Prediction for Disk Drives</a></li> - <li><a href="http://cmrr.ucsd.edu/smart/tech_papr/SmtPapTransReliFinalWeb.pdf">Improved -Disk-Drive Failure Warnings</a></li> - </ul> - </li> - <li>From the Seagate Corporation: - <ul> -<!-- <li><a href="http://www.seagate.com/newsinfo/docs/disc/drive_reliability.pdf" target="_blank"> -Estimating Drive Reliability in Desktop Computers and Consumer Electronics Systems</a></li> --> - <li><a href="http://www.seagate.com/docs/pdf/whitepaper/enhanced_smart.pdf" target="_blank">Enhanced SMART - Get SMART For Reliability</a></li> - <li><a href="http://www.seagate.com/docs/pdf/whitepaper/smart_u8.pdf" target="_blank">Playing it SMART</a></li> - <li><a href="http://www.seagate.com/docs/pdf/whitepaper/Enhanced_DST_Tech_Paper.pdf" target="_blank">Enhanced Drive Self-Test</a></li> - </ul> - </li> - <li><u>Specifying Reliability in the Disk Drive Industry: No More -MTBF's</u>, Jon G. Elerath (IBM Storage Systems Division) in -<i>Proceedings of the IEEE 2000 Annual Reliability and Maintainability -Symposium, pg 194, 0-7803-5848-1/00/$10.00.</i></li> - <li><a href="http://smartlinux.sourceforge.net/smart/">Zbigniew Chlondowski's SMART Information Site.</a> -This includes a useful list of <a href="http://smartlinux.sourceforge.net/smart/attributes.php">Attributes -and their meanings.</a> -</li> -</ul> - -<hr size="2" /><a name="sampleoutput"></a><b>Example output -from smartmontools smartctl utility:</b> - -<ul> - <li><a href="examples/MAXTOR-0.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM</li> - <li><a href="examples/MAXTOR-1.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (has failing SMART status - reallocated sector count)</li> - <li><a href="examples/MAXTOR-2.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (has had failing SMART test in the past. Look at the Seek Error Rate)</li> - <li><a href="examples/MAXTOR-7.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (has failing SMART status, some failed self-tests)</li> - <li><a href="examples/MAXTOR-8.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (has failing SMART status - calibration retry count)</li> - <li><a href="examples/MAXTOR-9.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (has failing SMART status - calibration retry count)</li> - <li><a href="examples/MAXTOR-10.txt">MAXTOR 4K080H4</a> 80 GB 5400 RPM (failing self-tests. Note Current_Pending_Sector raw value and Uncorrectable (UNC) read errors)</li> - <li><a href="examples/MAXTOR-3.txt">MAXTOR 6L080J4</a> 80 GB 7200 RPM</li> - <li><a href="examples/MAXTOR-4.txt">MAXTOR 6L080J4</a> 80 GB 7200 RPM</li> - <li><a href="examples/Maxtor-5.txt">Maxtor 98196H8</a> 80 GB 5400 RPM</li> - <li><a href="examples/MAXTOR-6.txt">Maxtor 4R080J0</a> Note: Attribute 9 (lifetime) stored in minutes!</li> - <li><a href="examples/IC35L120AVVA07-0-0.txt">IBM IC35L120AVVA07 (GXP 120 series)</a> 120 GB 7200 RPM (note 3 temperatures)</li> - <li><a href="examples/IC35L120AVVA07-0-1.txt">IBM IC35L120AVVA07 (GXP 120 series)</a> 120 GB 7200 RPM (note 3 temperatures)</li> - <li><a href="examples/IC35L120AVV207-0.txt">IBM IC35L120AVV207 (GXP 180 series)</a> 120 GB 7200 RPM (note 3 temperatures)</li> - <li><a href="examples/IC35L120AVV207-1.txt">IBM IC35L120AVV207 (GXP 180 series)</a> (failing SMART status and self-tests)</li> - <li><a href="examples/HITACHI_DK23BA-20-0.txt">HITACHI_DK23BA-20</a> Hitachi 20 GB Laptop Disk</li> - <li><a href="examples/HITACHI_DK23AA-12B.txt">HITACHI_DK23AA-12B</a> Really sick failing Hitachi Laptop Disk</li> - <li><a href="examples/TOSHIBA-0.txt">TOSHIBA MK2018GAS</a> Toshiba 20 GB Laptop Disk</li> - <li><a href="examples/TOSHIBA-MK6021GAS.txt">TOSHIBA MK6021GAS</a> Toshiba 60 GB Laptop Disk (note 3 temperatures)</li> - <li><a href="examples/FUJITSU1.txt">Fujitsu MHR2040AT</a> Fujitsu Laptop Disk (has failing SMART status - write error count)</li> - <li><a href="examples/FUJITSU_MHR2020AT.txt">Fujitsu MHR2020AT</a> Fujitsu Laptop Disk (has failing SMART status and self-tests)</li> - <li><a href="examples/WD2500JB.txt">Western Digital WD2500JB</a> Western Digital Disk (failing SMART status and self-tests)</li> - <li><a href="examples/WD800JD.txt">Western Digital WD800JD</a> Western Digital Disk (failing SMART status - too may reallocated sectors, and self-tests)</li> -</ul> - -<hr size="2" /> - -Maintained by: <a href="mailto:smartmontools-support@lists.sourceforge.net">Bruce Allen</a><br /> -Copyright (C) 2002-5 Bruce Allen<br /> -Last updated: <tt>$Date: 2006/09/01 17:12:49 $</tt><br /> -CVS tag: <tt>$Id: index.html,v 1.207 2006/09/01 17:12:49 guidog Exp $</tt> - -<hr size="2" /> - -<div align="center">Hosted by</div> - -<div align="center"><a href="http://sourceforge.net/"><img style="border:0;width=:88px;height:31px" - src="http://sourceforge.net/sflogo.php?group_id=64297&type=5" alt="SourceForge.net" /></a></div> - -<br /> - -<div align="center"><a href="http://validator.w3.org/check/referer"><img style="border:0;width=:88px;height:31px" - src="http://www.w3.org/Icons/valid-xhtml10.png" alt="Valid XHTML 1.0!" /></a></div> - -<br /> - -<div align="center"><a href="http://validator.w3.org/check?uri=http%3A%2F%2Fsmartmontools.sourceforge.net"> -Validate XHTML 1.0 Transitional.</a></div> - -<br /> - -<div align="center"><a href="http://validator.w3.org/checklink?uri=http%3A%2F%2Fsmartmontools.sourceforge.net&hide_type=all&depth=&check=Check">Check/Validate all links on this page.</a></div> - -</body> -</html> diff --git a/www/script b/www/script deleted file mode 100755 index fa7cd8c224367b6aba467f81fc960da4efc80375..0000000000000000000000000000000000000000 --- a/www/script +++ /dev/null @@ -1,24 +0,0 @@ -#! /bin/bash - -# This is a script to wrap smartctl output into http:// displayable form -# It requires a filename as input, and produces an file with a .html extension as output - -if [ $# -ne 1 ] ; then - echo This script requires one a file as input - exit 1 -fi - -model=`grep "Device Model" $1 | awk '{print $3}' ` - -# see if file name in use -let i=0 -while [ -f $model-$i.html ] ; do - let i+=1 -done - -filename=$model-$i -echo -e "<pre><tt>\n" > $filename.html -cat $1 >> $filename.html -echo -e "</tt></pre>\n" >> $filename.html - -echo created file $filename.html diff --git a/www/smart_logo.gif b/www/smart_logo.gif deleted file mode 100644 index 16a179c824bb4205d117f371eec0500eaf4d4c50..0000000000000000000000000000000000000000 Binary files a/www/smart_logo.gif and /dev/null differ diff --git a/www/smartmontools_scsi.xml b/www/smartmontools_scsi.xml deleted file mode 100644 index 77ad48d63212f6ec25b7c3a28f1d02242a7500ad..0000000000000000000000000000000000000000 --- a/www/smartmontools_scsi.xml +++ /dev/null @@ -1,1347 +0,0 @@ -<?xml version='1.0' encoding='ISO-8859-1'?> -<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" > - -<!-- -This is DocBook XML that can be rendered into a single HTML page with a -command like 'xmlto html-nochunks <this_file_name>'. It can -also be rendered into multi-page HTML (drop the "-nochunks") or pdf, -ps, txt, etc. ---> - -<article id="index"> - <articleinfo> - <title>Smartmontools for SCSI devices</title> - <author> - <firstname>Douglas</firstname> - <surname>Gilbert</surname> - <affiliation> - <address> - <email>dgilbert at interlog dot com</email> - </address> - </affiliation> - </author> - <authorinitials>dpg</authorinitials> - <pubdate>2006-06-24</pubdate> - - <revhistory> - <revision> - <revnumber>1.5</revnumber> - <date>2006-06-24</date> - <authorinitials>dpg</authorinitials> - <revremark> - device type sat - </revremark> - </revision> - <revision> - <revnumber>1.4</revnumber> - <date>2006-05-08</date> - <authorinitials>dpg</authorinitials> - <revremark> - 5.38 update, SATA, SAS - </revremark> - </revision> - <revision> - <revnumber>1.3</revnumber> - <date>2004-09-25</date> - <authorinitials>dpg</authorinitials> - <revremark> - error counter descriptions, error events log page - </revremark> - </revision> - <revision> - <revnumber>1.2</revnumber> - <date>2004-05-27</date> - <authorinitials>dpg</authorinitials> - <revremark> - reorganise, details in appendix, version 5.31 - </revremark> - </revision> - <revision> - <revnumber>1.1</revnumber> - <date>2003-10-13</date> - <authorinitials>dpg</authorinitials> - <revremark> - freebsd, timestamp - </revremark> - </revision> - <revision> - <revnumber>1.0</revnumber> - <date>2003-05-26</date> - <authorinitials>dpg</authorinitials> - <revremark> - first cut - </revremark> - </revision> - </revhistory> - - <copyright> - <year>2003</year> - <year>2004</year> - <year>2005</year> - <year>2006</year> - <holder>Douglas Gilbert</holder> - </copyright> - - <legalnotice> - <para> - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.1 - or any later version published by the Free Software Foundation; - with no Invariant Sections, with no Front-Cover Texts, and with - no Back-Cover Texts. - </para> - <para> - For an online copy of the license see - <ulink url="http://www.fsf.org/copyleft/fdl.html"> - <literal>www.fsf.org/copyleft/fdl.html</literal></ulink>. - </para> - - </legalnotice> - - <abstract> - <para> - This article describes how smartmontools interacts with SCSI - storage devices (including tapes). Smartmontools is a SMART - utility toolset. <acronym>SMART</acronym> is an acronym for - Self-Monitoring, Analysis and Reporting Technology. Smartmontools - is available for the these operating systems: Darwin (Mac OS X - but with no SCSI support yet), FreeBSD, Linux, NetBSD, OpenBSD, - Solaris and Windows. - </para> - </abstract> - </articleinfo> - -<!-- -<toc></toc> ---> - - <sect1 id="intro"> - <title>Introduction</title> -<para> -Smartmontools controls and monitors storage devices using the -Self-Monitoring, Analysis and Reporting Technology -(<acronym>SMART</acronym>) system. This toolset was originally built -for the Linux operating system and has been ported to Darwin for -Mac OS X (no SCSI support yet), FreeBSD, NetBSD, OpenBSD, Solaris -and Windows. -This article describes how smartmontools interacts with SCSI devices. -Passing reference is also made to devices that use the SCSI command -set such as USB mass storage devices and IEEE1394 devices that use -the "sbp2" protocol. In many situations SATA disks are accessed -using a (partial) SCSI command set. -</para> -<para> -The primary web site for smartmontools is at -<ulink url="http://smartmontools.sourceforge.net"> -<literal>smartmontools.sourceforge.net</literal></ulink> from which the -latest versions (both source and binaries) can be obtained. Smartmontools -grew out of the now dormant <emphasis>smartsuite</emphasis> project which -is still available on its sourceforge site. The smartmontools main page -concentrates on ATA devices. -This article supplies some SCSI specific information for -those users of smartmontools that wish to monitor SCSI storage devices. -</para> -<para> -This document outlines the features found in smartmontools -version 5.38 that are relevant to SCSI disks and tape drives. -This document was last altered on 24th May 2006. -</para> -</sect1> - -<sect1 id="overv"> - <title>Overview of Smartmontools</title> -<para> -Smartmontools is made up of two executable programs, a configuration file -and online documentation (on Unix systems in the form of "man" pages). -The two executable programs are: -<itemizedlist> -<listitem><para><command>smartctl</command>: a command line utility -</para></listitem> -<listitem><para><command>smartd</command>: a daemon program providing a -monitoring service -</para></listitem> -</itemizedlist> -</para> -<para> -SCSI disks and tape drives allow self tests of their media, often monitor -the temperature of the device, maintain error counters and report when -various failure prediction thresholds are exceeded. To view the information -available try a command like: <command>smartctl -a /dev/sda</command>. If -<acronym>SMART</acronym> reporting has not been turned on for this disk -then use this command -first: <command>smartctl -s on /dev/sda</command>. [For operating systems -other than Linux replace <filename>/dev/sda</filename> with a SCSI disk -device name.] -</para> -<para> -The <command>smartd</command> daemon program is a service typically started -when a machine boots up. In can monitor multiple disks (both ATA and SCSI). -In Unix systems its configuration file can -be found <filename>/etc/smartd.conf</filename>. It sends alerts to the -system logs and can be configured to email system administrators when -pending failures are reported. -</para> -</sect1> - -<sect1 id="oses"> - <title>Operating Systems</title> -<para> -Smartmontools was originally written for Linux. Since then it has been -ported to various other Unix based system and Windows. The names of -SCSI disk and tape devices vary. Here is a summary: - -<table frame="all"><title>SCSI device names in various systems</title> -<tgroup cols="4" align="left" colsep="1" rowsep="1"> -<thead> -<row> -<entry/> -<entry>disks</entry> -<entry>tapes</entry> -<entry>Notes</entry> -</row> -</thead> -<tbody> -<row> -<entry><command>Linux</command></entry> -<entry><filename>/dev/sd[a-z]</filename></entry> -<entry><filename>/dev/[n]st[0-9]</filename></entry> -</row> -<row> -<entry><command>FreeBSD</command></entry> -<entry><filename>/dev/da[0-9]</filename></entry> -<entry><filename>/dev/[n|e]sa[0-9]</filename></entry> -</row> -<row> -<entry><command>NetBSD</command></entry> -<entry><filename>/dev/sd[0-9]</filename></entry> -<entry><filename>/dev/enrst[0-9]</filename></entry> -</row> -<row> -<entry><command>Solaris</command></entry> -<entry><filename>/dev/rdsk/c?t?d?s?</filename></entry> -<entry><filename>/dev/rmt/*</filename></entry> -</row> -<row> -<entry><command>Windows</command></entry> -<entry><filename>/dev/scsi[0-9][0-f]</filename></entry> -<entry><filename>/dev/scsi[0-9][0-f]</filename></entry> -<entry>ASPI adapter:0-9, ID:0-15, <filename>/dev/</filename> optional -</entry> -</row> -</tbody> -</tgroup> -</table> -</para> -<para> -The above list is a simplification of course. In Linux there can be multiple -drive letters followed by a partition number (1 to 15). Smartmontools will -ignore the partition number if it is given and query the underlying device. -In Linux the SCSI tape device name can be "nst" and a letter can be -appended to the device name, both decorations are ignored by smartmontools -as it accesses the underlying tape drive. Also in Linux, SCSI devices can -be accessed via their generic name which is of the form -<filename>/dev/sg[0-9]</filename>. -</para> -<para> -Linux also has an optional Solaris like -naming scheme for SCSI device (scsidev), devfs (mainly used in the lk 2.4 -series) and udev (its replacement in the lk 2.6 series). In short, device -naming is a complex area and smartmontools does its best to find -and identify (i.e. whether ATA or SCSI) a device depending on its name. In -some cases smartmontools needs guidance from the user and this can be given -by the '-d ata|scsi|sat|marvell|3ware,N' option in the -<command>smartctl</command> utility and in <command>smartd</command> -daemon's configuration file. -</para> -</sect1> - -<sect1 id="scsidisk"> - <title>SCSI disks</title> -<para> -What is a SCSI disk? A SCSI disk is a storage device that "talks" the SCSI -command set. An ATA disk is a storage device that "talks" the ATA -command set. That seems pretty clear. However the command set that a -disk uses at its connector (and thus shown on its label) may not be -the command set that the operating system needs to use due to command -set translation between the OS and the disk. -</para> -<para> -The ATA command set is used over native ATA transports which are -parallel ATA (PATA) up to 133 MB/sec and serial ATA (SATA) at link -speeds of 1.5 Gbps (approximately 150 MB/sec) or 3 Gbps. In the past -when ATA disks needed to use some other transport (e.g. USB and IEEE1394) -the SCSI command set was sent over the foreign transport. So in this -case the operating system sees a device "talking" the SCSI command set -but the device is really an ATA disk. Many current disk external enclosures -contains ATA disks yet seen from the operating systems view point are -USB mass storage devices talking the SCSI command set. -</para> -<para> -The SCSI command set is used over various transports: the SCSI Parallel -Interface (SPI), Fibre Channel (FCP), Serial Attached SCSI (SAS), -IEEE1394 (SBP), USB (mass storage) and iSCSI. Many of these transports can -convey multiple command sets (i.e. not just the SCSI command set). The -SAS transport is interesting as it can convey both the SCSI -and ATA command sets. There is also the case of a RAID made up of ATA -disks which communicates to host operating system with the SCSI command -set (e.g. 3ware RAID controller). -</para> -<para> -So what does all this mean for smartmontools? In most cases the answer is -not good news. Devices such as USB external disk enclosures translate -incoming (from the host) SCSI commands to their ATA equivalents and process -responses as required. This translation is limited typically to a small -number of SCSI commands (e.g. READ and WRITE) but <emphasis>not</emphasis> -those commands needed by smartmontools. The author does not know of any -SCSI_over_USB devices that support Smartmontools. The 3ware RAID (6000, -7000, 8000 and 9000 series Escalade) controllers are supported -on several operating systems with special code. -<footnote><para> -The 3ware RAID solution tunnels the ATA commands needed for -smartmontools (together with a disk number) through a vendor specific -SCSI command. -</para></footnote> -</para> -<para> -There is an emerging SCSI to ATA Translation (SAT) standard -at <ulink url="http://www.t10.org"> <literal>www.t10.org</literal></ulink> -that may lead to improvements in this area. Apart from defining -some of the facilities smartmontools needs, it defines two ATA PASS THROUGH -SCSI commands. These pass through commands could be used in much the -same way that the 3ware RAID tunnels ATA commands. -</para> -<para> -In order for smartmontools to access an ATA device "behind" a SAT layer -that implements either of the ATA PASS THROUGH SCSI commands, a new device -type called '-d sat' has been introduced. For example this command: -<command>smartctl -a -d sat /dev/sda</command> will form ATA commands -to access various SMART attributes of an ATA disk, then package those -commands within ATA PASS THROUGH SCSI commands and then forward them -to the SCSI interface of <filename>/dev/sda</filename>. -</para> -<para> -It has been reported that many external USB enclosures use a "Cypress" -chipset. This contains an ATACB proprietary pass through (for ATA -commands passed through SCSI commands) for which some publicly -available information is available. Smartmontools has no ATACB specific -code but may move in this direction in the future. Another approach is -to hope USB and SBP2 external enclosures adopt the SAT standard in the -near future. One interesting comment about ATACB is that it should not -be used at the same time as other types of access to the disk (e.g. a -mounted file system)! That implies that a disk should be taken -offline before smartmontools is used on it. It also implies that -the smartd background daemon should not be used. -</para> -</sect1> - -<sect1 id="satadisk"> - <title>SATA disks</title> -<para> -SATA disks use a 1.5 or 3 Gbps serial transport which carries the -ATA command set. The serial connection is point to point so each SATA disk -needs its own cable and plug on the host adapter or motherboard. -<footnote><para> -There are SATA devices called port multipliers that allow up to 15 -SATA drives to be connected to one host. SAS expanders seem to be a -better approach to the problem of connecting a large number of disks -to one or more hosts. -</para></footnote> -Many aspects of SATA are like SCSI and some operating -systems use existing SCSI infrastructure to handle SATA hosts (e.g. -Linux's libata). -</para> -<para> -Serial Attached SCSI (SAS) can be viewed as a superset of SATA. -It can directly connect thousands of SAS disks to one or more controllers -spread across multiple machines in one SAS "domain". Such a domain can -also contain SATA disks, connected to intermediate fanout devices called -expanders (similar to switches in networking). Most SAS host adapters -can also have SATA disks connected directly to the adapter (which -technically is not a usage of SAS but that is of little concern to -the end user). -</para> -<para> -So a SATA disk may be connected -<itemizedlist> -<listitem><para> -to a SATA host controller (on a motherboard or an adapter) -</para></listitem> -<listitem><para> -directly to a SAS host adapter -</para></listitem> -<listitem><para> -to a SAS expander which is connected to one or more SAS host adapters -</para></listitem> -<listitem><para> -or connected via a bridge which is connected to the host computer via -some other transport (e.g. fibre channel) -</para></listitem> -</itemizedlist> -Since all but the first item might have other disks connected which -use the SCSI command set (e.g. SAS and FC disks) often the SATA disks -have a SAT layer put in front of them so they look like SCSI disks. -That SAT layer may be in: -<itemizedlist> -<listitem><para> -the operating system kernel (e.g. libata in Linux) -</para></listitem> -<listitem><para> -in the host adapter firmware (or RAID controller) -</para></listitem> -<listitem><para> -or external to the host computer: within a disk enclosure (e.g. -associated with a SAS expander) -</para></listitem> -</itemizedlist> -</para> -<para> -For normal file system work, a SCSI to ATA (SAT) translation layer only -needs to concern itself with around 6 commands. Unfortunately -smartmontools uses other commands (both in the SCSI and ATA -command sets). Probably the simplest way to handle SMART for SATA disks -behind a SAT layer is to use the ATA PASS THROUGH SCSI commands. -</para> -<para> -smartmontools guesses the disk command set (i.e. ATA or SCSI) -based on the device node it is given. For example in Linux, -<filename>/dev/hda</filename> would be assumed to use the ATA command set -while <filename>/dev/sda</filename> would be assumed to use the SCSI -command set. -<footnote><para> -Even sending trial ATA and SCSI commands to see which one a device -responds to could be tricked. ATAPI cd/dvd drives respond to -both ATA commands (a few, for example IDENTIFY PACKET DEVICE) and SCSI -commands (found in MMC). -</para></footnote> -By using either the '-d ata' or the '-d scsi' option, the command set -guess made by smartmontools can be overridden. The '-d sat' device -type causes smartmontools to generate ATA commands which are then packaged -within the ATA PASS THROUGH SCSI commands (defined by the SAT standard) -and then sent to the device via a SCSI pass through mechanism. -Future versions of smartmontools may automate the detection of a "SATA -disk behind a SAT layer" but currently if a SATA disk appears -with a SCSI type device node name then a command like this may be -required: <command>smartctl -a -d sat /dev/sda</command> . -</para> -</sect1> - -<sect1 id="smart"> - <title>SMART</title> -<para> -<acronym>SMART</acronym> never attained the status of "standard" and its -original documents have been withdrawn. Its catchy name lives on, especially -on vendors' web sites and obviously in the name of this toolset. Luckily -the good ideas in <acronym>SMART</acronym> have been incorporated into the -ATA and SCSI standards albeit in slightly different forms. -</para> -<para> -Initially <acronym>SMART</acronym> began on SCSI disks as vendor -specific extensions. Gradually the <acronym>SMART</acronym> functionality has -moved into the standards (often by other names) and vendors are improving -their standards' compliance. [In the vendors' defence some of the -"standards" are drafts and are yet to be ratified.] Some SCSI disk vendors -have product manuals (available on the net) that cover the parts of the SCSI -command set that their disks support. Some of these manuals fill in details -that are left deliberately vague in the the standards. -<footnote><para> -For example: Seagate's "Cheetah 15K.3 Product Manual, Rev F" contains -sections on <acronym>SMART</acronym>, -thermal monitor, and drive self test (section 5.2.7 to 5.2.9). It also -lists the supported mode pages with their default and changeable values. -</para></footnote> -</para> -<para> -SCSI standards (found at <ulink url="http://www.t10.org"> -<literal>www.t10.org</literal></ulink>) only make one footnote -reference to the term <acronym>SMART</acronym>. In its place -the awkward term "Informational Exceptions" is used. For SCSI tapes the term -"TapeAlert" is used. -</para> -</sect1> - -<sect1 id="smartctl"> - <title>smartctl command line utility</title> -<para> -The <command>smartctl</command> command line utility gets -<acronym>SMART</acronym> information from the nominated device. In some -cases <acronym>SMART</acronym> information held by the nominated device -can be modified by the <command>smartctl</command> command. The command -has many options that can be viewed by the long usage message output be -either of these invocations: <command>smartctl -h</command> or -<command>smartctl --help</command>. Those options that are only -available to ATA disks (i.e. not available to SCSI disks or tape drives) -are marked with "(ATA)". So called "man" page documentation is also -available online. -</para> -<para> -The following options are currently available for SCSI disks and tape -drives unless otherwise noted: -<itemizedlist> -<listitem><para><command>-a | --all</command>: equivalent to the -combination <command>-i -H -A -l error -l selftest</command> options -invoked in that order. -</para></listitem> -<listitem><para><command>-A | --attributes</command>: outputs the -current device temperature, trip temperature, the number of elements -in the grown defect table and data from the start-stop log page. -Outputs some vendor specific information if available. -</para></listitem> -<listitem><para><command>-C | --captive</command>: used in conjunction -with <command>-t short</command> or <command>-t long</command> options to -do short or long self tests in the foreground. [Has no effect on tape -drives.] -</para></listitem> -<listitem><para><command>-d TYPE | --device=TYPE</command> where TYPE -is "ata", "scsi", "marvell" or "3ware,N". Overrides utility's guess -about the class of the device which is based on the form of the nominated -device's name. -</para></listitem> -<listitem><para><command>-h | --help</command>: outputs lengthy usage -message and exits without any other action. -</para></listitem> -<listitem><para><command>-H | --health</command>: outputs single device -health metric determined by the device manufacturer. This will be "OK" -or a failure message. -</para></listitem> -<listitem><para><command>-i | --info</command>: outputs device -identification information (derived from a SCSI INQUIRY command) and -whether the device supports <acronym>SMART</acronym> (and temperature -warnings) and if those facilities are currently enabled. The -type of transport (e.g. FC or SAS) is also reported, if available. -Some users have reported disks that report the wrong transport. -</para></listitem> -<listitem><para><command>-l TYPE | --log=TYPE</command> where TYPE is -either "selftest" or "error". Outputs either the selftest log or the -error log. -</para></listitem> -<listitem><para><command>-q TYPE | --quietmode=TYPE</command> where TYPE is -either "silent" or "errorsonly". When the type is silent then nothing is -output to the console but the exit status is set (so it is suitable for -scripts). For "errorsonly" only errors are output to the console. The -exit status is always set. [See the smartctl man page.] -</para></listitem> -<listitem><para><command>-r TYPE | --report=TYPE</command> where TYPE is -either "ioctl[,<n>]" or "scsiioctl[,<n>]". Turns on low level -debugging of issued commands and responses. These commands are issued -through a system command called an "ioctl" in Unix. The debug can be for -all issued commands (i.e. "ioctl") or only SCSI commands ("scsiioctl"). -Optionally the TYPE can have a comma and a number post pended to increase -the volume of debug. See this <link linkend="ctldebug">section</link> for -more details. -</para></listitem> -<listitem><para><command>-s VALUE | --smart=VALUE</command> where VALUE is -either "on" or "off". Enables or disables <acronym>SMART</acronym> -monitoring (and temperature warnings). -</para></listitem> -<listitem><para><command>-S VALUE | --saveauto=VALUE</command> where VALUE -is either "on" or "off". Controls whether the error log values are -preserved across device power cycles. -</para></listitem> -<listitem><para><command>-t TEST | --test=TEST</command> where TEST -is either "offline", "short" or "long". Despite its name "offline" is -a short foreground test that all SCSI devices should support. A "short" -self test is typically 2 minutes or less. A "long" self test will be -considerably longer than 2 minutes, depending on the size of the media. -The estimated time that a "long" self test will take is printed after -the "selftest" log (i.e. with '-l selftest' or '-a') -</para></listitem> -<listitem><para><command>-V | --version</command>: outputs the smartctl -version number (including the cvs version of all its source files) -and build information then exits without any other action. -</para></listitem> -<listitem><para><command>-X | --abort</command>: will terminate a -background short or long self test. Usually the self test log notes -that a self test has been aborted. [Has no effect on tape drives.] -</para></listitem> -</itemizedlist> -</para> -<para> -After the options <command>smartctl</command> expects a device name. -This device name is not required for the '--help' or '--version' options. -If no options are given and a valid device name is given then the copyright -notice is output and the program exits. If the device name is invalid -then that is reported. Only one device name can be given. -</para> -<para> -Examples of various invocations of <command>smartctl</command> on a -SCSI disk follow: -<programlisting> -# smartctl -i /dev/sdc -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -Device: SEAGATE ST336754SS Version: 0003 -Serial number: xxxxxxxx -Device type: disk -Transport protocol: SAS -Local Time is: Fri Apr 28 15:55:34 2006 EDT -Device supports SMART and is Enabled -Temperature Warning Enabled -</programlisting> -</para> -<para> -<programlisting> -# smartctl -H /dev/sdd -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -SMART Health Status: O -</programlisting> -<programlisting> -# smartctl -A /dev/sdc -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - -Current Drive Temperature: 42 C -Drive Trip Temperature: 68 C -Elements in grown defect list: 0 -Vendor (Seagate) cache information - Blocks sent to initiator = 1666124337 - Blocks received from initiator = 1517744621 - Blocks read from cache and sent to initiator = 384030649 - Number of read and write commands whose size <= segment size = 21193148 - Number of read and write commands whose size > segment size = 1278317 -Vendor (Seagate/Hitachi) factory information - number of hours powered up = 277.08 - number of minutes until next internal SMART test = 108 -</programlisting> -</para> -</sect1> - -<sect1 id="selftest"> - <title>Self Tests</title> -<para> -Rather than wait for thresholds to be tripped, an administrator can -request a self test. Alternatively a self test can be scheduled -periodically (e.g. at 3 a.m. every night or perhaps weekly) with -<command>smartd</command>. All SCSI disks and tape drives should -support a <emphasis>default</emphasis> self test since it is mandatory. -This can be invoked with the -<command>smartctl -t offline <device></command> command. Despite -the term "offline" this is actually a foreground test of less than 2 -minutes. On completion the default self test reports any errors detected -in its response. The default self test makes no entry into the self test -log. Most SCSI devices perform a default self test when they are being -powered up. -</para> -<para> -The other self tests that are optionally supported by the device are listed -here with the <command>smartctl</command> invocation in brackets: -<itemizedlist> -<listitem><para> -background short [<command>smartctl -t short <device></command>] -</para></listitem> -<listitem><para> -background extended [<command>smartctl -t long <device></command>] -</para></listitem> -<listitem><para> -foreground short [<command>smartctl -C -t short <device></command>] -</para></listitem> -<listitem><para> -foreground extended [<command>smartctl -C -t long <device></command>] -</para></listitem> -</itemizedlist> -Short self tests should take less than two minutes to complete. The extended -self tests have been known to take more than one hour for disks that are over -100 GBytes in size. Care should be taken with foreground tests on disks -with mounted file systems as the OS may not take kindly to an hour delay -on a simple READ command. -<footnote><para> -Linux has an additional problem with the foreground extended self tests: -it will attempt to time out the command after 10 seconds. This will appear -in the self test log page as an aborted self test. This problem is fixed -in lk 2.4.22 and the lk 2.6 series (by extending the -timeout to 2 hours). To be on the safe side use the background extended -test instead. Also some disks silently ignore foreground self -tests (e.g. the Seagate Cheetah series). -</para></footnote> -</para> -<para> -Background self tests can be aborted with the <command>smartctl -X -<device> </command> command. The self test log will note that an -abort was requested. -</para> -<para> -Self tests other than the default self test cause an entry to be placed -in the self test results log page. The 20 most recent self tests are -held. The self test results can be viewed with the -<command>smartctl -l selftest <device></command> command. All tests -output the accumulated power on hours when the test was performed and -the success or otherwise (e.g. the self test was aborted by the user's -request) of the test. Unsuccessful self tests output a self test segment -number (vendor specific), the logical block address of the first failure -(if appropriate) and a sense_key,asc,ascq triple (see appendix). Following -the self test result table is the expected duration of an uninterrupted -extended self test (when that figure is provided by the device). -</para> -<para> -Here is an example of a self test log: -<programlisting> -# smartctl -l selftest /dev/sdd -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - - -SMART Self-test log -Num Test Status segment LifeTime LBA_first_err [SK ASC ASQ] - Description number (hours) -# 1 Background long Completed - 100 - [- - -] -# 2 Background long Completed - 25 - [- - -] -# 3 Background long Completed - 24 - [- - -] -# 4 Background short Completed - 0 - [- - -] - -Long (extended) Self Test duration: 603 seconds [10.1 minutes] -</programlisting> -</para> -</sect1> -<sect1 id="errorlog"> - <title>Error Logs</title> -<para> -The <command>smartctl -l error <device></command> command displays -the error counters maintained in the device's log pages. Here is an -example of an error log: -<programlisting> -# smartctl -l error /dev/sdd -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - - -Error counter log: - Errors Corrected by Total Correction Gigabytes Total - ECC rereads/ errors algorithm processed uncorrected - fast | delayed rewrites corrected invocations [10^9 bytes] errors -read: 5805 0 0 5805 5805 121.451 0 -write: 0 0 0 0 0 471.291 0 - -Non-medium error count: 0 -</programlisting> -The displayed error logs (if available) are displayed on separate lines: -<itemizedlist> -<listitem><para> -write error counters -</para></listitem> -<listitem><para> -read error counters -</para></listitem> -<listitem><para> -verify error counters (only displayed if non-zero) -</para></listitem> -<listitem><para> -non-medium error counter (only a single number displayed). This represents -the number of recoverable events other than write, read or verify errors. -</para></listitem> -<listitem><para> -error events are held in the "Last n error events" log page. The number -of error event records held (i.e. "n") is vendor specific (e.g. up to 23 -records are held for Hitachi 10K300 model disks). The contents of each -error event record is in ASCII and vendor specific. The parameter code -associated with each error event record indicates the relative time at -which the error event occurred. A higher parameter code indicates that the -error event occurred later in time. -If this log page is not supported by the device then "Error Events logging -not supported" is output. If this log page is supported and there are -error event records then each one is prefixed by "Error event <n>:" -where <n> is the parameter code. -</para></listitem> -</itemizedlist> -Each of the write, read and verify error counter logs has various -parameters codes. They are itemized below with the smartctl column -name followed, in brackets, with SCSI standard's description and -parameter code). A description taken from Seagate's SCSI -manual (publication 77738479, Rev J) is then given. -<itemizedlist> -<listitem><para> -Errors Corrected by ECC, fast [Errors corrected without substantial delay: -00h]. An error correction was applied to get perfect data (a.k.a. ECC -on-the-fly). "Without substantial delay" means the correction did not -postpone reading of later sectors (e.g. a revolution was not lost). The -counter is incremented once for each logical block that requires correction. -Two different blocks corrected during the same command are counted as two -events. -</para></listitem> -<listitem><para> -Errors Corrected by ECC: delayed [Errors corrected with possible delays: 01h]. -An error code or algorithm (e.g. ECC, checksum) is applied in order to -get perfect data with substantial delay. "With possible delay" means the -correction took longer than a sector time so that reading/writing of -subsequent sectors was delayed (e.g. a lost revolution). The counter is -incremented once for each logical block that requires correction. A -block with a double error that is correctable counts as one event and -two different blocks corrected during the same command count as two -events. -</para></listitem> -<listitem><para> -Error corrected by rereads/rewrites [Total (e.g. rewrites and rereads): 02h]. -This parameter code specifies the counter counting the number of errors -that are corrected by applying retries. This counts errors recovered, -not the number of retries. If five retries were required to recover one -block of data, the counter increments by one, not five. The counter is -incremented once for each logical block that is recovered using retries. -If an error is not recoverable while applying retries and is recovered -by ECC, it isn't counted by this counter; it will be counted by the -counter specified by parameter code 01h - Errors Corrected With Possible -Delays. -</para></listitem> -<listitem><para> -Total errors corrected [Total errors corrected: 03h]. -This counter counts the total of parameter code errors 00h, 01h and -02h (i.e. error corrected by ECC: fast and delayed plus errors corrected -by rereads and rewrites). There is no "double counting" of data errors -among these three counters. The sum of all correctable errors can be -reached by adding parameter code 01h and 02h errors, not by using this -total. [The author does not understand the previous sentence from the -Seagate manual.] -</para></listitem> -<listitem><para> -Correction algorithm invocations [Total times correction algorithm -processed: 04h]. This parameter code specifies the counter that counts -the total number of retries, or "times the retry algorithm is invoked". -If after five attempts a counter 02h type error is recovered, then five -is added to this counter. If three retries are required to get stable -ECC syndrome before a counter 01h type error is corrected, then those -three retries are also counted here. The number of retries applied to -unsuccessfully recover an error (counter 06h type error) are also -counted by this counter. -</para></listitem> -<listitem><para> -Gigabytes processed {10^9} [Total bytes processed: 05h]. This parameter -code specifies the counter that counts the total number of bytes either -successfully or unsuccessfully read, written or verified (depending -on the log page) from the drive. If a transfer terminates early because -of an unrecoverable error, only the logical blocks up to and including -the one with the uncorrected data are counted. [smartmontools divides -this counter by 10^9 before displaying it with three digits to the -right of the decimal point. This makes this 64 bit counter easier to -read.] -</para></listitem> -<listitem><para> -Total uncorrected errors [Total uncorrected errors: 06h]. This parameter -code specifies the counter that contains the total number of blocks for -which an uncorrected data error has occurred. -</para></listitem> -</itemizedlist> -</para> -<para> -The SCSI standard (SPC-3) cautions that the <emphasis>exact</emphasis> -definitions of the error counters is not part of the standard (i.e. they -are vendor specific). As noted the above list contains Seagate's -explanation for its disk products (the last revision of that document -was 1999). Seagate's disk product manuals imply that the disk firmware -collects these counter values and periodically commit them to persistent -storage (disk or non-volatile RAM). -<footnote><para> -This is why some models spring to life after minutes of inactivity and -perform some operation even though there are no external commands -pending. -</para></footnote> -They also imply that their firmware is monitoring these error counters -and if they exceed some threshold (e.g. in a certain time interval) -then the firmware will report a thresholds exceeded. -</para> -<para> -The error counter logs for some disks (e.g. some Seagate models) can -look worrying: -<programlisting> -# smartctl -l error /dev/sdc -smartctl version 5.37 [i686-pc-linux-gnu] Copyright (C) 2002-6 Bruce Allen -Home page is http://smartmontools.sourceforge.net/ - - -Error counter log: - Errors Corrected by Total Correction Gigabytes Total - ECC rereads/ errors algorithm processed uncorrected - fast | delayed rewrites corrected invocations [10^9 bytes] errors -read: 1111396 0 0 1111396 1113203 781.138 13 -write: 0 0 0 0 92 822.450 4 -verify: 341115 0 0 341115 341115 42.159 0 - -Non-medium error count: 1 -</programlisting> -The "fast" ECC corrected number is high. However the '-H' option reports the disk is -in good health as does an extended (long) background self test. The uncorrected errors -would be a problem had in not been for the fact that the author caused them on -purpose (by writing a bad sector with the SCSI WRITE LONG command). -</para> -</sect1> - -<sect1 id="smartd"> - <title>smartd daemon</title> -<para> -<command>smartd</command> is a daemon for monitoring disks (both ATA and -SCSI). It is recommended that tape drives and medium changers are monitored -in a more manual fashion with the <command>smartctl</command> command -as discussed in <xref linkend="tapes"/>. -</para> -<para> -The configuration file for <command>smartd</command> -is called <filename>/etc/smartd.conf</filename> and has a man page (as does -the <command>smartd</command> command). The controlling daemon script -is placed in the normal place for a distribution, typically -<filename>/etc/rc.d/init.d/smartd</filename>. -</para> -<para> -<command>smartd</command> polls the devices it has recognized when it -was started. By default it polls every 30 minutes. It reports any adverse -finding and noteworthy occurrences (e.g. disk drive temperature changes) -to a log file (<filename>/var/log/messages</filename>). <command>smartd -</command> can be configured to take other actions, for example send -email to a system administrator. -</para> -<para> -SCSI disks can be discovered by <command>smartd</command> via a scan of -device nodes (for linux: <filename>/dev/sda</filename> through to -<filename>/dev/sdz</filename>) by placing the word "DEVICESCAN" in -<filename>/etc/smartd.conf</filename> file. Alternatively the -"DEVICESCAN" word can be removed (or commented out) and SCSI devices -named explicitly: -<programlisting> -/dev/sda -d scsi -/dev/sdb -d scsi -</programlisting> -The "-d scsi" argument overrides what <command>smartd</command> would -guess as the device class (i.e. "ata", "scsi", "marvell" or "3ware,N"). -</para> -</sect1> - -<sect1 id="tapes"> - <title>TapeAlert</title> -<para> -TapeAlert (or "tape alerts") is closely related to the -<acronym>SMART</acronym> infrastructure provided for SCSI disks. -TapeAlert is specialized for tape and medium changer devices. An example of -a TapeAlert is an indication that the tape drive heads need to be cleaned. -</para> -<para> -Pending TapeAlert errors can be read from the TapeAlert log page -(using <command>smartctl</command>). This can be done even when -<acronym>SMART</acronym> -monitoring is disabled (e.g. after <command>smartctl -s off <tape_device -></command>). In fact, the best way to use the TapeAlert mechanism is -to poll the flags (with <command>smartctl</command>) at relevant times when -using the tape, for example: -<itemizedlist> -<listitem><para> -when starting a new job using the tape drive -</para></listitem> -<listitem><para> -after an unrecoverable error -</para></listitem> -<listitem><para> -at the end of using each tape (and before it is unloaded) -</para></listitem> -</itemizedlist> -</para> -<para> -The TapeAlert information is divided into three severity classes: -Critical, Warning, and Information. The critical messages require -urgent user intervention. Both critical and warning errors may lead to -loss of data. Some of the errors are related to the medium and others -to the tape drive itself. This is why the TapeAlert information should be -checked when the tape is in use and not polled periodically (i.e. the -<command>smartd</command> daemon with its periodic polling is not -particularly useful for TapeAlert mechanism). -</para> -<para> -Different sets of flags are defined for tape drives and media -changers. Most of the flags are optional and the set of flags -supported depends on the device. TapeAlert is being included into the -SCSI-3 standards. Many SCSI-2 drives support TapeAlert but the -implementation may not fully conform to the SCSI-3 draft definition -used by smartmontools. -</para> -<para> -It is important that only one application -(or OS driver) is monitoring tape alerts since reading the TapeAlert log -page deactivates all flags after they are read. -<footnote><para> -In a multi initiator environment (e.g. several computers sharing the same -tape jukebox) there should only be one application monitoring tape alerts -per initiator. -</para></footnote> -Currently the Linux SCSI tape drivers (st and osst) do not check the -TapeAlert log page. In Linux, a medium changer device (i.e. the robot in -a tape jukebox) is accessed via its SCSI generic (sg) device name. -</para> -<para> -Code and information on the TapeAlert mechanism have been provided by -Kai Mäkisara <email>Kai.Makisara at kolumbus dot fi</email>. -</para> -</sect1> - -<sect1 id="examples"> - <title>Examples</title> -<para> -Here is some output from the <command>smartctl</command> -command. Mostly it is for the '--all' option. -<itemizedlist> -<listitem><para> -StorageTek LT20 tape 'jukebox': the -<ulink url="examples/bnch_DLT1.html"> -<literal>tape reading mechanism</literal></ulink> -and the -<ulink url="examples/bnch_robot.html"> -<literal>medium changer</literal></ulink> (robot). -Note the TapeAlert warnings in the medium changer output. -</para></listitem> -<listitem><para> -HP DDS-4 -<ulink url="examples/hp_c5713a_smt_a.html"> -<literal>tape</literal></ulink> -drive. -</para></listitem> -<listitem><para> -Generic ATAPI CD-RW -<ulink url="examples/atapi_cdrw_smt_a.html"> -<literal>cd writer</literal></ulink> is an example of a device that -does not support <acronym>SMART</acronym>. -</para></listitem> -<listitem><para> -IBM DDRS 39130 -<ulink url="examples/ddrs_39130_smt_a.html"> -<literal>disk</literal></ulink> - manufactured in 1998. -</para></listitem> -<listitem><para> -Fujitsu MAM3184MP 18 GigaByte -<ulink url="examples/mam3184_smt_a.html"> -<literal>disk</literal></ulink> when all is well. Here is the output from -the <command>smartctl -H</command> command after the IEC Test bit has been -set (with the <command>smartctl -s on -r ioctl,3</command> command) on the -same Fujitsu <ulink url="examples/mam3184_smt_health.html"> -<literal>disk</literal></ulink> . -</para></listitem> -<listitem><para> -Fujitsu MAP3735NP 73 GigaByte -<ulink url="examples/map3735_smt_a.html"> -<literal>disk</literal></ulink> -</para></listitem> -<listitem><para> -Quantum ATLAS IV 36 WLS, 36 GigaByte -<ulink url="examples/ativ_36_smt_a.html"> -<literal>disk</literal></ulink> -</para></listitem> -<listitem><para> -Seagate Cheetah ST318451LW 18 GigaByte -<ulink url="examples/st318451_smt_a.html"> -<literal>disk</literal></ulink>. It would seem that the total count of bytes -written is reset every time the disk is power cycled. However the total -count of bytes read seems to accumulate over power cycles. -</para></listitem> -</itemizedlist> - -</para> -</sect1> - -<sect1 id="raid"> - <title>RAID, JBOD and Enclosures</title> -<para> -It is unlikely that a hardware RAID controller will directly support -smartmontools. A SCSI RAID controller is a virtual target device that -essentially remaps the SCSI commands it receives to the physical disks on its -internal buses. The physical disks in a "SCSI" RAID could be ATA or sATA -disks, in this case a SCSI bus is used between the host computer and an -external RAID controller since LVD SCSI buses (SPI-2,3 and 4) can run -up to 25 metres (plus other protocol related issues). -</para> -<para> -Some SCSI RAIDs equipped internally with SCSI disks allow access to the -physical disks via logical unit numbers (LUNs) greater than 0. The SCSI RAID -controller itself takes a LUN equal to 0. In this case smartmontools could -be applied to the LUNs greater than 0 that refer to physical disks. -</para> -<para> -Some SCSI RAIDs equipped internally ATA disks have a mechanism that -allows ATA commands to be tunnelled to the ATA disks. The 3ware 6000 -and 7000 series Escalade controllers are examples. In this case, -special provision has been made in smartmontools (starting with -release 5.1-16) to tunnel the ATA command required through to the -physical disks. This is done by using the <command>-d 3ware,N</command> -option/Directive. See the <command>smartctl</command> -and <command>smartd</command> man pages for details. -</para> -<para> -The approach that smartmontools takes is to communicate directly -with physical storage devices (e.g. a disk). Another approach is -to collectively monitor and manage a group of disks and/or tape -drives (be they a RAID, "Just a Bunch Of Disks" <acronym>JBOD</acronym> -or a collection of disks and tape drives) in an enclosure. The SCSI -Enclosure Services <acronym>SES</acronym> (reference: SES-2 at -<ulink url="http://www.t10.org"> <literal>www.t10.org</literal></ulink>) -is designed for this task. Both SCSI device and recent SATA disk -enclosures are using SES. Amongst other things SES can monitor the state -of individual devices within the enclosure, the temperature, power -supplies and fans. A user can set thresholds, define alarm types and -remotely administer the enclosure. -</para> -</sect1> - -<appendix id="Details"> - <title>Details</title> -<sect1 id="stand"> - <title>Standards</title> -<para> -One of the first surprises working with SCSI devices and smartmontools -is that the SCSI standards (found at <ulink url="http://www.t10.org"> -<literal>www.t10.org</literal></ulink>) do <emphasis>not</emphasis> use -the term <acronym>SMART</acronym>. In its place the awkward term "Informational -Exceptions" (IE) is used. -</para> -<para> -The original SCSI standard (over 20 years old now) and the SCSI-2 standard -were monolithic documents. In SCSI-3 and beyond the SCSI standards have -been sub-divided and three categories of interest are the: -<itemizedlist> -<listitem><para>architectural model [SAM-4]</para></listitem> -<listitem><para>command sets [SPC-4, SBC-3, SSC-3, SMC-2, etc] -</para></listitem> -<listitem><para>transports [SPI-4, SBP-2, FCP-3, SAS, etc]</para></listitem> -</itemizedlist> -The architectural model while interesting says nothing specific about -Informational Exceptions or related topics. With respect to the transports -the term <emphasis>SCSI</emphasis> has often been synonymous with one -of the SCSI Parallel Interface transports (e.g. SPI-4 which is often know -as "Ultra320") however this is unhelpful. For the purpose of smartmontools -the SCSI command sets are more interesting. The main reference is the -SCSI Primary Commands (SPC-4) document, specifically these sections: -<itemizedlist> -<listitem><para>self test operations; SEND DIAGNOSTIC command (which is -the mechanism for requesting self tests) -</para></listitem> -<listitem><para>MODE SENSE and MODE SELECT commands (both 6 and 10 byte -variants); Mode parameters [the Informational Exceptions Control (IEC) mode -page and the Control mode page] -</para></listitem> -<listitem><para>LOG SENSE and LOG SELECT commands; -Log parameters [these log pages: Informational exceptions, -read/write/verify error counters, non medium error count, temperature, -start-stop cycle counter and the self test results] -</para></listitem> -</itemizedlist> -The SCSI Block Commands (SBC-3) document covers random access storage -devices such as disks (but excluding CD/DVD readers and writers which are -covered by MMC-4) while the SCSI Streaming Commands (SSC-3) document covers -tape systems. The SBC-3 standard does not contain any additional -information (compared with SPC-4) about Informational Exceptions. -The SSC-3 standard covers TapeAlert (section 4.2.15), some extra facilities in -the IEC mode page (see the mode parameters section) and some additional -log pages. Medium changers, typically the "robots" in jukebox tape systems, -often support the TapeAlert mechanism and are described in the SMC-2 standard. -</para> -</sect1> - -<sect1 id="infoexc"> - <title>Informational Exceptions</title> -<para> -So what are Informational Exceptions in the SCSI context? They are a -set of vendor specific parameters that the device firmware monitors and -if a "failure prediction threshold" is exceeded then an exception is -reported. A user is also able to set thresholds on error counters and -have an exception reported if a condition is met. Additionally most -modern disks monitor their temperature and will issue a warning if -a temperature threshold is exceeded. -</para> -<para> -The "failure prediction threshold" exception reporting and the temperature -warning are separately controlled (in byte 2 of the Informational Exceptions -Control (IEC) mode page). -<footnote><para> -Henceforth the term <emphasis>Informational Exceptions</emphasis> -(or IE) will include both Informational Exceptions and the -temperature (or "enclosure degraded") warnings. -</para></footnote> -In smartmontools the -<command>smartctl -s on <device></command> command turns on IE. -There are various reasons why this may not (fully) work (e.g. IEC mode -page not available or not changeable) so this command queries the device -again after it has attempted the change and reports the state. -The <command>smartctl -s off <device></command> command turns off -IE reporting. -<footnote><para> -IE have a (minor) performance impact on a disk. There are various other -settings in the IEC mode page (e.g. PERF, EBF and LOGERR) that address -this. The standard gives a lot of latitude to the vendor in implementing -these additional flags. This finer level of control may be added to -smartmontools if the need arises. -</para></footnote> -</para> -<sect2 id="iereport"> - <title>IE reporting</title> -<para> -Informational Exceptions are reported via the standard SCSI status -reporting mechanism of an additional sense code (asc) and an additional -sense code qualifier (ascq) pair. A selection of these pairs and the -associated message (there is full list in the SPC-3 document) is listed -here: -<programlisting> -asc ascq message -------------------------------------------------------- -0xb 0x1 Warning - specified temperature exceeded -0x5d 0x0 Failure prediction threshold exceeded -0x5d 0x2 Media failure prediction threshold exceeded -0x5d 0x10 Hardware impending failure general hard drive failure -0x5d 0x11 Hardware impending failure drive error rate too high -0x5d 0x56 Spindle impending failure start unit times too high -0x5d 0xff Failure prediction threshold exceeded (false) -</programlisting> -The last entry in the above table results from setting the TEST bit and -is for exercising the reporting mechanism rather than the indication -of an actual error. -See this <link linkend="testbit">footnote</link> for more information. -</para> -<para> -One difficulty with IE is that the device firmware may detect these -conditions independently of any command executing. Even if it detects -an informational exception during a command it needs to be careful -sending IE error notifications back with a command especially if -that command succeeded (Linux will not handle this too well in the -2.4 kernel series). -There is asynchronous event notification (AEN) in SCSI but it is not -reliably supported across all transports. So smartmontools relies -on a poll from the <command>smartd</command> daemon (the default -is every 30 minutes) to detect informational exceptions. -</para> -<para> -The additional sense code and its qualifier are part of what is termed as -the <emphasis>sense buffer</emphasis> which is the response to a -REQUEST SENSE command. The sense key is also found in the sense buffer. -Synchronous SCSI commands that fail return a single byte status code of -CHECK CONDITION. An OS kernel would see this error/warning status and -then check the sense buffer (by doing a REQUEST SENSE or by other means) -and decide how to continue. From smartmontools's point of view, its -<command>smartd</command> daemon would like to process Informational -Exceptions without interference from the OS. This is done by setting up -the IEC mode page's MRIE field set to 6. This instructs the SCSI -device to hold a pending exception until an unsolicited REQUEST SENSE is -sent. If an exception is pending then the sense key will be "NO SENSE" -and the asc, ascq pair will be set accordingly. In the case of no pending -exception the asc,ascq pair will both be zero. The pending exception is -also visible in the IE log page, if that is supported. So -<command>smartd</command> can check the device during its normal polling -cycle. -</para> -<para> -Pending informational exceptions can also be checked by running -<command>smartctl -H <device></command>. A message of -"SMART Health Status: OK" indicates that there is no pending IE. -<footnote><para> -<anchor id="testbit"/> -One might worry whether the <command>smartd</command> daemon is properly set -up or if the device really will issue IE when the need arises. The mechanism -can be tested by setting the TEST bit in the IEC mode page. That is -done by this command: <command>smartctl -r ioctl,3 -s on <device> -</command> [ignore the extra debugging output that "-r ioctl,3" causes]. A -special asc/ascq pair is reserved for testing (0x5d,0xff) -and the standard associates with it this awkward message: "Failure prediction -threshold exceeded (false)". A call to -<command>smartctl -H <device></command> or waiting until the next -<command>smartd</command> poll should produce that message if the mechanism -is working. The IEC mode page TEST bit can be turned off (i.e. back to normal -IE) with <command>smartctl -s on <device></command>. The output -after the TEST bit has been activated is shown in the -Examples section for the Fujitsu MAM3184 disk. -</para></footnote> -</para> -</sect2> -</sect1> - -<sect1 id="ctldebug"> - <title>smartctl debug</title> -<para> -Debug information for <command>smartctl</command> is output when -the <command>-r ioctl</command> or the <command>-r scsiioctl</command> -option is used. More debug is output when the <command>-r ioctl,<n> -</command> form is used (where "n" is a number greater or equal to 1). Both -<command>-r ioctl</command> and <command>r scsiioctl,1</command> select -the same amount of SCSI debug information. The debug levels currently -defined are: -<itemizedlist> -<listitem><para> -1 - output SCSI commands sent to the device and the status received from -the device -</para></listitem> -<listitem><para> -2 - additionally, output the first 64 bytes of data sent to or received from -the device -</para></listitem> -<listitem><para> -3 - additionally, set the IEC mode page TEST bit if accompanying the '-s on' -option -</para></listitem> -</itemizedlist> -See this <link linkend="testbit">footnote</link> for more information about the -use of the IEC mode page TEST bit. -</para> -<para> -One shortcoming of the Informational Exception data provided by -SCSI devices (at least as defined in the current standard) is that -no LOG SENSE page tells the user how many hours the device has been -in use for. The device needs to track its "age" for applying timestamps -to self test results (seen in the "Lifetime (hours)" column of the -<command>smartctl -l selftest</command> command) if they are supported. -So one way to circumvent this shortcoming is to do dummy self -tests. Hence do a <command>smartctl -t short</command> command and then -wait 2 minutes to see the result in the self test log in which the most -recent self test row (i.e. the first) will have the current lifetime of -the device. -</para> -</sect1> - -<sect1 id="links"> - <title>Links</title> -<para> -Here are some links to related projects and packages: -<itemizedlist> -<listitem><para> -<anchor id="t10"/> -primary reference site for SCSI architecture, command sets and transports -<ulink url="http://www.t10.org"> -<literal>www.t10.org</literal></ulink>. -<footnote><para> -The documents found on the t10 site are actually <emphasis>draft</emphasis> -standards. Once they are ratified they become available from ANSI for -a fee. The t10 site maintains the last draft prior to ratification and -the most recent draft of yet to be ratified standards. -</para></footnote> -</para></listitem> -<listitem><para> -<anchor id="scsirastools"/> -SCSI raid monitoring tools plus a firmware update utility and other low level -tools <ulink url="http://scsirastools.sourceforge.net"> -<literal>scsirastools.sourceforge.net</literal></ulink> . -</para></listitem> -<listitem><para> -<anchor id="sdparm"/> -The <command>sdparm</command> utility allows mode page settings to be -viewed and changed. It can decode Vital Product Data (VPD) pages. -It implements a small number of commands to start and stop media, -and to eject and load removable media. -on this page <ulink url="http://www.torque.net/sg/sdparm.html"> -<literal>www.torque.net/sg/sdparm.html</literal></ulink> . -<command>sdparm</command> is available on Linux with ports to -FreeBSD and Tru64. -</para></listitem> -<listitem><para> -<anchor id="sg3utils"/> -A package of SCSI low level tools for Linux called sg3_utils can be found -on this page <ulink url="http://www.torque.net/sg/u_index.html"> -<literal>www.torque.net/sg/u_index.html</literal></ulink> (the most recent -version is sg3_utils-1.20). Allows command level access to SCSI devices. -</para></listitem> -<listitem><para> -<anchor id="howto"/> -There is a HOWTO on the Linux SCSI subsystem in the 2.4 series here: -<ulink url="http://www.tldp.org/HOWTO/SCSI-2.4-HOWTO"> -<literal>www.tldp.org/HOWTO/SCSI-2.4-HOWTO</literal></ulink>. -</para></listitem> -</itemizedlist> -</para> - -<para> -CVS $Id: smartmontools_scsi.xml,v 1.14 2006/06/24 13:05:58 dpgilbert Exp $ -</para> -</sect1> -</appendix> - -</article> -