From 7f0798ef6fb62d22114db3a803c366ca99dc8340 Mon Sep 17 00:00:00 2001 From: Giuseppe Iuculano Date: Fri, 26 Mar 2010 21:37:06 +0100 Subject: [PATCH] Imported Upstream version 5.39.1+svn3077 --- CHANGELOG | 67 +++- INSTALL | 31 +- Makefile.am | 62 ++-- NEWS | 10 +- atacmds.cpp | 108 ++++++- atacmds.h | 27 +- ataprint.cpp | 75 ++++- ataprint.h | 8 +- configure.in | 15 +- drivedb.h | 61 +++- os_freebsd.cpp | 12 +- os_linux.cpp | 10 +- os_win32.cpp | 599 ++++++++++++++--------------------- os_win32/smartctl_vc8.vcproj | 4 + os_win32/smartd_vc8.vcproj | 4 + scsiata.cpp | 8 +- smartctl.8.in | 19 +- smartctl.cpp | 21 +- smartd.8.in | 16 +- smartd.conf.5.in | 13 +- smartd.cpp | 24 +- update-smart-drivedb.in | 144 +++++++++ 22 files changed, 883 insertions(+), 455 deletions(-) create mode 100644 update-smart-drivedb.in diff --git a/CHANGELOG b/CHANGELOG index d1aef02..574545d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ CHANGELOG for smartmontools -$Id: CHANGELOG 3060 2010-02-03 22:16:40Z chrfranke $ +$Id: CHANGELOG 3077 2010-03-16 20:48:06Z chrfranke $ The most recent version of this file is: http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/CHANGELOG?view=markup @@ -43,6 +43,71 @@ NOTES FOR FUTURE RELEASES: see TODO file. + [CF] USB ID updates: + - LaCie Desktop Hard Drive + - Prolific PL2507 (unsupported) + - Seagate FreeAgent Go FW + - WD My Book Essential + + [CF] Linux: Add '/dev/sd[a-c][a-z]' to smartd DEVICESCAN. + + [CF] smartd: Other config entries may precede DEVICESCAN. + Very first step towards a more flexible device scanning. + + [CF] Windows: Use '.win64' in names of 64-bit binary packages. + Use correct 'strip' program when cross-compiling. + + [CF] Add update script to make targets 'dist' and 'clean', + set +x permission, update svn:ignore. + + [CF] Add 'update-smart-drivedb' script (ticket #59). + The script updates the drive database from SVN. + It is installed if '--enable-drivedb' is configured. + + [MS] drivedb.h updates: + - Seagate Medalist 1720 + - SuperTalent UltraDrive GX SSD + - Intel X25-M SSD + + [CF] Makefile.am: Fix unix2dos and makensis parameters to allow + to build the Windows installer on Linux also. + + [CF] Makefile.am: Use a separate build rule for each man page to + avoid compatibility problems with BSD make. + + [AS] drivedb.h updates: + - Fujitsu MHZ2 BK series + + [MS] drivedb.h updates: + - SAMSUNG SpinPoint F3 series + - SAMSUNG SpinPoint F3 EG series + - SAMSUNG SpinPoint M5 series + - Western Digital Caviar Green 6400AADS + - more Western Digital VelociRaptor variants + + [AS] FreeBSD: disable 48-bit commands in the ata_pass_through interface, + there is no 48-bit support in the IOCATAREQUEST ioctl. + + [CF] smartctl: Add option '-l scterc[,READTIME,WRITETIME]' to get/set + the SCT Error Recovery Control time limit (ticket #50). + + Patch was provided by Richard Gregory: + http://www.csc.liv.ac.uk/~greg/projects/erc/ + Modified for new ata_pass_through() interface. + Linux HPT fixes ommitted for now. + + [CF] Fix SCT temperature table commands on big endian CPUs. + + [MS] drivedb.h updates: + - more Seagate Momentus 5400.6 drives + - HP 500GB drive MM0500EANCR + + [CF] Windows: Cleanup I/O-control declarations, rely on include + files if possible. + + [CF] Windows: Compile fixes for 64-bit exe (EXPERIMENTAL). + Update build info in INSTALL file. + [CF] drivedb.h update: - Patriot Torqx SSD (patch provided by Gianpaolo Cugola) diff --git a/INSTALL b/INSTALL index bb0f4d7..0fd6a4c 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Smartmontools installation instructions ======================================= -$Id: INSTALL 3057 2010-02-03 20:56:41Z chrfranke $ +$Id: INSTALL 3074 2010-03-05 23:00:30Z chrfranke $ Please also see the smartmontools home page: http://smartmontools.sourceforge.net/ @@ -443,20 +443,28 @@ To compile the Windows release with MinGW gcc on MSYS, use: Instead of using "make install", copy the .exe files into some directory in the PATH. -To compile with MinGW gcc 3.x on Cygwin, use: +To compile on Cygwin with MinGW gcc 3.x (option '-mno-cygwin'): - ./configure --build=i686-pc-mingw32 + ./configure --build=i686-pc-mingw32 \ + CC=gcc-3 \ + CXX=g++-3 - The above does not work if gcc 4.x is installed and selected as - default by /usr/sbin/alternatives. If the configure command aborts - with error message '... does not support -mno-cygwin', select - gcc 3.x by: +To cross-compile on Debian Linux with gcc-mingw32: - CC=gcc-3 CXX=g++-3 ./configure --build=i686-pc-mingw32 + ./configure --build=$(./config.guess) \ + --host=i686-pc-mingw32 \ + CC=i586-mingw32msvc-gcc \ + CXX=i586-mingw32msvc-g++ - Alternatively, a MinGW-targeted cross-compiler can be used if available: +To compile statically linked 64-bit version with MinGW-w64: - ./configure --build=i686-pc-cygwin --host=i686-pc-mingw32 + ./configure --build=$(./config.guess) \ + --host=x86_64-w64-mingw32 \ + LDFLAGS=-static + + Tested on Cygwin and Linux with MinGW-w64 from + http://mingw-w64.sourceforge.net/. + WARNING: 64-bit version is still EXPERIMENTAL. To build the Windows binary distribution, use: @@ -484,6 +492,9 @@ To create a Windows installer, use: package. See http://nsis.sourceforge.net/ for documentation and download location. + It is also possible to (cross-)build the installer on Linux. + This was successfully tested on Debian with package "nsis". + To both create and run the (interactive) installer, use: make install-win32 diff --git a/Makefile.am b/Makefile.am index fe1d2b3..d2bc922 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in # -# $Id: Makefile.am 3059 2010-02-03 21:42:09Z chrfranke $ +# $Id: Makefile.am 3074 2010-03-05 23:00:30Z chrfranke $ # @SET_MAKE@ @@ -25,6 +25,11 @@ endif sbin_PROGRAMS = smartd \ smartctl +if ENABLE_DRIVEDB +sbin_SCRIPTS = update-smart-drivedb +endif + + smartd_SOURCES = smartd.cpp \ atacmdnames.cpp \ atacmdnames.h \ @@ -238,6 +243,7 @@ EXTRA_DIST = smartd.initd.in \ smartd.conf.5.in \ smartd.conf \ autogen.sh \ + update-smart-drivedb.in \ os_darwin/SMART.in \ os_darwin/StartupParameters.plist \ os_darwin/English_Localizable.strings \ @@ -258,6 +264,7 @@ CLEANFILES = smartd.conf.5 \ smartd.conf.5.txt \ smartd.initd \ svnversion.h \ + update-smart-drivedb \ SMART if SMARTD_SUFFIX @@ -327,6 +334,11 @@ smartd.conf.5.in: smartd.8.in cat $(top_builddir)/tmp.tail >> $(srcdir)/smartd.conf.5.in rm -f $(top_builddir)/tmp.head $(top_builddir)/tmp.tail $(top_builddir)/tmp.directives +update-smart-drivedb: update-smart-drivedb.in config.status + $(SHELL) ./config.status --file=$@ + chmod +x $@ + + if INSTALL_INITSCRIPT if OS_DARWIN initd_DATA = SMART \ @@ -444,15 +456,15 @@ MAN_FILTER = \ $(MAN_SAVESTATES) | \ $(MAN_ATTRIBUTELOG) -if OS_FREEBSD -.for file in $(man_MANS) -${file}: $(srcdir)/${file}.in Makefile svnversion.h - cat ${.ALLSRC:M*.in} | $(MAN_FILTER) > $@ -.endfor -else -smart%: $(srcdir)/smart%.in Makefile svnversion.h - cat $< | $(MAN_FILTER) > $@ -endif +# Implicit rule 'smart%: smart%.in ...' does not work with BSD make +smartctl.8: smartctl.8.in Makefile svnversion.h + cat $(srcdir)/smartctl.8.in | $(MAN_FILTER) > $@ + +smartd.8: smartd.8.in Makefile svnversion.h + cat $(srcdir)/smartd.8.in | $(MAN_FILTER) > $@ + +smartd.conf.5: smartd.conf.5.in Makefile svnversion.h + cat $(srcdir)/smartd.conf.5.in | $(MAN_FILTER) > $@ # Commands to convert man pages into .html and .txt # TODO: configure @@ -508,9 +520,15 @@ check: if OS_WIN32_MINGW # Definitions for Windows distribution -distdir_win32 = $(PACKAGE)-$(VERSION).win32 -distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip -distinst_win32= $(PACKAGE)-$(VERSION).win32-setup.exe +if OS_WIN64 +win_bits = 64 +else +win_bits = 32 +endif + +distdir_win32 = $(PACKAGE)-$(VERSION).win$(win_bits) +distzip_win32 = $(PACKAGE)-$(VERSION).win$(win_bits).zip +distinst_win32 = $(PACKAGE)-$(VERSION).win$(win_bits)-setup.exe exedir_win32 = $(distdir_win32)/bin docdir_win32 = $(distdir_win32)/doc @@ -538,9 +556,10 @@ CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe \ smartctl-nc.exe smartctl-nc.exe.tmp \ distdir.mkdir syslogevt.check -# Textfile converter from cygutils -UNIX2DOS = unix2dos -D -DOS2UNIX = dos2unix -U +# Textfile converter from package cygutils or tofrodos +# Note: Only use without options to be compatible with both packages +UNIX2DOS = unix2dos +DOS2UNIX = dos2unix # Build Windows distribution @@ -558,11 +577,12 @@ $(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) . # Build NSIS installer, try to locate makensis in default location first +# Note: Only option character '-' is also compatible with Linux version of makensis $(distinst_win32): $(srcdir)/os_win32/installer.nsi distdir.mkdir $(FILES_WIN32) syslogevt.check @makensis="$(MAKENSIS)"; if [ -z "$$makensis" ]; then \ - if [ ! -z "$$PROGRAMFILES" ] && "$$PROGRAMFILES/NSIS/makensis" /VERSION >/dev/null 2>&1; then \ + if [ ! -z "$$PROGRAMFILES" ] && "$$PROGRAMFILES/NSIS/makensis" -VERSION >/dev/null 2>&1; then \ makensis="$$PROGRAMFILES/NSIS/makensis"; \ - elif makensis /VERSION >/dev/null 2>&1; then \ + elif makensis -VERSION >/dev/null 2>&1; then \ makensis=makensis; \ else \ echo 'makensis: command not found. Please download and install NSIS' 1>&2; \ @@ -572,8 +592,8 @@ $(distinst_win32): $(srcdir)/os_win32/installer.nsi distdir.mkdir $(FILES_WIN32) date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`; \ rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`; \ verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO); \ - echo "$$makensis /V2 /NOCD /DINPDIR=$(distdir_win32) /DOUTFILE=$(distinst_win32) /DVERSTR='$$verstr' $(srcdir)/os_win32/installer.nsi"; \ - "$$makensis" /V2 /NOCD /DINPDIR="$(distdir_win32)" /DOUTFILE="$(distinst_win32)" /DVERSTR="$$verstr" "$(srcdir)/os_win32/installer.nsi" + echo "$$makensis -V2 -NOCD -DINPDIR=$(distdir_win32) -DOUTFILE=$(distinst_win32) -DVERSTR='$$verstr' $(srcdir)/os_win32/installer.nsi"; \ + "$$makensis" -V2 -NOCD -DINPDIR="$(distdir_win32)" -DOUTFILE="$(distinst_win32)" -DVERSTR="$$verstr" "$(srcdir)/os_win32/installer.nsi" cleandist-win32: rm -rf $(distdir_win32) distdir.mkdir syslogevt.check @@ -591,7 +611,7 @@ syslogevt.check: $(exedir_win32)/%.exe: %.exe cp -p $< $@ - strip -s $@ + if test -n '$(STRIP)'; then $(STRIP) -s $@; else strip -s $@; fi touch -r $< $@ $(docdir_win32)/%.html: %.html diff --git a/NEWS b/NEWS index 6892d0c..3e92419 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ smartmontools NEWS ------------------ -$Id: NEWS 3057 2010-02-03 20:56:41Z chrfranke $ +$Id: NEWS 3076 2010-03-12 22:23:08Z chrfranke $ The most up-to-date version of this file is: http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/NEWS?view=markup @@ -8,11 +8,19 @@ http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontool Date Summary: smartmontools release 5.40 ----------------------------------------------------------- +- Other config entries may precede smartd DEVICESCAN. - Option '-v' allows to specify byte order of attribute raw value - configure: New default value for '--with-docdir'. - Drive database is in a separate source file 'drivedb.h' which can be downloaded from SVN. +- New script 'update-smart-drivedb'. - smartd libcap-ng support, option '-C, --capabilities'. +- smartctl option '-l scterc[,...]' to get/set the + SCT Error Recovery Control time limit. +- Linux: Add '/dev/sd[a-c][a-z]' to smartd DEVICESCAN. +- Windows: Support for 64-bit executables. +- Windows: Support for cross compilation on Linux. +- Fix SCT temperature table commands on big endian CPUs. Date 2010-01-28 Summary: smartmontools release 5.39.1 diff --git a/atacmds.cpp b/atacmds.cpp index 06024e5..663a585 100644 --- a/atacmds.cpp +++ b/atacmds.cpp @@ -37,7 +37,7 @@ #include "utility.h" #include "dev_ata_cmd_set.h" // for parsed_ata_device -const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 3040 2010-01-18 20:57:39Z chrfranke $" +const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 3065 2010-02-10 22:16:50Z chrfranke $" ATACMDS_H_CVSID; // for passing global control variables @@ -2214,6 +2214,13 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * cmd.function_code = 1; // Read table cmd.table_id = 2; // Temperature History Table + // swap endian order if needed + if (isbigendian()) { + swapx(&cmd.action_code); + swapx(&cmd.function_code); + swapx(&cmd.table_id); + } + // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ syserror("Error Write SCT Data Table command failed"); @@ -2276,6 +2283,15 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten cmd.state = interval; cmd.option_flags = (persistent ? 0x01 : 0x00); + // swap endian order if needed + if (isbigendian()) { + swapx(&cmd.action_code); + swapx(&cmd.function_code); + swapx(&cmd.feature_code); + swapx(&cmd.state); + swapx(&cmd.option_flags); + } + // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ syserror("Error Write SCT Feature Control Command failed"); @@ -2294,6 +2310,96 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten return 0; } +// Get/Set SCT Error Recovery Control +static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, + bool set, unsigned short & time_limit) +{ + // Check initial status + ata_sct_status_response sts; + if (ataReadSCTStatus(device, &sts)) + return -1; + + // Do nothing if other SCT command is executing + if (sts.ext_status_code == 0xffff) { + pout("Another SCT command is executing, abort Error Recovery Control\n" + "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + + ata_sct_error_recovery_control_command cmd; memset(&cmd, 0, sizeof(cmd)); + // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) + cmd.action_code = 3; // Error Recovery Control command + cmd.function_code = (set ? 1 : 2); // 1=Set timer, 2=Get timer + cmd.selection_code = type; // 1=Read timer, 2=Write timer + if (set) + cmd.time_limit = time_limit; + + // swap endian order if needed + if (isbigendian()) { + swapx(&cmd.action_code); + swapx(&cmd.function_code); + swapx(&cmd.selection_code); + swapx(&cmd.time_limit); + } + + // write command via SMART log page 0xe0 + // TODO: Debug output + ata_cmd_in in; + in.in_regs.command = ATA_SMART_CMD; + in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW; + in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR; + in.in_regs.lba_low = 0xe0; + in.set_data_out(&cmd, 1); + + if (!set) + // Time limit returned in ATA registers + in.out_needed.sector_count = in.out_needed.lba_low = true; + + ata_cmd_out out; + if (!device->ata_pass_through(in, out)) { + pout("Error Write SCT Error Recovery Control Command failed: %s\n", device->get_errmsg()); + return -1; + } + + // re-read and check SCT status + if (ataReadSCTStatus(device, &sts)) + return -1; + + if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == (set ? 1 : 2))) { + pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + + if (!set) { + // Check whether registers are properly returned by ioctl() + if (!(out.out_regs.sector_count.is_set() && out.out_regs.lba_low.is_set())) { + // TODO: Output register support should be checked within each ata_pass_through() + // implementation before command is issued. + pout("Error SMART WRITE LOG does not return COUNT and LBA_LOW register\n"); + return -1; + } + // Return value to caller + time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8); + } + + return 0; +} + +// Get SCT Error Recovery Control +int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit) +{ + return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit); +} + +// Set SCT Error Recovery Control +int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit) +{ + return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit); +} + + // Print one self-test log entry. // Returns true if self-test showed an error. bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type, diff --git a/atacmds.h b/atacmds.h index f0a15a0..8dc913a 100644 --- a/atacmds.h +++ b/atacmds.h @@ -26,7 +26,7 @@ #ifndef ATACMDS_H_ #define ATACMDS_H_ -#define ATACMDS_H_CVSID "$Id: atacmds.h 3020 2009-12-31 01:11:51Z dlukes $" +#define ATACMDS_H_CVSID "$Id: atacmds.h 3065 2010-02-10 22:16:50Z chrfranke $" #include "dev_interface.h" // ata_device @@ -543,6 +543,20 @@ struct ata_sct_status_response #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_status_response, 512); +// SCT Error Recovery Control command (send with SMART_WRITE_LOG page 0xe0) +// Table 88 of T13/1699-D Revision 6a +#pragma pack(1) +struct ata_sct_error_recovery_control_command +{ + unsigned short action_code; // 3 = Error Recovery Control + unsigned short function_code; // 1 = Set, 2 = Return + unsigned short selection_code; // 1 = Read Timer, 2 = Write Timer + unsigned short time_limit; // If set: Recovery time limit in 100ms units + unsigned short words004_255[252]; // reserved +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_sct_error_recovery_control_command, 512); + // SCT Feature Control command (send with SMART_WRITE_LOG page 0xe0) // Table 72 of T13/1699-D Revision 3f #pragma pack(1) @@ -552,8 +566,8 @@ struct ata_sct_feature_control_command unsigned short function_code; // 1 = Set, 2 = Return, 3 = Return options unsigned short feature_code; // 3 = Temperature logging interval unsigned short state; // Interval - unsigned short option_flags; // Bit 0: persistent, Bits 1-31: reserved - unsigned short words005_255[251]; // reserved + unsigned short option_flags; // Bit 0: persistent, Bits 1-15: reserved + unsigned short words005_255[251]; // reserved } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_feature_control_command, 512); @@ -727,6 +741,10 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * // Set SCT temperature logging interval int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent); +// Get/Set SCT Error Recovery Control +int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit); +int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit); + /* Enable/Disable SMART on device */ int ataEnableSmart (ata_device * device); @@ -789,6 +807,9 @@ int isSupportSelectiveSelfTest(const ata_smart_values * data); inline bool isSCTCapable(const ata_identify_device *drive) { return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support +inline bool isSCTErrorRecoveryControlCapable(const ata_identify_device *drive) + { return ((drive->words088_255[206-88] & 0x09) == 0x09); } // 0x08 = SCT Error Recovery Control support + inline bool isSCTFeatureControlCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x11) == 0x11); } // 0x10 = SCT Feature Control support diff --git a/ataprint.cpp b/ataprint.cpp index 11d6be9..30b7a0b 100644 --- a/ataprint.cpp +++ b/ataprint.cpp @@ -44,7 +44,7 @@ #include "utility.h" #include "knowndrives.h" -const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 3037 2010-01-16 20:07:13Z chrfranke $" +const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 3065 2010-02-10 22:16:50Z chrfranke $" ATAPRINT_H_CVSID; // for passing global control variables @@ -883,6 +883,8 @@ static void ataPrintSCTCapability(const ata_identify_device *drive) if (!(sctcaps & 0x01)) return; pout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps); + if (sctcaps & 0x08) + pout("\t\t\t\t\tSCT Error Recovery Control supported.\n"); if (sctcaps & 0x10) pout("\t\t\t\t\tSCT Feature Control supported.\n"); if (sctcaps & 0x20) @@ -1698,6 +1700,20 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) return 0; } +// Print SCT Error Recovery Control timers +static void ataPrintSCTErrorRecoveryControl(unsigned short read_timer, unsigned short write_timer) +{ + pout("SCT Error Recovery Control:\n"); + if (!read_timer) + pout(" Read: Disabled\n"); + else + pout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0); + if (!write_timer) + pout(" Write: Disabled\n"); + else + pout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0); +} + // Compares failure type to policy in effect, and either exits or // simply returns to the calling routine. @@ -2285,14 +2301,21 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } } + // SCT commands + bool sct_ok = false; + if ( options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int + || options.sct_erc_get || options.sct_erc_set ) { + if (!isSCTCapable(&drive)) { + pout("Warning: device does not support SCT Commands\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + sct_ok = true; + } + // Print SCT status and temperature history table - if (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int) { + if (sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int)) { for (;;) { - if (!isSCTCapable(&drive)) { - pout("Warning: device does not support SCT Commands\n"); - failuretest(OPTIONAL_CMD, returnval|=FAILSMART); - break; - } if (options.sct_temp_sts || options.sct_temp_hist) { ata_sct_status_response sts; ata_sct_temperature_history_table tmh; @@ -2340,6 +2363,44 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } } + // SCT Error Recovery Control + if (sct_ok && (options.sct_erc_get || options.sct_erc_set)) { + if (!isSCTErrorRecoveryControlCapable(&drive)) { + pout("Warning: device does not support SCT Error Recovery Control command\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + bool sct_erc_get = options.sct_erc_get; + if (options.sct_erc_set) { + // Set SCT Error Recovery Control + if ( ataSetSCTErrorRecoveryControltime(device, 1, options.sct_erc_readtime ) + || ataSetSCTErrorRecoveryControltime(device, 2, options.sct_erc_writetime)) { + pout("Warning: device does not support SCT (Set) Error Recovery Control command\n"); + pout("Suggest common arguments: scterc,70,70 to enable ERC or sct,0,0 to disable\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + sct_erc_get = false; + } + else + sct_erc_get = true; + } + + if (sct_erc_get) { + // Print SCT Error Recovery Control + unsigned short read_timer, write_timer; + if ( ataGetSCTErrorRecoveryControltime(device, 1, read_timer ) + || ataGetSCTErrorRecoveryControltime(device, 2, write_timer)) { + pout("Warning: device does not support SCT (Get) Error Recovery Control command\n"); + if (options.sct_erc_set) + pout("The previous SCT (Set) Error Recovery Control command succeeded\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + ataPrintSCTErrorRecoveryControl(read_timer, write_timer); + } + pout("\n"); + } + } + // Print SATA Phy Event Counters if (options.sataphy) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true); diff --git a/ataprint.h b/ataprint.h index 7fd3be7..0194cca 100644 --- a/ataprint.h +++ b/ataprint.h @@ -26,7 +26,7 @@ #ifndef ATAPRINT_H_ #define ATAPRINT_H_ -#define ATAPRINT_H_CVSID "$Id: ataprint.h 3020 2009-12-31 01:11:51Z dlukes $\n" +#define ATAPRINT_H_CVSID "$Id: ataprint.h 3065 2010-02-10 22:16:50Z chrfranke $\n" #include @@ -63,6 +63,9 @@ struct ata_print_options std::vector log_requests; bool sct_temp_sts, sct_temp_hist; + bool sct_erc_get; + bool sct_erc_set; + unsigned sct_erc_readtime, sct_erc_writetime; bool sataphy, sataphy_reset; bool smart_disable, smart_enable; @@ -97,6 +100,9 @@ struct ata_print_options smart_ext_selftest_log(0), retry_error_log(false), retry_selftest_log(false), sct_temp_sts(false), sct_temp_hist(false), + sct_erc_get(false), + sct_erc_set(false), + sct_erc_readtime(0), sct_erc_writetime(0), sataphy(false), sataphy_reset(false), smart_disable(false), smart_enable(false), smart_auto_offl_disable(false), smart_auto_offl_enable(false), diff --git a/configure.in b/configure.in index 136526d..9db5131 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ # -# $Id: configure.in 3057 2010-02-03 20:56:41Z chrfranke $ +# $Id: configure.in 3074 2010-03-05 23:00:30Z chrfranke $ # dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) @@ -7,7 +7,7 @@ AC_INIT(smartmontools, 5.40, smartmontools-support@lists.sourceforge.net) AC_CONFIG_SRCDIR(smartctl.cpp) smartmontools_configure_date=`date -u +'%Y-%m-%d %T %Z'` -smartmontools_cvs_tag=`echo '$Id: configure.in 3057 2010-02-03 20:56:41Z chrfranke $'` +smartmontools_cvs_tag=`echo '$Id: configure.in 3074 2010-03-05 23:00:30Z chrfranke $'` smartmontools_release_date=2009-12-09 smartmontools_release_time="21:00:32 UTC" @@ -38,7 +38,7 @@ case "${host}" in # to select MinGW gcc. This does no longer work for gcc 4.x. if test "${build}" = "${host}" && test -x /usr/bin/uname && \ /usr/bin/uname | grep -i '^CYGWIN' >/dev/null; then - AC_MSG_CHECKING([whether C and C++ compiler support -mno-cygwin]) + AC_MSG_CHECKING([whether $CC and $CXX support -mno-cygwin]) gcc_support_m_no_cygwin=no if $CC -v -mno-cygwin >/dev/null 2>&1 && \ $CXX -v -mno-cygwin >/dev/null 2>&1; then @@ -138,7 +138,7 @@ AC_MSG_RESULT([$libc_have_working_snprintf]) # check for __attribute__((packed)) AH_TEMPLATE(HAVE_ATTR_PACKED, [Define to 1 if C++ compiler supports __attribute__((packed))]) -AC_MSG_CHECKING([whether C++ compiler supports __attribute__((packed))]) +AC_MSG_CHECKING([whether $CXX supports __attribute__((packed))]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[struct a { int b; } __attribute__((packed));]])], [gcc_have_attr_packed=yes], [gcc_have_attr_packed=no]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) @@ -269,6 +269,7 @@ AC_SUBST(smartmontools_release_time) AC_MSG_CHECKING([for OS dependent modules and libraries]) dnl if OS not recognized, then use the os_generic modules +os_win64=no case "${host}" in *-*-linux*) AC_SUBST([os_deps], ['os_linux.o cciss.o']) @@ -302,7 +303,8 @@ case "${host}" in AC_SUBST([os_libs], ['']) ;; *-*-mingw*) AC_SUBST([os_deps], ['os_win32.o']) - AC_SUBST([os_libs], ['']) ;; + AC_SUBST([os_libs], ['']) + test "$host_cpu" = "x86_64" && os_win64=yes ;; *-*-darwin*) AC_SUBST([os_deps], ['os_darwin.o']) AC_SUBST([os_libs], ['-framework CoreFoundation -framework IOKit']) ;; @@ -345,6 +347,7 @@ dnl Define platform-specific symbol. AM_CONDITIONAL(OS_DARWIN, [echo $host_os | grep '^darwin' > /dev/null]) AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null]) AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null]) +AM_CONDITIONAL(OS_WIN64, [test "$os_win64" = "yes"]) AM_CONDITIONAL(OS_FREEBSD, [echo $host_os | grep '^freebsd' > /dev/null]) dnl Add -Wall and -W if using g++ and its not already specified. @@ -364,7 +367,7 @@ if test "$GXX" = "yes"; then # Disable strict aliasing rules by default (see ticket #23). if test -z "`echo "$CXXFLAGS" | grep "\-f[[no-]]*strict-aliasing" 2> /dev/null`" ; then - AC_MSG_CHECKING([whether g++ supports -fno-strict-aliasing]) + AC_MSG_CHECKING([whether $CXX supports -fno-strict-aliasing]) ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-fno-strict-aliasing" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], diff --git a/drivedb.h b/drivedb.h index d87c3d4..48a544a 100644 --- a/drivedb.h +++ b/drivedb.h @@ -58,7 +58,7 @@ /* const drive_settings builtin_knowndrives[] = { */ - { "$Id: drivedb.h 3060 2010-02-03 22:16:40Z chrfranke $", + { "$Id: drivedb.h 3071 2010-03-04 21:17:09Z manfred99 $", "-", "-", "This is a dummy entry to hold the SVN-Id of drivedb.h", "" @@ -71,6 +71,28 @@ const drive_settings builtin_knowndrives[] = { "ASUS-PHISON SSD", "", "", "" }, + { "SuperTalent UltraDrive GX SSD", + "STT_FT[MD](28|32|56|64)GX25H", + "", "", + " -v 9,raw64" + " -v 12,raw64" + " -v 184,raw64,Initial_Bad_Block_Count" + " -v 195,raw64,Program_Failure_Blk_Ct" + " -v 196,raw64,Erase_Failure_Blk_Ct" + " -v 197,raw64,Read_Failure_Blk_Ct" + " -v 198,raw64,Read_Sectors_Tot_Ct" + " -v 199,raw64,Write_Sectors_Tot_Ct" + " -v 200,raw64,Read_Commands_Tot_Ct" + " -v 201,raw64,Write_Commands_Tot_Ct" + " -v 202,raw64,Error_Bits_Flash_Tot_Ct" + " -v 203,raw64,Corr_Read_Errors_Tot_Ct" + " -v 204,raw64,Bad_Block_Full_Flag" + " -v 205,raw64,Max_PE_Count_Spec" + " -v 206,raw64,Min_Erase_Count" + " -v 207,raw64,Max_Erase_Count" + " -v 208,raw64,Average_Erase_Count" + " -v 209,raw64,Remaining_Lifetime_Perc" + }, { "Patriot Torqx SSD", "Patriot[ -]Torqx.*", "", "", @@ -163,7 +185,12 @@ const drive_settings builtin_knowndrives[] = { " -v 211,raw64" }, { "Intel X25-E SSD", - "SSDSA2SH(032|064)G1.* INTEL", + "SSDSA2SH(032|064)G1.* INTEL", // G1 = first generation + "", "", + "-v 225,raw48,Host_Writes_Count" + }, + { "Intel X25-M SSD", + "INTEL SSDSA2M(080|160)G2.*", // G2 = second generation "", "", "-v 225,raw48,Host_Writes_Count" }, @@ -183,6 +210,10 @@ const drive_settings builtin_knowndrives[] = { "GB1000EAFJL", "", "", "" }, + { "HP 500GB SATA disk MM0500EANCR", + "MM0500EANCR", + "", "", "" + }, { "IBM Deskstar 60GXP series", // ER60A46A firmware "(IBM-|Hitachi )?IC35L0[12346]0AVER07.*", "ER60A46A", @@ -335,6 +366,10 @@ const drive_settings builtin_knowndrives[] = { "FUJITSU MHZ2(12|25)0BS.*", "", "", "" }, + { "Fujitsu MHZ2 BK series", + "FUJITSU MHZ2(08|12|16|25)0BK.*", + "", "", "" + }, { "", // Samsung SV4012H (known firmware) "SAMSUNG SV4012H", "RM100-08", @@ -405,6 +440,14 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HD(502H|10[23]S|15[34]U)I", "", "", "" }, + { "SAMSUNG SpinPoint F3 series", // tested with HD502HJ/1AJ100E4 + "SAMSUNG HD(502H|754J|103S)J", + "", "", "" + }, + { "SAMSUNG SpinPoint F3 EG series", // tested with HD503HI/1AJ100E4 + "SAMSUNG HD(253G|(324|503)H|754J|105S)I", + "", "", "" + }, { "SAMSUNG SpinPoint S250 series", // tested with HD200HJ/KF100-06 "SAMSUNG HD(162|200|250)HJ", "", "", "" @@ -472,6 +515,10 @@ const drive_settings builtin_knowndrives[] = { "", "-v 9,halfminutes" }, + { "SAMSUNG SpinPoint M5 series", // tested with HM160HI/HH100-12 + "SAMSUNG HM((061|080)G|(121|160)H|250J)I", + "", "", "" + }, /* // TODO: Make the entries below more specific. // These entries produce misleading results, because newer @@ -1031,7 +1078,7 @@ const drive_settings builtin_knowndrives[] = { "", "", "" }, { "Seagate Momentus 5400.6 series", - "ST9((12|25)0315AS|500325)ASG?", + "ST9(80313|160(301|314)|(12|25)0315|250317|(320|500)325|500327|640320)ASG?", "", "", "" }, { "Seagate Momentus 5400 PSD series", // Hybrid drives @@ -1058,8 +1105,8 @@ const drive_settings builtin_knowndrives[] = { "ST9((160413|25041[12]|320426|50042[12])AS|(16041[89]|2504[16]4|32042[67]|500426)ASG)", "", "", "" }, - { "Seagate Medalist 1010, 1721, 2120, 3230 and 4340", // ATA2, with -t permissive - "ST3(1010|1721|2120|3230|4340)A", + { "Seagate Medalist 1010, 1720, 1721, 2120, 3230 and 4340", // ATA2, with -t permissive + "ST3(1010|1720|1721|2120|3230|4340)A", "", "", "" }, { "Seagate Medalist 2110, 3221, 4321, 6531, and 8641", @@ -1333,7 +1380,7 @@ const drive_settings builtin_knowndrives[] = { "", "", "" }, { "Western Digital Caviar Green family", - "WDC WD((50|64|75)00AA(C|V)S|(50|75)00AADS|10EA(C|V)S|(10|15|20)EADS)-.*", + "WDC WD((50|64|75)00AA(C|V)S|(50|64|75)00AADS|10EA(C|V)S|(10|15|20)EADS)-.*", "", "", "" }, { "Western Digital Caviar Black family", @@ -1357,7 +1404,7 @@ const drive_settings builtin_knowndrives[] = { "", "", "" }, { "Western Digital VelociRaptor family", - "WDC WD((1500|3000)B|3000G)LFS-.*", + "WDC WD(800H|(1500|3000)[BH]|1600H|3000G)LFS-.*", "", "", "" }, { "Western Digital Scorpio EIDE family", diff --git a/os_freebsd.cpp b/os_freebsd.cpp index df58b92..c9df5fe 100644 --- a/os_freebsd.cpp +++ b/os_freebsd.cpp @@ -71,9 +71,9 @@ #define PATHINQ_SETTINGS_SIZE 128 #endif -static __unused const char *filenameandversion="$Id: os_freebsd.cpp 3037 2010-01-16 20:07:13Z chrfranke $"; +static __unused const char *filenameandversion="$Id: os_freebsd.cpp 3066 2010-02-15 23:10:49Z samm2 $"; -const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3037 2010-01-16 20:07:13Z chrfranke $" \ +const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3066 2010-02-15 23:10:49Z samm2 $" \ ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; extern smartmonctrl * con; @@ -121,7 +121,7 @@ void printwarning(int msgNo, const char* extra) { // global variable holding byte count of allocated memory long long bytes; -const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 3037 2010-01-16 20:07:13Z chrfranke $" +const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 3066 2010-02-15 23:10:49Z samm2 $" DEV_INTERFACE_H_CVSID; extern smartmonctrl * con; // con->reportscsiioctl @@ -261,7 +261,11 @@ int freebsd_ata_device::do_cmd( struct ata_ioc_request* request) bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { - if (!ata_cmd_is_ok(in, true, true, true)) // data_out_support + if (!ata_cmd_is_ok(in, + true, // data_out_support + true, // multi_sector_support + false) // no ata_48bit_support via IOCATAREQUEST + ) return false; struct ata_ioc_request request; diff --git a/os_linux.cpp b/os_linux.cpp index 20f66b9..16722ea 100644 --- a/os_linux.cpp +++ b/os_linux.cpp @@ -90,7 +90,7 @@ #define ARGUSED(x) ((void)(x)) -const char *os_XXXX_c_cvsid="$Id: os_linux.cpp 3052 2010-01-28 19:51:24Z chrfranke $" \ +const char *os_XXXX_c_cvsid="$Id: os_linux.cpp 3076 2010-03-12 22:23:08Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; /* for passing global control variables */ @@ -2948,8 +2948,12 @@ bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist, if (scan_ata) get_dev_list(devlist, "/dev/hd[a-t]", true, false, type, false); - if (scan_scsi) // Try USB autodetection if no type specifed - get_dev_list(devlist, "/dev/sd[a-z]", false, true, type, !*type); + if (scan_scsi) { + bool autodetect = !*type; // Try USB autodetection if no type specifed + get_dev_list(devlist, "/dev/sd[a-z]", false, true, type, autodetect); + // Support up to 104 devices + get_dev_list(devlist, "/dev/sd[a-c][a-z]", false, true, type, autodetect); + } // if we found traditional links, we are done if (devlist.size() > 0) diff --git a/os_win32.cpp b/os_win32.cpp index 2bf2b5a..49a1e65 100644 --- a/os_win32.cpp +++ b/os_win32.cpp @@ -16,6 +16,8 @@ */ #include "config.h" +#define _WIN32_WINNT 0x0510 + #include "int64.h" #include "atacmds.h" #include "extern.h" @@ -40,6 +42,17 @@ extern smartmonctrl * con; // con->permissive,reportataioctl #include // offsetof() #include // access() +// TODO: Add a configure test +#if defined(__CYGWIN__) || (defined(__MINGW32__) && !defined(__MINGW64__)) +#include +#include +#include +#else +// Win SDK, no DDK +#include +#include +#endif + #ifdef __CYGWIN__ #include // CYGWIN_VERSION_DLL_MAJOR #endif @@ -50,10 +63,18 @@ extern smartmonctrl * con; // con->permissive,reportataioctl #define ASSERT_SIZEOF(t, n) \ typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] -const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3049 2010-01-27 19:47:09Z chrfranke $"; +#ifndef _WIN64 +#define SELECT_WIN_32_64(x32, x64) (x32) +#else +#define SELECT_WIN_32_64(x32, x64) (x64) +#endif + +const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3062 2010-02-09 21:02:27Z chrfranke $"; // Disable Win9x/ME specific code if no longer supported by compiler. -#ifndef WIN9X_SUPPORT +#ifdef _WIN64 + #undef WIN9X_SUPPORT +#elif !defined(WIN9X_SUPPORT) #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007) // Win9x/ME support was dropped in Cygwin 1.7 #elif defined(_MSC_VER) && (_MSC_VER >= 1500) @@ -63,6 +84,207 @@ const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3049 2010-01-27 19:47:09Z c #endif #endif +///////////////////////////////////////////////////////////////////////////// +// Windows I/O-controls, some declarations are missing in the include files + +extern "C" { + +// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) + +ASSERT_CONST(SMART_GET_VERSION, 0x074080); +ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084); +ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088); +ASSERT_SIZEOF(GETVERSIONINPARAMS, 24); +ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1); +ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1); + + +// IDE PASS THROUGH (2000, XP, undocumented) + +#ifndef IOCTL_IDE_PASS_THROUGH + +#define IOCTL_IDE_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#endif // IOCTL_IDE_PASS_THROUGH + +#pragma pack(1) + +typedef struct { + IDEREGS IdeReg; + ULONG DataBufferSize; + UCHAR DataBuffer[1]; +} ATA_PASS_THROUGH; + +#pragma pack() + +ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028); +ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1); + + +// ATA PASS THROUGH (Win2003, XP SP2) + +#ifndef IOCTL_ATA_PASS_THROUGH + +#define IOCTL_ATA_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _ATA_PASS_THROUGH_EX { + USHORT Length; + USHORT AtaFlags; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR ReservedAsUchar; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG ReservedAsUlong; + ULONG_PTR DataBufferOffset; + UCHAR PreviousTaskFile[8]; + UCHAR CurrentTaskFile[8]; +} ATA_PASS_THROUGH_EX; + +#define ATA_FLAGS_DRDY_REQUIRED 0x01 +#define ATA_FLAGS_DATA_IN 0x02 +#define ATA_FLAGS_DATA_OUT 0x04 +#define ATA_FLAGS_48BIT_COMMAND 0x08 +#define ATA_FLAGS_USE_DMA 0x10 +#define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista + +#endif // IOCTL_ATA_PASS_THROUGH + +ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c); +ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48)); + + +// IOCTL_SCSI_PASS_THROUGH[_DIRECT] + +ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004); +ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014); +ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56)); +ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT, SELECT_WIN_32_64(44, 56)); + + +// SMART IOCTL via SCSI MINIPORT ioctl + +#ifndef FILE_DEVICE_SCSI + +#define FILE_DEVICE_SCSI 0x001b + +#define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500) +#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) +#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502) +#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503) +#define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504) +#define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505) +#define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506) +#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507) +#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508) +#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509) +#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a) +#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b) +#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c) + +#endif // FILE_DEVICE_SCSI + +ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008); +ASSERT_SIZEOF(SRB_IO_CONTROL, 28); + + +// IOCTL_STORAGE_QUERY_PROPERTY + +#ifndef IOCTL_STORAGE_QUERY_PROPERTY + +#define IOCTL_STORAGE_QUERY_PROPERTY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) + +typedef struct _STORAGE_DEVICE_DESCRIPTOR { + ULONG Version; + ULONG Size; + UCHAR DeviceType; + UCHAR DeviceTypeModifier; + BOOLEAN RemovableMedia; + BOOLEAN CommandQueueing; + ULONG VendorIdOffset; + ULONG ProductIdOffset; + ULONG ProductRevisionOffset; + ULONG SerialNumberOffset; + STORAGE_BUS_TYPE BusType; + ULONG RawPropertiesLength; + UCHAR RawDeviceProperties[1]; +} STORAGE_DEVICE_DESCRIPTOR; + +typedef enum _STORAGE_QUERY_TYPE { + PropertyStandardQuery = 0, + PropertyExistsQuery, + PropertyMaskQuery, + PropertyQueryMaxDefined +} STORAGE_QUERY_TYPE; + +typedef enum _STORAGE_PROPERTY_ID { + StorageDeviceProperty = 0, + StorageAdapterProperty, + StorageDeviceIdProperty, + StorageDeviceUniqueIdProperty, + StorageDeviceWriteCacheProperty, + StorageMiniportProperty, + StorageAccessAlignmentProperty +} STORAGE_PROPERTY_ID; + +typedef struct _STORAGE_PROPERTY_QUERY { + STORAGE_PROPERTY_ID PropertyId; + STORAGE_QUERY_TYPE QueryType; + UCHAR AdditionalParameters[1]; +} STORAGE_PROPERTY_QUERY; + +#endif // IOCTL_STORAGE_QUERY_PROPERTY + +ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400); +ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3); +ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3); + + +// IOCTL_STORAGE_PREDICT_FAILURE + +ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100); +ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512); + + +// 3ware specific versions of SMART ioctl structs + +#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters + +#pragma pack(1) + +typedef struct _GETVERSIONINPARAMS_EX { + BYTE bVersion; + BYTE bRevision; + BYTE bReserved; + BYTE bIDEDeviceMap; + DWORD fCapabilities; + DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map + WORD wIdentifier; // Vendor specific identifier + WORD wControllerId; // 3ware specific: Controller ID (0,1,...) + ULONG dwReserved[2]; +} GETVERSIONINPARAMS_EX; + +typedef struct _SENDCMDINPARAMS_EX { + DWORD cBufferSize; + IDEREGS irDriveRegs; + BYTE bDriveNumber; + BYTE bPortNumber; // 3ware specific: port number + WORD wIdentifier; // Vendor specific identifier + DWORD dwReserved[4]; + BYTE bBuffer[1]; +} SENDCMDINPARAMS_EX; + +#pragma pack() + +ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS)); +ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS)); + +} // extern "C" + ///////////////////////////////////////////////////////////////////////////// namespace os_win32 { // no need to publish anything, name provided for Doxygen @@ -270,6 +492,7 @@ protected: ////////////////////////////////////////////////////////////////////// +#ifndef _WIN64 // Running on 64-bit Windows as 32-bit app ? static bool is_wow64() { @@ -285,6 +508,7 @@ static bool is_wow64() return false; return !!w64; } +#endif // _WIN64 // Return info string about build host and OS version std::string win_smart_interface::get_os_version_str() @@ -335,7 +559,12 @@ std::string win_smart_interface::get_os_version_str() default: w = 0; break; } - const char * w64 = (is_wow64() ? "(64)" : ""); + const char * w64 = ""; +#ifndef _WIN64 + if (is_wow64()) + w64 = "(64)"; +#endif + if (!w) snprintf(vptr, vlen, "-%s%lu.%lu%s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"), @@ -527,8 +756,10 @@ std::string win_smart_interface::get_app_examples(const char * appname) " 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" +#if WIN9X_SUPPORT " smartctl -a /dev/scsi21\n" " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n" +#endif " smartctl -a /dev/sda\n" " (Prints all information for SCSI disk on PhysicalDrive 0)\n" " smartctl -a /dev/pd3\n" @@ -555,133 +786,9 @@ std::string win_smart_interface::get_app_examples(const char * appname) // ATA Interface ///////////////////////////////////////////////////////////////////////////// -// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) - -#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_SEND_DRIVE_COMMAND \ - CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define SMART_RCV_DRIVE_DATA \ - CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(SMART_GET_VERSION , 0x074080); -ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084); -ASSERT_CONST(SMART_RCV_DRIVE_DATA , 0x07c088); - #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; - -ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24); - - -#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters - -typedef struct _GETVERSIONINPARAMS_EX { - BYTE bVersion; - BYTE bRevision; - BYTE bReserved; - BYTE bIDEDeviceMap; - DWORD fCapabilities; - DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map - WORD wIdentifier; // Vendor specific identifier - WORD wControllerId; // 3ware specific: Controller ID (0,1,...) - ULONG dwReserved[2]; -} GETVERSIONINPARAMS_EX, *PGETVERSIONINPARAMS_EX, *LPGETVERSIONINPARAMS_EX; - -ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONOUTPARAMS)); - - -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; - -ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1); - -typedef struct _SENDCMDINPARAMS_EX { - DWORD cBufferSize; - IDEREGS irDriveRegs; - BYTE bDriveNumber; - BYTE bPortNumber; // 3ware specific: port number - WORD wIdentifier; // Vendor specific identifier - DWORD dwReserved[4]; - BYTE bBuffer[1]; -} SENDCMDINPARAMS_EX, *PSENDCMDINPARAMS_EX, *LPSENDCMDINPARAMS_EX; - -ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS)); - - -/* 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; - -ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1); - -#pragma pack() - - -///////////////////////////////////////////////////////////////////////////// - static void print_ide_regs(const IDEREGS * r, int out) { pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", @@ -703,7 +810,7 @@ static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro) static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0) { - GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers)); + GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers)); const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers; DWORD num_out; @@ -714,7 +821,7 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version errno = ENOSYS; return -1; } - assert(num_out == sizeof(GETVERSIONOUTPARAMS)); + assert(num_out == sizeof(GETVERSIONINPARAMS)); if (con->reportataioctl > 1) { pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n" @@ -829,35 +936,11 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u ///////////////////////////////////////////////////////////////////////////// - // IDE PASS THROUGH (2000, XP, undocumented) // // 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) - -ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028); - -#pragma pack(1) - -typedef struct { - IDEREGS IdeReg; - ULONG DataBufferSize; - UCHAR DataBuffer[1]; -} ATA_PASS_THROUGH; - -ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1); - -#pragma pack() - - -///////////////////////////////////////////////////////////////////////////// - static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) { if (datasize > 512) { @@ -932,41 +1015,8 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u ///////////////////////////////////////////////////////////////////////////// - // ATA PASS THROUGH (Win2003, XP SP2) -#define IOCTL_ATA_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c); - -typedef struct _ATA_PASS_THROUGH_EX { - USHORT Length; - USHORT AtaFlags; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR ReservedAsUchar; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG ReservedAsUlong; - ULONG/*_PTR*/ DataBufferOffset; - UCHAR PreviousTaskFile[8]; - UCHAR CurrentTaskFile[8]; -} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX; - -ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40); - -#define ATA_FLAGS_DRDY_REQUIRED 0x01 -#define ATA_FLAGS_DATA_IN 0x02 -#define ATA_FLAGS_DATA_OUT 0x04 -#define ATA_FLAGS_48BIT_COMMAND 0x08 -#define ATA_FLAGS_USE_DMA 0x10 -#define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista - - -///////////////////////////////////////////////////////////////////////////// - // Warning: // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data // transfer per command. Therefore, multi-sector transfers are only supported @@ -1081,41 +1131,11 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev ///////////////////////////////////////////////////////////////////////////// - // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only) -#define IOCTL_SCSI_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004); - -#define SCSI_IOCTL_DATA_OUT 0 -#define SCSI_IOCTL_DATA_IN 1 -#define SCSI_IOCTL_DATA_UNSPECIFIED 2 // undocumented SCSI opcode to for ATA passthrough #define SCSIOP_ATA_PASSTHROUGH 0xCC -typedef struct _SCSI_PASS_THROUGH { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG/*_PTR*/ DataBufferOffset; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; - -ASSERT_SIZEOF(SCSI_PASS_THROUGH, 44); - - -///////////////////////////////////////////////////////////////////////////// - static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) { typedef struct { @@ -1196,7 +1216,6 @@ static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char ///////////////////////////////////////////////////////////////////////////// - // SMART IOCTL via SCSI MINIPORT ioctl // This function is handled by ATAPI port driver (atapi.sys) or by SCSI @@ -1204,40 +1223,6 @@ static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char // It can be used to skip the missing or broken handling of some SMART // command codes (e.g. READ_LOG) in the disk class driver (disk.sys) -#define IOCTL_SCSI_MINIPORT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008); - -typedef struct _SRB_IO_CONTROL { - ULONG HeaderLength; - UCHAR Signature[8]; - ULONG Timeout; - ULONG ControlCode; - ULONG ReturnCode; - ULONG Length; -} SRB_IO_CONTROL, *PSRB_IO_CONTROL; - -ASSERT_SIZEOF(SRB_IO_CONTROL, 28); - -#define FILE_DEVICE_SCSI 0x001b - -#define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500) -#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) -#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502) -#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503) -#define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504) -#define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505) -#define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506) -#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507) -#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508) -#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509) -#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a) -#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b) -#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c) - -///////////////////////////////////////////////////////////////////////////// - static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize) { // Select code @@ -1724,77 +1709,8 @@ int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*se ///////////////////////////////////////////////////////////////////////////// - // IOCTL_STORAGE_QUERY_PROPERTY -#define FILE_DEVICE_MASS_STORAGE 0x0000002d -#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE -#define FILE_ANY_ACCESS 0 - -#define IOCTL_STORAGE_QUERY_PROPERTY \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - -typedef enum _STORAGE_BUS_TYPE { - BusTypeUnknown = 0x00, - BusTypeScsi = 0x01, - BusTypeAtapi = 0x02, - BusTypeAta = 0x03, - BusType1394 = 0x04, - BusTypeSsa = 0x05, - BusTypeFibre = 0x06, - BusTypeUsb = 0x07, - BusTypeRAID = 0x08, - BusTypeiScsi = 0x09, - BusTypeSas = 0x0A, - BusTypeSata = 0x0B, - BusTypeSd = 0x0C, - BusTypeMmc = 0x0D, - BusTypeMax = 0x0E, - BusTypeMaxReserved = 0x7F -} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; - -typedef struct _STORAGE_DEVICE_DESCRIPTOR { - ULONG Version; - ULONG Size; - UCHAR DeviceType; - UCHAR DeviceTypeModifier; - BOOLEAN RemovableMedia; - BOOLEAN CommandQueueing; - ULONG VendorIdOffset; - ULONG ProductIdOffset; - ULONG ProductRevisionOffset; - ULONG SerialNumberOffset; - STORAGE_BUS_TYPE BusType; - ULONG RawPropertiesLength; - UCHAR RawDeviceProperties[1]; -} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; - -typedef enum _STORAGE_QUERY_TYPE { - PropertyStandardQuery = 0, - PropertyExistsQuery, - PropertyMaskQuery, - PropertyQueryMaxDefined -} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; - -typedef enum _STORAGE_PROPERTY_ID { - StorageDeviceProperty = 0, - StorageAdapterProperty, - StorageDeviceIdProperty, - StorageDeviceUniqueIdProperty, - StorageDeviceWriteCacheProperty, - StorageMiniportProperty, - StorageAccessAlignmentProperty -} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; - -typedef struct _STORAGE_PROPERTY_QUERY { - STORAGE_PROPERTY_ID PropertyId; - STORAGE_QUERY_TYPE QueryType; - UCHAR AdditionalParameters[1]; -} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; - - -///////////////////////////////////////////////////////////////////////////// - union STORAGE_DEVICE_DESCRIPTOR_DATA { STORAGE_DEVICE_DESCRIPTOR desc; char raw[256]; @@ -1835,23 +1751,8 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO ///////////////////////////////////////////////////////////////////////////// - // IOCTL_STORAGE_PREDICT_FAILURE -#define IOCTL_STORAGE_PREDICT_FAILURE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS) - -typedef struct _STORAGE_PREDICT_FAILURE { - ULONG PredictFailure; - UCHAR VendorSpecific[512]; -} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE; - -ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512); - - -///////////////////////////////////////////////////////////////////////////// - - // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value // or -1 on error, opionally return VendorSpecific data. // (This works without admin rights) @@ -1900,15 +1801,16 @@ static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONIN if (storage_query_property_ioctl(hdevice, &data)) return DEV_UNKNOWN; + // Newer BusType* values are missing in older includes switch (data.desc.BusType) { case BusTypeAta: - case BusTypeSata: + case (STORAGE_BUS_TYPE)0x0b: // BusTypeSata if (ata_version_ex) memset(ata_version_ex, 0, sizeof(*ata_version_ex)); return DEV_ATA; case BusTypeScsi: - case BusTypeiScsi: - case BusTypeSas: + case (STORAGE_BUS_TYPE)0x09: // BusTypeiScsi + case (STORAGE_BUS_TYPE)0x0a: // BusTypeSas return DEV_SCSI; case BusTypeUsb: return DEV_USB; @@ -3116,7 +3018,7 @@ static int aspi_io_call(ASPI_SRB * srb, unsigned timeout) } else { pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n", - (unsigned long)event, rc, rc, GetLastError()); + (unsigned long)(ULONG_PTR)event, rc, rc, GetLastError()); } // TODO: ASPI_ABORT_IO command aspi_entry = 0; @@ -3478,25 +3380,6 @@ bool winnt_smart_interface::scsi_scan(smart_device_list & devlist) } -#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -typedef struct _SCSI_PASS_THROUGH_DIRECT { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - PVOID DataBuffer; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH_DIRECT; - typedef struct { SCSI_PASS_THROUGH_DIRECT spt; ULONG Filler; diff --git a/os_win32/smartctl_vc8.vcproj b/os_win32/smartctl_vc8.vcproj index d531052..980ff78 100644 --- a/os_win32/smartctl_vc8.vcproj +++ b/os_win32/smartctl_vc8.vcproj @@ -1037,6 +1037,10 @@ RelativePath="..\TODO" > + + diff --git a/os_win32/smartd_vc8.vcproj b/os_win32/smartd_vc8.vcproj index 2f7b69b..807d4a2 100644 --- a/os_win32/smartd_vc8.vcproj +++ b/os_win32/smartd_vc8.vcproj @@ -989,6 +989,10 @@ RelativePath="..\TODO" > + + diff --git a/scsiata.cpp b/scsiata.cpp index 0727017..a70a408 100644 --- a/scsiata.cpp +++ b/scsiata.cpp @@ -61,7 +61,7 @@ #include "dev_ata_cmd_set.h" // ata_device_with_command_set #include "dev_tunnelled.h" // tunnelled_device<> -const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 3048 2010-01-23 17:54:42Z chrfranke $"; +const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 3077 2010-03-16 20:48:06Z chrfranke $"; /* for passing global control variables */ extern smartmonctrl *con; @@ -1352,13 +1352,15 @@ const usb_id_entry usb_ids[] = { // LaCie { 0x059f, 0x0651, -1, d_unsup }, // LaCie hard disk (FA Porsche design) { 0x059f, 0x1018, -1, d_sat }, // LaCie hard disk (Neil Poulton design) + { 0x059f, 0x1019, -1, d_jmicron }, // LaCie Desktop Hard Drive // In-System Design { 0x05ab, 0x0060, 0x1101, d_cypress }, // In-System/Cypress ISD-300A1 // Genesys Logic { 0x05e3, 0x0702, -1, d_unsup }, // Genesys Logic GL881E { 0x05e3, 0x0718, 0x0041, d_sat }, // Genesys Logic ? (TODO: requires '-T permissive') // Prolific - { 0x067b, 0x3507, 0x0001, d_unsup }, // Prolific PL3507 + { 0x067b, 0x2507, -1, d_unsup }, // Prolific PL2507 (USB->PATA) + { 0x067b, 0x3507, 0x0001, d_unsup }, // Prolific PL3507 (USB+IEE1394->PATA) // Freecom { 0x07ab, 0xfc8e, 0x010f, d_sunplus }, // Freecom Hard Drive XS // Toshiba @@ -1368,6 +1370,7 @@ const usb_id_entry usb_ids[] = { { 0x0bc2, 0x2000, -1, d_sat }, // Seagate FreeAgent Go { 0x0bc2, 0x2100, -1, d_sat }, // Seagate FreeAgent Go { 0x0bc2, 0x2101, -1, d_sat }, // Seagate FreeAgent Go + { 0x0bc2, 0x2200, -1, d_sat }, // Seagate FreeAgent Go FW { 0x0bc2, 0x2300, -1, d_sat }, // Seagate Expansion Portable { 0x0bc2, 0x3000, -1, d_sat }, // Seagate FreeAgent Desktop { 0x0bc2, 0x3001, -1, d_sat }, // Seagate FreeAgent Desk @@ -1390,6 +1393,7 @@ const usb_id_entry usb_ids[] = { { 0x1058, 0x1010, 0x0105, d_sat }, // WD Elements { 0x1058, 0x1100, 0x0165, d_sat }, // WD My Book Essential { 0x1058, 0x1102, 0x1028, d_sat }, // WD My Book + { 0x1058, 0x1110, 0x1030, d_sat }, // WD My Book Essential // Initio { 0x13fd, 0x0540, -1, d_unsup }, // Initio 316000 { 0x13fd, 0x1240, 0x0104, d_sat }, // Initio ? (USB->SATA) diff --git a/smartctl.8.in b/smartctl.8.in index 9df3d04..a70fcaa 100644 --- a/smartctl.8.in +++ b/smartctl.8.in @@ -1,7 +1,7 @@ .ig Copyright (C) 2002-9 Bruce Allen - $Id: smartctl.8.in 3001 2009-12-19 15:25:27Z chrfranke $ + $Id: smartctl.8.in 3072 2010-03-04 21:56:41Z chrfranke $ 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 @@ -189,7 +189,7 @@ Prints all SMART and non-SMART information about the device. For ATA devices this is equivalent to .nf \'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective -\-l directory \-l scttemp \-l sataphy\'. +\-l directory \-l scttemp \-l scterc \-l sataphy\'. .fi and for SCSI, this is equivalent to .nf @@ -974,6 +974,16 @@ configured with the \'\-t scttempint,N[,p]\' option, see below. The SCT commands are specified in the proposed ATA\-8 Command Set (ACS), and are already implemented in some recent ATA\-7 disks. +.I scterc[,READTIME,WRITETIME] +\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values +and descriptions of the SCT Error Recovery Control settings. These +are equivalent to TLER (as used by Western Digital), CCTL (as used +by Samsung and Hitachi) and ERC (as used by Seagate. READTIME and +WRITETIME arguments (deciseconds) set the specified values. Values of 0 +disable the feature, other values less than 65 are probably not +supported. For RAID configurations, this is typically set to +70,70 deciseconds. + .I sataphy[,reset] \- [SATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values and descriptions of the SATA Phy Event Counters (General Purpose Log @@ -1303,6 +1313,9 @@ If this option is not specified, optional entries are read from the file .\" BEGIN ENABLE_DRIVEDB If \fB/usr/local/share/smartmontools/drivedb.h\fP is present, the contents of this file is used instead of the built in table. + +Run the script \fB/usr/local/sbin/update-smart-drivedb\fP to update this +file from the smartmontools SVN repository. .\" END ENABLE_DRIVEDB The database files use the same C/C++ syntax that is used to initialize @@ -1840,7 +1853,7 @@ these documents may be found in the References section of the .SH SVN ID OF THIS PAGE: -$Id: smartctl.8.in 3001 2009-12-19 15:25:27Z chrfranke $ +$Id: smartctl.8.in 3072 2010-03-04 21:56:41Z chrfranke $ .\" Local Variables: .\" mode: nroff .\" End: diff --git a/smartctl.cpp b/smartctl.cpp index 76924af..cffca12 100644 --- a/smartctl.cpp +++ b/smartctl.cpp @@ -56,7 +56,7 @@ #include "smartctl.h" #include "utility.h" -const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3032 2010-01-16 13:04:55Z chrfranke $" +const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3065 2010-02-10 22:16:50Z chrfranke $" CONFIG_H_CVSID EXTERN_H_CVSID SMARTCTL_H_CVSID; // This is a block containing all the "control variables". We declare @@ -126,7 +126,7 @@ void Usage (void){ " -l TYPE, --log=TYPE\n" " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" " background, sasphy[,reset], sataphy[,reset],\n" -" scttemp[sts,hist],\n" +" scttemp[sts,hist], scterc[,N,M],\n" " gplog,N[,RANGE], smartlog,N[,RANGE],\n" " xerror[,N][,error], xselftest[,N][,selftest]\n\n" " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" @@ -178,7 +178,7 @@ static std::string getvalidarglist(char opt) case 'S': return "on, off"; case 'l': - return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], " + return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], scterc[,N,M], " "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], " "xerror[,N][,error], xselftest[,N][,selftest]"; case 'P': @@ -430,6 +430,8 @@ const char * parse_options(int argc, char** argv, ataopts.sataphy = ataopts.sataphy_reset = true; } else if (!strcmp(optarg,"background")) { scsiopts.smart_background_log = true; + } else if (!strcmp(optarg,"scterc")) { + ataopts.sct_erc_get = true; } else if (!strcmp(optarg,"scttemp")) { ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; } else if (!strcmp(optarg,"scttempsts")) { @@ -467,6 +469,18 @@ const char * parse_options(int argc, char** argv, else badarg = true; + } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { + unsigned rt = ~0, wt = ~0; int n = -1; + sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); + if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { + ataopts.sct_erc_set = true; + ataopts.sct_erc_readtime = rt; + ataopts.sct_erc_writetime = wt; + } + else { + sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); + badarg = true; + } } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; @@ -526,6 +540,7 @@ const char * parse_options(int argc, char** argv, ataopts.smart_selective_selftest_log = true; ataopts.smart_logdir = ataopts.gp_logdir = true; ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; + ataopts.sct_erc_get = true; ataopts.sataphy = true; scsiopts.smart_background_log = true; scsiopts.sasphy = true; diff --git a/smartd.8.in b/smartd.8.in index 4568e45..f6931f9 100644 --- a/smartd.8.in +++ b/smartd.8.in @@ -1,7 +1,7 @@ .ig Copyright (C) 2002-8 Bruce Allen -$Id: smartd.8.in 3057 2010-02-03 20:56:41Z chrfranke $ +$Id: smartd.8.in 3076 2010-03-12 22:23:08Z chrfranke $ 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 @@ -89,7 +89,8 @@ On startup, in the absence of the configuration file devices that support SMART. The scanning is done as follows: .IP \fBLINUX:\fP 9 Examine all entries \fB"/dev/hd[a-t]"\fP for IDE/ATA -devices, and \fB"/dev/sd[a-z]"\fP for SCSI devices. +devices, and \fB"/dev/sd[a-z]"\fP, \fB"/dev/sd[a-c][a-z]"\fP +for SCSI or SATA devices. .IP \fBFREEBSD:\fP 9 Authoritative list of disk devices is obtained from SCSI (CAM) and ATA subsystems. .IP \fBNETBSD/OPENBSD:\fP 9 @@ -680,8 +681,7 @@ Section below! .SH CONFIGURATION FILE DIRECTIVES .PP -If the first non-comment entry in the configuration file is the text -string +If a non-comment entry in the configuration file is the text string .B DEVICESCAN in capital letters, then \fBsmartd\fP @@ -1699,11 +1699,15 @@ If you want more frequent information, use: .TP .B ADDITIONAL DETAILS ABOUT DEVICESCAN -If the first non-comment entry in the configuration file is the text +If a non-comment entry in the configuration file is the text string \fBDEVICESCAN\fP in capital letters, then \fBsmartd\fP will ignore any remaining lines in the configuration file, and will scan for devices. +[NEW EXPERIMENTAL SMARTD FEATURE] Configuration entries for devices +not found by the platform\-specific device scanning may precede the +\fBDEVICESCAN\fP entry. + If \fBDEVICESCAN\fP is not followed by any Directives, then smartd will scan for both ATA and SCSI devices, and will monitor all possible SMART properties of any devices that are found. @@ -2075,4 +2079,4 @@ smartmontools home page at \fBhttp://smartmontools.sourceforge.net/#references\f .SH SVN ID OF THIS PAGE: -$Id: smartd.8.in 3057 2010-02-03 20:56:41Z chrfranke $ +$Id: smartd.8.in 3076 2010-03-12 22:23:08Z chrfranke $ diff --git a/smartd.conf.5.in b/smartd.conf.5.in index ab71770..5a0e551 100644 --- a/smartd.conf.5.in +++ b/smartd.conf.5.in @@ -1,7 +1,7 @@ .ig Copyright (C) 2002-8 Bruce Allen -$Id: smartd.conf.5.in 3001 2009-12-19 15:25:27Z chrfranke $ +$Id: smartd.conf.5.in 3075 2010-03-12 22:01:44Z chrfranke $ 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 @@ -234,8 +234,7 @@ Section below! .SH CONFIGURATION FILE DIRECTIVES .PP -If the first non-comment entry in the configuration file is the text -string +If a non-comment entry in the configuration file is the text string .B DEVICESCAN in capital letters, then \fBsmartd\fP @@ -1253,11 +1252,15 @@ If you want more frequent information, use: .TP .B ADDITIONAL DETAILS ABOUT DEVICESCAN -If the first non-comment entry in the configuration file is the text +If a non-comment entry in the configuration file is the text string \fBDEVICESCAN\fP in capital letters, then \fBsmartd\fP will ignore any remaining lines in the configuration file, and will scan for devices. +[NEW EXPERIMENTAL SMARTD FEATURE] Configuration entries for devices +not found by the platform\-specific device scanning may precede the +\fBDEVICESCAN\fP entry. + If \fBDEVICESCAN\fP is not followed by any Directives, then smartd will scan for both ATA and SCSI devices, and will monitor all possible SMART properties of any devices that are found. @@ -1399,4 +1402,4 @@ SEE ALSO: .SH SVN ID OF THIS PAGE: -$Id: smartd.conf.5.in 3001 2009-12-19 15:25:27Z chrfranke $ +$Id: smartd.conf.5.in 3075 2010-03-12 22:01:44Z chrfranke $ diff --git a/smartd.cpp b/smartd.cpp index a2a053b..4be546f 100644 --- a/smartd.cpp +++ b/smartd.cpp @@ -126,7 +126,7 @@ extern "C" int getdomainname(char *, int); // no declaration in header files! #define ARGUSED(x) ((void)(x)) -const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3058 2010-02-03 21:04:24Z chrfranke $" +const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3075 2010-03-12 22:01:44Z chrfranke $" CONFIG_H_CVSID EXTERN_H_CVSID; extern const char *reportbug; @@ -3455,7 +3455,7 @@ static int ParseToken(char * token, dev_config & cfg) // -2: found an error // // Note: this routine modifies *line from the caller! -static int ParseConfigLine(dev_config_vector & conf_entries, int entry, int lineno, /*const*/ char * line) +static int ParseConfigLine(dev_config_vector & conf_entries, int /*entry*/, int lineno, /*const*/ char * line) { char *token=NULL; char *name=NULL; @@ -3470,10 +3470,6 @@ static int ParseConfigLine(dev_config_vector & conf_entries, int entry, int line // 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; - } } // We've got a legit entry, make space to store it @@ -3562,7 +3558,7 @@ static int ParseConfigLine(dev_config_vector & conf_entries, int entry, int line // possiblities: // Empty configuration file ==> conf_entries.empty() // No configuration file ==> conf_entries[0].lineno == 0 -// SCANDIRECTIVE found ==> conf_entries[0].lineno != 0 +// SCANDIRECTIVE found ==> conf_entries.back().lineno != 0 (size >= 1) static int ParseConfigFile(dev_config_vector & conf_entries) { // maximum line length in configuration file @@ -4046,6 +4042,10 @@ static int MakeConfigEntries(const dev_config & base_cfg, if (devlist.size() <= 0) return 0; + // add empty device slots for existing config entries + while (scanned_devs.size() < conf_entries.size()) + scanned_devs.push_back((smart_device *)0); + // loop over entries to create for (unsigned i = 0; i < devlist.size(); i++) { // Move device pointer @@ -4078,8 +4078,6 @@ static void CanNotRegister(const char *name, const char *type, int line, bool sc // 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. static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs) { // parse configuration file configfile (normally /etc/smartd.conf) @@ -4098,12 +4096,12 @@ static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_devic // we did not find a SCANDIRECTIVE and did find valid entries PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile); } - else if (conf_entries.size() == 1) { + else if (!conf_entries.empty()) { // we found a SCANDIRECTIVE or there was no configuration file so - // scan. Configuration file's first entry contains all options + // scan. Configuration file's last entry contains all options // that were set - dev_config first = conf_entries.front(); - conf_entries.clear(); + dev_config first = conf_entries.back(); + conf_entries.pop_back(); if (first.lineno) PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE); diff --git a/update-smart-drivedb.in b/update-smart-drivedb.in new file mode 100644 index 0000000..467531a --- /dev/null +++ b/update-smart-drivedb.in @@ -0,0 +1,144 @@ +#! /bin/sh +# +# smartmontools drive database update script +# +# Copyright (C) 2010 Christian Franke +# +# 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, see . +# +# $Id: update-smart-drivedb.in 3072 2010-03-04 21:56:41Z chrfranke $ +# + +set -e + +# Set by config.status +PACKAGE="@PACKAGE@" +VERSION="@VERSION@" +prefix="@prefix@" +exec_prefix="@exec_prefix@" +sbindir="@sbindir@" +datarootdir="@datarootdir@" +datadir="@datadir@" +drivedbdir="@drivedbdir@" + +# Default drivedb location +DEST="$drivedbdir/drivedb.h" + +# Smartctl used for syntax check +SMARTCTL="$sbindir/smartctl" + +# Trac repository browser (does not return HTTP 404 errors) +#SRCEXPR='http://sourceforge.net/apps/trac/smartmontools/export/HEAD/$location/smartmontools/drivedb.h' + +# ViewVC repository browser +SRCEXPR='http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/$location/smartmontools/drivedb.h?revision=HEAD' + +# Convert version into branch name: 5.41[.X] -> "RELEASE_5_41_DRIVEDB" +BRANCH="`echo $VERSION | sed -n 's|^\([0-9][0-9]*\)\.\([0-9][0-9]*\)\([^0-9].*\)\?$|RELEASE_\1_\2_DRIVEDB|p'`" + +if [ -z "$BRANCH" ]; then + echo "$0: syntax error in version number: $VERSION" >&2; exit 1 +fi + + +# Parse options +q="-q " +case "$1" in + -v) q=; shift ;; +esac + +case "$*" in + -*|*\ *) + cat </dev/null || exit 1 + +# Find download tool +if which curl >/dev/null 2>/dev/null; then + DOWNLOAD="curl ${q:+-s }"'-f -o "$DEST.new" "$SRC"' +elif which wget >/dev/null 2>/dev/null; then + DOWNLOAD="wget $q"'-O "$DEST.new" "$SRC"' +elif which lynx >/dev/null 2>/dev/null; then + DOWNLOAD='lynx -source "$SRC" >"$DEST.new"' +else + echo "$0: curl, wget or lynx not available" >&2; exit 1 +fi + +# Try possible branch first, then trunk +for location in "branches/$BRANCH" "trunk"; do + test -n "$q" || echo "Download from $location" + + errmsg= + rm -f "$DEST.new" + SRC="`eval echo "$SRCEXPR"`" + + if eval $DOWNLOAD; then :; else + errmsg="download from $location failed (HTTP error)" + continue + fi + if grep -i 'ViewVC Exception' "$DEST.new" >/dev/null; then + errmsg="download from $location failed (ViewVC error)" + continue + fi + + break +done + +if [ -n "$errmsg" ]; then + rm -f "$DEST.new" + echo "$0: $errmsg" >&2 + exit 1 +fi + +# Adjust timestamp and permissions +touch "$DEST.new" +chmod 0644 "$DEST.new" + +# Check syntax +rm -f "$DEST.error" +if $SMARTCTL -B "$DEST.new" -P showall >/dev/null; then :; else + mv "$DEST.new" "$DEST.error" + echo "$DEST.error: rejected by $SMARTCTL, probably no longer compatible" >&2 + exit 1 +fi + +# Keep old file if identical, ignore differences in Id string +rm -f "$DEST.lastcheck" +if [ -f "$DEST" ]; then + if cat "$DEST" | sed 's|\$''Id''[^$]*\$|$''Id''$|' | cmp - "$DEST.new" >/dev/null; then + rm -f "$DEST.new" + touch "$DEST.lastcheck" + echo "$DEST is already up to date" + exit 0 + fi + mv "$DEST" "$DEST.old" +fi + +mv "$DEST.new" "$DEST" + +echo "$DEST updated from $location" +