From ee38a438aafef7a04b7df628ca5ad38810a1d63e Mon Sep 17 00:00:00 2001 From: Giuseppe Iuculano Date: Sun, 2 Jun 2013 08:38:57 +0200 Subject: [PATCH] Imported Upstream version 6.1+svn3812 --- AUTHORS | 53 +- ChangeLog | 507 +++++ CHANGELOG => ChangeLog-5.0-6.0 | 225 +- INSTALL | 51 +- Makefile.am | 380 ++-- NEWS | 67 +- README | 6 +- atacmdnames.cpp | 223 +- atacmdnames.h | 6 +- atacmds.cpp | 397 ++-- atacmds.h | 89 +- ataidentify.cpp | 708 ++++++ ataidentify.h | 25 + ataprint.cpp | 650 ++++-- ataprint.h | 10 +- autogen.sh | 7 +- cciss.cpp | 6 +- configure.in => configure.ac | 55 +- csmisas.h | 4 + dev_areca.cpp | 704 ++++++ dev_areca.h | 184 ++ dev_interface.cpp | 38 +- dev_interface.h | 27 +- do_release | 30 +- drivedb.h | 847 +++++-- examplescripts/README | 6 +- getopt/getopt.c | 6 +- getopt/getopt.h | 4 +- int64.h | 6 +- knowndrives.cpp | 67 +- knowndrives.h | 11 +- megaraid.h | 332 +-- os_darwin.cpp | 63 +- os_darwin.h | 6 +- os_darwin/SMART.in | 6 +- os_freebsd.cpp | 838 +++---- os_freebsd.h | 6 +- os_generic.cpp | 11 +- os_generic.h | 6 +- os_linux.cpp | 859 +++---- os_linux.h | 6 +- os_netbsd.cpp | 28 +- os_netbsd.h | 6 +- os_openbsd.cpp | 28 +- os_openbsd.h | 6 +- os_os2.cpp | 31 +- os_os2.h | 6 +- os_qnxnto.cpp | 54 +- os_qnxnto.h | 6 +- os_solaris.cpp | 38 +- os_solaris.h | 6 +- os_solaris_ata.s | 6 +- os_win32.cpp | 2001 ++++------------- os_win32/daemon_win32.cpp | 1874 ++++++++------- os_win32/daemon_win32.h | 10 +- os_win32/hostname_win32.cpp | 186 -- os_win32/hostname_win32.h | 35 - os_win32/installer.nsi | 87 +- os_win32/smartctl_res.rc.in | 34 + os_win32/smartctl_vc10.vcxproj.filters | 154 -- os_win32/smartd_res.rc.in | 37 + os_win32/smartd_vc10.vcxproj.filters | 154 -- os_win32/smartd_warning.cmd | 159 ++ os_win32/syslog.h | 6 +- os_win32/syslog_win32.cpp | 18 +- os_win32/syslogevt.c | 152 -- os_win32/syslogevt.mc | 6 +- os_win32/syslogevt_vc10.vcxproj | 103 - .../runcmd.vcxproj} | 8 +- .../smartctl.vcxproj} | 234 +- os_win32/vc10/smartctl.vcxproj.filters | 171 ++ .../smartd.vcxproj} | 245 +- os_win32/vc10/smartd.vcxproj.filters | 173 ++ .../smartmontools.sln} | 16 +- os_win32/vc10/wtssendmsg.vcxproj | 82 + os_win32/wmiquery.cpp | 6 +- os_win32/wtssendmsg.c | 137 ++ regex/regcomp.c | 4 +- regex/regex.c | 4 +- regex/regex.h | 4 +- regex/regex_internal.c | 4 +- regex/regex_internal.h | 4 +- regex/regexec.c | 4 +- scsiata.cpp | 116 +- scsicmds.cpp | 829 +++++-- scsicmds.h | 79 +- scsiprint.cpp | 541 +++-- scsiprint.h | 17 +- smartctl.8.in | 628 +++--- smartctl.cpp | 124 +- smartctl.h | 6 +- smartd.8.in | 161 +- smartd.conf | 7 +- smartd.conf.5.in | 333 +-- smartd.cpp | 756 ++++--- smartd.initd.in | 17 +- smartd_warning.sh.in | 209 ++ utility.cpp | 59 +- utility.h | 10 +- 99 files changed, 10085 insertions(+), 7666 deletions(-) create mode 100644 ChangeLog rename CHANGELOG => ChangeLog-5.0-6.0 (96%) create mode 100644 ataidentify.cpp create mode 100644 ataidentify.h rename configure.in => configure.ac (94%) create mode 100644 dev_areca.cpp create mode 100644 dev_areca.h delete mode 100644 os_win32/hostname_win32.cpp delete mode 100644 os_win32/hostname_win32.h create mode 100644 os_win32/smartctl_res.rc.in delete mode 100644 os_win32/smartctl_vc10.vcxproj.filters create mode 100644 os_win32/smartd_res.rc.in delete mode 100644 os_win32/smartd_vc10.vcxproj.filters create mode 100644 os_win32/smartd_warning.cmd delete mode 100644 os_win32/syslogevt.c delete mode 100644 os_win32/syslogevt_vc10.vcxproj rename os_win32/{runcmd_vc10.vcxproj => vc10/runcmd.vcxproj} (94%) rename os_win32/{smartd_vc10.vcxproj => vc10/smartctl.vcxproj} (66%) create mode 100644 os_win32/vc10/smartctl.vcxproj.filters rename os_win32/{smartctl_vc10.vcxproj => vc10/smartd.vcxproj} (66%) create mode 100644 os_win32/vc10/smartd.vcxproj.filters rename os_win32/{smartmontools_vc10.sln => vc10/smartmontools.sln} (74%) create mode 100644 os_win32/vc10/wtssendmsg.vcxproj create mode 100644 os_win32/wtssendmsg.c create mode 100644 smartd_warning.sh.in diff --git a/AUTHORS b/AUTHORS index c9e56a0..74af815 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -$Id: AUTHORS 3096 2010-04-30 14:32:49Z chrfranke $ +$Id: AUTHORS 3751 2013-01-18 21:19:43Z chrfranke $ This code was originally developed as a Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory (now part of the Storage @@ -10,29 +10,30 @@ ucsc-smartsuite and smartsuite packages, and is derived from that code. Maintainers / Developers: -Bruce Allen -Erik Inge Bolsø -Stanislav Brabec -Peter Cassidy -Casper Dik -Christian Franke -Guilhem Frézou -Douglas Gilbert -Guido Guenther -Geoff Keating -Dr. David Kirkby -Kai Mäkisara -Eduard Martinescu -Frédéric L. W. Meunier -Keiji Sawada -Manfred Schwarb -David Snyder -Sergey Svishchev -Phil Williams -Richard Zybert -Yuri Dario -Shengfeng Zhou -Praveen Chidambaram +Bruce Allen +Erik Inge Bolsø +Stanislav Brabec +Peter Cassidy +Praveen Chidambaram +Yuri Dario +Casper Dik +Christian Franke +Guilhem Frézou +Douglas Gilbert +Guido Guenther +Jordan Hargrave Joerg Hering -Tomas Smetana -Jordan Hargrave +Geoff Keating +Dr. David Kirkby +Kai Mäkisara +Eduard Martinescu +Frédéric L. W. Meunier +Alex Samorukov +Keiji Sawada +Manfred Schwarb +Tomas Smetana +David Snyder +Sergey Svishchev +Phil Williams +Shengfeng Zhou +Richard Zybert diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..40fd918 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,507 @@ +$Id: ChangeLog 3812 2013-04-20 18:59:19Z chrfranke $ + +2013-04-20 Christian Franke + + drivedb.h: + - InnoDisk InnoLite SATADOM D150QV-L SSDs + - Intel 313 Series SSDs + - Intel 330 Series SSDs: 240GB + - JMicron based SSDs: Kingston V200 (ticket #267) + - Samsung based SSDs: SM843T Series + +2013-04-20 Christian Franke + + configure.ac: Linux: Try 'hostname -y' if 'nishostname' is missing. + +2013-04-18 Christian Franke + + configure.ac, smartd_warning.sh.in: Add platform specific commands for + host and domain names. + os_win32/smartd_warning.cmd: Use WMI for DNS domain name. + +2013-04-18 Christian Franke + + scsicmds.cpp, scsiprint.cpp: Silence -Wmaybe-uninitialized warning + (g++ 4.8.0 with -flto). + +2013-03-29 Christian Franke + + os_darwin.cpp: Silence -Wself-assign warning (ticket #266). + os_darwin.cpp, os_netbsd.cpp, os_os2.cpp, os_qnxnto.cpp, os_solaris.cpp: + Remove dummy functions no longer called since r3192. + +2013-03-27 Christian Franke + + os_win32.cpp: Silence -Wunused-local-typedefs warning. + +2013-03-24 Christian Franke + + dev_areca.cpp: Add casts to silence C++11 -Wnarrowing warning + from g++ 4.8. + +2013-03-24 Christian Franke + + Windows: Compile fixes for 64-bit Cygwin. + It uses LP64 model instead of LLP64 (64-bit MSVC, MinGW). + +2013-03-16 Christian Franke + + smartmontools 6.1 + +2013-03-15 Christian Franke + + os_win32.cpp: Support device names /dev/sd[a-z][a-z] (ticket #240). + Enhance DEVICESCAN to 128 drives. Add '-d [TYPE,]pd' option. + smartctl.8.in, smartd.8.in: Document these enhancements. + +2013-03-14 Christian Franke + + drivedb.h: + - Seagate Barracuda 7200.14: Fix regex for new firmware version. + +2013-03-13 Christian Franke + + drivedb.h: + - USB: Prolific PL3507 (0x067b:0x3507): works with '-d usbjmicron,p' + +2013-03-13 Christian Franke + + Create branch RELEASE_6_0_DRIVEDB with last drivedb.h file + compatible with smartmontools 6.0. + +2013-03-13 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: Fix format of attribute 198 (ticket #258). + - SandForce Driven SSDs: Corsair Force GS + - Indilinx Barefoot_2/Everest/Martini based SSDs: OCZ VERTEX PLUS R2 + - Samsung/Seagate SpinPoint M8: 320GB, 640GB + - Seagate Momentus Thin + - Quantum Fireball EX: 10.2GB + +2013-03-07 Christian Franke + + ataidentify.cpp, ataprint.cpp: ACS-3 updates. + ataprint.cpp: Improve device statistics error messages. + +2013-03-06 Christian Franke + + smartd_warning.sh.in: Support BSD variant of 'hostname' command + which prints FQDN. Add Windows domain name (Cygwin). + +2013-03-01 Douglas Gilbert + + scsicmds.h, scsicmds.cpp, scsiprint.cpp: + - for SCSI disks prefer READ DEFECT(12) for finding the + grown defect list length (previously used READ DEFECT(10) + only) + +2013-03-01 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: Transcend SSD320 + - Intel 520 Series SSDs: OEM variant + - JMicron based SSDs: Transcend SSD25 IDE + - HGST Travelstar 7K1000 + - Seagate Desktop HDD.15 + - Seagate LD25.2 + - Western Digital RE4 (SATA 6Gb/s) + - USB: Fujitsu/Zalman ZM-VE300 (0x04c5:0x2028) + +2013-02-23 Christian Franke + + drivedb.h: Crucial/Micron RealSSD C300: Remove bogus trailing '|' from + regex (Regression from r3772). + +2013-02-16 Douglas Gilbert + + scsicmds.h, scsicmds.cpp, scsiprint.h, scsiprint.cpp: + - for SCSI disks, in 'smartctl --info' report physical + block size and lowest LBA alignement (if PB size + different from LB size); logical block provisioning + status (if any); and disk protection (a.k.a. DIF) type + +2013-02-19 Alex Samorukov + + atacmds.cpp: fixed scttemphist on LE machines, including PPC. Patch + and report provided by Roger Roehrig. + +2013-02-16 Douglas Gilbert + + scsicmds.h, scsicmds.cpp, scsiprint.h, scsiprint.cpp: + - SCSI VPD work; improve rotation rate reporting and add form factor + +2013-02-14 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: Kingston V+ 200, Mushkin Chronos deluxe, + OCZ Talos 2 + - Plextor M3 (Pro) Series SSDs + +2013-02-13 Christian Franke + + drivedb.h: + - Crucial/Micron RealSSD C300: new separate entry + - Crucial/Micron RealSSD m4/C400: firmware bug warning + +2013-02-10 Alex Samorukov + + os_freebsd.cpp: adding device type fix for devices on MPT controllers. + +2013-02-06 Christian Franke + + drivedb.h: + - Seagate Samsung SpinPoint M8U (USB) + - Hitachi/HGST Travelstar Z5K500 + - Hitachi/HGST Travelstar 5K750 + - Hitachi/HGST Deskstar 7K4000 + - Toshiba 2.5" HDD MK..37GSX + - Toshiba 2.5" HDD MK..65GSX: GSXN variant + - Toshiba 2.5" HDD MQ01ABD... + - Seagate Momentus 7200.5 + - Western Digital Caviar Green (AF, SATA 6Gb/s): 2TB + - USB: Samsung M3 Portable USB 3.0 (0x04e8:0x61b6) + - USB: LaCie Rugged Mini USB 3.0 (0x059f:0x1051) + - Change short attribute names required before r3343. + +2013-02-05 Christian Franke + + smartd.cpp: Fix allocation of buffer passed to putenv(). + Using putenv("NAME") to unset NAME is not portable. + +2013-02-05 Christian Franke + + do_release: New Signing Key. + +2013-01-31 Christian Franke + + dev_areca.h: Use the C++ way to specify unused arguments. + This silences -Wself-assign warning from clang++. + +2013-01-30 Christian Franke + + configure.ac: Use AC_CHECK_TOOL for winmc and windres. + +2013-01-30 Christian Franke + + Windows smartd: Install service with delayed auto start enabled. + +2013-01-26 Christian Franke + + Windows smartd: Add eventlog MESSAGETABLE resource. + Install/remove smartd.exe as event message file. + Remove syslogevt.exe tool. + +2013-01-26 Christian Franke + + Windows: Add required string CompanyName to VERSIONINFO. + +2013-01-23 Christian Franke + + Windows: Add VERSIONINFO resource to exe files. + +2013-01-23 Christian Franke + + drivedb.h: + - Crucial/Micron RealSSD C300/C400/m4: m4 mSATA variant + - Indilinx Barefoot 3 based SSDs + - Intel DC S3700 Series SSDs + - Samsung based SSD: Samsung SSD 840 Series + +2013-01-18 Christian Franke + + AUTHORS: Convert to UTF-8. Sort names. Replace tabs. + +2013-01-18 Christian Franke + + Rename configure.in to configure.ac to silence warning from + new automake. + autogen.sh: automake 1.12.5 is OK. + +2013-01-16 Christian Franke + + atacmds.cpp: Fix assignment of BYTEORDER from -v option + (Regression from r3719). + +2013-01-13 Ole Jørgen LegÃ¥rd + + os_qnxnto.cpp: Fix include of errno.h. + +2013-01-12 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: Mushkin Callisto deluxe, SuperSSpeed S301 + - Intel 320 Series SSDs: 'B' (7mm) variant (ticket #257) + - SAMSUNG SpinPoint F1 EG + - SAMSUNG SpinPoint P80: SP0401N/TJ100-30 + - Western Digital Caviar Black: 4TB + - Western Digital Caviar Black (AF): Remove non-AF models + - Western Digital My Passport (USB, AF): 5000L, 10J variants + - USB: WD My Passport USB 3.0 (0x1058:0x07a8) + - USB: WD My Book Studio II (0x1058:0x1105) + +2013-01-02 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: ADATA S396, Kingston 3K, V+ + - Indilinx Everest/Martini based SSDs: OCZ VERTEX PLUS + - Samsung based SSD: Samsung SSD 840 PRO Series + +2013-01-02 Christian Franke + + Add '-d usbjmicron,p' device type for Prolific USB bridges. + Based on patch provided by Edward Sheldrake. + +2013-01-01 Christian Franke + + smartd: Use Attribute 190 for temperature (-W) if 194 is not present. + +2013-01-01 Christian Franke + + Happy New Year! Update copyright year in version info. + +2012-12-16 Alex Samorukov + + os_freebsd.cpp: WRITE LOG on LSI/Megaraid should work fine, disable + check, problem was linux related. + os_linux.cpp: Implemented autoscan for the megaraid SAS controolers. + os_linux.cpp: fix WRITE LOG command in SAT layer for -d megaraid. + Reason was direction flag always set to READ. + os_linux.cpp: unblock autodetection for the SAT drives in -d megaraid. + +2012-12-14 Christian Franke + + man pages: Fix usage of Hyphen (-) and Minus sign (\-). + +2012-12-13 Christian Franke + + man pages: Update EXPERIMENTAL notes. + Fix spelling (Red Hat Bugzilla 665028). + +2012-12-13 Christian Franke + + ataprint.cpp: Print Additional Product Identifier (OEM Id). + +2012-12-13 Stanislav Brabec + + Update FSF postal address in all files. + +2012-12-12 Christian Franke + + smartctl.cpp: Remove include for QNXNTO. + Should only be needed if placement new is used. + smartd.cpp: Remove very old _GNU_SOURCE define. + It was added 10 years ago in r147. It is not (or no longer) needed + and has an unwanted side effect (__USE_MINGW_ANSI_STDIO) on MinGW. + +2012-12-11 Christian Franke + + smartd.cpp: Add '-w PATH, --warnexec=PATH' option. + smartd.8.in: Document this option. + +2012-12-11 Christian Franke + + smartd.cpp: Add '-d ignore' directive. + smartd.conf.5.in: Document '-d ignore'. Add DEVICESCAN example. + Remove duplicate and outdated info about device scanning. + smartd.8.in: Add notes about RAID controllers to device scanning info. + +2012-12-11 Stanislav Brabec + + * smartd.initd.in: SUSE: Added sysconfig options to disable + persistent state writes, attribute log and set arbitrary smartd + options. + +2012-12-03 Christian Franke + + Avoid usage of strcpy(), strcat(), sprintf(). + Use snprintf() instead or change type to std::string. + Use array references instead of char pointers for parameters. + +2012-12-03 Christian Franke + + smartd.cpp: Ignore a device from DEVICESCAN if a preceding smartd.conf + entry for the same device exists. + +2012-11-28 Christian Franke + + smartd.conf.5.in: Document smartd_warning.sh/cmd scripts and + the new environment variables. + Makefile.am: Replace smartd_warning.* paths on man pages. + Reformat long sed commands. + +2012-11-27 Christian Franke + + smartd.cpp: Remove trailing newlines from some MailWarning() strings. + os_win32/smartd_warning.cmd: Fix SMARTD_MESSAGE with parentheses. + +2012-11-25 Alex Samorukov + + OpenBSD: remove dummy functions + +2012-11-24 Christian Franke + + Windows: Add tool wtssendmsg.exe based on no longer + used module os_win32/wtssendmsg.cpp. + os_win32/smartd_warning.cmd: Fix wtssendmsg call. + os_win32/installer.nsi: Install smartd_warning.cmd + and wtssendmsg.exe. Fix uninstall of old ChangeLog. + +2012-11-23 Christian Franke + + Move MSVC10 project files to new directory os_win32/vc10. + +2012-11-22 Christian Franke + + smartd: Move warning message formatting and mailer/command + startup to new script SYSCONFDIR/smartd_warning.sh + (Windows: smartd_warning.cmd). + Add environment variables SMARTD_PREVCNT and SMARTD_NEXTDAYS. + Remove host/domainname related code from smartd.cpp + and configure.in + +2012-11-22 Alex Samorukov + + smartctl: implemeted support for -g/-s rcache and -g/-s wcache for SCSI + devices to control read/write device cache. + +2012-11-19 Alex Samorukov + + smartctl: supports progress indicator on selftests + smartctl: prints rotation speed for SCSI drives, if supported + smartctl: add headers to SCSI output, fix data blocks formatting, + trim identification data + os_linux.cpp: add autodetection for PERC H700 array + smartd: trim SCSI vendor/model/serial before creating state files + +2012-11-18 Alex Samorukov + + smartd.cpp: implement error counters and temperature saving to the + attrlog file for SCSI devices. + smartd.cpp: added reset_warning_mail() if device is working for SCSI + +2012-11-18 Christian Franke + + drivedb.h: Western Digital Caviar Green: Add -F xerrorlba + +2012-11-17 Alex Samorukov + + smartd.cpp: print lu_id for SPC devices, it is supported by standard + smartd.cpp: added initial state file support for the SCSI devices + smartd.cpp: add S/N to SCSI device identifier, lu_id is not available + on some drives. + smartd.cpp: fix warning for SCSI drives with self test in progress (#249) + drivedb.h: added -F xerrorlba flag Seagate Barracuda LP/CC32 + +2012-11-09 Christian Franke + + Windows smartd: Allow quoting of '-M exec' argument + to support path names with spaces. + +2012-11-09 Christian Franke + + ataprint.cpp: Rework smartctl -l directory output. + Add R/W, R/O info. Report identical logs in one line. + +2012-11-09 Alex Samorukov + + os_freebsd.cpp: adding handling of SCSI devices exported with mfip + driver. FreeBSD changing PDT code to 0x1f and we are changing it back + to 0x00 (direct-access block device). + os_freebsd.cpp: improved error handling for the ATA devices + +2012-11-04 Christian Franke + + drivedb.h: + - SandForce Driven SSDs: Mushkin Chronos + - Indilinx Everest/Martini based SSDs: OCZ AGILITY4 + - Intel 710 Series SSDs: Add attribute 174 + - JMicron based SSDs: KINGSTON SSDNOW 30GB + - Hitachi Deskstar 7K1000.C: *CLA330 + - Seagate DiamondMax 23, Barracuda 7200.12, 7200.14 (AF), + LP, Green (AF): no warnings for newer firmware versions + - Western Digital Caviar Green (AF, SATA 6Gb/s): rename, add 1TB + - USB: Toshiba Stor.E (0x0930:0x0b1[9a]) + - USB: Verbatim Store'n'Go (0x18a5:0x022b) + +2012-11-02 Alex Samorukov + + os_freebsd.cpp: disabling 48bit commands on legacy ATA controllers + in ATACAM mode because of kernel bug. + +2012-10-31 Christian Franke + + atacmdnames.cpp: Update for ATA-8-ACS, ACS-2, ACS-3. + ataidentify.cpp: Mark retired/obsolete values. + ataprint.cpp: Add new ACS-3 logs, mark obsolete logs. + +2012-10-27 Alex Samorukov + + os_freebsd.cpp: Have smartd prefer real device names over passN. + Patch provided by dnelson, see ticket #21 + os_freebsd.cpp: fix 48-bit support for ATA legacy controllers in + ATACAM mode, patch provided by Alexander Motin + +2012-10-25 Christian Franke + + atacmds.cpp: Return error for get SCT ERC if ATA registers are + unchanged after SMART_WRITE_LOG command (see ticket #245). + +2012-10-24 Christian Franke + + dev_areca.cpp: Add missing parameter check to ata_pass_through(). + Update Areca info on man pages. + +2012-10-24 Christian Franke + + dev_interface: Rework ATA parameter checks, use new flags + ata_device::supports_* for new ata_cmd_is_supported(). + Replace ata_cmd_is_ok() by ata_cmd_is_supported() in scsiata.cpp + and os_win32.cpp. + +2012-10-19 Alex Samorukov + + os_freebsd.cpp - fixed 3ware twe controller support broken + by inerface migration. + +2012-10-18 Christian Franke + + utility.cpp: Add missing errno clear in split_selective_arg() + (Debian bug 690108). + Remove unused function split_report_arg2(). + +2012-10-18 Christian Franke + + os_win32.cpp: define _WIN32. This fixes build on + Cygwin with new w32api-headers. + +2012-10-18 Alex Samorukov + + Compile fixes for Areca patch on FreeBSD. + Added support for the /dev/twsX (3ware 9750) controller on FreeBSD. + Manual pages updated with /dev/twsX device + FreeBSD: Migrate 3ware interface to ata_pass_through() + FreeBSD: fix missing drives detection on -d 3ware + FreeBSD: 3ware - do not pass buffers direcly, use memcpy() instead + FreeBSD: improved detection of 3ware/LSI controllers + +2012-10-16 Christian Franke + + Compile fixes for Areca patch: + Add missing includes. Add GPL header. + Add dev_areca.* to configure.in and Makefile.am. + +2012-10-16 Hank Wu + + Move common Areca code from os_freebsd.cpp, os_linux.cpp, os_win32.cpp + to new files dev_areca.h, dev_areca.cpp. + Add SAS support for FreeBSD and Linux. + +2012-10-10 Christian Franke + + Rename old CHANGELOG to ChangeLog-5.0-6.0. + Start new ChangeLog. + +2012-10-10 Christian Franke + + smartmontools 6.0 diff --git a/CHANGELOG b/ChangeLog-5.0-6.0 similarity index 96% rename from CHANGELOG rename to ChangeLog-5.0-6.0 index cac139e..a468937 100644 --- a/CHANGELOG +++ b/ChangeLog-5.0-6.0 @@ -1,9 +1,6 @@ -CHANGELOG for smartmontools +CHANGELOG for smartmontools 5.0 to 6.0 -$Id: CHANGELOG 3561 2012-06-05 19:49:31Z chrfranke $ - -The most recent version of this file is: -http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/CHANGELOG?view=markup +$Id: ChangeLog-5.0-6.0 3645 2012-10-10 16:15:26Z chrfranke $ Maintainers / Developers Key (alphabetic order): [AS] Alex Samorukov @@ -41,6 +38,224 @@ Maintainers / Developers Key (alphabetic order): +smartmontools 6.0 2012-10-10 + + [CF] do_release: Fix for minor rev number 0. + + [CF] drivedb.h updates: + - SandForce Driven SSDs: Corsair Force 115GB + - Hitachi Ultrastar 7K4000 + - Seagate Barracuda 7200.7 and 7200.7 Plus: IBM OEM variants + - Western Digital Caviar Black (AF) + + [CF] man pages: Update introduction. Update ATA standards. + Remove some outdated info. + + [CF] man pages: Unify license headers. + + [CF] smartctl: Do not abort SCT status output on unknown temperature + history format version. + + [CF] smartctl: Remove duplicate note about selective self-test log + version. + + [CF] smartctl: Add '-l devstat' to '-x, --xall' output. + + [CF] smartctl: Rework ATA error messages and 'not supported' messages. + Avoid misleading warnings on unsupported features (ticket #182). + Avoid duplicate error messages. + + [CF] atacmds.h: Remove nonexistent functions. + + [CF] Windows installer: Add support for /S(ilent) install/uninstall. + + [CF] Windows installer: Update examples. Remove some doc shortcuts. + + [CF] Prepare release 6.0. Change Copyright output line. + Change AUTHORS sections on man pages. + + [CF] smartctl: Rework "ATA Version" output. Print major and minor + revision in one output line. Remove "ATA Standard" line. + + [CF] drivedb.h updates: + - Add firmware warnings for various Seagate series (ticket #239): + DiamondMax 23, Barracuda 7200.12, 7200.14 (AF), LP, Green (AF) + - Seagate Barracuda 7200.14 (AF): 2.5TB + + [CF] drivedb.h updates: + - SandForce Driven SSDs: SanDisk Extreme + - Indilinx Everest/Martini based SSDs: OCZ-VERTEX4, + fix Attribute 232 + - STEC Mach2 CompactFlash Cards + - Toshiba 2.5" HDD MK..55GSX: *55GSXF variants + - Western Digital VelociRaptor (AF) + + [CF] Windows: Remove EXPERIMENTAL notes for 64-bit version. + + [CF] autogen.sh: automake 1.11.6 and 1.12.3 are OK. + + [CF] smartctl: Fix '--identify' for big-endian CPUs. + + [CF] ataidentify.cpp: Document some older (now obsolete) features. + + [CF] ataidentify.cpp: Add some recent ACS-3 features. + + [CF] smartctl: Support '-l sataphy' also for Packet interface devices. + + [CF] atacmds.cpp: Add new ATA ACS-3 minor revision. + + [CF] smartctl: Print SATA version and speed in '-i' output. + + [CF] drivedb.h: Minor reordering of Seagate entries. + + [CF] drivedb.h: Use "AF" for Advanced Format (4KiB LPS). + + [CF] drivedb.h updates: + - Seagate Barracuda SpinPoint F3 + - SAMSUNG SpinPoint F3 RE + - Seagate Barracuda 7200.12: ST3750525AS + - Seagate Barracuda 7200.14 (AF): change name, add -v options + - Western Digital Red (AF) + - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xa013) (ticket #235) + - USB: Seagate Backup Plus Desktop USB 3.0 (0x0bc2:0xa0a4) + + [CF] os_win32.cpp: Add support for SAS disks behind Areca SAS + controllers. This includes SAS/SATA autodetection. + + Patch was provided by Hank Wu from Areca. + + [CF] ataidentify.cpp: Add some recent SATA features. + + [CF] smartctl: Add '--identify[=wnvb]' option. + Add new source files ataidentify.h/cpp. + + [CF] Makefile.am: Reformat lists of sources. + + [CF] Do not print HDD/SSD specific default attribute names if identify + data reports SSD/HDD device. + + [CF] drivedb.h updates: + - Intel 320 and 710 Series SSDs: Set '-F nologdir' + - Seagate Barracuda ES.2: Set '-F xerrorlba' + + [CF] Create branches RELEASE_5_4[0-3]_DRIVEDB with last drivedb.h file + compatible with smartmontools 5.4[0-3]. + + [CF] drivedb.h updates: + - SAMSUNG SpinPoint M40/60/80: HM120IC + - USB: Oxford (0x0928:0x0010) + - USB: Seagate External Drive/Cypress (0x0bc2:0x0503) + - USB: 0x1f75:0x0888 is Innostor IS888 + + [CF] smartctl: Print nominal media rotation rate in '-i' output (ATA). + + [CF] knowndrives.cpp: Fix missing '-F xerrorlba' in '-P show' output. + + [CF] os_win32.cpp: Use WMI to get serial number if + IOCTL_STORAGE_QUERY_PROPERTY is used. + + [CF] os_win32.cpp: Remove more Win9x/ME/NT4 specific code: ATA drive + number, GetDevicePowerState() handling. + + [CF] Add '-F xerrorlba' option/directive. + + [CF] Rework '-F' option handling. Add support for multiple '-F' options + and directives. + + [CF] Makefile.am: Fix typo in ACLOCAL_AMFLAGS. + + [CF] smartd.cpp: MailWarning(): Move variable declarations, use sizeof() + instead of numbers. + + [CF] smartd.cpp: Rework dnsname(). Print "[None]" instead of "[Unknown]" + if domain is not set. Print NIS domain only if supported. + + [CF] Windows smartd: Use gethostname/gethostbyname() from winsock. + Remove os_win32/hostname_win32.*. + + [CF] smartd: Include device identify info in warning emails (ticket #185). + Add SMARTD_DEVICEINFO environment variable. + + [CF] Add '-F nologdir' option/directive. + Prevents freeze of some Intel SSDs (ticket #214). + + [CF] smartd: Don't log ignored -W directive as critical. + + [CF] drivedb.h updates: + - Smart Storage Systems Xcel-10 SSDs: Move entry, change name + - Samsung: Remove very old and already commented out entries + - Seagate Momentus XT (Adv. Format) + - WD My Passport: 3 -> 2 entries, add 2TB + - USB: Imation (0x0718:0x1000) (ticket #231) + - USB: Initio (0x13fd:0x1040): unsupported + - USB: ASMedia USB 3.0 (0x174c:0x55aa): unsupported -> -d sat + - USB: PQI H560 (0x3538:0x0902) (ticket #232) + + [CF] smartctl: Override SMART disabled state with '-T permissive'. + + [CF] os_win32/daemon_win32.cpp: Drop remaining WinNT4 compatibility. + + [CF] Windows smartd: Add smartd.conf directives '-m console', + '-m active', '-m connected'. Send warning messages via + WTSSendMessage(). Remove use of MessageBox() which does no + longer work for services since Vista/2008. + + [CF] Fix 'smartctl -P show'. Regression from r3249. + + [CF] smartd.cpp: Fix setting of temporary environment in MailWarning(). + Stack space was passed to putenv() but variable was not unset + before return. Very old bug introduced 2003 in r1114. + + [CF] smartd.cpp: Add fflush() to support redirection of debug output + (Debian bug 681349). + + [CF] os_generic.cpp: Add missing int64.h (Debian bug 619208) + This obsoletes Debian patch fix-generic.diff. + + [CF] cciss.cpp: Fix build on GNU/kFreeBSD (Debian bug 676142). + This obsoletes Debian kfreebsd.patch. + + [CF] Windows: Drop backward compatibility with WinNT4. + + [CF] Windows: Drop backward compatibility with Win9x/ME. + +smartmontools 5.43 2012-06-30 + + [CF] drivedb.h USB updates: + - Toshiba Canvio Basics (0x0480:0xa006) + - A-DATA DashDrive (0x125f:0xa94a) + + [CF] drivedb.h: Hitachi Travelstar 7K500: *A362/3 variants + + [CF] Windows: Add Windows Server 2012 to get_os_version_str(). + + [CF] drivedb.h updates: + - Sandforce Driven SSDs: OWC Mercury Electra 3/6G SSD + - Seagate Momentus SpinPoint M8 + - Hitachi Deskstar 5K4000 + - Toshiba 2.5" HDD MK..61GSYN + - Seagate Barracuda (SATA 3Gb/s, 4K Sectors): 1TB, *DM003-* variant + + [CF] smartctl.8.in: Note performance impact of self-tests. + + [CF] os_win32.cpp: Add support for older Areca drivers which used a + different target id. Patch was provided by Hank Wu from Areca. + + [CF] smartctl.8.in: Add info about HP Smart Array controllers. + Original patch was provided by Don Brace from HP. + + [CF] os_freebsd.cpp: add SAT autodetection to '-d cciss,N' device type + (ticket #202). + Add missing freebsd_areca_device::m_encnum (regression from r3542). + Patch was provided by Don Brace from HP. + + [CF] os_linux.cpp: add SAT autodetection to '-d cciss,N' device type + (ticket #202). + + [CF] Makefile.am: FIXHTML modified for newer man2html versions. + + [CF] autogen.sh: automake 1.11.5 is OK. + [CF] man pages: Minor updates and syntax fixes. [CF] smartd.service.in: Add ExecReload and StandardOutput. diff --git a/INSTALL b/INSTALL index 1863c34..f63b04a 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Smartmontools installation instructions ======================================= -$Id: INSTALL 3555 2012-06-01 21:07:33Z chrfranke $ +$Id: INSTALL 3713 2012-11-23 21:26:17Z chrfranke $ Please also see the smartmontools home page: http://smartmontools.sourceforge.net/ @@ -97,7 +97,7 @@ Table of contents: E) Cygwin - The code was tested on Cygwin 1.7.7-1. It should also work on other + The code was tested on Cygwin 1.7.15-1. It should also work on other recent releases. Both Cygwin and Windows versions of smartmontools share the same code @@ -106,40 +106,14 @@ Table of contents: F) Windows - The code was tested on Windows 98SE, ME, NT4(SP5,SP6), 2000(SP4), - XP(up to SP3), 2003, Vista and Windows 7. - - -- Windows 9x/ME - - On 9x/ME, only standard (legacy) IDE/ATA devices 0-3 are supported. - The driver SMARTVSD.VXD must be present in WINDOWS\SYSTEM\IOSUBSYS - to get loaded at Windows startup. The default location in a new - installation of some versions of Windows is the WINDOWS\SYSTEM folder. - In this case, move SMARTVSD.VXD to WINDOWS\SYSTEM\IOSUBSYS and reboot - (http://support.microsoft.com/kb/265854/en-us). - - SMARTVSD.VXD relies on the standard IDE port driver ESDI_506.PDR. - If the system uses a vendor specific driver, access of SMART data - is not possible. - - Some ATA controllers (e.g. Promise) provided a custom SMARTVSD.VXD - for their Win9x/ME driver. To access SMART data from both the legacy - (/dev/h[a-d]) and this additional (/dev/hd[e-h]) controller, rename - this file to SMARTVSE.VXD. Open the file with a hex editor and replace - all occurrences of the string "SMARTVSD" with "SMARTVSE". Then reinstall - the original Windows SMARTVSD.VXD. - - To access SCSI and USB devices, an installed ASPI interface (WNASPI32.DLL) - is required. The code was tested with Adaptec Windows ASPI drivers 4.71.2. - (http://www.adaptec.com/en-US/support/_eol/scsi_sw/ASPI-4.70/) - Links to other ASPI drivers can be found at http://www.nu2.nu/aspi/. - - -- Windows NT4/2000/XP/2003/Vista/Win7 + The code was tested on Windows XP SP3, 2003, Vista, Windows 7 and + Windows 8 Release Preview. Support von Windows 9x/ME and NT4 was removed + after smartmontools 5.43. ATA or SATA devices are supported if the device driver implements the SMART IOCTLs or IOCTL_IDE_PASS_THROUGH or IOCTL_ATA_PASS_THROUGH. - The ATA SMART READ LOG command (smartctl -l, --log, -a, --all) is not - supported if only the SMART IOCTLs are implemented. + Only the latter provides full pass-through support which is needed + for all smartmontools features. SCSI and USB devices are accessed through SPTI. Special driver support is not required. @@ -458,8 +432,6 @@ Cross-compile statically linked 64-bit version with MinGW-w64: Tested on Cygwin and Debian Linux with MinGW-w64 from http://mingw-w64.sourceforge.net/. -WARNING: The smartmontools version for 64-bit Windows is still EXPERIMENTAL. - Cross-compile on Cygwin with old gcc-mingw 3.x: ./configure --build=$(./config.guess) \ @@ -532,10 +504,9 @@ use the following on MSYS or Cygwin: ../configure [... any MinGW option set from above ...] make config-vc10 - The MSVC project files (os_win32/smartmontools_vc10.sln, - os_win32/smart{ctl,d}_vc10.vcxproj) are included in SVN (but not in - source tarball). The target config-vc10 from a Makefile configured - for MinGW creates os_win32/{config,svnversion}_vc10.h from + The MSVC project files (os_win32/vc10/*) are included in SVN (but not + in source tarball). The target config-vc10 from a Makefile configured + for MinGW creates os_win32/vc10/{config,svnversion}.h from ./{config,svnversion}.h. The configure skript must be run outside of the source directory to avoid inclusion of the original config.h. @@ -621,7 +592,7 @@ The following files are installed if ./configure has no options: /usr/local/share/man/man8/smartctl.8 [Manual page] /usr/local/share/man/man8/smartd.8 [Manual page] /usr/local/share/doc/smartmontools/AUTHORS [Information about the authors and developers] -/usr/local/share/doc/smartmontools/CHANGELOG [A log of changes. Also see SVN] +/usr/local/share/doc/smartmontools/ChangeLog [A log of changes. Also see SVN] /usr/local/share/doc/smartmontools/COPYING [GNU General Public License Version 2] /usr/local/share/doc/smartmontools/INSTALL [Installation instructions: what you're reading!] /usr/local/share/doc/smartmontools/NEWS [Significant bugs discovered in old versions] diff --git a/Makefile.am b/Makefile.am index d105556..25f625e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,11 +1,11 @@ ## Process this file with automake to produce Makefile.in # -# $Id: Makefile.am 3545 2012-05-25 21:19:03Z chrfranke $ +# $Id: Makefile.am 3759 2013-01-26 21:11:02Z chrfranke $ # @SET_MAKE@ -ACLOCAL_AM_FLAGS = -I m4 +ACLOCAL_AMFLAGS = -I m4 # Make sure .cpp takes precedence to avoid compiling old .c file SUFFIXES = .cpp .c .s .o @@ -34,8 +34,9 @@ if NEED_REGEX AM_CPPFLAGS += -I$(srcdir)/regex endif -sbin_PROGRAMS = smartd \ - smartctl +sbin_PROGRAMS = \ + smartctl \ + smartd if ENABLE_DRIVEDB if OS_WIN32_MINGW @@ -45,115 +46,136 @@ endif endif -smartd_SOURCES = smartd.cpp \ - atacmdnames.cpp \ - atacmdnames.h \ - atacmds.cpp \ - atacmds.h \ - dev_ata_cmd_set.cpp \ - dev_ata_cmd_set.h \ - dev_interface.cpp \ - dev_interface.h \ - dev_tunnelled.h \ - drivedb.h \ - int64.h \ - knowndrives.cpp \ - knowndrives.h \ - scsicmds.cpp \ - scsicmds.h \ - scsiata.cpp \ - utility.cpp \ - utility.h +smartctl_SOURCES = \ + smartctl.cpp \ + smartctl.h \ + atacmdnames.cpp \ + atacmdnames.h \ + atacmds.cpp \ + atacmds.h \ + ataidentify.cpp \ + ataidentify.h \ + ataprint.cpp \ + ataprint.h \ + dev_ata_cmd_set.cpp \ + dev_ata_cmd_set.h \ + dev_interface.cpp \ + dev_interface.h \ + dev_tunnelled.h \ + drivedb.h \ + int64.h \ + knowndrives.cpp \ + knowndrives.h \ + scsicmds.cpp \ + scsicmds.h \ + scsiata.cpp \ + scsiprint.cpp \ + scsiprint.h \ + utility.cpp \ + utility.h + +smartctl_LDADD = @os_deps@ @os_libs@ +smartctl_DEPENDENCIES = @os_deps@ + +EXTRA_smartctl_SOURCES = \ + os_darwin.cpp \ + os_darwin.h \ + os_linux.cpp \ + os_linux.h \ + os_freebsd.cpp \ + os_freebsd.h \ + os_netbsd.cpp \ + os_netbsd.h \ + os_openbsd.cpp \ + os_openbsd.h \ + os_qnxnto.cpp \ + os_qnxnto.h \ + os_solaris.cpp \ + os_solaris.h \ + os_solaris_ata.s \ + os_win32.cpp \ + os_generic.cpp \ + os_generic.h \ + cciss.cpp \ + cciss.h \ + cissio_freebsd.h \ + dev_areca.cpp \ + dev_areca.h \ + dev_legacy.cpp \ + megaraid.h + +if OS_WIN32_MINGW + +smartctl_LDADD += smartctl_res.o +smartctl_DEPENDENCIES += smartctl_res.o + +endif + + +smartd_SOURCES = \ + smartd.cpp \ + atacmdnames.cpp \ + atacmdnames.h \ + atacmds.cpp \ + atacmds.h \ + dev_ata_cmd_set.cpp \ + dev_ata_cmd_set.h \ + dev_interface.cpp \ + dev_interface.h \ + dev_tunnelled.h \ + drivedb.h \ + int64.h \ + knowndrives.cpp \ + knowndrives.h \ + scsicmds.cpp \ + scsicmds.h \ + scsiata.cpp \ + utility.cpp \ + utility.h smartd_LDADD = @os_deps@ @os_libs@ @CAPNG_LDADD@ smartd_DEPENDENCIES = @os_deps@ -EXTRA_smartd_SOURCES = os_darwin.cpp \ - os_darwin.h \ - os_linux.cpp \ - os_linux.h \ - os_freebsd.cpp \ - os_freebsd.h \ - os_netbsd.cpp \ - os_netbsd.h \ - os_openbsd.cpp \ - os_openbsd.h \ - os_qnxnto.cpp \ - os_qnxnto.h \ - os_solaris.cpp \ - os_solaris.h \ - os_solaris_ata.s \ - os_win32.cpp \ - os_generic.cpp \ - os_generic.h \ - cciss.cpp \ - cciss.h \ - cissio_freebsd.h \ - dev_legacy.cpp \ - megaraid.h - +EXTRA_smartd_SOURCES = \ + os_darwin.cpp \ + os_darwin.h \ + os_linux.cpp \ + os_linux.h \ + os_freebsd.cpp \ + os_freebsd.h \ + os_netbsd.cpp \ + os_netbsd.h \ + os_openbsd.cpp \ + os_openbsd.h \ + os_qnxnto.cpp \ + os_qnxnto.h \ + os_solaris.cpp \ + os_solaris.h \ + os_solaris_ata.s \ + os_win32.cpp \ + os_generic.cpp \ + os_generic.h \ + cciss.cpp \ + cciss.h \ + cissio_freebsd.h \ + dev_areca.cpp \ + dev_areca.h \ + dev_legacy.cpp \ + megaraid.h if OS_WIN32_MINGW smartd_SOURCES += \ os_win32/daemon_win32.cpp \ os_win32/daemon_win32.h \ - os_win32/hostname_win32.cpp \ - os_win32/hostname_win32.h \ os_win32/syslog_win32.cpp \ os_win32/syslog.h -endif - -smartctl_SOURCES= smartctl.cpp \ - smartctl.h \ - atacmdnames.cpp \ - atacmdnames.h \ - atacmds.cpp \ - atacmds.h \ - ataprint.cpp \ - ataprint.h \ - dev_ata_cmd_set.cpp \ - dev_ata_cmd_set.h \ - dev_interface.cpp \ - dev_interface.h \ - dev_tunnelled.h \ - drivedb.h \ - int64.h \ - knowndrives.cpp \ - knowndrives.h \ - scsicmds.cpp \ - scsicmds.h \ - scsiata.cpp \ - scsiprint.cpp \ - scsiprint.h \ - utility.cpp \ - utility.h - +smartd_LDADD += smartd_res.o +smartd_DEPENDENCIES += smartd_res.o -smartctl_LDADD = @os_deps@ @os_libs@ -smartctl_DEPENDENCIES = @os_deps@ +endif -EXTRA_smartctl_SOURCES = os_linux.cpp \ - os_linux.h \ - os_freebsd.cpp \ - os_freebsd.h \ - os_netbsd.cpp \ - os_netbsd.h \ - os_openbsd.cpp \ - os_openbsd.h \ - os_qnxnto.cpp \ - os_qnxnto.h \ - os_solaris.cpp \ - os_solaris.h \ - os_win32.cpp \ - os_generic.cpp \ - os_generic.h \ - cciss.cpp \ - cciss.h \ - cissio_freebsd.h \ - dev_legacy.cpp \ - megaraid.h if NEED_GETOPT_LONG @@ -270,15 +292,17 @@ man_MANS = smartd.conf.5 \ endif docsdir=$(docdir) -docs_DATA = AUTHORS \ - CHANGELOG \ - COPYING \ - INSTALL \ - NEWS \ - README \ - TODO \ - WARNINGS \ - smartd.conf +docs_DATA = \ + AUTHORS \ + ChangeLog \ + ChangeLog-5.0-6.0 \ + COPYING \ + INSTALL \ + NEWS \ + README \ + TODO \ + WARNINGS \ + smartd.conf examplesdir=$(exampledir) examples_DATA = \ @@ -321,6 +345,10 @@ uninstall-sysconfDATA: echo " rm -f $$f"; \ rm -f "$$f" +# automake does not allow 'sysconf_SCRIPTS' +sysscriptdir = $(sysconfdir) +sysscript_SCRIPTS = smartd_warning.sh + EXTRA_DIST = \ autogen.sh \ smartd.initd.in \ @@ -330,6 +358,7 @@ EXTRA_DIST = \ smartd.conf.5.in \ smartd.conf \ smartd.service.in \ + smartd_warning.sh.in \ update-smart-drivedb.in \ m4/pkg.m4 \ os_darwin/SMART.in \ @@ -339,10 +368,13 @@ EXTRA_DIST = \ os_win32/runcmd.c \ os_win32/runcmda.exe.manifest \ os_win32/runcmdu.exe.manifest \ - os_win32/syslogevt.c \ + os_win32/smartctl_res.rc.in \ + os_win32/smartd_res.rc.in \ + os_win32/smartd_warning.cmd \ os_win32/syslogevt.mc \ os_win32/update-smart-drivedb.nsi \ os_win32/wbemcli_small.h \ + os_win32/wtssendmsg.c \ $(docs_DATA) \ $(examples_DATA) \ $(examples_SCRIPTS) @@ -363,6 +395,7 @@ CLEANFILES = \ smartd.initd \ smartd.freebsd.initd \ smartd.service \ + smartd_warning.sh \ svnversion.h \ update-smart-drivedb \ SMART @@ -386,7 +419,7 @@ utility.o: svnversion.h if IS_SVN_BUILD # Get version info from SVN -svnversion.h: CHANGELOG Makefile $(svn_deps) +svnversion.h: ChangeLog Makefile $(svn_deps) echo '/* svnversion.h. Generated by Makefile from svn info. */' > $@ (cd $(srcdir) \ && svnversion 2>/dev/null | sed -n 's,^\([0-9].*\),REV "\1",p' \ @@ -396,9 +429,9 @@ svnversion.h: CHANGELOG Makefile $(svn_deps) else # SVN not available, guess version info from Id strings -svnversion.h: CHANGELOG Makefile +svnversion.h: ChangeLog Makefile echo '/* svnversion.h. Generated by Makefile from Id strings. */' > $@ - (cd $(srcdir) && cat CHANGELOG Makefile.am configure.in smart*.in *.cpp *.h *.s) \ + (cd $(srcdir) && cat ChangeLog Makefile.am configure.ac smart*.in *.cpp *.h *.s) \ | sed -n 's,^.*\$$[I][d]: [^ ]* \([0-9][0-9]* [0-9][-0-9]* [0-9][:0-9]*\)[^:0-9][^$$]*\$$.*$$,\1,p' \ | sort -n -r \ | sed -n 'h;s,^\([^ ]*\) .*$$,REV "\1",p;g;s,^[^ ]* \([^ ]*\) .*$$,DATE "\1",p;g;s,^[^ ]* [^ ]* \([^ ]*\)$$,TIME "\1",p;q' \ @@ -424,6 +457,9 @@ update-smart-drivedb: update-smart-drivedb.in config.status $(SHELL) ./config.status --file=$@ chmod +x $@ +smartd_warning.sh: smartd_warning.sh.in config.status + $(SHELL) ./config.status --file=$@ + chmod +x $@ if INSTALL_INITSCRIPT if OS_DARWIN @@ -494,8 +530,8 @@ systemdsystemunit_DATA = smartd.service endif smartd.service: smartd.service.in Makefile - sed "s|/usr/local/sbin/smartd|$(sbindir)/smartd|g; \ - s|/usr/local/etc/sysconfig/smartmontools|$(sysconfdir)/sysconfig/smartmontools|g" \ + sed -e 's|/usr/local/sbin/smartd|$(sbindir)/smartd|g' \ + -e 's|/usr/local/etc/sysconfig/smartmontools|$(sysconfdir)/sysconfig/smartmontools|g' \ $(srcdir)/smartd.service.in > $@ if ENABLE_CAPABILITIES @@ -523,17 +559,18 @@ MAN_ATTRIBUTELOG = sed '/^\.\\" %IF ENABLE_ATTRIBUTELOG/,/^\.\\" %ENDIF ENABLE_A endif MAN_FILTER = \ - sed "s|CURRENT_SVN_VERSION|$(releaseversion)|g; \ - s|CURRENT_SVN_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g; \ - s|CURRENT_SVN_REV|`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`|g; \ - s|/usr/local/share/man/|$(mandir)/|g; \ - s|/usr/local/sbin/|$(sbindir)/|g; \ - s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g; \ - s|/usr/local/share/doc/smartmontools/examplescripts/|!exampledir!|g; \ - s|/usr/local/share/doc/smartmontools/|$(docsdir)/|g; \ - s|!exampledir!|$(exampledir)/|g; \ - s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g; \ - s|/usr/local/etc/smart_drivedb\\.h|$(sysconfdir)/smart_drivedb\\.h|g" | \ + sed -e 's|CURRENT_SVN_VERSION|$(releaseversion)|g' \ + -e "s|CURRENT_SVN_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g" \ + -e "s|CURRENT_SVN_REV|`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`|g" \ + -e 's|/usr/local/share/man/|$(mandir)/|g' \ + -e 's|/usr/local/sbin/|$(sbindir)/|g' \ + -e 's|/usr/local/etc/rc\.d/init.d/|$(initddir)/|g' \ + -e 's|/usr/local/share/doc/smartmontools/examplescripts/|!exampledir!|g' \ + -e 's|/usr/local/share/doc/smartmontools/|$(docsdir)/|g' \ + -e 's|!exampledir!|$(exampledir)/|g' \ + -e 's|/usr/local/etc/smartd\.conf|$(sysconfdir)/smartd.conf|g' \ + -e 's|/usr/local/etc/smartd_warning\.|$(sysconfdir)/smartd_warning.|g' \ + -e 's|/usr/local/etc/smart_drivedb\.h|$(sysconfdir)/smart_drivedb.h|g' | \ $(MAN_ATTRIBUTELOG) | \ $(MAN_CAPABILITIES) | \ $(MAN_DRIVEDB) | \ @@ -567,11 +604,11 @@ MAN2HTML = man2html MAN2TXT = groff -man -Tascii -P'-bcou' # Remove HTTP header and fix links in man2html output -FIXHTML = sed '1s,^Content-type.*,,' \ - | sed 's,,,g' \ - | sed 's,,,g' \ - | sed 's,\([^<]*\),\1,g' \ - | sed 's,\([^<]*\),\1,g' +FIXHTML = sed -e '1s,^Content-type.*,,' \ + -e 's,,,g' \ + -e 's,,,g' \ + -e 's,\([^<]*\),\1,g' \ + -e 's,\([^<]*\),\1,g' # Convert man pages into .html and .txt @@ -612,6 +649,30 @@ check: if OS_WIN32_MINGW +# Windows resources + +smartctl_res.o: smartctl_res.rc + $(WINDRES) $< $@ + +smartd_res.o: smartd_res.rc syslogevt.rc + $(WINDRES) -I. $< $@ + +# Convert version for VERSIONINFO resource: 6.1 r3754 -> 6.1.0.3754 +WIN_RC_FILTER = \ + ( ver=`echo '$(PACKAGE_VERSION).0' | sed -n 's,^\([0-9]*\.[0-9]*\.[0-9]*\).*$$,\1,p'`; \ + rev=`sed -n 's,^.*REV[^"]*"\([0-9]*\).*$$,\1,p' svnversion.h`; \ + txtver="$${ver:-0.0.0}.$${rev:-0}"; binver=`echo "$$txtver" | sed 's|\.|,|g'`; \ + sed -e "s|@BINARY_VERSION@|$$binver|g" -e "s|@TEXT_VERSION@|$$txtver|g"; ) + +smartctl_res.rc: os_win32/smartctl_res.rc.in Makefile svnversion.h + cat $< | $(WIN_RC_FILTER) > $@ + +smartd_res.rc: os_win32/smartd_res.rc.in Makefile svnversion.h + cat $< | $(WIN_RC_FILTER) > $@ + +syslogevt.rc: os_win32/syslogevt.mc + $(WINDMC) -b $< + # Definitions for Windows distribution if OS_WIN64 @@ -631,13 +692,10 @@ EXEFILES_WIN32 = \ $(exedir_win32)/smartctl.exe \ $(exedir_win32)/smartctl-nc.exe \ $(exedir_win32)/smartd.exe \ + $(exedir_win32)/smartd_warning.cmd \ $(exedir_win32)/runcmda.exe \ - $(exedir_win32)/runcmdu.exe - -if OS_WIN32_WINDMC -EXEFILES_WIN32 += \ - $(exedir_win32)/syslogevt.exe -endif + $(exedir_win32)/runcmdu.exe \ + $(exedir_win32)/wtssendmsg.exe if ENABLE_DRIVEDB if OS_WIN32_NSIS @@ -649,7 +707,8 @@ endif FILES_WIN32 = \ $(EXEFILES_WIN32) \ $(docdir_win32)/AUTHORS.txt \ - $(docdir_win32)/CHANGELOG.txt \ + $(docdir_win32)/ChangeLog.txt \ + $(docdir_win32)/ChangeLog-5.0-6.0.txt \ $(docdir_win32)/COPYING.txt \ $(docdir_win32)/INSTALL.txt \ $(docdir_win32)/NEWS.txt \ @@ -676,8 +735,11 @@ CLEANFILES += \ $(FILES_WIN32) \ runcmdu.exe \ smartctl-nc.exe smartctl-nc.exe.tmp \ - syslogevt.exe syslogevt.h syslogevt.o \ - syslogevt.res.o syslogevt.rc syslogevt_*.bin \ + smartctl_res.rc smartctl_res.o \ + smartd_res.rc smartd_res.o \ + syslogevt.h syslogevt.o \ + syslogevt.rc syslogevt_*.bin \ + wtssendmsg.exe \ update-smart-drivedb.exe \ distdir.mkdir @@ -758,6 +820,10 @@ $(exedir_win32)/%.exe.manifest: $(srcdir)/os_win32/%.exe.manifest $(UNIX2DOS) < $< > $@ touch -r $< $@ +$(exedir_win32)/%.cmd: $(srcdir)/os_win32/%.cmd + $(UNIX2DOS) < $< > $@ + touch -r $< $@ + $(docdir_win32)/%.html: %.html $(UNIX2DOS) < $< > $@ touch -r $< $@ @@ -794,36 +860,32 @@ smartctl-nc.exe: smartctl.exe else echo "EXE patch failed"; exit 1; fi mv -f $@.tmp $@ -# Build runcmd?.exe +# Build runcmd?.exe and wtssendmsg.exe runcmdu.exe: os_win32/runcmd.c $(CC) -Os -o $@ $< -if OS_WIN32_WINDMC -# Build syslogevt.exe event message file tool - -syslogevt.exe: syslogevt.o syslogevt.res.o - $(LINK) $^ +wtssendmsg.exe: os_win32/wtssendmsg.c + $(CC) -Os -o $@ $< -lwtsapi32 -syslogevt.o: os_win32/syslogevt.c syslogevt.rc - $(CC) -c -I. -Os -o $@ $< +# Build os_win32/vc10/{config.h,smart*.rc,svnversion.h} for MSVC10 from MinGW files -syslogevt.res.o: syslogevt.rc - $(WINDRES) $< $@ - -syslogevt.rc: os_win32/syslogevt.mc - $(WINDMC) -b $< -endif - -# Build {config,svnversion}_vc10.h for MSVC10 from MinGW {config,svnversion}.h +config-vc10: $(srcdir)/os_win32/vc10/config.h \ + $(srcdir)/os_win32/vc10/smartctl_res.rc \ + $(srcdir)/os_win32/vc10/smartd_res.rc \ + $(srcdir)/os_win32/vc10/svnversion.h -config-vc10: $(srcdir)/os_win32/config_vc10.h $(srcdir)/os_win32/svnversion_vc10.h - -$(srcdir)/os_win32/config_vc10.h: config.h Makefile - sed -e '1i/* config_vc10.h. Generated from config.h by Makefile. */' \ +$(srcdir)/os_win32/vc10/config.h: config.h Makefile + sed -e '1i/* os_win32/vc10/config.h. Generated from config.h by Makefile. */' \ -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|INTTYPES_H\|[DK_]*NTDDDISK_H\|STRINGS_H\|STRTOULL\|UNISTD_H\|WORKING_SNPRINTF\) 1$$,/* #undef HAVE_\1 */,' \ -e 's,^\(#define SMARTMONTOOLS_BUILD_HOST "[^-]*\)[^"]*,\1-pc-w32vc10,' $< > $@ -$(srcdir)/os_win32/svnversion_vc10.h: svnversion.h +$(srcdir)/os_win32/vc10/svnversion.h: svnversion.h + cp $< $@ + +$(srcdir)/os_win32/vc10/smartctl_res.rc: smartctl_res.rc + cp $< $@ + +$(srcdir)/os_win32/vc10/smartd_res.rc: smartd_res.rc cp $< $@ endif diff --git a/NEWS b/NEWS index a5b41cd..1b7edfe 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,73 @@ smartmontools NEWS ------------------ -$Id: NEWS 3557 2012-06-04 19:50:21Z chrfranke $ +$Id: NEWS 3808 2013-04-18 17:30:12Z chrfranke $ The most up-to-date version of this file is: http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/NEWS?view=markup Date +Summary: smartmontools release 6.2 +----------------------------------------------------------- +- Cygwin: 64-bit compile fixes. + +Date 2013-03-16 +Summary: smartmontools release 6.1 +----------------------------------------------------------- +- smartctl '-l directory': improved output format. +- smartctl: Fix parsing of '-l select,cont+SIZE' option. +- smartctl prints ATA Additional Product Id (OEM Id). +- smartctl '-s/-g wcache' for SCSI devices to control write cache. +- smartctl '-s/-g rcache' for SCSI devices to control read cache. +- smartctl prints more info for SCSI devices: media rotation rate, + form factor, physical block size, lowest LBA alignement, + logical block provisioning, disk protection type + and selftest progress status. +- smartctl '--identify' updated for latest ATA ACS-3 spec. +- smartd runs /etc/smartd_warning.sh to generate warning emails + (Windows: smartd_warning.cmd). +- smartd '-w PATH' option to specify this executable. +- smartd '-d ignore' directive. +- smartd DEVICESCAN ignores devices already specified. +- smartd: added support for state persistence ('-s') and attribute logging + ('-A') for SCSI devices. +- smartd '-W' directive uses ATA attribute 190 if 194 is missing. +- Support of larger SCSI defect lists via READ DEFECT(12). +- Device type '-d usbjmicron,p' for Prolific USB bridges. +- Many HDD, SSD and USB additions to drive database. +- Linux: Support for SAS disks behind Areca controllers. +- Linux: Improved support for SATA disks on LSI/Megaraid controllers +- Linux: disks on MegaRaid controllers are automatically scanned +- FreeBSD: Support for SAS disks behind Areca controllers. +- FreeBSD: Enhanced ATA command support for 3ware. +- FreeBSD: Support for 3ware 9750 (/dev/twsX). +- FreeBSD: Fixed support for 48-bit ATA commands on legacy controllers + with ATACAM driver. +- FreeBSD: Improved support for SAS/SCSI disks on LSI/Megaraid controllers. +- Windows: smartd.conf '-M exec' supports path names with spaces. +- Windows: Tool wtssendmsg.exe to handle smartd.conf '-m console'. +- Windows: DEVICESCAN now supports up to 128 drives. +- Windows: smartctl.exe and smartd.exe include VERSIONINFO resource. +- Windows: smartd.exe includes MESSAGETABLE resource. +- Windows: syslogevt.exe is no longer provided. + +Date 2012-10-10 +Summary: smartmontools release 6.0 +----------------------------------------------------------- +- option/directive '-F nologdir' and '-F xerrorlba'. +- smartctl '--identify' option. +- smartctl prints nominal media rotation rate (ATA). +- smartctl prints SATA version and speed. +- smartctl '-l sataphy' works for CD/DVD drives also. +- smartctl '-x' includes ATA Device Statistics. +- smartd warning emails include device identify info. +- smartd '-d' output is flushed to support redirection. +- HDD, SSD and USB additions to drive database. +- Windows smartd: smartd.conf directives '-m console', + '-m active', '-m connected'. +- Windows: Support for SAS disks behind Areca controllers. +- Windows: Win9x/ME and NT4 are no longer supported. + +Date 2012-06-30 Summary: smartmontools release 5.43 ----------------------------------------------------------- - smartctl options '-g, --get' and '-s, --set' to get/set @@ -21,7 +83,8 @@ Summary: smartmontools release 5.43 - Controller-independent SAT detection: '-d sat,auto[+TYPE]'. - smartd.conf DEFAULT directive. - Many HDD, SSD and USB additions to drive database. -- Linux and FreeBSD: Support for SATA disks behind Areca SAS controllers. +- Linux and FreeBSD: Support for SATA disks behind Areca SAS + RAID controllers and HP Smart Array controllers. - Windows: Support for SATA disks behind Areca controllers. - Windows smartd: directives '-l offlinests,ns' and '-l selfteststs,ns'. diff --git a/README b/README index aa7278e..5a6d12e 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows. ========================================================== -$Id: README 2844 2009-07-18 12:59:21Z chrfranke $ +$Id: README 3727 2012-12-13 17:23:06Z samm2 $ == HOME == The home for smartmontools is located at: @@ -27,8 +27,8 @@ 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. +example COPYING); if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. == CREDITS == diff --git a/atacmdnames.cpp b/atacmdnames.cpp index ecd483b..9ba90f6 100644 --- a/atacmdnames.cpp +++ b/atacmdnames.cpp @@ -1,13 +1,10 @@ /* * atacmdnames.cpp * - * 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-8 Philip Williams + * Copyright (C) 2012 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 @@ -15,8 +12,7 @@ * 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. + * (for example COPYING); If not, see . * */ @@ -24,32 +20,33 @@ #include #include -#define COMMAND_TABLE_SIZE 256 - -const char *atacmdnames_c_cvsid="$Id: atacmdnames.cpp,v 1.17 2008/03/29 23:41:28 shattered Exp $" ATACMDNAMES_H_CVSID; +const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 3670 2012-10-31 22:00:50Z chrfranke $" + 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_reserved_mcpt[] = "[RESERVED FOR MEDIA CARD PASS THROUGH]"; // ACS-3: Reserved const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]"; const char cmd_seek_ret4[] = "SEEK [RET-4]"; -const char *command_table[COMMAND_TABLE_SIZE] = { +// Tables B.3 and B.4 of T13/2161-D (ACS-3) Revision 4, September 4, 2012 + +const char * const command_table[] = { /*-------------------------------------------------- 00h-0Fh -----*/ "NOP", cmd_reserved, cmd_reserved, - "CFA REQUEST EXTENDED ERROR CODE", - cmd_reserved, + "CFA REQUEST EXTENDED ERROR", cmd_reserved, cmd_reserved, + "DATA SET MANAGEMENT", // ACS-2 cmd_reserved, "DEVICE RESET", cmd_reserved, cmd_reserved, - cmd_reserved, + "REQUEST SENSE DATA EXT", // ACS-2 cmd_reserved, cmd_reserved, cmd_reserved, @@ -74,46 +71,46 @@ const char *command_table[COMMAND_TABLE_SIZE] = { /*-------------------------------------------------- 20h-2Fh -----*/ "READ SECTOR(S)", "READ SECTOR(S) [OBS-5]", - "READ LONG (w/ retry) [OBS-4]", + "READ LONG [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", + "READ DMA QUEUED EXT [OBS-ACS-2]", + "READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]", cmd_reserved, "READ MULTIPLE EXT", "READ STREAM DMA", - "READ STREAM PIO", + "READ STREAM", 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 SECTOR(S) (w/o retry) [OBS-5]", + "WRITE LONG [OBS-4]", + "WRITE LONG (w/o retry) [OBS-4]", "WRITE SECTORS(S) EXT", "WRITE DMA EXT", - "WRITE DMA QUEUED EXT", - "SET MAX ADDRESS EXT", + "WRITE DMA QUEUED EXT [OBS-ACS-2]", + "SET NATIVE MAX ADDRESS EXT [OBS-ACS-3]", "CFA WRITE SECTORS WITHOUT ERASE", "WRITE MULTIPLE EXT", "WRITE STREAM DMA", - "WRITE STREAM PIO", + "WRITE STREAM", "WRITE VERIFY [OBS-4]", "WRITE DMA FUA EXT", - "WRITE DMA QUEUED FUA EXT", + "WRITE DMA QUEUED FUA EXT [OBS-ACS-2]", "WRITE LOG EXT", /*-------------------------------------------------- 40h-4Fh -----*/ "READ VERIFY SECTOR(S)", - "READ VERIFY SECTOR(S) [OBS-5]", + "READ VERIFY SECTOR(S) (w/o retry) [OBS-5]", "READ VERIFY SECTOR(S) EXT", cmd_reserved, cmd_reserved, + "WRITE UNCORRECTABLE EXT", // ATA-8 cmd_reserved, - cmd_reserved, - cmd_reserved, + "READ LOG DMA EXT", // ATA-8 cmd_reserved, cmd_reserved, cmd_reserved, @@ -130,22 +127,22 @@ const char *command_table[COMMAND_TABLE_SIZE] = { cmd_reserved, cmd_reserved, cmd_reserved, + "WRITE LOG DMA EXT", // ATA-8 cmd_reserved, cmd_reserved, cmd_reserved, - cmd_reserved, - cmd_reserved, - cmd_reserved, - cmd_reserved, - cmd_reserved, - cmd_reserved, + "TRUSTED NON-DATA", // ATA-8 + "TRUSTED RECEIVE", // ATA-8 + "TRUSTED RECEIVE DMA", // ATA-8 + "TRUSTED SEND", // ATA-8 + "TRUSTED SEND DMA", // ATA-8 /*-------------------------------------------------- 60h-6Fh -----*/ - "READ FPDMA QUEUED", - "WRITE FPDMA QUEUED", - cmd_reserved_sa, - cmd_reserved_sa, - cmd_reserved_sa, + "READ FPDMA QUEUED", // ATA-8 + "WRITE FPDMA QUEUED", // ATA-8 cmd_reserved_sa, + "NCQ QUEUE MANAGEMENT", // ACS-3 + "SEND FPDMA QUEUED", // ACS-3 + "RECEIVE FPDMA QUEUED", // ACS-3 cmd_reserved_sa, cmd_reserved_sa, cmd_reserved, @@ -164,8 +161,8 @@ const char *command_table[COMMAND_TABLE_SIZE] = { cmd_seek_ret4, cmd_seek_ret4, cmd_seek_ret4, - cmd_seek_ret4, - cmd_seek_ret4, + "SET DATE & TIME EXT", // ACS-3 + "ACCESSIBLE MAX ADDRESS CONFIGURATION", // ACS-3 cmd_seek_ret4, cmd_seek_ret4, cmd_seek_ret4, @@ -194,7 +191,7 @@ const char *command_table[COMMAND_TABLE_SIZE] = { "EXECUTE DEVICE DIAGNOSTIC", "INITIALIZE DEVICE PARAMETERS [OBS-6]", "DOWNLOAD MICROCODE", - cmd_reserved, + "DOWNLOAD MICROCODE DMA", // ACS-2 "STANDBY IMMEDIATE [RET-4]", "IDLE IMMEDIATE [RET-4]", "STANDBY [RET-4]", @@ -210,7 +207,7 @@ const char *command_table[COMMAND_TABLE_SIZE] = { /*-------------------------------------------------- A0h-AFh -----*/ "PACKET", "IDENTIFY PACKET DEVICE", - "SERVICE", + "SERVICE [OBS-ACS-2]", cmd_reserved, cmd_reserved, cmd_reserved, @@ -226,21 +223,21 @@ const char *command_table[COMMAND_TABLE_SIZE] = { cmd_reserved, /*-------------------------------------------------- B0h-BFh -----*/ "SMART", - "DEVICE CONFIGURATION", - cmd_reserved, + "DEVICE CONFIGURATION [OBS-ACS-3]", cmd_reserved, cmd_reserved, + "SANITIZE DEVICE", // ACS-2 cmd_reserved, - cmd_reserved, - cmd_reserved, - cmd_reserved_cf, - cmd_reserved_cf, - cmd_reserved_cf, + "NV CACHE [OBS-ACS-3]", // ATA-8 cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, /*-------------------------------------------------- C0h-CFh -----*/ "CFA ERASE SECTORS [VS IF NO CFA]", cmd_vendor_specific, @@ -249,18 +246,18 @@ const char *command_table[COMMAND_TABLE_SIZE] = { "READ MULTIPLE", "WRITE MULTIPLE", "SET MULTIPLE MODE", - "READ DMA QUEUED", + "READ DMA QUEUED [OBS-ACS-2]", "READ DMA", - "READ DMA [OBS-5]", + "READ DMA (w/o retry) [OBS-5]", "WRITE DMA", - "WRITE DMA [OBS-5]", - "WRITE DMA QUEUED", + "WRITE DMA (w/o retry) [OBS-5]", + "WRITE DMA QUEUED [OBS-ACS-2]", "CFA WRITE MULTIPLE WITHOUT ERASE", "WRITE MULTIPLE FUA EXT", cmd_reserved, /*-------------------------------------------------- D0h-DFh -----*/ cmd_reserved, - "CHECK MEDIA CARD TYPE", + "CHECK MEDIA CARD TYPE [OBS-ACS-2]", cmd_reserved_mcpt, cmd_reserved_mcpt, cmd_reserved_mcpt, @@ -269,12 +266,12 @@ const char *command_table[COMMAND_TABLE_SIZE] = { cmd_reserved, cmd_reserved, cmd_reserved, - "GET MEDIA STATUS", + "GET MEDIA STATUS [OBS-8]", "ACKNOWLEDGE MEDIA CHANGE [RET-4]", "BOOT POST-BOOT [RET-4]", "BOOT PRE-BOOT [RET-4]", - "MEDIA LOCK", - "MEDIA UNLOCK", + "MEDIA LOCK [OBS-8]", + "MEDIA UNLOCK [OBS-8]", /*-------------------------------------------------- E0h-EFh -----*/ "STANDBY IMMEDIATE", "IDLE IMMEDIATE", @@ -285,14 +282,11 @@ const char *command_table[COMMAND_TABLE_SIZE] = { "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(). */ + "READ BUFFER DMA", // ACS-2 (was: WRITE SAME [RET-4]) "FLUSH CACHE EXT", - cmd_reserved, + "WRITE BUFFER DMA", // ACS-2 "IDENTIFY DEVICE", - "MEDIA EJECT", + "MEDIA EJECT [OBS-8]", "IDENTIFY DEVICE DMA [OBS-4]", "SET FEATURES", /*-------------------------------------------------- F0h-FFh -----*/ @@ -304,8 +298,8 @@ const char *command_table[COMMAND_TABLE_SIZE] = { "SECURITY FREEZE LOCK", "SECURITY DISABLE PASSWORD", cmd_vendor_specific, - "READ NATIVE MAX ADDRESS", - "SET MAX", + "READ NATIVE MAX ADDRESS [OBS-ACS-3]", + "SET MAX ADDRESS [OBS-ACS-3]", cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific, @@ -314,6 +308,9 @@ const char *command_table[COMMAND_TABLE_SIZE] = { cmd_vendor_specific }; +typedef char ASSERT_command_table_size[ + sizeof(command_table)/sizeof(command_table[0]) == 256 ? 1 : -1]; + /* 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 @@ -321,33 +318,28 @@ const char *command_table[COMMAND_TABLE_SIZE] = { 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]"; + return "NOP [Don't abort queued commands] [OBS-ACS-2]"; default: - return "NOP [Reserved subcommand]"; + return "NOP [Reserved subcommand] [OBS-ACS-2]"; } case 0x92: /* DOWNLOAD MICROCODE */ switch (f_reg) { case 0x01: - return "DOWNLOAD MICROCODE [Temporary]"; + return "DOWNLOAD MICROCODE [Temporary] [OBS-8]"; + case 0x03: + return "DOWNLOAD MICROCODE [Save with offsets]"; // ATA-8 case 0x07: return "DOWNLOAD MICROCODE [Save]"; + case 0x0e: + return "DOWNLOAD MICROCODE [Save for future use]"; // ACS-3 + case 0x0f: + return "DOWNLOAD MICROCODE [Activate]"; // ACS-3 default: return "DOWNLOAD MICROCODE [Reserved subcommand]"; } @@ -379,36 +371,27 @@ const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]"; default: if (f_reg >= 0xE0) - return "[Vendor specific SMART command]"; + return "SMART [Vendor specific subcommand]"; else - return "[Reserved SMART command]"; + return "SMART [Reserved subcommand]"; } case 0xB1: /* DEVICE CONFIGURATION */ switch (f_reg) { case 0xC0: - return "DEVICE CONFIGURATION RESTORE"; + return "DEVICE CONFIGURATION RESTORE [OBS-ACS-3]"; case 0xC1: - return "DEVICE CONFIGURATION FREEZE LOCK"; + return "DEVICE CONFIGURATION FREEZE LOCK [OBS-ACS-3]"; case 0xC2: - return "DEVICE CONFIGURATION IDENTIFY"; + return "DEVICE CONFIGURATION IDENTIFY [OBS-ACS-3]"; case 0xC3: - return "DEVICE CONFIGURATION SET"; + return "DEVICE CONFIGURATION SET [OBS-ACS-3]"; default: - return "DEVICE CONFIGURATION [Reserved command]"; + return "DEVICE CONFIGURATION [Reserved subcommand] [OBS-ACS-3]"; } - 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]"; + return "SET FEATURES [Enable 8-bit PIO] [OBS-3]"; // Now CFA case 0x02: return "SET FEATURES [Enable write cache]"; case 0x03: @@ -422,39 +405,45 @@ const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { case 0x07: return "SET FEATURES [Set device spin-up]"; case 0x09: - return "SET FEATURES [Reserved (address offset)]"; + return "SET FEATURES [Reserved (address offset)] [OPS-ACS-3]"; case 0x0A: return "SET FEATURES [Enable CFA power mode 1]"; case 0x10: - return "SET FEATURES [Reserved for Serial ATA]"; + return "SET FEATURES [Enable SATA feature]"; // ACS-3 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]"; + return "SET FEATURES [Disable Media Status Notf] [OBS-8]"; case 0x33: return "SET FEATURES [Disable retry] [OBS-4]"; + case 0x41: + return "SET FEATURES [Enable Free-fall Control]"; // ATA-8 case 0x42: - return "SET FEATURES [Enable AAM]"; + return "SET FEATURES [Enable AAM] [OBS-ACS-2]"; case 0x43: return "SET FEATURES [Set Max Host I/F S Times]"; case 0x44: return "SET FEATURES [Length of VS data] [OBS-4]"; + case 0x4a: + return "SET FEATURES [Ext. Power Conditions]"; // ACS-2 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]"; + return "SET FEATURES [Enable release interrupt] [OBS-ACS-2]"; case 0x5E: - return "SET FEATURES [Enable SERVICE interrupt]"; + return "SET FEATURES [Enable SERVICE interrupt] [OBS-ACS-2]"; case 0x66: return "SET FEATURES [Disable revert defaults]"; + case 0x69: + return "SET FEATURES [LPS Error Reporting Control]"; // ACS-2 case 0x77: return "SET FEATURES [Disable ECC] [OBS-4]"; case 0x81: - return "SET FEATURES [Disable 8-bit PIO]"; + return "SET FEATURES [Disable 8-bit PIO] [OBS-3]"; // Now CFA case 0x82: return "SET FEATURES [Disable write cache]"; case 0x84: @@ -470,9 +459,9 @@ const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { case 0x8A: return "SET FEATURES [Disable CFA power mode 1]"; case 0x90: - return "SET FEATURES [Reserved for Serial ATA]"; + return "SET FEATURES [Disable SATA feature]"; // ACS-3 case 0x95: - return "SET FEATURES [Enable Media Status Notf]"; + return "SET FEATURES [Enable Media Status Notf] [OBS-8]"; case 0x99: return "SET FEATURES [Enable retries] [OBS-4]"; case 0x9A: @@ -483,16 +472,20 @@ const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { return "SET FEATURES [Set max prefetch] [OBS-4]"; case 0xBB: return "SET FEATURES [4 bytes VS data] [OBS-4]"; + case 0xC1: + return "SET FEATURES [Disable Free-fall Control]"; // ATA-8 case 0xC2: - return "SET FEATURES [Disable AAM]"; + return "SET FEATURES [Disable AAM] [OBS-ACS-2]"; + case 0xC3: + return "SET FEATURES [Sense Data Reporting]"; // ACS-2 case 0xCC: return "SET FEATURES [Enable revert to defaults]"; case 0xDD: - return "SET FEATURES [Disable release interrupt]"; + return "SET FEATURES [Disable release interrupt] [OBS-ACS-2]"; case 0xDE: - return "SET FEATURES [Disable SERVICE interrupt]"; + return "SET FEATURES [Disable SERVICE interrupt] [OBS-ACS-2]"; case 0xE0: - return "SET FEATURES [Obsolete subcommand]"; + return "SET FEATURES [Vendor specific] [OBS-7]"; default: if (f_reg >= 0xF0) return "SET FEATURES [Reserved for CFA]"; @@ -504,15 +497,15 @@ const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { case 0x00: return "SET MAX ADDRESS [OBS-6]"; case 0x01: - return "SET MAX SET PASSWORD"; + return "SET MAX SET PASSWORD [OBS-ACS-3]"; case 0x02: - return "SET MAX LOCK"; + return "SET MAX LOCK [OBS-ACS-3]"; case 0x03: - return "SET MAX UNLOCK"; + return "SET MAX UNLOCK [OBS-ACS-3]"; case 0x04: - return "SET MAX FREEZE LOCK"; + return "SET MAX FREEZE LOCK [OBS-ACS-3]"; default: - return "[Reserved SET MAX command]"; + return "SET MAX [Reserved subcommand] [OBS-ACS-3]"; } default: return command_table[c_code]; diff --git a/atacmdnames.h b/atacmdnames.h index b2e65c9..ac0e446 100644 --- a/atacmdnames.h +++ b/atacmdnames.h @@ -15,15 +15,15 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef ATACMDNAMES_H_ #define ATACMDNAMES_H_ -#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h,v 1.6 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 3728 2012-12-13 17:57:50Z chrfranke $\n" /* Returns the name of the command (and possibly sub-command) with the given command code and feature register values. */ diff --git a/atacmds.cpp b/atacmds.cpp index f9ab53b..d84d1f0 100644 --- a/atacmds.cpp +++ b/atacmds.cpp @@ -4,7 +4,7 @@ * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002-11 Bruce Allen - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * Copyright (C) 2000 Andre Hedrick * @@ -14,8 +14,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -36,7 +35,7 @@ #include "utility.h" #include "dev_ata_cmd_set.h" // for parsed_ata_device -const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 3528 2012-03-25 17:13:47Z chrfranke $" +const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 3777 2013-02-19 18:32:15Z samm2 $" ATACMDS_H_CVSID; // Print ATA debug messages? @@ -57,103 +56,6 @@ bool dont_print_serial_number = false; #define SRET_STATUS_HI_EXCEEDED 0x2C #define SRET_STATUS_MID_EXCEEDED 0xF4 -// 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/Documents/UploadedDocuments/project/d2008r7b-ATA-3.pdf -// to see this. -#define NOVAL_0 0x0000 -#define NOVAL_1 0xffff -/* word 81: minor version number */ -#define MINOR_MAX 0x22 -static const char * const 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 */ - "ATA/ATAPI-7 published, ANSI INCITS 397-2005",/* 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 -static 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: */ - 7, /* 0x001d WARNING: */ - 7, /* 0x001e WARNING: */ - 0, /* 0x001f WARNING: */ - 0, /* 0x0020 WARNING: */ - 7, /* 0x0021 WARNING: */ - 6 /* 0x0022 WARNING: */ -}; - -// Compile time check of above array sizes -typedef char assert_sizeof_minor_str [sizeof(minor_str) /sizeof(minor_str[0]) == MINOR_MAX+1 ? 1 : -1]; -typedef char assert_sizeof_actual_ver[sizeof(actual_ver)/sizeof(actual_ver[0]) == MINOR_MAX+1 ? 1 : -1]; // Get ID and increase flag of current pending or offline // uncorrectable attribute. @@ -246,7 +148,7 @@ const char * map_old_vendor_opts[][2] = { {"198,increasing" , "198,raw48+,Total_Offl_Uncorrectabl"}, // '+' sets flag {"200,writeerrorcount" , "200,raw48,Write_Error_Count"}, {"201,detectedtacount" , "201,raw48,Detected_TA_Count"}, - {"220,temp" , "220,raw48,Temperature_Celsius"}, + {"220,temp" , "220,tempminmax,Temperature_Celsius"}, }; const unsigned num_old_vendor_opts = sizeof(map_old_vendor_opts)/sizeof(map_old_vendor_opts[0]); @@ -327,7 +229,7 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs, defs[i].priority = priority; defs[i].raw_format = format; defs[i].flags = flags; - strcpy(defs[i].byteorder, byteorder); + snprintf(defs[i].byteorder, sizeof(defs[i].byteorder), "%s", byteorder); } } else if (defs[id].priority <= priority) { @@ -337,7 +239,7 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs, defs[id].raw_format = format; defs[id].priority = priority; defs[id].flags = flags; - strcpy(defs[id].byteorder, byteorder); + snprintf(defs[id].byteorder, sizeof(defs[id].byteorder), "%s", byteorder); } return true; @@ -359,6 +261,35 @@ std::string create_vendor_attribute_arg_list() return s; } + +// Parse firmwarebug def (-F option). +// Return false on error. +bool parse_firmwarebug_def(const char * opt, firmwarebug_defs & firmwarebugs) +{ + if (!strcmp(opt, "none")) + firmwarebugs.set(BUG_NONE); + else if (!strcmp(opt, "nologdir")) + firmwarebugs.set(BUG_NOLOGDIR); + else if (!strcmp(opt, "samsung")) + firmwarebugs.set(BUG_SAMSUNG); + else if (!strcmp(opt, "samsung2")) + firmwarebugs.set(BUG_SAMSUNG2); + else if (!strcmp(opt, "samsung3")) + firmwarebugs.set(BUG_SAMSUNG3); + else if (!strcmp(opt, "xerrorlba")) + firmwarebugs.set(BUG_XERRORLBA); + else + return false; + return true; +} + +// Return a string of valid argument words for parse_firmwarebug_def() +const char * get_valid_firmwarebug_args() +{ + return "none, nologdir, samsung, samsung2, samsung3, xerrorlba"; +} + + // swap two bytes. Point to low address void swap2(char *location){ char tmp=*location; @@ -433,17 +364,18 @@ static const char * const commandstrings[]={ }; -static const char * preg(const ata_register & r, char * buf) +static const char * preg(const ata_register & r, char (& buf)[8]) { if (!r.is_set()) //return "n/a "; return "...."; - sprintf(buf, "0x%02x", r.val()); return buf; + snprintf(buf, sizeof(buf), "0x%02x", r.val()); + return buf; } static void print_regs(const char * prefix, const ata_in_regs & r, const char * suffix = "\n") { - char bufs[7][4+1+13]; + char bufs[7][8]; pout("%s FR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, CMD=%s%s", prefix, preg(r.features, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), @@ -452,7 +384,7 @@ static void print_regs(const char * prefix, const ata_in_regs & r, const char * static void print_regs(const char * prefix, const ata_out_regs & r, const char * suffix = "\n") { - char bufs[7][4+1+13]; + char bufs[7][8]; pout("%sERR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, STS=%s%s", prefix, preg(r.error, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), @@ -672,7 +604,7 @@ int smartcommandhandler(ata_device * device, smart_command_set command, int sele } else { // We haven't gotten output that makes sense; print out some debugging info - pout("Error SMART Status command failed\n"); + pout("SMART Status command failed\n"); pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE); pout("Register values returned from SMART Status command are:\n"); print_regs(" ", out.out_regs); @@ -883,7 +815,8 @@ bool ata_set_features(ata_device * device, unsigned char features, // 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 ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id) +int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id, + unsigned char * raw_buf /* = 0 */) { unsigned short *rawshort=(unsigned short *)buf; unsigned char *rawbyte =(unsigned char *)buf; @@ -909,6 +842,10 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s swap2((char *)(buf->model+i)); } + // If requested, save raw data before endianness adjustments + if (raw_buf) + memcpy(raw_buf, buf, sizeof(*buf)); + #ifndef __NetBSD__ // if machine is big-endian, swap byte order as needed // NetBSD kernel delivers IDENTIFY data in host byte order @@ -963,68 +900,6 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s 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, const ata_identify_device * drive, unsigned short * minor) -{ - // get major and minor ATA revision numbers - unsigned short 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 0; // No info found - } - - // 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; - } - } - - // Try new ATA-8 ACS minor revision numbers. - // Table 55 of T13/2015-D Revision 4a (ACS-2), December 9, 2010. - // (not in actual_ver/minor_str to avoid large sparse tables) - const char *desc; - switch (*minor) { - case 0x0027: desc = "ATA-8-ACS revision 3c"; break; - case 0x0028: desc = "ATA-8-ACS revision 6"; break; - case 0x0029: desc = "ATA-8-ACS revision 4"; break; - case 0x0031: desc = "ACS-2 revision 2"; break; - case 0x0033: desc = "ATA-8-ACS revision 3e"; break; - case 0x0039: desc = "ATA-8-ACS revision 4c"; break; - case 0x0042: desc = "ATA-8-ACS revision 3f"; break; - case 0x0052: desc = "ATA-8-ACS revision 3b"; break; - case 0x0107: desc = "ATA-8-ACS revision 2d"; break; - case 0x0110: desc = "ACS-2 revision 3"; break; - default: desc = 0; break; - } - if (desc) { - *description = desc; - return 8; - } - - // 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. - int i; - for (i=15; i>0; i--) - if (major & (0x1<> 12); } +// Get nominal media rotation rate. +// Returns: 0 = not reported, 1 = SSD, >1 = HDD rpm, < 0 = -(Unknown value) +int ata_get_rotation_rate(const ata_identify_device * id) +{ + // Table 37 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 + // Table A.31 of T13/2161-D (ACS-3) Revision 3b, August 25, 2012 + unsigned short word217 = id->words088_255[217-88]; + if (word217 == 0x0000 || word217 == 0xffff) + return 0; + else if (word217 == 0x0001) + return 1; + else if (word217 > 0x0400) + return word217; + else + return -(int)word217; +} + // returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell int ataSmartSupport(const ata_identify_device * drive) { @@ -1082,7 +974,6 @@ int ataIsSmartEnabled(const ata_identify_device * drive) int ataReadSmartValues(ata_device * device, struct ata_smart_values *data){ if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){ - pout("Error SMART Values Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1127,12 +1018,11 @@ static void fixsamsungselftestlog(ata_smart_selftestlog * data) // Reads the Self Test Log (log #6) int ataReadSelfTestLog (ata_device * device, ata_smart_selftestlog * data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){ - pout("Error SMART Error Self-Test Log Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1141,7 +1031,7 @@ int ataReadSelfTestLog (ata_device * device, ata_smart_selftestlog * data, checksumwarning("SMART Self-Test Log Structure"); // fix firmware bugs in self-test log - if (fix_firmwarebug == FIX_SAMSUNG) + if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungselftestlog(data); // swap endian order if needed @@ -1272,7 +1162,6 @@ int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_t // get data from device if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){ - pout("Error SMART Read Selective Self-Test Log failed: %s\n", device->get_errmsg()); return -1; } @@ -1294,9 +1183,6 @@ int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_t swap2((char *)&(data->pendingtime)); } - if (data->logversion != 1) - pout("Note: selective self-test log revision number (%d) not 1 implies that no selective self-test has ever been run\n", data->logversion); - return 0; } @@ -1315,6 +1201,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg struct ata_selective_self_test_log sstlog, *data=&sstlog; unsigned char *ptr=(unsigned char *)data; if (ataReadSelectiveSelfTestLog(device, data)) { + pout("SMART Read Selective Self-test Log failed: %s\n", device->get_errmsg()); pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n"); return -1; } @@ -1325,7 +1212,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg // Host is NOT allowed to write selective self-test log if a selective // self-test is in progress. if (0currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) { - pout("Error SMART Selective or other Self-Test in progress.\n"); + pout("SMART Selective or other Self-test in progress\n"); return -4; } @@ -1470,7 +1357,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg // write new selective self-test log if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){ - pout("Error Write Selective Self-Test Log failed: %s\n", device->get_errmsg()); + pout("Write Selective Self-test Log failed: %s\n", device->get_errmsg()); return -3; } @@ -1512,12 +1399,11 @@ static void fixsamsungerrorlog2(ata_smart_errorlog * data) // Error Log is #2, and the Extended Comprehensive SMART Error log is // #3 int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){ - pout("Error SMART Error Log Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1527,9 +1413,9 @@ int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, // Some disks have the byte order reversed in some SMART Summary // Error log entries - if (fix_firmwarebug == FIX_SAMSUNG) + if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungerrorlog(data); - else if (fix_firmwarebug == FIX_SAMSUNG2) + else if (firmwarebugs.is_set(BUG_SAMSUNG2)) fixsamsungerrorlog2(data); // swap endian order if needed @@ -1553,9 +1439,34 @@ int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, return 0; } + +// Fix LBA byte ordering of Extended Comprehensive Error Log +// if little endian instead of ATA register ordering is provided +template +static inline void fix_exterrlog_lba_cmd(T & cmd) +{ + T org = cmd; + cmd.lba_mid_register_hi = org.lba_high_register; + cmd.lba_low_register_hi = org.lba_mid_register_hi; + cmd.lba_high_register = org.lba_mid_register; + cmd.lba_mid_register = org.lba_low_register_hi; +} + +static void fix_exterrlog_lba(ata_smart_exterrlog * log, unsigned nsectors) +{ + for (unsigned i = 0; i < nsectors; i++) { + for (int ei = 0; ei < 4; ei++) { + ata_smart_exterrlog_error_log & entry = log[i].error_logs[ei]; + fix_exterrlog_lba_cmd(entry.error); + for (int ci = 0; ci < 5; ci++) + fix_exterrlog_lba_cmd(entry.commands[ci]); + } + } +} + // Read Extended Comprehensive Error Log bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, - unsigned nsectors) + unsigned nsectors, firmwarebug_defs firmwarebugs) { if (!ataReadLogExt(device, 0x03, 0x00, 0, log, nsectors)) return false; @@ -1573,6 +1484,9 @@ bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, } } + if (firmwarebugs.is_set(BUG_XERRORLBA)) + fix_exterrlog_lba(log, nsectors); + return true; } @@ -1581,7 +1495,6 @@ int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt // get data from device if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){ - pout("Error SMART Thresholds Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1598,7 +1511,6 @@ int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt int ataEnableSmart (ata_device * device ){ if (smartcommandhandler(device, ENABLE, 0, NULL)){ - pout("Error SMART Enable failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1607,7 +1519,6 @@ int ataEnableSmart (ata_device * device ){ int ataDisableSmart (ata_device * device ){ if (smartcommandhandler(device, DISABLE, 0, NULL)){ - pout("Error SMART Disable failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1615,7 +1526,6 @@ int ataDisableSmart (ata_device * device ){ int ataEnableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){ - pout("Error SMART Enable Auto-save failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1624,7 +1534,6 @@ int ataEnableAutoSave(ata_device * device){ int ataDisableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){ - pout("Error SMART Disable Auto-save failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1638,7 +1547,6 @@ int ataEnableAutoOffline (ata_device * device){ /* timer hard coded to 4 hours */ if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ - pout("Error SMART Enable Automatic Offline failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1649,7 +1557,6 @@ int ataEnableAutoOffline (ata_device * device){ int ataDisableAutoOffline (ata_device * device){ if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){ - pout("Error SMART Disable Automatic Offline failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1730,11 +1637,11 @@ int ataSmartTest(ata_device * device, int testtype, bool force, // 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"); + snprintf(cmdmsg, sizeof(cmdmsg), "Abort SMART off-line mode self-test routine"); else if (!type) - sprintf(cmdmsg, "SMART EXECUTE OFF-LINE IMMEDIATE subcommand 0x%02x", testtype); + snprintf(cmdmsg, sizeof(cmdmsg), "SMART EXECUTE OFF-LINE IMMEDIATE subcommand 0x%02x", testtype); else - sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive); + snprintf(cmdmsg, sizeof(cmdmsg), "Execute SMART %s routine immediately in %s mode", type, captive); pout("Sending command: \"%s\".\n",cmdmsg); if (select) { @@ -2195,8 +2102,13 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, // Attribute names shouldn't be longer than 23 chars, otherwise they break the // output of smartctl. -static const char * get_default_attr_name(unsigned char id) +static const char * get_default_attr_name(unsigned char id, int rpm) { + bool hdd = (rpm > 1), ssd = (rpm == 1); + + static const char Unknown_HDD_Attribute[] = "Unknown_HDD_Attribute"; + static const char Unknown_SSD_Attribute[] = "Unknown_SSD_Attribute"; + switch (id) { case 1: return "Raw_Read_Error_Rate"; @@ -2209,36 +2121,48 @@ static const char * get_default_attr_name(unsigned char id) case 5: return "Reallocated_Sector_Ct"; case 6: + if (ssd) return Unknown_SSD_Attribute; return "Read_Channel_Margin"; case 7: + if (ssd) return Unknown_SSD_Attribute; return "Seek_Error_Rate"; case 8: + if (ssd) return Unknown_SSD_Attribute; return "Seek_Time_Performance"; case 9: return "Power_On_Hours"; case 10: + if (ssd) return Unknown_SSD_Attribute; return "Spin_Retry_Count"; case 11: + if (ssd) return Unknown_SSD_Attribute; return "Calibration_Retry_Count"; case 12: return "Power_Cycle_Count"; case 13: return "Read_Soft_Error_Rate"; case 175: + if (hdd) return Unknown_HDD_Attribute; return "Program_Fail_Count_Chip"; case 176: + if (hdd) return Unknown_HDD_Attribute; return "Erase_Fail_Count_Chip"; case 177: + if (hdd) return Unknown_HDD_Attribute; return "Wear_Leveling_Count"; case 178: + if (hdd) return Unknown_HDD_Attribute; return "Used_Rsvd_Blk_Cnt_Chip"; case 179: + if (hdd) return Unknown_HDD_Attribute; return "Used_Rsvd_Blk_Cnt_Tot"; case 180: + if (hdd) return Unknown_HDD_Attribute; return "Unused_Rsvd_Blk_Cnt_Tot"; case 181: return "Program_Fail_Cnt_Total"; case 182: + if (hdd) return Unknown_HDD_Attribute; return "Erase_Fail_Count_Total"; case 183: return "Runtime_Bad_Block"; @@ -2249,6 +2173,7 @@ static const char * get_default_attr_name(unsigned char id) case 188: return "Command_Timeout"; case 189: + if (ssd) return Unknown_SSD_Attribute; return "High_Fly_Writes"; case 190: // Western Digital uses this for temperature. @@ -2260,10 +2185,12 @@ static const char * get_default_attr_name(unsigned char id) // 55C sometime in the past. return "Airflow_Temperature_Cel"; case 191: + if (ssd) return Unknown_SSD_Attribute; return "G-Sense_Error_Rate"; case 192: return "Power-Off_Retract_Count"; case 193: + if (ssd) return Unknown_SSD_Attribute; return "Load_Cycle_Count"; case 194: return "Temperature_Celsius"; @@ -2279,11 +2206,14 @@ static const char * get_default_attr_name(unsigned char id) case 199: return "UDMA_CRC_Error_Count"; case 200: + if (ssd) return Unknown_SSD_Attribute; // Western Digital return "Multi_Zone_Error_Rate"; case 201: + if (ssd) return Unknown_SSD_Attribute; return "Soft_Read_Error_Rate"; case 202: + if (ssd) return Unknown_SSD_Attribute; // Fujitsu: "TA_Increase_Count" return "Data_Address_Mark_Errs"; case 203: @@ -2298,36 +2228,49 @@ static const char * get_default_attr_name(unsigned char id) return "Thermal_Asperity_Rate"; case 206: // Fujitsu + if (ssd) return Unknown_SSD_Attribute; return "Flying_Height"; case 207: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Spin_High_Current"; case 208: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Spin_Buzz"; case 209: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Offline_Seek_Performnce"; case 220: + if (ssd) return Unknown_SSD_Attribute; return "Disk_Shift"; case 221: + if (ssd) return Unknown_SSD_Attribute; return "G-Sense_Error_Rate"; case 222: + if (ssd) return Unknown_SSD_Attribute; return "Loaded_Hours"; case 223: + if (ssd) return Unknown_SSD_Attribute; return "Load_Retry_Count"; case 224: + if (ssd) return Unknown_SSD_Attribute; return "Load_Friction"; case 225: + if (ssd) return Unknown_SSD_Attribute; return "Load_Cycle_Count"; case 226: + if (ssd) return Unknown_SSD_Attribute; return "Load-in_Time"; case 227: + if (ssd) return Unknown_SSD_Attribute; return "Torq-amp_Count"; case 228: return "Power-off_Retract_Count"; case 230: // seen in IBM DTPA-353750 + if (ssd) return Unknown_SSD_Attribute; return "Head_Amplitude"; case 231: return "Temperature_Celsius"; @@ -2336,8 +2279,10 @@ static const char * get_default_attr_name(unsigned char id) return "Available_Reservd_Space"; case 233: // seen in Intel X25-E SSD + if (hdd) return Unknown_HDD_Attribute; return "Media_Wearout_Indicator"; case 240: + if (ssd) return Unknown_SSD_Attribute; return "Head_Flying_Hours"; case 241: return "Total_LBAs_Written"; @@ -2346,6 +2291,7 @@ static const char * get_default_attr_name(unsigned char id) case 250: return "Read_Error_Retry_Rate"; case 254: + if (ssd) return Unknown_SSD_Attribute; return "Free_Fall_Sensor"; default: return "Unknown_Attribute"; @@ -2353,12 +2299,13 @@ static const char * get_default_attr_name(unsigned char id) } // Get attribute name -std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs) +std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs, + int rpm /* = 0 */) { if (!defs[id].name.empty()) return defs[id].name; else - return get_default_attr_name(id); + return get_default_attr_name(id, rpm); } // Find attribute index for attribute id, -1 if not found. @@ -2377,11 +2324,11 @@ int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval) // non-default interpretations. If the Attribute does not exist, return 0 unsigned char ata_return_temperature_value(const ata_smart_values * data, const ata_vendor_attr_defs & defs) { - for (int i = 0; i < 3; i++) { - static const unsigned char ids[3] = {194, 9, 220}; + for (int i = 0; i < 4; i++) { + static const unsigned char ids[4] = {194, 190, 9, 220}; unsigned char id = ids[i]; const ata_attr_raw_format format = defs[id].raw_format; - if (!( (id == 194 && format == RAWFMT_DEFAULT) + if (!( ((id == 194 || id == 190) && format == RAWFMT_DEFAULT) || format == RAWFMT_TEMPMINMAX || format == RAWFMT_TEMP10X)) continue; int idx = ata_find_attr_index(id, *data); @@ -2409,7 +2356,7 @@ int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts) // read SCT status via SMART log 0xe0 memset(sts, 0, sizeof(*sts)); if (smartcommandhandler(device, READ_LOG, 0xe0, (char *)sts)){ - pout("Error Read SCT Status failed: %s\n", device->get_errmsg()); + pout("Read SCT Status failed: %s\n", device->get_errmsg()); return -1; } @@ -2427,7 +2374,7 @@ int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts) // Check format version if (!(sts->format_version == 2 || sts->format_version == 3)) { - pout("Error unknown SCT Status format version %u, should be 2 or 3.\n", sts->format_version); + pout("Unknown SCT Status format version %u, should be 2 or 3.\n", sts->format_version); return -1; } return 0; @@ -2464,14 +2411,14 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ - pout("Error Write SCT Data Table command failed: %s\n", device->get_errmsg()); + pout("Write SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } // read SCT data via SMART log page 0xe1 memset(tmh, 0, sizeof(*tmh)); if (smartcommandhandler(device, READ_LOG, 0xe1, (char *)tmh)){ - pout("Error Read SCT Data Table failed: %s\n", device->get_errmsg()); + pout("Read SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } @@ -2480,7 +2427,7 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * return -1; if (!(sts->ext_status_code == 0 && sts->action_code == 5 && sts->function_code == 1)) { - pout("Error unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts->ext_status_code, sts->action_code, sts->function_code); return -1; } @@ -2490,12 +2437,8 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * swapx(&tmh->format_version); swapx(&tmh->sampling_period); swapx(&tmh->interval); - } - - // Check format version - if (tmh->format_version != 2) { - pout("Error unknown SCT Temperature History Format Version (%u), should be 2.\n", tmh->format_version); - return -1; + swapx(&tmh->cb_index); + swapx(&tmh->cb_size); } return 0; } @@ -2535,7 +2478,7 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ - pout("Error Write SCT Feature Control Command failed: %s\n", device->get_errmsg()); + pout("Write SCT Feature Control Command failed: %s\n", device->get_errmsg()); return -1; } @@ -2544,7 +2487,7 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 4 && sts.function_code == 1)) { - pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } @@ -2599,7 +2542,7 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty ata_cmd_out out; if (!device->ata_pass_through(in, out)) { - pout("Error Write SCT (%cet) Error Recovery Control Command failed: %s\n", + pout("Write SCT (%cet) Error Recovery Control Command failed: %s\n", (!set ? 'G' : 'S'), device->get_errmsg()); return -1; } @@ -2609,7 +2552,7 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty 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", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } @@ -2619,9 +2562,16 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty 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"); + pout("SMART WRITE LOG does not return COUNT and LBA_LOW register\n"); + return -1; + } + if ( out.out_regs.sector_count == in.in_regs.sector_count + && out.out_regs.lba_low == in.in_regs.lba_low ) { + // 0xe001 (5734.5s) - this is most likely a broken ATA pass-through implementation + pout("SMART WRITE LOG returns COUNT and LBA_LOW register unchanged\n"); return -1; } + // Return value to caller time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8); } @@ -2713,8 +2663,9 @@ int ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type, char msglba[32]; if (retval < 0 && failing_lba < 0xffffffffffffULL) snprintf(msglba, sizeof(msglba), "%"PRIu64, failing_lba); - else - strcpy(msglba, "-"); + else { + msglba[0] = '-'; msglba[1] = 0; + } pout("#%2u %-19s %-29s %1d0%% %8u %s\n", testnum, msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba); @@ -2727,11 +2678,11 @@ int ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type, // 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(const ata_smart_selftestlog * data, bool allentries, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { if (allentries) pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber); - if ((data->revnumber!=0x0001) && allentries && fix_firmwarebug != FIX_SAMSUNG) + if (data->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_SAMSUNG)) pout("Warning: ATA Specification requires self-test log structure revision number = 1\n"); if (data->mostrecenttest==0){ if (allentries) diff --git a/atacmds.h b/atacmds.h index 9a71565..a0d965d 100644 --- a/atacmds.h +++ b/atacmds.h @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -26,7 +25,7 @@ #ifndef ATACMDS_H_ #define ATACMDS_H_ -#define ATACMDS_H_CVSID "$Id: atacmds.h 3528 2012-03-25 17:13:47Z chrfranke $" +#define ATACMDS_H_CVSID "$Id: atacmds.h 3632 2012-10-09 10:10:53Z chrfranke $" #include "dev_interface.h" // ata_device @@ -67,14 +66,6 @@ typedef enum { WRITE_LOG } smart_command_set; -// Possible values for fix_firmwarebug. -enum { - FIX_NOTSPECIFIED = 0, - FIX_NONE, - FIX_SAMSUNG, - FIX_SAMSUNG2, - FIX_SAMSUNG3 -}; // ATA Specification Command Register Values (Commands) #define ATA_CHECK_POWER_MODE 0xe5 @@ -727,6 +718,37 @@ private: }; +// Possible values for firmwarebugs +enum firmwarebug_t { + BUG_NONE = 0, + BUG_NOLOGDIR, + BUG_SAMSUNG, + BUG_SAMSUNG2, + BUG_SAMSUNG3, + BUG_XERRORLBA +}; + +// Set of firmware bugs +class firmwarebug_defs +{ +public: + firmwarebug_defs() + : m_bugs(0) { } + + bool is_set(firmwarebug_t bug) const + { return !!(m_bugs & (1 << bug)); } + + void set(firmwarebug_t bug) + { m_bugs |= (1 << bug); } + + void set(firmwarebug_defs bugs) + { m_bugs |= bugs.m_bugs; } + +private: + unsigned m_bugs; +}; + + // Print ATA debug messages? extern unsigned char ata_debugmode; @@ -734,7 +756,8 @@ extern unsigned char ata_debugmode; extern bool dont_print_serial_number; // Get information from drive -int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id); +int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id, + unsigned char * raw_buf = 0); int ataCheckPowerMode(ata_device * device); // Issue a no-data ATA command with optional sector count register value @@ -747,11 +770,10 @@ bool ata_set_features(ata_device * device, unsigned char features, int sector_co int ataReadSmartValues(ata_device * device,struct ata_smart_values *); int ataReadSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *); int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, - unsigned char fix_firmwarebug); + firmwarebug_defs firmwarebugs); int ataReadSelfTestLog(ata_device * device, ata_smart_selftestlog * data, - unsigned char fix_firmwarebug); + firmwarebug_defs firmwarebugs); int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data); -int ataSetSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *); int ataReadLogDirectory(ata_device * device, ata_smart_log_directory *, bool gpl); // Read GP Log page(s) @@ -763,7 +785,7 @@ bool ataReadSmartLog(ata_device * device, unsigned char logaddr, void * data, unsigned nsectors); // Read SMART Extended Comprehensive Error Log bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, - unsigned nsectors); + unsigned nsectors, firmwarebug_defs firwarebugs); // Read SMART Extended Self-test Log bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log, unsigned nsectors); @@ -799,14 +821,14 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg const ata_smart_values * sv, uint64_t num_sectors, const ata_selective_selftest_args * prev_spans = 0); -// Returns the latest compatibility of ATA/ATAPI Version the device -// supports. Returns -1 if Version command is not supported -int ataVersionInfo(const char ** description, const ata_identify_device * drive, unsigned short * minor); - // Get World Wide Name (WWN) fields. // Return NAA field or -1 if WWN is unsupported. int ata_get_wwn(const ata_identify_device * id, unsigned & oui, uint64_t & unique_id); +// Get nominal media rotation rate. +// Returns: 0 = not reported, 1 = SSD, >1 = HDD rpm, < 0 = -(Unknown value) +int ata_get_rotation_rate(const ata_identify_device * id); + // If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0. int ataDoesSmartWork(ata_device * device); @@ -883,7 +905,8 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, // Get attribute name std::string ata_get_smart_attr_name(unsigned char id, - const ata_vendor_attr_defs & defs); + const ata_vendor_attr_defs & defs, + int rpm = 0); // External handler function, for when a checksum is not correct. Can // simply return if no action is desired, or can print error messages @@ -899,15 +922,6 @@ int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval); unsigned char ata_return_temperature_value(const ata_smart_values * data, const ata_vendor_attr_defs & defs); -// 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. -const char *SelfTestFailureCodeName(unsigned char which); - - #define MAX_ATTRIBUTE_NUM 256 // Parse vendor attribute display def (-v option). @@ -924,6 +938,13 @@ unsigned char get_unc_attr_id(bool offline, const ata_vendor_attr_defs & defs, // parse_attribute_def(). std::string create_vendor_attribute_arg_list(); +// Parse firmwarebug def (-F option). +// Return false on error. +bool parse_firmwarebug_def(const char * opt, firmwarebug_defs & firmwarebugs); + +// Return a string of valid argument words for parse_firmwarebug_def() +const char * get_valid_firmwarebug_args(); + // These are two of the functions that are defined in os_*.c and need // to be ported to get smartmontools onto another OS. @@ -935,12 +956,6 @@ std::string create_vendor_attribute_arg_list(); //int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data); -// Optional functions of os_*.c -#ifdef HAVE_ATA_IDENTIFY_IS_CACHED -// Return true if OS caches the ATA identify sector -//int ata_identify_is_cached(int fd); -#endif - // This function is exported to give low-level capability int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data); @@ -957,7 +972,7 @@ int ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type, // Print Smart self-test log, used by smartctl and smartd. int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * data, bool allentries, - unsigned char fix_firmwarebug); + firmwarebug_defs firmwarebugs); // Get capacity and sector sizes from IDENTIFY data struct ata_size_info diff --git a/ataidentify.cpp b/ataidentify.cpp new file mode 100644 index 0000000..900cd20 --- /dev/null +++ b/ataidentify.cpp @@ -0,0 +1,708 @@ +/* + * ataidentify.cpp + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2012-13 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 . + * + */ + +#include "config.h" +#include "ataidentify.h" + +const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 3785 2013-03-07 21:58:05Z chrfranke $" + ATAIDENTIFY_H_CVSID; + +#include "int64.h" +#include "utility.h" + + +// Table 12 of X3T10/0948D (ATA-2) Revision 4c, March 18, 1996 +// Table 9 of X3T13/2008D (ATA-3) Revision 7b, January 27, 1997 +// Tables 11 and 13 of T13/1153D (ATA/ATAPI-4) revision 18, August 19, 1998 +// Tables 20 and 22 of T13/1321D (ATA/ATAPI-5) Revision 3, February 29, 2000 +// Tables 27 and 29 of T13/1410D (ATA/ATAPI-6) Revision 3b, February 26, 2002 +// Tables 16 and 18 of T13/1532D (ATA/ATAPI-7) Volume 1 Revision 4b, April 21, 2004 +// Tables 29 and 39 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 +// Tables 50 and 61 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 +// Tables 51 and 56 of T13/2161-D (ACS-3) Revision 4g, February 27, 2013 + +const char * const identify_descriptions[] = { + " 0 General configuration", + ". 15 Device identifier: 0 = ATA, 1 = ATAPI", + ". 14:8 ATA: Vendor specific [RET-3]", + ". 14 ATAPI: Must be set to 0", + ". 13 ATAPI: Reserved", + ". 12:8 ATAPI: Command set: 0x05 = CD/DVD", + ". 7 Removable media device", + ". 6 ATA: Not removable controller and/or device [OBS-6]", + ". 5:3 ATA: Vendor specific [RET-3]", + ". 6:5 ATAPI: DRQ after PACKET cmd: 0x0 = 3ms, 0x2 = 50us", + ". 4:3 ATAPI: Reserved", + ". 2 Response incomplete", + ". 1 ATA: Vendor specific [RET-3]", + ". 0 ATA: Reserved", + ". 1:0 ATAPI: Packet size: 0x0 = 12 byte, 0x1 = 16 byte", + + " 1 Cylinders [OBS-6]", + " 2 Specific configuration (0x37c8/738c/8c73/c837)", + " 3 Heads [OBS-6]", + " 4 Vendor specific [RET-3]", + " 5 Vendor specific [RET-3]", + " 6 Sectors per track [OBS-6]", + " 7-8 Reserved for CFA (Sectors per card)", + " 9 Vendor specific [RET-4]", + " 10-19 Serial number (String)", + " 20 Vendor specific [RET-3]", + " 21 Vendor specific [RET-3]", + " 22 Vendor specific bytes on READ/WRITE LONG [OBS-4]", + " 23-26 Firmware revision (String)", + " 27-46 Model number (String)", + + " 47 READ/WRITE MULTIPLE support", + ". 15:8 Must be set to 0x80", + ". 7:0 Maximum sectors per DRQ on READ/WRITE MULTIPLE", + + " 48 Trusted Computing feature set options", + ". 15:14 Must be set to 0x1", + ". 13:1 Reserved for the Trusted Computing Group", + ". 0 Trusted Computing feature set supported", + + " 49 Capabilities", + ". 15:14 ATA: Reserved for IDENTIFY PACKET DEVICE", + ". 15 ATAPI: Interleaved DMA supported [OBS-8]", + ". 14 ATAPI: Command queuing supported [OBS-8]", + ". 13 ATA: Standard standby timer values supported", + ". 13 ATAPI: Overlap operation supported [OBS-8]", + ". 12 ATA: Reserved for IDENTIFY PACKET DEVICE", + ". 12 ATAPI: ATA software reset required [OBS-5]", + ". 11 IORDY supported", + ". 10 IORDY may be disabled", + ". 9 LBA supported", + ". 8 DMA supported", + ". 7:0 Vendor specific [RET-4]", + + " 50 Capabilities", + ". 15:14 Must be set to 0x1", + ". 13:2 Reserved", + ". 1 Reserved [OBS-6]", + ". 0 Vendor specific minimum standby timer value", + + " 51 PIO data transfer mode [OBS-5]", + " 52 Single Word DMA data transfer mode [OBS-3]", + + " 53 Field validity / Free-fall Control", + ". 15:8 Free-fall Control sensitivity", + ". 7:3 Reserved", + ". 2 Word 88 (Ultra DMA modes) is valid", + ". 1 Words 64-70 (PIO modes) are valid", + ". 0 Words 54-58 (CHS) are valid [OBS-6]", + + " 54 Current cylinders [OBS-6]", + " 55 Current heads [OBS-6]", + " 56 Current sectors per track [OBS-6]", + " 57-58 Current capacity in sectors (DWord) [OBS-6]", + + " 59 Sanitize Device - READ/WRITE MULTIPLE support", + ". 15 BLOCK ERASE EXT supported", + ". 14 OVERWRITE EXT supported", + ". 13 CRYPTO SCRAMBLE EXT supported", + ". 12 Sanitize Device feature set supported", + ". 11:9 Reserved", + ". 8 Bits 7:0 are valid", + ". 7:0 Current sectors per DRQ on READ/WRITE MULTIPLE", + + " 60-61 User addressable sectors for 28-bit commands (DWord)", + " 62 Single Word DMA modes [OBS-3]", + + " 63 Multiword DMA modes", + ". 15:11 Reserved", + ". 10 Multiword DMA mode 2 selected", + ". 9 Multiword DMA mode 1 selected", + ". 8 Multiword DMA mode 0 selected", + ". 7:3 Reserved", + ". 2 Multiword DMA mode 2 and below supported", + ". 1 Multiword DMA mode 1 and below supported", + ". 0 Multiword DMA mode 0 supported", + + " 64 PIO modes", + ". 15:2 Reserved", + ". 1 PIO mode 4 supported", + ". 0 PIO mode 3 supported", + + " 65 Minimum Multiword DMA cycle time per word in ns", + " 66 Recommended Multiword DMA cycle time in ns", + " 67 Minimum PIO cycle time without flow control in ns", + " 68 Minimum PIO cycle time with IORDY flow control in ns", + + " 69 Additional support", + ". 15 CFast specification supported", + ". 14 Deterministic data after trim supported", + ". 13 LPS Alignment Error Reporting Control supported", + ". 12 DCO IDENTIFY/SET DMA supported [OBS-ACS-3]", + ". 11 READ BUFFER DMA supported", + ". 10 WRITE BUFFER DMA supported", + ". 9 SET MAX SET PASSWORD/UNLOCK DMA supported [OBS-ACS-3]", + ". 8 DOWNLOAD MICROCODE DMA supported", + ". 7 Reserved for IEEE 1667", + ". 6 Optional ATA device 28-bit commands supported", + ". 5 Trimmed LBA range(s) returning zeroed data supported", + ". 4 Device encrypts all user data", + ". 3 Extended number of user addressable sectors supported", + ". 2 All write cache is non-volatile", + ". 1:0 Reserved", + + " 70 Reserved", + " 71-74 ATA: Reserved for IDENTIFY PACKET DEVICE", + " 71 ATAPI: Time in ns from PACKET to bus release [OBS-8]", + " 72 ATAPI: Time in ns from SERVICE to BSY cleared [OBS-8]", + " 73-74 ATAPI: Reserved", + + " 75 Queue depth", + ". 15:5 Reserved", + ". 4:0 Maximum queue depth - 1", + + " 76 Serial ATA capabilities", + ". 15 READ LOG DMA EXT as equiv to READ LOG EXT supported", + ". 14 Device Auto Partial to Slumber transitions supported", + ". 13 Host Auto Partial to Slumber transitions supported", + ". 12 NCQ priority information supported", + ". 11 Unload while NCQ commands are outstanding supported", + ". 10 Phy Event Counters supported", + ". 9 Receipt of host initiated PM requests supported", + ". 8 NCQ feature set supported", + ". 7:4 Reserved for Serial ATA", + ". 3 SATA Gen3 signaling speed (6.0 Gb/s) supported", + ". 2 SATA Gen2 signaling speed (3.0 Gb/s) supported", + ". 1 SATA Gen1 signaling speed (1.5 Gb/s) supported", + ". 0 Must be set to 0", + + " 77 Serial ATA additional capabilities", // ACS-3 + ". 15:7 Reserved for Serial ATA", + ". 6 RECEIVE/SEND FPDMA QUEUED supported", + ". 5 NCQ Queue Management supported", + ". 4 NCQ Streaming supported", + ". 3:1 Current Serial ATA signal speed", + ". 0 Must be set to 0", + + " 78 Serial ATA features supported", + ". 15:8 Reserved for Serial ATA", + ". 7 NCQ Autosense supported", // ACS-3 + ". 6 Software Settings Preservation supported", + ". 5 Hardware Feature Control supported", // ACS-3 + ". 4 In-order data delivery supported", + ". 3 Device initiated power management supported", + ". 2 DMA Setup auto-activation supported", + ". 1 Non-zero buffer offsets supported", + ". 0 Must be set to 0", + + " 79 Serial ATA features enabled", + ". 15:8 Reserved for Serial ATA", + ". 7 Automatic Partial to Slumber transitions enabled", // ACS-3 + ". 6 Software Settings Preservation enabled", + ". 5 Hardware Feature Control enabled", // ACS-3 + ". 4 In-order data delivery enabled", + ". 3 Device initiated power management enabled", + ". 2 DMA Setup auto-activation enabled", + ". 1 Non-zero buffer offsets enabled", + ". 0 Must be set to 0", + + " 80 Major version number", + ". 15:11 Reserved", + ". 10 ACS-3 supported", + ". 9 ACS-2 supported", + ". 8 ATA8-ACS supported", + ". 7 ATA/ATAPI-7 supported", + ". 6 ATA/ATAPI-6 supported", + ". 5 ATA/ATAPI-5 supported", + ". 4 ATA/ATAPI-4 supported [OBS-8]", + ". 3 ATA-3 supported [OBS-7]", + ". 2 ATA-2 supported [OBS-6]", + ". 1 ATA-1 supported [OBS-5]", + ". 0 Reserved", + + " 81 Minor version number", + + " 82 Commands and feature sets supported", + ". 15 IDENTIFY DEVICE DMA supported [OBS-4]", // ATA-4 r07-r14 only + ". 14 NOP supported", + ". 13 READ BUFFER supported", + ". 12 WRITE BUFFER supported", + ". 11 WRITE VERIFY supported [OBS-4]", // ATA-4 r07-r13 only + ". 10 HPA feature set supported [OBS-ACS-3]", + ". 9 DEVICE RESET supported", // ATA:0, ATAPI:1 + ". 8 SERVICE interrupt supported [OBS-ACS-2]", + ". 7 Release interrupt supported [OBS-ACS-2]", + ". 6 Read look-ahead supported", + ". 5 Volatile write cache supported", + ". 4 PACKET feature set supported", // ATA:0, ATAPI:1 + ". 3 Power Management feature set supported", + ". 2 Removable Media feature set supported [OBS-8]", + ". 1 Security feature set supported", + ". 0 SMART feature set supported", + + " 83 Commands and feature sets supported", + ". 15:14 Must be set to 0x1", + ". 13 FLUSH CACHE EXT supported", + ". 12 FLUSH CACHE supported", + ". 11 DCO feature set supported [OBS-ACS-3]", + ". 10 48-bit Address feature set supported", + ". 9 AAM feature set supported [OBS-ACS-2]", + ". 8 SET MAX security extension supported [OBS-ACS-3]", + ". 7 Reserved for Address Offset Reserved Area Boot Method", + ". 6 SET FEATURES subcommand required to spin-up", + ". 5 PUIS feature set supported", + ". 4 Removable Media Status Notification supported [OBS-8]", + ". 3 APM feature set supported", + ". 2 CFA feature set supported", + ". 1 TCQ feature set supported [OBS-ACS-2]", + ". 0 DOWNLOAD MICROCODE supported", + + " 84 Commands and feature sets supported", + ". 15:14 Must be set to 0x1", + ". 13 IDLE IMMEDIATE with UNLOAD feature supported", + ". 12:11 Reserved for TLC [OBS-ACS-3]", + ". 10 URG bit for WRITE STREAM (DMA) EXT supported [OBS-8]", + ". 9 URG bit for READ STREAM (DMA) EXT supported [OBS-8]", + ". 8 64-bit World Wide Name supported", + ". 7 WRITE DMA QUEUED FUA EXT supported", + ". 6 WRITE DMA/MULTIPLE FUA EXT supported", + ". 5 GPL feature set supported", + ". 4 Streaming feature set supported [OBS-ACS-3]", + ". 3 Media Card Pass Through Command supported [OBS-ACS-2]", + ". 2 Media serial number supported", // ACS-3 r3 or later: Reserved + ". 1 SMART self-test supported", + ". 0 SMART error logging supported", + + " 85 Commands and feature sets supported or enabled", + ". 15 IDENTIFY DEVICE DMA supported [OBS-4]", // ATA-4 r07-r14 only + ". 14 NOP supported", + ". 13 READ BUFFER supported", + ". 12 WRITE BUFFER supported", + ". 11 WRITE VERIFY supported [OBS-4]", // ATA-4 r07-r13 only + ". 10 HPA feature set supported [OBS-ACS-3]", + ". 9 DEVICE RESET supported", // ATA:0, ATAPI:1 + ". 8 SERVICE interrupt enabled [OBS-ACS-2]", + ". 7 Release interrupt enabled [OBS-ACS-2]", + ". 6 Read look-ahead enabled", + ". 5 Write cache enabled", + ". 4 PACKET feature set supported", // ATA:0, ATAPI:1 + ". 3 Power Management feature set supported", + ". 2 Removable Media feature set supported [OBS-8]", + ". 1 Security feature set enabled", + ". 0 SMART feature set enabled", + + " 86 Commands and feature sets supported or enabled", + ". 15 Words 119-120 are valid", + ". 14 Reserved", + ". 13 FLUSH CACHE EXT supported", + ". 12 FLUSH CACHE supported", + ". 11 DCO feature set supported [OBS-ACS-3]", + ". 10 48-bit Address features set supported", + ". 9 AAM feature set enabled [OBS-ACS-2]", + ". 8 SET MAX security extension enabled [OBS-ACS-3]", + ". 7 Reserved for Address Offset Reserved Area Boot Method", + ". 6 SET FEATURES subcommand required to spin-up", + ". 5 PUIS feature set enabled", + ". 4 Removable Media Status Notification enabled [OBS-8]", + ". 3 APM feature set enabled", + ". 2 CFA feature set supported", + ". 1 TCQ feature set supported [OBS-ACS-2]", + ". 0 DOWNLOAD MICROCODE supported", + + " 87 Commands and feature sets supported or enabled", + ". 15:14 Must be set to 0x1", + ". 13 IDLE IMMEDIATE with UNLOAD FEATURE supported", + ". 12:11 Reserved for TLC [OBS-ACS-3]", + ". 10 URG bit for WRITE STREAM (DMA) EXT supported [OBS-8]", + ". 9 URG bit for READ STREAM (DMA) EXT supported [OBS-8]", + ". 8 64-bit World Wide Name supported", + ". 7 WRITE DMA QUEUED FUA EXT supported [OBS-ACS-2]", + ". 6 WRITE DMA/MULTIPLE FUA EXT supported", + ". 5 GPL feature set supported", + ". 4 Valid CONFIGURE STREAM has been executed [OBS-8]", + ". 3 Media Card Pass Through Command supported [OBS-ACS-2]", + ". 2 Media serial number is valid", + ". 1 SMART self-test supported", + ". 0 SMART error logging supported", + + " 88 Ultra DMA modes", + ". 15 Reserved", + ". 14 Ultra DMA mode 6 selected", + ". 13 Ultra DMA mode 5 selected", + ". 12 Ultra DMA mode 4 selected", + ". 11 Ultra DMA mode 3 selected", + ". 10 Ultra DMA mode 2 selected", + ". 9 Ultra DMA mode 1 selected", + ". 8 Ultra DMA mode 0 selected", + ". 7 Reserved", + ". 6 Ultra DMA mode 6 and below supported", + ". 5 Ultra DMA mode 5 and below supported", + ". 4 Ultra DMA mode 4 and below supported", + ". 3 Ultra DMA mode 3 and below supported", + ". 2 Ultra DMA mode 2 and below supported", + ". 1 Ultra DMA mode 1 and below supported", + ". 0 Ultra DMA mode 0 supported", + + " 89 SECURITY ERASE UNIT time", + " 90 ENHANCED SECURITY ERASE UNIT time", + " 91 Current APM level", + " 92 Master password revision code", + + " 93 Hardware reset result (PATA)", + ". 15:14 Must be set to 0x1", + ". 13 Device detected CBLID- above(1)/below(0) ViHB", + ". 12 Reserved", + ". 11 Device 1 asserted PDIAG-", + ". 10:9 Device 1 detection method: -, Jumper, CSEL, other", + ". 8 Must be set to 1", + ". 7 Reserved", + ". 6 Device 0 responds when device 1 selected", + ". 5 Device 0 detected the assertion of DASP-", + ". 4 Device 0 detected the assertion of PDIAG-", + ". 3 Device 0 passed diagnostics", + ". 2:1 Device 0 detection method: -, Jumper, CSEL, other", + ". 0 Must be set to 1", + + " 94 AAM level [OBS-ACS-2]", + ". 15:8 Recommended AAM level [OBS-ACS-2]", + ". 7:0 Current AAM level [OBS-ACS-2]", + + " 95 Stream Minimum Request Size", + " 96 Streaming Transfer Time - DMA", + " 97 Streaming Access Latency - DMA and PIO", + " 98-99 Streaming Performance Granularity (DWord)", + "100-103 User addressable sectors for 48-bit commands (QWord)", + "104 Streaming Transfer Time - PIO", + "105 Max blocks of LBA Range Entries per DS MANAGEMENT cmd", + + "106 Physical sector size / logical sector size", + ". 15:14 Must be set to 0x1", + ". 13 Multiple logical sectors per physical sector", + ". 12 Logical Sector longer than 256 words", + ". 11:4 Reserved", + ". 3:0 2^X logical sectors per physical sector", + + "107 Inter-seek delay for ISO 7779 acoustic testing", + "108-111 64-bit World Wide Name", + "112-115 Reserved for a 128-bit World Wide Name", + "116 Reserved for TLC [OBS-ACS-3]", + "117-118 Logical sector size (DWord)", + + "119 Commands and feature sets supported", + ". 15:14 Must be set to 0x1", + ". 13:10 Reserved", + ". 9 DSN feature set supported", // ACS-3 + ". 8 Accessible Max Address Config feature set supported", // ACS-3 + ". 7 Extended Power Conditions feature set supported", + ". 6 Sense Data Reporting feature set supported", + ". 5 Free-fall Control feature set supported", + ". 4 DOWNLOAD MICROCODE with mode 3 supported", + ". 3 READ/WRITE LOG DMA EXT supported", + ". 2 WRITE UNCORRECTABLE EXT supported", + ". 1 Write-Read-Verify feature set supported", + ". 0 Reserved for DDT [OBS-ACS-3]", + + "120 Commands and feature sets supported or enabled", + ". 15:14 Must be set to 0x1", + ". 13:10 Reserved", + ". 9 DSN feature set enabled", // ACS-3 + ". 8 Reserved", + ". 7 Extended Power Conditions feature set enabled", + ". 6 Sense Data Reporting feature set enabled", + ". 5 Free-fall Control feature set enabled", + ". 4 DOWNLOAD MICROCODE with mode 3 supported", + ". 3 READ/WRITE LOG DMA EXT supported", + ". 2 WRITE UNCORRECTABLE EXT supported", + ". 1 Write-Read-Verify feature set enabled", + ". 0 Reserved for DDT [OBS-ACS-3]", + + "121-126 ATA: Reserved", + "121-124 ATAPI: Reserved", + "125 ATAPI: Byte count = 0 behavior", + "126 ATAPI: Byte count = 0 behavior [OBS-6]", + + "127 Removable Media Status Notification [OBS-8]", + ". 15:1 Reserved", + ". 0 Removable Media Status Notification supported", + + "128 Security status", + ". 15:9 Reserved", + ". 8 Master password capability: 0 = High, 1 = Maximum", + ". 7:6 Reserved", + ". 5 Enhanced security erase supported", + ". 4 Security count expired", + ". 3 Security frozen", + ". 2 Security locked", + ". 1 Security enabled", + ". 0 Security supported", + + "129-159 Vendor specific", + + "160 CFA power mode", + // ". 15 Word 160 supported", + // ". 14 Reserved", + // ". 13 CFA power mode 1 is required for some commands", + // ". 12 CFA power mode 1 disabled", + // ". 11:0 Maximum current in mA", + "161-167 Reserved for CFA", + + "168 Form factor", + ". 15:4 Reserved", + ". 3:0 Nominal form factor: -, 5.25, 3.5, 2.5, 1.8, <1.8", + + "169 Data Set Management support", + ". 15:1 Reserved", + ". 0 Trim bit in DATA SET MANAGEMENT command supported", + + "170-173 Additional product identifier (String)", + "174-175 Reserved", + "176-205 Current media serial number (String)", + + "206 SCT Command Transport", + ". 15:12 Vendor Specific", + ". 11:8 Reserved", + ". 7 Reserved for Serial ATA", + ". 6 Reserved", + ". 5 SCT Data Tables supported", + ". 4 SCT Feature Control supported", + ". 3 SCT Error Recovery Control supported", + ". 2 SCT Write Same supported", + ". 1 SCT Read/Write Long supported [OBS-ACS-2]", + ". 0 SCT Command Transport supported", + + "207-208 Reserved for CE-ATA", + + "209 Alignment of logical sectors", + ". 15:14 Must be set to 0x1", + ". 13:0 Logical sector offset", + + "210-211 Write-Read-Verify sector count mode 3 (DWord)", + "212-213 Write-Read-Verify sector count mode 2 (DWord)", + + "214 NV Cache capabilities [OBS-ACS-3]", + ". 15:12 NV Cache feature set version [OBS-ACS-3]", + ". 11:8 NV Cache Power Mode feature set version [OBS-ACS-3]", + ". 7:5 Reserved [OBS-ACS-3]", + ". 4 NV Cache feature set enabled [OBS-ACS-3]", + ". 3:2 Reserved", + ". 1 NV Cache Power Mode feature set enabled [OBS-ACS-3]", + ". 0 NV Cache Power Mode feature set supported [OBS-ACS-3]", + + "215-216 NV Cache size in logical blocks (DWord) [OBS-ACS-3]", + "217 Nominal media rotation rate", + "218 Reserved", + + "219 NV Cache options [OBS-ACS-3]", + ". 15:8 Reserved [OBS-ACS-3]", + ". 7:0 Estimated time to spin up in seconds [OBS-ACS-3]", + + "220 Write-Read-Verify mode", + ". 15:8 Reserved", + ". 7:0 Write-Read-Verify feature set current mode", + + "221 Reserved", + + "222 Transport major version number", + ". 15:12 Transport type: 0x0 = Parallel, 0x1 = Serial", + ". 11:7 Reserved | Reserved", + ". 6 Reserved | SATA 3.1", + ". 5 Reserved | SATA 3.0", + ". 4 Reserved | SATA 2.6", + ". 3 Reserved | SATA 2.5", + ". 2 Reserved | SATA II: Extensions", + ". 1 ATA/ATAPI-7 | SATA 1.0a", + ". 0 ATA8-APT | ATA8-AST", + + "223 Transport minor version number", + "224-229 Reserved", + "230-233 Extended number of user addressable sectors (QWord)", + "234 Minimum blocks per DOWNLOAD MICROCODE mode 3 command", + "235 Maximum blocks per DOWNLOAD MICROCODE mode 3 command", + "236-254 Reserved", + + "255 Integrity word", + ". 15:8 Checksum", + ". 7:0 Signature" +}; + +const int num_identify_descriptions = sizeof(identify_descriptions)/sizeof(identify_descriptions[0]); + +static inline unsigned short get_word(const void * id, int word) +{ + const unsigned char * p = ((const unsigned char *)id) + 2 * word; + return p[0] + (p[1] << 8); +} + +void ata_print_identify_data(const void * id, bool all_words, int bit_level) +{ + // ATA or ATAPI ? + unsigned short w = get_word(id, 0); + bool is_atapi = ((w & 0x8000) && (w != 0x848a/*CompactFlash Signature*/)); + + int prev_word = -1, prev_bit = -1; + pout("Word %s Value Description\n", (bit_level >= 0 ? "Bit " : " ")); + + for (int i = 0; i < num_identify_descriptions; i++) { + // Parse table entry + const char * desc = identify_descriptions[i]; + + int word = prev_word, word2 = -1; + int bit = -1, bit2 = -1; + + int nc; + unsigned v1, v2; + if (word >= 0 && sscanf(desc, ". %u:%u %n", &v1, &v2, (nc=-1, &nc)) == 2 && nc > 0 && 16 > v1 && v1 > v2) { + bit = v1; bit2 = v2; + } + else if (word >= 0 && sscanf(desc, ". %u %n", &v1, (nc=-1, &nc)) == 1 && nc > 0 && v1 < 16) { + bit = v1; + } + else if (sscanf(desc, "%u-%u %n", &v1, &v2, (nc=-1, &nc)) == 2 && nc > 0 && v1 < v2 && v2 < 256) { + word = v1, word2 = v2; + } + else if (sscanf(desc, "%u %n", &v1, (nc=-1, &nc)) == 1 && nc > 0 && v1 < 256) { + word = v1; + } + else { + pout("Error: #%d: Syntax\n", i); + continue; + } + desc += nc; + + // Check for ATA/ATAPI specific entries + if (str_starts_with(desc, "ATA: ")) { + if (is_atapi) + continue; + desc += sizeof("ATA: ")-1; + } + else if (str_starts_with(desc, "ATAPI: ")) { + if (!is_atapi) + continue; + } + + // Check table entry + if (bit < 0) { + if (word != prev_word+1) { + pout("Error: #%d: Missing word %d\n", i, prev_word+1); + return; + } + else if (prev_bit > 0) { + pout("Error: #%d: Missing bit 0 from word %d\n", i, prev_word); + return; + } + } + else if (!((prev_bit < 0 && bit == 15) || bit == prev_bit-1)) { + pout("Error: #%d: Missing bit %d from word %d\n", i, bit+1, word); + return; + } + + w = get_word(id, word); + bool w_is_set = (w != 0x0000 && w != 0xffff); + + if (bit >= 0) { + int b; + if (bit2 >= 0) + b = (w >> bit2) & ~(~0 << (bit-bit2+1)); + else + b = (w >> bit) & 1; + + if ( (bit_level >= 0 && b) + || (bit_level >= 1 && w_is_set) + || (bit_level >= 2 && all_words)) { + if (bit2 >= 0) { + // Print bitfield + char valstr[20]; + snprintf(valstr, sizeof(valstr), "0x%0*x", (bit - bit2 + 4) >> 2, b); + pout("%4d %2d:%-2d %6s %s\n", word, bit, bit2, valstr, desc); + } + else { + // Print bit + pout("%4d %2d %u %s\n", word, bit, b, desc); + } + } + + prev_bit = (bit2 >= 0 ? bit2 : bit); + } + else { + if (word2 >= 0) { + for (int j = word+1; !w_is_set && j <= word2; j++) { + if (get_word(id, j) != w) + w_is_set = true; + } + + // Print word array + if (all_words || w_is_set) { + pout("%s%4d-%-3d %s", + (bit_level >= 0 ? "\n" : ""), word, word2, + (bit_level >= 0 ? "- " : "")); + + if (!w_is_set) { + pout("0x%02x... %s\n", w & 0xff, desc); + } + else { + bool is_str = !!strstr(desc, "(String)"); + pout(". %s", desc); + + for (int j = word; j <= word2; j += 4) { + if (j + 2*4 < word2 && !nonempty((const unsigned char *)id + 2*j, 2*(word2-j+1))) { + // Remaining words are null + pout("\n%4d-%-3d %s0x0000:0000:0000:00...", j, word2, + (bit_level >= 0 ? ". " : "")); + break; + } + // Print 4 words in a row + pout("\n%4d-%-3d %s0x", j, (j+3 <= word2 ? j+3 : word2), + (bit_level >= 0 ? ". " : "")); + int k; + for (k = 0; k < 4 && j+k <= word2; k++) + pout("%s%04x", (k == 0 ? "" : ":"), get_word(id, j+k)); + + if (is_str) { + // Append little endian string + pout("%*s \"", 20 - 5 * k, ""); + for (k = 0; k < 4 && j+k <= word2; k++) { + char c2 = ((const char *)id)[2*(j+k) ]; + char c1 = ((const char *)id)[2*(j+k) + 1]; + pout("%c%c", (' ' <= c1 && c1 <= '~' ? c1 : '.'), + (' ' <= c2 && c2 <= '~' ? c2 : '.') ); + } + pout("\""); + } + } + + // Print decimal value of D/QWords + if (word + 1 == word2 && strstr(desc, "(DWord)")) + pout(" (%u)\n", ((unsigned)get_word(id, word2) << 16) | w); + else if (word + 3 == word2 && strstr(desc, "(QWord)")) + pout(" (%"PRIu64")\n", ((uint64_t)get_word(id, word + 3) << 48) + | ((uint64_t)get_word(id, word + 2) << 32) + | ((unsigned)get_word(id, word + 1) << 16) | (unsigned)w); + else + pout("\n"); + } + } + } + else { + // Print word + if (all_words || w_is_set) + pout("%s%4d %s0x%04x %s\n", + (bit_level >= 0 ? "\n" : ""), word, + (bit_level >= 0 ? "- " : ""), w, desc); + } + + prev_word = (word2 >= 0 ? word2 : word); + prev_bit = -1; + } + } + + pout("\n"); +} diff --git a/ataidentify.h b/ataidentify.h new file mode 100644 index 0000000..6375388 --- /dev/null +++ b/ataidentify.h @@ -0,0 +1,25 @@ +/* + * ataidentify.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2012 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 . + * + */ + +#ifndef ATAIDENTIFY_H +#define ATAIDENTIFY_H + +#define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 3610 2012-09-20 21:27:19Z chrfranke $" + +void ata_print_identify_data(const void * id, bool all_words, int bit_level); + +#endif // ATAIDENTIFY_H diff --git a/ataprint.cpp b/ataprint.cpp index 4fd2724..51fe7d4 100644 --- a/ataprint.cpp +++ b/ataprint.cpp @@ -4,7 +4,7 @@ * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002-11 Bruce Allen - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * * This program is free software; you can redistribute it and/or modify @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -34,13 +33,14 @@ #include "int64.h" #include "atacmdnames.h" #include "atacmds.h" +#include "ataidentify.h" #include "dev_interface.h" #include "ataprint.h" #include "smartctl.h" #include "utility.h" #include "knowndrives.h" -const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 3554 2012-06-01 20:11:46Z chrfranke $" +const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 3786 2013-03-07 22:09:17Z chrfranke $" ATAPRINT_H_CVSID; @@ -60,10 +60,9 @@ static bool is_permissive() /* 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 caller passes the string - * buffer and the return value is a pointer to this string. If the - * meanings of the flags of the error register are not known for the given - * command then it returns NULL. + * Register (ST) and Error Register (ER). If the meanings of the flags of + * the error register are not known for the given command then it returns an + * empty string. * * 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. @@ -72,8 +71,7 @@ static bool is_permissive() * should probably be redesigned. */ -static const char * construct_st_er_desc( - char * s, +static std::string format_st_er_desc( unsigned char CR, unsigned char FR, unsigned char ST, unsigned char ER, unsigned short SC, @@ -111,6 +109,8 @@ static const char * construct_st_er_desc( for (i = 0; i < 8; i++) error_flag[i] = NULL; + std::string str; + switch (CR) { case 0x10: // RECALIBRATE error_flag[2] = abrt; @@ -281,7 +281,7 @@ static const char * construct_st_er_desc( error_flag[2] = abrt; break; default: - return NULL; + return str; // "" break; } break; @@ -291,7 +291,7 @@ static const char * construct_st_er_desc( error_flag[2] = abrt; break; default: - return NULL; + return str; // "" break; } break; @@ -320,39 +320,34 @@ static const char * construct_st_er_desc( error_flag[2] = abrt; break; default: - return NULL; + return str; // "" } - 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"); + str = "Device Fault"; if (ST & 1) // Error flag - strcat(s, "; "); + str += "; "; } if (ST & 1) { // Error flag int count = 0; - strcat(s, "Error: "); + str += "Error: "; for (i = 7; i >= 0; i--) if ((ER & (1 << i)) && (error_flag[i])) { if (count++ > 0) - strcat(s, ", "); - strcat(s, error_flag[i]); + str += ", "; + str += 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]; // print number of sectors, if known, and append to print string - if (print_sector) { - snprintf(tmp, 128, " %d sectors", print_sector); - strcat(s, tmp); - } + if (print_sector) + str += strprintf(" %d sectors", print_sector); if (lba28_regs) { unsigned lba; @@ -367,8 +362,7 @@ static const char * construct_st_er_desc( lba <<= 8; // bits 0-7: SN lba |= lba28_regs->sector_number; - snprintf(tmp, 128, " at LBA = 0x%08x = %u", lba, lba); - strcat(s, tmp); + str += strprintf(" at LBA = 0x%08x = %u", lba, lba); } else if (lba48_regs) { // This assumes that upper LBA registers are 0 for 28-bit commands @@ -386,18 +380,17 @@ static const char * construct_st_er_desc( lba48 |= lba48_regs->lba_mid_register; lba48 <<= 8; lba48 |= lba48_regs->lba_low_register; - snprintf(tmp, 128, " at LBA = 0x%08"PRIx64" = %"PRIu64, lba48, lba48); - strcat(s, tmp); + str += strprintf(" at LBA = 0x%08"PRIx64" = %"PRIu64, lba48, lba48); } } - return s; + return str; } -static inline const char * construct_st_er_desc(char * s, +static inline std::string format_st_er_desc( const ata_smart_errorlog_struct * data) { - return construct_st_er_desc(s, + return format_st_er_desc( data->commands[4].commandreg, data->commands[4].featuresreg, data->error_struct.status, @@ -406,10 +399,10 @@ static inline const char * construct_st_er_desc(char * s, &data->error_struct, (const ata_smart_exterrlog_error *)0); } -static inline const char * construct_st_er_desc(char * s, +static inline std::string format_st_er_desc( const ata_smart_exterrlog_error_log * data) { - return construct_st_er_desc(s, + return format_st_er_desc( data->commands[4].command_register, data->commands[4].features_register, data->error.status_register, @@ -418,8 +411,141 @@ static inline const char * construct_st_er_desc(char * s, (const ata_smart_errorlog_error_struct *)0, &data->error); } + +static int find_msb(unsigned short word) +{ + for (int bit = 15; bit >= 0; bit--) + if (word & (1 << bit)) + return bit; + return -1; +} + +static const char * get_ata_major_version(const ata_identify_device * drive) +{ + switch (find_msb(drive->major_rev_num)) { + case 10: return "ACS-3"; + case 9: return "ACS-2"; + case 8: return "ATA8-ACS"; + case 7: return "ATA/ATAPI-7"; + case 6: return "ATA/ATAPI-6"; + case 5: return "ATA/ATAPI-5"; + case 4: return "ATA/ATAPI-4"; + case 3: return "ATA-3"; + case 2: return "ATA-2"; + case 1: return "ATA-1"; + default: return 0; + } +} + +static const char * get_ata_minor_version(const ata_identify_device * drive) +{ + switch (drive->minor_rev_num) { + case 0x0001: return "ATA-1 X3T9.2/781D prior to revision 4"; + case 0x0002: return "ATA-1 published, ANSI X3.221-1994"; + case 0x0003: return "ATA-1 X3T9.2/781D revision 4"; + case 0x0004: return "ATA-2 published, ANSI X3.279-1996"; + case 0x0005: return "ATA-2 X3T10/948D prior to revision 2k"; + case 0x0006: return "ATA-3 X3T10/2008D revision 1"; + case 0x0007: return "ATA-2 X3T10/948D revision 2k"; + case 0x0008: return "ATA-3 X3T10/2008D revision 0"; + case 0x0009: return "ATA-2 X3T10/948D revision 3"; + case 0x000a: return "ATA-3 published, ANSI X3.298-1997"; + case 0x000b: return "ATA-3 X3T10/2008D revision 6"; // 1st ATA-3 revision with SMART + case 0x000c: return "ATA-3 X3T13/2008D revision 7 and 7a"; + case 0x000d: return "ATA/ATAPI-4 X3T13/1153D revision 6"; + case 0x000e: return "ATA/ATAPI-4 T13/1153D revision 13"; + case 0x000f: return "ATA/ATAPI-4 X3T13/1153D revision 7"; + case 0x0010: return "ATA/ATAPI-4 T13/1153D revision 18"; + case 0x0011: return "ATA/ATAPI-4 T13/1153D revision 15"; + case 0x0012: return "ATA/ATAPI-4 published, ANSI NCITS 317-1998"; + case 0x0013: return "ATA/ATAPI-5 T13/1321D revision 3"; + case 0x0014: return "ATA/ATAPI-4 T13/1153D revision 14"; + case 0x0015: return "ATA/ATAPI-5 T13/1321D revision 1"; + case 0x0016: return "ATA/ATAPI-5 published, ANSI NCITS 340-2000"; + case 0x0017: return "ATA/ATAPI-4 T13/1153D revision 17"; + case 0x0018: return "ATA/ATAPI-6 T13/1410D revision 0"; + case 0x0019: return "ATA/ATAPI-6 T13/1410D revision 3a"; + case 0x001a: return "ATA/ATAPI-7 T13/1532D revision 1"; + case 0x001b: return "ATA/ATAPI-6 T13/1410D revision 2"; + case 0x001c: return "ATA/ATAPI-6 T13/1410D revision 1"; + case 0x001d: return "ATA/ATAPI-7 published, ANSI INCITS 397-2005"; + case 0x001e: return "ATA/ATAPI-7 T13/1532D revision 0"; + case 0x001f: return "ACS-3 T13/2161-D revision 3b"; + + case 0x0021: return "ATA/ATAPI-7 T13/1532D revision 4a"; + case 0x0022: return "ATA/ATAPI-6 published, ANSI INCITS 361-2002"; + + case 0x0027: return "ATA8-ACS T13/1699-D revision 3c"; + case 0x0028: return "ATA8-ACS T13/1699-D revision 6"; + case 0x0029: return "ATA8-ACS T13/1699-D revision 4"; + + case 0x0031: return "ACS-2 T13/2015-D revision 2"; + + case 0x0033: return "ATA8-ACS T13/1699-D revision 3e"; + + case 0x0039: return "ATA8-ACS T13/1699-D revision 4c"; + + case 0x0042: return "ATA8-ACS T13/1699-D revision 3f"; + + case 0x0052: return "ATA8-ACS T13/1699-D revision 3b"; + + case 0x0107: return "ATA8-ACS T13/1699-D revision 2d"; + + case 0x0110: return "ACS-2 T13/2015-D revision 3"; + + default: return 0; + } +} + +static const char * get_sata_version(const ata_identify_device * drive) +{ + unsigned short word222 = drive->words088_255[222-88]; + if ((word222 & 0xf000) != 0x1000) + return 0; + switch (find_msb(word222 & 0x0fff)) { + default: return "SATA >3.1"; + case 6: return "SATA 3.1"; + case 5: return "SATA 3.0"; + case 4: return "SATA 2.6"; + case 3: return "SATA 2.5"; + case 2: return "SATA II Ext"; + case 1: return "SATA 1.0a"; + case 0: return "ATA8-AST"; + case -1: return 0; + } +} + +static const char * get_sata_speed(int level) +{ + if (level <= 0) + return 0; + switch (level) { + default: return ">6.0 Gb/s"; + case 3: return "6.0 Gb/s"; + case 2: return "3.0 Gb/s"; + case 1: return "1.5 Gb/s"; + } +} + +static const char * get_sata_maxspeed(const ata_identify_device * drive) +{ + unsigned short word076 = drive->words047_079[76-47]; + if (word076 & 0x0001) + return 0; + return get_sata_speed(find_msb(word076 & 0x00fe)); +} + +static const char * get_sata_curspeed(const ata_identify_device * drive) +{ + unsigned short word077 = drive->words047_079[77-47]; + if (word077 & 0x0001) + return 0; + return get_sata_speed((word077 >> 1) & 0x7); +} + + static void print_drive_info(const ata_identify_device * drive, - const ata_size_info & sizes, + const ata_size_info & sizes, int rpm, const drive_settings * dbentry) { // format drive information (with byte swapping as needed) @@ -440,6 +566,15 @@ static void print_drive_info(const ata_identify_device * drive, int naa = ata_get_wwn(drive, oui, unique_id); if (naa >= 0) pout("LU WWN Device Id: %x %06x %09"PRIx64"\n", naa, oui, unique_id); + + // Additional Product Identifier (OEM Id) string in words 170-173 + // (e08130r1, added in ACS-2 Revision 1, December 17, 2008) + if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) { + char add[8+1]; + ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1); + if (add[0]) + pout("Add. Product Id: %s\n", add); + } } pout("Firmware Version: %s\n", infofound(firmware)); @@ -462,50 +597,58 @@ static void print_drive_info(const ata_identify_device * drive, } } + // Print nominal media rotation rate if reported + if (rpm) { + if (rpm == 1) + pout("Rotation Rate: Solid State Device\n"); + else if (rpm > 1) + pout("Rotation Rate: %d rpm\n", rpm); + else + pout("Rotation Rate: Unknown (0x%04x)\n", -rpm); + } + // See if drive is recognized pout("Device is: %s\n", !dbentry ? "Not in smartctl database [for details use: -P showall]": "In smartctl database [for details use: -P show]"); - // now get ATA version info - const char *description; unsigned short minorrev; - int version = ataVersionInfo(&description, drive, &minorrev); - - // 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/Documents/UploadedDocuments/project/d2008r7b-ATA-3.pdf - // to see this. So it's not enough to check if we are ATA-3. - // Version=-3 indicates ATA-3 BEFORE Revision 3. - // Version=0 indicates that no info is found. This may happen if - // the OS provides only part of the IDENTIFY data. - - std::string majorstr, minorstr; - if (version) { - if (version <= 8) { - majorstr = strprintf("%d", abs(version)); - if (description) - minorstr = description; - else if (!minorrev) - minorstr = "Exact ATA specification draft version not indicated"; - else - minorstr = strprintf("Not recognized. Minor revision code: 0x%04x", minorrev); + // Print ATA version + std::string ataver; + if ( (drive->major_rev_num != 0x0000 && drive->major_rev_num != 0xffff) + || (drive->minor_rev_num != 0x0000 && drive->minor_rev_num != 0xffff)) { + const char * majorver = get_ata_major_version(drive); + const char * minorver = get_ata_minor_version(drive); + + if (majorver && minorver && str_starts_with(minorver, majorver)) { + // Major and minor strings match, print minor string only + ataver = minorver; } else { - // Bit 9 in word 80 of ATA IDENTIFY data does not mean "ATA-9" but "ACS-2" - // TODO: handle this in ataVersionInfo() - majorstr = "8"; - if (description) - minorstr = description; - else if (!minorrev) - minorstr = strprintf("ACS-%d (revision not indicated)", version-9+2); + if (majorver) + ataver = majorver; + else + ataver = strprintf("Unknown(0x%04x)", drive->major_rev_num); + + if (minorver) + ataver += strprintf(", %s", minorver); + else if (drive->minor_rev_num != 0x0000 && drive->minor_rev_num != 0xffff) + ataver += strprintf(" (unknown minor revision code: 0x%04x)", drive->minor_rev_num); else - minorstr = strprintf("ACS-%d (unknown minor revision code: 0x%04x)", version-9+2, minorrev); + ataver += " (minor revision not indicated)"; } } + pout("ATA Version is: %s\n", infofound(ataver.c_str())); - pout("ATA Version is: %s\n", infofound(majorstr.c_str())); - pout("ATA Standard is: %s\n", infofound(minorstr.c_str())); + // If SATA drive print SATA version and speed + const char * sataver = get_sata_version(drive); + if (sataver) { + const char * maxspeed = get_sata_maxspeed(drive); + const char * curspeed = get_sata_curspeed(drive); + pout("SATA Version is: %s%s%s%s%s%s\n", sataver, + (maxspeed ? ", " : ""), (maxspeed ? maxspeed : ""), + (curspeed ? " (current: " : ""), (curspeed ? curspeed : ""), + (curspeed ? ")" : "")); + } // print current time and date and timezone char timedatetz[DATEANDEPOCHLEN]; dateandtimezone(timedatetz); @@ -514,12 +657,6 @@ static void print_drive_info(const ata_identify_device * drive, // Print warning message, if there is one if (dbentry && *dbentry->warningmsg) pout("\n==> WARNING: %s\n\n", dbentry->warningmsg); - - if (!version || 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"); } static const char *OfflineDataCollectionStatus(unsigned char status_byte) @@ -573,7 +710,7 @@ static void PrintSmartOfflineStatus(const ata_smart_values * data) } static void PrintSmartSelfExecStatus(const ata_smart_values * data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { pout("Self-test execution status: "); @@ -633,7 +770,7 @@ static void PrintSmartSelfExecStatus(const ata_smart_values * data, pout("damage.\n"); break; case 15: - if (fix_firmwarebug == FIX_SAMSUNG3 && data->self_test_exec_status == 0xf0) { + if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) { pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", (int)data->self_test_exec_status); pout("with unknown result or self-test in\n\t\t\t\t\t"); @@ -798,7 +935,7 @@ static int find_failed_attr(const ata_smart_values * data, // onlyfailed=2: ones that are failed, or have failed with or without prefailure bit set static void PrintSmartAttribWithThres(const ata_smart_values * data, const ata_smart_thresholds_pvt * thresholds, - const ata_vendor_attr_defs & defs, + const ata_vendor_attr_defs & defs, int rpm, int onlyfailed, unsigned char format) { bool brief = !!(format & ata_print_options::FMT_BRIEF); @@ -859,7 +996,7 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data, // Print line for each valid attribute std::string idstr = (!hexid ? strprintf("%3d", attr.id) : strprintf("0x%02x", attr.id)); - std::string attrname = ata_get_smart_attr_name(attr.id, defs); + std::string attrname = ata_get_smart_attr_name(attr.id, defs, rpm); std::string rawstr = ata_format_attr_raw_value(attr, defs); if (!brief) @@ -922,14 +1059,14 @@ static void ataPrintSCTCapability(const ata_identify_device *drive) static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_identify_device *drive, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { pout("General SMART Values:\n"); PrintSmartOfflineStatus(data); if (isSupportSelfTest(data)){ - PrintSmartSelfExecStatus(data, fix_firmwarebug); + PrintSmartSelfExecStatus(data, firmwarebugs); } PrintSmartTotalTimeCompleteOffline(data); @@ -971,7 +1108,7 @@ static unsigned GetNumLogSectors(const ata_smart_log_directory * logdir, unsigne } // Get name of log. -// Table A.2 of T13/2161-D Revision 2 (ACS-3), February 21, 2012. +// Table A.2 of T13/2161-D (ACS-3) Revision 4, September 4, 2012 static const char * GetLogName(unsigned logaddr) { switch (logaddr) { @@ -980,12 +1117,16 @@ static const char * GetLogName(unsigned logaddr) case 0x02: return "Comprehensive SMART error log"; case 0x03: return "Ext. Comprehensive SMART error log"; case 0x04: return "Device Statistics log"; - case 0x05: return "Reserved for the CFA"; // ACS-2 + case 0x05: return "Reserved for CFA"; // ACS-2 case 0x06: return "SMART self-test log"; case 0x07: return "Extended self-test log"; case 0x08: return "Power Conditions log"; // ACS-2 case 0x09: return "Selective self-test log"; + case 0x0a: return "Device Statistics Notification"; // ACS-3 + case 0x0b: return "Reserved for CFA"; // ACS-3 + case 0x0d: return "LPS Mis-alignment log"; // ACS-2 + case 0x10: return "NCQ Command Error log"; case 0x11: return "SATA Phy Event Counters"; case 0x12: return "SATA NCQ Queue Management log"; // ACS-3 @@ -993,14 +1134,18 @@ static const char * GetLogName(unsigned logaddr) case 0x14: case 0x15: case 0x16: return "Reserved for Serial ATA"; + case 0x19: return "LBA Status log"; // ACS-3 - case 0x20: return "Streaming performance log"; // Obsolete + + case 0x20: return "Streaming performance log [OBS-8]"; case 0x21: return "Write stream error log"; case 0x22: return "Read stream error log"; - case 0x23: return "Delayed sector log"; // Obsolete + case 0x23: return "Delayed sector log [OBS-8]"; case 0x24: return "Current Device Internal Status Data log"; // ACS-3 case 0x25: return "Saved Device Internal Status Data log"; // ACS-3 + case 0x30: return "IDENTIFY DEVICE data log"; // ACS-3 + case 0xe0: return "SCT Command/Status"; case 0xe1: return "SCT Data Transfer"; default: @@ -1013,6 +1158,44 @@ static const char * GetLogName(unsigned logaddr) /*NOTREACHED*/ } +// Get log access permissions +static const char * get_log_rw(unsigned logaddr) +{ + if ( ( logaddr <= 0x08) + || (0x0d == logaddr) + || (0x10 <= logaddr && logaddr <= 0x13) + || (0x19 == logaddr) + || (0x20 <= logaddr && logaddr <= 0x25) + || (0x30 == logaddr)) + return "R/O"; + + if ( (0x09 <= logaddr && logaddr <= 0x0a) + || (0x80 <= logaddr && logaddr <= 0x9f) + || (0xe0 <= logaddr && logaddr <= 0xe1)) + return "R/W"; + + if (0xa0 <= logaddr && logaddr <= 0xdf) + return "VS"; // Vendor specific + + return "-"; // Unknown/Reserved +} + +// Init a fake log directory, assume that standard logs are supported +const ata_smart_log_directory * fake_logdir(ata_smart_log_directory * logdir, + const ata_print_options & options) +{ + memset(logdir, 0, sizeof(*logdir)); + logdir->logversion = 255; + logdir->entry[0x01-1].numsectors = 1; + logdir->entry[0x03-1].numsectors = (options.smart_ext_error_log + (4-1)) / 4; + logdir->entry[0x04-1].numsectors = 8; + logdir->entry[0x06-1].numsectors = 1; + logdir->entry[0x07-1].numsectors = (options.smart_ext_selftest_log + (19-1)) / 19; + logdir->entry[0x09-1].numsectors = 1; + logdir->entry[0x11-1].numsectors = 1; + return logdir; +} + // Print SMART and/or GP Log Directory static void PrintLogDirectories(const ata_smart_log_directory * gplogdir, const ata_smart_log_directory * smartlogdir) @@ -1024,6 +1207,8 @@ static void PrintLogDirectories(const ata_smart_log_directory * gplogdir, (gplogdir ? " " : ""), smartlogdir->logversion, (smartlogdir->logversion==1 ? " [multi-sector log support]" : "")); + pout("Address Access R/W Size Description\n"); + for (unsigned i = 0; i <= 0xff; i++) { // Get number of sectors unsigned smart_numsect = GetNumLogSectors(smartlogdir, i, false); @@ -1032,18 +1217,47 @@ static void PrintLogDirectories(const ata_smart_log_directory * gplogdir, if (!(smart_numsect || gp_numsect)) continue; // Log does not exist + const char * acc; unsigned size; + if (smart_numsect == gp_numsect) { + acc = "GPL,SL"; size = gp_numsect; + } + else if (!smart_numsect) { + acc = "GPL"; size = gp_numsect; + } + else if (!gp_numsect) { + acc = " SL"; size = smart_numsect; + } + else { + acc = 0; size = 0; + } + + unsigned i2 = i; + if (acc && ((0x80 <= i && i < 0x9f) || (0xa0 <= i && i < 0xdf))) { + // Find range of Host/Device vendor specific logs with same size + unsigned imax = (i < 0x9f ? 0x9f : 0xdf); + for (unsigned j = i+1; j <= imax; j++) { + unsigned sn = GetNumLogSectors(smartlogdir, j, false); + unsigned gn = GetNumLogSectors(gplogdir , j, true ); + + if (!(sn == smart_numsect && gn == gp_numsect)) + break; + i2 = j; + } + } + const char * name = GetLogName(i); + const char * rw = get_log_rw(i); - // Print name and length of log. - // If both SMART and GP exist, print separate entries if length differ. - if (smart_numsect == gp_numsect) - pout( "GP/S Log at address 0x%02x has %4d sectors [%s]\n", i, smart_numsect, name); + if (i2 > i) { + pout("0x%02x-0x%02x %-6s %-3s %5u %s\n", i, i2, acc, rw, size, name); + i = i2; + } + else if (acc) + pout( "0x%02x %-6s %-3s %5u %s\n", i, acc, rw, size, name); else { - if (gp_numsect) - pout("GP %sLog at address 0x%02x has %4d sectors [%s]\n", (smartlogdir?" ":""), - i, gp_numsect, name); - if (smart_numsect) - pout("SMART Log at address 0x%02x has %4d sectors [%s]\n", i, smart_numsect, name); + // GPL and SL support different sizes + pout( "0x%02x %-6s %-3s %5u %s\n", i, "GPL", rw, gp_numsect, name); + pout( "0x%02x %-6s %-3s %5u %s\n", i, "SL", rw, smart_numsect, name); } } pout("\n"); @@ -1227,7 +1441,7 @@ static void print_device_statistics_page(const unsigned char * data, int page, } else { // Value not known (yet) - strcpy(valstr, "-"); + valstr[0] = '-'; valstr[1] = 0; } pout("%3d 0x%03x %d%c %15s%c %s\n", @@ -1247,12 +1461,14 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors, { // Read list of supported pages from page 0 unsigned char page_0[512] = {0, }; - if (!ataReadLogExt(device, 0x04, 0, 0, page_0, 1)) + if (!ataReadLogExt(device, 0x04, 0, 0, page_0, 1)) { + pout("Read Device Statistics page 0 failed\n\n"); return false; + } unsigned char nentries = page_0[8]; if (!(page_0[2] == 0 && nentries > 0)) { - pout("Device Statistics page 0 is invalid (page=%d, nentries=%d)\n", page_0[2], nentries); + pout("Device Statistics page 0 is invalid (page=%d, nentries=%d)\n\n", page_0[2], nentries); return false; } @@ -1303,8 +1519,10 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors, for (i = 0; i < pages.size(); i++) { int page = pages[i]; unsigned char page_n[512] = {0, }; - if (!ataReadLogExt(device, 0x04, 0, page, page_n, 1)) + if (!ataReadLogExt(device, 0x04, 0, page, page_n, 1)) { + pout("Read Device Statistics page %d failed\n\n", page); return false; + } print_device_statistics_page(page_n, page, need_trailer); } @@ -1385,6 +1603,25 @@ static void PrintSataPhyEventCounters(const unsigned char * data, bool reset) pout("\n"); } +// Format milliseconds from error log entry as "DAYS+H:M:S.MSEC" +static std::string format_milliseconds(unsigned msec) +{ + unsigned days = msec / 86400000U; + msec -= days * 86400000U; + unsigned hours = msec / 3600000U; + msec -= hours * 3600000U; + unsigned min = msec / 60000U; + msec -= min * 60000U; + unsigned sec = msec / 1000U; + msec -= sec * 1000U; + + std::string str; + if (days) + str = strprintf("%2ud+", days); + str += strprintf("%02u:%02u:%02u.%03u", hours, min, sec, msec); + return str; +} + // Get description for 'state' value from SMART Error Logs static const char * get_error_log_state_desc(unsigned state) { @@ -1403,7 +1640,7 @@ static const char * get_error_log_state_desc(unsigned state) // returns number of errors static int PrintSmartErrorlog(const ata_smart_errorlog *data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { pout("SMART Error Log Version: %d\n", (int)data->revnumber); @@ -1422,7 +1659,7 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data, } // Some internal consistency checking of the data structures - if ((data->ata_error_count-data->error_log_pointer)%5 && fix_firmwarebug != FIX_SAMSUNG2) { + if ((data->ata_error_count-data->error_log_pointer) % 5 && !firmwarebugs.is_set(BUG_SAMSUNG2)) { pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n", data->ata_error_count,data->error_log_pointer); } @@ -1481,10 +1718,9 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data, (int)summary->drive_head); // Add a description of the contents of the status and error registers // if possible - char descbuf[256]; - const char * st_er_desc = construct_st_er_desc(descbuf, elog); - if (st_er_desc) - pout(" %s", st_er_desc); + std::string st_er_desc = format_st_er_desc(elog); + if (!st_er_desc.empty()) + pout(" %s", st_er_desc.c_str()); 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" @@ -1494,11 +1730,6 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data, // Spec says: unused data command structures shall be zero filled if (nonempty(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, @@ -1508,7 +1739,7 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data, (int)thiscommand->cylinder_high, (int)thiscommand->drive_head, (int)thiscommand->devicecontrolreg, - timestring, + format_milliseconds(thiscommand->timestamp).c_str(), look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg)); } } @@ -1626,10 +1857,9 @@ static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log, // Add a description of the contents of the status and error registers // if possible - char descbuf[256]; - const char * st_er_desc = construct_st_er_desc(descbuf, &entry); - if (st_er_desc) - pout(" %s", st_er_desc); + std::string st_er_desc = format_st_er_desc(&entry); + if (!st_er_desc.empty()) + pout(" %s", st_er_desc.c_str()); pout("\n\n"); // Print command history @@ -1644,9 +1874,6 @@ static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log, continue; // Print registers, timestamp and ATA command name - char timestring[32]; - MsecToText(cmd.timestamp, timestring); - pout(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n", cmd.command_register, cmd.features_register_hi, @@ -1661,7 +1888,7 @@ static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log, cmd.lba_low_register, cmd.device_register, cmd.device_control_register, - timestring, + format_milliseconds(cmd.timestamp).c_str(), look_up_ata_command(cmd.command_register, cmd.features_register)); } pout("\n"); @@ -1869,16 +2096,15 @@ static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log } // Format SCT Temperature value -static const char * sct_ptemp(signed char x, char * buf) +static const char * sct_ptemp(signed char x, char (& buf)[20]) { if (x == -128 /*0x80 = unknown*/) - strcpy(buf, " ?"); - else - sprintf(buf, "%2d", x); + return " ?"; + snprintf(buf, sizeof(buf), "%2d", x); return buf; } -static const char * sct_pbar(int x, char * buf) +static const char * sct_pbar(int x, char (& buf)[64]) { if (x <= 19) x = 0; @@ -1957,8 +2183,9 @@ static int ataPrintSCTStatus(const ata_sct_status_response * sts) // Print SCT Temperature History Table static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) { - char buf1[20], buf2[80]; - pout("SCT Temperature History Version: %u\n", tmh->format_version); + char buf1[20], buf2[20], buf3[64]; + pout("SCT Temperature History Version: %u%s\n", tmh->format_version, + (tmh->format_version != 2 ? " (Unknown, should be 2)" : "")); pout("Temperature Sampling Period: %u minute%s\n", tmh->sampling_period, (tmh->sampling_period==1?"":"s")); pout("Temperature Logging Interval: %u minute%s\n", @@ -1968,8 +2195,12 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) pout("Min/Max Temperature Limit: %s/%s Celsius\n", sct_ptemp(tmh->under_limit, buf1), sct_ptemp(tmh->over_limit, buf2)); pout("Temperature History Size (Index): %u (%u)\n", tmh->cb_size, tmh->cb_index); + if (!(0 < tmh->cb_size && tmh->cb_size <= sizeof(tmh->cb) && tmh->cb_index < tmh->cb_size)) { - pout("Error invalid Temperature History Size or Index\n"); + if (!tmh->cb_size) + pout("Temperature History is empty\n"); + else + pout("Invalid Temperature History Size or Index\n"); return 0; } @@ -1992,11 +2223,11 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) // TODO: Don't print times < boot time strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&t)); pout(" %3u %s %s %s\n", i, date, - sct_ptemp(tmh->cb[i], buf1), sct_pbar(tmh->cb[i], buf2)); + sct_ptemp(tmh->cb[i], buf1), sct_pbar(tmh->cb[i], buf3)); } else if (n == n1+1) { pout(" ... ..(%3u skipped). .. %s\n", - n2-n1-2, sct_pbar(tmh->cb[i], buf2)); + n2-n1-2, sct_pbar(tmh->cb[i], buf3)); } t += interval * 60; i = (i+1) % tmh->cb_size; n++; } @@ -2206,7 +2437,6 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) options.gp_logdir || options.smart_ext_error_log || options.smart_ext_selftest_log - || options.sataphy || options.devstat_all_pages || options.devstat_ssd_page || !options.devstat_pages.empty() @@ -2230,9 +2460,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) ); // Exit if no further options specified - if (!( options.drive_info || need_smart_support - || need_smart_logdir || need_gp_logdir - || need_sct_support || options.get_set_used)) { + if (!( options.drive_info || options.show_presets + || need_smart_support || need_smart_logdir + || need_gp_logdir || need_sct_support + || options.sataphy + || options.identify_word_level >= 0 + || options.get_set_used )) { if (powername) pout("Device is in %s mode\n", powername); else @@ -2244,15 +2477,17 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Start by getting Drive ID information. We need this, to know if SMART is supported. int returnval = 0; ata_identify_device drive; memset(&drive, 0, sizeof(drive)); + unsigned char raw_drive[sizeof(drive)]; memset(&raw_drive, 0, sizeof(raw_drive)); + device->clear_err(); - int retid = ata_read_identity(device, &drive, options.fix_swapped_id); + int retid = ata_read_identity(device, &drive, options.fix_swapped_id, raw_drive); if (retid < 0) { - pout("Smartctl: Device Read Identity Failed: %s\n\n", + pout("Read Device Identity failed: %s\n\n", (device->get_errno() ? device->get_errmsg() : "Unknown error")); failuretest(MANDATORY_CMD, returnval|=FAILID); } else if (!nonempty(&drive, sizeof(drive))) { - pout("Smartctl: Device Read Identity Failed: empty IDENTIFY data\n\n"); + pout("Read Device Identity failed: empty IDENTIFY data\n\n"); failuretest(MANDATORY_CMD, returnval|=FAILID); } @@ -2264,20 +2499,28 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Use preset vendor attribute options unless user has requested otherwise. ata_vendor_attr_defs attribute_defs = options.attribute_defs; - unsigned char fix_firmwarebug = options.fix_firmwarebug; + firmwarebug_defs firmwarebugs = options.firmwarebugs; const drive_settings * dbentry = 0; if (!options.ignore_presets) dbentry = lookup_drive_apply_presets(&drive, attribute_defs, - fix_firmwarebug); + firmwarebugs); - // Get capacity and sector sizes + // Get capacity, sector sizes and rotation rate ata_size_info sizes; ata_get_size_info(&drive, sizes); + int rpm = ata_get_rotation_rate(&drive); + + // Print ATA IDENTIFY info if requested + if (options.identify_word_level >= 0) { + pout("=== ATA IDENTIFY DATA ===\n"); + // Pass raw data without endianness adjustments + ata_print_identify_data(raw_drive, (options.identify_word_level > 0), options.identify_bit_level); + } // Print most drive identity information if requested if (options.drive_info) { pout("=== START OF INFORMATION SECTION ===\n"); - print_drive_info(&drive, sizes, dbentry); + print_drive_info(&drive, sizes, rpm, dbentry); } // Check and print SMART support and state @@ -2476,7 +2719,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Enable/Disable SMART commands if (options.smart_enable) { if (ataEnableSmart(device)) { - pout("Smartctl: SMART Enable Failed.\n\n"); + pout("SMART Enable failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else { @@ -2488,21 +2731,23 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Turn off SMART on device if (options.smart_disable) { if (ataDisableSmart(device)) { - pout( "Smartctl: SMART Disable Failed.\n\n"); + pout("SMART Disable failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD,returnval|=FAILSMART); } } // Exit if SMART is disabled but must be enabled to proceed - if (options.smart_disable || (smart_enabled <= 0 && need_smart_enabled)) { + if (options.smart_disable || (smart_enabled <= 0 && need_smart_enabled && !is_permissive())) { pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); + if (!options.smart_disable) + pout("(override with '-T permissive' option)\n"); return returnval; } // Enable/Disable Auto-save attributes if (options.smart_auto_save_enable) { if (ataEnableAutoSave(device)){ - pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n"); + pout("SMART Enable Attribute Autosave failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else @@ -2511,7 +2756,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (options.smart_auto_save_disable) { if (ataDisableAutoSave(device)){ - pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n"); + pout("SMART Disable Attribute Autosave failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else @@ -2525,7 +2770,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (need_smart_val) { if (ataReadSmartValues(device, &smartval)) { - pout("Smartctl: SMART Read Values failed.\n\n"); + pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { @@ -2533,7 +2778,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (options.smart_check_status || options.smart_vendor_attrib) { if (ataReadSmartThresholds(device, &smartthres)){ - pout("Smartctl: SMART Read Thresholds failed.\n\n"); + pout("Read SMART Thresholds failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2546,12 +2791,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) bool needupdate = false; if (options.smart_auto_offl_enable) { if (!isSupportAutomaticTimer(&smartval)){ - pout("Warning: device does not support SMART Automatic Timers.\n\n"); + pout("SMART Automatic Timers not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } needupdate = smart_val_ok; if (ataEnableAutoOffline(device)){ - pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); + pout("SMART Enable Automatic Offline failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2560,12 +2805,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (options.smart_auto_offl_disable) { if (!isSupportAutomaticTimer(&smartval)){ - pout("Warning: device does not support SMART Automatic Timers.\n\n"); + pout("SMART Automatic Timers not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } needupdate = smart_val_ok; if (ataDisableAutoOffline(device)){ - pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n"); + pout("SMART Disable Automatic Offline failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2573,7 +2818,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } if (needupdate && ataReadSmartValues(device, &smartval)){ - pout("Smartctl: SMART Read Values failed.\n\n"); + pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); smart_val_ok = false; } @@ -2606,7 +2851,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) else { print_on(); pout("Please note the following marginal Attributes:\n"); - PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, 2, options.output_format); + PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 2, options.output_format); } returnval|=FAILAGE; } @@ -2627,7 +2872,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) else { print_on(); pout("Failed Attributes:\n"); - PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, 1, options.output_format); + PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 1, options.output_format); } } else @@ -2660,7 +2905,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) else { print_on(); pout("Failed Attributes:\n"); - PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, 1, options.output_format); + PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 1, options.output_format); } } else { @@ -2672,7 +2917,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) else { print_on(); pout("Please note the following marginal Attributes:\n"); - PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, 2, options.output_format); + PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 2, options.output_format); } returnval|=FAILAGE; } @@ -2688,12 +2933,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Print general SMART values if (smart_val_ok && options.smart_general_values) - PrintGeneralSmartValues(&smartval, &drive, fix_firmwarebug); + PrintGeneralSmartValues(&smartval, &drive, firmwarebugs); // Print vendor-specific attributes if (smart_val_ok && options.smart_vendor_attrib) { print_on(); - PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, + PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, (printing_is_switchable ? 2 : 0), options.output_format); print_off(); } @@ -2710,8 +2955,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Read SMART Log directory if (need_smart_logdir) { - if (ataReadLogDirectory(device, &smartlogdir_buf, false)) { - pout("Read SMART Log Directory failed.\n\n"); + if (firmwarebugs.is_set(BUG_NOLOGDIR)) + smartlogdir = fake_logdir(&smartlogdir_buf, options); + else if (ataReadLogDirectory(device, &smartlogdir_buf, false)) { + pout("Read SMART Log Directory failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2720,8 +2967,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Read GP Log directory if (need_gp_logdir) { - if (ataReadLogDirectory(device, &gplogdir_buf, true)) { - pout("Read GP Log Directory failed.\n\n"); + if (firmwarebugs.is_set(BUG_NOLOGDIR)) + gplogdir = fake_logdir(&gplogdir_buf, options); + else if (ataReadLogDirectory(device, &gplogdir_buf, true)) { + pout("Read GP Log Directory failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2729,8 +2978,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } // Print log directories - if ((options.gp_logdir && gplogdir) || (options.smart_logdir && smartlogdir)) - PrintLogDirectories(gplogdir, smartlogdir); + if ((options.gp_logdir && gplogdir) || (options.smart_logdir && smartlogdir)) { + if (firmwarebugs.is_set(BUG_NOLOGDIR)) + pout("Log Directories not read due to '-F nologdir' option\n\n"); + else + PrintLogDirectories(gplogdir, smartlogdir); + } // Print log pages for (i = 0; i < options.log_requests.size(); i++) { @@ -2787,14 +3040,16 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) bool ok = false; unsigned nsectors = GetNumLogSectors(gplogdir, 0x03, true); if (!nsectors) - pout("SMART Extended Comprehensive Error Log (GP Log 0x03) not supported\n"); + pout("SMART Extended Comprehensive Error Log (GP Log 0x03) not supported\n\n"); else if (nsectors >= 256) - pout("SMART Extended Comprehensive Error Log size %u not supported\n", nsectors); + pout("SMART Extended Comprehensive Error Log size %u not supported\n\n", nsectors); else { raw_buffer log_03_buf(nsectors * 512); ata_smart_exterrlog * log_03 = (ata_smart_exterrlog *)log_03_buf.data(); - if (!ataReadExtErrorLog(device, log_03, nsectors)) + if (!ataReadExtErrorLog(device, log_03, nsectors, firmwarebugs)) { + pout("Read SMART Extended Comprehensive Error Log failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else { if (PrintSmartExtErrorLog(log_03, nsectors, options.smart_ext_error_log)) returnval |= FAILERR; @@ -2815,17 +3070,17 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (!( ( smartlogdir && GetNumLogSectors(smartlogdir, 0x01, false)) || (!smartlogdir && isSmartErrorLogCapable(&smartval, &drive) ) || is_permissive() )) { - pout("SMART Error Log not supported\n"); + pout("SMART Error Log not supported\n\n"); } else { ata_smart_errorlog smarterror; memset(&smarterror, 0, sizeof(smarterror)); - if (ataReadErrorLog(device, &smarterror, fix_firmwarebug)) { - pout("Smartctl: SMART Error Log Read Failed\n"); + if (ataReadErrorLog(device, &smarterror, firmwarebugs)) { + pout("Read SMART Error Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { // quiet mode is turned on inside PrintSmartErrorLog() - if (PrintSmartErrorlog(&smarterror, fix_firmwarebug)) + if (PrintSmartErrorlog(&smarterror, firmwarebugs)) returnval|=FAILERR; print_off(); } @@ -2838,14 +3093,16 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) bool ok = false; unsigned nsectors = GetNumLogSectors(gplogdir, 0x07, true); if (!nsectors) - pout("SMART Extended Self-test Log (GP Log 0x07) not supported\n"); + pout("SMART Extended Self-test Log (GP Log 0x07) not supported\n\n"); else if (nsectors >= 256) - pout("SMART Extended Self-test Log size %u not supported\n", nsectors); + pout("SMART Extended Self-test Log size %u not supported\n\n", nsectors); else { raw_buffer log_07_buf(nsectors * 512); ata_smart_extselftestlog * log_07 = (ata_smart_extselftestlog *)log_07_buf.data(); - if (!ataReadExtSelfTestLog(device, log_07, nsectors)) + if (!ataReadExtSelfTestLog(device, log_07, nsectors)) { + pout("Read SMART Extended Self-test Log failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else { if (PrintSmartExtSelfTestLog(log_07, nsectors, options.smart_ext_selftest_log)) returnval |= FAILLOG; @@ -2866,17 +3123,17 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (!( ( smartlogdir && GetNumLogSectors(smartlogdir, 0x06, false)) || (!smartlogdir && isSmartTestLogCapable(&smartval, &drive) ) || is_permissive() )) { - pout("SMART Self-test Log not supported\n"); + pout("SMART Self-test Log not supported\n\n"); } else { ata_smart_selftestlog smartselftest; memset(&smartselftest, 0, sizeof(smartselftest)); - if (ataReadSelfTestLog(device, &smartselftest, fix_firmwarebug)) { - pout("Smartctl: SMART Self Test Log Read Failed\n"); + if (ataReadSelfTestLog(device, &smartselftest, firmwarebugs)) { + pout("Read SMART Self-test Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { print_on(); - if (ataPrintSmartSelfTestlog(&smartselftest, !printing_is_switchable, fix_firmwarebug)) + if (ataPrintSmartSelfTestlog(&smartselftest, !printing_is_switchable, firmwarebugs)) returnval |= FAILLOG; print_off(); pout("\n"); @@ -2889,9 +3146,9 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) ata_selective_self_test_log log; if (!isSupportSelectiveSelfTest(&smartval)) - pout("Device does not support Selective Self Tests/Logging\n"); + pout("Selective Self-tests/Logging not supported\n\n"); else if(ataReadSelectiveSelfTestLog(device, &log)) { - pout("Smartctl: SMART Selective Self Test Log Read Failed\n"); + pout("Read SMART Selective Self-test Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { @@ -2910,7 +3167,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) bool sct_ok = false; if (need_sct_support) { if (!isSCTCapable(&drive)) { - pout("Warning: device does not support SCT Commands\n"); + pout("SCT Commands not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else @@ -2932,12 +3189,13 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } else { if (!isSCTDataTableCapable(&drive)) { - pout("Warning: device does not support SCT Data Table command\n"); + pout("SCT Data Table command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } // Read SCT status and temperature history if (ataReadSCTTempHist(device, &tmh, &sts)) { + pout("Read SCT Temperature History failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } @@ -2951,11 +3209,12 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (options.sct_temp_int) { // Set new temperature logging interval if (!isSCTFeatureControlCapable(&drive)) { - pout("Warning: device does not support SCT Feature Control command\n"); + pout("SCT Feature Control command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } if (ataSetSCTTempInterval(device, options.sct_temp_int, options.sct_temp_int_pers)) { + pout("Write Temperature Logging Interval failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } @@ -2970,7 +3229,7 @@ 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"); + pout("SCT Error Recovery Control command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { @@ -2979,7 +3238,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // 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("SCT (Set) Error Recovery Control command failed\n"); if (!( (options.sct_erc_readtime == 70 && options.sct_erc_writetime == 70) || (options.sct_erc_readtime == 0 && options.sct_erc_writetime == 0))) pout("Retry with: 'scterc,70,70' to enable ERC or 'scterc,0,0' to disable\n"); @@ -2996,7 +3255,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) 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"); + pout("SCT (Get) Error Recovery Control command failed\n"); if (options.sct_erc_set) { pout("The previous SCT (Set) Error Recovery Control command succeeded\n"); ataPrintSCTErrorRecoveryControl(true, options.sct_erc_readtime, @@ -3015,7 +3274,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (options.devstat_all_pages || options.devstat_ssd_page || !options.devstat_pages.empty()) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x04, true); if (!nsectors) - pout("Device Statistics (GP Log 0x04) not supported\n"); + pout("Device Statistics (GP Log 0x04) not supported\n\n"); else if (!print_device_statistics(device, nsectors, options.devstat_pages, options.devstat_all_pages, options.devstat_ssd_page)) failuretest(OPTIONAL_CMD, returnval|=FAILSMART); @@ -3024,15 +3283,20 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) // Print SATA Phy Event Counters if (options.sataphy) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true); + // Packet interface devices do not provide a log directory, check support bit + if (!nsectors && (drive.words047_079[76-47] & 0x0401) == 0x0400) + nsectors = 1; if (!nsectors) - pout("SATA Phy Event Counters (GP Log 0x11) not supported\n"); + pout("SATA Phy Event Counters (GP Log 0x11) not supported\n\n"); else if (nsectors != 1) - pout("SATA Phy Event Counters with %u sectors not supported\n", nsectors); + pout("SATA Phy Event Counters with %u sectors not supported\n\n", nsectors); else { unsigned char log_11[512] = {0, }; unsigned char features = (options.sataphy_reset ? 0x01 : 0x00); - if (!ataReadLogExt(device, 0x11, features, 0, log_11, 1)) + if (!ataReadLogExt(device, 0x11, features, 0, log_11, 1)) { + pout("Read SATA Phy Event Counters failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else PrintSataPhyEventCounters(log_11, options.sataphy_reset); } @@ -3058,7 +3322,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) switch (options.smart_selftest_type) { case OFFLINE_FULL_SCAN: if (!isSupportExecuteOfflineImmediate(&smartval)){ - pout("Warning: device does not support Execute Offline Immediate function.\n\n"); + pout("Execute Offline Immediate function not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } break; @@ -3068,21 +3332,21 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) case SHORT_CAPTIVE_SELF_TEST: case EXTEND_CAPTIVE_SELF_TEST: if (!isSupportSelfTest(&smartval)){ - pout("Warning: device does not support Self-Test functions.\n\n"); + pout("Self-test functions not supported\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"); + pout("Conveyance Self-test functions not supported\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"); + pout("Selective Self-test functions not supported\n\n"); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } break; @@ -3105,7 +3369,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) if (isSupportOfflineAbort(&smartval)) pout("Note: giving further SMART commands will abort Offline testing\n"); else if (ataReadSmartValues(device, &smartval)){ - pout("Smartctl: SMART Read Values failed.\n"); + pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } } diff --git a/ataprint.h b/ataprint.h index 93fdb72..d8ca067 100644 --- a/ataprint.h +++ b/ataprint.h @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -26,7 +25,7 @@ #ifndef ATAPRINT_H_ #define ATAPRINT_H_ -#define ATAPRINT_H_CVSID "$Id: ataprint.h 3530 2012-03-27 19:54:06Z chrfranke $\n" +#define ATAPRINT_H_CVSID "$Id: ataprint.h 3609 2012-09-20 21:24:43Z chrfranke $\n" #include @@ -47,6 +46,7 @@ struct ata_log_request struct ata_print_options { bool drive_info; + int identify_word_level, identify_bit_level; bool smart_check_status; bool smart_general_values; bool smart_vendor_attrib; @@ -84,7 +84,7 @@ struct ata_print_options enum { FMT_BRIEF = 0x01, FMT_HEX_ID = 0x02, FMT_HEX_VAL = 0x04 }; unsigned char output_format; // FMT_* flags - unsigned char fix_firmwarebug; // FIX_*, see atacmds.h + firmwarebug_defs firmwarebugs; // -F options bool fix_swapped_id; // Fix swapped ID strings returned by some buggy drivers ata_vendor_attr_defs attribute_defs; // -v options @@ -109,6 +109,7 @@ struct ata_print_options ata_print_options() : drive_info(false), + identify_word_level(-1), identify_bit_level(-1), smart_check_status(false), smart_general_values(false), smart_vendor_attrib(false), @@ -131,7 +132,6 @@ struct ata_print_options smart_selftest_type(-1), smart_selftest_force(false), sct_temp_int(0), sct_temp_int_pers(false), output_format(0), - fix_firmwarebug(FIX_NOTSPECIFIED), fix_swapped_id(false), ignore_presets(false), show_presets(false), diff --git a/autogen.sh b/autogen.sh index 82da0ff..90d1e95 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,5 @@ #!/bin/sh -# $Id: autogen.sh 3537 2012-04-28 13:22:41Z chrfranke $ +# $Id: autogen.sh 3748 2013-01-18 20:12:44Z chrfranke $ # # Generate ./configure from config.in and Makefile.in from Makefile.am. # This also adds files like missing,depcomp,install-sh to the source @@ -32,7 +32,8 @@ typep() return 1 } -test -x "$AUTOMAKE" || AUTOMAKE=`typep automake-1.11` || AUTOMAKE=`typep automake-1.10` || +test -x "$AUTOMAKE" || AUTOMAKE=`typep automake-1.12` || + AUTOMAKE=`typep automake-1.11` || AUTOMAKE=`typep automake-1.10` || AUTOMAKE=`typep automake-1.9` || AUTOMAKE=`typep automake-1.8` || AUTOMAKE=`typep automake-1.7` || AUTOMAKE=`typep automake17` || { @@ -83,7 +84,7 @@ case "$ver" in rm -f casetest.tmp ;; - 1.9.[1-6]|1.10|1.10.[12]|1.11|1.11.[1-3]) + 1.9.[1-6]|1.10|1.10.[12]|1.11|1.11.[1-6]|1.12.[3-5]) # OK ;; diff --git a/cciss.cpp b/cciss.cpp index 1634916..7d039b1 100644 --- a/cciss.cpp +++ b/cciss.cpp @@ -24,6 +24,10 @@ # define _HAVE_CCISS #elif defined(__FreeBSD_kernel__) # include +# ifdef __GLIBC__ +# include +# include +# endif # include CISS_LOCATION # define _HAVE_CCISS #endif @@ -34,7 +38,7 @@ #include "scsicmds.h" #include "utility.h" -const char * cciss_cpp_cvsid = "$Id: cciss.cpp 3446 2011-10-13 22:36:28Z samm2 $" +const char * cciss_cpp_cvsid = "$Id: cciss.cpp 3578 2012-07-20 17:26:32Z chrfranke $" CCISS_H_CVSID; typedef struct _ReportLUNdata_struct diff --git a/configure.in b/configure.ac similarity index 94% rename from configure.in rename to configure.ac index 5c3d352..912c0d2 100644 --- a/configure.in +++ b/configure.ac @@ -1,15 +1,15 @@ # -# $Id: configure.in 3527 2012-03-25 16:42:24Z chrfranke $ +# $Id: configure.ac 3811 2013-04-20 17:10:41Z chrfranke $ # dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(smartmontools, 5.43, smartmontools-support@lists.sourceforge.net) +AC_INIT(smartmontools, 6.2, 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 3527 2012-03-25 16:42:24Z chrfranke $'` -smartmontools_release_date=2011-10-20 -smartmontools_release_time="19:19:34 UTC" +smartmontools_cvs_tag=`echo '$Id: configure.ac 3811 2013-04-20 17:10:41Z chrfranke $'` +smartmontools_release_date=2013-03-16 +smartmontools_release_time="14:35:27 UTC" AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args", [smartmontools Configure Arguments]) AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_DATE, "$smartmontools_configure_date", [smartmontools Configure Date]) @@ -48,8 +48,8 @@ case "${host}" in /usr/bin/uname | grep -i '^CYGWIN' >/dev/null; then AC_MSG_ERROR([Build with MinGW on Cygwin requires cross-compilation, see INSTALL file.]) fi - AC_CHECK_PROGS(WINDMC, [${host}-windmc windmc]) - AC_CHECK_PROGS(WINDRES, [${host}-windres windres]) + AC_CHECK_TOOL(WINDMC, [windmc]) + AC_CHECK_TOOL(WINDRES, [windres]) AC_MSG_CHECKING([checking for makensis]) if test -z "$MAKENSIS"; then @@ -80,14 +80,6 @@ AC_SUBST([svn_deps]) AM_CONDITIONAL(IS_SVN_BUILD, [test "$is_svn_build" = "yes"]) AC_MSG_RESULT([$is_svn_build]) -dnl Checks for libraries needed for name services (Solaris needs -dnl libnsl, might in the future also need libsocket) -# AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES]) -AC_SEARCH_LIBS(socket, socket) -AC_SEARCH_LIBS(gethostbyname, nsl) -AC_SEARCH_LIBS(getaddrinfo, nsl) -AC_SEARCH_LIBS(getdomainname, nsl) - # Note: On Linux, clock_gettime() requires -lrt which implies -lpthreads # Check ommitted for now, gettimeofday() provides reasonable precision # AC_SEARCH_LIBS(clock_gettime, rt) @@ -134,10 +126,6 @@ AM_CONDITIONAL(NEED_GETOPT_LONG, [test "$need_getopt_long" = "yes"]) AC_CHECK_FUNCS([regcomp], [need_regex=no], [need_regex=yes]) AM_CONDITIONAL(NEED_REGEX, [test "$need_regex" = "yes"]) -AC_CHECK_FUNCS([getdomainname]) -AC_CHECK_FUNCS([gethostname]) -AC_CHECK_FUNCS([getaddrinfo]) -AC_CHECK_FUNCS([gethostbyname]) AC_CHECK_FUNCS([sigset]) AC_CHECK_FUNCS([strtoull]) AC_CHECK_FUNCS([uname]) @@ -347,7 +335,10 @@ AC_SUBST(smartmontools_release_time) # Set platform-specific modules and symbols os_libs= os_dltools='curl wget lynx' -os_mailer= +os_mailer=mail +os_hostname="'hostname'" +os_dnsdomainname= +os_nisdomainname="'domainname'" os_darwin=no os_solaris=no os_win32=no @@ -356,11 +347,13 @@ os_win64=no os_man_filter= case "${host}" in *-*-linux*) - os_deps='os_linux.o cciss.o' + os_deps='os_linux.o cciss.o dev_areca.o' + os_dnsdomainname="'dnsdomainname' 'hostname -d'" + os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'" os_man_filter=Linux ;; *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*) - os_deps='os_freebsd.o cciss.o' + os_deps='os_freebsd.o cciss.o dev_areca.o' os_libs='-lcam' os_dltools='curl wget lynx fetch' AC_CHECK_LIB(usb, libusb20_dev_get_device_desc) @@ -390,19 +383,22 @@ case "${host}" in os_man_filter=OpenBSD ;; *-*-cygwin*) - os_deps='os_win32.o' + os_deps='os_win32.o dev_areca.o' + os_hostname="'hostname' 'echo "'"${HOSTNAME?unset}"'"'" + os_dnsdomainname="'dnsdomainname' 'hostname -d' 'echo "'"${USERDNSDOMAIN?unset}"'"'" + os_nisdomainname= os_win32=yes os_man_filter=Cygwin ;; x86_64-*-mingw*) - os_deps='os_win32.o' + os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_win64=yes os_man_filter=Windows ;; *-*-mingw*) - os_deps='os_win32.o' + os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_man_filter=Windows @@ -439,10 +435,11 @@ AC_MSG_RESULT([$os_new_interface]) AC_SUBST([os_deps]) AC_SUBST([os_libs]) AC_SUBST([os_dltools]) +AC_SUBST([os_mailer]) +AC_SUBST([os_hostname]) +AC_SUBST([os_dnsdomainname]) +AC_SUBST([os_nisdomainname]) AC_SUBST([os_man_filter]) -if test -n "$os_mailer"; then - AC_DEFINE_UNQUOTED(DEFAULT_MAILER, "$os_mailer", [Default mailer if "mail" is unavailable]) -fi # Create drivedb.h update branch name from version: 5.41[.X] -> RELEASE_5_41_DRIVEDB DRIVEDB_BRANCH=`echo $VERSION | sed 's,^\([[0-9]]*\.[[0-9]]*\)\..*$,\1,' \ @@ -458,7 +455,6 @@ AM_CONDITIONAL(OS_SOLARIS, [test "$os_solaris" = "yes"]) AM_CONDITIONAL(OS_WIN32, [test "$os_win32" = "yes"]) AM_CONDITIONAL(OS_WIN32_MINGW, [test "$os_win32_mingw" = "yes"]) AM_CONDITIONAL(OS_WIN32_NSIS, [test -n "$MAKENSIS"]) -AM_CONDITIONAL(OS_WIN32_WINDMC, [test -n "$WINDMC" && test "$WINDMC" != "no" && test -n "$WINDRES"]) AM_CONDITIONAL(OS_WIN64, [test "$os_win64" = "yes"]) dnl Add -Wall and -W if using g++ and its not already specified. @@ -548,6 +544,7 @@ case "$host_os" in fi echo "local drive database: `eval eval eval echo $sysconfdir`/smart_drivedb.h" >&AS_MESSAGE_FD echo "smartd config file: `eval eval eval echo $sysconfdir`/smartd.conf${smartd_suffix}" >&AS_MESSAGE_FD + echo "smartd warning script: `eval eval eval echo $sysconfdir`/smartd_warning.sh" >&AS_MESSAGE_FD if test -n "$initddir"; then echo "smartd initd script: `eval eval eval echo $initddir`/smartd${smartd_suffix}" >&AS_MESSAGE_FD elif test -z "$systemdsystemunitdir"; then diff --git a/csmisas.h b/csmisas.h index 087fa78..3e5aa21 100644 --- a/csmisas.h +++ b/csmisas.h @@ -236,7 +236,11 @@ typedef struct _IOCTL_HEADER { #define __u8 unsigned char #define __u16 unsigned short +#ifndef __LP64__ // ILP32 (32-bit), LLP64 (64-bit MSVC, MinGW) #define __u32 unsigned long +#else // LP64 (64-bit Cygwin) +#define __u32 unsigned int +#endif #define __u64 unsigned __int64 #define __i8 char diff --git a/dev_areca.cpp b/dev_areca.cpp new file mode 100644 index 0000000..9ee8be7 --- /dev/null +++ b/dev_areca.cpp @@ -0,0 +1,704 @@ +/* + * dev_areca.cpp + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2012 Hank Wu + * + * 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 . + * + */ + +#include "config.h" +#include "int64.h" + +#include "dev_interface.h" +#include "dev_areca.h" + +const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 3803 2013-03-24 18:52:54Z chrfranke $" + DEV_ARECA_H_CVSID; + +#include "atacmds.h" +#include "scsicmds.h" + +#include + +#if 0 // For debugging areca code +static void dumpdata(unsigned char *block, int len) +{ + int ln = (len / 16) + 1; // total line# + unsigned char c; + int pos = 0; + + printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); + printf("=====================================================================\n"); + + for ( int l = 0; l < ln && len; l++ ) + { + // printf the line# and the HEX data + // if a line data length < 16 then append the space to the tail of line to reach 16 chars + printf("%02X | ", l); + for ( pos = 0; pos < 16 && len; pos++, len-- ) + { + c = block[l*16+pos]; + printf("%02X ", c); + } + + if ( pos < 16 ) + { + for ( int loop = pos; loop < 16; loop++ ) + { + printf(" "); + } + } + + // print ASCII char + for ( int loop = 0; loop < pos; loop++ ) + { + c = block[l*16+loop]; + if ( c >= 0x20 && c <= 0x7F ) + { + printf("%c", c); + } + else + { + printf("."); + } + } + printf("\n"); + } + printf("=====================================================================\n"); +} +#endif + +generic_areca_device::generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca"), + m_disknum(disknum), + m_encnum(encnum) +{ + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} + +generic_areca_device::~generic_areca_device() throw() +{ + +} + +// 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 Windows interface to the Areca "arcmsr" 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 127) in the RAID array +// 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" +int generic_areca_device::arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len) +{ + unsigned int cmds[] = + { + ARCMSR_IOCTL_READ_RQBUFFER, + ARCMSR_IOCTL_WRITE_WQBUFFER, + ARCMSR_IOCTL_CLEAR_RQBUFFER, + ARCMSR_IOCTL_CLEAR_WQBUFFER, + ARCMSR_IOCTL_RETURN_CODE_3F + }; + + int ioctlreturn = 0; + sSRB_BUFFER sBuf; + struct scsi_cmnd_io iop; + int dir = DXFER_TO_DEVICE; + + UINT8 cdb[10]={0}; + UINT8 sense[32]={0}; + + unsigned char *areca_return_packet; + int total = 0; + int expected = -1; + unsigned char return_buff[2048]={0}; + unsigned char *ptr = &return_buff[0]; + + memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); + memset(&iop, 0, sizeof(iop)); + + sBuf.srbioctl.HeaderLength = sizeof(sARCMSR_IO_HDR); + memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); + sBuf.srbioctl.Timeout = 10000; + sBuf.srbioctl.ControlCode = cmds[arcmsr_cmd]; + + if(arcmsr_cmd >= ARCMSR_CMD_TOTAL) + { + return -1; + } + + switch ( arcmsr_cmd ) + { + // command for writing data to driver + case ARCMSR_WRITE_WQBUFFER: + if ( data && data_len ) + { + sBuf.srbioctl.Length = data_len; + memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); + } + // commands for clearing related buffer of driver + case ARCMSR_CLEAR_RQBUFFER: + case ARCMSR_CLEAR_WQBUFFER: + cdb[0] = 0x3B; //SCSI_WRITE_BUF command; + break; + // command for reading data from driver + case ARCMSR_READ_RQBUFFER: + // command for identifying driver + case ARCMSR_RETURN_CODE_3F: + cdb[0] = 0x3C; //SCSI_READ_BUF command; + dir = DXFER_FROM_DEVICE; + break; + default: + // unknown arcmsr commands + return -1; + } + + cdb[1] = 0x01; + cdb[2] = 0xf0; + cdb[5] = cmds[arcmsr_cmd] >> 24; + cdb[6] = cmds[arcmsr_cmd] >> 16; + cdb[7] = cmds[arcmsr_cmd] >> 8; + cdb[8] = cmds[arcmsr_cmd] & 0x0F; + + iop.dxfer_dir = dir; + iop.dxfer_len = sizeof(sBuf); + iop.dxferp = (unsigned char *)&sBuf; + iop.cmnd = cdb; + iop.cmnd_len = sizeof(cdb); + iop.sensep = sense; + iop.max_sense_len = sizeof(sense); + iop.timeout = SCSI_TIMEOUT_DEFAULT; + + while ( 1 ) + { + ioctlreturn = arcmsr_do_scsi_io(&iop); + if(ioctlreturn || iop.scsi_status) + { + break; + } + + if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER ) + { + // if succeeded, just returns the length of outgoing data + return data_len; + } + + if ( sBuf.srbioctl.Length ) + { + memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); + ptr += sBuf.srbioctl.Length; + total += sBuf.srbioctl.Length; + // the returned bytes enough to compute payload length ? + if ( expected < 0 && total >= 5 ) + { + areca_return_packet = (unsigned char *)&return_buff[0]; + if ( areca_return_packet[0] == 0x5E && + areca_return_packet[1] == 0x01 && + areca_return_packet[2] == 0x61 ) + { + // valid header, let's compute the returned payload length, + // we expected the total length is + // payload + 3 bytes header + 2 bytes length + 1 byte checksum + expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; + } + } + + if ( total >= 7 && total >= expected ) + { + //printf("total bytes received = %d, expected length = %d\n", total, expected); + + // ------ Okay! we received enough -------- + break; + } + } + } + + // Deal with the different error cases + if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F ) + { + // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) + return -4; + } + + if ( ioctlreturn ) + { + pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); + return -2; + } + + if ( iop.scsi_status ) + { + pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status); + return -3; + } + + if ( data ) + { + memcpy(data, return_buff, total); + } + + return total; +} + +bool generic_areca_device::arcmsr_probe() +{ + if(!is_open()) + { + open(); + } + + if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0) + { + return false; + } + return true; +} + +int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result) +{ + int expected = 0; + unsigned char return_buff[2048]; + unsigned char cs = 0; + int cs_pos = 0; + + // ----- ADD CHECKSUM ----- + cs_pos = areca_packet_len - 1; + for(int i = 3; i < cs_pos; i++) + { + areca_packet[cs_pos] += areca_packet[i]; + } + + if(!arcmsr_lock()) + { + return -1; + } + expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0); + if (expected==-3) { + return set_err(EIO); + } + expected = arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0); + expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len); + if ( expected > 0 ) + { + expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff)); + } + + if ( expected < 0 ) + { + return -1; + } + + if(!arcmsr_unlock()) + { + return -1; + } + + // ----- VERIFY THE CHECKSUM ----- + cs = 0; + for ( int loop = 3; loop < expected - 1; loop++ ) + { + cs += return_buff[loop]; + } + + if ( return_buff[expected - 1] != cs ) + { + return -1; + } + + memcpy(result, return_buff, expected); + + return expected; +} + +int generic_areca_device::arcmsr_get_controller_type() +{ + int expected = 0; + unsigned char return_buff[2048]; + unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00}; + + memset(return_buff, 0, sizeof(return_buff)); + expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); + if ( expected < 0 ) + { + return -1; + } + + return return_buff[0xc2]; +} + +int generic_areca_device::arcmsr_get_dev_type() +{ + int expected = 0; + unsigned char return_buff[2048]; + int ctlr_type = -1; + int encnum = get_encnum(); + int disknum = get_disknum(); + unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22, + (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00}; + + memset(return_buff, 0, sizeof(return_buff)); + expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); + if ( expected < 0 ) + { + return -1; + } + + ctlr_type = arcmsr_get_controller_type(); + + if( ctlr_type < 0 ) + { + return ctlr_type; + } + + if( ctlr_type == 0x02/* SATA Controllers */ || + (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) ) + { + // SATA device + return 1; + } + + // SAS device + return 0; +} + +bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + // ATA input registers + typedef struct _ATA_INPUT_REGISTERS + { + unsigned char features; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + unsigned char device_head; + unsigned char command; + unsigned char reserved[8]; + unsigned char data[512]; // [in/out] buffer for outgoing/incoming data + } sATA_INPUT_REGISTERS; + + // ATA output registers + // Note: The output registers is re-sorted for areca internal use only + typedef struct _ATA_OUTPUT_REGISTERS + { + unsigned char error; + unsigned char status; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + } sATA_OUTPUT_REGISTERS; + + // Areca packet format for outgoing: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes command length + variant data length, little endian + // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c + // B[6~last-1] : variant bytes payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte + // +--------------------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | + // +--------------------------------------------------------------------------------+ + // + + //Areca packet format for incoming: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes payload length, little endian + // B[5~last-1] : variant bytes returned payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes payload data x bytes cs 1 byte + // +-------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | + // +-------------------------------------------------------------------+ + unsigned char areca_packet[640]; + int areca_packet_len = sizeof(areca_packet); + unsigned char return_buff[2048]; + int expected = 0; + + sATA_INPUT_REGISTERS *ata_cmd; + + // For debugging +#if 0 + memset(sInq, 0, sizeof(sInq)); + scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); + dumpdata((unsigned char *)sInq, sizeof(sInq)); +#endif + memset(areca_packet, 0, areca_packet_len); + + // ----- BEGIN TO SETUP HEADERS ------- + areca_packet[0] = 0x5E; + areca_packet[1] = 0x01; + areca_packet[2] = 0x61; + areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); + areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); + areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command + + // ----- BEGIN TO SETUP PAYLOAD DATA ----- + memcpy(&areca_packet[7], "SmrT", 4); // areca defined password + ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; + + // Set registers + { + const ata_in_regs & r = in.in_regs; + ata_cmd->features = r.features; + ata_cmd->sector_count = r.sector_count; + ata_cmd->sector_number = r.lba_low; + ata_cmd->cylinder_low = r.lba_mid; + ata_cmd->cylinder_high = r.lba_high; + ata_cmd->device_head = r.device; + ata_cmd->command = r.command; + } + bool readdata = false; + if (in.direction == ata_cmd_in::data_in) { + readdata = true; + // the command will read data + areca_packet[6] = 0x13; + } + else if ( in.direction == ata_cmd_in::no_data ) + { + // the commands will return no data + areca_packet[6] = 0x15; + } + else if (in.direction == ata_cmd_in::data_out) + { + // the commands will write data + memcpy(ata_cmd->data, in.buffer, in.size); + areca_packet[6] = 0x14; + } + else { + // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE + return set_err(ENOSYS); + } + + areca_packet[11] = get_disknum() - 1; // disk# + areca_packet[19] = get_encnum() - 1; // enc# + + // ----- BEGIN TO SEND TO ARECA DRIVER ------ + expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); + if ( expected < 0 ) + { + return set_err(EIO); + } + + sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; + if ( ata_out->status ) + { + if ( in.in_regs.command == ATA_IDENTIFY_DEVICE + && !nonempty((unsigned char *)in.buffer, in.size)) + { + return set_err(ENODEV, "No drive on port %d", get_disknum()); + } + } + + // returns with data + if (readdata) + { + memcpy(in.buffer, &return_buff[7], in.size); + } + + // Return register values + { + ata_out_regs & r = out.out_regs; + r.error = ata_out->error; + r.sector_count = ata_out->sector_count; + r.lba_low = ata_out->sector_number; + r.lba_mid = ata_out->cylinder_low; + r.lba_high = ata_out->cylinder_high; + r.status = ata_out->status; + } + return true; +} + +bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop) +{ + // Areca packet format for outgoing: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes command length + variant data length, little endian + // B[5] : 1 bytes areca defined command code + // B[6~last-1] : variant bytes payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte + // +--------------------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | + // +--------------------------------------------------------------------------------+ + // + + //Areca packet format for incoming: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes payload length, little endian + // B[5~last-1] : variant bytes returned payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes payload data x bytes cs 1 byte + // +-------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | + // +-------------------------------------------------------------------+ + unsigned char areca_packet[640]; + int areca_packet_len = sizeof(areca_packet); + unsigned char return_buff[2048]; + int expected = 0; + + if (iop->cmnd_len > 16) { + set_err(EINVAL, "cmnd_len too large"); + return false; + } + + memset(areca_packet, 0, areca_packet_len); + + // ----- BEGIN TO SETUP HEADERS ------- + areca_packet[0] = 0x5E; + areca_packet[1] = 0x01; + areca_packet[2] = 0x61; + areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); + areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); + areca_packet[5] = 0x1c; + + // ----- BEGIN TO SETUP PAYLOAD DATA ----- + areca_packet[6] = 0x16; // scsi pass through + memcpy(&areca_packet[7], "SmrT", 4); // areca defined password + areca_packet[12] = iop->cmnd_len; // cdb length + memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb + areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes) + areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8); + areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16); + areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24); + if(iop->dxfer_dir == DXFER_TO_DEVICE) + { + areca_packet[13] |= 0x01; + memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len); + } + else if (iop->dxfer_dir == DXFER_FROM_DEVICE) + { + } + else if( iop->dxfer_dir == DXFER_NONE) + { + } + else { + // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE + return set_err(ENOSYS); + } + + areca_packet[11] = get_disknum() - 1; // disk# + areca_packet[19] = get_encnum() - 1; // enc# + + // ----- BEGIN TO SEND TO ARECA DRIVER ------ + expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); + if ( expected < 0 ) + { + return set_err(EIO); + } + + int scsi_status = return_buff[5]; + int in_data_len = return_buff[11] | return_buff[12] << 8 | return_buff[13] << 16 | return_buff[14] << 24; + + if (iop->dxfer_dir == DXFER_FROM_DEVICE) + { + memset(iop->dxferp, 0, iop->dxfer_len); // need? + memcpy(iop->dxferp, &return_buff[15], in_data_len); + } + + if(scsi_status == 0xE1 /* Underrun, actual data length < requested data length */) + { + // don't care, just ignore + scsi_status = 0x0; + } + + if(scsi_status != 0x00 && scsi_status != SCSI_STATUS_CHECK_CONDITION) + { + return set_err(EIO); + } + + if(scsi_status == SCSI_STATUS_CHECK_CONDITION) + { + // check condition + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + iop->resp_sense_len = 4; + iop->sensep[0] = return_buff[7]; + iop->sensep[1] = return_buff[8]; + iop->sensep[2] = return_buff[9]; + iop->sensep[3] = return_buff[10]; + } + + return true; +} + +///////////////////////////////////////////////////////////// +areca_ata_device::areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca") +{ + set_encnum(encnum); + set_disknum(disknum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} + +areca_ata_device::~areca_ata_device() throw() +{ + +} + +bool areca_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + //ata_device::supports_multi_sector | // TODO + ata_device::supports_48bit_hi_null, + "Areca") + ) + return false; + + return arcmsr_ata_pass_through(in, out); +} + +///////////////////////////////////////////////////////////// +areca_scsi_device::areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca") +{ + set_encnum(encnum); + set_disknum(disknum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} + +areca_scsi_device::~areca_scsi_device() throw() +{ + +} + +bool areca_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) +{ + return arcmsr_scsi_pass_through(iop); +} + + + + diff --git a/dev_areca.h b/dev_areca.h new file mode 100644 index 0000000..ace29cb --- /dev/null +++ b/dev_areca.h @@ -0,0 +1,184 @@ +/* + * dev_areca.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2012 Hank Wu + * + * 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 . + * + */ + +#ifndef DEV_ARECA_H +#define DEV_ARECA_H + +#define DEV_ARECA_H_CVSID "$Id: dev_areca.h 3763 2013-01-31 22:25:25Z chrfranke $" + +///////////////////////////////////////////////////////////////////////////// +/// Areca RAID support + +/* GENERIC ARECA IO CONTROL CODE*/ +enum _GENERIC_ARCMSR_CMDS +{ +ARCMSR_READ_RQBUFFER = 0, +ARCMSR_WRITE_WQBUFFER, +ARCMSR_CLEAR_RQBUFFER, +ARCMSR_CLEAR_WQBUFFER, +ARCMSR_RETURN_CODE_3F, +ARCMSR_CMD_TOTAL +}; + +#define ARECA_SIG_STR "ARCMSR" + +#if defined(_WIN32) || defined(__CYGWIN__) +#define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004 +#define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008 +#define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C +#define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010 +#define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018 +#elif defined(__linux__) +/*DeviceType*/ +#define ARECA_SATA_RAID 0x90000000 +/*FunctionCode*/ +#define FUNCTION_READ_RQBUFFER 0x0801 +#define FUNCTION_WRITE_WQBUFFER 0x0802 +#define FUNCTION_CLEAR_RQBUFFER 0x0803 +#define FUNCTION_CLEAR_WQBUFFER 0x0804 +#define FUNCTION_RETURN_CODE_3F 0x0806 + +/* ARECA IO CONTROL CODE*/ +#define ARCMSR_IOCTL_READ_RQBUFFER (ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER) +#define ARCMSR_IOCTL_WRITE_WQBUFFER (ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER) +#define ARCMSR_IOCTL_CLEAR_RQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER) +#define ARCMSR_IOCTL_CLEAR_WQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER) +#define ARCMSR_IOCTL_RETURN_CODE_3F (ARECA_SATA_RAID | FUNCTION_RETURN_CODE_3F) +#elif defined(__FreeBSD__) +#include // _IOWR + +/*FunctionCode*/ +#define FUNCTION_READ_RQBUFFER 0x0801 +#define FUNCTION_WRITE_WQBUFFER 0x0802 +#define FUNCTION_CLEAR_RQBUFFER 0x0803 +#define FUNCTION_CLEAR_WQBUFFER 0x0804 +#define FUNCTION_RETURN_CODE_3F 0x0806 + +/* ARECA IO CONTROL CODE*/ +#define ARCMSR_IOCTL_READ_RQBUFFER _IOWR('F', FUNCTION_READ_RQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_WRITE_WQBUFFER _IOWR('F', FUNCTION_WRITE_WQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_CLEAR_RQBUFFER _IOWR('F', FUNCTION_CLEAR_RQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_CLEAR_WQBUFFER _IOWR('F', FUNCTION_CLEAR_WQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_RETURN_CODE_3F _IOWR('F', FUNCTION_RETURN_CODE_3F, sSRB_BUFFER) +#endif + + +// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver +typedef struct _ARCMSR_IO_HDR +{ + unsigned int HeaderLength; + unsigned char Signature[8]; + unsigned int Timeout; + unsigned int ControlCode; + unsigned int ReturnCode; + unsigned int Length; +} sARCMSR_IO_HDR; + +typedef struct _SRB_BUFFER +{ + sARCMSR_IO_HDR srbioctl; + unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware +} sSRB_BUFFER; + +class generic_areca_device : +virtual public smart_device +{ +public: + generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + ~generic_areca_device() throw(); + + ///////////////////////////////////////////////////////////////////// + // OS-dependent functions + virtual bool arcmsr_lock() = 0; + virtual bool arcmsr_unlock() = 0; + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) = 0; + + ///////////////////////////////////////////////////////////////////// + // OS-independent functions + virtual int arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len); + virtual int arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result); + virtual bool arcmsr_probe(); + virtual int arcmsr_get_dev_type(); + virtual int arcmsr_get_controller_type(); + virtual bool arcmsr_scsi_pass_through(scsi_cmnd_io * iop); + virtual bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); + +protected: + generic_areca_device() : smart_device(never_called) + { + } + + void set_disknum(int disknum) + {m_disknum = disknum;} + + void set_encnum(int encnum) + {m_encnum = encnum;} + + int get_disknum() + {return m_disknum;} + + int get_encnum() + {return m_encnum;} + +private: + int m_disknum; ///< Disk number. + int m_encnum; ///< Enclosure number. + }; + +// SATA(ATA) device behind Areca RAID Controller +class areca_ata_device +: public ata_device, + public generic_areca_device +{ +public: + areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + ~areca_ata_device() throw(); + bool arcmsr_lock() { return true; } + bool arcmsr_unlock() { return true; } + int arcmsr_do_scsi_io(struct scsi_cmnd_io * /* iop */) + { + return -1; + } +protected: + areca_ata_device(): smart_device(never_called) + { + } + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); +}; + +// SAS(SCSI) device behind Areca RAID Controller +class areca_scsi_device +: public scsi_device, + public generic_areca_device +{ +public: + areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + ~areca_scsi_device() throw(); + bool arcmsr_lock() { return true; } + bool arcmsr_unlock() { return true; } + int arcmsr_do_scsi_io(struct scsi_cmnd_io * /* iop */) + { + return -1; + } +protected: + areca_scsi_device(): smart_device(never_called) + { + } + virtual bool scsi_pass_through(scsi_cmnd_io * iop); +}; + +#endif diff --git a/dev_interface.cpp b/dev_interface.cpp index 7651244..71bf8bc 100644 --- a/dev_interface.cpp +++ b/dev_interface.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 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 @@ -19,6 +19,7 @@ #include "int64.h" #include "dev_interface.h" #include "dev_tunnelled.h" +#include "atacmds.h" // ATA_SMART_CMD/STATUS #include "utility.h" #include @@ -31,7 +32,7 @@ #include #endif -const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 3554 2012-06-01 20:11:46Z chrfranke $" +const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 3741 2013-01-02 17:06:54Z chrfranke $" DEV_INTERFACE_H_CVSID; ///////////////////////////////////////////////////////////////////////////// @@ -138,10 +139,8 @@ bool ata_device::ata_pass_through(const ata_cmd_in & in) return ata_pass_through(in, dummy); } -bool ata_device::ata_cmd_is_ok(const ata_cmd_in & in, - bool data_out_support /*= false*/, - bool multi_sector_support /*= false*/, - bool ata_48bit_support /*= false*/) +bool ata_device::ata_cmd_is_supported(const ata_cmd_in & in, + unsigned flags, const char * type /* = 0 */) { // Check DATA IN/OUT switch (in.direction) { @@ -167,12 +166,25 @@ bool ata_device::ata_cmd_is_ok(const ata_cmd_in & in, } // Check features - if (in.direction == ata_cmd_in::data_out && !data_out_support) - return set_err(ENOSYS, "DATA OUT ATA commands not supported"); - if (!(in.size == 0 || in.size == 512) && !multi_sector_support) - return set_err(ENOSYS, "Multi-sector ATA commands not supported"); - if (in.in_regs.is_48bit_cmd() && !ata_48bit_support) - return set_err(ENOSYS, "48-bit ATA commands not supported"); + const char * errmsg = 0; + if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out)) + errmsg = "DATA OUT ATA commands not implemented"; + else if ( in.out_needed.is_set() && !(flags & supports_output_regs) + && !( in.in_regs.command == ATA_SMART_CMD + && in.in_regs.features == ATA_SMART_STATUS + && (flags & supports_smart_status))) + errmsg = "Read of ATA output registers not implemented"; + else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector)) + errmsg = "Multi-sector ATA commands not implemented"; + else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit))) + errmsg = "48-bit ATA commands not implemented"; + else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit)) + errmsg = "48-bit ATA commands not fully implemented"; + + if (errmsg) + return set_err(ENOSYS, "%s%s%s%s", errmsg, + (type ? " [" : ""), (type ? type : ""), (type ? "]" : "")); + return true; } @@ -246,7 +258,7 @@ std::string smart_interface::get_valid_dev_types_str() { // default std::string s = - "ata, scsi, sat[,auto][,N][+TYPE], usbcypress[,X], usbjmicron[,x][,N], usbsunplus"; + "ata, scsi, sat[,auto][,N][+TYPE], usbcypress[,X], usbjmicron[,p][,x][,N], usbsunplus"; // append custom std::string s2 = get_valid_custom_dev_types_str(); if (!s2.empty()) { diff --git a/dev_interface.h b/dev_interface.h index 136831c..fe3bb2a 100644 --- a/dev_interface.h +++ b/dev_interface.h @@ -18,7 +18,7 @@ #ifndef DEV_INTERFACE_H #define DEV_INTERFACE_H -#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 3554 2012-06-01 20:11:46Z chrfranke $\n" +#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 3663 2012-10-24 20:35:55Z chrfranke $\n" #include "utility.h" @@ -506,12 +506,35 @@ public: virtual bool ata_identify_is_cached() const; protected: + /// Flags for ata_cmd_is_supported(). + enum { + supports_data_out = 0x01, // PIO DATA OUT + supports_smart_status = 0x02, // read output registers for SMART STATUS only + supports_output_regs = 0x04, // read output registers for all commands + supports_multi_sector = 0x08, // more than one sector (1 DRQ/sector variant) + supports_48bit_hi_null = 0x10, // 48-bit commands with null high bytes only + supports_48bit = 0x20, // all 48-bit commands + }; + /// Check command input parameters. + /// Return false if required features are not implemented. /// Calls set_err(...) accordingly. + bool ata_cmd_is_supported(const ata_cmd_in & in, unsigned flags, + const char * type = 0); + + /// Check command input parameters (old version). + // TODO: Remove if no longer used. bool ata_cmd_is_ok(const ata_cmd_in & in, bool data_out_support = false, bool multi_sector_support = false, - bool ata_48bit_support = false); + bool ata_48bit_support = false) + { + return ata_cmd_is_supported(in, + (data_out_support ? supports_data_out : 0) | + supports_output_regs | + (multi_sector_support ? supports_multi_sector : 0) | + (ata_48bit_support ? supports_48bit : 0)); + } /// Hide/unhide ATA interface. void hide_ata(bool hide = true) diff --git a/do_release b/do_release index 66c6fc7..f88d4a4 100755 --- a/do_release +++ b/do_release @@ -3,27 +3,27 @@ # do a smartmontools release # (C) 2003-11 Bruce Allen , # Guido Guenther -# Christian Franke -# $Id: do_release 3282 2011-03-04 19:26:59Z chrfranke $ +# (C) 2006-13 Christian Franke +# $Id: do_release 3765 2013-02-05 17:17:13Z chrfranke $ # Notes on generating releases: # (1) update NEWS -# (2) update CHANGELOG -- put in release number -# (3) update release number in configure.in +# (2) update ChangeLog -- put in release number +# (3) update release number in configure.ac # (4) to test, run without '--commit' # (5) when satisfied, add option '--commit' set -e -# Smartmontools Signing Key (through 2012) -KEYID=0x2753E77A +# Smartmontools Signing Key (through 2014) +KEYID=0x8F6ED8AA inc_release() { MINOR=`echo $VERSION | cut -d. -f2` MAJOR=`echo $VERSION | cut -d. -f1` PERL_OLD=$MAJOR\\.$MINOR - ((MINOR++)) + ((++MINOR)) NEW_VERSION=$MAJOR.$MINOR PERL_NEW=$MAJOR\\.$MINOR NEW_RELEASE="RELEASE_${NEW_VERSION//\./_}" @@ -63,9 +63,9 @@ fi (cd $WDROOT && svn status) | while read s; do case "`echo $s | tr -s ' '`" in - "M "$DIRPAT/smartmontools/CHANGELOG) echo "$s: OK";; + "M "$DIRPAT/smartmontools/ChangeLog) echo "$s: OK";; "M "$DIRPAT/smartmontools/NEWS) echo "$s: OK";; - "M "$DIRPAT/smartmontools/configure.in) echo "$s: OK";; + "M "$DIRPAT/smartmontools/configure.ac) echo "$s: OK";; *) echo "$s: not allowed"; exit 1;; esac done @@ -74,9 +74,9 @@ if [ $? -ne 0 ]; then fi # Get release number -VERSION=`sed -n 's|^AC_INIT[^,]*, *\([0-9.]*\) *,.*$|\1|p' configure.in` +VERSION=`sed -n 's|^AC_INIT[^,]*, *\([0-9.]*\) *,.*$|\1|p' configure.ac` if [ -z "$VERSION" ]; then - echo "AC_INIT not found in configure.in"; exit 1 + echo "AC_INIT not found in configure.ac"; exit 1 fi VERSIONRC="$VERSION" RELEASE="RELEASE_${VERSION//\./_}" @@ -95,8 +95,8 @@ echo "r$REV: Release $VERSIONRC $RELEASE" # Update timestamp smartmontools_release_date=`date -u +"%Y-%m-%d"` smartmontools_release_time=`date -u +"%T %Z"` -cat configure.in | sed "s|smartmontools_release_date=.*|smartmontools_release_date=${smartmontools_release_date}|" > configure.tmp -cat configure.tmp | sed "s|smartmontools_release_time=.*|smartmontools_release_time=\"${smartmontools_release_time}\"|" > configure.in +cat configure.ac | sed "s|smartmontools_release_date=.*|smartmontools_release_date=${smartmontools_release_date}|" > configure.tmp +cat configure.tmp | sed "s|smartmontools_release_time=.*|smartmontools_release_time=\"${smartmontools_release_time}\"|" > configure.ac rm -f configure.tmp # Review changes @@ -134,8 +134,8 @@ md5sum $TARFILE > $TARFILE.md5 if [ -z "$RC" -a "$DIRPAT" = "trunk" ]; then inc_release if [ "$COMMIT" = "yes" ]; then - perl -p -i.bak -e "s/$PERL_OLD/$PERL_NEW/" configure.in - # svn commit -m "Bump release number to $NEW_VERSION" configure.in + perl -p -i.bak -e "s/$PERL_OLD/$PERL_NEW/" configure.ac + # svn commit -m "Bump release number to $NEW_VERSION" configure.ac fi fi diff --git a/drivedb.h b/drivedb.h index 31cce28..5d60396 100644 --- a/drivedb.h +++ b/drivedb.h @@ -4,7 +4,7 @@ * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2003-11 Philip Williams, Bruce Allen - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 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 @@ -75,7 +75,7 @@ /* const drive_settings builtin_knowndrives[] = { */ - { "$Id: drivedb.h 3559 2012-06-05 18:35:10Z chrfranke $", + { "$Id: drivedb.h 3812 2013-04-20 18:59:19Z chrfranke $", "-", "-", "This is a dummy entry to hold the SVN-Id of drivedb.h", "" @@ -85,72 +85,72 @@ const drive_settings builtin_knowndrives[] = { "-v 3,raw16(avg16),Spin_Up_Time " "-v 4,raw48,Start_Stop_Count " "-v 5,raw16(raw16),Reallocated_Sector_Ct " - "-v 6,raw48,Read_Channel_Margin " - "-v 7,raw48,Seek_Error_Rate " - "-v 8,raw48,Seek_Time_Performance " - "-v 9,raw24(raw8),Power_On_Hours " // smartmontools <= r3527: raw48 - "-v 10,raw48,Spin_Retry_Count " - "-v 11,raw48,Calibration_Retry_Count " + "-v 6,raw48,Read_Channel_Margin " // HDD only + "-v 7,raw48,Seek_Error_Rate " // HDD only + "-v 8,raw48,Seek_Time_Performance " // HDD only + "-v 9,raw24(raw8),Power_On_Hours " + "-v 10,raw48,Spin_Retry_Count " // HDD only + "-v 11,raw48,Calibration_Retry_Count " // HDD only "-v 12,raw48,Power_Cycle_Count " "-v 13,raw48,Read_Soft_Error_Rate " // 14-174 Unknown_Attribute - "-v 175,raw48,Program_Fail_Count_Chip " - "-v 176,raw48,Erase_Fail_Count_Chip " - "-v 177,raw48,Wear_Leveling_Count " - "-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip " - "-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot " - "-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot " + "-v 175,raw48,Program_Fail_Count_Chip " // SSD only + "-v 176,raw48,Erase_Fail_Count_Chip " // SSD only + "-v 177,raw48,Wear_Leveling_Count " // SSD only + "-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip " // SSD only + "-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot " // SSD only + "-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot " // SSD only "-v 181,raw48,Program_Fail_Cnt_Total " - "-v 182,raw48,Erase_Fail_Count_Total " + "-v 182,raw48,Erase_Fail_Count_Total " // SSD only "-v 183,raw48,Runtime_Bad_Block " "-v 184,raw48,End-to-End_Error " // 185-186 Unknown_Attribute "-v 187,raw48,Reported_Uncorrect " "-v 188,raw48,Command_Timeout " - "-v 189,raw48,High_Fly_Writes " + "-v 189,raw48,High_Fly_Writes " // HDD only "-v 190,tempminmax,Airflow_Temperature_Cel " - "-v 191,raw48,G-Sense_Error_Rate " + "-v 191,raw48,G-Sense_Error_Rate " // HDD only "-v 192,raw48,Power-Off_Retract_Count " - "-v 193,raw48,Load_Cycle_Count " + "-v 193,raw48,Load_Cycle_Count " // HDD only "-v 194,tempminmax,Temperature_Celsius " "-v 195,raw48,Hardware_ECC_Recovered " "-v 196,raw16(raw16),Reallocated_Event_Count " "-v 197,raw48,Current_Pending_Sector " "-v 198,raw48,Offline_Uncorrectable " "-v 199,raw48,UDMA_CRC_Error_Count " - "-v 200,raw48,Multi_Zone_Error_Rate " - "-v 201,raw48,Soft_Read_Error_Rate " - "-v 202,raw48,Data_Address_Mark_Errs " + "-v 200,raw48,Multi_Zone_Error_Rate " // HDD only + "-v 201,raw48,Soft_Read_Error_Rate " // HDD only + "-v 202,raw48,Data_Address_Mark_Errs " // HDD only "-v 203,raw48,Run_Out_Cancel " "-v 204,raw48,Soft_ECC_Correction " "-v 205,raw48,Thermal_Asperity_Rate " - "-v 206,raw48,Flying_Height " - "-v 207,raw48,Spin_High_Current " - "-v 208,raw48,Spin_Buzz " - "-v 209,raw48,Offline_Seek_Performnce " + "-v 206,raw48,Flying_Height " // HDD only + "-v 207,raw48,Spin_High_Current " // HDD only + "-v 208,raw48,Spin_Buzz " // HDD only + "-v 209,raw48,Offline_Seek_Performnce " // HDD only // 210-219 Unknown_Attribute - "-v 220,raw48,Disk_Shift " - "-v 221,raw48,G-Sense_Error_Rate " - "-v 222,raw48,Loaded_Hours " - "-v 223,raw48,Load_Retry_Count " - "-v 224,raw48,Load_Friction " - "-v 225,raw48,Load_Cycle_Count " - "-v 226,raw48,Load-in_Time " - "-v 227,raw48,Torq-amp_Count " + "-v 220,raw48,Disk_Shift " // HDD only + "-v 221,raw48,G-Sense_Error_Rate " // HDD only + "-v 222,raw48,Loaded_Hours " // HDD only + "-v 223,raw48,Load_Retry_Count " // HDD only + "-v 224,raw48,Load_Friction " // HDD only + "-v 225,raw48,Load_Cycle_Count " // HDD only + "-v 226,raw48,Load-in_Time " // HDD only + "-v 227,raw48,Torq-amp_Count " // HDD only "-v 228,raw48,Power-off_Retract_Count " // 229 Unknown_Attribute - "-v 230,raw48,Head_Amplitude " + "-v 230,raw48,Head_Amplitude " // HDD only "-v 231,raw48,Temperature_Celsius " "-v 232,raw48,Available_Reservd_Space " - "-v 233,raw48,Media_Wearout_Indicator " + "-v 233,raw48,Media_Wearout_Indicator " // SSD only // 234-239 Unknown_Attribute - "-v 240,raw48,Head_Flying_Hours " + "-v 240,raw48,Head_Flying_Hours " // HDD only "-v 241,raw48,Total_LBAs_Written " "-v 242,raw48,Total_LBAs_Read " // 243-249 Unknown_Attribute "-v 250,raw48,Read_Error_Retry_Rate " // 251-253 Unknown_Attribute - "-v 254,raw48,Free_Fall_Sensor " + "-v 254,raw48,Free_Fall_Sensor " // HDD only */ }, { "Apple SSD SM128", // Samsung? @@ -161,10 +161,9 @@ const drive_settings builtin_knowndrives[] = { "ASUS-PHISON SSD", "", "", "" }, - { "Crucial/Micron RealSSD C300/C400/m4", - "C300-CTFDDA[AC](064|128|256)MAG|" // Marvell 88SS9174 BJP2, tested with C300-CTFDDAC128MAG/0002 - "C400-MTFDDA[ACK](064|128|256|512)MAM|" // Marvel 9176, tested with C400-MTFDDAC256MAM/0002 - "M4-CT(064|128|256|512)M4SSD2", // tested with M4-CT064M4SSD2/0002, M4-CT512M4SSD2/0309 + { "Crucial/Micron RealSSD C300", // Marvell 88SS9174 BJP2 + "C300-CTFDDA[AC](064|128|256)MAG", // tested with C300-CTFDDAC128MAG/0002, + // C300-CTFDDAC064MAG/0006 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " @@ -173,7 +172,7 @@ const drive_settings builtin_knowndrives[] = { "-v 170,raw48,Grown_Failing_Block_Ct " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " - "-v 173,raw48,Wear_Levelling_Count " + "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 181,raw16,Non4k_Aligned_Access " "-v 183,raw48,SATA_Iface_Downshift " @@ -190,18 +189,78 @@ const drive_settings builtin_knowndrives[] = { "-v 202,raw48,Perc_Rated_Life_Used " "-v 206,raw48,Write_Error_Rate" }, + { "Crucial/Micron RealSSD m4/C400", // Marvell 9176, fixed firmware + "C400-MTFDDA[ACK](064|128|256|512)MAM|" + "M4-CT(064|128|256|512)M4SSD[23]", // tested with M4-CT512M4SSD2/0309 + "030[9-Z]|03[1-Z].|0[4-Z]..|[1-Z]....*", // >= "0309" + "", + //"-v 1,raw48,Raw_Read_Error_Rate " + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + "-v 170,raw48,Grown_Failing_Block_Ct " + "-v 171,raw48,Program_Fail_Count " + "-v 172,raw48,Erase_Fail_Count " + "-v 173,raw48,Wear_Leveling_Count " + "-v 174,raw48,Unexpect_Power_Loss_Ct " + "-v 181,raw16,Non4k_Aligned_Access " + "-v 183,raw48,SATA_Iface_Downshift " + //"-v 184,raw48,End-to-End_Error " + //"-v 187,raw48,Reported_Uncorrect " + //"-v 188,raw48,Command_Timeout " + "-v 189,raw48,Factory_Bad_Block_Ct " + //"-v 194,tempminmax,Temperature_Celsius " + //"-v 195,raw48,Hardware_ECC_Recovered " + //"-v 196,raw16(raw16),Reallocated_Event_Count " + //"-v 197,raw48,Current_Pending_Sector " + //"-v 198,raw48,Offline_Uncorrectable " + //"-v 199,raw48,UDMA_CRC_Error_Count " + "-v 202,raw48,Perc_Rated_Life_Used " + "-v 206,raw48,Write_Error_Rate" + }, + { "Crucial/Micron RealSSD m4/C400", // Marvell 9176, buggy or unknown firmware + "C400-MTFDDA[ACK](064|128|256|512)MAM|" // tested with C400-MTFDDAC256MAM/0002 + "M4-CT(064|128|256|512)M4SSD[23]", // tested with M4-CT064M4SSD2/0002, + // M4-CT064M4SSD2/0009, M4-CT256M4SSD3/000F + "", + "This drive may hang after 5184 hours of power-on time:\n" + "http://www.tomshardware.com/news/Crucial-m4-Firmware-BSOD,14544.html\n" + "See the following web pages for firmware updates:\n" + "http://www.crucial.com/support/firmware.aspx\n" + "http://www.micron.com/products/solid-state-storage/client-ssd#software", + "-v 170,raw48,Grown_Failing_Block_Ct " + "-v 171,raw48,Program_Fail_Count " + "-v 172,raw48,Erase_Fail_Count " + "-v 173,raw48,Wear_Leveling_Count " + "-v 174,raw48,Unexpect_Power_Loss_Ct " + "-v 181,raw16,Non4k_Aligned_Access " + "-v 183,raw48,SATA_Iface_Downshift " + "-v 189,raw48,Factory_Bad_Block_Ct " + "-v 202,raw48,Perc_Rated_Life_Used " + "-v 206,raw48,Write_Error_Rate" + }, { "SandForce Driven SSDs", "SandForce 1st Ed\\.|" // Demo Drive, tested with firmware 320A13F0 - "ADATA SSD S(510|599) .?..GB|" // tested with ADATA SSD S510 60GB/320ABBF0, + "ADATA SSD S(396|510|599) .?..GB|" // tested with ADATA SSD S510 60GB/320ABBF0, // ADATA SSD S599 256GB/3.1.0, 64GB/3.4.6 - "Corsair CSSD-F(40|60|80|120|160|240)GBP?2.*|" // Corsair Force, tested with - // Corsair CSSD-F40GB2/1.1 - "Corsair Force (3 SSD|GT)|" // SF-2281, tested with Corsair Force 3 SSD/1.3.2, GT/1.3.3 + "Corsair CSSD-F(40|60|80|115|120|160|240)GBP?2.*|" // Corsair Force, tested with + // Corsair CSSD-F40GB2/1.1, Corsair CSSD-F115GB2-A/2.1a + "Corsair Force (3 SSD|GS|GT)|" // SF-2281, tested with + // Corsair Force 3 SSD/1.3.2, GT/1.3.3, GS/5.03 "FM-25S2S-(60|120|240)GBP2|" // G.SKILL Phoenix Pro, SF-1200, tested with // FM-25S2S-240GBP2/4.2 "FTM(06|12|24|48)CT25H|" // Supertalent TeraDrive CT, tested with // FTM24CT25H/STTMP2P1 - "KINGSTON SH100S3(120|240)G|" // Hyper-X, SF-2281, tested with SH100S3240G/320ABBF0 + "KINGSTON SH10[03]S3(90|120|240|480)G|" // HyperX (3K), SF-2281, tested with + // SH100S3240G/320ABBF0, SH103S3120G/505ABBF0 + "KINGSTON SVP200S3(7A)?(60|90|120|240|480)G|" // V+ 200, SF-2281, tested with + // SVP200S37A480G/502ABBF0, SVP200S390G/332ABBF0 + "KINGSTON SMS450S3(32|64|128)G|" // mSATA, SF-2281, tested with SMS450S3128G/503ABBF0 + "KINGSTON (SV300|SKC100|SE100)S3.*G|" // other SF-2281 + "MKNSSDCR(45|60|90|120|180|240|480)GB(-DX)?|" // Mushkin Chronos (deluxe), SF-2281, + // tested with MKNSSDCR120GB + "Mushkin MKNSSDCL(40|60|80|90|115|120|180|240|480)GB-DX2?|" // Mushkin Callisto deluxe, + // SF-1200/1222, Mushkin MKNSSDCL60GB-DX/361A13F0 "OCZ[ -](AGILITY2([ -]EX)?|COLOSSUS2|ONYX2|VERTEX(2|-LE))( [123]\\..*)?|" // SF-1200, // tested with OCZ-VERTEX2/1.11, OCZ-VERTEX2 3.5/1.11 "OCZ-NOCTI|" // mSATA, SF-2100, tested with OCZ-NOCTI/2.15 @@ -215,15 +274,22 @@ const drive_settings builtin_knowndrives[] = { // OCZ-AGILITY3/2.11, OCZ-SOLID3/2.15, OCZ-VERTEX3 MI/2.15 "OCZ Z-DRIVE R4 [CR]M8[48]|" // PCIe, SF-2282/2582, tested with OCZ Z-DRIVE R4 CM84/2.13 // (Bogus attributes under Linux) + "TALOS2|" // OCZ Talos 2 C/R, SAS (works with -d sat), 2*SF-2282, tested with TALOS2/3.20E "(APOC|DENC|DENEVA|FTNC|GFGC|MANG|MMOC|NIMC|TMSC).*|" // other OCZ SF-1200, // tested with DENCSTE251M11-0120/1.33, DENEVA PCI-E/1.33 "(DENR|DRSAK|EC188|NIMR|PSIR|TRSAK).*|" // other OCZ SF-1500 + "OWC Mercury Electra [36]G SSD|" // tested with + // OWC Mercury Electra 6G SSD/502ABBF0 "OWC Mercury Extreme Pro (RE )?SSD|" // tested with // OWC Mercury Extreme Pro SSD/360A13F0 "Patriot Pyro|" // tested with Patriot Pyro/332ABBF0 - "(TX32|TX31C1|VN0..GCNMK|VN0...GCNMK).*|" // Smart Storage Systems XceedSTOR + "SanDisk SDSSDX(60|120|240|480)GG25|" // SanDisk Extreme, SF-2281, tested with + // SDSSDX240GG25/R201 + "SuperSSpeed S301 [0-9]*GB|" // SF-2281, tested with SuperSSpeed S301 128GB/503 + "(TX32|TX31C1|VN0.?..GCNMK).*|" // Smart Storage Systems XceedSTOR "(TX22D1|TX21B1).*|" // Smart Storage Systems XceedIOPS2 "TX52D1.*|" // Smart Storage Systems Xcel-200 + "TS(64|128|256|512)GSSD320|" // Transcend SSD320, SF-2281, tested with TS128GSSD320 "UGB(88P|99S)GC...H[BF].", // Unigen, tested with // UGB88PGC100HF2/MP Rev2, UGB99SGC100HB3/RC Rev3 "", "", @@ -242,10 +308,11 @@ const drive_settings builtin_knowndrives[] = { "-v 182,raw48,Erase_Fail_Count " "-v 184,raw48,IO_Error_Detect_Code_Ct " //"-v 187,raw48,Reported_Uncorrect " + "-v 189,tempminmax,Airflow_Temperature_Cel " //"-v 194,tempminmax,Temperature_Celsius " "-v 195,raw24/raw32,ECC_Uncorr_Error_Count " //"-v 196,raw16(raw16),Reallocated_Event_Count " - "-v 198,hex48,Uncorrectable_Sector_Ct " + "-v 198,raw24/raw32:210zr54,Uncorrectable_Sector_Ct " // KINGSTON SE100S3100G/510ABBF0 "-v 199,raw48,SATA_CRC_Error_Count " "-v 201,raw24/raw32,Unc_Soft_Read_Err_Rate " "-v 204,raw24/raw32,Soft_ECC_Correct_Rate " @@ -295,9 +362,12 @@ const drive_settings builtin_knowndrives[] = { "-v 212,raw64,SATA_Error_Ct_Handshake " "-v 213,raw64,Indilinx_Internal" }, - { "Indilinx Everest/Martini based SSDs", - "OCZ VERTEX-PLUS|" // tested with OCZ VERTEX-PLUS/3.55 - "OCZ-PETROL", // tested with OCZ-PETROL/3.12 + { "Indilinx Barefoot_2/Everest/Martini based SSDs", + "OCZ VERTEX[ -]PLUS|" // tested with OCZ VERTEX-PLUS/3.55, OCZ VERTEX PLUS/3.55 + "OCZ-VERTEX PLUS R2|" // Barefoot 2, tested with OCZ-VERTEX PLUS R2/1.2 + "OCZ-PETROL|" // Everest 1, tested with OCZ-PETROL/3.12 + "OCZ-AGILITY4|" // Everest 2, tested with OCZ-AGILITY4/1.5.2 + "OCZ-VERTEX4", // Everest 2, tested with OCZ-VERTEX4/1.5 "", "", "" //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 3,raw16(avg16),Spin_Up_Time " @@ -305,9 +375,52 @@ const drive_settings builtin_knowndrives[] = { //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " - //"-v 232,raw48,Available_Reservd_Space " + "-v 232,raw48,Lifetime_Writes " // LBA? //"-v 233,raw48,Media_Wearout_Indicator" }, + { "Indilinx Barefoot 3 based SSDs", + "OCZ-VECTOR", // tested with OCZ-VECTOR/1.03 + "", "", "" + "-v 5,raw48,Runtime_Bad_Block " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + "-v 171,raw48,Avail_OP_Block_Count " + "-v 174,raw48,Pwr_Cycle_Ct_Unplanned " + "-v 187,raw48,Total_Unc_NAND_Reads " + "-v 195,raw48,Total_Prog_Failures " + "-v 196,raw48,Total_Erase_Failures " + "-v 197,raw48,Total_Unc_Read_Failures " + "-v 198,raw48,Host_Reads_GiB " + "-v 199,raw48,Host_Writes_GiB " + "-v 208,raw48,Average_Erase_Count " + "-v 210,raw48,SATA_CRC_Error_Count " + "-v 233,raw48,Remaining_Lifetime_Perc " + "-v 249,raw48,Total_NAND_Prog_Ct_GiB" + }, + { "InnoDisk InnoLite SATADOM D150QV-L SSDs", // tested with InnoLite SATADOM D150QV-L/120319 + "InnoLite SATADOM D150QV-L", + "", "", + //"-v 1,raw48,Raw_Read_Error_Rate " + //"-v 2,raw48,Throughput_Performance " + //"-v 3,raw16(avg16),Spin_Up_Time " + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " + //"-v 7,raw48,Seek_Error_Rate " // from InnoDisk iSMART Linux tool, useless for SSD + //"-v 8,raw48,Seek_Time_Performance " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 10,raw48,Spin_Retry_Count " + //"-v 12,raw48,Power_Cycle_Count " + "-v 168,raw48,SATA_PHY_Error_Count " + "-v 170,raw48,Bad_Block_Count " + "-v 173,raw48,Erase_Count " + "-v 175,raw48,Bad_Cluster_Table_Count " + "-v 192,raw48,Unexpect_Power_Loss_Ct " + //"-v 194,tempminmax,Temperature_Celsius " + //"-v 197,raw48,Current_Pending_Sector " + "-v 229,hex48,Flash_ID " + "-v 235,raw48,Later_Bad_Block " + "-v 236,raw48,Unstable_Power_Count " + "-v 240,raw48,Write_Head" + }, { "Intel X25-E SSDs", "SSDSA2SH(032|064)G1.* INTEL", // G1 = first generation "", "", @@ -372,9 +485,35 @@ const drive_settings builtin_knowndrives[] = { "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes" }, - { "Intel 320 Series SSDs", // tested with INTEL SSDSA2CT040G3/4PC10362 - "INTEL SSDSA[12]C[WT](040|080|120|160|300|600)G3", + { "Intel 313 Series SSDs", // tested with INTEL SSDSA2VP020G3/9CV10379 + "INTEL SSDSA2VP(020|024)G3", + "", "", + //"-v 3,raw16(avg16),Spin_Up_Time " + //"-v 4,raw48,Start_Stop_Count " + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + "-v 170,raw48,Reserve_Block_Count " + "-v 171,raw48,Program_Fail_Count " + "-v 172,raw48,Erase_Fail_Count " + "-v 183,raw48,SATA_Downshift_Count " + //"-v 184,raw48,End-to-End_Error " + //"-v 187,raw48,Reported_Uncorrect " + "-v 192,raw48,Unsafe_Shutdown_Count " + "-v 225,raw48,Host_Writes_32MiB " + "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) + "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage + "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' + //"-v 232,raw48,Available_Reservd_Space " + //"-v 233,raw48,Media_Wearout_Indicator " + "-v 241,raw48,Host_Writes_32MiB " + "-v 242,raw48,Host_Reads_32MiB" + }, + { "Intel 320 Series SSDs", // tested with INTEL SSDSA2CT040G3/4PC10362, + // INTEL SSDSA2CW160G3/4PC10362, INTEL SSDSA2BT040G3/4PC10362 + "INTEL SSDSA[12][BC][WT](040|080|120|160|300|600)G3", "", "", + "-F nologdir " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " @@ -395,9 +534,10 @@ const drive_settings builtin_knowndrives[] = { "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, - { "Intel 710 Series SSDs", // tested with INTEL SSDSA2BZ100G3/6PB10362 + { "Intel 710 Series SSDs", // tested with INTEL SSDSA2BZ[12]00G3/6PB10362 "INTEL SSDSA2BZ(100|200|300)G3", "", "", + "-F nologdir " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " @@ -406,7 +546,7 @@ const drive_settings builtin_knowndrives[] = { "-v 170,raw48,Reserve_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " - //"-v 174,raw48,Unknown_Attribute " + "-v 174,raw48,Unexpect_Power_Loss_Ct " // Missing in 710 specification from September 2011 "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " @@ -434,8 +574,8 @@ const drive_settings builtin_knowndrives[] = { //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator" }, - { "Intel 520 Series SSDs", // tested with INTEL SSDSC2CW120A3/400i - "INTEL SSDSC2CW(060|120|180|240|480)A3", + { "Intel 520 Series SSDs", // tested with INTEL SSDSC2CW120A3/400i, SSDSC2BW480A3F/400i + "INTEL SSDSC2[BC]W(060|120|180|240|480)A3F?", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " @@ -457,8 +597,8 @@ const drive_settings builtin_knowndrives[] = { "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, - { "Intel 330 Series SSDs", // tested with INTEL SSDSC2CT180A3/300i - "INTEL SSDSC2CT(060|120|180)A3", + { "Intel 330 Series SSDs", // tested with INTEL SSDSC2CT180A3/300i, SSDSC2CT240A3/300i + "INTEL SSDSC2CT(060|120|180|240)A3", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " @@ -473,6 +613,37 @@ const drive_settings builtin_knowndrives[] = { "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, + { "Intel DC S3700 Series SSDs", // tested with INTEL SSDSC2BA200G3/5DV10250 + "INTEL SSDSC(1N|2B)A(100|200|400|800)G3", + "", "", + //"-v 3,raw16(avg16),Spin_Up_Time " + //"-v 4,raw48,Start_Stop_Count " + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + "-v 170,raw48,Available_Reservd_Space " + "-v 171,raw48,Program_Fail_Count " + "-v 172,raw48,Erase_Fail_Count " + "-v 174,raw48,Unsafe_Shutdown_Count " + "-v 175,raw48,Power_Loss_Cap_Test " + "-v 183,raw48,SATA_Downshift_Count " + //"-v 184,raw48,End-to-End_Error " + //"-v 187,raw48,Reported_Uncorrect " + "-v 190,tempminmax,Temperature_Case " + "-v 192,raw48,Unsafe_Shutdown_Count " + "-v 194,tempminmax,Temperature_Internal " + //"-v 197,raw48,Current_Pending_Sector " + "-v 199,raw48,CRC_Error_Count " + "-v 225,raw48,Host_Writes_32MiB " + "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) + "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage + "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' + //"-v 232,raw48,Available_Reservd_Space " + //"-v 233,raw48,Media_Wearout_Indicator " + "-v 234,raw48,Thermal_Throttle " + "-v 241,raw48,Host_Writes_32MiB " + "-v 242,raw48,Host_Reads_32MiB" + }, { "Kingston branded X25-V SSDs", // fixed firmware "KINGSTON SSDNow 40GB", "2CV102(J[89A-Z]|[K-Z].)", // >= "2CV102J8" @@ -507,23 +678,24 @@ const drive_settings builtin_knowndrives[] = { "-v 229,hex64:w012345r,Halt_System/Flash_ID " // Halt, Flash[7] "-v 232,hex64:w012345r,Firmware_Version_Info " // "YYMMDD", #Channels, #Banks "-v 233,hex48:w01234,ECC_Fail_Record " // Fail number, Row[3], Channel, Bank - "-v 234,raw24/raw24:w01234,Avg/Max_Erase_Ct " - "-v 235,raw24/raw24:w01z23,Good/Sys_Block_Ct" - // 1.....................................40 chars limit for smartmontools <= r3342 + "-v 234,raw24/raw24:w01234,Avg/Max_Erase_Count " + "-v 235,raw24/raw24:w01z23,Good/Sys_Block_Count" }, { "JMicron based SSDs", // JMicron JMF61x "ADATA S596 Turbo|" // tested with ADATA S596 Turbo 256GB SATA SSD (JMicron JMF616) "APPLE SSD TS.*|" // Toshiba?, tested with APPLE SSD TS064C/CJAA0201 "KINGSTON SNV425S2(64|128)GB|" // SSDNow V Series (2. Generation, JMF618), // tested with KINGSTON SNV425S264GB/C091126a + "KINGSTON SSDNOW 30GB|" // tested with KINGSTON SSDNOW 30GB/AJXA0202 "KINGSTON SS100S2(8|16)G|" // SSDNow S100 Series, tested with KINGSTON SS100S28G/D100309a "KINGSTON SVP?100S2B?(64|96|128|256|512)G|" // SSDNow V100/V+100 Series, - // tested with KINGSTON SVP100S296G/CJR10202, - // KINGSTON SV100S2256G/D110225a + // tested with KINGSTON SVP100S296G/CJR10202, KINGSTON SV100S2256G/D110225a + "KINGSTON SV200S3(64|128|256)G|" // SSDNow V200 Series, tested with KINGSTON SV200S3128G/E120506a "TOSHIBA THNS128GG4BBAA|" // Toshiba / Super Talent UltraDrive DX, // tested with Toshiba 128GB 2.5" SSD (built in MacBooks) "TOSHIBA THNSNC128GMLJ|" // tested with THNSNC128GMLJ/CJTA0202 (built in Toshiba Protege/Dynabook) - "TS(8|16|32|64|128|192|256|512)GSSD25S-(MD?|S)", // Transcend SATA (JMF612), tested with TS256GSSD25S-M/101028 + "TS(8|16|32|64|128|192|256|512)GSSD25S?-(MD?|S)", // Transcend IDE and SATA (JMF612), tested with + // TS256GSSD25S-M/101028, TS32GSSD25-M/20101227 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " @@ -545,12 +717,38 @@ const drive_settings builtin_knowndrives[] = { //"-v 197,raw48,Current_Pending_Sector " "-v 240,raw48,Unknown_Attribute" }, + { "Plextor M3 (Pro) Series SSDs", // Marvell 9174, tested with PLEXTOR PX-128M3/1.01, + // PLEXTOR PX-128M3P/1.04, PLEXTOR PX-256M3/1.05 + // (1.04/5 Firmware self-test log lifetime unit is bogus, possibly 1/256 hours) + "PLEXTOR PX-(64|128|256|512)M3P?", + "", "", + //"-v 1,raw48,Raw_Read_Error_Rate " + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + //"-v 177,raw48,Wear_Leveling_Count " + //"-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip " + //"-v 181,raw48,Program_Fail_Cnt_Total " + //"-v 182,raw48,Erase_Fail_Count_Total " + //"-v 187,raw48,Reported_Uncorrect " + //"-v 192,raw48,Power-Off_Retract_Count " + //"-v 196,raw16(raw16),Reallocated_Event_Count " + //"-v 198,raw48,Offline_Uncorrectable " + //"-v 199,raw48,UDMA_CRC_Error_Count " + //"-v 232,raw48,Available_Reservd_Space " + "" + }, { "Samsung based SSDs", "SAMSUNG SSD PM800 .*GB|" // SAMSUNG PM800 SSDs, tested with SAMSUNG SSD PM800 TH 64GB/VBM25D1Q "SAMSUNG SSD PM810 .*GB|" // SAMSUNG PM810 (470 series) SSDs, tested with SAMSUNG SSD PM810 2.5" 128GB/AXM06D1Q "SAMSUNG 470 Series SSD|" // tested with SAMSUNG 470 Series SSD 64GB/AXM09B1Q - "SAMSUNG SSD 830 Series", // tested with SAMSUNG SSD 830 Series 64GB/CXM03B1Q + "SAMSUNG SSD 830 Series|" // tested with SAMSUNG SSD 830 Series 64GB/CXM03B1Q + "Samsung SSD 840 (PRO )?Series|" // tested with Samsung SSD 840 PRO Series 128GB/DXM04B0Q, + // Samsung SSD 840 Series/DXT06B0Q + "SAMSUNG MZ7WD((120|240)HAFV|480HAGM|960HAGP)-00003", // SM843T Series, tested with + // SAMSUNG MZ7WD120HAFV-00003/DXM85W3Q "", "", + //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " //"-v 175,raw48,Program_Fail_Count_Chip " @@ -562,16 +760,34 @@ const drive_settings builtin_knowndrives[] = { //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 182,raw48,Erase_Fail_Count_Total " //"-v 183,raw48,Runtime_Bad_Block " + //"-v 184,raw48,End-to-End_Error " // SM843T Series "-v 187,raw48,Uncorrectable_Error_Cnt " //"-v 190,tempminmax,Airflow_Temperature_Cel " // seems to be some sort of temperature value for 470 Series? //"-v 194,tempminmax,Temperature_Celsius " - "-v 195,raw48,ECC_Rate " + "-v 195,raw48,ECC_Error_Rate " //"-v 198,raw48,Offline_Uncorrectable " "-v 199,raw48,CRC_Error_Count " "-v 201,raw48,Supercap_Status " "-v 202,raw48,Exception_Mode_Status " - "-v 240,raw48,Unknown_Attribute" // 830 Series - //"-v 241,raw48,Total_LBAs_Written" // 830 Series + "-v 235,raw48,POR_Recovery_Count " // 830/840 Series + //"-v 241,raw48,Total_LBAs_Written" + }, + { "Smart Storage Systems Xcel-10 SSDs", // based on http://www.smartm.com/files/salesLiterature/storage/xcel10.pdf + "SMART A25FD-(32|64|128)GI32N", // tested with SMART A25FD-128GI32N/B9F23D4K + "", + "", // attributes info from http://www.adtron.com/pdf/SMART_Attributes_Xcel-10_810800014_RevB.pdf + "-v 1,raw48,Not_Supported " + "-v 2,raw48,Not_Supported " + //"-v 9,raw24(raw8),Power_On_Hours " + //"-v 12,raw48,Power_Cycle_Count " + "-v 191,raw48,Not_Supported " + //"-v 192,raw48,Power-Off_Retract_Count " + "-v 197,raw48,ECC_Error_Count " + //"-v 198,raw48,Offline_Uncorrectable " + //"-v 199,raw48,UDMA_CRC_Error_Count " + "-v 251,raw48,Min_Spares_Remain_Perc " // percentage of the total number of spare blocks available + "-v 252,raw48,Added_Bad_Flash_Blk_Ct " // number of bad flash blocks + "-v 254,raw48,Total_Erase_Blocks_Ct" // number of times the drive has erased any erase block }, { "Smart Storage Systems XceedSecure2 SSDs", "(SMART|Adtron) ([AIS]25FBS|S35FCS).*", @@ -600,8 +816,7 @@ const drive_settings builtin_knowndrives[] = { "-v 126,hex64,Proprietary_SF1st " "-v 127,hex64,Proprietary_SF2nd " "-v 128,hex64,Proprietary_SF3rd " - "-v 194,raw24/raw32:zvzzzw,Fract_Temperature" - // 1.....................................40 chars limit for smartmontools <= r3342 + "-v 194,raw24/raw32:zvzzzw,Fractional_Temperature" }, { "Smart Storage Systems Adtron A25FB 3xN SSDs", "(SMART|Adtron) A25FB-.*3.N", @@ -611,6 +826,19 @@ const drive_settings builtin_knowndrives[] = { "-v 130,raw48:54321,Minimum_Spares_All_Zs" //"-v 194,tempminmax,Temperature_Celsius" }, + { "STEC Mach2 CompactFlash Cards", // tested with STEC M2P CF 1.0.0/K1385MS + "STEC M2P CF 1.0.0", + "", "", + "-v 100,raw48,Erase_Program_Cycles " + "-v 103,raw48,Remaining_Energy_Storg " + "-v 170,raw48,Reserved_Block_Count " + "-v 171,raw48,Program_Fail_Count " + "-v 172,raw48,Erase_Fail_Count " + "-v 173,raw48,Wear_Leveling_Count " + "-v 174,raw48,Unexpect_Power_Loss_Ct " + "-v 211,raw48,Unknown_Attribute " // ] Missing in specification + "-v 212,raw48,Unknown_Attribute" // ] from September 2012 + }, { "Transcend CompactFlash Cards", // tested with TRANSCEND/20080820, // TS4GCF133/20100709, TS16GCF133/20100709 "TRANSCEND|TS(4|8|16)GCF133", @@ -864,6 +1092,10 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HD(083G|16[12]G|25[12]H|32[12]H|50[12]I|642J|75[23]L|10[23]U)J", "", "", "" }, + { "SAMSUNG SpinPoint F1 EG", // tested with HD103UI/1AA01113 + "SAMSUNG HD(252H|322H|502I|642J|753L|103U)I", + "", "", "" + }, { "SAMSUNG SpinPoint F1 RE", // tested with HE103UJ/1AA01113 "SAMSUNG HE(252H|322H|502I|642J|753L|103U)J", "", "", "" @@ -876,11 +1108,19 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HD(502H|754J|103S)J", "", "", "" }, + { "Seagate Barracuda SpinPoint F3", // tested with ST1000DM005 HD103SJ/1AJ100E5 + "ST[0-9DM]* HD(502H|754J|103S)J", + "", "", "" + }, { "SAMSUNG SpinPoint F3 EG", // tested with HD503HI/1AJ100E4, HD153WI/1AN10002 "SAMSUNG HD(253G|(324|503)H|754J|105S|(153|203)W)I", "", "", "" }, - { "SAMSUNG SpinPoint F4 EG (AFT)",// tested with HD204UI/1AQ10001(buggy|fixed) + { "SAMSUNG SpinPoint F3 RE", // tested with HE103SJ/1AJ30001 + "SAMSUNG HE(502H|754J|103S)J", + "", "", "" + }, + { "SAMSUNG SpinPoint F4 EG (AF)",// tested with HD204UI/1AQ10001(buggy|fixed) "SAMSUNG HD(155|204)UI", "", // 1AQ10001 "Using smartmontools or hdparm with this\n" @@ -933,8 +1173,9 @@ const drive_settings builtin_knowndrives[] = { "May need -F samsung3 enabled; see manual for details.", "" }, - { "SAMSUNG SpinPoint P80", // firmware *-25...34, tested with SP1614C/SW100-25 and -34 - "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]", + { "SAMSUNG SpinPoint P80", // firmware *-25...34, tested with + // SP0401N/TJ100-30, SP1614C/SW100-25 and -34 + "SAMSUNG SP(04[05]1|08[0124]2|12[0145]3|16[0145]4)[CN]", ".*-(2[5-9]|3[0-4])", "", "-v 9,halfminutes -v 198,increasing" @@ -954,8 +1195,8 @@ const drive_settings builtin_knowndrives[] = { "May need -F samsung2 or -F samsung3 enabled; see manual for details.", "" }, - { "SAMSUNG SpinPoint M40/60/80", // tested with HM160JI/AD100-16 - "SAMSUNG HM(0[468]0H|1[026]0J)[CI]", + { "SAMSUNG SpinPoint M40/60/80", // tested with HM120IC/AN100-16, HM160JI/AD100-16 + "SAMSUNG HM(0[468]0H|120I|1[026]0J)[CI]", "", "", "-v 9,halfminutes" @@ -972,7 +1213,7 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HM(250H|320I|[45]00J)I", "", "", "" }, - { "SAMSUNG SpinPoint M7E (AFT)", // tested with HM321HI/2AJ10001, HM641JI/2AJ10001 + { "SAMSUNG SpinPoint M7E (AF)", // tested with HM321HI/2AJ10001, HM641JI/2AJ10001 "SAMSUNG HM(161G|(251|321)H|501I|641J)I", "", "", "" }, @@ -980,14 +1221,23 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HM(162H|252H|322I|502J)X", "", "", "" }, - { "SAMSUNG SpinPoint M8 (AFT)", // tested with HN-M101MBB/2AR10001 - "SAMSUNG HN-M(250|500|750|101)MBB", + { "SAMSUNG SpinPoint M8 (AF)", // tested with HN-M101MBB/2AR10001 + "SAMSUNG HN-M(250|320|500|640|750|101)MBB", + "", "", "" + }, + { "Seagate Momentus SpinPoint M8 (AF)", // tested with + // ST750LM022 HN-M750MBB/2AR10001, ST320LM001 HN-M320MBB/2AR10002 + "ST(250|320|500|640|750|1000)LM0[012][124] HN-M[0-9]*MBB", "", "", "" }, { "SAMSUNG SpinPoint M8U (USB)", // tested with HN-M500XBB/2AR10001 "SAMSUNG HN-M(320|500|750|101)XBB", "", "", "" }, + { "Seagate Samsung SpinPoint M8U (USB)", // tested with ST1000LM025 HN-M101ABB/2AR10001 + "ST(250|320|500|640|750|1000)LM0[012][3459] HN-M[0-9]*ABB", + "", "", "" + }, { "SAMSUNG SpinPoint MP5", // tested with HM250HJ/2AK10001 "SAMSUNG HM(250H|320H|500J|640J)J", "", "", "" @@ -1010,46 +1260,6 @@ const drive_settings builtin_knowndrives[] = { "SAMSUNG HS(122H|2[05]YJ)Z", "", "", "" }, - { "SMART Xcel-10 2.5 SATA SSD", // based on http://www.smartm.com/files/salesLiterature/storage/xcel10.pdf - "SMART A25FD-(32|64|128)GI32N", // tested with SMART A25FD-128GI32N/B9F23D4K - "", - "", // attributes info from http://www.adtron.com/pdf/SMART_Attributes_Xcel-10_810800014_RevB.pdf - "-v 1,raw48,Not_Supported " - "-v 2,raw48,Not_Supported " - //"-v 9,raw24(raw8),Power_On_Hours " - //"-v 12,raw48,Power_Cycle_Count " - "-v 191,raw48,Not_Supported " - //"-v 192,raw48,Power-Off_Retract_Count " - "-v 197,raw48,ECC_Error_Count " - //"-v 198,raw48,Offline_Uncorrectable " - //"-v 199,raw48,UDMA_CRC_Error_Count " - "-v 251,raw48,Min_Spares_Remain_Perc " // percentage of the total number of spare blocks available - "-v 252,raw48,Added_Bad_Flash_Blk_Ct " // number of bad flash blocks - "-v 254,raw48,Total_Erase_Blocks_Ct" // number of times the drive has erased any erase block - }, -/* - // TODO: Make the entries below more specific. - // These entries produce misleading results, because newer - // Samsung disks reuse the version numbers *-NN. - { "", // All Samsung drives with '.*-25' firmware - "SAMSUNG.*", - ".*-25", - "May need -F samsung2 disabled; see manual for details.", - "-v 9,halfminutes -F samsung2" - }, - { "", // All Samsung drives with '.*-26 or later (currently to -39)' firmware - "SAMSUNG.*", - ".*-(2[6789]|3[0-9])", - "", - "-v 9,halfminutes" - }, - { "", // Samsung ALL OTHER DRIVES - "SAMSUNG.*", - "", - "May need -F samsung or -F samsung2 enabled; see manual for details.", - "" - }, -*/ { "Maxtor Fireball 541DX", "Maxtor 2B0(0[468]|1[05]|20)H1", "", @@ -1276,9 +1486,19 @@ const drive_settings builtin_knowndrives[] = { "http://knowledge.seagate.com/articles/en_US/FAQ/207975en", "" }, - { "Seagate Maxtor DiamondMax 23", + { "Seagate Maxtor DiamondMax 23", // new firmware "STM3((160|250)31|(320|500)41|(750|1000)52)8AS?", - "", "", "" + "CC3[D-Z]", + "", "" + }, + { "Seagate Maxtor DiamondMax 23", // unknown firmware + "STM3((160|250)31|(320|500)41|(750|1000)52)8AS?", + "", + "A firmware update for this drive may be available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/213911en", + "" }, { "Maxtor MaXLine Plus II", "Maxtor 7Y250[PM]0", @@ -1426,6 +1646,14 @@ const drive_settings builtin_knowndrives[] = { "(Hitachi )?HT[ES]5450(12|16|25|32|40|50)B9A30[01]", "", "", "" }, + { "Hitachi/HGST Travelstar Z5K500", // tested with HGST HTS545050A7E380/GG2OAC90 + "HGST HT[ES]5450(25|32|50)A7E38[01]", + "", "", "" + }, + { "Hitachi/HGST Travelstar 5K750", // tested with Hitachi HTS547575A9E384/JE4OA60A + "Hitachi HT[ES]5475(50|64|75)A9E38[14]", + "", "", "" + }, { "Hitachi Travelstar 7K60", "(Hitachi )?HTS726060M9AT00", "", "", "" @@ -1456,7 +1684,11 @@ const drive_settings builtin_knowndrives[] = { "", "", "" }, { "Hitachi Travelstar 7K500", - "(Hitachi )?HT[ES]7250(12|16|25|32|50)A9A36[45]", + "(Hitachi )?HT[ES]7250(12|16|25|32|50)A9A36[2-5]", + "", "", "" + }, + { "HGST Travelstar 7K1000", // tested with HGST HTS721010A9E630/JB0OA3B0 + "HGST HTS721010A9E630", "", "", "" }, { "IBM Deskstar 14GXP and 16GP", @@ -1483,6 +1715,10 @@ const drive_settings builtin_knowndrives[] = { "(Hitachi )?HDS5C30(15|20|30)ALA63[02].*", "", "", "" }, + { "Hitachi Deskstar 5K4000", // tested with HDS5C4040ALE630/MPAOA250 + "(Hitachi )?HDS5C40(30|40)ALE63[01].*", + "", "", "" + }, { "Hitachi Deskstar 7K80", "(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320|80PLA380).*", "", "", "" @@ -1527,8 +1763,8 @@ const drive_settings builtin_knowndrives[] = { "(Hitachi )?HDT7210((16|25)SLA380|(32|50|64|75|10)SLA360)", "", "", "" }, - { "Hitachi Deskstar 7K1000.C", - "(Hitachi )?HDS7210((16|25)CLA382|(32|50)CLA362|(64|75|10)CLA332)", + { "Hitachi Deskstar 7K1000.C", // tested with Hitachi HDS721010CLA330/JP4OA3MA + "(Hitachi )?HDS7210((16|25)CLA382|(32|50)CLA362|(64|75|10)CLA33[02])", "", "", "" }, { "Hitachi Deskstar 7K1000.D", // tested with HDS721010DLE630/MS2OA5Q0 @@ -1547,6 +1783,10 @@ const drive_settings builtin_knowndrives[] = { "Hitachi HDS7230((15|20)BLA642|30ALA640)", "", "", "" }, + { "Hitachi/HGST Deskstar 7K4000", // tested with Hitachi HDS724040ALE640/MJAOA250 + "Hitachi HDS724040ALE640", + "", "", "" + }, { "Hitachi Ultrastar A7K1000", // tested with // HUA721010KLA330 44X2459 42C0424IBM/GKAOAB4A "(Hitachi )?HUA7210(50|75|10)KLA330.*", @@ -1561,6 +1801,10 @@ const drive_settings builtin_knowndrives[] = { "Hitachi HUA7230(20|30)ALA640", "", "", "" }, + { "Hitachi Ultrastar 7K4000", // tested with Hitachi HUS724040ALE640/MJAOA3B0 + "Hitachi HUS7240(20|30|40)ALE640", + "", "", "" + }, { "Toshiba 2.5\" HDD (10-20 GB)", "TOSHIBA MK(101[67]GAP|15[67]GAP|20(1[678]GAP|(18|23)GAS))", "", "", "" @@ -1573,6 +1817,10 @@ const drive_settings builtin_knowndrives[] = { "TOSHIBA MK(80(25GAS|26GAX|32GAX|32GSX)|10(31GAS|32GAX)|12(33GAS|34G[AS]X)|2035GSS)", "", "", "" }, + { "Toshiba 2.5\" HDD MK..37GSX", // tested with TOSHIBA MK1637GSX/DL032C + "TOSHIBA MK(12|16)37GSX", + "", "", "" + }, { "Toshiba 2.5\" HDD MK..50GACY", // tested with TOSHIBA MK8050GACY/TF105A "TOSHIBA MK8050GACY", "", "", "" @@ -1581,8 +1829,8 @@ const drive_settings builtin_knowndrives[] = { "TOSHIBA MK(80|12|16|25|32)52GSX", "", "", "" }, - { "Toshiba 2.5\" HDD MK..55GSX", // tested with TOSHIBA MK5055GSX/FG001A - "TOSHIBA MK(12|16|25|32|40|50)55GSX", + { "Toshiba 2.5\" HDD MK..55GSX", // tested with TOSHIBA MK5055GSX/FG001A, MK3255GSXF/FH115B + "TOSHIBA MK(12|16|25|32|40|50)55GSXF?", "", "", "" }, { "Toshiba 2.5\" HDD MK..56GSY", // tested with TOSHIBA MK2556GSYF/LJ001D @@ -1591,16 +1839,20 @@ const drive_settings builtin_knowndrives[] = { "", "-v 9,minutes" }, - { "Toshiba 2.5\" HDD MK..59GSXP (Adv. Format)", + { "Toshiba 2.5\" HDD MK..59GSXP (AF)", "TOSHIBA MK(32|50|64|75)59GSXP?", "", "", "" }, - { "Toshiba 2.5\" HDD MK..59GSM (Adv. Format)", + { "Toshiba 2.5\" HDD MK..59GSM (AF)", "TOSHIBA MK(75|10)59GSM", "", "", "" }, - { "Toshiba 2.5\" HDD MK..65GSX", // tested with TOSHIBA MK5065GSX/GJ003A - "TOSHIBA MK(16|25|32|50|64)65GSX", + { "Toshiba 2.5\" HDD MK..61GSYN", // tested with TOSHIBA MK5061GSYN/MH000A + "TOSHIBA MK(16|25|32|50|64)61GSYN", + "", "", "" + }, + { "Toshiba 2.5\" HDD MK..65GSX", // tested with TOSHIBA MK5065GSX/GJ003A, MK3265GSXN/GH012H + "TOSHIBA MK(16|25|32|50|64)65GSXN?", "", "", "" }, { "Toshiba 2.5\" HDD MK..76GSX", // tested with TOSHIBA MK3276GSX/GS002D @@ -1609,6 +1861,10 @@ const drive_settings builtin_knowndrives[] = { "", "-v 9,minutes" }, + { "Toshiba 2.5\" HDD MQ01ABD...", // tested with TOSHIBA MQ01ABD100/AX001U + "TOSHIBA MQ01ABD(025|032|050|064|075|100)", + "", "", "" + }, { "Toshiba 3.5\" HDD MK.002TSKB", // tested with TOSHIBA MK1002TSKB/MT1A "TOSHIBA MK(10|20)02TSKB", "", "", "" @@ -1673,7 +1929,7 @@ const drive_settings builtin_knowndrives[] = { "ST9(160316|(250|320)310|(500|640)320)AS", "", "", "" }, - { "Seagate Momentus 5400.7 (Adv. Format)", // tested with ST9640322AS/0001BSM2 + { "Seagate Momentus 5400.7 (AF)", // tested with ST9640322AS/0001BSM2 // (device reports 4KiB LPS with 1 sector offset) "ST9(320312|400321|640322|750423)AS", "", "", "" @@ -1702,6 +1958,10 @@ const drive_settings builtin_knowndrives[] = { "ST9((160413|25041[12]|320426|50042[12])AS|(16041[489]|2504[16]4|32042[67]|500426)ASG)", "", "", "" }, + { "Seagate Momentus 7200.5", // tested with ST9750420AS/0001SDM5, ST9750420AS/0002SDM1 + "ST9(50042[34]|64042[012]|75042[02])ASG?", + "", "", "" + }, { "Seagate Momentus XT", // fixed firmware "ST9(2505610|3205620|5005620)AS", "SD2[68]", // http://knowledge.seagate.com/articles/en_US/FAQ/215451en @@ -1728,6 +1988,14 @@ const drive_settings builtin_knowndrives[] = { "http://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac", "" }, + { "Seagate Momentus XT (AF)", // tested with ST750LX003-1AC154/SM12 + "ST750LX003-.*", + "", "", "" + }, + { "Seagate Momentus Thin", // tested with ST320LT007-9ZV142/0004LVM1 + "ST(160|250|320)LT0(07|09|11|14)-.*", + "", "", "" + }, { "Seagate Medalist 1010, 1720, 1721, 2120, 3230 and 4340", // ATA2, with -t permissive "ST3(1010|1720|1721|2120|3230|4340)A", "", "", "" @@ -1792,8 +2060,8 @@ const drive_settings builtin_knowndrives[] = { "ST340015A", "", "", "" }, - { "Seagate Barracuda 7200.7 and 7200.7 Plus", - "ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]082[78]AS|8001[13]AS?|8081[79]AS|60014A|40111AS|40014AS?)", + { "Seagate Barracuda 7200.7 and 7200.7 Plus", // tested with "ST380819AS 39M3701 39M0171 IBM"/3.03 + "ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]082[78]AS|8001[13]AS?|8081[79]AS|60014A|40111AS|40014AS?)( .* IBM)?", "", "", "" }, { "Seagate Barracuda 7200.8", @@ -1844,10 +2112,95 @@ const drive_settings builtin_knowndrives[] = { "http://knowledge.seagate.com/articles/en_US/FAQ/207957en", "" }, - { "Seagate Barracuda 7200.12", // tested with ST3250312AS/JC45, ST31000524AS/JC45, ST3500413AS/JC4B - "ST3(160318|25031[128]|320418|50041[038]|750(518|52[38])|100052[348])AS", + { "Seagate Barracuda 7200.12", // new firmware + "ST3(160318|250318|320418|50041[08]|750528|1000528)AS", + "CC4[9A-Z]", + "", "" + }, + { "Seagate Barracuda 7200.12", // unknown firmware + "ST3(160318|250318|320418|50041[08]|750528|1000528)AS", + "", + "A firmware update for this drive may be available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/213891en", + "" + }, + { "Seagate Barracuda 7200.12", // tested with ST3250312AS/JC45, ST31000524AS/JC45, + // ST3500413AS/JC4B, ST3750525AS/JC4B + "ST3(160318|25031[128]|320418|50041[038]|750(518|52[358])|100052[348])AS", "", "", "" }, + { "Seagate Barracuda XT", // tested with ST32000641AS/CC13, + // ST4000DX000-1C5160/CC42 + "ST(3(2000641|3000651)AS|4000DX000-.*)", + "", "", "" + }, + { "Seagate Barracuda 7200.14 (AF)", // new firmware, tested with + // ST3000DM001-9YN166/CC4H, ST3000DM001-9YN166/CC9E + "ST(1000|1500|2000|2500|3000)DM00[1-3]-.*", + "CC(4[H-Z]|[5-9A-Z]..*)", // >= "CC4H" + "", + "-v 188,raw16 -v 240,msec24hour32" // tested with ST3000DM001-9YN166/CC4H + }, + { "Seagate Barracuda 7200.14 (AF)", // old firmware, tested with + // ST1000DM003-9YN162/CC46 + "ST(1000|1500|2000|2500|3000)DM00[1-3]-.*", + "CC4[679CG]", + "A firmware update for this drive is available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/223651en", + "-v 188,raw16 -v 240,msec24hour32" + }, + { "Seagate Barracuda 7200.14 (AF)", // unknown firmware + "ST(1000|1500|2000|2500|3000)DM00[1-3]-.*", + "", + "A firmware update for this drive may be available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/223651en", + "-v 188,raw16 -v 240,msec24hour32" + }, + { "Seagate Barracuda 7200.14 (AF)", // < 1TB, tested with ST250DM000-1BC141 + "ST(250|320|500|750)DM00[0-3]-.*", + "", "", + "-v 188,raw16 -v 240,msec24hour32" + }, + { "Seagate Desktop HDD.15", // tested with ST4000DM000-1CD168/CC43 + "ST4000DM000-.*", + "", "", + "-v 188,raw16 -v 240,msec24hour32" + }, + { "Seagate Barracuda LP", // new firmware + "ST3(500412|1000520|1500541|2000542)AS", + "CC3[5-9A-Z]", + "", + "" // -F xerrorlba ? + }, + { "Seagate Barracuda LP", // unknown firmware + "ST3(500412|1000520|1500541|2000542)AS", + "", + "A firmware update for this drive may be available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/213915en", + "-F xerrorlba" // tested with ST31000520AS/CC32 + }, + { "Seagate Barracuda Green (AF)", // new firmware + "ST((10|15|20)00DL00[123])-.*", + "CC3[2-9A-Z]", + "", "" + }, + { "Seagate Barracuda Green (AF)", // unknown firmware + "ST((10|15|20)00DL00[123])-.*", + "", + "A firmware update for this drive may be available,\n" + "see the following Seagate web pages:\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" + "http://knowledge.seagate.com/articles/en_US/FAQ/218171en", + "" + }, { "Seagate Barracuda ES", "ST3(250[68]2|32062|40062|50063|75064)0NS", "", "", "" @@ -1856,7 +2209,8 @@ const drive_settings builtin_knowndrives[] = { "ST3(25031|50032|75033|100034)0NS", "SN[01]6|" // http://knowledge.seagate.com/articles/en_US/FAQ/207963en "MA(0[^7]|[^0].)", // http://dellfirmware.seagate.com/dell_firmware/DellFirmwareRequest.jsp - "", "" + "", + "-F xerrorlba" // tested with ST31000340NS/SN06 }, { "Seagate Barracuda ES.2", // buggy firmware (Dell) "ST3(25031|50032|75033|100034)0NS", @@ -1876,23 +2230,6 @@ const drive_settings builtin_knowndrives[] = { "http://knowledge.seagate.com/articles/en_US/FAQ/207963en", "" }, - { "Seagate Barracuda LP", - "ST3(500412|1000520|1500541|2000542)AS", - "", "", "" - }, - { "Seagate Barracuda Green (Adv. Format)", - "ST((10|15|20)00DL00[123])-.*", - "", "", "" - }, - { "Seagate Barracuda XT", // tested with ST32000641AS/CC13, - // ST4000DX000-1C5160/CC42 - "ST(3(2000641|3000651)AS|4000DX000-.*)", - "", "", "" - }, - { "Seagate Barracuda (SATA 3Gb/s, 4K Sectors)", // tested with ST250DM000-1BC141 - "ST(3000|2000|1500|750|500|320|250)DM00[012]-.*", - "", "", "" - }, { "Seagate Constellation (SATA)", // tested with ST9500530NS/SN03 "ST9(160511|500530)NS", "", "", "" @@ -1953,6 +2290,10 @@ const drive_settings builtin_knowndrives[] = { "ST3(750640SCE|((80|160)215|(250|320|400)820|500830|750840)[AS]CE)", "", "", "" }, + { "Seagate LD25.2", // tested with ST940210AS/3.ALC + "ST9(40|80)210AS?", + "", "", "" + }, { "Seagate ST1.2 CompactFlash", // tested with ST68022CF/3.01 "ST6[468]022CF", "", "", "" @@ -2074,7 +2415,7 @@ const drive_settings builtin_knowndrives[] = { "WDC WD((25|32|50|75)02A|(75|10)02F)BYS-.*", "", "", "" }, - { "Western Digital RE4 Serial ATA", + { "Western Digital RE4", // tested with WDC WD2003FYYS-18W0B0/01.01D02 "WDC WD((((25|50)03A|1003F)BYX)|((15|20)03FYYS))-.*", "", "", "" }, @@ -2082,25 +2423,37 @@ const drive_settings builtin_knowndrives[] = { "WDC WD2002FYPS-.*", "", "", "" }, + { "Western Digital RE4 (SATA 6Gb/s)", // tested with WDC WD2000FYYZ-01UL1B0/01.01K01 + "WDC WD(20|30|40)00FYYZ-.*", + "", "", "" + }, { "Western Digital Caviar Green", "WDC WD((50|64|75)00AA(C|V)S|(50|64|75)00AADS|10EA(C|V)S|(10|15|20)EADS)-.*", - "", "", "" + "", + "", + "-F xerrorlba" // tested with WDC WD7500AADS-00M2B0/01.00A01 }, - { "Western Digital Caviar Green (Adv. Format)", + { "Western Digital Caviar Green (AF)", "WDC WD(((64|75|80)00AA|(10|15|20)EA|(25|30)EZ)R|20EAC)S-.*", "", "", "" }, - { "Western Digital Caviar Green (Adv. Format)", // SATA 6Gb/s variants - // tested with WDC WD30EZRX-00MMMB0/80.00A80 - "WDC WD(7500AA|(10|15|20)EA|(25|30)EZ)RX-.*", + { "Western Digital Caviar Green (AF, SATA 6Gb/s)", // tested with + // WDC WD10EZRX-00A8LB0/01.01A01, WDC WD20EZRX-00DC0B0/80.00A80, + // WDC WD30EZRX-00MMMB0/80.00A80 + "WDC WD(7500AA|(10|15|20)EA|(10|20|25|30)EZ)RX-.*", "", "", "" }, { "Western Digital Caviar Black", "WDC WD((500|640|750)1AAL|1001FA[EL]|2001FAS)S-.*", "", "", "" }, - { "Western Digital Caviar Black", // SATA 3.0 variants - "WDC WD(5002AAL|(64|75)02AAE|(10|15|20)02FAE)X-.*", + { "Western Digital Caviar Black", // SATA 6 Gb/s variants, tested with + // WDC WD4001FAEX-00MJRA0/01.01L01 + "WDC WD(5002AAL|(64|75)02AAE|((10|15|20)02|4001)FAE)X-.*", + "", "", "" + }, + { "Western Digital Caviar Black (AF)", // tested with WDC WD5003AZEX-00RKKA0/80.00A80 + "WDC WD(5003AZE)X-.*", "", "", "" }, { "Western Digital AV ATA", // tested with WDC WD3200AVJB-63J5A0/01.03E01 @@ -2115,7 +2468,7 @@ const drive_settings builtin_knowndrives[] = { "WDC WD((16|25|32|50|64|75)00AV[CDV]S|(10|15|20)EV[CDV]S)-.*", "", "", "" }, - { "Western Digital AV-GP (Adv. Format)", // tested with WDC WD10EURS-630AB1/80.00A80, WDC WD10EUCX-63YZ1Y0/51.0AB52 + { "Western Digital AV-GP (AF)", // tested with WDC WD10EURS-630AB1/80.00A80, WDC WD10EUCX-63YZ1Y0/51.0AB52 "WDC WD(7500AURS|10EU[CR]X|(10|15|20|25|30)EURS)-.*", "", "", "" }, @@ -2135,6 +2488,10 @@ const drive_settings builtin_knowndrives[] = { "WDC WD(((800H|(1500|3000)[BH]|1600H|3000G)LFS)|((1500|3000|4500|6000)[BH]LHX))-.*", "", "", "" }, + { "Western Digital VelociRaptor (AF)", // tested with WDC WD1000DHTZ-04N21V0/04.06A00 + "WDC WD(2500H|5000H|1000D)HTZ-.*", + "", "", "" + }, { "Western Digital Scorpio EIDE", "WDC WD(4|6|8|10|12|16)00(UE|VE)-.*", "", "", "" @@ -2151,7 +2508,7 @@ const drive_settings builtin_knowndrives[] = { "WDC WD((4|6|8|10|12|16|25)00BEVS|(8|12|16|25|32|40|50|64)00BEVT|7500KEVT|10TEVT)-.*", "", "", "" }, - { "Western Digital Scorpio Blue Serial ATA (Adv. Format)", // tested with + { "Western Digital Scorpio Blue Serial ATA (AF)", // tested with // WDC WD10JPVT-00A1YT0/01.01A01 "WDC WD((16|25|32|50|64|75)00BPVT|10[JT]PVT)-.*", "", "", "" @@ -2160,22 +2517,22 @@ const drive_settings builtin_knowndrives[] = { "WDC WD(8|12|16|25|32|50)00B[EJ]KT-.*", "", "", "" }, - { "Western Digital Scorpio Black (Adv. Format)", + { "Western Digital Scorpio Black (AF)", "WDC WD(50|75)00BPKT-.*", "", "", "" }, - { "Western Digital My Passport Essential (USB)", - "WDC WD(25|32|40|50)00BMVU-.*", + { "Western Digital Red (AF)", // tested with WDC WD10EFRX-68JCSN0/01.01A01 + "WDC WD(10|20|30)EFRX-.*", "", "", "" }, - { "Western Digital My Passport Essential SE (USB, Adv. Format)", // tested with - // WDC WD10TMVW-11ZSMS5/01.01A01 - "WDC WD(7500K|10T)MV[VW]-.*", + { "Western Digital My Passport (USB)", // tested with WDC WD5000BMVW-11AMCS0/01.01A01 + "WDC WD(25|32|40|50)00BMV[UVW]-.*", // *W-* = USB 3.0 "", "", "" }, - { "Western Digital My Passport (USB)", // tested with - // WDC WD5000BMVW-11AMCS0/01.01A01 - "WDC WD(3200BMVV|5000BMVW)-.*", + { "Western Digital My Passport (USB, AF)", // tested with + // WDC WD5000KMVV-11TK7S1/01.01A01, WDC WD10TMVW-11ZSMS5/01.01A01, + // WDC WD10JMVW-11S5XS1/01.01A01, WDC WD20NMVW-11W68S0/01.01A01 + "WDC WD(5000[LK]|7500K|10[JT]|20N)MV[VW]-.*", // *W-* = USB 3.0 "", "", "" }, { "Quantum Bigfoot", // tested with TS10.0A/A21.0G00, TS12.7A/A21.0F00 @@ -2198,8 +2555,8 @@ const drive_settings builtin_knowndrives[] = { "QUANTUM FIREBALL CR(4.3|6.4|8.4|13.0)A", "", "", "" }, - { "Quantum Fireball EX", - "QUANTUM FIREBALL EX(3.2|6.4)A", + { "Quantum Fireball EX", // tested with QUANTUM FIREBALL EX10.2A/A0A.0D00 + "QUANTUM FIREBALL EX(3\\.2|6\\.4|10\\.2)A", "", "", "" }, { "Quantum Fireball ST", @@ -2280,12 +2637,18 @@ const drive_settings builtin_knowndrives[] = { "-d sat" }, // Toshiba - { "USB: Toshiba Canvio 500GB; ", + { "USB: Toshiba Canvio 500GB; SunPlus", "0x0480:0xa004", "", "", "-d usbsunplus" }, + { "USB: Toshiba Canvio Basics; ", + "0x0480:0xa006", + "", // 0x0001 + "", + "-d sat" + }, // Cypress { "USB: ; Cypress CY7C68300A (AT2)", "0x04b4:0x6830", @@ -2299,6 +2662,13 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbcypress" }, + // Fujitsu + { "USB: Fujitsu/Zalman ZM-VE300; ", // USB 3.0 + "0x04c5:0x2028", + "", // 0x0001 + "", + "-d sat" + }, // Myson Century { "USB: ; Myson Century CS8818", "0x04cf:0x8818", @@ -2349,6 +2719,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d sat" }, + { "USB: Samsung M3 Portable USB 3.0; ", // 1TB + "0x04e8:0x61b6", + "", // 0x0e00 + "", + "-d sat" + }, // Sunplus { "USB: ; SunPlus", "0x04fc:0x0c05", @@ -2466,6 +2842,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d sat" }, + { "USB: LaCie Rugged Mini USB 3.0; ", + "0x059f:0x1051", + "", // 0x0000 + "", + "-d sat" + }, // In-System Design { "USB: ; In-System/Cypress ISD-300A1", "0x05ab:0x0060", @@ -2504,7 +2886,14 @@ const drive_settings builtin_knowndrives[] = { "0x067b:0x3507", "", // 0x0001 "", - "" // unsupported + "-d usbjmicron,p" + }, + // Imation + { "USB: Imation ; ", // Imation Odyssey external USB dock + "0x0718:0x1000", + "", // 0x5104 + "", + "-d sat" }, // Freecom { "USB: Freecom Mobile Drive XXS; JMicron", @@ -2537,19 +2926,6 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbjmicron" }, - // Toshiba - { "USB: Toshiba PX1270E-1G16; Sunplus", - "0x0930:0x0b03", - "", - "", - "-d usbsunplus" - }, - { "USB: Toshiba STOR.E; ", - "0x0930:0x0b1b", - "", // 0x0001 - "", - "-d sat" - }, // Oxford Semiconductor, Ltd { "USB: ; Oxford", "0x0928:0x0000", @@ -2563,7 +2939,19 @@ const drive_settings builtin_knowndrives[] = { "", "" // unsupported }, - // Toshiba Corp. + { "USB: ; Oxford", // Zalman ZM-VE200 + "0x0928:0x0010", + "", // 0x0304 + "", + "-d sat" + }, + // Toshiba + { "USB: Toshiba PX1270E-1G16; Sunplus", + "0x0930:0x0b03", + "", + "", + "-d usbsunplus" + }, { "USB: Toshiba PX1396E-3T01; Sunplus", // similar to Dura Micro 501 "0x0930:0x0b09", "", @@ -2576,6 +2964,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbsunplus" }, + { "USB: Toshiba Stor.E; ", + "0x0930:0x0b1[9ab]", + "", // 0x0001 + "", + "-d sat" + }, // Lumberg, Inc. { "USB: Toshiba Stor.E; Sunplus", "0x0939:0x0b16", @@ -2584,6 +2978,12 @@ const drive_settings builtin_knowndrives[] = { "-d usbsunplus" }, // Seagate + { "USB: Seagate External Drive; Cypress", + "0x0bc2:0x0503", + "", // 0x0240 + "", + "-d usbcypress" + }, { "USB: Seagate FreeAgent Go; ", "0x0bc2:0x2(000|100|101)", "", @@ -2656,6 +3056,18 @@ const drive_settings builtin_knowndrives[] = { "", "-d sat" }, + { "USB: Seagate Backup Plus USB 3.0; ", // 1TB + "0x0bc2:0xa013", + "", // 0x0100 + "", + "-d sat" + }, + { "USB: Seagate Backup Plus Desktop USB 3.0; ", // 3TB, 8 LBA/1 PBA offset + "0x0bc2:0xa0a4", + "", + "", + "-d sat" + }, // Dura Micro { "USB: Dura Micro; Cypress", "0x0c0b:0xb001", @@ -2777,7 +3189,7 @@ const drive_settings builtin_knowndrives[] = { "-d sat" }, { "USB: WD My Passport USB 3.0; ", - "0x1058:0x0748", + "0x1058:0x07[4a]8", "", "", "-d sat" @@ -2848,6 +3260,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d sat" }, + { "USB: WD My Book Studio II; ", // 2x1TB + "0x1058:0x1105", + "", + "", + "-d sat" + }, { "USB: WD My Book Essential; ", "0x1058:0x1110", "", // 0x1030 @@ -2874,6 +3292,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbcypress" }, + { "USB: A-DATA DashDrive; Cypress", + "0x125f:0xa94a", + "", + "", + "-d usbcypress" + }, // Initio { "USB: ; Initio 316000", "0x13fd:0x0540", @@ -2887,6 +3311,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d sat" }, + { "USB: ; Initio", // USB->SATA+PATA, Chieftec CEB-25I + "0x13fd:0x1040", + "", // 0x0106 + "", + "" // unsupported + }, { "USB: ; Initio 6Y120L0", // CoolerMaster XCraft RX-3HU "0x13fd:0x1150", "", @@ -2993,20 +3423,22 @@ const drive_settings builtin_knowndrives[] = { }, // ASMedia { "USB: ; ASMedia ASM1051", - "0x174c:0x5106", + "0x174c:0x5106", // 0x174c:0x55aa after firmware update "", "", "-d sat" }, - { "USB: ; ASMedia USB 3.0", // BYTECC T-200U3 + { "USB: ; ASMedia USB 3.0", // MEDION HDDrive-n-GO, LaCie Rikiki USB 3.0, + // Silicon Power Armor A80 (ticket #237) + // reported as unsupported: BYTECC T-200U3, Kingwin USB 3.0 docking station "0x174c:0x55aa", + "", // 0x0100 "", - "", - "" // unsupported + "-d sat" }, // LucidPort - { "USB: RaidSonic ICY BOX IB-110StU3-B; LucidPORT USB300", - "0x1759:0x500[02]", + { "USB: ; LucidPORT USB300", // RaidSonic ICY BOX IB-110StU3-B, Sharkoon SATA QuickPort H3 + "0x1759:0x500[02]", // 0x5000: USB 2.0, 0x5002: USB 3.0 "", "", "-d sat" @@ -3042,6 +3474,12 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbjmicron" }, + { "USB: Verbatim Store'n'Go; JMicron", // USB->SATA + "0x18a5:0x022b", + "", // 0x0100 + "", + "-d usbjmicron" + }, // Silicon Image { "USB: Vantec NST-400MX-SR; Silicon Image 5744", "0x1a4a:0x1670", @@ -3056,13 +3494,20 @@ const drive_settings builtin_knowndrives[] = { "", "-d usbsunplus" }, - // Unknown: 0x1f75 - { "USB: Sharkoon SATA QuickDeck Pro; ", // USB 2.0/3.0 + // Innostor + { "USB: ; Innostor IS888", // Sharkoon SATA QuickDeck Pro USB 3.0 "0x1f75:0x0888", "", // 0x0034 "", "" // unsupported }, + // Power Quotient International + { "USB: PQI H560; ", + "0x3538:0x0902", + "", // 0x0000 + "", + "-d sat" + }, // Hitachi/SimpleTech { "USB: Hitachi Touro Desk; JMicron", // 3TB "0x4971:0x1011", diff --git a/examplescripts/README b/examplescripts/README index 0d855ad..dfeccfe 100644 --- a/examplescripts/README +++ b/examplescripts/README @@ -1,6 +1,6 @@ # Home page: http://smartmontools.sourceforge.net # -# $Id: README,v 1.7 2008/03/04 22:09:47 ballen4705 Exp $ +# $Id: README 3728 2012-12-13 17:57:50Z chrfranke $ # # Copyright (C) 2003-8 Bruce Allen # @@ -10,8 +10,8 @@ # 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. +# example COPYING); if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, 02110-1301 USA. # # This code was originally developed as a Senior Thesis by Michael Cornwell # at the Concurrent Systems Laboratory (now part of the Storage Systems diff --git a/getopt/getopt.c b/getopt/getopt.c index 2aad1f2..b61ab8e 100644 --- a/getopt/getopt.c +++ b/getopt/getopt.c @@ -18,9 +18,9 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ + /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO diff --git a/getopt/getopt.h b/getopt/getopt.h index 4283c35..c4f1ea4 100644 --- a/getopt/getopt.h +++ b/getopt/getopt.h @@ -14,8 +14,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ #ifndef _GETOPT_H diff --git a/int64.h b/int64.h index b346bd9..bc328ab 100644 --- a/int64.h +++ b/int64.h @@ -12,15 +12,15 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef INT64_H_ #define INT64_H_ -#define INT64_H_CVSID "$Id: int64.h 3475 2011-11-10 21:43:40Z chrfranke $" +#define INT64_H_CVSID "$Id: int64.h 3727 2012-12-13 17:23:06Z samm2 $" // 64 bit integer typedefs and format strings diff --git a/knowndrives.cpp b/knowndrives.cpp index 422b865..25d3bab 100644 --- a/knowndrives.cpp +++ b/knowndrives.cpp @@ -5,7 +5,7 @@ * Address of support mailing list: smartmontools-support@lists.sourceforge.net * * Copyright (C) 2003-11 Philip Williams, Bruce Allen - * Copyright (C) 2008-11 Christian Franke + * Copyright (C) 2008-12 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 @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * */ @@ -34,7 +33,7 @@ #include -const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3447 2011-10-14 20:32:00Z chrfranke $" +const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $" KNOWNDRIVES_H_CVSID; #define MODEL_STRING_LENGTH 40 @@ -124,14 +123,16 @@ void drive_database::push_back(const drive_settings & src) const char * drive_database::copy_string(const char * src) { - char * dest = new char[strlen(src)+1]; + size_t len = strlen(src); + char * dest = new char[len+1]; + memcpy(dest, src, len+1); try { m_custom_strings.push_back(dest); } catch (...) { delete [] dest; throw; } - return strcpy(dest, src); + return dest; } @@ -208,7 +209,7 @@ static const drive_settings * lookup_drive(const char * model, const char * firm // Parse drive or USB options in preset string, return false on error. static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, - unsigned char * fix_firmwarebug, std::string * type) + firmwarebug_defs * firmwarebugs, std::string * type) { for (int i = 0; ; ) { i += strspn(presets+i, " \t"); @@ -222,19 +223,13 @@ static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE)) return false; } - else if (opt == 'F' && fix_firmwarebug) { - unsigned char fix; - if (!strcmp(arg, "samsung")) - fix = FIX_SAMSUNG; - else if (!strcmp(arg, "samsung2")) - fix = FIX_SAMSUNG2; - else if (!strcmp(arg, "samsung3")) - fix = FIX_SAMSUNG3; - else + else if (opt == 'F' && firmwarebugs) { + firmwarebug_defs bug; + if (!parse_firmwarebug_def(arg, bug)) return false; - // Set only if not set by user - if (*fix_firmwarebug == FIX_NOTSPECIFIED) - *fix_firmwarebug = fix; + // Don't set if user specified '-F none'. + if (!firmwarebugs->is_set(BUG_NONE)) + firmwarebugs->set(bug); } else if (opt == 'd' && type) { // TODO: Check valid types @@ -251,9 +246,9 @@ static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, // Parse '-v' and '-F' options in preset string, return false on error. static inline bool parse_presets(const char * presets, ata_vendor_attr_defs & defs, - unsigned char & fix_firmwarebug) + firmwarebug_defs & firmwarebugs) { - return parse_db_presets(presets, &defs, &fix_firmwarebug, 0); + return parse_db_presets(presets, &defs, &firmwarebugs, 0); } // Parse '-d' option in preset string, return false on error. @@ -365,11 +360,11 @@ static int showonepreset(const drive_settings * dbentry) pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily); // if there are any presets, then show them - unsigned char fix_firmwarebug = 0; + firmwarebug_defs firmwarebugs; bool first_preset = true; if (*dbentry->presets) { ata_vendor_attr_defs defs; - if (!parse_presets(dbentry->presets, defs, fix_firmwarebug)) { + if (!parse_presets(dbentry->presets, defs, firmwarebugs)) { pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); errcnt++; } @@ -393,18 +388,26 @@ static int showonepreset(const drive_settings * dbentry) pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); // describe firmwarefix - if (fix_firmwarebug) { + for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) { + if (!firmwarebugs.is_set((firmwarebug_t)b)) + continue; const char * fixdesc; - switch (fix_firmwarebug) { - case FIX_SAMSUNG: + switch ((firmwarebug_t)b) { + case BUG_NOLOGDIR: + fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)"; + break; + case BUG_SAMSUNG: fixdesc = "Fixes byte order in some SMART data (same as -F samsung)"; break; - case FIX_SAMSUNG2: + case BUG_SAMSUNG2: fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)"; break; - case FIX_SAMSUNG3: + case BUG_SAMSUNG3: fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)"; break; + case BUG_XERRORLBA: + fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)"; + break; default: fixdesc = "UNKNOWN"; errcnt++; break; @@ -525,12 +528,12 @@ void show_presets(const ata_identify_device * drive) } // Searches drive database and sets preset vendor attribute -// options in defs and fix_firmwarebug. +// options in defs and firmwarebugs. // Values that have already been set will not be changed. // Returns pointer to database entry or nullptr if none found const drive_settings * lookup_drive_apply_presets( const ata_identify_device * drive, ata_vendor_attr_defs & defs, - unsigned char & fix_firmwarebug) + firmwarebug_defs & firmwarebugs) { // get the drive's model/firmware strings char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; @@ -544,7 +547,7 @@ const drive_settings * lookup_drive_apply_presets( if (*dbentry->presets) { // Apply presets - if (!parse_presets(dbentry->presets, defs, fix_firmwarebug)) + if (!parse_presets(dbentry->presets, defs, firmwarebugs)) pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); } return dbentry; @@ -765,7 +768,7 @@ static bool parse_drive_database(parse_ptr src, drive_database & db, const char case 4: if (!token.value.empty()) { if (!is_usb_modelfamily(values[0].c_str())) { - ata_vendor_attr_defs defs; unsigned char fix = 0; + ata_vendor_attr_defs defs; firmwarebug_defs fix; if (!parse_presets(token.value.c_str(), defs, fix)) { pout("%s(%d): Syntax error in preset option string\n", path, token.line); ok = false; diff --git a/knowndrives.h b/knowndrives.h index 4578849..7d2a8cc 100644 --- a/knowndrives.h +++ b/knowndrives.h @@ -5,7 +5,7 @@ * Address of support mailing list: smartmontools-support@lists.sourceforge.net * * Copyright (C) 2003-11 Philip Williams, Bruce Allen - * Copyright (C) 2008-11 Christian Franke + * Copyright (C) 2008-12 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 @@ -13,15 +13,14 @@ * 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. + * (for example COPYING); If not, see . * */ #ifndef KNOWNDRIVES_H_ #define KNOWNDRIVES_H_ -#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 3288 2011-03-09 18:40:36Z chrfranke $\n" +#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 3597 2012-09-04 21:10:37Z chrfranke $\n" // Structure to store drive database entries, see drivedb.h for a description. struct drive_settings { @@ -56,12 +55,12 @@ int showallpresets(); int showmatchingpresets(const char *model, const char *firmware); // Searches drive database and sets preset vendor attribute -// options in defs and fix_firmwarebug. +// options in defs and firmwarebugs. // Values that have already been set will not be changed. // Returns pointer to database entry or nullptr if none found. const drive_settings * lookup_drive_apply_presets( const ata_identify_device * drive, ata_vendor_attr_defs & defs, - unsigned char & fix_firmwarebug); + firmwarebug_defs & firmwarebugs); // Get path for additional database file const char * get_drivedb_path_add(); diff --git a/megaraid.h b/megaraid.h index b1f398d..9c2bf6e 100644 --- a/megaraid.h +++ b/megaraid.h @@ -8,14 +8,14 @@ int megaraid_io_interface(int device, int target, struct scsi_cmnd_io *, int); #define u64 uint64_t /*====================================================== - * PERC2/3/4 Passthrough SCSI Command Interface - * - * Contents from: - * drivers/scsi/megaraid/megaraid_ioctl.h - * drivers/scsi/megaraid/mbox_defs.h - *======================================================*/ -#define MEGAIOC_MAGIC 'm' -#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t) +* PERC2/3/4 Passthrough SCSI Command Interface +* +* Contents from: +* drivers/scsi/megaraid/megaraid_ioctl.h +* drivers/scsi/megaraid/mbox_defs.h +*======================================================*/ +#define MEGAIOC_MAGIC 'm' +#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t) /* Following subopcode work for opcode == 0x82 */ #define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | adapno) @@ -30,42 +30,42 @@ int megaraid_io_interface(int device, int target, struct scsi_cmnd_io *, int); typedef struct { - uint8_t timeout : 3; - uint8_t ars : 1; - uint8_t reserved : 3; - uint8_t islogical : 1; - uint8_t logdrv; - uint8_t channel; - uint8_t target; - uint8_t queuetag; - uint8_t queueaction; - uint8_t cdb[MAX_CDB_LEN]; - uint8_t cdblen; - uint8_t reqsenselen; - uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; - uint8_t numsgelements; - uint8_t scsistatus; - uint32_t dataxferaddr; - uint32_t dataxferlen; + uint8_t timeout : 3; + uint8_t ars : 1; + uint8_t reserved : 3; + uint8_t islogical : 1; + uint8_t logdrv; + uint8_t channel; + uint8_t target; + uint8_t queuetag; + uint8_t queueaction; + uint8_t cdb[MAX_CDB_LEN]; + uint8_t cdblen; + uint8_t reqsenselen; + uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; + uint8_t numsgelements; + uint8_t scsistatus; + uint32_t dataxferaddr; + uint32_t dataxferlen; } __attribute__((packed)) mega_passthru; typedef struct { - uint8_t cmd; - uint8_t cmdid; - uint8_t opcode; - uint8_t subopcode; - uint32_t lba; - uint32_t xferaddr; - uint8_t logdrv; - uint8_t resvd[3]; - uint8_t numstatus; - uint8_t status; + uint8_t cmd; + uint8_t cmdid; + uint8_t opcode; + uint8_t subopcode; + uint32_t lba; + uint32_t xferaddr; + uint8_t logdrv; + uint8_t resvd[3]; + uint8_t numstatus; + uint8_t status; } __attribute__((packed)) megacmd_t; typedef union { - uint8_t *pointer; - uint8_t pad[8]; + uint8_t *pointer; + uint8_t pad[8]; } ptr_t; // The above definition assumes sizeof(void*) <= 8. @@ -79,151 +79,177 @@ typedef char assert_sizeof_ptr_t[sizeof(ptr_t) == 8 ? 1 : -1]; struct uioctl_t { - uint32_t inlen; - uint32_t outlen; - union { - uint8_t fca[16]; - struct { - uint8_t opcode; - uint8_t subopcode; - uint16_t adapno; - ptr_t buffer; - uint32_t length; - } __attribute__((packed)) fcs; - } __attribute__((packed)) ui; - - megacmd_t mbox; - mega_passthru pthru; - ptr_t data; + uint32_t inlen; + uint32_t outlen; + union { + uint8_t fca[16]; + struct { + uint8_t opcode; + uint8_t subopcode; + uint16_t adapno; + ptr_t buffer; + uint32_t length; + } __attribute__((packed)) fcs; + } __attribute__((packed)) ui; + + megacmd_t mbox; + mega_passthru pthru; + ptr_t data; } __attribute__((packed)); /*=================================================== - * PERC5/6 Passthrough SCSI Command Interface - * - * Contents from: - * drivers/scsi/megaraid/megaraid_sas.h - *===================================================*/ +* PERC5/6 Passthrough SCSI Command Interface +* +* Contents from: +* drivers/scsi/megaraid/megaraid_sas.h +*===================================================*/ #define MEGASAS_MAGIC 'M' #define MEGASAS_IOC_FIRMWARE _IOWR(MEGASAS_MAGIC, 1, struct megasas_iocpacket) #define MFI_CMD_PD_SCSI_IO 0x04 +#define MFI_CMD_DCMD 0x05 #define MFI_FRAME_SGL64 0x02 -#define MFI_FRAME_DIR_READ 0x10 - -#define MAX_IOCTL_SGE 16 +#define MFI_STAT_OK 0x00 +#define MFI_DCMD_PD_GET_LIST 0x02010000 +/* +* Number of mailbox bytes in DCMD message frame +*/ +#define MFI_MBOX_SIZE 12 +#define MAX_IOCTL_SGE 16 +#define MFI_FRAME_DIR_NONE 0x0000 +#define MFI_FRAME_DIR_WRITE 0x0008 +#define MFI_FRAME_DIR_READ 0x0010 +#define MFI_FRAME_DIR_BOTH 0x0018 + +#define MAX_SYS_PDS 240 struct megasas_sge32 { - - u32 phys_addr; - u32 length; - + + u32 phys_addr; + u32 length; + } __attribute__ ((packed)); struct megasas_sge64 { - - u64 phys_addr; - u32 length; - + + u64 phys_addr; + u32 length; + } __attribute__ ((packed)); union megasas_sgl { - - struct megasas_sge32 sge32[1]; - struct megasas_sge64 sge64[1]; - + + struct megasas_sge32 sge32[1]; + struct megasas_sge64 sge64[1]; + } __attribute__ ((packed)); struct megasas_header { - - u8 cmd; /*00h */ - u8 sense_len; /*01h */ - u8 cmd_status; /*02h */ - u8 scsi_status; /*03h */ - - u8 target_id; /*04h */ - u8 lun; /*05h */ - u8 cdb_len; /*06h */ - u8 sge_count; /*07h */ - - u32 context; /*08h */ - u32 pad_0; /*0Ch */ - - u16 flags; /*10h */ - u16 timeout; /*12h */ - u32 data_xferlen; /*14h */ - + + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xferlen; /*14h */ + } __attribute__ ((packed)); struct megasas_pthru_frame { - - u8 cmd; /*00h */ - u8 sense_len; /*01h */ - u8 cmd_status; /*02h */ - u8 scsi_status; /*03h */ - - u8 target_id; /*04h */ - u8 lun; /*05h */ - u8 cdb_len; /*06h */ - u8 sge_count; /*07h */ - - u32 context; /*08h */ - u32 pad_0; /*0Ch */ - - u16 flags; /*10h */ - u16 timeout; /*12h */ - u32 data_xfer_len; /*14h */ - - u32 sense_buf_phys_addr_lo; /*18h */ - u32 sense_buf_phys_addr_hi; /*1Ch */ - - u8 cdb[16]; /*20h */ - union megasas_sgl sgl; /*30h */ - + + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xfer_len; /*14h */ + + u32 sense_buf_phys_addr_lo; /*18h */ + u32 sense_buf_phys_addr_hi; /*1Ch */ + + u8 cdb[16]; /*20h */ + union megasas_sgl sgl; /*30h */ + } __attribute__ ((packed)); struct megasas_dcmd_frame { - - u8 cmd; /*00h */ - u8 reserved_0; /*01h */ - u8 cmd_status; /*02h */ - u8 reserved_1[4]; /*03h */ - u8 sge_count; /*07h */ - - u32 context; /*08h */ - u32 pad_0; /*0Ch */ - - u16 flags; /*10h */ - u16 timeout; /*12h */ - - u32 data_xfer_len; /*14h */ - u32 opcode; /*18h */ - - union { /*1Ch */ - u8 b[12]; - u16 s[6]; - u32 w[3]; - } mbox; - - union megasas_sgl sgl; /*28h */ - + + u8 cmd; /*00h */ + u8 reserved_0; /*01h */ + u8 cmd_status; /*02h */ + u8 reserved_1[4]; /*03h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + + u32 data_xfer_len; /*14h */ + u32 opcode; /*18h */ + + union { /*1Ch */ + u8 b[12]; + u16 s[6]; + u32 w[3]; + } mbox; + + union megasas_sgl sgl; /*28h */ + } __attribute__ ((packed)); struct megasas_iocpacket { + u16 host_no; + u16 __pad1; + u32 sgl_off; + u32 sge_count; + u32 sense_off; + u32 sense_len; + union { + u8 raw[128]; + struct megasas_header hdr; + struct megasas_pthru_frame pthru; + struct megasas_dcmd_frame dcmd; + } frame; + + struct iovec sgl[MAX_IOCTL_SGE]; +} __attribute__ ((packed)); - u16 host_no; - u16 __pad1; - u32 sgl_off; - u32 sge_count; - u32 sense_off; - u32 sense_len; - union { - u8 raw[128]; - struct megasas_header hdr; - struct megasas_pthru_frame pthru; - } frame; - - struct iovec sgl[MAX_IOCTL_SGE]; - +struct megasas_pd_address { + u16 device_id; + u16 encl_device_id; + u8 encl_index; + u8 slot_number; + u8 scsi_dev_type; /* 0 = disk */ + u8 connect_port_bitmap; + u64 sas_addr[2]; +} __attribute__ ((packed)); + +struct megasas_pd_list { + u32 size; + u32 count; + struct megasas_pd_address addr[MAX_SYS_PDS]; } __attribute__ ((packed)); #undef u8 diff --git a/os_darwin.cpp b/os_darwin.cpp index 61c3425..eabdee9 100644 --- a/os_darwin.cpp +++ b/os_darwin.cpp @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include @@ -44,7 +44,7 @@ #include "os_darwin.h" // Needed by '-V' option (CVS versioning) of smartd/smartctl -const char *os_XXXX_c_cvsid="$Id: os_darwin.cpp,v 1.21 2008/06/12 21:46:31 ballen4705 Exp $" \ +const char *os_XXXX_c_cvsid="$Id: os_darwin.cpp 3805 2013-03-29 19:54:18Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; // Print examples for smartctl. @@ -72,9 +72,8 @@ void print_smartctl_examples(){ // tries to guess device type given the name (a path). See utility.h // for return values. -int guess_device_type (const char* dev_name) { +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; } @@ -420,59 +419,7 @@ ata_command_interface(int fd, smart_command_set command, 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; -} - -int areca_command_interface(int fd, int escalade_port, - smart_command_set command, int select, - char *data) -{ - fd = fd; - escalade_port = escalade_port; - command = command; - select = select; - data = data; - return -1; -} - - - - -int marvell_command_interface(int fd, smart_command_set command, - int select, char *data) -{ - fd = fd; - command = command; - select = select; - data = data; - return -1; -} - -int highpoint_command_interface(int fd, smart_command_set command, int select, char *data) -{ - fd = fd; - 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) { - fd = fd; - iop = iop; - report = report; +int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) { return -ENOSYS; } diff --git a/os_darwin.h b/os_darwin.h index b3a7ec4..5978187 100644 --- a/os_darwin.h +++ b/os_darwin.h @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -24,7 +24,7 @@ #ifndef OS_DARWIN_H_ #define OS_DARWIN_H_ -#define OS_DARWIN_H_CVSID "$Id: os_darwin.h,v 1.7 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 3728 2012-12-13 17:57:50Z chrfranke $\n" // Isn't in 10.3.9? diff --git a/os_darwin/SMART.in b/os_darwin/SMART.in index eb6137d..df2cb5c 100644 --- a/os_darwin/SMART.in +++ b/os_darwin/SMART.in @@ -12,10 +12,10 @@ # 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. +# example COPYING); if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# $Id: SMART.in,v 1.5 2008/03/04 22:09:47 ballen4705 Exp $ +# $Id: SMART.in 3728 2012-12-13 17:57:50Z chrfranke $ ## # SMART monitoring diff --git a/os_freebsd.cpp b/os_freebsd.cpp index d9a6add..4f00cdd 100644 --- a/os_freebsd.cpp +++ b/os_freebsd.cpp @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); If not, see . + * */ #include @@ -46,6 +46,7 @@ #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #define USBDEV "/dev/usb" #if defined(__FreeBSD_version) @@ -74,7 +75,7 @@ #define PATHINQ_SETTINGS_SIZE 128 #endif -const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3542 2012-05-18 18:18:45Z samm2 $" \ +const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3771 2013-02-10 15:36:55Z samm2 $" \ ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; #define NO_RETURN 0 @@ -121,6 +122,7 @@ void printwarning(int msgNo, const char* extra) { // global variable holding byte count of allocated memory long long bytes; +extern unsigned char failuretest_permissive; ///////////////////////////////////////////////////////////////////////////// @@ -189,6 +191,7 @@ static const char smartctl_examples[] = " (Prints Self-Test & Attribute errors)\n\n" " smartctl -a --device=3ware,2 /dev/twa0\n" " smartctl -a --device=3ware,2 /dev/twe0\n" + " smartctl -a --device=3ware,2 /dev/tws0\n" " (Prints all SMART information for ATA disk on\n" " third port of first 3ware RAID controller)\n" " smartctl -a --device=cciss,0 /dev/ciss0\n" @@ -252,9 +255,11 @@ freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_ int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { - int fd = get_fd(); + int fd = get_fd(), ret; ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST - return ioctl(fd, IOCATAREQUEST, request); + ret = ioctl(fd, IOCATAREQUEST, request); + if (ret) set_err(errno); + return ret; } @@ -269,8 +274,10 @@ bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o true, // data_out_support true, // multi_sector_support ata_48bit) - ) - return false; + ) { + set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); + return false; + } struct ata_ioc_request request; bzero(&request,sizeof(struct ata_ioc_request)); @@ -303,7 +310,7 @@ bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o clear_err(); errno = 0; if (do_cmd(&request, in.in_regs.is_48bit_cmd())) - return set_err(errno); + return false; if (request.error) return set_err(EIO, "request failed, error code 0x%02x", request.error); @@ -328,7 +335,8 @@ bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o { // 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", + 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)request.u.ata.command, (int)request.u.ata.feature, (int)request.u.ata.count, @@ -385,6 +393,16 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi union ccb ccb; int camflags; + // FIXME: + // 48bit commands are broken in ATACAM before r242422/HEAD + // and may cause system hang + // Waiting for MFC to make sure that bug is fixed, + // later version check needs to be added + if(!strcmp("ata",m_camdev->sim_name) && is_48bit_cmd) { + set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); + return -1; + } + memset(&ccb, 0, sizeof(ccb)); if (request->count == 0) @@ -393,8 +411,6 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi camflags = CAM_DIR_IN; else camflags = CAM_DIR_OUT; - if(is_48bit_cmd) - camflags |= CAM_ATAIO_48BIT; cam_fill_ataio(&ccb.ataio, 0, @@ -405,7 +421,8 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi request->count, request->timeout * 1000); // timeout in seconds - ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT; + ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT | + (is_48bit_cmd ? CAM_ATAIO_48BIT : 0); // ata_28bit_cmd ccb.ataio.cmd.command = request->u.ata.command; ccb.ataio.cmd.features = request->u.ata.feature; @@ -423,12 +440,13 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(m_camdev, &ccb) < 0) { - err(1, "cam_send_ccb"); + set_err(EIO, "cam_send_ccb failed"); return -1; } if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + set_err(EIO); return -1; } @@ -449,10 +467,10 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi #endif ///////////////////////////////////////////////////////////////////////////// -/// Implement AMCC/3ware RAID support with old functions +/// Implement AMCC/3ware RAID support class freebsd_escalade_device -: public /*implements*/ ata_device_with_command_set, +: public /*implements*/ ata_device, public /*extends*/ freebsd_smart_device { public: @@ -460,7 +478,7 @@ public: int escalade_type, int disknum); protected: - virtual int ata_command_interface(smart_command_set command, int select, char * data); + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); virtual bool open(); private: @@ -493,13 +511,18 @@ bool freebsd_escalade_device::open() return true; } -int freebsd_escalade_device::ata_command_interface(smart_command_set command, int select, char * data) +bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { // to hold true file descriptor int fd = get_fd(); - // return value and buffer for ioctl() - int ioctlreturn, readdata=0; + if (!ata_cmd_is_ok(in, + true, // data_out_support + false, // TODO: multi_sector_support + true) // ata_48bit_support + ) + return false; + struct twe_usercommand* cmd_twe = NULL; TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; TWE_Command_ATA* ata = NULL; @@ -517,16 +540,16 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; - cmd_twa->driver_pkt.buffer_length = 512; + cmd_twa->driver_pkt.buffer_length = in.size; + // using "old" packet format to speak with SATA devices ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { cmd_twe = (struct twe_usercommand*)ioctl_buffer; ata = &cmd_twe->tu_command.ata; } else { - pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n" - "Please contact " PACKAGE_BUGREPORT "\n", m_escalade_type, m_disknum); - errno=ENOSYS; - return -1; + return set_err(ENOSYS, + "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum); } ata->opcode = TWE_OP_ATA_PASSTHROUGH; @@ -534,18 +557,20 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // Same for (almost) all commands - but some reset below ata->request_id = 0xFF; ata->unit = m_disknum; - ata->status = 0; + ata->status = 0; ata->flags = 0x1; - ata->drive_head = 0x0; - ata->sector_num = 0; - - // All SMART commands use this CL/CH signature. These are magic - // values from the ATA specifications. - ata->cylinder_lo = 0x4F; - ata->cylinder_hi = 0xC2; - - // SMART ATA COMMAND REGISTER value - ata->command = ATA_SMART_CMD; + ata->size = 0x5; // TODO: multisector support + // Set registers + { + const ata_in_regs_48bit & r = in.in_regs; + ata->features = r.features_16; + ata->sector_count = r.sector_count_16; + ata->sector_num = r.lba_low_16; + ata->cylinder_lo = r.lba_mid_16; + ata->cylinder_hi = r.lba_high_16; + ata->drive_head = r.device; + ata->command = r.command; + } // Is this a command that reads or returns 512 bytes? // passthru->param values are: @@ -554,113 +579,51 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // 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; + bool readdata = false; + if (in.direction == ata_cmd_in::data_in) { if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { - cmd_twe->tu_data = data; + cmd_twe->tu_data = in.buffer; cmd_twe->tu_size = 512; } - ata->sgl_offset = 0x5; - ata->size = 0x5; + + readdata=true; + ata->sgl_offset = 0x5; ata->param = 0xD; - 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) + // if (sizeof(long)==8) // ata->size++; } - else { + else if (in.direction == ata_cmd_in::no_data) { // Non data command -- but doesn't use large sector - // count register values. - ata->sgl_offset = 0x0; - ata->size = 0x5; + // count register values. + ata->sgl_offset = 0x0; ata->param = 0x8; ata->sector_count = 0x0; } + else if (in.direction == ata_cmd_in::data_out) { + ata->sgl_offset = 0x5; + ata->param = 0xF; // PIO data write + if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { + cmd_twe->tu_data = in.buffer; + cmd_twe->tu_size = 512; + } + else if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { + memcpy(cmd_twa->pdata, in.buffer, in.size); + } + } + else + return set_err(EINVAL); - // Now set ATA registers depending upon command - switch (command){ - case CHECK_POWER_MODE: - ata->command = ATA_CHECK_POWER_MODE; - ata->features = 0; - ata->cylinder_lo = 0; - ata->cylinder_hi = 0; - break; - case READ_VALUES: - ata->features = ATA_SMART_READ_VALUES; - break; - case READ_THRESHOLDS: - ata->features = ATA_SMART_READ_THRESHOLDS; - break; - case READ_LOG: - ata->features = ATA_SMART_READ_LOG_SECTOR; - // log number to return - ata->sector_num = select; - break; - case WRITE_LOG: - readdata=0; - ata->features = ATA_SMART_WRITE_LOG_SECTOR; - ata->sector_count = 1; - ata->sector_num = select; - ata->param = 0xF; // PIO data write - break; - case IDENTIFY: - // ATA IDENTIFY DEVICE - ata->command = ATA_IDENTIFY_DEVICE; - ata->features = 0; - ata->cylinder_lo = 0; - 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", m_disknum); - errno=ENODEV; - return -1; - case ENABLE: - ata->features = ATA_SMART_ENABLE; - break; - case DISABLE: - ata->features = ATA_SMART_DISABLE; - break; - case AUTO_OFFLINE: - ata->features = ATA_SMART_AUTO_OFFLINE; - // Enable or disable? - ata->sector_count = select; - break; - case AUTOSAVE: - ata->features = ATA_SMART_AUTOSAVE; - // Enable or disable? - ata->sector_count = select; - break; - case IMMEDIATE_OFFLINE: - ata->features = ATA_SMART_IMMEDIATE_OFFLINE; - // What test type to run? - ata->sector_num = select; - break; - case STATUS_CHECK: - 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. - 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, m_disknum); - errno=ENOSYS; - return -1; + // 3WARE controller can NOT have packet device internally + if (in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) { + return set_err(ENODEV, "No drive on port %d", m_disknum); } // Now send the command down through an ioctl() + int ioctlreturn; if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); } else { @@ -669,9 +632,7 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // Deal with the different error cases if (ioctlreturn) { - if (!errno) - errno=EIO; - return -1; + return set_err(EIO); } // See if the ATA command failed. Now that we have returned from @@ -687,50 +648,36 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // happened. if (ata->status || (ata->command & 0x21)) { - pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags); - errno=EIO; - return -1; + if (scsi_debugmode) + pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags); + return set_err(EIO); } // If this is a read data command, copy data to output buffer if (readdata) { if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) - memcpy(data, cmd_twa->pdata, 512); + memcpy(in.buffer, cmd_twa->pdata, in.size); + else if(m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { + memcpy(in.buffer, cmd_twe->tu_data, in.size); // untested + } } - - // 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=ata->cylinder_lo; - unsigned short cyl_hi=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; + // Return register values + if (ata) { + ata_out_regs_48bit & r = out.out_regs; + r.error = ata->features; + r.sector_count_16 = ata->sector_count; + r.lba_low_16 = ata->sector_num; + r.lba_mid_16 = ata->cylinder_lo; + r.lba_high_16 = ata->cylinder_hi; + r.device = ata->drive_head; + r.status = ata->command; } - - // copy sector count register (one byte!) to return data - if (command==CHECK_POWER_MODE) - *data=*(char *)&(ata->sector_count); - // look for nonexistent devices/ports - if (command==IDENTIFY && !nonempty(data, 512)) { - errno=ENODEV; - return -1; + if (in.in_regs.command == ATA_IDENTIFY_DEVICE + && !nonempty((unsigned char *)in.buffer, in.size)) { + return set_err(ENODEV, "No drive on port %d", m_disknum); } - - return 0; + return true; } @@ -911,7 +858,8 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i // 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", + 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)pide_pt_hdr_out->command, (int)pide_pt_hdr_out->feature, (int)pide_pt_hdr_out->sectorcount, @@ -1021,13 +969,16 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); } // SMART WRITE LOG SECTOR causing media errors - if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 && iop->cmnd[14] == ATA_SMART_CMD - && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || - (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 && iop->cmnd[9] == ATA_SMART_CMD && - iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) - return set_err(ENOSYS, "SMART WRITE LOG SECTOR command is not supported by controller firmware"); + if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 + && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && + iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || + (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 + && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) + { + if(!failuretest_permissive) + return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); + } } - // 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)); @@ -1077,6 +1028,16 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } + // mfip replacing PDT of the device so response does not make a sense + // this sets PDT to 00h - direct-access block device + if((!strcmp("mfi", m_camdev->sim_name) || !strcmp("mpt", m_camdev->sim_name)) + && iop->cmnd[0] == INQUIRY) { + if (report > 0) { + pout("device on %s controller, patching PDT\n", m_camdev->sim_name); + } + iop->dxferp[0] = iop->dxferp[0] & 0xe0; + } + return true; } @@ -1084,433 +1045,148 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support -class freebsd_areca_device -: public /*implements*/ ata_device, +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class freebsd_areca_ata_device +: public /*implements*/ areca_ata_device, public /*extends*/ freebsd_smart_device { public: - freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); - -protected: - virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); + freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); +}; -private: - int m_disknum; ///< Disk number. +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class freebsd_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ freebsd_smart_device +{ +public: + freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; -// 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 Areca "arcmsr" 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 -// 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" - - -/*FunctionCode*/ -#define FUNCTION_READ_RQBUFFER 0x0801 -#define FUNCTION_WRITE_WQBUFFER 0x0802 -#define FUNCTION_CLEAR_RQBUFFER 0x0803 -#define FUNCTION_CLEAR_WQBUFFER 0x0804 - -/* ARECA IO CONTROL CODE*/ -#define ARCMSR_IOCTL_READ_RQBUFFER _IOWR('F', FUNCTION_READ_RQBUFFER, sSRB_BUFFER) -#define ARCMSR_IOCTL_WRITE_WQBUFFER _IOWR('F', FUNCTION_WRITE_WQBUFFER, sSRB_BUFFER) -#define ARCMSR_IOCTL_CLEAR_RQBUFFER _IOWR('F', FUNCTION_CLEAR_RQBUFFER, sSRB_BUFFER) -#define ARCMSR_IOCTL_CLEAR_WQBUFFER _IOWR('F', FUNCTION_CLEAR_WQBUFFER, sSRB_BUFFER) -#define ARECA_SIG_STR "ARCMSR" - - - -// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver -typedef struct _SRB_IO_CONTROL +// Areca RAID Controller(SATA Disk) +freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca"), + freebsd_smart_device("ATA") +{ + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} + + +smart_device * freebsd_areca_ata_device::autodetect_open() { - unsigned int HeaderLength; - unsigned char Signature[8]; - unsigned int Timeout; - unsigned int ControlCode; - unsigned int ReturnCode; - unsigned int Length; -} sSRB_IO_CONTROL; - -typedef struct _SRB_BUFFER + int is_ata = 1; + + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; + } + + if(is_ata == 1) + { + // SATA device + return this; + } + + // SAS device + smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); + close(); + delete this; + newdev->open(); // TODO: Can possibly pass open fd + + return newdev.release(); +} + +int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { - sSRB_IO_CONTROL srbioctl; - unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware -} sSRB_BUFFER; + int ioctlreturn = 0; + + if(!is_open()) { + if(!open()){ + } + } + ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); + if (ioctlreturn) + { + // errors found + return -1; + } -// For debugging areca code + return ioctlreturn; +} -static void areca_dumpdata(unsigned char *block, int len) +bool freebsd_areca_ata_device::arcmsr_lock() { - int ln = (len / 16) + 1; // total line# - unsigned char c; - int pos = 0; - - printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); - printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); - printf("=====================================================================\n"); - - for ( int l = 0; l < ln && len; l++ ) - { - // printf the line# and the HEX data - // if a line data length < 16 then append the space to the tail of line to reach 16 chars - printf("%02X | ", l); - for ( pos = 0; pos < 16 && len; pos++, len-- ) - { - c = block[l*16+pos]; - printf("%02X ", c); - } - - if ( pos < 16 ) - { - for ( int loop = pos; loop < 16; loop++ ) - { - printf(" "); - } - } - - // print ASCII char - for ( int loop = 0; loop < pos; loop++ ) - { - c = block[l*16+loop]; - if ( c >= 0x20 && c <= 0x7F ) - { - printf("%c", c); - } - else - { - printf("."); - } - } - printf("\n"); - } - printf("=====================================================================\n"); + return true; } -static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) +bool freebsd_areca_ata_device::arcmsr_unlock() { - ARGUSED(ext_data); - - int ioctlreturn = 0; - sSRB_BUFFER sBuf; - - unsigned char *areca_return_packet; - int total = 0; - int expected = -1; - unsigned char return_buff[2048]; - unsigned char *ptr = &return_buff[0]; - memset(return_buff, 0, sizeof(return_buff)); - - memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); - - - sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); - memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); - sBuf.srbioctl.Timeout = 10000; - sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER; - - switch ( arcmsr_cmd ) - { - // command for writing data to driver - case ARCMSR_IOCTL_WRITE_WQBUFFER: - if ( data && data_len ) - { - sBuf.srbioctl.Length = data_len; - memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); - } - // commands for clearing related buffer of driver - case ARCMSR_IOCTL_CLEAR_RQBUFFER: - case ARCMSR_IOCTL_CLEAR_WQBUFFER: - break; - // command for reading data from driver - case ARCMSR_IOCTL_READ_RQBUFFER: - break; - default: - // unknown arcmsr commands - return -1; - } - - - while ( 1 ) - { - ioctlreturn = ioctl(fd,arcmsr_cmd,&sBuf); - if ( ioctlreturn ) - { - // errors found - break; - } - - if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) - { - // if succeeded, just returns the length of outgoing data - return data_len; - } - - if ( sBuf.srbioctl.Length ) - { - if(ata_debugmode) - areca_dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - ptr += sBuf.srbioctl.Length; - total += sBuf.srbioctl.Length; - // the returned bytes enough to compute payload length ? - if ( expected < 0 && total >= 5 ) - { - areca_return_packet = (unsigned char *)&return_buff[0]; - if ( areca_return_packet[0] == 0x5E && - areca_return_packet[1] == 0x01 && - areca_return_packet[2] == 0x61 ) - { - // valid header, let's compute the returned payload length, - // we expected the total length is - // payload + 3 bytes header + 2 bytes length + 1 byte checksum - expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; - } - } - - if ( total >= 7 && total >= expected ) - { - //printf("total bytes received = %d, expected length = %d\n", total, expected); - - // ------ Okay! we received enough -------- - break; - } - } - } - - // Deal with the different error cases - if ( ioctlreturn ) - { - pout("ioctl write buffer failed code = %x\n", ioctlreturn); - return -2; - } - - - if ( data ) - { - memcpy(data, return_buff, total); - } - - return total; + return true; } -freebsd_areca_device::freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +// Areca RAID Controller(SAS Device) +freebsd_areca_scsi_device::freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), - freebsd_smart_device("ATA"), - m_disknum(disknum), - m_encnum(encnum) + freebsd_smart_device("SCSI") { + set_disknum(disknum); + set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -// Areca RAID Controller -// int freebsd_areca_device::ata_command_interface(smart_command_set command, int select, char * data) -bool freebsd_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +smart_device * freebsd_areca_scsi_device::autodetect_open() +{ + return this; +} + +int freebsd_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; + + if(!is_open()) { + if(!open()){ + } + } + ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); + if (ioctlreturn) + { + // errors found + return -1; + } + + return ioctlreturn; +} + +bool freebsd_areca_scsi_device::arcmsr_lock() { -if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // TODO: multi_sector_support - true) // ata_48bit_support - ) - return false; - - // ATA input registers - typedef struct _ATA_INPUT_REGISTERS - { - unsigned char features; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - unsigned char device_head; - unsigned char command; - unsigned char reserved[8]; - unsigned char data[512]; // [in/out] buffer for outgoing/incoming data - } sATA_INPUT_REGISTERS; - - // ATA output registers - // Note: The output registers is re-sorted for areca internal use only - typedef struct _ATA_OUTPUT_REGISTERS - { - unsigned char error; - unsigned char status; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - }sATA_OUTPUT_REGISTERS; - - // Areca packet format for outgoing: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes command length + variant data length, little endian - // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c - // B[6~last-1] : variant bytes payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte - // +--------------------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | - // +--------------------------------------------------------------------------------+ - // - - //Areca packet format for incoming: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes payload length, little endian - // B[5~last-1] : variant bytes returned payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes payload data x bytes cs 1 byte - // +-------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | - // +-------------------------------------------------------------------+ - unsigned char areca_packet[640]; - int areca_packet_len = sizeof(areca_packet); - unsigned char cs = 0; - - sATA_INPUT_REGISTERS *ata_cmd; - - // For debugging - memset(areca_packet, 0, areca_packet_len); - - // ----- BEGIN TO SETUP HEADERS ------- - areca_packet[0] = 0x5E; - areca_packet[1] = 0x01; - areca_packet[2] = 0x61; - areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); - areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); - areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command - - // ----- BEGIN TO SETUP PAYLOAD DATA ----- - memcpy(&areca_packet[7], "SmrT", 4); // areca defined password - ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; - - // Set registers - { - const ata_in_regs_48bit & r = in.in_regs; - ata_cmd->features = r.features_16; - ata_cmd->sector_count = r.sector_count_16; - ata_cmd->sector_number = r.lba_low_16; - ata_cmd->cylinder_low = r.lba_mid_16; - ata_cmd->cylinder_high = r.lba_high_16; - ata_cmd->device_head = r.device; - ata_cmd->command = r.command; - } - bool readdata = false; - if (in.direction == ata_cmd_in::data_in) { - readdata = true; - // the command will read data - areca_packet[6] = 0x13; - } - else if ( in.direction == ata_cmd_in::no_data ) - { - // the commands will return no data - areca_packet[6] = 0x15; - } - else if (in.direction == ata_cmd_in::data_out) - { - // the commands will write data - memcpy(ata_cmd->data, in.buffer, in.size); - areca_packet[6] = 0x14; - } - else { - // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE - return set_err(ENOTSUP, "DATA OUT not supported for this Areca controller type"); - } - - areca_packet[11] = m_disknum - 1; // disk # - areca_packet[19] = m_encnum - 1; // enc# - - // ----- BEGIN TO SETUP CHECKSUM ----- - for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) - { - cs += areca_packet[loop]; - } - areca_packet[areca_packet_len-1] = cs; - - // ----- BEGIN TO SEND TO ARECA DRIVER ------ - int expected = 0; - unsigned char return_buff[2048]; - memset(return_buff, 0, sizeof(return_buff)); - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL); - if (expected==-3) { - return set_err(EIO); - } - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL); - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL); - if ( expected > 0 ) - { - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL); - } - if ( expected < 0 ) - { - return -1; - } - - // ----- VERIFY THE CHECKSUM ----- - cs = 0; - for ( int loop = 3; loop < expected - 1; loop++ ) - { - cs += return_buff[loop]; - } - - if ( return_buff[expected - 1] != cs ) - { - return set_err(EIO); - } - - sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; - if ( ata_out->status ) - { - if ( in.in_regs.command == ATA_IDENTIFY_DEVICE - && !nonempty((unsigned char *)in.buffer, in.size)) - { - return set_err(ENODEV, "No drive on port %d", m_disknum); - } - } - - // returns with data - if (readdata) - { - memcpy(in.buffer, &return_buff[7], in.size); - } - - // Return register values - { - ata_out_regs_48bit & r = out.out_regs; - r.error = ata_out->error; - r.sector_count_16 = ata_out->sector_count; - r.lba_low_16 = ata_out->sector_number; - r.lba_mid_16 = ata_out->cylinder_low; - r.lba_high_16 = ata_out->cylinder_high; - r.status = ata_out->status; - } - return true; + return true; } +bool freebsd_areca_scsi_device::arcmsr_unlock() +{ + return true; +} + ///////////////////////////////////////////////////////////////////////////// /// Implement CCISS RAID support with old functions @@ -1599,10 +1275,11 @@ smart_device * freebsd_scsi_device::autodetect_open() // Use INQUIRY to detect type // 3ware ? - if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) { + if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4) || + !strcmp("tws",m_camdev->sim_name) || !strcmp("twa",m_camdev->sim_name)) { close(); - set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n" - "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name()); + set_err(EINVAL, "3ware/LSI controller, please try adding '-d 3ware,N',\n" + "you may need to replace %s with /dev/twaN, /dev/tweN or /dev/twsN", get_dev_name()); return this; } @@ -1791,10 +1468,13 @@ bool get_dev_names_cam(std::vector & names, bool show_all) } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && (skip_device == 0 || show_all)) { /* One device may be populated as many peripherals (pass0 & da0 for example). - * We are searching for latest name + * We are searching for best name */ periph_result = &ccb.cdm.matches[i].result.periph_result; - devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); + /* Prefer non-"pass" names */ + if (devname.empty() || strncmp(periph_result->periph_name, "pass", 4) != 0) { + devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); + } changed = 0; }; if ((changed == 1 || show_all) && !devname.empty()) { @@ -2200,6 +1880,7 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam // 3Ware ? static const char * fbsd_dev_twe_ctrl = "/dev/twe"; static const char * fbsd_dev_twa_ctrl = "/dev/twa"; + static const char * fbsd_dev_tws_ctrl = "/dev/tws"; int disknum = -1, n1 = -1, n2 = -1, contr = -1; if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { @@ -2213,7 +1894,8 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam } // guess 3ware device type based on device name - if (!strncmp(fbsd_dev_twa_ctrl, name, strlen(fbsd_dev_twa_ctrl))){ + if (str_starts_with(name, fbsd_dev_twa_ctrl) || + str_starts_with(name, fbsd_dev_tws_ctrl) ) { contr=CONTROLLER_3WARE_9000_CHAR; } if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){ @@ -2221,12 +1903,12 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam } if(contr == -1){ - set_err(EINVAL, "3ware controller type unknown, use %sX or %sX devices", - fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl); + set_err(EINVAL, "3ware controller type unknown, use %sX, %sX or %sX devices", + fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl, fbsd_dev_tws_ctrl); return 0; } return new freebsd_escalade_device(this, name, contr, disknum); - } + } // Highpoint ? int controller = -1, channel = -1; disknum = 1; @@ -2263,7 +1945,7 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } - return new freebsd_cciss_device(this, name, disknum); + return get_sat_device("sat,auto", new freebsd_cciss_device(this, name, disknum)); } #if FREEBSDVER > 800100 // adaX devices ? @@ -2282,7 +1964,7 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } - return new freebsd_areca_device(this, name, disknum); + return new freebsd_areca_ata_device(this, name, disknum, encnum); } return 0; diff --git a/os_freebsd.h b/os_freebsd.h index d00e9f6..a22442f 100644 --- a/os_freebsd.h +++ b/os_freebsd.h @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -82,7 +82,7 @@ #ifndef OS_FREEBSD_H_ #define OS_FREEBSD_H_ -#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 3266 2011-02-21 16:33:04Z chrfranke $" +#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 3727 2012-12-13 17:23:06Z samm2 $" #define MAX_NUM_DEV 26 diff --git a/os_generic.cpp b/os_generic.cpp index e4b7999..0999833 100644 --- a/os_generic.cpp +++ b/os_generic.cpp @@ -1,5 +1,5 @@ /* - * os_generic.c + * os_generic.cpp * * Home page of code is: http://smartmontools.sourceforge.net * @@ -13,8 +13,8 @@ * 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. + * (for example COPYING); If not, see . + * */ @@ -71,6 +71,7 @@ // These are needed to define prototypes and structures for the // functions defined below +#include "int64.h" #include "atacmds.h" #include "utility.h" @@ -82,8 +83,8 @@ // should have one *_H_CVSID macro appearing below for each file // appearing with #include "*.h" above. Please list these (below) in // alphabetic/dictionary order. -const char *os_XXXX_c_cvsid="$Id: os_generic.cpp 3191 2010-10-27 19:55:33Z chrfranke $" \ -ATACMDS_H_CVSID CONFIG_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID; +const char * os_XXXX_cpp_cvsid="$Id: os_generic.cpp 3579 2012-07-20 17:50:12Z chrfranke $" + ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID; // This is here to prevent compiler warnings for unused arguments of // functions. diff --git a/os_generic.h b/os_generic.h index fcc6b12..4c0ba66 100644 --- a/os_generic.h +++ b/os_generic.h @@ -12,8 +12,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -25,7 +25,7 @@ // In the three following lines, change 'GENERIC' to your OS name #ifndef OS_GENERIC_H_ #define OS_GENERIC_H_ -#define OS_GENERIC_H_CVSID "$Id: os_generic.h,v 1.7 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_GENERIC_H_CVSID "$Id: os_generic.h 3728 2012-12-13 17:57:50Z chrfranke $\n" // Additional material should start here. Note: to keep the '-V' CVS // reporting option working as intended, you should only #include diff --git a/os_linux.cpp b/os_linux.cpp index b68575a..164e8f7 100644 --- a/os_linux.cpp +++ b/os_linux.cpp @@ -65,6 +65,7 @@ #include // for offsetof() #include #include +#include #ifndef makedev // old versions of types.h do not include sysmacros.h #include #endif @@ -82,6 +83,7 @@ #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #ifndef ENOTSUP #define ENOTSUP ENOSYS @@ -89,9 +91,9 @@ #define ARGUSED(x) ((void)(x)) -const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 3558 2012-06-05 16:42:05Z chrfranke $" +const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 3738 2012-12-17 12:01:35Z samm2 $" OS_LINUX_H_CVSID; - +extern unsigned char failuretest_permissive; namespace os_linux { // No need to publish anything, name provided for Doxygen @@ -121,13 +123,15 @@ protected: int get_fd() const { return m_fd; } + void set_fd(int fd) + { m_fd = fd; } + private: int m_fd; ///< filedesc, -1 if not open. int m_flags; ///< Flags for ::open() int m_retry_flags; ///< Flags to retry ::open(), -1 if no retry }; - linux_smart_device::~linux_smart_device() throw() { if (m_fd >= 0) @@ -201,7 +205,6 @@ static const char smartctl_examples[] = " on Areca RAID controller)\n" ; - ///////////////////////////////////////////////////////////////////////////// /// Linux ATA support @@ -242,7 +245,6 @@ linux_ata_device::linux_ata_device(smart_interface * intf, const char * dev_name // 0 if the command succeeded and disk SMART status is "OK" // 1 if the command succeeded and disk SMART status is "FAILING" - #define BUFFER_LENGTH (4+512) int linux_ata_device::ata_command_interface(smart_command_set command, int select, char * data) @@ -849,7 +851,6 @@ linux_scsi_device::linux_scsi_device(smart_interface * intf, { } - bool linux_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); @@ -875,7 +876,7 @@ public: virtual bool open(); virtual bool close(); - + virtual bool scsi_pass_through(scsi_cmnd_io *iop); private: @@ -885,11 +886,11 @@ private: int m_fd; bool (linux_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); bool megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); bool megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int senseLen, void *sense, int report); + int senseLen, void *sense, int report, int direction); }; linux_megaraid_device::linux_megaraid_device(smart_interface *intf, @@ -900,6 +901,7 @@ linux_megaraid_device::linux_megaraid_device(smart_interface *intf, m_fd(-1), pt_cmd(0) { set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum); + set_info().dev_type = strprintf("megaraid,%d", tgt); } linux_megaraid_device::~linux_megaraid_device() throw() @@ -939,61 +941,55 @@ smart_device * linux_megaraid_device::autodetect_open() // Use INQUIRY to detect type { - // SAT or USB ? + // SAT? ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); - if (newdev) { - // NOTE: 'this' is now owned by '*newdev' - newdev->close(); - newdev->set_err(ENOSYS, "SATA device detected,\n" - "MegaRAID SAT layer is reportedly buggy, use '-d sat+megaraid,N' to try anyhow"); + if (newdev) // NOTE: 'this' is now owned by '*newdev' return newdev; - } } // Nothing special found return this; } - bool linux_megaraid_device::open() { char line[128]; - int mjr, n1; - FILE *fp; + int mjr; int report = scsi_debugmode; - if (!linux_smart_device::open()) - return false; - - /* Get device HBA */ - struct sg_scsi_id sgid; - if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) { - m_hba = sgid.host_no; - } - else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) { - int err = errno; + if(sscanf(get_dev_name(),"/dev/bus/%d", &m_hba) == 0) { + if (!linux_smart_device::open()) + return false; + /* Get device HBA */ + struct sg_scsi_id sgid; + if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) { + m_hba = sgid.host_no; + } + else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) { + int err = errno; + linux_smart_device::close(); + return set_err(err, "can't get bus number"); + } // we dont need this device anymore linux_smart_device::close(); - return set_err(err, "can't get bus number"); } - /* Perform mknod of device ioctl node */ - fp = fopen("/proc/devices", "r"); + FILE * fp = fopen("/proc/devices", "r"); while (fgets(line, sizeof(line), fp) != NULL) { - n1=0; - if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { - n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); - if(report > 0) - pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); - if (n1 >= 0 || errno == EEXIST) - break; - } - else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) { - n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0)); - if(report > 0) - pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno); - if (n1 >= 0 || errno == EEXIST) - break; - } + int n1 = 0; + if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { + n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); + if(report > 0) + pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); + if (n1 >= 0 || errno == EEXIST) + break; + } + else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) { + n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0)); + if(report > 0) + pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno); + if (n1 >= 0 || errno == EEXIST) + break; + } } fclose(fp); @@ -1009,7 +1005,7 @@ bool linux_megaraid_device::open() linux_smart_device::close(); return set_err(err, "cannot open /dev/megaraid_sas_ioctl_node or /dev/megadev0"); } - + set_fd(m_fd); return true; } @@ -1018,7 +1014,8 @@ bool linux_megaraid_device::close() if (m_fd >= 0) ::close(m_fd); m_fd = -1; m_hba = 0; pt_cmd = 0; - return linux_smart_device::close(); + set_fd(m_fd); + return true; } bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) @@ -1061,23 +1058,25 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); } // SMART WRITE LOG SECTOR causing media errors - if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 && iop->cmnd[14] == ATA_SMART_CMD - && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || - (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 && iop->cmnd[9] == ATA_SMART_CMD && - iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) - return set_err(ENOSYS, "SMART WRITE LOG SECTOR command is not supported by controller firmware"); - + if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 // SAT16 WRITE LOG + && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || + (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 // SAT12 WRITE LOG + && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) + { + if(!failuretest_permissive) + return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); + } if (pt_cmd == NULL) return false; - return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, + return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, iop->dxfer_len, iop->dxferp, - iop->max_sense_len, iop->sensep, report); + iop->max_sense_len, iop->sensep, report, iop->dxfer_dir); } /* Issue passthrough scsi command to PERC5/6 controllers */ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int /*senseLen*/, void * /*sense*/, int /*report*/) + int /*senseLen*/, void * /*sense*/, int /*report*/, int dxfer_dir) { struct megasas_pthru_frame *pthru; struct megasas_iocpacket uio; @@ -1092,7 +1091,21 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, pthru->lun = 0; pthru->cdb_len = cdbLen; pthru->timeout = 0; - pthru->flags = MFI_FRAME_DIR_READ; + switch (dxfer_dir) { + case DXFER_NONE: + pthru->flags = MFI_FRAME_DIR_NONE; + break; + case DXFER_FROM_DEVICE: + pthru->flags = MFI_FRAME_DIR_READ; + break; + case DXFER_TO_DEVICE: + pthru->flags = MFI_FRAME_DIR_WRITE; + break; + default: + pout("megasas_cmd: bad dxfer_dir\n"); + return set_err(EINVAL, "megasas_cmd: bad dxfer_dir\n"); + } + if (dataLen > 0) { pthru->sge_count = 1; pthru->data_xfer_len = dataLen; @@ -1126,7 +1139,7 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, /* Issue passthrough scsi commands to PERC2/3/4 controllers */ bool linux_megaraid_device::megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data, - int /*senseLen*/, void * /*sense*/, int /*report*/) + int /*senseLen*/, void * /*sense*/, int /*report*/, int /* dir */) { struct uioctl_t uio; int rc; @@ -1261,7 +1274,6 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name) int selinux_enforced = security_getenforce(); #endif - /* 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"); @@ -1301,7 +1313,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name) #endif /* Now check if nodes are correct */ for (index=0; index<16; index++) { - sprintf(nodestring, "/dev/%s%d", nodename, index); + snprintf(nodestring, sizeof(nodestring), "/dev/%s%d", nodename, index); #ifdef WITH_SELINUX /* Get context of the node and set it as the default */ if (selinux_enabled) { @@ -1434,7 +1446,6 @@ bool linux_escalade_device::open() // 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 @@ -1644,84 +1655,39 @@ bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out return true; } - ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support -class linux_areca_device -: public /*implements*/ ata_device, +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class linux_areca_ata_device +: public /*implements*/ areca_ata_device, public /*extends*/ linux_smart_device { public: - linux_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); - -protected: - virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); - -private: - int m_disknum; ///< Disk number. - int m_encnum; ///< Enclosure number. + linux_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; - -// 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 Areca "arcmsr" 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 -// 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" - - -/*DeviceType*/ -#define ARECA_SATA_RAID 0x90000000 -/*FunctionCode*/ -#define FUNCTION_READ_RQBUFFER 0x0801 -#define FUNCTION_WRITE_WQBUFFER 0x0802 -#define FUNCTION_CLEAR_RQBUFFER 0x0803 -#define FUNCTION_CLEAR_WQBUFFER 0x0804 - -/* ARECA IO CONTROL CODE*/ -#define ARCMSR_IOCTL_READ_RQBUFFER (ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER) -#define ARCMSR_IOCTL_WRITE_WQBUFFER (ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER) -#define ARCMSR_IOCTL_CLEAR_RQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER) -#define ARCMSR_IOCTL_CLEAR_WQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER) -#define ARECA_SIG_STR "ARCMSR" - -// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver -typedef struct _SRB_IO_CONTROL -{ - unsigned int HeaderLength; - unsigned char Signature[8]; - unsigned int Timeout; - unsigned int ControlCode; - unsigned int ReturnCode; - unsigned int Length; -} sSRB_IO_CONTROL; - -typedef struct _SRB_BUFFER +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class linux_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ linux_smart_device { - sSRB_IO_CONTROL srbioctl; - unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware -} sSRB_BUFFER; +public: + linux_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); +}; // Looks in /proc/scsi to suggest correct areca devices -// If hint not NULL, return device path guess -static int find_areca_in_proc(char *hint) +static int find_areca_in_proc() { const char* proc_format_string="host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"; @@ -1760,9 +1726,6 @@ static int find_areca_in_proc(char *hint) dev++; if (id == 16 && type == 3) { // devices with id=16 and type=3 might be Areca controllers - if (!found && hint) { - sprintf(hint, "/dev/sg%d", dev); - } pout("Device /dev/sg%d appears to be an Areca controller.\n", dev); found++; } @@ -1771,404 +1734,117 @@ static int find_areca_in_proc(char *hint) return 0; } - -#if 0 // For debugging areca code - -static void dumpdata(unsigned char *block, int len) -{ - int ln = (len / 16) + 1; // total line# - unsigned char c; - int pos = 0; - - printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); - printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); - printf("=====================================================================\n"); - - for ( int l = 0; l < ln && len; l++ ) - { - // printf the line# and the HEX data - // if a line data length < 16 then append the space to the tail of line to reach 16 chars - printf("%02X | ", l); - for ( pos = 0; pos < 16 && len; pos++, len-- ) - { - c = block[l*16+pos]; - printf("%02X ", c); - } - - if ( pos < 16 ) - { - for ( int loop = pos; loop < 16; loop++ ) - { - printf(" "); - } - } - - // print ASCII char - for ( int loop = 0; loop < pos; loop++ ) - { - c = block[l*16+loop]; - if ( c >= 0x20 && c <= 0x7F ) - { - printf("%c", c); - } - else - { - printf("."); - } - } - printf("\n"); - } - printf("=====================================================================\n"); +// Areca RAID Controller(SATA Disk) +linux_areca_ata_device::linux_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca"), + linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK) +{ + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -#endif +smart_device * linux_areca_ata_device::autodetect_open() +{ + int is_ata = 1; -static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) -{ - ARGUSED(ext_data); - - int ioctlreturn = 0; - sSRB_BUFFER sBuf; - struct scsi_cmnd_io io_hdr; - int dir = DXFER_TO_DEVICE; - - UINT8 cdb[10]; - UINT8 sense[32]; - - unsigned char *areca_return_packet; - int total = 0; - int expected = -1; - unsigned char return_buff[2048]; - unsigned char *ptr = &return_buff[0]; - memset(return_buff, 0, sizeof(return_buff)); - - memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); - memset(&io_hdr, 0, sizeof(io_hdr)); - memset(cdb, 0, sizeof(cdb)); - memset(sense, 0, sizeof(sense)); - - - sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); - memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); - sBuf.srbioctl.Timeout = 10000; - sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER; - - switch ( arcmsr_cmd ) - { - // command for writing data to driver - case ARCMSR_IOCTL_WRITE_WQBUFFER: - if ( data && data_len ) - { - sBuf.srbioctl.Length = data_len; - memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); - } - // commands for clearing related buffer of driver - case ARCMSR_IOCTL_CLEAR_RQBUFFER: - case ARCMSR_IOCTL_CLEAR_WQBUFFER: - cdb[0] = 0x3B; //SCSI_WRITE_BUF command; - break; - // command for reading data from driver - case ARCMSR_IOCTL_READ_RQBUFFER: - cdb[0] = 0x3C; //SCSI_READ_BUF command; - dir = DXFER_FROM_DEVICE; - break; - default: - // unknown arcmsr commands - return -1; - } + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; + } - cdb[1] = 0x01; - cdb[2] = 0xf0; - // - // cdb[5][6][7][8] areca defined command code( to/from driver ) - // - cdb[5] = (char)( arcmsr_cmd >> 24); - cdb[6] = (char)( arcmsr_cmd >> 16); - cdb[7] = (char)( arcmsr_cmd >> 8); - cdb[8] = (char)( arcmsr_cmd & 0x0F ); - - io_hdr.dxfer_dir = dir; - io_hdr.dxfer_len = sizeof(sBuf); - io_hdr.dxferp = (unsigned char *)&sBuf; - 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; - - while ( 1 ) - { - ioctlreturn = do_normal_scsi_cmnd_io(fd, &io_hdr, 0); - if ( ioctlreturn || io_hdr.scsi_status ) - { - // errors found - break; - } - - if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) - { - // if succeeded, just returns the length of outgoing data - return data_len; - } - - if ( sBuf.srbioctl.Length ) - { - //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - ptr += sBuf.srbioctl.Length; - total += sBuf.srbioctl.Length; - // the returned bytes enough to compute payload length ? - if ( expected < 0 && total >= 5 ) - { - areca_return_packet = (unsigned char *)&return_buff[0]; - if ( areca_return_packet[0] == 0x5E && - areca_return_packet[1] == 0x01 && - areca_return_packet[2] == 0x61 ) - { - // valid header, let's compute the returned payload length, - // we expected the total length is - // payload + 3 bytes header + 2 bytes length + 1 byte checksum - expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; - } - } - - if ( total >= 7 && total >= expected ) - { - //printf("total bytes received = %d, expected length = %d\n", total, expected); - - // ------ Okay! we received enough -------- - break; - } - } - } + if(is_ata == 1) + { + // SATA device + return this; + } - // Deal with the different error cases - if ( ioctlreturn ) - { - pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); - return -2; - } + // SAS device + smart_device_auto_ptr newdev(new linux_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); + close(); + delete this; + newdev->open(); // TODO: Can possibly pass open fd + return newdev.release(); +} - if ( io_hdr.scsi_status ) - { - pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status); - return -3; - } +int linux_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } - if ( data ) - { - memcpy(data, return_buff, total); - } + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - return total; + return ioctlreturn; } +bool linux_areca_ata_device::arcmsr_lock() +{ + return true; +} -linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +bool linux_areca_ata_device::arcmsr_unlock() +{ + return true; +} + +// Areca RAID Controller(SAS Device) +linux_areca_scsi_device::linux_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), - linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK), - m_disknum(disknum), - m_encnum(encnum) + linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK) { + set_disknum(disknum); + set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -// Areca RAID Controller -// int linux_areca_device::ata_command_interface(smart_command_set command, int select, char * data) -bool linux_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) -{ -if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // TODO: multi_sector_support - true) // ata_48bit_support - ) - return false; - - // ATA input registers - typedef struct _ATA_INPUT_REGISTERS - { - unsigned char features; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - unsigned char device_head; - unsigned char command; - unsigned char reserved[8]; - unsigned char data[512]; // [in/out] buffer for outgoing/incoming data - } sATA_INPUT_REGISTERS; - - // ATA output registers - // Note: The output registers is re-sorted for areca internal use only - typedef struct _ATA_OUTPUT_REGISTERS - { - unsigned char error; - unsigned char status; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - }sATA_OUTPUT_REGISTERS; - - // Areca packet format for outgoing: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes command length + variant data length, little endian - // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c - // B[6~last-1] : variant bytes payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte - // +--------------------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | - // +--------------------------------------------------------------------------------+ - // - - //Areca packet format for incoming: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes payload length, little endian - // B[5~last-1] : variant bytes returned payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes payload data x bytes cs 1 byte - // +-------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | - // +-------------------------------------------------------------------+ - unsigned char areca_packet[640]; - int areca_packet_len = sizeof(areca_packet); - unsigned char cs = 0; - - sATA_INPUT_REGISTERS *ata_cmd; - - // For debugging -#if 0 - memset(sInq, 0, sizeof(sInq)); - scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); - dumpdata((unsigned char *)sInq, sizeof(sInq)); -#endif - memset(areca_packet, 0, areca_packet_len); - - // ----- BEGIN TO SETUP HEADERS ------- - areca_packet[0] = 0x5E; - areca_packet[1] = 0x01; - areca_packet[2] = 0x61; - areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); - areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); - areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command - - // ----- BEGIN TO SETUP PAYLOAD DATA ----- - memcpy(&areca_packet[7], "SmrT", 4); // areca defined password - ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; - - // Set registers - { - const ata_in_regs_48bit & r = in.in_regs; - ata_cmd->features = r.features_16; - ata_cmd->sector_count = r.sector_count_16; - ata_cmd->sector_number = r.lba_low_16; - ata_cmd->cylinder_low = r.lba_mid_16; - ata_cmd->cylinder_high = r.lba_high_16; - ata_cmd->device_head = r.device; - ata_cmd->command = r.command; - } - bool readdata = false; - if (in.direction == ata_cmd_in::data_in) { - readdata = true; - // the command will read data - areca_packet[6] = 0x13; - } - else if ( in.direction == ata_cmd_in::no_data ) - { - // the commands will return no data - areca_packet[6] = 0x15; - } - else if (in.direction == ata_cmd_in::data_out) - { - // the commands will write data - memcpy(ata_cmd->data, in.buffer, in.size); - areca_packet[6] = 0x14; - } - else { - // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE - return set_err(ENOTSUP, "DATA OUT not supported for this Areca controller type"); - } - - areca_packet[11] = m_disknum - 1; // disk# - areca_packet[19] = m_encnum - 1; // enc# - - // ----- BEGIN TO SETUP CHECKSUM ----- - for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) - { - cs += areca_packet[loop]; - } - areca_packet[areca_packet_len-1] = cs; - - // ----- BEGIN TO SEND TO ARECA DRIVER ------ - int expected = 0; - unsigned char return_buff[2048]; - memset(return_buff, 0, sizeof(return_buff)); - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL); - if (expected==-3) { - find_areca_in_proc(NULL); - return set_err(EIO); - } - - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL); - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL); - if ( expected > 0 ) - { - expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL); - } - if ( expected < 0 ) - { - return -1; - } +smart_device * linux_areca_scsi_device::autodetect_open() +{ + return this; +} - // ----- VERIFY THE CHECKSUM ----- - cs = 0; - for ( int loop = 3; loop < expected - 1; loop++ ) - { - cs += return_buff[loop]; - } +int linux_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - if ( return_buff[expected - 1] != cs ) - { - return set_err(EIO); - } + if(!is_open()) { + if(!open()){ + find_areca_in_proc(); + } + } - sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; - if ( ata_out->status ) - { - if ( in.in_regs.command == ATA_IDENTIFY_DEVICE - && !nonempty((unsigned char *)in.buffer, in.size)) - { - return set_err(ENODEV, "No drive on port %d", m_disknum); - } - } + ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } - // returns with data - if (readdata) - { - memcpy(in.buffer, &return_buff[7], in.size); - } + return ioctlreturn; +} - // Return register values - { - ata_out_regs_48bit & r = out.out_regs; - r.error = ata_out->error; - r.sector_count_16 = ata_out->sector_count; - r.lba_low_16 = ata_out->sector_number; - r.lba_mid_16 = ata_out->cylinder_low; - r.lba_high_16 = ata_out->cylinder_high; - r.status = ata_out->status; - } - return true; +bool linux_areca_scsi_device::arcmsr_lock() +{ + return true; } +bool linux_areca_scsi_device::arcmsr_unlock() +{ + return true; +} ///////////////////////////////////////////////////////////////////////////// /// Marvell support @@ -2317,7 +1993,6 @@ int linux_marvell_device::ata_command_interface(smart_command_set command, int s return 0; } - ///////////////////////////////////////////////////////////////////////////// /// Highpoint RAID support @@ -2550,7 +2225,6 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int return 0; } - #if 0 // TODO: Migrate from 'smart_command_set' to 'ata_in_regs' OR remove the function // Utility function for printing warnings void printwarning(smart_command_set command){ @@ -2582,7 +2256,6 @@ void printwarning(smart_command_set command){ } #endif - ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support @@ -2640,7 +2313,9 @@ smart_device * linux_scsi_device::autodetect_open() } // DELL? - if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8)) { + if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8) + || !memcmp(req_buff + 16, "PERC H700", 9) || !memcmp(req_buff + 8, "LSI\0",4) + ) { close(); set_err(EINVAL, "DELL or MegaRaid controller, please try adding '-d megaraid,N'"); return this; @@ -2676,7 +2351,6 @@ smart_device * linux_scsi_device::autodetect_open() return this; } - ////////////////////////////////////////////////////////////////////// // USB bridge ID detection @@ -2730,7 +2404,6 @@ static bool get_usb_id(const char * name, unsigned short & vendor_id, return true; } - ////////////////////////////////////////////////////////////////////// /// Linux interface @@ -2759,8 +2432,11 @@ protected: private: bool get_dev_list(smart_device_list & devlist, const char * pattern, bool scan_ata, bool scan_scsi, const char * req_type, bool autodetect); - + bool get_dev_megasas(smart_device_list & devlist); smart_device * missing_option(const char * opt); + int megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, + size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp); + int megasas_pd_add_list(int bus_no, smart_device_list & devlist); }; std::string linux_smart_interface::get_os_version_str() @@ -2779,7 +2455,6 @@ std::string linux_smart_interface::get_app_examples(const char * appname) 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 @@ -2876,7 +2551,61 @@ bool linux_smart_interface::get_dev_list(smart_device_list & devlist, // free memory globfree(&globbuf); + return true; +} +// getting devices from LSI SAS MegaRaid, if available +bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist) +{ + /* Scanning of disks on MegaRaid device */ + /* Perform mknod of device ioctl node */ + int mjr, n1; + char line[128]; + bool scan_megasas = false; + FILE * fp = fopen("/proc/devices", "r"); + while (fgets(line, sizeof(line), fp) != NULL) { + n1=0; + if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { + scan_megasas = true; + n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); + if(scsi_debugmode > 0) + pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); + if (n1 >= 0 || errno == EEXIST) + break; + } + } + fclose(fp); + + if(!scan_megasas) + return false; + + // getting bus numbers with megasas devices + struct dirent *ep; + unsigned int host_no = 0; + char sysfsdir[256]; + + /* we are using sysfs to get list of all scsi hosts */ + DIR * dp = opendir ("/sys/class/scsi_host/"); + if (dp != NULL) + { + while ((ep = readdir (dp)) != NULL) { + if (!sscanf(ep->d_name, "host%d", &host_no)) + continue; + /* proc_name should be megaraid_sas */ + snprintf(sysfsdir, sizeof(sysfsdir) - 1, + "/sys/class/scsi_host/host%d/proc_name", host_no); + if((fp = fopen(sysfsdir, "r")) == NULL) + continue; + if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line,"megaraid_sas",12)) { + megasas_pd_add_list(host_no, devlist); + } + fclose(fp); + } + (void) closedir (dp); + } else { /* sysfs not mounted ? */ + for(unsigned i = 0; i <=16; i++) // trying to add devices on first 16 buses + megasas_pd_add_list(i, devlist); + } return true; } @@ -2904,6 +2633,8 @@ bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist, 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); + // get device list from the megaraid device + get_dev_megasas(devlist); } // if we found traditional links, we are done @@ -2931,6 +2662,99 @@ smart_device * linux_smart_interface::missing_option(const char * opt) return 0; } +int +linux_smart_interface::megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, + size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp) +{ + struct megasas_iocpacket ioc; + + if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || + (mbox == NULL && mboxlen != 0)) + { + errno = EINVAL; + return (-1); + } + + bzero(&ioc, sizeof(ioc)); + struct megasas_dcmd_frame * dcmd = &ioc.frame.dcmd; + ioc.host_no = bus_no; + if (mbox) + bcopy(mbox, dcmd->mbox.w, mboxlen); + dcmd->cmd = MFI_CMD_DCMD; + dcmd->timeout = 0; + dcmd->flags = 0; + dcmd->data_xfer_len = bufsize; + dcmd->opcode = opcode; + + if (bufsize > 0) { + dcmd->sge_count = 1; + dcmd->data_xfer_len = bufsize; + dcmd->sgl.sge32[0].phys_addr = (intptr_t)buf; + dcmd->sgl.sge32[0].length = (uint32_t)bufsize; + ioc.sge_count = 1; + ioc.sgl_off = offsetof(struct megasas_dcmd_frame, sgl); + ioc.sgl[0].iov_base = buf; + ioc.sgl[0].iov_len = bufsize; + } + + int fd; + if ((fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) <= 0) { + return (errno); + } + + int r = ioctl(fd, MEGASAS_IOC_FIRMWARE, &ioc); + if (r < 0) { + return (r); + } + + if (statusp != NULL) + *statusp = dcmd->cmd_status; + else if (dcmd->cmd_status != MFI_STAT_OK) { + fprintf(stderr, "command %x returned error status %x\n", + opcode, dcmd->cmd_status); + errno = EIO; + return (-1); + } + return (0); +} + +int +linux_smart_interface::megasas_pd_add_list(int bus_no, smart_device_list & devlist) +{ + /* + * Keep fetching the list in a loop until we have a large enough + * buffer to hold the entire list. + */ + megasas_pd_list * list = 0; + for (unsigned list_size = 1024; ; ) { + list = (megasas_pd_list *)realloc(list, list_size); + if (!list) + throw std::bad_alloc(); + bzero(list, list_size); + if (megasas_dcmd_cmd(bus_no, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 0, + NULL) < 0) + { + free(list); + return (-1); + } + if (list->size <= list_size) + break; + list_size = list->size; + } + + // adding all SCSI devices + for (unsigned i = 0; i < list->count; i++) { + if(list->addr[i].scsi_dev_type) + continue; /* non disk device found */ + char line[128]; + snprintf(line, sizeof(line) - 1, "/dev/bus/%d", bus_no); + smart_device * dev = new linux_megaraid_device(this, line, 0, list->addr[i].device_id); + devlist.push_back(dev); + } + free(list); + return (0); +} + // Return kernel release as integer ("2.6.31" -> 206031) static unsigned get_kernel_release() { @@ -3068,7 +2892,7 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name, set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } - return new linux_areca_device(this, name, disknum, encnum); + return new linux_areca_ata_device(this, name, disknum, encnum); } // Highpoint ? @@ -3107,7 +2931,7 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name, set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } - return new linux_cciss_device(this, name, disknum); + return get_sat_device("sat,auto", new linux_cciss_device(this, name, disknum)); } #endif // HAVE_LINUX_CCISS_IOCTL_H @@ -3129,7 +2953,6 @@ std::string linux_smart_interface::get_valid_custom_dev_types_str() } // namespace - ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() diff --git a/os_linux.h b/os_linux.h index 5fabc04..3cb2f33 100644 --- a/os_linux.h +++ b/os_linux.h @@ -24,8 +24,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -38,7 +38,7 @@ #ifndef OS_LINUX_H_ #define OS_LINUX_H_ -#define OS_LINUX_H_CVSID "$Id: os_linux.h,v 1.27 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_LINUX_H_CVSID "$Id: os_linux.h 3728 2012-12-13 17:57:50Z chrfranke $\n" /* The following definitions/macros/prototypes are used for three diff --git a/os_netbsd.cpp b/os_netbsd.cpp index 09d4a7e..dce2d9d 100644 --- a/os_netbsd.cpp +++ b/os_netbsd.cpp @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ @@ -26,7 +26,7 @@ #include #include -const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 3265 2011-02-21 16:21:14Z chrfranke $" +const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 3806 2013-03-29 20:17:03Z chrfranke $" OS_NETBSD_H_CVSID; /* global variable holding byte count of allocated memory */ @@ -169,14 +169,6 @@ deviceclose(int fd) return close(fd); } -int -marvell_command_interface(int fd, smart_command_set command, int select, char *data) -{ return -1; } - -int -highpoint_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) { @@ -345,20 +337,6 @@ ata_command_interface(int fd, smart_command_set command, int select, char *data) 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 -areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data) -{ - printwarning(NO_ARECA, NULL); - return -1; -} - int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { diff --git a/os_netbsd.h b/os_netbsd.h index 7721815..6b8c758 100644 --- a/os_netbsd.h +++ b/os_netbsd.h @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -24,7 +24,7 @@ #ifndef OS_NETBSD_H_ #define OS_NETBSD_H_ -#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h,v 1.10 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 3728 2012-12-13 17:57:50Z chrfranke $\n" #include #include diff --git a/os_openbsd.cpp b/os_openbsd.cpp index 7acf85a..896b88d 100644 --- a/os_openbsd.cpp +++ b/os_openbsd.cpp @@ -13,8 +13,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ @@ -27,7 +27,7 @@ #include -const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 3265 2011-02-21 16:21:14Z chrfranke $" +const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 3727 2012-12-13 17:23:06Z samm2 $" OS_OPENBSD_H_CVSID; /* global variable holding byte count of allocated memory */ @@ -173,14 +173,6 @@ deviceclose(int fd) return close(fd); } -int -marvell_command_interface(int fd, smart_command_set command, int select, char *data) -{ return -1; } - -int -highpoint_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) { @@ -359,20 +351,6 @@ ata_command_interface(int fd, smart_command_set command, int select, char *data) 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 -areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data) -{ - printwarning(NO_ARECA, NULL); - return -1; -} - int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { diff --git a/os_openbsd.h b/os_openbsd.h index be1553d..b686c78 100644 --- a/os_openbsd.h +++ b/os_openbsd.h @@ -13,8 +13,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -26,7 +26,7 @@ #ifndef OS_OPENBSD_H_ #define OS_OPENBSD_H_ -#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h,v 1.6 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 3728 2012-12-13 17:57:50Z chrfranke $\n" /* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */ /* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */ diff --git a/os_os2.cpp b/os_os2.cpp index 16f4e2e..7ad7f19 100644 --- a/os_os2.cpp +++ b/os_os2.cpp @@ -11,8 +11,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* @@ -31,7 +31,7 @@ #include "os_os2.h" // Needed by '-V' option (CVS versioning) of smartd/smartctl -const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 3191 2010-10-27 19:55:33Z chrfranke $" \ +const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \ ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; // global handle to device driver @@ -522,31 +522,6 @@ int ata_command_interface(int device, smart_command_set command, int select, cha return 0; } -int marvell_command_interface(int fd, smart_command_set command, int select, char *data){ - unsupported(1); - return -1; -} - -int highpoint_command_interface(int fd, smart_command_set command, int select, char *data) -{ - unsupported(1); - 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(2); - return -1; -} - -int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data){ - unsupported(2); - return -1; -} - // Interface to SCSI devices. See os_linux.c int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { unsupported(3); diff --git a/os_os2.h b/os_os2.h index 57d3ad3..3d8cb86 100644 --- a/os_os2.h +++ b/os_os2.h @@ -11,14 +11,14 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef OS_OS2_H_ #define OS_OS2_H_ -#define OS_XXXX_H_CVSID "$Id: os_os2.h,v 1.5 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_XXXX_H_CVSID "$Id: os_os2.h 3728 2012-12-13 17:57:50Z chrfranke $\n" // Additional material should start here. Note: to keep the '-V' CVS // reporting option working as intended, you should only #include diff --git a/os_qnxnto.cpp b/os_qnxnto.cpp index 7dbcafe..0f039f4 100644 --- a/os_qnxnto.cpp +++ b/os_qnxnto.cpp @@ -13,12 +13,13 @@ // This is to include whatever structures and prototypes you define in // os_generic.h #include "os_qnxnto.h" +#include // Needed by '-V' option (CVS versioning) of smartd/smartctl. You // should have one *_H_CVSID macro appearing below for each file // appearing with #include "*.h" above. Please list these (below) in // alphabetic/dictionary order. -const char *os_XXXX_c_cvsid="$Id: os_qnxnto.cpp 3191 2010-10-27 19:55:33Z chrfranke $" \ +const char *os_XXXX_c_cvsid="$Id: os_qnxnto.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_QNXNTO_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; @@ -370,57 +371,6 @@ int status,rc; return(rc); } //---------------------------------------------------------------------------------------------- -int marvell_command_interface(int fd, smart_command_set command, int select, char *data) -{ - ARGUSED(fd); - ARGUSED(command); - ARGUSED(select); - ARGUSED(data); - unsupported(); - return -1; -} -//---------------------------------------------------------------------------------------------- -int highpoint_command_interface(int fd, smart_command_set command, int select, char *data) -{ - ARGUSED(fd); - ARGUSED(command); - ARGUSED(select); - ARGUSED(data); - unsupported(); - return -1; -} -//---------------------------------------------------------------------------------------------- -// Interface to ATA devices behind 3ware escalade/apache RAID -// controller cards. Same description as ata_command_interface() -// above except that 0 <= disknum <= 15 specifies the ATA disk -// attached to the controller, and controller_type specifies the -// precise type of 3ware controller. See os_linux.c -int escalade_command_interface(int fd,int disknum,int controller_type,smart_command_set command,int select,char *data) -{ - ARGUSED(fd); - ARGUSED(disknum); - ARGUSED(controller_type); - ARGUSED(command); - ARGUSED(select); - ARGUSED(data); - - unsupported(); - return -1; -} - -int areca_command_interface(int fd,int disknum,smart_command_set command,int select,char *data) -{ - ARGUSED(fd); - ARGUSED(disknum); - ARGUSED(command); - ARGUSED(select); - ARGUSED(data); - - unsupported(); - return -1; -} -//---------------------------------------------------------------------------------------------- -#include // Interface to SCSI devices. See os_linux.c int do_scsi_cmnd_io(int fd,struct scsi_cmnd_io * iop,int report) { diff --git a/os_qnxnto.h b/os_qnxnto.h index 2feb235..3b12cd4 100644 --- a/os_qnxnto.h +++ b/os_qnxnto.h @@ -12,8 +12,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -23,7 +23,7 @@ */ #ifndef OS_QNXNTO_H_ #define OS_QNXNTO_H_ -#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h,v 1.2 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 3728 2012-12-13 17:57:50Z chrfranke $\n" // Additional material should start here. Note: to keep the '-V' CVS // reporting option working as intended, you should only #include diff --git a/os_solaris.cpp b/os_solaris.cpp index b36f700..793aec7 100644 --- a/os_solaris.cpp +++ b/os_solaris.cpp @@ -12,8 +12,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ @@ -39,9 +39,9 @@ extern long long bytes; -static const char *filenameandversion="$Id: os_solaris.cpp,v 1.32 2008/06/12 21:46:31 ballen4705 Exp $"; +static const char *filenameandversion="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $"; -const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp,v 1.32 2008/06/12 21:46:31 ballen4705 Exp $" \ +const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; // The printwarning() function warns about unimplemented functions @@ -285,17 +285,6 @@ static void swap_sector(void *p) #endif // 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 highpoint_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; @@ -352,25 +341,6 @@ int ata_command_interface(int fd, smart_command_set command, int select, char *d 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){ - ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); - ARGUSED(command); ARGUSED(select); ARGUSED(data); - - if (printwarning(1)) - return -1; - return -1; -} - -int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data){ - ARGUSED(fd); ARGUSED(disknum); - ARGUSED(command); ARGUSED(select); ARGUSED(data); - - if (printwarning(1)) - return -1; - return -1; -} - #include #include #include diff --git a/os_solaris.h b/os_solaris.h index 4813a92..70dc3ee 100644 --- a/os_solaris.h +++ b/os_solaris.h @@ -12,8 +12,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -25,7 +25,7 @@ #ifndef OS_SOLARIS_H_ #define OS_SOLARIS_H_ -#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h,v 1.14 2008/03/04 22:09:47 ballen4705 Exp $\n" +#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 3728 2012-12-13 17:57:50Z chrfranke $\n" // Additional material should start here. Note: to keep the '-V' CVS // reporting option working as intended, you should only #include diff --git a/os_solaris_ata.s b/os_solaris_ata.s index 8f01694..fc502b9 100644 --- a/os_solaris_ata.s +++ b/os_solaris_ata.s @@ -16,8 +16,8 @@ ! General Public License for more details. ! ! You should have received a copy of the GNU General Public License -! along with this program; if not, write to the Free Software -! Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +! along with this program; if not, write to the Free Software Foundation, +! Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ! ! ! -------------------------------------------------------- @@ -97,7 +97,7 @@ .section ".rodata" .align 8 .LLC0: - .asciz "$Id: os_solaris_ata.s,v 1.6 2008/03/04 22:09:47 ballen4705 Exp $" + .asciz "$Id: os_solaris_ata.s 3728 2012-12-13 17:57:50Z chrfranke $" .global os_solaris_ata_s_cvsid .section ".data" .align 4 diff --git a/os_win32.cpp b/os_win32.cpp index ace6d32..6c5fc51 100644 --- a/os_win32.cpp +++ b/os_win32.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-12 Christian Franke + * Copyright (C) 2004-13 Christian Franke * Copyright (C) 2012 Hank Wu * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #include "os_win32/wmiquery.h" @@ -48,14 +49,14 @@ #include #if HAVE_NTDDDISK_H -// i686-w64-mingw32, x86_64-w64-mingw32 +// i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32 // (Missing: FILE_DEVICE_SCSI) #include #include #include #include #elif HAVE_DDK_NTDDDISK_H -// i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc +// older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI) #include #include @@ -67,18 +68,26 @@ #include #endif +#ifndef _WIN32 +// csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin +#define _WIN32 +#endif + // CSMI support #include "csmisas.h" -#ifdef __CYGWIN__ -#include // CYGWIN_VERSION_DLL_MAJOR +// Silence -Wunused-local-typedefs warning from g++ >= 4.8 +#if __GNUC__ >= 4 +#define ATTR_UNUSED __attribute__((unused)) +#else +#define ATTR_UNUSED /**/ #endif // Macro to check constants at compile time using a dummy typedef #define ASSERT_CONST(c, n) \ - typedef char assert_const_##c[((c) == (n)) ? 1 : -1] + typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED #define ASSERT_SIZEOF(t, n) \ - typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] + typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED #ifndef _WIN64 #define SELECT_WIN_32_64(x32, x64) (x32) @@ -86,20 +95,7 @@ #define SELECT_WIN_32_64(x32, x64) (x64) #endif -const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3558 2012-06-05 16:42:05Z chrfranke $"; - -// Disable Win9x/ME specific code if no longer supported by compiler. -#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) - // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0) - #else - #define WIN9X_SUPPORT 1 - #endif -#endif +const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3804 2013-03-27 20:39:41Z chrfranke $"; ///////////////////////////////////////////////////////////////////////////// // Windows I/O-controls, some declarations are missing in the include files @@ -320,16 +316,6 @@ namespace os_win32 { // no need to publish anything, name provided for Doxygen #pragma warning(disable:4250) #endif -// Running on Win9x/ME ? -#if WIN9X_SUPPORT -// Set true in win9x_smart_interface ctor. -static bool win9x = false; -#else -// Never true (const allows compiler to remove dead code). -const bool win9x = false; -#endif - - class win_smart_device : virtual public /*implements*/ smart_device { @@ -382,9 +368,10 @@ private: std::string m_options; bool m_usr_options; // options set by user? bool m_admin; // open with admin access? + int m_phydrive; // PhysicalDriveN or -1 bool m_id_is_cached; // ata_identify_is_cached() return value. - bool m_is_3ware; // AMCC/3ware controller detected? - int m_drive, m_port; + bool m_is_3ware; // LSI/3ware controller detected? + int m_port; // LSI/3ware port int m_smartver_state; }; @@ -409,32 +396,6 @@ private: ///////////////////////////////////////////////////////////////////////////// -#if WIN9X_SUPPORT - -class win_aspi_device -: public /*implements*/ scsi_device -{ -public: - win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type); - - virtual bool is_open() const; - - virtual bool open(); - - virtual bool close(); - - virtual bool scsi_pass_through(scsi_cmnd_io * iop); - -private: - int m_adapter; - unsigned char m_id; -}; - -#endif // WIN9X_SUPPORT - - -////////////////////////////////////////////////////////////////////// - class csmi_device : virtual public /*extends*/ smart_device { @@ -535,58 +496,46 @@ private: ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support -/* ARECA IO CONTROL CODE*/ -#define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004 -#define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008 -#define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C -#define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010 -#define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018 -#define ARECA_SIG_STR "ARCMSR" - - -// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver -typedef struct _SRB_IO_CONTROL -{ - unsigned int HeaderLength; - unsigned char Signature[8]; - unsigned int Timeout; - unsigned int ControlCode; - unsigned int ReturnCode; - unsigned int Length; -} sSRB_IO_CONTROL; - -typedef struct _SRB_BUFFER -{ - sSRB_IO_CONTROL srbioctl; - unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware -} sSRB_BUFFER; - -class win_areca_device -: public /*implements*/ ata_device, +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class win_areca_ata_device +: public /*implements*/ areca_ata_device, public /*extends*/ win_smart_device { public: - win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum = 1); - - static int arcmsr_command_handler(HANDLE fh, unsigned long arcmsr_cmd, unsigned char *data, int data_len); - -protected: + win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual bool open(); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); - virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); +private: + HANDLE m_mutex; +}; - bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class win_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ win_smart_device +{ +public: + win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual bool open(); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); private: - int m_disknum; ///< Disk number. - int m_encnum; ///< Enclosure number. + HANDLE m_mutex; }; ////////////////////////////////////////////////////////////////////// -// Platform specific interfaces +// Platform specific interface -// Common to all windows flavors class win_smart_interface : public /*implements part of*/ smart_interface { @@ -599,52 +548,14 @@ public: virtual int64_t get_timer_usec(); #endif -//virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, -// const char * pattern = 0); - -protected: - virtual ata_device * get_ata_device(const char * name, const char * type); - -//virtual scsi_device * get_scsi_device(const char * name, const char * type); - - virtual smart_device * autodetect_smart_device(const char * name); -}; - -#if WIN9X_SUPPORT - -// Win9x/ME reduced functionality -class win9x_smart_interface -: public /*extends*/ win_smart_interface -{ -public: - win9x_smart_interface() - { win9x = true; } - - virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, - const char * pattern = 0); - -protected: - virtual scsi_device * get_scsi_device(const char * name, const char * type); - -private: - bool ata_scan(smart_device_list & devlist); - - bool scsi_scan(smart_device_list & devlist); -}; - -#endif // WIN9X_SUPPORT - -// WinNT,2000,XP,... -class winnt_smart_interface -: public /*extends*/ win_smart_interface -{ -public: virtual bool disable_system_auto_standby(bool disable); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: + virtual ata_device * get_ata_device(const char * name, const char * type); + virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual smart_device * autodetect_smart_device(const char * name); @@ -721,7 +632,7 @@ std::string win_smart_interface::get_os_version_str() : "2008r2"); break; case VER_PLATFORM_WIN32_NT <<16|0x0600| 2: w = (vi.wProductType == VER_NT_WORKSTATION ? "win8" - : "win8s"); break; + : "2012"); break; default: w = 0; break; } @@ -732,9 +643,9 @@ std::string win_smart_interface::get_os_version_str() #endif if (!w) - snprintf(vptr, vlen, "-%s%lu.%lu%s", + snprintf(vptr, vlen, "-%s%u.%u%s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"), - vi.dwMajorVersion, vi.dwMinorVersion, w64); + (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); else if (vi.wServicePackMinor) snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor); else if (vi.wServicePackMajor) @@ -819,25 +730,17 @@ ata_device * win_smart_interface::get_ata_device(const char * name, const char * return new win_ata_device(this, name, type); } -#ifdef WIN9X_SUPPORT - -scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type) +scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type) { - return new win_aspi_device(this, name, type); + return new win_scsi_device(this, name, type); } -#endif - -scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type) +static int sdxy_to_phydrive(const char (& xy)[2+1]) { - const char * testname = skipdev(name); - if (!strncmp(testname, "scsi", 4)) -#if WIN9X_SUPPORT - return new win_aspi_device(this, name, type); -#else - return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0); -#endif - return new win_scsi_device(this, name, type); + int phydrive = xy[0] - 'a'; + if (xy[1]) + phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a'); + return phydrive; } static win_dev_type get_dev_type(const char * name, int & phydrive) @@ -857,9 +760,9 @@ static win_dev_type get_dev_type(const char * name, int & phydrive) return (type != DEV_UNKNOWN ? type : DEV_SCSI); } - char drive[1+1] = ""; - if (sscanf(name, "sd%1[a-z]", drive) == 1) { - phydrive = drive[0] - 'a'; + char drive[2+1] = ""; + if (sscanf(name, "sd%2[a-z]", drive) == 1) { + phydrive = sdxy_to_phydrive(drive); return get_phy_drive_type(phydrive); } @@ -869,27 +772,11 @@ static win_dev_type get_dev_type(const char * name, int & phydrive) return DEV_UNKNOWN; } -smart_device * win_smart_interface::autodetect_smart_device(const char * name) -{ - const char * testname = skipdev(name); - if (!strncmp(testname, "hd", 2)) - return new win_ata_device(this, name, ""); -#if WIN9X_SUPPORT - if (!strncmp(testname, "scsi", 4)) - return new win_aspi_device(this, name, ""); -#endif - if (!strncmp(testname, "tw_cli", 6)) - return new win_tw_cli_device(this, name, ""); - return 0; -} - - -smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, const char * type) +smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type) { // Areca? int disknum = -1, n1 = -1, n2 = -1; int encnum = 1; - HANDLE fh = INVALID_HANDLE_VALUE; char devpath[32]; if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { @@ -911,18 +798,16 @@ smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and 2. map arcmsrX into "\\\\.\\scsiX" */ - for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) { + for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) { memset(devpath, 0, sizeof(devpath)); - sprintf(devpath, "\\\\.\\scsi%d:", idx); - if ( (fh = CreateFile( devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE ) { - if (win_areca_device::arcmsr_command_handler(fh, ARCMSR_IOCTL_RETURN_CODE_3F, NULL, 0) == 0) { - if (ctlrindex-- == 0) { - return new win_areca_device(this, devpath, fh, disknum, encnum); - } + snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx); + win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum); + if(arcdev->arcmsr_probe()) { + if(ctlrindex-- == 0) { + return arcdev; } - CloseHandle(fh); } + delete arcdev; } set_err(ENOENT, "No Areca controller found"); } @@ -933,19 +818,22 @@ smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, return 0; } -std::string winnt_smart_interface::get_valid_custom_dev_types_str() +std::string win_smart_interface::get_valid_custom_dev_types_str() { return "areca,N[/E]"; } -smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) +smart_device * win_smart_interface::autodetect_smart_device(const char * name) { - smart_device * dev = win_smart_interface::autodetect_smart_device(name); - if (dev) - return dev; + const char * testname = skipdev(name); + if (str_starts_with(testname, "hd")) + return new win_ata_device(this, name, ""); + + if (str_starts_with(testname, "tw_cli")) + return new win_tw_cli_device(this, name, ""); - if (!strncmp(skipdev(name), "csmi", 4)) + if (str_starts_with(testname, "csmi")) return new win_csmi_device(this, name, ""); int phydrive = -1; @@ -975,11 +863,9 @@ smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) } -#if WIN9X_SUPPORT - -// Scan for devices on Win9x/ME +// Scan for devices -bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist, +bool win_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /* = 0*/) { if (pattern) { @@ -987,29 +873,20 @@ bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist, return false; } - if (!type || !strcmp(type, "ata")) { - if (!ata_scan(devlist)) - return false; - } - - if (!type || !strcmp(type, "scsi")) { - if (!scsi_scan(devlist)) - return false; - } - return true; -} - -#endif // WIN9X_SUPPORT - - -// Scan for devices - -bool winnt_smart_interface::scan_smart_devices(smart_device_list & devlist, - const char * type, const char * pattern /* = 0*/) -{ - if (pattern) { - set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); - return false; + // Check for "[*,]pd" type + bool pd = false; + char type2[16+1] = ""; + if (type) { + int nc = -1; + if (!strcmp(type, "pd")) { + pd = true; + type = 0; + } + else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 && + nc == (int)strlen(type)) { + pd = true; + type = type2; + } } // Set valid types @@ -1028,78 +905,89 @@ bool winnt_smart_interface::scan_smart_devices(smart_device_list & devlist, else if (!strcmp(type, "csmi")) csmi = true; else { - set_err(EINVAL, "Invalid type '%s', valid arguments are: ata, scsi, usb, csmi", type); + set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], usb[,pd], csmi, pd", type); return false; } } - // Scan up to 10 drives and 2 3ware controllers - const int max_raid = 2; - bool raid_seen[max_raid] = {false, false}; - char name[20]; - for (int i = 0; i <= 9; i++) { - sprintf(name, "/dev/sd%c", 'a'+i); - GETVERSIONINPARAMS_EX vers_ex; - - switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) { - case DEV_ATA: - // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA - if (!ata) - continue; - // Interpret RAID drive map if present - if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) { - // Skip if too many controllers or logical drive from this controller already seen - if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId])) + if (ata || scsi || usb) { + // Scan up to 128 drives and 2 3ware controllers + const int max_raid = 2; + bool raid_seen[max_raid] = {false, false}; + + for (int i = 0; i < 128; i++) { + if (pd) + snprintf(name, sizeof(name), "/dev/pd%d", i); + else if (i + 'a' <= 'z') + snprintf(name, sizeof(name), "/dev/sd%c", i + 'a'); + else + snprintf(name, sizeof(name), "/dev/sd%c%c", + i / ('z'-'a'+1) - 1 + 'a', + i % ('z'-'a'+1) + 'a'); + + GETVERSIONINPARAMS_EX vers_ex; + + switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) { + case DEV_ATA: + // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA + if (!ata) continue; - raid_seen[vers_ex.wControllerId] = true; - // Add physical drives - int len = strlen(name); - for (int pi = 0; pi < 32; pi++) { - if (vers_ex.dwDeviceMapEx & (1L << pi)) { - sprintf(name+len, ",%u", pi); - devlist.push_back( new win_ata_device(this, name, "ata") ); + + // Interpret RAID drive map if present + if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) { + // Skip if too many controllers or logical drive from this controller already seen + if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId])) + continue; + raid_seen[vers_ex.wControllerId] = true; + // Add physical drives + int len = strlen(name); + for (int pi = 0; pi < 32; pi++) { + if (vers_ex.dwDeviceMapEx & (1L << pi)) { + snprintf(name+len, sizeof(name)-1-len, ",%u", pi); + devlist.push_back( new win_ata_device(this, name, "ata") ); + } } } - } - else { - devlist.push_back( new win_ata_device(this, name, "ata") ); - } - break; - - case DEV_SCSI: - // STORAGE_QUERY_PROPERTY returned SCSI/SAS/... - if (!scsi) - continue; - devlist.push_back( new win_scsi_device(this, name, "scsi") ); - break; + else { + devlist.push_back( new win_ata_device(this, name, "ata") ); + } + break; - case DEV_USB: - // STORAGE_QUERY_PROPERTY returned USB - if (!usb) - continue; - { - // TODO: Use common function for this and autodetect_smart_device() - // Get USB bridge ID - unsigned short vendor_id = 0, product_id = 0; - if (!get_usb_id(i, vendor_id, product_id)) - continue; - // Get type name for this ID - const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id); - if (!usbtype) + case DEV_SCSI: + // STORAGE_QUERY_PROPERTY returned SCSI/SAS/... + if (!scsi) continue; - // Return SAT/USB device for this type - ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, "")); - if (!dev) + devlist.push_back( new win_scsi_device(this, name, "scsi") ); + break; + + case DEV_USB: + // STORAGE_QUERY_PROPERTY returned USB + if (!usb) continue; - devlist.push_back(dev); - } - break; + { + // TODO: Use common function for this and autodetect_smart_device() + // Get USB bridge ID + unsigned short vendor_id = 0, product_id = 0; + if (!get_usb_id(i, vendor_id, product_id)) + continue; + // Get type name for this ID + const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id); + if (!usbtype) + continue; + // Return SAT/USB device for this type + ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, "")); + if (!dev) + continue; + devlist.push_back(dev); + } + break; - default: - // Unknown type - break; + default: + // Unknown type + break; + } } } @@ -1132,20 +1020,16 @@ std::string win_smart_interface::get_app_examples(const char * appname) if (strcmp(appname, "smartctl")) return ""; return "=================================================== SMARTCTL EXAMPLES =====\n\n" - " smartctl -a /dev/hda (Prints all SMART information)\n\n" - " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " smartctl -a /dev/sda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\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" + " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\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" + " (Prints all information for disk on PhysicalDrive 0)\n" " smartctl -a /dev/pd3\n" - " (Prints all information for SCSI disk on PhysicalDrive 3)\n" + " (Prints all information for disk on PhysicalDrive 3)\n" " smartctl -a /dev/tape1\n" " (Prints all information for SCSI tape on Tape 1)\n" " smartctl -A /dev/hdb,3\n" @@ -1159,15 +1043,15 @@ std::string win_smart_interface::get_app_examples(const char * appname) " ATA SMART access methods and ordering may be specified by modifiers\n" " following the device name: /dev/hdX:[saicm], where\n" " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n" - " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n" - " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n" + " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n" + " 'm': IOCTL_SCSI_MINIPORT_*.\n" + strprintf( " The default on this system is /dev/sdX:%s\n", ata_get_def_options() ); } -bool winnt_smart_interface::disable_system_auto_standby(bool disable) +bool win_smart_interface::disable_system_auto_standby(bool disable) { if (disable) { SYSTEM_POWER_STATUS ps; @@ -1199,8 +1083,8 @@ bool winnt_smart_interface::disable_system_auto_standby(bool disable) 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", - (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, - r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); + (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, + r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); } static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro) @@ -1224,20 +1108,20 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version if (!DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { if (ata_debugmode) - pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError()); + pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } assert(num_out == sizeof(GETVERSIONINPARAMS)); if (ata_debugmode > 1) { - pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n" - " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n", - num_out, vers.bVersion, vers.bRevision, - vers.fCapabilities, vers.bIDEDeviceMap); + pout(" SMART_GET_VERSION suceeded, bytes returned: %u\n" + " Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n", + (unsigned)num_out, vers.bVersion, vers.bRevision, + (unsigned)vers.fCapabilities, vers.bIDEDeviceMap); if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) - pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n", - vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx); + pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n", + vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx); } if (ata_version_ex) @@ -1250,7 +1134,7 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version // call SMART_* ioctl -static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port) +static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port) { SENDCMDINPARAMS inpar; SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar; @@ -1263,9 +1147,14 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u 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; + + // Older drivers may require bits 5 and 7 set + // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete + inpar.irDriveRegs.bDriveHeadReg |= 0xa0; + + // Drive number 0-3 was required on Win9x/ME only + //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4; + //inpar.bDriveNumber = drive; if (port >= 0) { // Set RAID port @@ -1294,7 +1183,7 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u 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() + // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through() long err = GetLastError(); if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) { pout(" %s failed, Error=%ld\n", name, err); @@ -1320,8 +1209,8 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u } if (ata_debugmode > 1) { - pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name, - num_out, outpar->cBufferSize); + pout(" %s suceeded, bytes returned: %u (buffer %u)\n", name, + (unsigned)num_out, (unsigned)outpar->cBufferSize); print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ? (const IDEREGS *)(outpar->bBuffer) : NULL)); } @@ -1397,8 +1286,8 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u if ( num_out != size || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) { if (ata_debugmode) { - pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n", - num_out, buf->DataBufferSize); + pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n", + (unsigned)num_out, (unsigned)buf->DataBufferSize); print_ide_regs_io(regs, &buf->IdeReg); } VirtualFree(buf, 0, MEM_RELEASE); @@ -1409,8 +1298,8 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u } if (ata_debugmode > 1) { - pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n", - num_out, buf->DataBufferSize); + pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n", + (unsigned)num_out, (unsigned)buf->DataBufferSize); print_ide_regs_io(regs, &buf->IdeReg); } *regs = buf->IdeReg; @@ -1516,7 +1405,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev if ( num_out != size || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) { if (ata_debugmode) { - pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out); + pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } errno = EIO; @@ -1526,7 +1415,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev } if (ata_debugmode > 1) { - pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out); + pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } *regs = *ctfregs; @@ -1537,91 +1426,6 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev } -///////////////////////////////////////////////////////////////////////////// -// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only) - -// undocumented SCSI opcode to for ATA passthrough -#define SCSIOP_ATA_PASSTHROUGH 0xCC - -static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) -{ - typedef struct { - SCSI_PASS_THROUGH spt; - ULONG Filler; - UCHAR ucSenseBuf[32]; - UCHAR ucDataBuf[512]; - } SCSI_PASS_THROUGH_WITH_BUFFERS; - - SCSI_PASS_THROUGH_WITH_BUFFERS sb; - IDEREGS * cdbregs; - unsigned int size; - DWORD num_out; - const unsigned char magic = 0xcf; - - memset(&sb, 0, sizeof(sb)); - sb.spt.Length = sizeof(SCSI_PASS_THROUGH); - //sb.spt.PathId = 0; - sb.spt.TargetId = 1; - //sb.spt.Lun = 0; - sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24; - sb.spt.TimeOutValue = 10; - sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); - size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf); - sb.spt.DataBufferOffset = size; - - if (datasize) { - if (datasize > sizeof(sb.ucDataBuf)) { - errno = EINVAL; - return -1; - } - sb.spt.DataIn = SCSI_IOCTL_DATA_IN; - sb.spt.DataTransferLength = datasize; - size += datasize; - sb.ucDataBuf[0] = magic; - } - else { - sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; - //sb.spt.DataTransferLength = 0; - } - - // Use pseudo SCSI command followed by registers - sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH; - cdbregs = (IDEREGS *)(sb.spt.Cdb+2); - *cdbregs = *regs; - - if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH, - &sb, size, &sb, size, &num_out, NULL)) { - long err = GetLastError(); - if (ata_debugmode) - pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err); - errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); - return -1; - } - - // Cannot check ATA status, because command does not return IDEREGS - - // Check and copy data - if (datasize) { - if ( num_out != size - || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) { - if (ata_debugmode) { - pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out); - print_ide_regs_io(regs, NULL); - } - errno = EIO; - return -1; - } - memcpy(data, sb.ucDataBuf, datasize); - } - - if (ata_debugmode > 1) { - pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out); - print_ide_regs_io(regs, NULL); - } - return 0; -} - - ///////////////////////////////////////////////////////////////////////////// // SMART IOCTL via SCSI MINIPORT ioctl @@ -1724,7 +1528,7 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha // Check result if (sb.srbc.ReturnCode) { if (ata_debugmode) { - pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode); + pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode); print_ide_regs_io(regs, NULL); } errno = EIO; @@ -1742,8 +1546,8 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha } if (ata_debugmode > 1) { - pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name, - num_out, sb.params.out.cBufferSize); + pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name, + (unsigned)num_out, (unsigned)sb.params.out.cBufferSize); print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ? (const IDEREGS *)(sb.params.out.bBuffer) : 0)); } @@ -1775,7 +1579,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d return -1; } memset(&sb, 0, sizeof(sb)); - strcpy((char *)sb.srbc.Signature, "<3ware>"); + strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature)); sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL); sb.srbc.Timeout = 60; // seconds sb.srbc.ControlCode = 0xA0000000; @@ -1798,7 +1602,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d if (sb.srbc.ReturnCode) { if (ata_debugmode) { - pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode); + pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode); print_ide_regs_io(regs, NULL); } errno = EIO; @@ -1810,7 +1614,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d memcpy(data, sb.buffer, datasize); if (ata_debugmode > 1) { - pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out); + pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, &sb.regs); } *regs = sb.regs; @@ -1828,7 +1632,7 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice) { SRB_IO_CONTROL srbc; memset(&srbc, 0, sizeof(srbc)); - strcpy((char *)srbc.Signature, "<3ware>"); + strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature)); srbc.HeaderLength = sizeof(SRB_IO_CONTROL); srbc.Timeout = 60; // seconds srbc.ControlCode = 0xCC010014; @@ -1846,7 +1650,7 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice) } if (srbc.ReturnCode) { if (ata_debugmode) - pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode); + pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode); errno = EIO; return -1; } @@ -2135,7 +1939,7 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) { if (ata_debugmode > 1 || scsi_debugmode > 1) - pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError()); + pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } @@ -2173,16 +1977,16 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0) if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE, 0, 0, &pred, sizeof(pred), &num_out, NULL)) { if (ata_debugmode > 1) - pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError()); + pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } if (ata_debugmode > 1) { pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n" - " PredictFailure: 0x%08lx\n" + " PredictFailure: 0x%08x\n" " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n", - pred.PredictFailure, + (unsigned)pred.PredictFailure, pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2], pred.VendorSpecific[sizeof(pred.VendorSpecific)-1] ); @@ -2330,6 +2134,31 @@ static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device return 0; } +// Get Serial Number in IDENTIFY from WMI +static bool get_serial_from_wmi(int drive, ata_identify_device * id) +{ + bool debug = (ata_debugmode > 1); + + wbem_services ws; + if (!ws.connect()) { + if (debug) + pout("WMI connect failed\n"); + return false; + } + + wbem_object wo; + if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE " + "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive)) + return false; + + std::string serial = wo.get_str("SerialNumber"); + if (debug) + pout(" WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str()); + + copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no)); + return true; +} + ///////////////////////////////////////////////////////////////////////////// // USB ID detection using WMI @@ -2416,7 +2245,7 @@ static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & p continue; } - // Fail if previos USB bridge is associated to other controller or ID is unknown + // Fail if previous USB bridge is associated to other controller or ID is unknown if (!(ant == prev_usb_ant && prev_usb_venid)) { if (debug) pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str()); @@ -2459,43 +2288,14 @@ static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & p ///////////////////////////////////////////////////////////////////////////// -// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003) +// Call GetDevicePowerState() // returns: 1=active, 0=standby, -1=error // (This would also work for SCSI drives) static int get_device_power_state(HANDLE hdevice) { - static bool unsupported = false; - if (unsupported) { - errno = ENOSYS; - return -1; - } - -#ifdef __CYGWIN__ - static DWORD kernel_dll_pid = 0; -#endif - static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0; - - if (!GetDevicePowerState_p -#ifdef __CYGWIN__ - || kernel_dll_pid != GetCurrentProcessId() // detect fork() -#endif - ) { - if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *)) - GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) { - if (ata_debugmode) - pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError()); - unsupported = true; - errno = ENOSYS; - return -1; - } -#ifdef __CYGWIN__ - kernel_dll_pid = GetCurrentProcessId(); -#endif - } - BOOL state = TRUE; - if (!GetDevicePowerState_p(hdevice, &state)) { + if (!GetDevicePowerState(hdevice, &state)) { long err = GetLastError(); if (ata_debugmode) pout(" GetDevicePowerState() failed, Error=%ld\n", err); @@ -2513,68 +2313,12 @@ static int get_device_power_state(HANDLE hdevice) ///////////////////////////////////////////////////////////////////////////// -#if WIN9X_SUPPORT -// 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; - // SMARTVSD.VXD present? - strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD"); - if (!access(path, 0)) { - // Yes, standard IDE driver used? - HANDLE h; - if ( (h = CreateFileA("\\\\.\\ESDI_506", - GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE - && GetLastError() == ERROR_FILE_NOT_FOUND ) { - pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n"); - return ENOENT; - } - else { - if (h != INVALID_HANDLE_VALUE) // should not happen - CloseHandle(h); - pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n"); - return ENOSYS; - } - } - else { - strcpy(path+len, "\\SMARTVSD.VXD"); - if (!access(path, 0)) { - // Some Windows versions install SMARTVSD.VXD in SYSTEM directory - // (http://support.microsoft.com/kb/265854/en-us). - 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); - } - else { - // Some Windows versions do not provide SMARTVSD.VXD - // (http://support.microsoft.com/kb/199886/en-us). - path[len] = 0; - pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path); - } - return ENOSYS; - } -} - -#endif // WIN9X_SUPPORT - // Get default ATA device options static const char * ata_get_def_options() { - DWORD ver = GetVersion(); - if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME - return "s"; // SMART_* only - else if ((ver & 0xff) == 4) // WinNT4 - return "sc"; // SMART_*, SCSI_PASS_THROUGH - else // WinXP, 2003, Vista - return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH, - // STORAGE_*, SCSI_MINIPORT_* + return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH, + // STORAGE_*, SCSI_MINIPORT_* } @@ -2606,9 +2350,9 @@ win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, co : smart_device(intf, dev_name, "ata", req_type), m_usr_options(false), m_admin(false), + m_phydrive(-1), m_id_is_cached(false), m_is_3ware(false), - m_drive(0), m_port(-1), m_smartver_state(0) { @@ -2624,18 +2368,18 @@ win_ata_device::~win_ata_device() throw() bool win_ata_device::open() { const char * name = skipdev(get_dev_name()); int len = strlen(name); - // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options - char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1; - if ( sscanf(name, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1 - && ((n1 == len && !options[0]) || n2 == len) ) { - return open(drive[0] - 'a', -1, options, -1); + // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options + char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1; + if ( sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1 + && ((n1 == len && !options[0]) || n2 == len) ) { + return open(sdxy_to_phydrive(drive), -1, options, -1); } - // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options + // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1; unsigned port = ~0; - if ( sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2 - && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) { - return open(drive[0] - 'a', -1, options, port); + if ( sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2 + && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) { + return open(sdxy_to_phydrive(drive), -1, options, port); } // pd,N => Physical drive , RAID port N int phydrive = -1; port = ~0; n1 = -1; n2 = -1; @@ -2655,28 +2399,25 @@ bool win_ata_device::open() bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port) { - // path depends on Windows Version + m_phydrive = -1; char devpath[30]; - if (win9x && 0 <= phydrive && phydrive <= 7) - // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details - strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE")); - else if (!win9x && 0 <= phydrive && phydrive <= 255) - snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive); - else if (!win9x && 0 <= logdrive && logdrive <= 'Z'-'A') + if (0 <= phydrive && phydrive <= 255) + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive)); + else if (0 <= logdrive && logdrive <= 'Z'-'A') snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive); else return set_err(ENOENT); // Open device HANDLE h = INVALID_HANDLE_VALUE; - if (win9x || !(*options && !options[strspn(options, "fp")])) { + if (!(*options && !options[strspn(options, "fp")])) { // Open with admin rights m_admin = true; h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); } - if (!win9x && h == INVALID_HANDLE_VALUE) { + if (h == INVALID_HANDLE_VALUE) { // Open without admin rights m_admin = false; h = CreateFileA(devpath, 0, @@ -2685,10 +2426,6 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int } if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); -#if WIN9X_SUPPORT - if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND) - smartvsd_error(); -#endif if (err == ERROR_FILE_NOT_FOUND) set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) @@ -2725,13 +2462,12 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int m_options = def_options; } - // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call - m_drive = 0; m_port = port; - if (!win9x && port < 0) + // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call + m_port = port; + if (port < 0) return true; - // Win9X/ME: Get drive map - // RAID: Get port map + // 3ware RAID: Get port map GETVERSIONINPARAMS_EX vers_ex; int devmap = smart_get_version(h, &vers_ex); @@ -2760,7 +2496,7 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int } m_smartver_state = 1; - if (port >= 0) { + { // 3ware RAID: update devicemap first if (!update_3ware_devicemap_ioctl(h)) { @@ -2775,75 +2511,23 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port); } } - return true; } - // Win9x/ME: Check device presence & type - if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) { - unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10; - // Win9x drive existence check may not work as expected - // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01 - // (The related KB Article Q196120 is no longer available) - if (!is_permissive()) { - close(); - return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)", - devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap); - } - } - // Drive number must be passed to ioctl - m_drive = (phydrive & 0x3); return true; } -#if WIN9X_SUPPORT - -// Scan for ATA drives on Win9x/ME - -bool win9x_smart_interface::ata_scan(smart_device_list & devlist) -{ - // Open device - const char devpath[] = "\\\\.\\SMARTVSD"; - HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); - if (h == INVALID_HANDLE_VALUE) { - if (ata_debugmode > 1) - pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError()); - return true; // SMARTVSD.VXD missing or no ATA devices - } - - // Get drive map - int devmap = smart_get_version(h); - CloseHandle(h); - if (devmap < 0) - return true; // Should not happen - - // Check ATA device presence, remove ATAPI devices - devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf); - char name[20]; - for (int i = 0; i < 4; i++) { - if (!(devmap & (1 << i))) - continue; - sprintf(name, "/dev/hd%c", 'a'+i); - devlist.push_back( new win_ata_device(this, name, "ata") ); - } - return true; -} - -#endif // WIN9X_SUPPORT - - -///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// // Interface to ATA devices bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { // No multi-sector support for now, see above // warning about IOCTL_ATA_PASS_THROUGH - if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // !multi_sector_support - true) // ata_48bit_support + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + ata_device::supports_48bit) ) return false; @@ -2861,7 +2545,7 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) case ATA_IDENTIFY_PACKET_DEVICE: // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE // and SCSI_MINIPORT_* if requested by user - valid_options = (m_usr_options ? "saicmf" : "saicf"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; case ATA_CHECK_POWER_MODE: @@ -2879,21 +2563,21 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) case ATA_SMART_AUTO_OFFLINE: // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE // and SCSI_MINIPORT_* if requested by user - valid_options = (m_usr_options ? "saicmf" : "saicf"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; case ATA_SMART_IMMEDIATE_OFFLINE: - // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME - valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ? - "saicm3" : "aicm3"); + // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST + valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ? + "saim3" : "aim3"); break; case ATA_SMART_READ_LOG_SECTOR: - // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME + // SMART_RCV_DRIVE_DATA does not support READ_LOG // Try SCSI_MINIPORT also to skip buggy class driver // SMART functions do not support multi sector I/O. if (in.size == 512) - valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3"); + valid_options = (m_usr_options ? "saim3" : "aim3"); else valid_options = "a"; break; @@ -2905,11 +2589,7 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) break; case ATA_SMART_STATUS: - // May require lba_mid,lba_high register return - if (in.out_needed.is_set()) - valid_options = (m_usr_options ? "saimf" : "saif"); - else - valid_options = (m_usr_options ? "saicmf" : "saicf"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; default: @@ -2930,11 +2610,9 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) || in.in_regs.is_48bit_cmd() ) // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only valid_options = "a"; - else if (in.out_needed.is_set()) - // Need output registers: ATA/IDE_PASS_THROUGH - valid_options = "ai"; else - valid_options = "aic"; + // ATA/IDE_PASS_THROUGH + valid_options = "ai"; } if (!m_admin) { @@ -3047,13 +2725,13 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) m_smartver_state = 1; } - rc = smart_ioctl(get_fh(), m_drive, ®s, data, datasize, m_port); + rc = smart_ioctl(get_fh(), ®s, data, datasize, m_port); out_regs_set = (in.in_regs.features == ATA_SMART_STATUS); - id_is_cached = (m_port < 0 && !win9x); // Not cached by 3ware or Win9x/ME driver + id_is_cached = (m_port < 0); // Not cached by 3ware driver break; case 'm': rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s, data, datasize); - id_is_cached = (m_port < 0 && !win9x); + id_is_cached = (m_port < 0); break; case 'a': rc = ata_pass_through_ioctl(get_fh(), ®s, @@ -3065,12 +2743,11 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) rc = ide_pass_through_ioctl(get_fh(), ®s, data, datasize); out_regs_set = true; break; - case 'c': - rc = ata_via_scsi_pass_through_ioctl(get_fh(), ®s, data, datasize); - break; case 'f': if (in.in_regs.command == ATA_IDENTIFY_DEVICE) { rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data); + if (rc == 0 && m_phydrive >= 0) + get_serial_from_wmi(m_phydrive, (ata_identify_device *)data); id_is_cached = true; } else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) { @@ -3261,10 +2938,12 @@ bool csmi_device::select_phy(unsigned phy_no) bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { - if (!ata_cmd_is_ok(in, - true, // data_out_support - true, // multi_sector_support - true) // ata_48bit_support + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + ata_device::supports_multi_sector | + ata_device::supports_48bit, + "CMSI") ) return false; @@ -3479,556 +3158,19 @@ bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer, // Check result if (csmi_buffer->ReturnCode) { if (scsi_debugmode) { - pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n", - code, csmi_buffer->ReturnCode); + pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n", + code, (unsigned)csmi_buffer->ReturnCode); } - return set_err(EIO, "CSMI(%u) failed with ReturnCode=%lu", code, csmi_buffer->ReturnCode); + return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode); } if (scsi_debugmode > 1) - pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code, num_out); + pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out); return true; } -///////////////////////////////////////////////////////////////////////////// -// ASPI Interface (for SCSI devices on 9x/ME) -///////////////////////////////////////////////////////////////////////////// - -#if WIN9X_SUPPORT - -#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: Timed out\n", srb->h.adapter); - aspi_entry = 0; - h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE; - errno = EIO; - return -1; - } - if (scsi_debugmode > 1) - pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i); - 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 = (HINSTANCE)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 = (HINSTANCE)INVALID_HANDLE_VALUE; - errno = ENOENT; - return -1; - } - if (scsi_debugmode > 1) { - // Print full path of WNASPI32.DLL - char path[MAX_PATH]; - if (!GetModuleFileName(h_aspi_dll, path, sizeof(path))) - strcpy(path, "*unknown*"); - pout("Using ASPI interface \"%s\"\n", path); - } - - // 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)(); - if (scsi_debugmode > 1) - pout("GetASPI32SupportInfo() returns 0x%04x\n", info); - 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 = (HINSTANCE)INVALID_HANDLE_VALUE; - errno = ENOENT; - return -1; - } - - if (scsi_debugmode) - pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":"")); - -#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, unsigned timeout) -{ - 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, timeout*1000L); - if (rc != WAIT_OBJECT_0) { - if (rc == WAIT_TIMEOUT) { - pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n", - srb->h.adapter, srb->i.target_id, timeout); - } - else { - pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n", - (unsigned long)(ULONG_PTR)event, rc, rc, GetLastError()); - } - // TODO: ASPI_ABORT_IO command - aspi_entry = 0; - h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE; - return -EIO; - } - } - CloseHandle(event); - return 0; -} - - -win_aspi_device::win_aspi_device(smart_interface * intf, - const char * dev_name, const char * req_type) -: smart_device(intf, dev_name, "scsi", req_type), - m_adapter(-1), m_id(0) -{ -} - -bool win_aspi_device::is_open() const -{ - return (m_adapter >= 0); -} - -bool win_aspi_device::open() -{ - // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0 - unsigned adapter = ~0, id = ~0; int n1 = -1; - const char * name = skipdev(get_dev_name()); - if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name) - && adapter <= 9 && id < 16)) - return set_err(EINVAL); - - if (!aspi_entry_valid()) { - if (aspi_open_dll(1/*verbose*/)) - return set_err(ENOENT); - } - - // Adapter OK? - if (adapter >= num_aspi_adapters) { - pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n", - adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":"")); - if (!is_permissive()) - return set_err(ENOENT); - } - - // Device present ? - ASPI_SRB srb; - memset(&srb, 0, sizeof(srb)); - srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE; - srb.h.adapter = adapter; srb.i.target_id = id; - if (aspi_call(&srb)) - return set_err(EIO); - if (srb.h.status != ASPI_STATUS_NO_ERROR) { - pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status); - if (!is_permissive()) - return set_err(srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO); - } - else if (scsi_debugmode) - pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype); - - m_adapter = (int)adapter; m_id = (unsigned char)id; - return true; -} - - -bool win_aspi_device::close() -{ - // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads - return true; -} - - -// Scan for ASPI drives - -bool win9x_smart_interface::scsi_scan(smart_device_list & devlist) -{ - if (!aspi_entry_valid()) { - if (aspi_open_dll(scsi_debugmode/*default is quiet*/)) - return true; - } - - for (unsigned ad = 0; ad < num_aspi_adapters; ad++) { - ASPI_SRB srb; - - if (ad > 9) { - if (scsi_debugmode) - pout(" ASPI Adapter %u: Ignored\n", ad); - continue; - } - - // Get adapter name - memset(&srb, 0, sizeof(srb)); - srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE; - srb.h.adapter = ad; - if (aspi_call(&srb)) - break; - - if (srb.h.status != ASPI_STATUS_NO_ERROR) { - if (scsi_debugmode) - pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status); - continue; - } - - if (scsi_debugmode) { - for (int i = 1; i < 16 && srb.q.adapter_id[i]; i++) - if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~')) - srb.q.adapter_id[i] = '?'; - pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id); - } - - bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5); - - for (unsigned 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; - if (srb.h.status != ASPI_STATUS_NO_ERROR) { - if (scsi_debugmode > 1) - pout(" ID %u: No such device (Status=0x%02x)\n", id, srb.h.status); - continue; - } - - if (!ignore && srb.t.devtype == 0x00/*HDD*/) { - if (scsi_debugmode) - pout(" ID %u: Device Type=0x%02x\n", id, srb.t.devtype); - char name[20]; - sprintf(name, "/dev/scsi%u%u", ad, id); - devlist.push_back( new win_aspi_device(this, name, "scsi") ); - } - else if (scsi_debugmode) - pout(" ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype); - } - } - return true; -} - - -// Interface to ASPI SCSI devices -bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop) -{ - int report = scsi_debugmode; // TODO - - if (m_adapter < 0) { - set_err(EBADF); - return false; - } - - if (!aspi_entry_valid()) { - set_err(EBADF); - return false; - } - - if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) { - set_err(EINVAL, "bad CDB length"); - return false; - } - - 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 : ""); - 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("%s", buff); - } - - ASPI_SRB srb; - memset(&srb, 0, sizeof(srb)); - srb.h.cmd = ASPI_CMD_EXECUTE_IO; - srb.h.adapter = m_adapter; - srb.i.target_id = m_id; - //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: - set_err(EINVAL, "bad dxfer_dir"); - return false; - } - - iop->resp_sense_len = 0; - iop->scsi_status = 0; - iop->resid = 0; - - if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) { - // Timeout - set_err(EIO, "ASPI Timeout"); return false; - } - - 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 true; - } - 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); - set_err(EIO); - return false; - } - } - - 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 true; -} - -#endif // WIN9X_SUPPORT - ///////////////////////////////////////////////////////////////////////////// // SPT Interface (for SCSI devices and ATA devices behind SATLs) // Only supported in NT and later @@ -4043,11 +3185,11 @@ win_scsi_device::win_scsi_device(smart_interface * intf, bool win_scsi_device::open() { const char * name = skipdev(get_dev_name()); int len = strlen(name); - // sd[a-z],N => Physical drive 0-26, RAID port N - char drive[1+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1; - if ( sscanf(name, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1 + // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N + char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1; + if ( sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0)) ) { - return open(drive[0] - 'a', -1, -1, sub_addr); + return open(sdxy_to_phydrive(drive), -1, -1, sub_addr); } // pd,N => Physical drive , RAID port N int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1; @@ -4098,7 +3240,7 @@ bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr* FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { - set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError()); + set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError()); return false; } set_fh(h); @@ -4285,7 +3427,7 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) } // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c -static long scsi_pass_through_direct(HANDLE fd, struct scsi_cmnd_io * iop) +static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop) { int report = scsi_debugmode; // TODO @@ -4322,7 +3464,7 @@ static long scsi_pass_through_direct(HANDLE fd, struct scsi_cmnd_io * iop) memset(&sb, 0, sizeof(sb)); sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); //sb.spt.PathId = 0; - sb.spt.TargetId = 127; + sb.spt.TargetId = targetid; //sb.spt.Lun = 0; sb.spt.CdbLength = iop->cmnd_len; memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len); @@ -4411,229 +3553,109 @@ static long scsi_pass_through_direct(HANDLE fd, struct scsi_cmnd_io * iop) return 0; } - -#if 0 // For debugging areca code - -static void dumpdata(unsigned char *block, int len) +// Areca RAID Controller(SAS Device) +win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca") { - int ln = (len / 16) + 1; // total line# - unsigned char c; - int pos = 0; - - printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); - printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); - printf("=====================================================================\n"); - - for ( int l = 0; l < ln && len; l++ ) - { - // printf the line# and the HEX data - // if a line data length < 16 then append the space to the tail of line to reach 16 chars - printf("%02X | ", l); - for ( pos = 0; pos < 16 && len; pos++, len-- ) - { - c = block[l*16+pos]; - printf("%02X ", c); - } - - if ( pos < 16 ) - { - for ( int loop = pos; loop < 16; loop++ ) - { - printf(" "); - } - } - - // print ASCII char - for ( int loop = 0; loop < pos; loop++ ) - { - c = block[l*16+loop]; - if ( c >= 0x20 && c <= 0x7F ) - { - printf("%c", c); - } - else - { - printf("."); - } - } - printf("\n"); - } - printf("=====================================================================\n"); + set_fh(INVALID_HANDLE_VALUE); + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -#endif - -// 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 Windows interface to the Areca "arcmsr" 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 127) in the RAID array -// 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" -int win_areca_device::arcmsr_command_handler(HANDLE fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len) +bool win_areca_scsi_device::open() { - int ioctlreturn = 0; - sSRB_BUFFER sBuf; - struct scsi_cmnd_io io_hdr; - int dir = DXFER_TO_DEVICE; - - UINT8 cdb[10]; - UINT8 sense[32]; - - unsigned char *areca_return_packet; - int total = 0; - int expected = -1; - unsigned char return_buff[2048]; - unsigned char *ptr = &return_buff[0]; - memset(return_buff, 0, sizeof(return_buff)); - - memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); - memset(&io_hdr, 0, sizeof(io_hdr)); - memset(cdb, 0, sizeof(cdb)); - memset(sense, 0, sizeof(sense)); - - - sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); - memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); - sBuf.srbioctl.Timeout = 10000; - sBuf.srbioctl.ControlCode = arcmsr_cmd; + HANDLE hFh; - switch ( arcmsr_cmd ) + if( is_open() ) { - // command for writing data to driver - case ARCMSR_IOCTL_WRITE_WQBUFFER: - if ( data && data_len ) - { - sBuf.srbioctl.Length = data_len; - memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); - } - // commands for clearing related buffer of driver - case ARCMSR_IOCTL_CLEAR_RQBUFFER: - case ARCMSR_IOCTL_CLEAR_WQBUFFER: - cdb[0] = 0x3B; //SCSI_WRITE_BUF command; - break; - // command for reading data from driver - case ARCMSR_IOCTL_READ_RQBUFFER: - // command for identifying driver - case ARCMSR_IOCTL_RETURN_CODE_3F: - cdb[0] = 0x3C; //SCSI_READ_BUF command; - dir = DXFER_FROM_DEVICE; - break; - default: - // unknown arcmsr commands - return -1; + return true; + } + hFh = CreateFile( get_dev_name(), + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL ); + if(hFh == INVALID_HANDLE_VALUE) + { + return false; } - cdb[1] = 0x01; - cdb[2] = 0xf0; + set_fh(hFh); + return true; +} - io_hdr.dxfer_dir = dir; - io_hdr.dxfer_len = sizeof(sBuf); - io_hdr.dxferp = (unsigned char *)&sBuf; - 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; +smart_device * win_areca_scsi_device::autodetect_open() +{ + return this; +} - while ( 1 ) - { - ioctlreturn = scsi_pass_through_direct(fd, &io_hdr); - if ( ioctlreturn || io_hdr.scsi_status ) - { - // errors found - break; - } +int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) - { - // if succeeded, just returns the length of outgoing data - return data_len; - } + ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop); + if ( ioctlreturn || iop->scsi_status ) + { + ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop); + if ( ioctlreturn || iop->scsi_status ) + { + // errors found + return -1; + } + } - if ( sBuf.srbioctl.Length ) - { - //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); - ptr += sBuf.srbioctl.Length; - total += sBuf.srbioctl.Length; - // the returned bytes enough to compute payload length ? - if ( expected < 0 && total >= 5 ) - { - areca_return_packet = (unsigned char *)&return_buff[0]; - if ( areca_return_packet[0] == 0x5E && - areca_return_packet[1] == 0x01 && - areca_return_packet[2] == 0x61 ) - { - // valid header, let's compute the returned payload length, - // we expected the total length is - // payload + 3 bytes header + 2 bytes length + 1 byte checksum - expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; - } - } + return ioctlreturn; +} - if ( total >= 7 && total >= expected ) - { - //printf("total bytes received = %d, expected length = %d\n", total, expected); +bool win_areca_scsi_device::arcmsr_lock() +{ +#define SYNCOBJNAME "Global\\SynIoctlMutex" + int ctlrnum = -1; + char mutexstr[64]; - // ------ Okay! we received enough -------- - break; - } - } - } + if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1) + return set_err(EINVAL, "unable to parse device name"); - // Deal with the different error cases - if ( arcmsr_cmd == ARCMSR_IOCTL_RETURN_CODE_3F ) + snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum); + m_mutex = CreateMutex(NULL, FALSE, mutexstr); + if ( m_mutex == NULL ) { - // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) - return -4; + return set_err(EIO, "CreateMutex failed"); } - if ( ioctlreturn ) - { - pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); - return -2; - } + // atomic access to driver + WaitForSingleObject(m_mutex, INFINITE); + + return true; +} - if ( io_hdr.scsi_status ) - { - pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status); - return -3; - } - if ( data ) +bool win_areca_scsi_device::arcmsr_unlock() +{ + if( m_mutex != NULL) { - memcpy(data, return_buff, total); + ReleaseMutex(m_mutex); + CloseHandle(m_mutex); } - return total; + return true; } -win_areca_device::win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum) -: smart_device(intf, dev_name, "areca", "areca"), - m_disknum(disknum), - m_encnum(encnum) +// Areca RAID Controller(SATA Disk) +win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca") { - set_fh(fh); + set_fh(INVALID_HANDLE_VALUE); + set_disknum(disknum); + set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -bool win_areca_device::open() +bool win_areca_ata_device::open() { HANDLE hFh; @@ -4641,7 +3663,6 @@ bool win_areca_device::open() { return true; } - hFh = CreateFile( get_dev_name(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, @@ -4658,256 +3679,83 @@ bool win_areca_device::open() return true; } -// Areca RAID Controller -bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +smart_device * win_areca_ata_device::autodetect_open() { - // ATA input registers - typedef struct _ATA_INPUT_REGISTERS - { - unsigned char features; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - unsigned char device_head; - unsigned char command; - unsigned char reserved[8]; - unsigned char data[512]; // [in/out] buffer for outgoing/incoming data - } sATA_INPUT_REGISTERS; - - // ATA output registers - // Note: The output registers is re-sorted for areca internal use only - typedef struct _ATA_OUTPUT_REGISTERS - { - unsigned char error; - unsigned char status; - unsigned char sector_count; - unsigned char sector_number; - unsigned char cylinder_low; - unsigned char cylinder_high; - } sATA_OUTPUT_REGISTERS; - - // Areca packet format for outgoing: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes command length + variant data length, little endian - // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c - // B[6~last-1] : variant bytes payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte - // +--------------------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | - // +--------------------------------------------------------------------------------+ - // - - //Areca packet format for incoming: - // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 - // B[3~4] : 2 bytes payload length, little endian - // B[5~last-1] : variant bytes returned payload data - // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) - // - // - // header 3 bytes length 2 bytes payload data x bytes cs 1 byte - // +-------------------------------------------------------------------+ - // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | - // +-------------------------------------------------------------------+ - unsigned char areca_packet[640]; - int areca_packet_len = sizeof(areca_packet); - unsigned char cs = 0; - - sATA_INPUT_REGISTERS *ata_cmd; - - // For debugging -#if 0 - memset(sInq, 0, sizeof(sInq)); - scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); - dumpdata((unsigned char *)sInq, sizeof(sInq)); -#endif - memset(areca_packet, 0, areca_packet_len); + int is_ata = 1; - // ----- BEGIN TO SETUP HEADERS ------- - areca_packet[0] = 0x5E; - areca_packet[1] = 0x01; - areca_packet[2] = 0x61; - areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); - areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); - areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command - - // ----- BEGIN TO SETUP PAYLOAD DATA ----- - memcpy(&areca_packet[7], "SmrT", 4); // areca defined password - ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; - - // Set registers - { - const ata_in_regs & r = in.in_regs; - ata_cmd->features = r.features; - ata_cmd->sector_count = r.sector_count; - ata_cmd->sector_number = r.lba_low; - ata_cmd->cylinder_low = r.lba_mid; - ata_cmd->cylinder_high = r.lba_high; - ata_cmd->device_head = r.device; - ata_cmd->command = r.command; - } - bool readdata = false; - if (in.direction == ata_cmd_in::data_in) { - readdata = true; - // the command will read data - areca_packet[6] = 0x13; - } - else if ( in.direction == ata_cmd_in::no_data ) + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) { - // the commands will return no data - areca_packet[6] = 0x15; + set_err(EIO); + return this; } - else if (in.direction == ata_cmd_in::data_out) - { - // the commands will write data - memcpy(ata_cmd->data, in.buffer, in.size); - areca_packet[6] = 0x14; - } - else { - // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE - return set_err(ENOSYS); - } - - areca_packet[11] = m_disknum - 1; // disk# - areca_packet[19] = m_encnum - 1; // enc# - // ----- BEGIN TO SETUP CHECKSUM ----- - for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) + if(is_ata == 1) { - cs += areca_packet[loop]; - } - areca_packet[areca_packet_len-1] = cs; - - // ----- BEGIN TO SEND TO ARECA DRIVER ------ - int expected = 0; - unsigned char return_buff[2048]; - memset(return_buff, 0, sizeof(return_buff)); - - expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0); - if (expected==-3) { - return set_err(EIO); + // SATA device + return this; } - expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0); - expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len); - if ( expected > 0 ) - { - expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff)); - } - if ( expected < 0 ) - { - return set_err(EIO); - } + // SAS device + smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); + close(); + delete this; + newdev->open(); // TODO: Can possibly pass open fd - // ----- VERIFY THE CHECKSUM ----- - cs = 0; - for ( int loop = 3; loop < expected - 1; loop++ ) - { - cs += return_buff[loop]; - } + return newdev.release(); +} - if ( return_buff[expected - 1] != cs ) - { - return set_err(EIO); - } +int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; - if ( ata_out->status ) - { - if ( in.in_regs.command == ATA_IDENTIFY_DEVICE - && !nonempty((unsigned char *)in.buffer, in.size)) + ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop); + if ( ioctlreturn || iop->scsi_status ) + { + ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop); + if ( ioctlreturn || iop->scsi_status ) { - return set_err(ENODEV, "No drive on port %d", m_disknum); + // errors found + return -1; } - } + } - // returns with data - if (readdata) - { - memcpy(in.buffer, &return_buff[7], in.size); - } - - // Return register values - { - ata_out_regs & r = out.out_regs; - r.error = ata_out->error; - r.sector_count = ata_out->sector_count; - r.lba_low = ata_out->sector_number; - r.lba_mid = ata_out->cylinder_low; - r.lba_high = ata_out->cylinder_high; - r.status = ata_out->status; - } - return true; + return ioctlreturn; } - -bool win_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +bool win_areca_ata_device::arcmsr_lock() { #define SYNCOBJNAME "Global\\SynIoctlMutex" int ctlrnum = -1; char mutexstr[64]; - SECURITY_ATTRIBUTES sa; - PSECURITY_DESCRIPTOR pSD; - HANDLE hmutex; - - if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // TODO: multi_sector_support - true) // ata_48bit_support - ) - return false; - - // Support 48-bit commands with zero high bytes - if (in.in_regs.is_real_48bit_cmd()) - return set_err(ENOSYS, "48-bit ATA commands not fully supported by Areca"); if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1) return set_err(EINVAL, "unable to parse device name"); - memset(mutexstr, 0, sizeof(mutexstr)); - sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum); - pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if ( !InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) ) + snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum); + m_mutex = CreateMutex(NULL, FALSE, mutexstr); + if ( m_mutex == NULL ) { - LocalFree((HLOCAL)pSD); - return set_err(EIO, "InitializeSecurityDescriptor failed"); - } - - if ( !SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE) ) - { - LocalFree((HLOCAL)pSD); - return set_err(EIO, "SetSecurityDescriptor failed"); - } - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = pSD; - sa.bInheritHandle = TRUE; - hmutex = CreateMutex(&sa, FALSE, mutexstr); - if ( hmutex == NULL ) - { - LocalFree((HLOCAL)pSD); return set_err(EIO, "CreateMutex failed"); } // atomic access to driver - WaitForSingleObject(hmutex, INFINITE); - bool ok = arcmsr_ata_pass_through(in,out); - ReleaseMutex(hmutex); + WaitForSingleObject(m_mutex, INFINITE); - if(hmutex) - { - CloseHandle(hmutex); - } + return true; +} - if ( (HLOCAL)pSD ) + +bool win_areca_ata_device::arcmsr_unlock() +{ + if( m_mutex != NULL) { - LocalFree((HLOCAL)pSD); + ReleaseMutex(m_mutex); + CloseHandle(m_mutex); } - return ok; + return true; } @@ -4930,19 +3778,8 @@ void smart_interface::init() SetDllDirectoryA_p(""); } - // Select interface for Windows flavor - if (GetVersion() & 0x80000000) { -#if WIN9X_SUPPORT - static os_win32::win9x_smart_interface the_win9x_interface; - smart_interface::set(&the_win9x_interface); -#else - throw std::runtime_error("Win9x/ME not supported"); -#endif - } - else { - static os_win32::winnt_smart_interface the_winnt_interface; - smart_interface::set(&the_winnt_interface); - } + static os_win32::win_smart_interface the_win_interface; + smart_interface::set(&the_win_interface); } diff --git a/os_win32/daemon_win32.cpp b/os_win32/daemon_win32.cpp index 481f0b5..ae3dc29 100644 --- a/os_win32/daemon_win32.cpp +++ b/os_win32/daemon_win32.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-11 Christian Franke + * Copyright (C) 2004-13 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 @@ -11,15 +11,18 @@ * 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. + * (for example COPYING); If not, see . * */ -// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP) -#define WINVER 0x0400 +#define WINVER 0x0600 #define _WIN32_WINNT WINVER +#include "daemon_win32.h" + +const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3760 2013-01-30 18:43:39Z chrfranke $" + DAEMON_WIN32_H_CVSID; + #include #include #include @@ -31,16 +34,14 @@ #include #endif -#include "daemon_win32.h" - -const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3426 2011-10-06 18:23:15Z chrfranke $" - DAEMON_WIN32_H_CVSID; +#ifndef SERVICE_CONFIG_DELAYED_AUTO_START_INFO +// Missing in older MinGW headers +#define SERVICE_CONFIG_DELAYED_AUTO_START_INFO 3 +#endif ///////////////////////////////////////////////////////////////////////////// -#define ARGUSED(x) ((void)(x)) - // Prevent spawning of child process if debugging #ifdef _DEBUG #define debugging() IsDebuggerPresent() @@ -58,94 +59,94 @@ const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3426 2011-10-06 18: 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); + 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; - if (sig >= 0) - make_name(name, sig); - else - name[0] = 0; - if (exists) - *exists = FALSE; - if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) { - 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; + char name[EVT_NAME_LEN]; + HANDLE h; + if (sig >= 0) + make_name(name, sig); + else + name[0] = 0; + if (exists) + *exists = FALSE; + if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) { + 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); + 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; + 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; + 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); + fprintf(f, + "%s: %s.\n" + "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", + ident, message, ident); + fflush(f); } @@ -155,64 +156,64 @@ static void daemon_help(FILE * f, const char * ident, const char * message) 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 ... + 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; + 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; } @@ -248,77 +249,77 @@ static int reopen_stdin, reopen_stdout, reopen_stderr; 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: // (SIGINT) - h = sigint_handle; break; - case CTRL_BREAK_EVENT: // (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; + // Caution: runs in a new thread + // TODO: Guard with a mutex + HANDLE h = 0; + switch (event) { + case CTRL_C_EVENT: // (SIGINT) + h = sigint_handle; break; + case CTRL_BREAK_EVENT: // (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); + 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; + // Keep EVT_RUNNING open until exit + running_event = hev; - // Install console handler - SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); + // Install console handler + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); - // Install restart handler - atexit(child_exit); + // Install restart handler + atexit(child_exit); - // Continue in main_func() to do the real work - return main_func(argc, argv); + // Continue in main_func() to do the real work + return main_func(argc, argv); } @@ -326,33 +327,33 @@ static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char 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((!svc_mode ? sig : -1), 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 SIGHUP: sighup_handle = h; break; - case SIGINT: sigint_handle = h; break; - case SIGTERM: sigterm_handle = h; break; - case SIGBREAK: sigbreak_handle = h; break; - case SIGUSR1: sigusr1_handle = h; break; - } - num_sig_handlers++; - return SIG_DFL; + 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((!svc_mode ? sig : -1), 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 SIGHUP: sighup_handle = h; break; + case SIGINT: sigint_handle = h; break; + case SIGTERM: sigterm_handle = h; break; + case SIGBREAK: sigbreak_handle = h; break; + case SIGUSR1: sigusr1_handle = h; break; + } + num_sig_handlers++; + return SIG_DFL; } @@ -360,15 +361,15 @@ sigfunc_t daemon_signal(int sig, sigfunc_t func) 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*"; - } + 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*"; + } } @@ -376,26 +377,26 @@ const char * daemon_strsignal(int sig) 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); + 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); } @@ -403,39 +404,39 @@ void daemon_sleep(int seconds) 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*/); + 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; + 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; } @@ -443,101 +444,28 @@ int daemon_enable_console(const char * title) 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); - reopen_stdin = reopen_stdout = reopen_stderr = 1; - } - - 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; - 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; + 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); + reopen_stdin = reopen_stdout = reopen_stderr = 1; + } + + return 0; } @@ -550,151 +478,108 @@ 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; - char temp_path[MAX_PATH]; - DWORD flags, num_io, exitcode; - int use_file, state, i; - SECURITY_ATTRIBUTES sa; - STARTUPINFO si; PROCESS_INFORMATION pi; - HANDLE self = GetCurrentProcess(); - - if (GetVersion() & 0x80000000L) { - // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or - // any other DOS program is redirected via a pipe. Using a temp file instead. - use_file = 1; flags = DETACHED_PROCESS; - } - else { - // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window - // for each external command in a redirected .BAT file. - // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work. - use_file = 0; flags = CREATE_NO_WINDOW; - } - - // 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/*inherit*/, inpsize*2+13)) - return -1; - if (!DuplicateHandle(self, h, self, &pipe_inp_w, - 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { - CloseHandle(pipe_inp_r); - return -1; - } - - memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); - sa.bInheritHandle = TRUE; - if (!use_file) { - // Create stdout pipe with inheritable write side - if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) { - CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); - return -1; - } - } - else { - // Create temp file with inheritable write handle - char temp_dir[MAX_PATH]; - if (!GetTempPathA(sizeof(temp_dir), temp_dir)) - strcpy(temp_dir, "."); - if (!GetTempFileNameA(temp_dir, "out"/*prefix*/, 0/*create unique*/, temp_path)) { - CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); - return -1; - } - if ((h = CreateFileA(temp_path, GENERIC_READ|GENERIC_WRITE, - 0/*no sharing*/, &sa/*inherit*/, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { - CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); - return -1; - } - if (!DuplicateHandle(self, h, self, &pipe_out_w, - GENERIC_WRITE, TRUE/*inherit*/, 0)) { - CloseHandle(h); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); - return -1; - } - } - - if (!DuplicateHandle(self, h, self, &pipe_out_r, - GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) { - CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); - return -1; - } - - // 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/file 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*/, - flags/*DETACHED_PROCESS or CREATE_NO_WINDOW*/, - 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); - - exitcode = 42; - for (state = 0; state < 2; state++) { - // stdout pipe: read pipe first - // stdout file: wait for process first - if (state == use_file) { - // Copy stdout to output buffer until full, rest to /dev/null - // convert \r\n => \n - if (use_file) - SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN); - 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); - if (use_file) - DeleteFileA(temp_path); - } - else { - // Wait for process exitcode - WaitForSingleObject(pi.hProcess, INFINITE); - GetExitCodeProcess(pi.hProcess, &exitcode); - CloseHandle(pi.hProcess); - } - } - return exitcode; + HANDLE self = GetCurrentProcess(); + + // Create stdin pipe with inheritable read side + SECURITY_ATTRIBUTES sa; + memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + HANDLE pipe_inp_r, pipe_inp_w, h; + if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13)) + return -1; + if (!DuplicateHandle(self, h, self, &pipe_inp_w, + 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { + CloseHandle(pipe_inp_r); + return -1; + } + + // Create stdout pipe with inheritable write side + memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + HANDLE pipe_out_w; + if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) { + CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + + HANDLE pipe_out_r; + if (!DuplicateHandle(self, h, self, &pipe_out_r, + GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) { + CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); + return -1; + } + + // Create stderr handle as dup of stdout write side + HANDLE pipe_err_w; + 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 + STARTUPINFO si; + 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; + PROCESS_INFORMATION pi; + if (!CreateProcessA( + NULL, (char*)cmd, + NULL, NULL, TRUE/*inherit*/, + CREATE_NO_WINDOW, // DETACHED_PROCESS does not work + 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 + DWORD num_io; + int i; + 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]; + if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0) + break; + for (int 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 + DWORD exitcode = 42; + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pi.hProcess); + return exitcode; } @@ -703,128 +588,128 @@ int daemon_spawn(const char * cmd, 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); - } + 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); - } + 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; + 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); - } - } + 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); + } + } } @@ -841,38 +726,30 @@ static SERVICE_STATUS svc_status; static void service_report_status(int state, int seconds) { - // TODO: Avoid race - static DWORD checkpoint = 1; - static DWORD accept_more = SERVICE_ACCEPT_PARAMCHANGE; // Win2000/XP - 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|accept_more; - break; - } - if (!SetServiceStatus(svc_handle, &svc_status)) { - if (svc_status.dwControlsAccepted & accept_more) { - // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4) - svc_status.dwControlsAccepted &= ~accept_more; - accept_more = 0; - SetServiceStatus(svc_handle, &svc_status); - } - } + // 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); } @@ -880,35 +757,35 @@ static void service_report_status(int state, int seconds) 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; - SetEvent(sigterm_handle); - break; - case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP - service_report_status(svc_status.dwCurrentState, 0); - svc_paused = 0; - SetEvent(sighup_handle); // 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; - SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck - } - break; - case SERVICE_CONTROL_INTERROGATE: - default: // unknown - service_report_status(svc_status.dwCurrentState, 0); - break; - } + switch (ctrlcode) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + service_report_status(SERVICE_STOP_PENDING, 30); + svc_paused = 0; + SetEvent(sigterm_handle); + break; + case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP + service_report_status(svc_status.dwCurrentState, 0); + svc_paused = 0; + SetEvent(sighup_handle); // 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; + SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck + } + break; + case SERVICE_CONTROL_INTERROGATE: + default: // unknown + service_report_status(svc_status.dwCurrentState, 0); + break; + } } @@ -916,19 +793,19 @@ static void WINAPI service_control(DWORD ctrlcode) 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); + // 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); } @@ -939,54 +816,106 @@ static char ** svc_main_argv; // Main function for service, called by service dispatcher -static void WINAPI service_main(DWORD argc, LPSTR * argv) +static void WINAPI service_main(DWORD /*argc*/, LPSTR * argv) { - char path[MAX_PATH], *p; - ARGUSED(argc); + char path[MAX_PATH], *p; + + // Register control handler + svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); - // Register control handler - svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); + // Init service status + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + service_report_status(SERVICE_START_PENDING, 10); - // 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); + } - // 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); + // 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); + // 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() + 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) +// Make registry key name for event message file +static bool make_evtkey(char * buf, unsigned size, const char * ident) +{ + static const char prefix[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\"; + const unsigned pfxlen = sizeof(prefix)-1; + unsigned idlen = strlen(ident); + if (pfxlen + idlen >= size) { + printf(" Buffer overflow\n"); + return false; + } + memcpy(buf, prefix, pfxlen); + memcpy(buf+pfxlen, ident, idlen+1); + return true; +} + +// Install this exe as event message file +static void inst_evtmsg(const char * ident) +{ + printf("Installing event message file for %s:", ident); fflush(stdout); + + char mypath[MAX_PATH]; + if (!GetModuleFileNameA((HMODULE)0, mypath, sizeof(mypath))) { + printf(" unknown program path, Error=%ld\n", GetLastError()); + return; + } + + char subkey[MAX_PATH]; + if (!make_evtkey(subkey, sizeof(subkey), ident)) + return; + + HKEY hk; + LONG err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, (char *)0, 0, KEY_ALL_ACCESS, + (SECURITY_ATTRIBUTES *)0, &hk, (DWORD *)0); + if (err != ERROR_SUCCESS) { + printf(" RegCreateKeyEx failed, error=%ld\n", err); + return; + } + + err = RegSetValueExA(hk, "EventMessageFile", 0, REG_SZ, + (const BYTE *)mypath, strlen(mypath)+1); + if (err == ERROR_SUCCESS) { + DWORD val = EVENTLOG_INFORMATION_TYPE + |EVENTLOG_WARNING_TYPE + |EVENTLOG_ERROR_TYPE; + err = RegSetValueExA(hk, "TypesSupported", 0, REG_DWORD, + (const BYTE *)&val, sizeof(val)); + } + if (err != ERROR_SUCCESS) + printf(" RegSetValueEx failed, error=%ld\n", err); + + RegCloseKey(hk); + puts(" done"); +} + +// Uninstall event message file +static void uninst_evtmsg(const char * ident) { - HINSTANCE 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; + printf("Removing event message file for %s:", ident); fflush(stdout); + + char subkey[MAX_PATH]; + if (!make_evtkey(subkey, sizeof(subkey), ident)) + return; + + LONG err = RegDeleteKeyA(HKEY_LOCAL_MACHINE, subkey); + if (err != ERROR_SUCCESS && err != ERROR_FILE_NOT_FOUND) { + printf(" RegDeleteKey failed, error=%ld\n", err); + return; + } + puts(" done"); } @@ -995,111 +924,128 @@ static int svcadm_setdesc(SC_HANDLE hs, const char * desc) static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, int argc, char **argv ) { - int remove; long err; - 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))) { - if ((err = GetLastError()) == ERROR_ACCESS_DENIED) - puts(" access to SCManager denied"); - else if (err == ERROR_CALL_NOT_IMPLEMENTED) - puts(" services not implemented on this version of Windows"); - else - printf(" cannot open SCManager, Error=%ld\n", err); - 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; - } - // Add quotes if necessary - if (strchr(path, ' ')) { - i = strlen(path); - path[i+1] = '"'; path[i+2] = 0; - while (--i >= 0) - path[i+1] = path[i]; - path[0] = '"'; - } - // Append options - strcat(path, " "); strcat(path, svc_opts->cmd_opt); - for (i = 2; i < argc; i++) { - const char * s = argv[i]; - if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) - break; - // Add quotes if necessary - if (strchr(s, ' ') && !strchr(s, '"')) { - strcat(path, " \""); strcat(path, s); strcat(path, "\""); - } - else { - 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*/))) { - if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) - puts(" the service is already installed"); - else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) - puts(" service is still running and marked for deletion\n" - "Stop the service and retry install"); - else - printf(" failed, Error=%ld\n", err); - CloseServiceHandle(hm); - return 1; - } - // Set optional description - if (svc_opts->descript) - svcadm_setdesc(hs, svc_opts->descript); - } - else { - // Open - if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { - puts(" not found"); - CloseServiceHandle(hm); - return 1; - } - // TODO: Stop service if running - // Remove - if (!DeleteService(hs)) { - if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) - puts(" service is still running and marked for deletion\n" - "Stop the service to remove it"); - else - printf(" failed, Error=%ld\n", err); - CloseServiceHandle(hs); CloseServiceHandle(hm); - return 1; - } - } - puts(" done"); - CloseServiceHandle(hs); CloseServiceHandle(hm); - return 0; + int remove; long err; + 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))) { + if ((err = GetLastError()) == ERROR_ACCESS_DENIED) + puts(" access to SCManager denied"); + else + printf(" cannot open SCManager, Error=%ld\n", err); + 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; + } + // Add quotes if necessary + if (strchr(path, ' ')) { + i = strlen(path); + path[i+1] = '"'; path[i+2] = 0; + while (--i >= 0) + path[i+1] = path[i]; + path[0] = '"'; + } + // Append options + strcat(path, " "); strcat(path, svc_opts->cmd_opt); + for (i = 2; i < argc; i++) { + const char * s = argv[i]; + if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) + break; + // Add quotes if necessary + if (strchr(s, ' ') && !strchr(s, '"')) { + strcat(path, " \""); strcat(path, s); strcat(path, "\""); + } + else { + 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*/))) { + if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) + puts(" the service is already installed"); + else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) + puts(" service is still running and marked for deletion\n" + "Stop the service and retry install"); + else + printf(" failed, Error=%ld\n", err); + CloseServiceHandle(hm); + return 1; + } + // Set optional description + if (svc_opts->descript) { + SERVICE_DESCRIPTIONA sd = { const_cast(svc_opts->descript) }; + ChangeServiceConfig2A(hs, SERVICE_CONFIG_DESCRIPTION, &sd); + } + // Enable delayed auto start if supported + OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver); + if ( GetVersionExA(&ver) + && ver.dwPlatformId == VER_PLATFORM_WIN32_NT + && ver.dwMajorVersion >= 6 /* Vista */ ) { + SERVICE_DELAYED_AUTO_START_INFO sdasi = { TRUE }; + ChangeServiceConfig2A(hs, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &sdasi); + } + } + else { + // Open + if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { + puts(" not found"); + CloseServiceHandle(hm); + return 1; + } + // TODO: Stop service if running + // Remove + if (!DeleteService(hs)) { + if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) + puts(" service is still running and marked for deletion\n" + "Stop the service to remove it"); + else + printf(" failed, Error=%ld\n", err); + CloseServiceHandle(hs); CloseServiceHandle(hm); + return 1; + } + } + puts(" done"); + CloseServiceHandle(hs); CloseServiceHandle(hm); + + // Install/Remove event message file registry entry + if (!remove) { + inst_evtmsg(ident); + } + else { + uninst_evtmsg(ident); + } + + return 0; } @@ -1112,77 +1058,77 @@ static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opt int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, int (*main_func)(int, char **), int argc, char **argv ) { - int rc; + 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); + // 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)) { - printf("%s: cannot dispatch service, Error=%ld\n" - "Option \"%s\" cannot be used to start %s as a service from console.\n" - "Use \"%s install ...\" to install the service\n" - "and \"net start %s\" to start it.\n", - ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident); + // 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)) { + printf("%s: cannot dispatch service, Error=%ld\n" + "Option \"%s\" cannot be used to start %s as a service from console.\n" + "Use \"%s install ...\" to install the service\n" + "and \"net start %s\" to start it.\n", + ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident); #ifdef _DEBUG - if (debugging()) - service_main(argc, argv); + if (debugging()) + service_main(argc, argv); #endif - return 100; - } - Sleep(1000); - ExitThread(0); // Do not redo exit() processing - /*NOTREACHED*/ - return 0; - } + return 100; + } + Sleep(1000); + ExitThread(0); // Do not redo exit() processing + /*NOTREACHED*/ + return 0; + } } @@ -1195,88 +1141,88 @@ static volatile sig_atomic_t caughtsig = 0; static void sig_handler(int sig) { - caughtsig = sig; + caughtsig = sig; } static void test_exit(void) { - printf("Main exit\n"); + printf("Main exit\n"); } int test_main(int argc, char **argv) { - int i; - int debug = 0; - char * cmd = 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; - } - if (argc > 1 && argv[argc-1][0] != '-') - cmd = argv[argc-1]; - - daemon_signal(SIGINT, sig_handler); - daemon_signal(SIGBREAK, sig_handler); - daemon_signal(SIGTERM, sig_handler); - daemon_signal(SIGHUP, sig_handler); - daemon_signal(SIGUSR1, 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(); - } - else if (caughtsig == SIGUSR1 && cmd) { - char inpbuf[200], outbuf[1000]; int rc; - strcpy(inpbuf, "Hello\nWorld!\n"); - rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf)); - if (!debug) - daemon_enable_console("Command output"); - printf("\"%s\" returns %d\n", cmd, rc); - if (rc >= 0) - printf("output:\n%s.\n", outbuf); - fflush(stdout); - if (!debug) { - Sleep(10000); 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 i; + int debug = 0; + char * cmd = 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; + } + if (argc > 1 && argv[argc-1][0] != '-') + cmd = argv[argc-1]; + + daemon_signal(SIGINT, sig_handler); + daemon_signal(SIGBREAK, sig_handler); + daemon_signal(SIGTERM, sig_handler); + daemon_signal(SIGHUP, sig_handler); + daemon_signal(SIGUSR1, 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(); + } + else if (caughtsig == SIGUSR1 && cmd) { + char inpbuf[200], outbuf[1000]; int rc; + strcpy(inpbuf, "Hello\nWorld!\n"); + rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf)); + if (!debug) + daemon_enable_console("Command output"); + printf("\"%s\" returns %d\n", cmd, rc); + if (rc >= 0) + printf("output:\n%s.\n", outbuf); + fflush(stdout); + if (!debug) { + Sleep(10000); 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" - }; + 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); + return daemon_main("testd", &svc_opts, test_main, argc, argv); } #endif diff --git a/os_win32/daemon_win32.h b/os_win32/daemon_win32.h index cb20d84..2d9c1c5 100644 --- a/os_win32/daemon_win32.h +++ b/os_win32/daemon_win32.h @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-8 Christian Franke + * Copyright (C) 2004-12 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 @@ -11,15 +11,14 @@ * 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. + * (for example COPYING); If not, see . * */ #ifndef DAEMON_WIN32_H #define DAEMON_WIN32_H -#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h,v 1.7 2008/03/04 22:09:48 ballen4705 Exp $\n" +#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 3584 2012-08-05 17:05:32Z chrfranke $" #include @@ -60,9 +59,6 @@ int daemon_enable_console(const char * title); // Detach from console int daemon_detach(const char * ident); -// Display a message box -int daemon_messagebox(int system, const char * title, const char * text); - // Spawn a process and redirect stdio int daemon_spawn(const char * cmd, const char * inpbuf, int inpsize, diff --git a/os_win32/hostname_win32.cpp b/os_win32/hostname_win32.cpp deleted file mode 100644 index 1ad4a52..0000000 --- a/os_win32/hostname_win32.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * os_win32/hostname_win32.cpp - * - * Home page of code is: http://smartmontools.sourceforge.net - * - * Copyright (C) 2004-8 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, 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.cpp,v 1.6 2008/03/04 22:09:48 ballen4705 Exp $" HOSTNAME_WIN32_H_CVSID; - -#define WIN32_LEAN_AND_MEAN -#include -#include - -#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) -{ - HINSTANCE hdll; - BOOL (WINAPI * GetComputerNameExA_p)(int/*enum COMPUTER_NAME_FORMAT*/, LPSTR, LPDWORD); - BOOL ret; - if (!(hdll = LoadLibraryA("KERNEL32.DLL"))) - return FALSE; - if (!(GetComputerNameExA_p = (BOOL (WINAPI *)(int, LPSTR, LPDWORD))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) -{ - HINSTANCE hdll; - DWORD (WINAPI * GetNetworkParams_p)(PFIXED_INFO, PULONG); - DWORD ret; - if (!(hdll = LoadLibraryA("IPHlpApi.dll"))) - return ERROR_NOT_SUPPORTED; - if (!(GetNetworkParams_p = (DWORD (WINAPI *)(PFIXED_INFO, PULONG))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, (unsigned char *)name, &size) == ERROR_SUCCESS && type == REG_SZ)) - size = 0; - if (size == 0 && domain) { - size = len-1; - if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, (unsigned char *)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 - -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/os_win32/hostname_win32.h b/os_win32/hostname_win32.h deleted file mode 100644 index 7825764..0000000 --- a/os_win32/hostname_win32.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * os_win32/hostname_win32.h - * - * Home page of code is: http://smartmontools.sourceforge.net - * - * Copyright (C) 2004-8 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, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef HOSTNAME_WIN32_H -#define HOSTNAME_WIN32_H - -#define HOSTNAME_WIN32_H_CVSID "$Id: hostname_win32.h,v 1.5 2008/03/04 22:09:48 ballen4705 Exp $\n" - -#ifdef __cplusplus -extern "C" { -#endif - -int gethostname(char * name, int len); -int getdomainname(char * name, int len); - -#ifdef __cplusplus -} -#endif - -#endif // HOSTNAME_WIN32_H diff --git a/os_win32/installer.nsi b/os_win32/installer.nsi index d918ac6..9682946 100644 --- a/os_win32/installer.nsi +++ b/os_win32/installer.nsi @@ -3,7 +3,7 @@ ; ; Home page of code is: http://smartmontools.sourceforge.net ; -; Copyright (C) 2006-12 Christian Franke +; Copyright (C) 2006-13 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 @@ -13,7 +13,7 @@ ; You should have received a copy of the GNU General Public License ; (for example COPYING); If not, see . ; -; $Id: installer.nsi 3545 2012-05-25 21:19:03Z chrfranke $ +; $Id: installer.nsi 3759 2013-01-26 21:11:02Z chrfranke $ ; @@ -87,7 +87,7 @@ InstType "Drive menu" ; Sections !ifdef INPDIR64 - Section "64-bit version (EXPERIMENTAL)" X64_SECTION + Section "64-bit version" X64_SECTION ; Handled in Function CheckX64 SectionEnd !endif @@ -135,16 +135,15 @@ SectionGroup "!Program files" !insertmacro FileExe "bin\smartd.exe" "" IfFileExists "$INSTDIR\bin\smartd.conf" 0 +2 - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Replace existing configuration file$\n$INSTDIR\bin\smartd.conf ?" IDYES 0 IDNO +2 + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Replace existing configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO +2 File "${INPDIR}\doc\smartd.conf" - IfFileExists "$WINDIR\system32\cmd.exe" 0 nosysl - !insertmacro FileExe "bin\syslogevt.exe" /nonfatal - nosysl: + File "${INPDIR}\bin\smartd_warning.cmd" + !insertmacro FileExe "bin\wtssendmsg.exe" "" ; Restart service ? StrCmp $1 "0" 0 +3 - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Restart smartd service ?" IDYES 0 IDNO +2 + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Restart smartd service ?" /SD IDNO IDYES 0 IDNO +2 ExecWait "net start smartd" SectionEnd @@ -176,7 +175,8 @@ Section "!Documentation" DOC_SECTION SetOutPath "$INSTDIR\doc" File "${INPDIR}\doc\AUTHORS.txt" - File "${INPDIR}\doc\CHANGELOG.txt" + File "${INPDIR}\doc\ChangeLog.txt" + File "${INPDIR}\doc\ChangeLog-5.0-6.0.txt" File "${INPDIR}\doc\COPYING.txt" File "${INPDIR}\doc\INSTALL.txt" File "${INPDIR}\doc\NEWS.txt" @@ -253,16 +253,14 @@ Section "Start Menu Shortcuts" MENU_SECTION ; smartctl IfFileExists "$INSTDIR\bin\smartctl.exe" 0 noctl SetOutPath "$INSTDIR\bin" - IfFileExists "$WINDIR\system32\cmd.exe" 0 nocmd - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl (Admin CMD).lnk" "$WINDIR\system32\cmd.exe" '/k PATH=$INSTDIR\bin;%PATH%&cd /d "$INSTDIR\bin"' - nocmd: + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl (Admin CMD).lnk" "$WINDIR\system32\cmd.exe" '/k PATH=$INSTDIR\bin;%PATH%&cd /d "$INSTDIR\bin"' CreateDirectory "$SMPROGRAMS\smartmontools\smartctl Examples" FileOpen $0 "$SMPROGRAMS\smartmontools\smartctl Examples\!Read this first!.txt" "w" FileWrite $0 "All the example commands in this directory$\r$\napply to the first drive (sda).$\r$\n" FileClose $0 - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\All info (-a).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -a sda" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\All info (-x).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -x sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Identify drive (-i).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -i sda" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART attributes (-A).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -A sda" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART attributes (-A -f brief).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -A -f brief sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART capabilities (-c).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -c sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART health status (-H).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -H sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART error log (-l error).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -l error sda" @@ -288,15 +286,13 @@ Section "Start Menu Shortcuts" MENU_SECTION CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd.conf (view).lnk" "$EDITOR" "$INSTDIR\bin\smartd.conf" CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd.log (view).lnk" "$EDITOR" "$INSTDIR\bin\smartd.log" - ; smartd service (not on 9x/ME) - IfFileExists "$WINDIR\system32\cmd.exe" 0 nosvc - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, eventlog, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 10min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0 -i 600" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service remove.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd remove" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service start.lnk" "$INSTDIR\bin\runcmdu.exe" "net start smartd" - !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service stop.lnk" "$INSTDIR\bin\runcmdu.exe" "net stop smartd" - nosvc: + ; smartd service + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, eventlog, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 10min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0 -i 600" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service remove.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd remove" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service start.lnk" "$INSTDIR\bin\runcmdu.exe" "net start smartd" + !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service stop.lnk" "$INSTDIR\bin\runcmdu.exe" "net stop smartd" nod: ; Documentation @@ -314,14 +310,9 @@ Section "Start Menu Shortcuts" MENU_SECTION CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb.h (view).lnk" "$EDITOR" "$INSTDIR\bin\drivedb.h" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb-add.h (create, edit).lnk" "$EDITOR" "$INSTDIR\bin\drivedb-add.h" nodb: - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\AUTHORS.lnk" "$INSTDIR\doc\AUTHORS.txt" - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\CHANGELOG.lnk" "$INSTDIR\doc\CHANGELOG.txt" + CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\ChangeLog.lnk" "$INSTDIR\doc\ChangeLog.txt" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\COPYING.lnk" "$INSTDIR\doc\COPYING.txt" - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\INSTALL.lnk" "$INSTDIR\doc\INSTALL.txt" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\NEWS.lnk" "$INSTDIR\doc\NEWS.txt" - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\README.lnk" "$INSTDIR\doc\README.txt" - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\TODO.lnk" "$INSTDIR\doc\TODO.txt" - CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\WARNINGS.lnk" "$INSTDIR\doc\WARNINGS.txt" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\Windows version download page.lnk" "http://smartmontools-win32.dyndns.org/smartmontools/" nodoc: @@ -346,9 +337,8 @@ Section "Add install dir to PATH" PATH_SECTION SectionIn 1 - IfFileExists "$WINDIR\system32\cmd.exe" 0 +3 - Push "$INSTDIR\bin" - Call AddToPath + Push "$INSTDIR\bin" + Call AddToPath SectionEnd @@ -379,9 +369,9 @@ SectionGroup "Add smartctl to drive menu" SectionEnd !macroend - !insertmacro DriveSection 0 "SMART all info" "-a" + !insertmacro DriveSection 0 "SMART all info" "-x" !insertmacro DriveSection 1 "SMART status" "-Hc" - !insertmacro DriveSection 2 "SMART attributes" "-A" + !insertmacro DriveSection 2 "SMART attributes" "-A -f brief" !insertmacro DriveSection 3 "SMART short selftest" "-t short" !insertmacro DriveSection 4 "SMART long selftest" "-t long" !insertmacro DriveSection 5 "SMART continue selective selftest" '-t "selective,cont"' @@ -397,7 +387,7 @@ Section "Uninstall" ReadRegStr $0 HKLM "System\CurrentControlSet\Services\smartd" "ImagePath" StrCmp $0 "" nosrv ExecWait "net stop smartd" - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Remove smartd service ?" IDYES 0 IDNO nosrv + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Remove smartd service ?" /SD IDNO IDYES 0 IDNO nosrv ExecWait "$INSTDIR\bin\smartd.exe remove" nosrv: @@ -411,25 +401,25 @@ Section "Uninstall" GetFileTime "$INSTDIR\bin\smartd.conf" $0 $1 GetFileTime "$INSTDIR\doc\smartd.conf" $2 $3 StrCmp "$0:$1" "$2:$3" +2 0 - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" IDYES 0 IDNO noconf + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO noconf Delete "$INSTDIR\bin\smartd.conf" noconf: ; Remove log file ? IfFileExists "$INSTDIR\bin\smartd.log" 0 +3 - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete log file$\n$INSTDIR\bin\smartd.log ?" IDYES 0 IDNO +2 + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete log file$\n$INSTDIR\bin\smartd.log ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\smartd.log" ; Remove drivedb-add file ? IfFileExists "$INSTDIR\bin\drivedb-add.h" 0 +3 - MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete local drive database file$\n$INSTDIR\bin\drivedb-add.h ?" IDYES 0 IDNO +2 + MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete local drive database file$\n$INSTDIR\bin\drivedb-add.h ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\drivedb-add.h" ; Remove files Delete "$INSTDIR\bin\smartctl.exe" Delete "$INSTDIR\bin\smartctl-nc.exe" Delete "$INSTDIR\bin\smartd.exe" - Delete "$INSTDIR\bin\syslogevt.exe" + Delete "$INSTDIR\bin\smartd_warning.cmd" ; TODO: Check for modifications? Delete "$INSTDIR\bin\drivedb.h" Delete "$INSTDIR\bin\drivedb.h.error" Delete "$INSTDIR\bin\drivedb.h.lastcheck" @@ -442,8 +432,10 @@ Section "Uninstall" Delete "$INSTDIR\bin\runcmda.exe.manifest" Delete "$INSTDIR\bin\runcmdu.exe" Delete "$INSTDIR\bin\runcmdu.exe.manifest" + Delete "$INSTDIR\bin\wtssendmsg.exe" Delete "$INSTDIR\doc\AUTHORS.txt" - Delete "$INSTDIR\doc\CHANGELOG.txt" + Delete "$INSTDIR\doc\ChangeLog.txt" + Delete "$INSTDIR\doc\ChangeLog-5.0-6.0.txt" Delete "$INSTDIR\doc\COPYING.txt" Delete "$INSTDIR\doc\INSTALL.txt" Delete "$INSTDIR\doc\NEWS.txt" @@ -477,22 +469,21 @@ Section "Uninstall" RMDir "$INSTDIR" ; Remove install dir from PATH - IfFileExists "$WINDIR\system32\cmd.exe" 0 +3 - Push "$INSTDIR\bin" - Call un.RemoveFromPath + Push "$INSTDIR\bin" + Call un.RemoveFromPath ; Remove drive menu registry entries !insertmacro DriveMenuRemove ; Check for still existing entries IfFileExists "$INSTDIR\bin\smartd.exe" 0 +3 - MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR\bin\smartd.exe could not be removed.$\nsmartd is possibly still running." + MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR\bin\smartd.exe could not be removed.$\nsmartd is possibly still running." /SD IDOK Goto +3 IfFileExists "$INSTDIR" 0 +2 - MessageBox MB_OK "Note: $INSTDIR could not be removed." + MessageBox MB_OK "Note: $INSTDIR could not be removed." /SD IDOK IfFileExists "$SMPROGRAMS\smartmontools" 0 +2 - MessageBox MB_OK "Note: $SMPROGRAMS\smartmontools could not be removed." + MessageBox MB_OK "Note: $SMPROGRAMS\smartmontools could not be removed." /SD IDOK SectionEnd @@ -530,10 +521,6 @@ Function .onInit IfFileExists "$EDITOR" +2 0 StrCpy $EDITOR "notepad.exe" - ; Hide "Add install dir to PATH" on 9x/ME - IfFileExists "$WINDIR\system32\cmd.exe" +2 0 - SectionSetText ${PATH_SECTION} "" - Call ParseCmdLine FunctionEnd diff --git a/os_win32/smartctl_res.rc.in b/os_win32/smartctl_res.rc.in new file mode 100644 index 0000000..b4d6d37 --- /dev/null +++ b/os_win32/smartctl_res.rc.in @@ -0,0 +1,34 @@ +// +// os_win32/smartctl_res.rc.in +// +// $Id: smartctl_res.rc.in 3755 2013-01-26 15:13:08Z chrfranke $ +// + +1 VERSIONINFO + FILEVERSION @BINARY_VERSION@ + PRODUCTVERSION @BINARY_VERSION@ + FILEFLAGSMASK 0x0 + FILEFLAGS 0x0 + FILEOS 0x4 // VOS__WINDOWS32 + FILETYPE 0x1 // VFT_APP + FILESUBTYPE 0x0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" + BEGIN + VALUE "CompanyName", "www.smartmontools.org" + VALUE "FileDescription", "Control and Monitor Utility for SMART Disks" + VALUE "FileVersion", "@TEXT_VERSION@" + VALUE "InternalName", "smartctl" + VALUE "LegalCopyright", "(C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org" + VALUE "OriginalFilename", "smartctl.exe" + VALUE "ProductName", "smartmontools" + VALUE "ProductVersion", "@TEXT_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x0000 + END +END diff --git a/os_win32/smartctl_vc10.vcxproj.filters b/os_win32/smartctl_vc10.vcxproj.filters deleted file mode 100644 index fbcb4bf..0000000 --- a/os_win32/smartctl_vc10.vcxproj.filters +++ /dev/null @@ -1,154 +0,0 @@ - - - - - {9321e988-dd03-496d-9933-7c8b235a46bf} - - - {bfbfd4ea-fae3-45ec-9cfb-c5424218c47c} - - - {8ff0bf5f-c96e-45c3-bcb4-7450cecfa0d4} - - - - - os_win32 - - - os_win32 - - - os_win32 - - - os_win32 - - - regex - - - regex - - - regex - - - regex - - - getopt - - - getopt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - os_win32 - - - os_win32 - - - regex - - - regex - - - getopt - - - - - - - - - - - - - - - - - - - - os_win32 - - - os_win32 - - - - - - - - - - - - - - - - - - - - - - - os_win32 - - - os_win32 - - - os_win32 - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/os_win32/smartd_res.rc.in b/os_win32/smartd_res.rc.in new file mode 100644 index 0000000..f7900ee --- /dev/null +++ b/os_win32/smartd_res.rc.in @@ -0,0 +1,37 @@ +// +// os_win32/smartd_res.rc.in +// +// $Id: smartd_res.rc.in 3756 2013-01-26 16:16:35Z chrfranke $ +// + +1 VERSIONINFO + FILEVERSION @BINARY_VERSION@ + PRODUCTVERSION @BINARY_VERSION@ + FILEFLAGSMASK 0x0 + FILEFLAGS 0x0 + FILEOS 0x4 // VOS__WINDOWS32 + FILETYPE 0x1 // VFT_APP + FILESUBTYPE 0x0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" + BEGIN + VALUE "CompanyName", "www.smartmontools.org" + VALUE "FileDescription", "SMART Disk Monitoring Daemon" + VALUE "FileVersion", "@TEXT_VERSION@" + VALUE "InternalName", "smartd" + VALUE "LegalCopyright", "(C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org" + VALUE "OriginalFilename", "smartd.exe" + VALUE "ProductName", "smartmontools" + VALUE "ProductVersion", "@TEXT_VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x0000 + END +END + +// eventlog MESSAGETABLE for syslog() emulation +#include "syslogevt.rc" diff --git a/os_win32/smartd_vc10.vcxproj.filters b/os_win32/smartd_vc10.vcxproj.filters deleted file mode 100644 index 0557738..0000000 --- a/os_win32/smartd_vc10.vcxproj.filters +++ /dev/null @@ -1,154 +0,0 @@ - - - - - {44bb110e-4d44-4d5e-8186-354210a8d3ba} - - - {fd3bb1d5-a72e-4f92-84a3-6eac97975ec7} - - - {1d1b67d5-219f-4e86-a5ff-9d94f1f751f9} - - - - - os_win32 - - - os_win32 - - - os_win32 - - - os_win32 - - - regex - - - regex - - - regex - - - regex - - - getopt - - - getopt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - os_win32 - - - os_win32 - - - os_win32 - - - os_win32 - - - os_win32 - - - regex - - - regex - - - getopt - - - - - - - - - - - - - - - - - os_win32 - - - os_win32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/os_win32/smartd_warning.cmd b/os_win32/smartd_warning.cmd new file mode 100644 index 0000000..a0eaa84 --- /dev/null +++ b/os_win32/smartd_warning.cmd @@ -0,0 +1,159 @@ +@echo off +:: +:: smartd warning script +:: +:: Copyright (C) 2012-13 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: smartd_warning.cmd 3810 2013-04-18 20:08:39Z chrfranke $ +:: + +set err= + +:: Parse options +set dryrun= +if "%1" == "--dryrun" ( + set dryrun=t + shift +) + +if not "%1" == "" ( + echo smartd warning message script + echo. + echo Usage: + echo set SMARTD_MAILER='Path to external script, empty for "blat"' + echo set SMARTD_ADDRESS='Space separated mail adresses, empty if none' + echo set SMARTD_MESSAGE='Error Message' + echo set SMARTD_FAILTYPE='Type of failure, "EMailTest" for tests' + echo set SMARTD_TFIRST='Date of first message sent, empty if none' + echo :: set SMARTD_TFIRSTEPOCH='time_t format of above' + echo set SMARTD_PREVCNT='Number of previous messages, 0 if none' + echo set SMARTD_NEXTDAYS='Number of days until next message, empty if none' + echo set SMARTD_DEVICEINFO='Device identify information' + echo :: set SMARTD_DEVICE='Device name' + echo :: set SMARTD_DEVICESTRING='Annotated device name' + echo :: set SMARTD_DEVICETYPE='Device type from -d directive, "auto" if none' + + echo smartd_warning.cmd [--dryrun] + goto EOF +) + +if "%SMARTD_ADDRESS%%SMARTD_MAILER%" == "" ( + echo smartd_warning.cmd: SMARTD_ADDRESS or SMARTD_MAILER must be set + goto EOF +) + +:: USERDNSDOMAIN may be unset if running as service +if "%USERDNSDOMAIN%" == "" ( + for /f "delims== tokens=2 usebackq" %%d in (`WMIC PATH Win32_Computersystem WHERE "PartOfDomain=TRUE" GET Domain /VALUE 2^>nul ^| find "Domain=" 2^>nul`) do set USERDNSDOMAIN=%%~d +) + +:: Format subject +set SMARTD_SUBJECT=SMART error (%SMARTD_FAILTYPE%) detected on host: %COMPUTERNAME% + +:: Temp file for message +if not "%TMP%" == "" set SMARTD_FULLMSGFILE=%TMP%\smartd_warning-%DATE%-%RANDOM%.txt +if "%TMP%" == "" set SMARTD_FULLMSGFILE=smartd_warning-%DATE%-%RANDOM%.txt + +:: Format message +( + echo This message was generated by the smartd service running on: + echo. + echo. host name: %COMPUTERNAME% + if not "%USERDNSDOMAIN%" == "" echo. DNS domain: %USERDNSDOMAIN% + if "%USERDNSDOMAIN%" == "" echo. DNS domain: [Empty] + if not "%USERDOMAIN%" == "" echo. Win domain: %USERDOMAIN% + echo. + echo The following warning/error was logged by the smartd service: + echo. + :: SMARTD_MESSAGE and SMARTD_DEVICEINFO may contain parentheses + for %%m in ("%SMARTD_MESSAGE%") do echo.%%~m + echo. + echo Device info: + for %%m in ("%SMARTD_DEVICEINFO%") do echo.%%~m + set m= + echo. + echo For details see the event log or log file of smartd. + if not "%SMARTD_FAILTYPE%" == "EmailTest" ( + echo. + echo You can also use the smartctl utility for further investigation. + if not "%SMARTD_PREVCNT%" == "0" echo The original message about this issue was sent at %SMARTD_TFIRST% + if "%SMARTD_NEXTDAYS%" == "" ( + echo No additional messages about this problem will be sent. + ) else ( if "%SMARTD_NEXTDAYS%" == "1" ( + echo Another message will be sent in 24 hours if the problem persists. + ) else ( + echo Another message will be sent in %SMARTD_NEXTDAYS% days if the problem persists. + )) + ) +) > "%SMARTD_FULLMSGFILE%" + +if not "%dryrun%" == "" ( + echo %SMARTD_FULLMSGFILE%: + type "%SMARTD_FULLMSGFILE%" + echo --EOF-- +) + +:: Check first address +set first= +for /F "tokens=1*" %%a in ("%SMARTD_ADDRESS%") do (set first=%%a) +set wtssend= +if "%first%" == "console" set wtssend=-c +if "%first%" == "active" set wtssend=-a +if "%first%" == "connected" set wtssend=-s +set first= + +if not "%wtssend%" == "" ( + :: Show Message box(es) via WTSSendMessage() + if not "%dryrun%" == "" ( + echo call wtssendmsg %wtssend% "%SMARTD_SUBJECT%" - ^< "%SMARTD_FULLMSGFILE%" + ) else ( + call wtssendmsg %wtssend% "%SMARTD_SUBJECT%" - < "%SMARTD_FULLMSGFILE%" + if errorlevel 1 set err=t + ) + :: Remove first address + for /F "tokens=1*" %%a in ("%SMARTD_ADDRESS%") do (set SMARTD_ADDRESS=%%b) +) +set wtssend= + +:: Make comma separated address list +set SMARTD_ADDRCSV= +if not "%SMARTD_ADDRESS%" == "" set SMARTD_ADDRCSV=%SMARTD_ADDRESS: =,% + +:: Use blat mailer by default +if not "%SMARTD_ADDRESS%" == "" if "%SMARTD_MAILER%" == "" set SMARTD_MAILER=blat + +:: Send mail or run command +if not "%SMARTD_ADDRCSV%" == "" ( + + :: Send mail + if not "%dryrun%" == "" ( + echo call "%SMARTD_MAILER%" - -q -subject "%SMARTD_SUBJECT%" -to "%SMARTD_ADDRCSV%" ^< "%SMARTD_FULLMSGFILE%" + ) else ( + call "%SMARTD_MAILER%" - -q -subject "%SMARTD_SUBJECT%" -to "%SMARTD_ADDRCSV%" < "%SMARTD_FULLMSGFILE%" + if errorlevel 1 set err=t + ) + +) else ( if not "%SMARTD_MAILER%" == "" ( + + :: Run command + if not "%dryrun%" == "" ( + echo call "%SMARTD_MAILER%" ^nul 2>nul + +:EOF +if not "%err%" == "" goto ERROR 2>nul diff --git a/os_win32/syslog.h b/os_win32/syslog.h index 6d1e646..3061d07 100644 --- a/os_win32/syslog.h +++ b/os_win32/syslog.h @@ -11,15 +11,15 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef SYSLOG_H #define SYSLOG_H -#define SYSLOG_H_CVSID "$Id: syslog.h,v 1.6 2008/03/04 22:09:48 ballen4705 Exp $\n" +#define SYSLOG_H_CVSID "$Id: syslog.h 3728 2012-12-13 17:57:50Z chrfranke $\n" #include diff --git a/os_win32/syslog_win32.cpp b/os_win32/syslog_win32.cpp index 3099edf..02fa4de 100644 --- a/os_win32/syslog_win32.cpp +++ b/os_win32/syslog_win32.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-8 Christian Franke + * Copyright (C) 2004-12 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 @@ -11,15 +11,13 @@ * 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. + * (for example COPYING); If not, see . * */ // 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 ".log" on 9x/ME. // If facility is set to LOG_LOCAL[0-7], log is written to // file ".log", stdout, stderr, "[1-5].log". @@ -35,8 +33,8 @@ #define WIN32_LEAN_AND_MEAN #include // RegisterEventSourceA(), ReportEventA(), ... -const char *syslog_win32_c_cvsid = "$Id: syslog_win32.cpp,v 1.8 2008/03/04 22:09:48 ballen4705 Exp $" -SYSLOG_H_CVSID; +const char *syslog_win32_cpp_cvsid = "$Id: syslog_win32.cpp 3575 2012-07-19 21:32:56Z chrfranke $" + SYSLOG_H_CVSID; #ifdef _MSC_VER // MSVC @@ -350,12 +348,8 @@ void openlog(const char *ident, int logopt, int facility) // 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); + fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n", + sl_ident, err, sl_logpath); } else { // Start event log thread diff --git a/os_win32/syslogevt.c b/os_win32/syslogevt.c deleted file mode 100644 index 08019fe..0000000 --- a/os_win32/syslogevt.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * os_win32/syslogevt.c - * - * Home page of code is: http://smartmontools.sourceforge.net - * - * Copyright (C) 2004-10 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, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -static char rcsid[] = "$Id: syslogevt.c 3257 2011-02-14 22:19:42Z manfred99 $"; - -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include - -#include "syslogevt.h" -// Compile time check for Message Ids, see also syslog_win32.cpp -typedef assert_msg_syslog [MSG_SYSLOG == 0 ? 1 : -1]; -typedef assert_msg_syslog_01[MSG_SYSLOG_01 == 1 ? 1 : -1]; -typedef assert_msg_syslog_10[MSG_SYSLOG_10 == 10 ? 1 : -1]; - - -static int usage() -{ - puts( - "syslogevt $Revision: 3257 $ Copyright (C) 2004-10 Christian Franke\n" - "Home page is http://smartmontools.sourceforge.net/\n" - "\n" - "Usage: syslogevt [-ru] name [ident ...]\n" - "\n" - "Creates registry files \"name-r.reg\" and \"name-u.reg\" to (un)register\n" - "this program as an event message file for message source(s) \"ident\".\n" - "If \"ident\" is ommited, \"name\" is used. Options:\n" - "\n" - " -r run \"regedit name-r.reg\" after creating files\n" - " -u run \"regedit name-u.reg\" after creating files\n" - "\n" - "Examples:\n" - "\n" - "syslogevt smartd (Create smartd-r.reg and smartd-u.reg)\n" - "regedit smartd-r.reg (Register syslogevt.exe for smartd messages)\n" - "\n" - "syslogevt -r smartd (Same as above in one step)\n" - "\n" - "regedit smartd-u.reg (Undo the registration)\n" - "\n" - "CAUTION: A registry entry of an existing event source with the same \"ident\"\n" - " will be overwritten by regedit without notice." - ); - return 1; -} - -main(int argc, char ** argv) -{ - int regedit, a1, ai; - char name1[30+1], name2[30+1], mypath[MAX_PATH+1]; - const char * ident; - FILE * f1, * f2; - - if (argc < 2) - return usage(); - - a1 = 1; - regedit = 0; - if (!strcmp(argv[a1], "-r")) { - regedit = 1; a1++; - } - else if (!strcmp(argv[a1], "-u")) { - regedit = -1; a1++; - } - - for (ai = a1; ai < argc; ai++) { - ident = argv[ai]; - if (!(ident[0] && strlen(ident) < sizeof(name1)-10 - && strcspn(ident, "-.:/\\") == strlen(ident) )) { - return usage(); - } - } - - if (!GetModuleFileName(NULL, mypath, sizeof(mypath)-1)) { - fputs("GetModuleFileName failed\n", stderr); - return 1; - } - - ident = argv[a1]; - strcpy(name1, ident); strcat(name1, "-r.reg"); - strcpy(name2, ident); strcat(name2, "-u.reg"); - - if (!(f1 = fopen(name1, "w"))) { - perror(name1); return 1; - } - if (!(f2 = fopen(name2, "w"))) { - perror(name2); fclose(f1); unlink(name1); return 1; - } - - fputs("REGEDIT4\n\n", f1); - fputs("REGEDIT4\n\n", f2); - - for (ai = (argc > a1+1 ? a1+1 : a1); ai < argc; ai++) { - int i; - ident = argv[ai]; - fputs("[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\", f1); - fputs(ident, f1); fputs("]\n\"EventMessageFile\"=\"", f1); - for (i = 0; mypath[i]; i++) { - if (mypath[i] == '\\') - fputc('\\', f1); - fputc(mypath[i], f1); - } - fputs("\"\n\"TypesSupported\"=dword:00000007\n\n", f1); - - fputs("[-HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\", f2); - fputs(ident, f2); fputs("]\n\n", f2); - } - - fclose(f1); - fclose(f2); - - if (GetVersion() & 0x80000000) { - puts("Warning: Event log not supported on Win9x/ME\n"); - if (regedit) - return 1; - } - - if (regedit) { - if (spawnlp(P_WAIT, "regedit", "regedit", (regedit > 0 ? name1 : name2), (const char *)0) == -1) { - fputs("regedit: cannot execute\n", stderr); - return 1; - } - } - else { - fputs("Files generated. Use\n\n regedit ", stdout); - puts(name1); - fputs("\nto register event message file, and\n\n regedit ", stdout); - puts(name2); - fputs("\nto remove registration later.\n\n" - "Do not remove this program when registered.\n", stdout); - } - - return 0; -} diff --git a/os_win32/syslogevt.mc b/os_win32/syslogevt.mc index c477ec6..c6d3ac6 100644 --- a/os_win32/syslogevt.mc +++ b/os_win32/syslogevt.mc @@ -11,12 +11,12 @@ ; * 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. +; * (for example COPYING); if not, write to the Free Software Foundation, +; * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ; * ; */ ; -;// $Id: syslogevt.mc 3166 2010-09-24 19:43:31Z chrfranke $ +;// $Id: syslogevt.mc 3727 2012-12-13 17:23:06Z samm2 $ ; ;// Use message compiler "mc" or "windmc" to generate ;// syslogevt.rc, syslogevt.h, msg00001.bin diff --git a/os_win32/syslogevt_vc10.vcxproj b/os_win32/syslogevt_vc10.vcxproj deleted file mode 100644 index 8654915..0000000 --- a/os_win32/syslogevt_vc10.vcxproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {FAB7557B-86EA-405D-B49D-33AB3F4D3E33} - syslogevt - Win32Proj - syslogevt - - - - Application - MultiByte - true - - - Application - MultiByte - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - syslogevt_vc10.d\ - syslogevt_vc10.d\ - true - .\ - syslogevt_vc10.r\ - false - - - - Disabled - _DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level3 - EditAndContinue - - - true - Console - MachineX86 - - - - - NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - - - Level3 - ProgramDatabase - - - syslogevt.exe - true - $(IntDir)$(TargetName).pdb - Console - true - true - MachineX86 - - - - - - - - mc -r $(IntDir) syslogevt.mc - mc -r $(IntDir) syslogevt.mc - - $(IntDir)syslogevt.rc;$(IntDir)msg00001.bin;syslogevt.h;%(Outputs) - mc -r $(IntDir) syslogevt.mc - mc -r $(IntDir) syslogevt.mc - - $(IntDir)syslogevt.rc;$(IntDir)msg00001.bin;syslogevt.h;%(Outputs) - - - - - - \ No newline at end of file diff --git a/os_win32/runcmd_vc10.vcxproj b/os_win32/vc10/runcmd.vcxproj similarity index 94% rename from os_win32/runcmd_vc10.vcxproj rename to os_win32/vc10/runcmd.vcxproj index f254c6f..36a294e 100644 --- a/os_win32/runcmd_vc10.vcxproj +++ b/os_win32/vc10/runcmd.vcxproj @@ -40,14 +40,12 @@ true - .\runcmdu_vc10.d\ - .\runcmdu_vc10.d\ + $(Configuration)\$(ProjectName).tmp\ $(ProjectName)u false - .\runcmdu_vc10.r\ - .\runcmdu_vc10.r\ + $(Configuration)\$(ProjectName).tmp\ $(ProjectName)u @@ -77,7 +75,7 @@ - + diff --git a/os_win32/smartd_vc10.vcxproj b/os_win32/vc10/smartctl.vcxproj similarity index 66% rename from os_win32/smartd_vc10.vcxproj rename to os_win32/vc10/smartctl.vcxproj index af63ceb..20e0b18 100644 --- a/os_win32/smartd_vc10.vcxproj +++ b/os_win32/vc10/smartctl.vcxproj @@ -11,10 +11,10 @@ - {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6} - smartd + {3AFEDCDD-D289-4543-A91D-EFBA6C710247} + smartctl Win32Proj - smartd + smartctl @@ -38,17 +38,15 @@ <_ProjectFileVersion>10.0.30319.1 - .\smartd_vc10.d\ - .\smartd_vc10.d\ + $(Configuration)\$(ProjectName).tmp\ true - .\ - .\smartd_vc10.r\ + $(Configuration)\$(ProjectName).tmp\ false Disabled - .;..\getopt;..\regex;$(IntDir);%(AdditionalIncludeDirectories) + .;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories) _DEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) true EnableFastChecks @@ -67,7 +65,7 @@ - .;..\getopt;..\regex;$(IntDir);%(AdditionalIncludeDirectories) + .;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories) NDEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) MultiThreadedDLL @@ -78,7 +76,6 @@ true - $(IntDir)$(TargetName).pdb Console true true @@ -86,214 +83,207 @@ - - - - - + + + true true - - + true true - + + true true - - - - - + + true true - + true true - - - + + + + + + true true - - + + + true true - + + true true - + true true - + true true - + true true - + true true - + true true - + true true - + true true - - - - + true true - + + + + + + true true - - + - - - - - - - - - - - + + + + + true true - + true true - - true - true - - - Copy %(FullPath) $(IntDir)config.h - copy %(FullPath) $(IntDir)config.h - - $(IntDir)config.h;%(Outputs) - Copy %(FullPath) $(IntDir)config.h - copy %(FullPath) $(IntDir)config.h - - $(IntDir)config.h;%(Outputs) - - - - - - - - - + + + + + + + + + true true - + true true - - + + + + + + + + + true true - + true true - + true true - + true true - + true true - + true true - + true true - + true true - - + true true - + true true - - Copy %(FullPath) $(IntDir)svnversion.h - copy %(FullPath) $(IntDir)svnversion.h - - $(IntDir)svnversion.h;%(Outputs) - Copy %(FullPath) $(IntDir)svnversion.h - copy %(FullPath) $(IntDir)svnversion.h - - $(IntDir)svnversion.h;%(Outputs) - - + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + diff --git a/os_win32/vc10/smartctl.vcxproj.filters b/os_win32/vc10/smartctl.vcxproj.filters new file mode 100644 index 0000000..be8a021 --- /dev/null +++ b/os_win32/vc10/smartctl.vcxproj.filters @@ -0,0 +1,171 @@ + + + + + {9321e988-dd03-496d-9933-7c8b235a46bf} + + + {bfbfd4ea-fae3-45ec-9cfb-c5424218c47c} + + + {8ff0bf5f-c96e-45c3-bcb4-7450cecfa0d4} + + + + + os_win32 + + + os_win32 + + + os_win32 + + + regex + + + regex + + + regex + + + regex + + + getopt + + + getopt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + regex + + + regex + + + getopt + + + + + + + + + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + + + + + + + + + + + + + + + + + + + os_win32 + + + + os_win32 + + + os_win32 + + + os_win32 + + + + + os_win32 + + + os_win32 + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + \ No newline at end of file diff --git a/os_win32/smartctl_vc10.vcxproj b/os_win32/vc10/smartd.vcxproj similarity index 66% rename from os_win32/smartctl_vc10.vcxproj rename to os_win32/vc10/smartd.vcxproj index 97f5bcb..75304c7 100644 --- a/os_win32/smartctl_vc10.vcxproj +++ b/os_win32/vc10/smartd.vcxproj @@ -11,10 +11,10 @@ - {3AFEDCDD-D289-4543-A91D-EFBA6C710247} - smartctl + {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6} + smartd Win32Proj - smartctl + smartd @@ -38,17 +38,15 @@ <_ProjectFileVersion>10.0.30319.1 - .\smartctl_vc10.d\ - .\smartctl_vc10.d\ + $(Configuration)\$(ProjectName).tmp\ true - .\ - .\smartctl_vc10.r\ + $(Configuration)\$(ProjectName).tmp\ false Disabled - .;..\getopt;..\regex;$(IntDir);%(AdditionalIncludeDirectories) + .;..;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories) _DEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) true EnableFastChecks @@ -67,7 +65,7 @@ - .;..\getopt;..\regex;$(IntDir);%(AdditionalIncludeDirectories) + .;..;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories) NDEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) MultiThreadedDLL @@ -78,7 +76,6 @@ true - $(IntDir)$(TargetName).pdb Console true true @@ -86,217 +83,227 @@ - + true true - + + + + + true true - + + true true - - + true true - - + + + + + true true - + true true - - - - - - + + + true true - - - + + true true - - + true true - + true true - + true true - + true true - + true true - + true true - + true true - + true true - + + + + true true - - - - - - + true true - + + - + true true - - + + + + + + + + + + + + + + true true - + true true - - - - - - - - - + + true + true + + + + + + + + + true true - + true true - - - Copy %(FullPath) $(IntDir)config.h - copy %(FullPath) $(IntDir)config.h - - $(IntDir)config.h;%(Outputs) - Copy %(FullPath) $(IntDir)config.h - copy %(FullPath) $(IntDir)config.h - - $(IntDir)config.h;%(Outputs) - - - - - - - - - + + true true - + true true - + true true - + true true - + true true - + true true - + true true - + true true - + + true true - + true true - - - - - Copy %(FullPath) $(IntDir)svnversion.h - copy %(FullPath) $(IntDir)svnversion.h - - $(IntDir)svnversion.h;%(Outputs) - Copy %(FullPath) $(IntDir)svnversion.h - copy %(FullPath) $(IntDir)svnversion.h - - $(IntDir)svnversion.h;%(Outputs) + + + + + + + + + + Document + mc -r $(IntDir) ..\syslogevt.mc + mc -r $(IntDir) ..\syslogevt.mc + $(IntDir)syslogevt.rc;$(IntDir)msg00001.bin;syslogevt.h;%(Outputs) + mc -r $(IntDir) ..\syslogevt.mc + mc -r $(IntDir) ..\syslogevt.mc + $(IntDir)syslogevt.rc;$(IntDir)msg00001.bin;syslogevt.h;%(Outputs) - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + true + true + + + $(IntDir) + $(IntDir) + diff --git a/os_win32/vc10/smartd.vcxproj.filters b/os_win32/vc10/smartd.vcxproj.filters new file mode 100644 index 0000000..910d829 --- /dev/null +++ b/os_win32/vc10/smartd.vcxproj.filters @@ -0,0 +1,173 @@ + + + + + {44bb110e-4d44-4d5e-8186-354210a8d3ba} + + + {fd3bb1d5-a72e-4f92-84a3-6eac97975ec7} + + + {1d1b67d5-219f-4e86-a5ff-9d94f1f751f9} + + + + + os_win32 + + + os_win32 + + + os_win32 + + + regex + + + regex + + + regex + + + regex + + + getopt + + + getopt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + os_win32 + + + os_win32 + + + regex + + + regex + + + getopt + + + + + + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + + + + + + + + + + + + + + + + + + + os_win32 + + + + os_win32 + + + os_win32 + + + + + + + + + + + + + + + + + + + + + os_win32 + + + os_win32 + + + + + os_win32 + + + \ No newline at end of file diff --git a/os_win32/smartmontools_vc10.sln b/os_win32/vc10/smartmontools.sln similarity index 74% rename from os_win32/smartmontools_vc10.sln rename to os_win32/vc10/smartmontools.sln index 0cc69fe..c6530b4 100644 --- a/os_win32/smartmontools_vc10.sln +++ b/os_win32/vc10/smartmontools.sln @@ -1,13 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartctl", "smartctl_vc10.vcxproj", "{3AFEDCDD-D289-4543-A91D-EFBA6C710247}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartctl", "smartctl.vcxproj", "{3AFEDCDD-D289-4543-A91D-EFBA6C710247}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartd", "smartd_vc10.vcxproj", "{C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartd", "smartd.vcxproj", "{C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "syslogevt", "syslogevt_vc10.vcxproj", "{FAB7557B-86EA-405D-B49D-33AB3F4D3E33}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runcmd", "runcmd.vcxproj", "{11A4B619-D97B-499F-AF17-CF9F80BF70E8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runcmd", "runcmd_vc10.vcxproj", "{11A4B619-D97B-499F-AF17-CF9F80BF70E8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wtssendmsg", "wtssendmsg.vcxproj", "{0B6B7D19-07AE-41DD-B70E-2F74362A4EC0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,14 +23,14 @@ Global {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Debug|Win32.Build.0 = Debug|Win32 {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Release|Win32.ActiveCfg = Release|Win32 {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Release|Win32.Build.0 = Release|Win32 - {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Debug|Win32.ActiveCfg = Debug|Win32 - {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Debug|Win32.Build.0 = Debug|Win32 - {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Release|Win32.ActiveCfg = Release|Win32 - {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Release|Win32.Build.0 = Release|Win32 {11A4B619-D97B-499F-AF17-CF9F80BF70E8}.Debug|Win32.ActiveCfg = Debug|Win32 {11A4B619-D97B-499F-AF17-CF9F80BF70E8}.Debug|Win32.Build.0 = Debug|Win32 {11A4B619-D97B-499F-AF17-CF9F80BF70E8}.Release|Win32.ActiveCfg = Release|Win32 {11A4B619-D97B-499F-AF17-CF9F80BF70E8}.Release|Win32.Build.0 = Release|Win32 + {0B6B7D19-07AE-41DD-B70E-2F74362A4EC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {0B6B7D19-07AE-41DD-B70E-2F74362A4EC0}.Debug|Win32.Build.0 = Debug|Win32 + {0B6B7D19-07AE-41DD-B70E-2F74362A4EC0}.Release|Win32.ActiveCfg = Release|Win32 + {0B6B7D19-07AE-41DD-B70E-2F74362A4EC0}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/os_win32/vc10/wtssendmsg.vcxproj b/os_win32/vc10/wtssendmsg.vcxproj new file mode 100644 index 0000000..92e143a --- /dev/null +++ b/os_win32/vc10/wtssendmsg.vcxproj @@ -0,0 +1,82 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + {0B6B7D19-07AE-41DD-B70E-2F74362A4EC0} + Win32Proj + wtssendmsg + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName).tmp\ + + + false + $(Configuration)\$(ProjectName).tmp\ + + + + Level3 + Disabled + _DEBUG;_CRT_NONSTDC_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + wtsapi32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + NDEBUG;_CRT_NONSTDC_NO_WARNINGS;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + wtsapi32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/os_win32/wmiquery.cpp b/os_win32/wmiquery.cpp index 5181f92..ade992d 100644 --- a/os_win32/wmiquery.cpp +++ b/os_win32/wmiquery.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2011 Christian Franke + * Copyright (C) 2011-13 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 @@ -23,7 +23,7 @@ #include -const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 3243 2011-01-19 20:03:47Z chrfranke $" +const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 3802 2013-03-24 18:36:21Z chrfranke $" WMIQUERY_H_CVSID; @@ -67,7 +67,7 @@ std::string wbem_object::get_str(const char * name) /*const*/ return s; VARIANT var; VariantInit(&var); - if (m_intf->Get(com_bstr(name), 0L, &var, (CIMTYPE*)0, (long*)0) /* != WBEM_S_NO_ERROR */) + if (m_intf->Get(com_bstr(name), 0L, &var, (CIMTYPE*)0, (LPLONG)0) /* != WBEM_S_NO_ERROR */) return s; if (var.vt == VT_BSTR) diff --git a/os_win32/wtssendmsg.c b/os_win32/wtssendmsg.c new file mode 100644 index 0000000..3298dfc --- /dev/null +++ b/os_win32/wtssendmsg.c @@ -0,0 +1,137 @@ +/* + * WTSSendMessage() command line tool + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2012 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 . + * + */ + +#define WINVER 0x0500 +#define _WIN32_WINNT WINVER + +char svnid[] = "$Id: wtssendmsg.c 3714 2012-11-24 16:34:47Z chrfranke $"; + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + + +static int usage() +{ + printf("wtssendmsg $Revision: 3714 $ - Display a message box on client desktops\n" + "Copyright (C) 2012 Christian Franke, smartmontools.org\n\n" + "Usage: wtssendmsg [-cas] [-v] [\"Caption\"] \"Message\"|-\n" + " wtssendmsg -v\n\n" + " -c Console session [default]\n" + " -a Active sessions\n" + " -s Connected sessions\n" + " -v List sessions\n" + ); + return 1; +} + +int main(int argc, const char **argv) +{ + int mode = 0, verbose = 0, status = 0, i; + const char * message = 0, * caption = ""; + char msgbuf[1024]; + WTS_SESSION_INFOA * sessions; DWORD count; + + for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1]; i++) { + int j; + for (j = 1; argv[i][j]; j++) + switch (argv[i][j]) { + case 'c': mode = 0; break; + case 'a': mode = 1; break; + case 's': mode = 2; break; + case 'v': verbose = 1; break; + default: return usage(); + } + } + + if (i < argc) { + if (i+1 < argc) + caption = argv[i++]; + + message = argv[i++]; + if (i < argc) + return usage(); + + if (!strcmp(message, "-")) { + // Read message from stdin + i = fread(msgbuf, 1, sizeof(msgbuf)-1, stdin); + if (i < 0) { + perror("stdin"); + return 1; + } + msgbuf[i] = 0; + message = msgbuf; + } + } + else { + if (!verbose) + return usage(); + } + + // Get session list + if (!WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count)) { + fprintf(stderr, "WTSEnumerateSessions() failed\n"); + return 1; + } + + for (i = 0; i < (int)count; i++) { + + if (verbose) { + printf("Session %d (\"%s\", State=%d)%s", + i, sessions[i].pWinStationName, sessions[i].State, + (!message ? "\n" : ": ")); + if (!message) + continue; // List sessions only + fflush(stdout); + } + + if ( !strcmpi(sessions[i].pWinStationName, "Console") + || (mode >= 1 && sessions[i].State == WTSActive) + || (mode >= 2 && sessions[i].State == WTSConnected)) { + + // Send Message, don't wait for OK button + DWORD result; + if (WTSSendMessageA(WTS_CURRENT_SERVER_HANDLE, sessions[i].SessionId, + (char *)caption, strlen(caption), + (char *)message, strlen(message), + MB_OK|MB_ICONEXCLAMATION, 0 /*Timeout*/, + &result, FALSE /*!Wait*/)) { + if (verbose) + printf("message sent\n"); + } + else { + status = 1; + if (verbose) + printf("WTSSendMessage() failed with error=%d\n", (int)GetLastError()); + else + fprintf(stderr, "Session %d (\"%s\", State=%d): WTSSendMessage() failed with error=%d\n", + i, sessions[i].pWinStationName, sessions[i].State, (int)GetLastError()); + } + } + else { + if (verbose) + printf("ignored\n"); + } + } + + WTSFreeMemory(sessions); + + return status; +} diff --git a/regex/regcomp.c b/regex/regcomp.c index f25ecae..dcb7143 100644 --- a/regex/regcomp.c +++ b/regex/regcomp.c @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, int length, reg_syntax_t syntax); diff --git a/regex/regex.c b/regex/regex.c index 98d86e1..521a761 100644 --- a/regex/regex.c +++ b/regex/regex.c @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ #ifdef _LIBC /* We have to keep the namespace clean. */ diff --git a/regex/regex.h b/regex/regex.h index 9575857..dc96a34 100644 --- a/regex/regex.h +++ b/regex/regex.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ #ifndef _REGEX_H #define _REGEX_H 1 diff --git a/regex/regex_internal.c b/regex/regex_internal.c index f969c7c..03484d2 100644 --- a/regex/regex_internal.c +++ b/regex/regex_internal.c @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ static void re_string_construct_common (const char *str, int len, re_string_t *pstr, diff --git a/regex/regex_internal.h b/regex/regex_internal.h index bf84ad6..28d300b 100644 --- a/regex/regex_internal.h +++ b/regex/regex_internal.h @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ #ifndef _REGEX_INTERNAL_H #define _REGEX_INTERNAL_H 1 diff --git a/regex/regexec.c b/regex/regexec.c index 6ea14a6..3296028 100644 --- a/regex/regexec.c +++ b/regex/regexec.c @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, re_string_t *input, int n); diff --git a/scsiata.cpp b/scsiata.cpp index 7ce6d5e..f7b790d 100644 --- a/scsiata.cpp +++ b/scsiata.cpp @@ -4,7 +4,7 @@ * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2006-12 Douglas Gilbert - * Copyright (C) 2009-12 Christian Franke + * Copyright (C) 2009-13 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 @@ -12,8 +12,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * The code in this file is based on the SCSI to ATA Translation (SAT) * draft found at http://www.t10.org . The original draft used for this @@ -62,7 +62,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 3519 2012-03-06 20:01:44Z chrfranke $"; +const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 3741 2013-01-02 17:06:54Z chrfranke $"; /* This is a slightly stretched SCSI sense "descriptor" format header. The addition is to allow the 0x70 and 0x71 response codes. The idea @@ -92,12 +92,6 @@ struct sg_scsi_sense_hdr { static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, struct sg_scsi_sense_hdr * sshp); -/* Attempt to find the first SCSI sense data descriptor that matches the - given 'desc_type'. If found return pointer to start of sense data - descriptor; otherwise (including fixed format sense data) returns NULL. */ -static const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, - int sense_len, int desc_type); - #define SAT_ATA_PASSTHROUGH_12LEN 12 #define SAT_ATA_PASSTHROUGH_16LEN 16 @@ -147,6 +141,8 @@ sat_device::sat_device(smart_interface * intf, scsi_device * scsidev, hide_ata(); // Start as SCSI, switch to ATA in autodetect_open() else hide_scsi(); // ATA always + if (strcmp(scsidev->get_dev_type(), "scsi")) + set_info().dev_type += strprintf("+%s", scsidev->get_dev_type()); set_info().info_name = strprintf("%s [%sSAT]", scsidev->get_info_name(), (enable_auto ? "SCSI/" : "")); @@ -232,10 +228,12 @@ sat_device::~sat_device() throw() bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { - if (!ata_cmd_is_ok(in, - true, // data_out_support - true, // multi_sector_support - true) // ata_48bit_support + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + ata_device::supports_multi_sector | + ata_device::supports_48bit, + "SAT") ) return false; @@ -535,32 +533,6 @@ static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, } -static const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, - int sense_len, int desc_type) -{ - int add_sen_len, add_len, desc_len, k; - const unsigned char * descp; - - if ((sense_len < 8) || (0 == (add_sen_len = sensep[7]))) - return NULL; - if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) - return NULL; - add_sen_len = (add_sen_len < (sense_len - 8)) ? - add_sen_len : (sense_len - 8); - descp = &sensep[8]; - for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { - descp += desc_len; - add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; - desc_len = add_len + 2; - if (descp[0] == desc_type) - return descp; - if (add_len < 0) /* short descriptor ?? */ - break; - } - return NULL; -} - - // Call scsi_pass_through and check sense. // TODO: Provide as member function of class scsi_device (?) static bool scsi_pass_through_and_check(scsi_device * scsidev, scsi_cmnd_io * iop, @@ -925,7 +897,8 @@ class usbjmicron_device { public: usbjmicron_device(smart_interface * intf, scsi_device * scsidev, - const char * req_type, bool ata_48bit_support, int port); + const char * req_type, bool prolific, + bool ata_48bit_support, int port); virtual ~usbjmicron_device() throw(); @@ -936,16 +909,19 @@ public: private: bool get_registers(unsigned short addr, unsigned char * buf, unsigned short size); + bool m_prolific; bool m_ata_48bit_support; int m_port; }; usbjmicron_device::usbjmicron_device(smart_interface * intf, scsi_device * scsidev, - const char * req_type, bool ata_48bit_support, int port) + const char * req_type, bool prolific, + bool ata_48bit_support, int port) : smart_device(intf, scsidev->get_dev_name(), "usbjmicron", req_type), tunnelled_device(scsidev), - m_ata_48bit_support(ata_48bit_support), m_port(port) + m_prolific(prolific), m_ata_48bit_support(ata_48bit_support), + m_port(port >= 0 || !prolific ? port : 0) { set_info().info_name = strprintf("%s [USB JMicron]", scsidev->get_info_name()); } @@ -989,24 +965,14 @@ bool usbjmicron_device::open() bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { - if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // !multi_sector_support - m_ata_48bit_support) // limited, see below + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_smart_status | + (m_ata_48bit_support ? ata_device::supports_48bit_hi_null : 0), + "JMicron") ) return false; - bool is_smart_status = ( in.in_regs.command == ATA_SMART_CMD - && in.in_regs.features == ATA_SMART_STATUS); - - // Support output registers for SMART STATUS - if (in.out_needed.is_set() && !is_smart_status) - return set_err(ENOSYS, "ATA output registers not supported"); - - // Support 48-bit commands with zero high bytes - if (in.in_regs.is_real_48bit_cmd()) - return set_err(ENOSYS, "48-bit ATA commands not fully supported"); - if (m_port < 0) return set_err(EIO, "Unknown JMicron port"); @@ -1016,6 +982,9 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou bool rwbit = true; unsigned char smart_status = 0; + bool is_smart_status = ( in.in_regs.command == ATA_SMART_CMD + && in.in_regs.features == ATA_SMART_STATUS); + if (is_smart_status && in.out_needed.is_set()) { io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = 1; @@ -1042,7 +1011,7 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou } // Build pass through command - unsigned char cdb[12]; + unsigned char cdb[14]; cdb[ 0] = 0xdf; cdb[ 1] = (rwbit ? 0x10 : 0x00); cdb[ 2] = 0x00; @@ -1055,9 +1024,12 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou cdb[ 9] = in.in_regs.lba_high; cdb[10] = in.in_regs.device | (m_port == 0 ? 0xa0 : 0xb0); cdb[11] = in.in_regs.command; + // Prolific PL3507 + cdb[12] = 0x06; + cdb[13] = 0x7b; io_hdr.cmnd = cdb; - io_hdr.cmnd_len = sizeof(cdb); + io_hdr.cmnd_len = (!m_prolific ? 12 : 14); scsi_device * scsidev = get_tunnel_dev(); if (!scsi_pass_through_and_check(scsidev, &io_hdr, @@ -1104,7 +1076,7 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou bool usbjmicron_device::get_registers(unsigned short addr, unsigned char * buf, unsigned short size) { - unsigned char cdb[12]; + unsigned char cdb[14]; cdb[ 0] = 0xdf; cdb[ 1] = 0x10; cdb[ 2] = 0x00; @@ -1117,6 +1089,9 @@ bool usbjmicron_device::get_registers(unsigned short addr, cdb[ 9] = 0x00; cdb[10] = 0x00; cdb[11] = 0xfd; + // Prolific PL3507 + cdb[12] = 0x06; + cdb[13] = 0x7b; scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr)); @@ -1125,6 +1100,7 @@ bool usbjmicron_device::get_registers(unsigned short addr, io_hdr.dxferp = buf; io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); + io_hdr.cmnd_len = (!m_prolific ? 12 : 14); scsi_device * scsidev = get_tunnel_dev(); if (!scsi_pass_through_and_check(scsidev, &io_hdr, @@ -1169,10 +1145,11 @@ usbsunplus_device::~usbsunplus_device() throw() bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { - if (!ata_cmd_is_ok(in, - true, // data_out_support - false, // !multi_sector_support - true) // ata_48bit_support + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + ata_device::supports_48bit, + "Sunplus") ) return false; @@ -1325,6 +1302,11 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc else if (!strncmp(type, "usbjmicron", 10)) { const char * t = type + 10; + bool prolific = false; + if (!strncmp(t, ",p", 2)) { + t += 2; + prolific = true; + } bool ata_48bit_support = false; if (!strncmp(t, ",x", 2)) { t += 2; @@ -1333,10 +1315,10 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc int port = -1, n = -1; if (*t && !( (sscanf(t, ",%d%n", &port, &n) == 1 && n == (int)strlen(t) && 0 <= port && port <= 1))) { - set_err(EINVAL, "Option '-d usbmicron[,x],' requires to be 0 or 1"); + set_err(EINVAL, "Option '-d usbjmicron[,p][,x],' requires to be 0 or 1"); return 0; } - return new usbjmicron_device(this, scsidev, type, ata_48bit_support, port); + return new usbjmicron_device(this, scsidev, type, prolific, ata_48bit_support, port); } else if (!strcmp(type, "usbsunplus")) { diff --git a/scsicmds.cpp b/scsicmds.cpp index 30a9356..3adc913 100644 --- a/scsicmds.cpp +++ b/scsicmds.cpp @@ -7,7 +7,7 @@ * Copyright (C) 1999-2000 Michael Cornwell * * Additional SCSI work: - * Copyright (C) 2003-10 Douglas Gilbert + * Copyright (C) 2003-13 Douglas Gilbert * * 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 @@ -15,8 +15,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -49,14 +48,46 @@ #include "dev_interface.h" #include "utility.h" -const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 3302 2011-03-25 23:04:36Z dpgilbert $" +const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 3807 2013-04-18 17:11:12Z chrfranke $" SCSICMDS_H_CVSID; // Print SCSI debug messages? unsigned char scsi_debugmode = 0; +supported_vpd_pages * supported_vpd_pages_p = NULL; + + +supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0) +{ + unsigned char b[0x1fc]; /* size chosen for old INQUIRY command */ + int n; + + memset(b, 0, sizeof(b)); + if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES, + b, sizeof(b)))) { + num_valid = (b[2] << 8) + b[3]; + n = sizeof(pages); + if (num_valid > n) + num_valid = n; + memcpy(pages, b + 4, num_valid); + } +} + +bool +supported_vpd_pages::is_supported(int vpd_page_num) const +{ + /* Supported VPD pages numbers start at offset 4 and should be in + * ascending order but don't assume that. */ + for (int k = 0; k < num_valid; ++k) { + if (vpd_page_num == pages[k]) + return true; + } + return false; +} + /* output binary in hex and optionally ascii */ -void dStrHex(const char* str, int len, int no_ascii) +void +dStrHex(const char* str, int len, int no_ascii) { const char* p = str; unsigned char c; @@ -67,11 +98,11 @@ void dStrHex(const char* str, int len, int no_ascii) 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); + k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a); buff[k + 1] = ' '; if (bpos >= ((bpstart + (9 * 3)))) bpos++; @@ -82,7 +113,7 @@ void dStrHex(const char* str, int len, int no_ascii) bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; - sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); + snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if (no_ascii) buff[cpos++] = ' '; @@ -98,7 +129,7 @@ void dStrHex(const char* str, int len, int no_ascii) cpos = cpstart; a += 16; memset(buff,' ',80); - k = sprintf(buff + 1, "%.2x", a); + k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a); buff[k + 1] = ' '; } } @@ -133,13 +164,15 @@ static struct scsi_opcode_name opcode_name_arr[] = { {READ_CAPACITY_16, "read capacity(16)"}, /* 0x9e,0x10 */ {REPORT_LUNS, "report luns"}, /* 0xa0 */ {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */ + {READ_DEFECT_12, "read defect list(12)"}, /* 0xb7 */ }; static const char * vendor_specific = ""; /* Need to expand to take service action into account. For commands * of interest the service action is in the 2nd command byte */ -const char * scsi_get_opcode_name(UINT8 opcode) +const char * +scsi_get_opcode_name(UINT8 opcode) { int k; int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]); @@ -157,16 +190,16 @@ const char * scsi_get_opcode_name(UINT8 opcode) return NULL; } - -void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf, - struct scsi_sense_disect * out) +void +scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf, + struct scsi_sense_disect * out) { int resp_code; memset(out, 0, sizeof(struct scsi_sense_disect)); if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) { resp_code = (io_buf->sensep[0] & 0x7f); - out->error_code = resp_code; + out->resp_code = resp_code; if (resp_code >= 0x72) { out->sense_key = (io_buf->sensep[1] & 0xf); out->asc = io_buf->sensep[2]; @@ -181,14 +214,15 @@ void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf, } } -int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) +int +scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) { switch (sinfo->sense_key) { case SCSI_SK_NO_SENSE: case SCSI_SK_RECOVERED_ERR: return SIMPLE_NO_ERROR; case SCSI_SK_NOT_READY: - if (SCSI_ASC_NO_MEDIUM == sinfo->asc) + if (SCSI_ASC_NO_MEDIUM == sinfo->asc) return SIMPLE_ERR_NO_MEDIUM; else if (SCSI_ASC_NOT_READY == sinfo->asc) { if (0x1 == sinfo->ascq) @@ -203,7 +237,7 @@ int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) case SCSI_SK_ILLEGAL_REQUEST: if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc) return SIMPLE_ERR_BAD_OPCODE; - else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc) + else if (SCSI_ASC_INVALID_FIELD == sinfo->asc) return SIMPLE_ERR_BAD_FIELD; else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc) return SIMPLE_ERR_BAD_PARAM; @@ -218,34 +252,35 @@ int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) } } -const char * scsiErrString(int scsiErr) +const char * +scsiErrString(int scsiErr) { if (scsiErr < 0) return strerror(-scsiErr); switch (scsiErr) { - case SIMPLE_NO_ERROR: + case SIMPLE_NO_ERROR: return "no error"; - case SIMPLE_ERR_NOT_READY: + case SIMPLE_ERR_NOT_READY: return "device not ready"; - case SIMPLE_ERR_BAD_OPCODE: + case SIMPLE_ERR_BAD_OPCODE: return "unsupported scsi opcode"; - case SIMPLE_ERR_BAD_FIELD: + case SIMPLE_ERR_BAD_FIELD: return "unsupported field in scsi command"; - case SIMPLE_ERR_BAD_PARAM: + case SIMPLE_ERR_BAD_PARAM: return "badly formed scsi parameters"; - case SIMPLE_ERR_BAD_RESP: + case SIMPLE_ERR_BAD_RESP: return "scsi response fails sanity test"; - case SIMPLE_ERR_NO_MEDIUM: + case SIMPLE_ERR_NO_MEDIUM: return "no medium present"; - case SIMPLE_ERR_BECOMING_READY: + case SIMPLE_ERR_BECOMING_READY: return "device will be ready soon"; - case SIMPLE_ERR_TRY_AGAIN: + case SIMPLE_ERR_TRY_AGAIN: return "unit attention reported, try again"; - case SIMPLE_ERR_MEDIUM_HARDWARE: + case SIMPLE_ERR_MEDIUM_HARDWARE: return "medium or hardware error (serious)"; - case SIMPLE_ERR_UNKNOWN: + case SIMPLE_ERR_UNKNOWN: return "unknown error (unexpected sense key)"; - case SIMPLE_ERR_ABORTED_COMMAND: + case SIMPLE_ERR_ABORTED_COMMAND: return "aborted command"; default: return "unknown error"; @@ -261,9 +296,9 @@ const char * scsiErrString(int scsiErr) * descriptor; returns -1 if normal end condition and -2 for an abnormal * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ -int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, - int page_len, int * off, int m_assoc, - int m_desig_type, int m_code_set) +int +scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, + int * off, int m_assoc, int m_desig_type, int m_code_set) { const unsigned char * ucp; int k, c_set, assoc, desig_type; @@ -290,20 +325,21 @@ int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, /* Decode VPD page 0x83 logical unit designator into a string. If both * numeric address and SCSI name string present, prefer the former. * Returns 0 on success, -1 on error with error string in s. */ -int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, - int slen, int * transport) +int +scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen, + int * transport) { int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns; const unsigned char * ucp; const unsigned char * ip; - char * orig_s = s; + int si = 0; if (transport) - *transport = -1; + *transport = -1; if (slen < 32) { - if (slen > 0) - s[0] = '\0'; - return -1; + if (slen > 0) + s[0] = '\0'; + return -1; } have_scsi_ns = 0; s[0] = '\0'; @@ -312,14 +348,14 @@ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, ucp = b + off; i_len = ucp[3]; if ((off + i_len + 4) > blen) { - s += sprintf(s, "error: designator length"); - return -1; + snprintf(s+si, slen-si, "error: designator length"); + return -1; } assoc = ((ucp[1] >> 4) & 0x3); - if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0)) - *transport = (ucp[0] >> 4) & 0xf; - if (0 != assoc) - continue; + if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0)) + *transport = (ucp[0] >> 4) & 0xf; + if (0 != assoc) + continue; ip = ucp + 4; c_set = (ucp[0] & 0xf); desig_type = (ucp[1] & 0xf); @@ -330,52 +366,52 @@ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, break; case 2: /* EUI-64 based */ if ((8 != i_len) && (12 != i_len) && (16 != i_len)) { - s += sprintf(s, "error: EUI-64 length"); - return -1; - } - if (have_scsi_ns) - s = orig_s; - s += sprintf(s, "0x"); + snprintf(s+si, slen-si, "error: EUI-64 length"); + return -1; + } + if (have_scsi_ns) + si = 0; + si += snprintf(s+si, slen-si, "0x"); for (m = 0; m < i_len; ++m) - s += sprintf(s, "%02x", (unsigned int)ip[m]); + si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); break; case 3: /* NAA */ if (1 != c_set) { - s += sprintf(s, "error: NAA bad code_set"); - return -1; - } + snprintf(s+si, slen-si, "error: NAA bad code_set"); + return -1; + } naa = (ip[0] >> 4) & 0xff; if ((naa < 2) || (naa > 6) || (4 == naa)) { - s += sprintf(s, "error: unexpected NAA"); - return -1; + snprintf(s+si, slen-si, "error: unexpected NAA"); + return -1; } - if (have_scsi_ns) - s = orig_s; + if (have_scsi_ns) + si = 0; if (2 == naa) { /* NAA IEEE Extended */ if (8 != i_len) { - s += sprintf(s, "error: NAA 2 length"); - return -1; + snprintf(s+si, slen-si, "error: NAA 2 length"); + return -1; } - s += sprintf(s, "0x"); + si += snprintf(s+si, slen-si, "0x"); for (m = 0; m < 8; ++m) - s += sprintf(s, "%02x", (unsigned int)ip[m]); + si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } else if ((3 == naa ) || (5 == naa)) { /* NAA=3 Locally assigned; NAA=5 IEEE Registered */ if (8 != i_len) { - s += sprintf(s, "error: NAA 3 or 5 length"); - return -1; + snprintf(s+si, slen-si, "error: NAA 3 or 5 length"); + return -1; } - s += sprintf(s, "0x"); + si += snprintf(s+si, slen-si, "0x"); for (m = 0; m < 8; ++m) - s += sprintf(s, "%02x", (unsigned int)ip[m]); + si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } else if (6 == naa) { /* NAA IEEE Registered extended */ if (16 != i_len) { - s += sprintf(s, "error: NAA 6 length"); - return -1; + snprintf(s+si, slen-si, "error: NAA 6 length"); + return -1; } - s += sprintf(s, "0x"); + si += snprintf(s+si, slen-si, "0x"); for (m = 0; m < 16; ++m) - s += sprintf(s, "%02x", (unsigned int)ip[m]); + si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } break; case 4: /* Relative target port */ @@ -385,22 +421,22 @@ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, break; case 8: /* SCSI name string */ if (3 != c_set) { - s += sprintf(s, "error: SCSI name string"); - return -1; + snprintf(s+si, slen-si, "error: SCSI name string"); + return -1; } /* does %s print out UTF-8 ok?? */ - if (orig_s == s) { - s += sprintf(s, "%s", (const char *)ip); - ++have_scsi_ns; - } + if (si == 0) { + si += snprintf(s+si, slen-si, "%s", (const char *)ip); + ++have_scsi_ns; + } break; default: /* reserved */ break; } } if (-2 == u) { - s += sprintf(s, "error: bad structure"); - return -1; + snprintf(s+si, slen-si, "error: bad structure"); + return -1; } return 0; } @@ -412,11 +448,12 @@ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, 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 + 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(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf, - int bufLen, int known_resp_len) +int +scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf, + int bufLen, int known_resp_len) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -468,7 +505,7 @@ int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf, pageLen = 252; /* some IBM tape drives don't like double fetch */ /* some SCSI HBA don't like "odd" length transfers */ if (pageLen % 2) - pageLen += 1; + pageLen += 1; if (pageLen > bufLen) pageLen = bufLen; } @@ -507,8 +544,9 @@ int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf, * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if * field in command not supported, * 4 if bad parameter to command or * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */ -int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum, - int subpagenum, UINT8 *pBuf, int bufLen) +int +scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum, + int subpagenum, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -540,10 +578,11 @@ int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum, /* 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. + * 3 if field in command not supported or returns negated errno. * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */ -int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc, - UINT8 *pBuf, int bufLen) +int +scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc, + UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -594,10 +633,11 @@ int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc, * 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), + * 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-3 sections 6.7 and 7.4 (rev 22a) */ -int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) +int +scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -634,12 +674,13 @@ int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) return scsiSimpleSenseFilter(&sinfo); } -/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command +/* 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. + * command not supported or returns negated errno. * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */ -int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc, - UINT8 *pBuf, int bufLen) +int +scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc, + UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -688,11 +729,12 @@ int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc, /* 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 + * (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. SPC-3 sections 6.8 and 7.4 (rev 22a) */ -int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) +int +scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -707,7 +749,7 @@ int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) hdr_plus_1_pg = pg_offset + pg_len; if (hdr_plus_1_pg > bufLen) return -EINVAL; - pBuf[0] = 0; + pBuf[0] = 0; pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */ pBuf[pg_offset] &= 0x7f; /* Mask out PS bit from byte 0 of page data */ memset(&io_hdr, 0, sizeof(io_hdr)); @@ -733,14 +775,15 @@ int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen) /* 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-3 sections 6.4 and 7.6 (rev 22a) */ -int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen) +int +scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen) { struct scsi_sense_disect sinfo; struct scsi_cmnd_io io_hdr; UINT8 cdb[6]; UINT8 sense[32]; - if ((bufLen < 0) || (bufLen > 255)) + if ((bufLen < 0) || (bufLen > 1023)) return -EINVAL; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); @@ -748,7 +791,8 @@ int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen) io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = INQUIRY; - cdb[4] = bufLen; + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = (bufLen & 0xff); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; @@ -762,10 +806,11 @@ int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen) } /* 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 + * (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-3 section 6.4 and 7.6 (rev 22a) */ -int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) +int +scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -773,8 +818,15 @@ int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) UINT8 sense[32]; int res; - if ((bufLen < 0) || (bufLen > 255)) + /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */ + if ((SCSI_VPD_SUPPORTED_VPD_PAGES != vpd_page) && + supported_vpd_pages_p && + (! supported_vpd_pages_p->is_supported(vpd_page))) + return 3; + + if ((bufLen < 0) || (bufLen > 1023)) return -EINVAL; +try_again: memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); if (bufLen > 1) @@ -785,7 +837,8 @@ int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) cdb[0] = INQUIRY; cdb[1] = 0x1; /* set EVPD bit (enable Vital Product Data) */ cdb[2] = vpd_page; - cdb[4] = bufLen; + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = (bufLen & 0xff); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; @@ -795,6 +848,14 @@ int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); + if ((SCSI_STATUS_CHECK_CONDITION == io_hdr.scsi_status) && + (SCSI_SK_ILLEGAL_REQUEST == sinfo.sense_key) && + (SCSI_ASC_INVALID_FIELD == sinfo.asc) && + (cdb[3] > 0)) { + bufLen &= 0xff; /* make sure cdb[3] is 0 next time around */ + goto try_again; + } + if ((res = scsiSimpleSenseFilter(&sinfo))) return res; /* Guard against devices that ignore EVPD bit and do standard INQUIRY */ @@ -810,14 +871,15 @@ int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen) /* REQUEST SENSE command. Returns 0 if ok, anything else major problem. * SPC-3 section 6.27 (rev 22a) */ -int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info) +int +scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info) { struct scsi_cmnd_io io_hdr; UINT8 cdb[6]; UINT8 sense[32]; UINT8 buff[18]; int len; - UINT8 ecode; + UINT8 resp_code; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); @@ -835,18 +897,54 @@ int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); if (sense_info) { - ecode = buff[0] & 0x7f; - sense_info->error_code = ecode; + resp_code = buff[0] & 0x7f; + sense_info->resp_code = resp_code; sense_info->sense_key = buff[2] & 0xf; sense_info->asc = 0; sense_info->ascq = 0; - if ((0x70 == ecode) || (0x71 == ecode)) { + if ((0x70 == resp_code) || (0x71 == resp_code)) { len = buff[7] + 8; if (len > 13) { sense_info->asc = buff[12]; sense_info->ascq = buff[13]; } } + // fill progrss indicator, if available + sense_info->progress = -1; + switch (resp_code) { + const unsigned char * ucp; + int sk, sk_pr; + case 0x70: + case 0x71: + sk = (buff[2] & 0xf); + if ((sizeof(buff) < 18) || + ((SCSI_SK_NO_SENSE != sk) && (SCSI_SK_NOT_READY != sk))) { + break; + } + if (buff[15] & 0x80) { /* SKSV bit set */ + sense_info->progress = (buff[16] << 8) + buff[17]; + break; + } else { + break; + } + case 0x72: + case 0x73: + /* sense key specific progress (0x2) or progress descriptor (0xa) */ + sk = (buff[1] & 0xf); + sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk); + if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 2))) && + (0x6 == ucp[1]) && (0x80 & ucp[4])) { + sense_info->progress = (ucp[5] << 8) + ucp[6]; + break; + } else if (((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 0xa))) && + ((0x6 == ucp[1]))) { + sense_info->progress = (ucp[6] << 8) + ucp[7]; + break; + } else + break; + default: + return 0; + } } return 0; } @@ -854,7 +952,9 @@ int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info /* 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-3 section 6.28 (rev 22a) */ -int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen) +int +scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, + int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -881,7 +981,7 @@ int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int io_hdr.max_sense_len = sizeof(sense); /* worst case is an extended foreground self test on a big disk */ io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST; - + if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); @@ -891,7 +991,8 @@ int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int /* 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-3 section 6.18 (rev 22a) */ -int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf, +int +scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; @@ -922,7 +1023,8 @@ int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBu } /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */ -static int _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo) +static int +_testunitready(scsi_device * device, struct scsi_sense_disect * sinfo) { struct scsi_cmnd_io io_hdr; UINT8 cdb[6]; @@ -948,7 +1050,8 @@ static int _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo /* Returns 0 for device responds and media ready, 1 for device responds and media not ready, or returns a negated errno value */ -int scsiTestUnitReady(scsi_device * device) +int +scsiTestUnitReady(scsi_device * device) { struct scsi_sense_disect sinfo; int status; @@ -959,7 +1062,7 @@ int scsiTestUnitReady(scsi_device * device) status = scsiSimpleSenseFilter(&sinfo); if (SIMPLE_ERR_TRY_AGAIN == status) { /* power on reset, media changed, ok ... try again */ - status = _testunitready(device, &sinfo); + status = _testunitready(device, &sinfo); if (0 != status) return status; status = scsiSimpleSenseFilter(&sinfo); @@ -970,8 +1073,9 @@ int scsiTestUnitReady(scsi_device * device) /* READ DEFECT (10) 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. SBC-2 section 5.12 (rev 16) */ -int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format, - UINT8 *pBuf, int bufLen) +int +scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, + int dl_format, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -1000,11 +1104,52 @@ int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_ return scsiSimpleSenseFilter(&sinfo); } +/* READ DEFECT (12) 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. SBC-3 section 5.18 (rev 35; vale Mark Evans) */ +int +scsiReadDefect12(scsi_device * device, int req_plist, int req_glist, + int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[12]; + UINT8 sense[32]; + + 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] = READ_DEFECT_12; + cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) | + ((req_glist << 3) & 0x8) | (dl_format & 0x7)); + cdb[2] = (addrDescIndex >> 24) & 0xff; + cdb[3] = (addrDescIndex >> 16) & 0xff; + cdb[4] = (addrDescIndex >> 8) & 0xff; + cdb[5] = addrDescIndex & 0xff; + cdb[6] = (bufLen >> 24) & 0xff; + cdb[7] = (bufLen >> 16) & 0xff; + cdb[8] = (bufLen >> 8) & 0xff; + cdb[9] = 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; + + if (!device->scsi_pass_through(&io_hdr)) + return -device->get_errno(); + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + /* READ CAPACITY (10) 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. SBC-3 section 5.15 (rev 26) */ -int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap, - unsigned int * lb_sizep) +int +scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap, + unsigned int * lb_sizep) { int res; struct scsi_cmnd_io io_hdr; @@ -1044,7 +1189,8 @@ int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap, /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0 * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */ -int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen) +int +scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; @@ -1077,43 +1223,72 @@ int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen) /* Return number of bytes of storage in 'device' or 0 if error. If * successful and lb_sizep is not NULL then the logical block size * in bytes is written to the location pointed to by lb_sizep. */ -uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep) +uint64_t +scsiGetSize(scsi_device * device, unsigned int * lb_sizep, + int * lb_per_pb_expp) { - unsigned int last_lba, lb_size; + unsigned int last_lba = 0, lb_size = 0; int k, res; uint64_t ret_val = 0; UINT8 rc16resp[32]; res = scsiReadCapacity10(device, &last_lba, &lb_size); if (res) { - if (scsi_debugmode) - pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res); - return ret_val; + if (scsi_debugmode) + pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res); + return 0; } if (0xffffffff == last_lba) { - res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp)); + res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp)); if (res) { - if (scsi_debugmode) - pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res); - return ret_val; - } - for (k = 0; k < 8; ++k) { - if (k > 0) + if (scsi_debugmode) + pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res); + return 0; + } + for (k = 0; k < 8; ++k) { + if (k > 0) ret_val <<= 8; ret_val |= rc16resp[k + 0]; } - } else - ret_val = last_lba; + if (lb_per_pb_expp) + *lb_per_pb_expp = (rc16resp[13] & 0xf); + } else { + ret_val = last_lba; + if (lb_per_pb_expp) + *lb_per_pb_expp = 0; + } if (lb_sizep) - *lb_sizep = lb_size; - ++ret_val; /* last_lba is origin 0 so need to bump to get number of */ + *lb_sizep = lb_size; + ++ret_val; /* last_lba is origin 0 so need to bump to get number of */ return ret_val * lb_size; } +/* Gets drive Protection and Logical/Physical block information. Writes + * back bytes 12 to 31 from a READ CAPACITY 16 command to the rc16_12_31p + * pointer. So rc16_12_31p should point to an array of 20 bytes. Returns 0 + * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command + * not supported or returns negated errno. */ +int +scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p) +{ + int res; + UINT8 rc16resp[32]; + + res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp)); + if (res) { + if (scsi_debugmode) + pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res); + return res; + } + if (rc16_12_31p) + memcpy(rc16_12_31p, rc16resp + 12, 20); + return 0; +} /* 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 +scsiModePageOffset(const UINT8 * resp, int len, int modese_len) { int resp_len, bd_len; int offset = -1; @@ -1155,7 +1330,9 @@ int scsiModePageOffset(const UINT8 * resp, int len, int modese_len) * 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(scsi_device * device, struct scsi_iec_mode_page *iecp, int modese_len) +int +scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, + int modese_len) { int err = 0; @@ -1183,7 +1360,7 @@ int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int iecp->modese_len = 0; return err; } - } + } iecp->gotCurrent = 1; iecp->requestedChangeable = 1; if (10 == iecp->modese_len) @@ -1191,8 +1368,8 @@ int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int 0, MPAGE_CONTROL_CHANGEABLE, iecp->raw_chg, sizeof(iecp->raw_chg)); else if (6 == iecp->modese_len) - err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, - 0, MPAGE_CONTROL_CHANGEABLE, + err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + 0, MPAGE_CONTROL_CHANGEABLE, iecp->raw_chg, sizeof(iecp->raw_chg)); if (err) return err; @@ -1200,7 +1377,8 @@ int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int return 0; } -int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp) +int +scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp) { int offset; @@ -1215,7 +1393,8 @@ int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp) return 0; } -int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) +int +scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) { int offset; @@ -1231,10 +1410,10 @@ int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) } /* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */ -#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 +#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_MRIE 6 #define SCSI_IEC_MP_INTERVAL_T 0 #define SCSI_IEC_MP_REPORT_COUNT 1 @@ -1245,8 +1424,9 @@ int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) * 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(scsi_device * device, int enabled, - const struct scsi_iec_mode_page *iecp) +int +scsiSetExceptionControlAndWarning(scsi_device * device, int enabled, + const struct scsi_iec_mode_page *iecp) { int k, offset, resp_len; int err = 0; @@ -1304,10 +1484,10 @@ int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled, pout("scsiSetExceptionControlAndWarning: already disabled\n"); return 0; /* nothing to do, leave other setting alone */ } - if (wEnabled) + if (wEnabled) rout[offset + 2] &= EWASC_DISABLE; if (eCEnabled) { - if (iecp->gotChangeable && + 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 */ @@ -1320,7 +1500,8 @@ int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled, return err; } -int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp) +int +scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp) { UINT8 tBuf[252]; int err; @@ -1342,9 +1523,9 @@ int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp) * 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(scsi_device * device, int hasIELogPage, int hasTempLogPage, - UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, - UINT8 *triptemp) +int +scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, + UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp) { UINT8 tBuf[252]; struct scsi_sense_disect sense_info; @@ -1352,7 +1533,7 @@ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, int temperatureSet = 0; unsigned short pagesize; UINT8 currTemp, trTemp; - + *asc = 0; *ascq = 0; *currenttemp = 0; @@ -1366,23 +1547,23 @@ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, return err; } // pull out page size from response, don't forget to add 4 - pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 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.asc = tBuf[8]; sense_info.ascq = tBuf[9]; if (! hasTempLogPage) { - if (tBuf[7] > 2) + 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) { + 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)); @@ -1401,7 +1582,7 @@ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, } // The first character (W, C, I) tells the severity -static const char * TapeAlertsMessageTable[]= { +static const char * TapeAlertsMessageTable[]= { " ", /* 0x01 */ "W: The tape drive is having problems reading data. No data has been lost,\n" @@ -1625,16 +1806,17 @@ static const char * TapeAlertsMessageTable[]= { " fault. If problem persists, call the supplier help line.", }; -const char * scsiTapeAlertsTapeDevice(unsigned short code) +const char * +scsiTapeAlertsTapeDevice(unsigned short code) { const int num = sizeof(TapeAlertsMessageTable) / sizeof(TapeAlertsMessageTable[0]); - return (code < num) ? TapeAlertsMessageTable[code] : "Unknown Alert"; + return (code < num) ? TapeAlertsMessageTable[code] : "Unknown Alert"; } // The first character (W, C, I) tells the severity -static const char * ChangerTapeAlertsMessageTable[]= { +static const char * ChangerTapeAlertsMessageTable[]= { " ", /* 0x01 */ "C: The library mechanism is having difficulty communicating with the\n" @@ -1760,12 +1942,13 @@ static const char * ChangerTapeAlertsMessageTable[]= { "I: The library was unable to read the bar code on a cartridge.", }; -const char * scsiTapeAlertsChangerDevice(unsigned short code) +const char * +scsiTapeAlertsChangerDevice(unsigned short code) { const int num = sizeof(ChangerTapeAlertsMessageTable) / sizeof(ChangerTapeAlertsMessageTable[0]); - return (code < num) ? ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; + return (code < num) ? ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; } @@ -1894,14 +2077,15 @@ static const char * strs_for_asc_b[] = { static char spare_buff[128]; -const char * scsiGetIEString(UINT8 asc, UINT8 ascq) +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 < + else if (ascq < (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) { rp = strs_for_asc_5d[ascq]; if (strlen(rp) > 0) @@ -1925,11 +2109,12 @@ const char * scsiGetIEString(UINT8 asc, UINT8 ascq) /* 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(scsi_device * device) -{ +int +scsiSmartIBMOfflineTest(scsi_device * device) +{ UINT8 tBuf[256]; int res; - + memset(tBuf, 0, sizeof(tBuf)); /* Build SMART Off-line Immediate Diag Header */ tBuf[0] = 0x80; /* Page Code */ @@ -1946,8 +2131,9 @@ int scsiSmartIBMOfflineTest(scsi_device * device) return res; } -int scsiSmartDefaultSelfTest(scsi_device * device) -{ +int +scsiSmartDefaultSelfTest(scsi_device * device) +{ int res; res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0); @@ -1956,8 +2142,9 @@ int scsiSmartDefaultSelfTest(scsi_device * device) return res; } -int scsiSmartShortSelfTest(scsi_device * device) -{ +int +scsiSmartShortSelfTest(scsi_device * device) +{ int res; res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0); @@ -1966,8 +2153,9 @@ int scsiSmartShortSelfTest(scsi_device * device) return res; } -int scsiSmartExtendSelfTest(scsi_device * device) -{ +int +scsiSmartExtendSelfTest(scsi_device * device) +{ int res; res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0); @@ -1977,8 +2165,9 @@ int scsiSmartExtendSelfTest(scsi_device * device) return res; } -int scsiSmartShortCapSelfTest(scsi_device * device) -{ +int +scsiSmartShortCapSelfTest(scsi_device * device) +{ int res; res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0); @@ -1987,7 +2176,8 @@ int scsiSmartShortCapSelfTest(scsi_device * device) return res; } -int scsiSmartExtendCapSelfTest(scsi_device * device) +int +scsiSmartExtendCapSelfTest(scsi_device * device) { int res; @@ -1998,7 +2188,8 @@ int scsiSmartExtendCapSelfTest(scsi_device * device) return res; } -int scsiSmartSelfTestAbort(scsi_device * device) +int +scsiSmartSelfTestAbort(scsi_device * device) { int res; @@ -2010,7 +2201,9 @@ int scsiSmartSelfTestAbort(scsi_device * device) /* Returns 0 and the expected duration of an extended self test (in seconds) if successful; any other return value indicates a failure. */ -int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int modese_len) +int +scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, + int modese_len) { int err, offset, res; UINT8 buff[64]; @@ -2029,11 +2222,11 @@ int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int m } if (10 == modese_len) { err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, - MPAGE_CONTROL_CURRENT, + MPAGE_CONTROL_CURRENT, buff, sizeof(buff)); if (err) return err; - } + } offset = scsiModePageOffset(buff, sizeof(buff), modese_len); if (offset < 0) return -EINVAL; @@ -2046,8 +2239,8 @@ int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int m return -EINVAL; } -void scsiDecodeErrCounterPage(unsigned char * resp, - struct scsiErrorCounter *ecp) +void +scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp) { int k, j, num, pl, pc; unsigned char * ucp; @@ -2061,17 +2254,17 @@ void scsiDecodeErrCounterPage(unsigned char * resp, 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: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: ecp->gotPC[pc] = 1; ullp = &ecp->counter[pc]; break; - default: + default: ecp->gotExtraPC = 1; ullp = &ecp->counter[7]; break; @@ -2093,8 +2286,9 @@ void scsiDecodeErrCounterPage(unsigned char * resp, } } -void scsiDecodeNonMediumErrPage(unsigned char *resp, - struct scsiNonMediumError *nmep) +void +scsiDecodeNonMediumErrPage(unsigned char *resp, + struct scsiNonMediumError *nmep) { int k, j, num, pl, pc, szof; unsigned char * ucp; @@ -2108,7 +2302,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp, pc = (ucp[0] << 8) | ucp[1]; pl = ucp[3] + 4; switch (pc) { - case 0: + case 0: nmep->gotPC0 = 1; k = pl - 4; xp = ucp + 4; @@ -2123,7 +2317,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp, nmep->counterPC0 |= xp[j]; } break; - case 0x8009: + case 0x8009: nmep->gotTFE_H = 1; k = pl - 4; xp = ucp + 4; @@ -2138,7 +2332,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp, nmep->counterTFE_H |= xp[j]; } break; - case 0x8015: + case 0x8015: nmep->gotPE_H = 1; k = pl - 4; xp = ucp + 4; @@ -2153,7 +2347,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp, nmep->counterPE_H |= xp[j]; } break; - default: + default: nmep->gotExtraPC = 1; break; } @@ -2167,10 +2361,11 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp, 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 + 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 22a) */ -int scsiCountFailedSelfTests(scsi_device * fd, int noisy) +int +scsiCountFailedSelfTests(scsi_device * fd, int noisy) { int num, k, n, err, res, fails, fail_hour; UINT8 * ucp; @@ -2210,7 +2405,7 @@ int scsiCountFailedSelfTests(scsi_device * fd, int noisy) res = ucp[4] & 0xf; if ((res > 2) && (res < 8)) { fails++; - if (1 == fails) + if (1 == fails) fail_hour = (ucp[6] << 8) + ucp[7]; } } @@ -2219,7 +2414,8 @@ int scsiCountFailedSelfTests(scsi_device * fd, int noisy) /* 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(scsi_device * fd, int * inProgress) +int +scsiSelfTestInProgress(scsi_device * fd, int * inProgress) { int num; UINT8 * ucp; @@ -2246,7 +2442,8 @@ int scsiSelfTestInProgress(scsi_device * fd, int * inProgress) 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(scsi_device * device, int modese_len, int current) +int +scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current) { int err, offset; UINT8 buff[64]; @@ -2268,18 +2465,163 @@ int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current) 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; } +/* Returns a negative value on error, 0 if unknown and 1 if SSD, + * otherwise the positive returned value is the speed in rpm. First checks + * the Block Device Characteristics VPD page and if that fails it tries the + * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */ + +int +scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp) +{ + int err, offset, speed; + UINT8 buff[64]; + int pc = MPAGE_CONTROL_DEFAULT; + + memset(buff, 0, sizeof(buff)); + if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS, + buff, sizeof(buff))) && + (((buff[2] << 8) + buff[3]) > 2)) { + speed = (buff[4] << 8) + buff[5]; + if (form_factorp) + *form_factorp = buff[7] & 0xf; + return speed; + } + if (form_factorp) + *form_factorp = 0; + if (modese_len <= 6) { + if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, 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, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc, + buff, sizeof(buff)); + if (err) + return -EINVAL; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + return (buff[offset + 20] << 8) | buff[offset + 21]; +} + +/* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value, + 0 - clear bit, 1 - set bit */ + +int +scsiGetSetCache(scsi_device * device, int modese_len, short int * wcep, + short int * rcdp) +{ + int err, offset, resp_len, sp; + UINT8 buff[64], ch_buff[64]; + short set_wce = *wcep; + short set_rcd = *rcdp; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else { + device->set_err(EINVAL, "SCSI MODE SENSE failed"); + return -EINVAL; + } + } else if (0 == modese_len) + modese_len = 6; + } + + if (10 == modese_len) { + err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) { + device->set_err(EINVAL, "SCSI MODE SENSE failed"); + return -EINVAL; + } + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset < 0) || (buff[offset + 1] < 0xa)) { + device->set_err(EINVAL, "Bad response"); + return SIMPLE_ERR_BAD_RESP; + } + + *wcep = ((buff[offset + 2] & 0x04) != 0); + *rcdp = ((buff[offset + 2] & 0x01) != 0); + + if((*wcep == set_wce || set_wce == -1) + && ((*rcdp == set_rcd) || set_rcd == -1)) + return 0; // no changes needed or nothing to set + + if (modese_len == 6) + err = scsiModeSense(device, CACHING_PAGE, 0, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + else + err = scsiModeSense10(device, CACHING_PAGE, 0, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + if (err) { + device->set_err(EINVAL, "WCE/RCD bits not changable"); + return err; + } + + // set WCE bit + if(set_wce >= 0 && *wcep != set_wce) { + if (0 == (ch_buff[offset + 2] & 0x04)) { + device->set_err(EINVAL, "WCE bit not changable"); + return 1; + } + if(set_wce) + buff[offset + 2] |= 0x04; // set bit + else + buff[offset + 2] &= 0xfb; // clear bit + } + // set RCD bit + if(set_rcd >= 0 && *rcdp != set_rcd) { + if (0 == (ch_buff[offset + 2] & 0x01)) { + device->set_err(EINVAL, "RCD bit not changable"); + return 1; + } + if(set_rcd) + buff[offset + 2] |= 0x01; // set bit + else + buff[offset + 2] &= 0xfe; // clear bit + } + + if (10 == modese_len) { + resp_len = (buff[0] << 8) + buff[1] + 2; + buff[3] &= 0xef; /* for disks mask out DPOFUA bit */ + } else { + resp_len = buff[0] + 1; + buff[2] &= 0xef; /* for disks mask out DPOFUA bit */ + } + sp = 0; /* Do not change saved values */ + if (10 == modese_len) + err = scsiModeSelect10(device, sp, buff, resp_len); + else if (6 == modese_len) + err = scsiModeSelect(device, sp, buff, resp_len); + if(err) + device->set_err(EINVAL, "MODE SELECT command failed"); + return err; +} + + /* 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(scsi_device * device, int enabled, int modese_len) +int +scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len) { int err, offset, resp_len, sp; UINT8 buff[64]; @@ -2303,7 +2645,7 @@ int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len) 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; @@ -2325,7 +2667,7 @@ int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len) 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; buff[3] &= 0xef; /* for disks mask out DPOFUA bit */ @@ -2345,10 +2687,11 @@ int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len) return err; } -/* Returns a negative value if failed to fetch Protocol specific port mode +/* 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(scsi_device * device, int modese_len) +int +scsiFetchTransportProtocol(scsi_device * device, int modese_len) { int err, offset; UINT8 buff[64]; @@ -2371,12 +2714,70 @@ int scsiFetchTransportProtocol(scsi_device * device, int modese_len) 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))) + (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) return (buff[offset + 2] & 0xf); } return -EINVAL; } + +const unsigned char * +sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, + int desc_type) +{ + int add_sen_len, add_len, desc_len, k; + const unsigned char * descp; + + if ((sense_len < 8) || (0 == (add_sen_len = sensep[7]))) + return NULL; + if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) + return NULL; + add_sen_len = (add_sen_len < (sense_len - 8)) ? + add_sen_len : (sense_len - 8); + descp = &sensep[8]; + for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { + descp += desc_len; + add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; + desc_len = add_len + 2; + if (descp[0] == desc_type) + return descp; + if (add_len < 0) /* short descriptor ?? */ + break; + } + return NULL; +} + +// Convenience function for formatting strings from SCSI identify +void +scsi_format_id_string(char * out, const unsigned char * in, int n) +{ + char tmp[65]; + n = n > 64 ? 64 : n; + strncpy(tmp, (const char *)in, n); + tmp[n] = '\0'; + + // Find the first non-space character (maybe none). + int first = -1; + int i; + for (i = 0; tmp[i]; i++) + if (!isspace((int)tmp[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(tmp)-1; i >= first && isspace((int)tmp[i]); i--); + int last = i; + + strncpy(out, tmp+first, last-first+1); + out[last-first+1] = '\0'; +} diff --git a/scsicmds.h b/scsicmds.h index 3b31147..afe656e 100644 --- a/scsicmds.h +++ b/scsicmds.h @@ -7,7 +7,7 @@ * Copyright (C) 2000 Michael Cornwell * * Additional SCSI work: - * Copyright (C) 2003-11 Douglas Gilbert + * Copyright (C) 2003-13 Douglas Gilbert * * 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 @@ -15,8 +15,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -32,7 +32,7 @@ #ifndef SCSICMDS_H_ #define SCSICMDS_H_ -#define SCSICMDS_H_CVSID "$Id: scsicmds.h 3413 2011-09-06 21:23:00Z dpgilbert $\n" +#define SCSICMDS_H_CVSID "$Id: scsicmds.h 3783 2013-03-02 01:51:12Z dpgilbert $\n" #include #include @@ -78,6 +78,9 @@ #ifndef READ_DEFECT_10 #define READ_DEFECT_10 0x37 #endif +#ifndef READ_DEFECT_12 +#define READ_DEFECT_12 0xb7 +#endif #ifndef START_STOP_UNIT #define START_STOP_UNIT 0x1b #endif @@ -114,11 +117,11 @@ struct scsi_cmnd_io { UINT8 * cmnd; /* [in]: ptr to SCSI command block (cdb) */ size_t cmnd_len; /* [in]: number of bytes in SCSI command */ - int dxfer_dir; /* [in]: DXFER_NONE, DXFER_FROM_DEVICE, or + int dxfer_dir; /* [in]: DXFER_NONE, DXFER_FROM_DEVICE, or DXFER_TO_DEVICE */ UINT8 * dxferp; /* [in]: ptr to outgoing or incoming data buffer */ size_t dxfer_len; /* [in]: bytes to be transferred to/from dxferp */ - UINT8 * sensep; /* [in]: ptr to sense buffer, filled when + UINT8 * sensep; /* [in]: ptr to sense buffer, filled when CHECK CONDITION status occurs */ size_t max_sense_len; /* [in]: max number of bytes to write to sensep */ unsigned timeout; /* [in]: seconds, 0-> default timeout (60 seconds?) */ @@ -130,10 +133,11 @@ struct scsi_cmnd_io }; struct scsi_sense_disect { - UINT8 error_code; + UINT8 resp_code; UINT8 sense_key; - UINT8 asc; + UINT8 asc; UINT8 ascq; + int progress; /* -1 -> N/A, 0-65535 -> available */ }; /* Useful data from Informational Exception Control mode page (0x1c) */ @@ -201,7 +205,7 @@ struct scsiNonMediumError { /* Log page response lengths */ #define LOG_RESP_SELF_TEST_LEN 0x194 -/* See the SSC-2 document at www.t10.org . Earler note: From IBM +/* See the SSC-2 document at www.t10.org . Earler note: From IBM Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */ #define TAPE_ALERTS_LPAGE 0x2e @@ -240,6 +244,18 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */ #define MPAGE_CONTROL_DEFAULT 2 #define MPAGE_CONTROL_SAVED 3 +/* SCSI Vital Product Data (VPD) pages */ +#define SCSI_VPD_SUPPORTED_VPD_PAGES 0x0 +#define SCSI_VPD_UNIT_SERIAL_NUMBER 0x80 +#define SCSI_VPD_DEVICE_IDENTIFICATION 0x83 +#define SCSI_VPD_EXTENDED_INQUIRY_DATA 0x86 +#define SCSI_VPD_ATA_INFORMATION 0x89 +#define SCSI_VPD_POWER_CONDITION 0x8a +#define SCSI_VPD_POWER_CONSUMPTION 0x8d +#define SCSI_VPD_BLOCK_LIMITS 0xb0 +#define SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS 0xb1 +#define SCSI_VPD_LOGICAL_BLOCK_PROVISIONING 0xb2 + /* defines for useful SCSI Status codes */ #define SCSI_STATUS_CHECK_CONDITION 0x2 @@ -257,7 +273,7 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */ #define SCSI_ASC_NOT_READY 0x4 /* more info in ASCQ code */ #define SCSI_ASC_NO_MEDIUM 0x3a /* more info in ASCQ code */ #define SCSI_ASC_UNKNOWN_OPCODE 0x20 -#define SCSI_ASC_UNKNOWN_FIELD 0x24 +#define SCSI_ASC_INVALID_FIELD 0x24 #define SCSI_ASC_UNKNOWN_PARAM 0x26 #define SCSI_ASC_WARNING 0xb #define SCSI_ASC_IMPENDING_FAILURE 0x5d @@ -301,6 +317,27 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */ class scsi_device; +// Set of supported SCSI VPD pages. Constructor fetches Supported VPD pages +// VPD page and remembers the response for later queries. +class supported_vpd_pages +{ +public: + supported_vpd_pages(scsi_device * device); + ~supported_vpd_pages() { num_valid = 0; } + + bool is_supported(int vpd_page_num) const; + + /* Returns 0 or less for VPD pages not supported or error */ + int num_pages() const { return num_valid; } + +private: + int num_valid; /* 0 or less for invalid */ + unsigned char pages[256]; +}; + +extern supported_vpd_pages * supported_vpd_pages_p; + + // Print SCSI debug messages? extern unsigned char scsi_debugmode; @@ -354,14 +391,14 @@ int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBu int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format, UINT8 *pBuf, int bufLen); +int scsiReadDefect12(scsi_device * device, int req_plist, int req_glist, + int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen); + int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbp, unsigned int * lb_sizep); int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen); -uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep); - - /* SMART specific commands */ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp); @@ -383,7 +420,12 @@ int scsiSelfTestInProgress(scsi_device * device, int * inProgress); int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current); int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len); int scsiFetchTransportProtocol(scsi_device * device, int modese_len); - +int scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp); +int scsiGetSetCache(scsi_device * device, int modese_len, short int * wce, + short int * rcd); +uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep, + int * lb_per_pb_expp); +int scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p); /* T10 Standard IE Additional Sense Code strings taken from t10.org */ const char* scsiGetIEString(UINT8 asc, UINT8 ascq); @@ -403,10 +445,18 @@ const char * scsiTapeAlertsTapeDevice(unsigned short code); const char * scsiTapeAlertsChangerDevice(unsigned short code); const char * scsi_get_opcode_name(UINT8 opcode); +void scsi_format_id_string(char * out, const unsigned char * in, int n); + void dStrHex(const char* str, int len, int no_ascii); inline void dStrHex(const unsigned char* str, int len, int no_ascii) { dStrHex((const char *)str, len, no_ascii); } +/* Attempt to find the first SCSI sense data descriptor that matches the + given 'desc_type'. If found return pointer to start of sense data + descriptor; otherwise (including fixed format sense data) returns NULL. */ +const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, + int sense_len, int desc_type); + /* SCSI command transmission interface function declaration. Its * definition is target OS specific (see os_.c file). @@ -422,4 +472,3 @@ inline void dStrHex(const unsigned char* str, int len, int no_ascii) #endif - diff --git a/scsiprint.cpp b/scsiprint.cpp index 3a3b95d..5c5b6e0 100644 --- a/scsiprint.cpp +++ b/scsiprint.cpp @@ -7,7 +7,7 @@ * Copyright (C) 2000 Michael Cornwell * * Additional SCSI work: - * Copyright (C) 2003-10 Douglas Gilbert + * Copyright (C) 2003-13 Douglas Gilbert * * 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 @@ -15,8 +15,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -42,7 +42,7 @@ #define GBUF_SIZE 65535 -const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 3441 2011-10-12 17:22:15Z chrfranke $" +const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 3807 2013-04-18 17:11:12Z chrfranke $" SCSIPRINT_H_CVSID; @@ -76,17 +76,19 @@ static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ /* Remember last successful mode sense/select command */ static int modese_len = 0; -static void scsiGetSupportedLogPages(scsi_device * device) + +static void +scsiGetSupportedLogPages(scsi_device * device) { int i, err; if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) - pout("Log Sense for supported pages failed [%s]\n", - scsiErrString(err)); + pout("Log Sense for supported pages failed [%s]\n", + scsiErrString(err)); return; - } + } for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) { switch (gBuf[i]) @@ -144,7 +146,8 @@ static void scsiGetSupportedLogPages(scsi_device * device) /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad (or at least something to report). */ -static int scsiGetSmartData(scsi_device * device, bool attribs) +static int +scsiGetSmartData(scsi_device * device, bool attribs) { UINT8 asc; UINT8 ascq; @@ -152,7 +155,6 @@ static int scsiGetSmartData(scsi_device * device, bool attribs) UINT8 triptemp = 0; const char * cp; int err = 0; - print_on(); if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, ¤ttemp, &triptemp)) { @@ -165,14 +167,12 @@ static int scsiGetSmartData(scsi_device * device, bool attribs) if (cp) { err = -2; print_on(); - pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); + pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); print_off(); } 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); @@ -182,6 +182,7 @@ static int scsiGetSmartData(scsi_device * device, bool attribs) if (triptemp) pout("Drive Trip Temperature: %d C\n", triptemp); } + pout("\n"); return err; } @@ -190,7 +191,8 @@ static int scsiGetSmartData(scsi_device * device, bool attribs) // TapeAlerts fails static const char * const severities = "CWI"; -static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) +static int +scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) { unsigned short pagelength; unsigned short parametercode; @@ -223,9 +225,10 @@ static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) scsiTapeAlertsTapeDevice(parametercode); if (*ts == *s) { if (!failures) - pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n"); + pout("TapeAlert Errors (C=Critical, W=Warning, " + "I=Informational):\n"); pout("[0x%02x] %s\n", parametercode, ts); - failures += 1; + failures += 1; } } } @@ -238,7 +241,8 @@ static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) return failures; } -static void scsiGetStartStopData(scsi_device * device) +static void +scsiGetStartStopData(scsi_device * device) { UINT32 u; int err, len, k, extra, pc; @@ -312,21 +316,50 @@ static void scsiGetStartStopData(scsi_device * device) break; } } -} +} -static void scsiPrintGrownDefectListLen(scsi_device * device) +static void +scsiPrintGrownDefectListLen(scsi_device * device) { - int err, dl_format, dl_len, div; + int err, dl_format, got_rd12, generation; + unsigned int dl_len, div; + + memset(gBuf, 0, 8); + if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */, + 4 /* format: bytes from index */, + 0 /* addr desc index */, gBuf, 8))) { + if (2 == err) { /* command not supported */ + if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, + 4 /* format: bytes from index */, gBuf, 4))) { + if (scsi_debugmode > 0) { + print_on(); + pout("Read defect list (10) Failed: %s\n", scsiErrString(err)); + print_off(); + } + return; + } else + got_rd12 = 0; + } else { + if (scsi_debugmode > 0) { + print_on(); + pout("Read defect list (12) Failed: %s\n", scsiErrString(err)); + print_off(); + } + return; + } + } else + got_rd12 = 1; - memset(gBuf, 0, 4); - if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, - 4 /* bytes from index */, gBuf, 4))) { - if (scsi_debugmode > 0) { + if (got_rd12) { + generation = (gBuf[2] << 8) + gBuf[3]; + if ((generation > 1) && (scsi_debugmode > 0)) { print_on(); - pout("Read defect list (10) Failed: %s\n", scsiErrString(err)); + pout("Read defect list (12): generation=%d\n", generation); print_off(); } - return; + dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7]; + } else { + dl_len = (gBuf[2] << 8) + gBuf[3]; } if (0x8 != (gBuf[1] & 0x18)) { print_on(); @@ -340,6 +373,11 @@ static void scsiPrintGrownDefectListLen(scsi_device * device) case 0: /* short block */ div = 4; break; + case 1: /* extended bytes from index */ + case 2: /* extended physical sector */ + /* extended = 1; # might use in future */ + div = 8; + break; case 3: /* long block */ case 4: /* bytes from index */ case 5: /* physical sector */ @@ -351,19 +389,19 @@ static void scsiPrintGrownDefectListLen(scsi_device * device) print_off(); break; } - dl_len = (gBuf[2] << 8) + gBuf[3]; if (0 == dl_len) - pout("Elements in grown defect list: 0\n"); + pout("Elements in grown defect list: 0\n\n"); else { if (0 == div) - pout("Grown defect list length=%d bytes [unknown " - "number of elements]\n", dl_len); + pout("Grown defect list length=%u bytes [unknown " + "number of elements]\n\n", dl_len); else - pout("Elements in grown defect list: %d\n", dl_len / div); + pout("Elements in grown defect list: %u\n\n", dl_len / div); } } -static void scsiPrintSeagateCacheLPage(scsi_device * device) +static void +scsiPrintSeagateCacheLPage(scsi_device * device) { int k, j, num, pl, pc, err, len; unsigned char * ucp; @@ -392,7 +430,7 @@ static void scsiPrintSeagateCacheLPage(scsi_device * device) switch (pc) { case 0: case 1: case 2: case 3: case 4: break; - default: + default: if (scsi_debugmode > 0) { print_on(); pout("Vendor (Seagate) cache lpage has unexpected parameter" @@ -436,9 +474,11 @@ static void scsiPrintSeagateCacheLPage(scsi_device * device) num -= pl; ucp += pl; } + pout("\n"); } -static void scsiPrintSeagateFactoryLPage(scsi_device * device) +static void +scsiPrintSeagateFactoryLPage(scsi_device * device) { int k, j, num, pl, pc, len, err, good, bad; unsigned char * ucp; @@ -470,7 +510,7 @@ static void scsiPrintSeagateFactoryLPage(scsi_device * device) case 0: case 8: ++good; break; - default: + default: ++bad; break; } @@ -530,9 +570,11 @@ static void scsiPrintSeagateFactoryLPage(scsi_device * device) num -= pl; ucp += pl; } + pout("\n"); } -static void scsiPrintErrorCounterLog(scsi_device * device) +static void +scsiPrintErrorCounterLog(scsi_device * device) { struct scsiErrorCounter errCounterArr[3]; struct scsiErrorCounter * ecp; @@ -563,7 +605,7 @@ static void scsiPrintErrorCounterLog(scsi_device * device) } } if (found[0] || found[1] || found[2]) { - pout("\nError counter log:\n"); + pout("Error counter log:\n"); pout(" Errors Corrected by Total " "Correction Gigabytes Total\n"); pout(" ECC rereads/ errors " @@ -574,15 +616,15 @@ static void scsiPrintErrorCounterLog(scsi_device * device) 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], + 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 = ecp->counter[5] / 1000000000.0; pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]); } } - else - pout("\nError Counter logging not supported\n"); + else + pout("Error Counter logging not supported\n"); if (gNonMediumELPage && (0 == scsiLogSense(device, NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeNonMediumErrPage(gBuf, &nme); @@ -636,15 +678,16 @@ static void scsiPrintErrorCounterLog(scsi_device * device) "bytes\n", LOG_RESP_LONG_LEN, truncated); } } + pout("\n"); } static const char * self_test_code[] = { - "Default ", - "Background short", - "Background long ", + "Default ", + "Background short", + "Background long ", "Reserved(3) ", - "Abort background", - "Foreground short", + "Abort background", + "Foreground short", "Foreground long ", "Reserved(7) " }; @@ -658,12 +701,12 @@ static const char * self_test_result[] = { "Failed in first segment ", "Failed in second segment ", "Failed in segment --> ", - "Reserved(8) ", - "Reserved(9) ", - "Reserved(10) ", - "Reserved(11) ", - "Reserved(12) ", - "Reserved(13) ", + "Reserved(8) ", + "Reserved(9) ", + "Reserved(10) ", + "Reserved(11) ", + "Reserved(12) ", + "Reserved(13) ", "Reserved(14) ", "Self test in progress ..." }; @@ -672,13 +715,23 @@ static const char * self_test_result[] = { // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or // FAILSMART is returned. -static int scsiPrintSelfTest(scsi_device * device) +static int +scsiPrintSelfTest(scsi_device * device) { int num, k, n, res, err, durationSec; int noheader = 1; int retval = 0; UINT8 * ucp; uint64_t ull=0; + struct scsi_sense_disect sense_info; + + // check if test is running + if (!scsiRequestSense(device, &sense_info) && + (sense_info.asc == 0x04 && sense_info.ascq == 0x09 && + sense_info.progress != -1)) { + pout("Self-test execution status:\t\t%d%% of test remaining\n", + 100 - ((sense_info.progress * 100) / 65535)); + } if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, LOG_RESP_SELF_TEST_LEN, 0))) { @@ -716,7 +769,7 @@ static int scsiPrintSelfTest(scsi_device * device) // only print header if needed if (noheader) { - pout("\nSMART Self-test log\n"); + pout("SMART Self-test log\n"); pout("Num Test Status segment " "LifeTime LBA_first_err [SK ASC ASQ]\n"); pout(" Description number " @@ -725,7 +778,7 @@ static int scsiPrintSelfTest(scsi_device * device) } // print parameter code (test number) & self-test code text - pout("#%2d %s", (ucp[0] << 8) | ucp[1], + pout("#%2d %s", (ucp[0] << 8) | ucp[1], self_test_code[(ucp[4] >> 5) & 0x7]); // check the self-test result nibble, using the self-test results @@ -777,9 +830,9 @@ static int scsiPrintSelfTest(scsi_device * device) if (n==0 && res==0xf) // self-test in progress pout(" NOW"); - else + else pout(" %5d", n); - + // construct 8-byte integer address of first failure for (i = 0; i < 8; i++) { ull <<= 8; @@ -808,12 +861,12 @@ static int scsiPrintSelfTest(scsi_device * device) 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); } + pout("\n"); return retval; } @@ -845,7 +898,8 @@ static const char * reassign_status[] = { // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). -static int scsiPrintBackgroundResults(scsi_device * device) +static int +scsiPrintBackgroundResults(scsi_device * device) { int num, j, m, err, pc, pl, truncated; int noheader = 1; @@ -888,7 +942,7 @@ static int scsiPrintBackgroundResults(scsi_device * device) case 0: if (noheader) { noheader = 0; - pout("\nBackground scan results log\n"); + pout("Background scan results log\n"); } pout(" Status: "); if ((pl < 16) || (num < 16)) { @@ -945,6 +999,7 @@ static int scsiPrintBackgroundResults(scsi_device * device) if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); + pout("\n"); return retval; } @@ -952,7 +1007,8 @@ static int scsiPrintBackgroundResults(scsi_device * device) // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). -static int scsiPrintSSMedia(scsi_device * device) +static int +scsiPrintSSMedia(scsi_device * device) { int num, err, pc, pl, truncated; int retval = 0; @@ -990,14 +1046,14 @@ static int scsiPrintSSMedia(scsi_device * device) pl = ucp[3] + 4; switch (pc) { case 1: - if (pl < 8) { + if (pl < 8) { print_on(); pout("Percentage used endurance indicator too short (pl=%d)\n", pl); print_off(); return FAILSMART; - } + } pout("SS Media used endurance indicator: %d%%\n", ucp[7]); - default: /* ignore other parameter codes */ + default: /* ignore other parameter codes */ break; } num -= pl; @@ -1006,8 +1062,8 @@ static int scsiPrintSSMedia(scsi_device * device) return retval; } -static void show_sas_phy_event_info(int peis, unsigned int val, - unsigned thresh_val) +static void +show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val) { unsigned int u; @@ -1137,7 +1193,8 @@ static void show_sas_phy_event_info(int peis, unsigned int val, } } -static void show_sas_port_param(unsigned char * ucp, int param_len) +static void +show_sas_port_param(unsigned char * ucp, int param_len) { int j, m, n, nphys, t, sz, spld_len; unsigned char * vcp; @@ -1268,7 +1325,8 @@ static void show_sas_port_param(unsigned char * ucp, int param_len) } // Returns 1 if okay, 0 if non SAS descriptors -static int show_protocol_specific_page(unsigned char * resp, int len) +static int +show_protocol_specific_page(unsigned char * resp, int len) { int k, num, param_len; unsigned char * ucp; @@ -1284,6 +1342,7 @@ static int show_protocol_specific_page(unsigned char * resp, int len) k += param_len; ucp += param_len; } + pout("\n"); return 1; } @@ -1294,20 +1353,21 @@ static int show_protocol_specific_page(unsigned char * resp, int len) // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or // FAILSMART is returned. -static int scsiPrintSasPhy(scsi_device * device, int reset) +static int +scsiPrintSasPhy(scsi_device * device, int reset) { int num, err; if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); - pout("scsiPrintSasPhy Log Sense Failed [%s]\n", scsiErrString(err)); + pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) { print_on(); - pout("Protocol specific Log Sense Failed, page mismatch\n"); + pout("Protocol specific Log Sense Failed, page mismatch\n\n"); print_off(); return FAILSMART; } @@ -1315,7 +1375,7 @@ static int scsiPrintSasPhy(scsi_device * device, int reset) num = (gBuf[2] << 8) + gBuf[3]; if (1 != show_protocol_specific_page(gBuf, num + 4)) { print_on(); - pout("Only support protocol specific log page on SAS devices\n"); + pout("Only support protocol specific log page on SAS devices\n\n"); print_off(); return FAILSMART; } @@ -1323,7 +1383,7 @@ static int scsiPrintSasPhy(scsi_device * device, int reset) if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */, PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) { print_on(); - pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n", + pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n", scsiErrString(err)); print_off(); return FAILSMART; @@ -1372,16 +1432,19 @@ static const char * transport_proto_arr[] = { }; /* Returns 0 on success, 1 on general error and 2 for early, clean exit */ -static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all) +static int +scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all) { char timedatetz[DATEANDEPOCHLEN]; struct scsi_iec_mode_page iec; - int err, iec_err, len, req_len, avail_len; + int err, iec_err, len, req_len, avail_len, n; int is_tape = 0; int peri_dt = 0; int returnval = 0; int transport = -1; - + int form_factor = 0; + int protect = 0; + memset(gBuf, 0, 96); req_len = 36; if ((err = scsiStdInquiry(device, gBuf, req_len))) { @@ -1411,11 +1474,18 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool print_off(); return 1; } + if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) { - pout("Vendor: %.8s\n", (char *)&gBuf[8]); - pout("Product: %.16s\n", (char *)&gBuf[16]); - if (gBuf[32] >= ' ') - pout("Revision: %.4s\n", (char *)&gBuf[32]); + char vendor[8+1], product[16+1], revision[4+1]; + scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8); + scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16); + scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4); + + pout("=== START OF INFORMATION SECTION ===\n"); + pout("Vendor: %.8s\n", vendor); + pout("Product: %.16s\n", product); + if (gBuf[32] >= ' ') + pout("Revision: %.4s\n", revision); } if (!*device->get_req_type()/*no type requested*/ && @@ -1427,18 +1497,122 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool if (! all) return 0; - unsigned int lb_size; - char cap_str[64]; - char si_str[64]; - char lb_str[16]; - uint64_t capacity = scsiGetSize(device, &lb_size); + protect = gBuf[5] & 0x1; /* from and including SPC-3 */ + + if (! is_tape) { /* only do this for disks */ + unsigned int lb_size = 0; + unsigned char lb_prov_resp[8]; + char cap_str[64]; + char si_str[64]; + char lb_str[16]; + int lb_per_pb_exp = 0; + uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp); + + if (capacity) { + format_with_thousands_sep(cap_str, sizeof(cap_str), capacity); + format_capacity(si_str, sizeof(si_str), capacity); + pout("User Capacity: %s bytes [%s]\n", cap_str, si_str); + snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size); + pout("Logical block size: %s bytes\n", lb_str); + } + int lbpme = -1; + int lbprz = -1; + if (protect || lb_per_pb_exp) { + unsigned char rc16_12[20] = {0, }; + + if (0 == scsiGetProtPBInfo(device, rc16_12)) { + lb_per_pb_exp = rc16_12[1] & 0xf; /* just in case */ + if (lb_per_pb_exp > 0) { + snprintf(lb_str, sizeof(lb_str) - 1, "%u", + (lb_size * (1 << lb_per_pb_exp))); + pout("Physical block size: %s bytes\n", lb_str); + n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3]; + pout("Lowest aligned LBA: %d\n", n); + } + if (rc16_12[0] & 0x1) { /* PROT_EN set */ + int p_type = ((rc16_12[0] >> 1) & 0x7); + + switch (p_type) { + case 0 : + pout("Formatted with type 1 protection\n"); + break; + case 1 : + pout("Formatted with type 2 protection\n"); + break; + case 2 : + pout("Formatted with type 3 protection\n"); + break; + default: + pout("Formatted with unknown protection type [%d]\n", + p_type); + break; + } + int p_i_exp = ((rc16_12[1] >> 4) & 0xf); + + if (p_i_exp > 0) + pout("%d protection information intervals per " + "logical block\n", (1 << p_i_exp)); + } + /* Pick up some LB provisioning info since its available */ + lbpme = !! (rc16_12[2] & 0x80); + lbprz = !! (rc16_12[2] & 0x40); + } + } + if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING, + lb_prov_resp, sizeof(lb_prov_resp))) { + int prov_type = lb_prov_resp[6] & 0x7; + + if (-1 == lbprz) + lbprz = !! (lb_prov_resp[5] & 0x4); + switch (prov_type) { + case 0: + pout("Logical block provisioning type unreported, " + "LBPME=%d, LBPRZ=%d\n", lbpme, lbprz); + break; + case 1: + pout("LU is resource provisioned, LBPRZ=%d\n", lbprz); + break; + case 2: + pout("LU is thin provisioned, LBPRZ=%d\n", lbprz); + break; + default: + pout("LU provisioning type reserved [%d], LBPRZ=%d\n", + prov_type, lbprz); + break; + } + } else if (1 == lbpme) + pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz); - if (capacity) { - format_with_thousands_sep(cap_str, sizeof(cap_str), capacity); - format_capacity(si_str, sizeof(si_str), capacity); - pout("User Capacity: %s bytes [%s]\n", cap_str, si_str); - snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size); - pout("Logical block size: %s bytes\n", lb_str); + int rpm = scsiGetRPM(device, modese_len, &form_factor); + if (rpm > 0) { + if (1 == rpm) + pout("Rotation Rate: Solid State Device\n"); + else + pout("Rotation Rate: %d rpm\n", rpm); + } + if (form_factor > 0) { + const char * cp = NULL; + + switch (form_factor) { + case 1: + cp = "5.25"; + break; + case 2: + cp = "3.5"; + break; + case 3: + cp = "2.5"; + break; + case 4: + cp = "1.8"; + break; + case 5: + cp = "< 1.8"; + break; + } + if (cp) + pout("Form Factor: %s inches\n", cp); + } } /* Do this here to try and detect badly conforming devices (some USB @@ -1455,12 +1629,13 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool modese_len = iec.modese_len; if (! dont_print_serial_number) { - if (0 == (err = scsiInquiryVpd(device, 0x83, gBuf, 200))) { - char s[256]; + if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION, + gBuf, 252))) { + char s[256]; len = gBuf[3]; - scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport); - if (strlen(s) > 0) + scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport); + if (strlen(s) > 0) pout("Logical Unit id: %s\n", s); } else if (scsi_debugmode > 0) { print_on(); @@ -1470,10 +1645,14 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); print_off(); } - if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) { + if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER, + gBuf, 252))) { + char serial[256]; len = gBuf[3]; + gBuf[4 + len] = '\0'; - pout("Serial number: %s\n", &gBuf[4]); + scsi_format_id_string(serial, &gBuf[4], len); + pout("Serial number: %s\n", serial); } else if (scsi_debugmode > 0) { print_on(); if (SIMPLE_ERR_BAD_RESP == err) @@ -1485,7 +1664,7 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool } // print SCSI peripheral device type - if (peri_dt < (int)(sizeof(peripheral_dt_arr) / + if (peri_dt < (int)(sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0]))) pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); else @@ -1528,15 +1707,13 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool } failuretest(MANDATORY_CMD, returnval|=FAILID); } - + if (iec_err) { if (!is_tape) { print_on(); - pout("Device does not support SMART"); + pout("SMART support is: Unavailable - device lacks SMART capability.\n"); if (scsi_debugmode > 0) pout(" [%s]\n", scsiErrString(iec_err)); - else - pout("\n"); print_off(); } gIecMPage = 0; @@ -1544,22 +1721,24 @@ static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool } if (!is_tape) - pout("Device supports SMART and is %s\n", + pout("SMART support is: Available - device has SMART capability.\n" + "SMART support is: %s\n", (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled"); - pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? - "Temperature Warning Enabled" : - "Temperature Warning Disabled or Not Supported"); + pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? + "Temperature Warning: Enabled" : + "Temperature Warning: Disabled or Not Supported"); return 0; } -static int scsiSmartEnable(scsi_device * device) +static int +scsiSmartEnable(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); - pout("unable to fetch IEC (SMART) mode page [%s]\n", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; @@ -1575,7 +1754,7 @@ static int scsiSmartEnable(scsi_device * device) } /* 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", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else @@ -1587,15 +1766,16 @@ static int scsiSmartEnable(scsi_device * device) scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); return 0; } - -static int scsiSmartDisable(scsi_device * device) + +static int +scsiSmartDisable(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); - pout("unable to fetch IEC (SMART) mode page [%s]\n", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; @@ -1611,7 +1791,7 @@ static int scsiSmartDisable(scsi_device * device) } /* 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", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else @@ -1624,14 +1804,15 @@ static int scsiSmartDisable(scsi_device * device) return 0; } -static void scsiPrintTemp(scsi_device * device) +static void +scsiPrintTemp(scsi_device * 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); @@ -1640,55 +1821,140 @@ static void scsiPrintTemp(scsi_device * device) } if (trip) pout("Drive Trip Temperature: %d C\n", trip); + if (temp || trip) + pout("\n"); } /* Main entry point used by smartctl command. Return 0 for success */ -int scsiPrintMain(scsi_device * device, const scsi_print_options & options) +int +scsiPrintMain(scsi_device * device, const scsi_print_options & options) { int checkedSupportedLogPages = 0; UINT8 peripheral_type = 0; int returnval = 0; int res, durationSec; + struct scsi_sense_disect sense_info; bool any_output = options.drive_info; + if (supported_vpd_pages_p) { + delete supported_vpd_pages_p; + supported_vpd_pages_p = NULL; + } + supported_vpd_pages_p = new supported_vpd_pages(device); + res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info); if (res) { if (2 == res) return 0; else failuretest(MANDATORY_CMD, returnval |= FAILID); - any_output = true; + any_output = true; } + // Print read look-ahead status for disks + short int wce = -1, rcd = -1; + if (options.get_rcd || options.get_wce) { + if (SCSI_PT_DIRECT_ACCESS == peripheral_type) + res = scsiGetSetCache(device, modese_len, &wce, &rcd); + else + res = -1; // fetch for disks only + any_output = true; + } + + if (options.get_rcd) { + pout("Read Cache is: %s\n", + res ? "Unavailable" : // error + rcd ? "Disabled" : "Enabled"); + } + + if (options.get_wce) { + pout("Writeback Cache is: %s\n", + res ? "Unavailable" : // error + !wce ? "Disabled" : "Enabled"); + } + if (options.drive_info) + pout("\n"); + + // START OF THE ENABLE/DISABLE SECTION OF THE CODE + if ( options.smart_disable || options.smart_enable + || options.smart_auto_save_disable || options.smart_auto_save_enable) + pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); + if (options.smart_enable) { if (scsiSmartEnable(device)) failuretest(MANDATORY_CMD, returnval |= FAILSMART); - any_output = true; + any_output = true; } if (options.smart_disable) { if (scsiSmartDisable(device)) failuretest(MANDATORY_CMD,returnval |= FAILSMART); - any_output = true; + any_output = true; } - + if (options.smart_auto_save_enable) { if (scsiSetControlGLTSD(device, 0, modese_len)) { pout("Enable autosave (clear GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } + else { + pout("Autosave enabled (GLTSD bit set).\n"); + } + any_output = true; + } + + // Enable/Disable write cache + if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) { + short int enable = wce = (options.set_wce > 0); + rcd = -1; + if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { + pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"), + device->get_errmsg()); + failuretest(OPTIONAL_CMD,returnval |= FAILSMART); + } + else + pout("Write cache %sabled\n", (enable ? "en" : "dis")); + any_output = true; + } + + // Enable/Disable read cache + if (options.set_rcd && SCSI_PT_DIRECT_ACCESS == peripheral_type) { + short int enable = (options.set_rcd > 0); + rcd = !enable; + wce = -1; + if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { + pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"), + device->get_errmsg()); + failuretest(OPTIONAL_CMD,returnval |= FAILSMART); + } + else + pout("Read cache %sabled\n", (enable ? "en" : "dis")); any_output = true; } - + if (options.smart_auto_save_disable) { if (scsiSetControlGLTSD(device, 1, modese_len)) { pout("Disable autosave (set GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } + else { + pout("Autosave disabled (GLTSD bit cleared).\n"); + } any_output = true; } - + if ( options.smart_disable || options.smart_enable + || options.smart_auto_save_disable || options.smart_auto_save_enable) + pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE + + // START OF READ-ONLY OPTIONS APART FROM -V and -i + if ( options.smart_check_status || options.smart_ss_media_log + || options.smart_vendor_attrib || options.smart_error_log + || options.smart_selftest_log || options.smart_vendor_attrib + || options.smart_background_log || options.sasphy + ) + pout("=== START OF READ SMART DATA SECTION ===\n"); + if (options.smart_check_status) { scsiGetSupportedLogPages(device); checkedSupportedLogPages = 1; @@ -1711,7 +1977,8 @@ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) } } any_output = true; - } + } + if (options.smart_ss_media_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); @@ -1726,8 +1993,6 @@ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); if (gTempLPage) { - if (options.smart_check_status) - pout("\n"); scsiPrintTemp(device); } if (gStartStopLPage) @@ -1790,6 +2055,24 @@ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) pout("Short Foreground Self Test Successful\n"); any_output = true; } + // check if another test is running + if (options.smart_short_selftest || options.smart_extend_selftest) { + if (!scsiRequestSense(device, &sense_info) && + (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) { + if (!options.smart_selftest_force) { + pout("Can't start self-test without aborting current test"); + if (sense_info.progress != -1) { + pout(" (%d%% remaining)", + 100 - sense_info.progress * 100 / 65535); + } + pout(",\nadd '-t force' option to override, or run 'smartctl -X' " + "to abort test.\n"); + return -1; + } + else + scsiSmartSelfTestAbort(device); + } + } if (options.smart_short_selftest) { if (scsiSmartShortSelfTest(device)) return returnval | FAILSMART; @@ -1806,11 +2089,11 @@ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) time_t t = time(NULL); t += durationSec; - pout("Please wait %d minutes for test to complete.\n", + 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"); + pout("Use smartctl -X to abort test\n"); any_output = true; } if (options.smart_extend_cap_selftest) { @@ -1823,12 +2106,12 @@ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) return returnval | FAILSMART; pout("Self Test returned without error\n"); any_output = true; - } + } if (options.sasphy) { if (scsiPrintSasPhy(device, options.sasphy_reset)) return returnval | FAILSMART; any_output = true; - } + } if (!any_output) pout("SCSI device successfully opened\n\n" diff --git a/scsiprint.h b/scsiprint.h index fc332f6..fad594d 100644 --- a/scsiprint.h +++ b/scsiprint.h @@ -7,7 +7,7 @@ * Copyright (C) 2000 Michael Cornwell * * Additional SCSI work: - * Copyright (C) 2003-10 Douglas Gilbert + * Copyright (C) 2003-13 Douglas Gilbert * * 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 @@ -15,8 +15,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -29,7 +29,7 @@ #ifndef SCSI_PRINT_H_ #define SCSI_PRINT_H_ -#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 3413 2011-09-06 21:23:00Z dpgilbert $\n" +#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 3776 2013-02-17 04:25:42Z dpgilbert $\n" // Options for scsiPrintMain struct scsi_print_options @@ -49,8 +49,12 @@ struct scsi_print_options bool smart_short_selftest, smart_short_cap_selftest; bool smart_extend_selftest, smart_extend_cap_selftest; bool smart_selftest_abort; + bool smart_selftest_force; // Ignore already running test bool sasphy, sasphy_reset; + + bool get_wce, get_rcd; + short int set_wce, set_rcd; // disable(-1), enable(1) cache scsi_print_options() : drive_info(false), @@ -66,7 +70,10 @@ struct scsi_print_options smart_short_selftest(false), smart_short_cap_selftest(false), smart_extend_selftest(false), smart_extend_cap_selftest(false), smart_selftest_abort(false), - sasphy(false), sasphy_reset(false) + smart_selftest_force(false), + sasphy(false), sasphy_reset(false), + get_wce(false), get_rcd(false), + set_wce(0), set_rcd(0) { } }; diff --git a/smartctl.8.in b/smartctl.8.in index 42ea719..c30e03e 100644 --- a/smartctl.8.in +++ b/smartctl.8.in @@ -1,21 +1,21 @@ .ig - Copyright (C) 2002-10 Bruce Allen - - $Id: smartctl.8.in 3561 2012-06-05 19:49:31Z 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 - 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/ +Copyright (C) 2002-10 Bruce Allen +Copyright (C) 2004-13 Christian Franke + +$Id: smartctl.8.in 3799 2013-03-15 17:47:25Z 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 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 . + +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/ .. .TH SMARTCTL 8 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE @@ -39,23 +39,16 @@ CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL -\fBsmartctl\fP controls the Self\-Monitoring, Analysis and Reporting -Technology (SMART) system built into many ATA\-3 and later ATA, IDE and -SCSI\-3 hard drives. The purpose of SMART is to monitor the reliability -of the hard drive and predict drive failures, and to carry out -different types of drive self\-tests. This version of \fBsmartctl\fP -is compatible with ATA/ATAPI\-7 and earlier standards (see REFERENCES -below) - -\fBsmartctl\fP is a command line utility designed to perform SMART -tasks such as printing the SMART self\-test and error logs, enabling -and disabling SMART automatic testing, and initiating device -self\-tests. Note: if the user issues a SMART command that is -(apparently) not implemented by the device, \fBsmartctl\fP will print -a warning message but issue the command anyway (see the \fB\-T, -\-\-tolerance\fP option below). This should not cause problems: on -most devices, unimplemented SMART commands issued to a drive are -ignored and/or return an error. +\fBsmartctl\fP controls the Self-Monitoring, Analysis and +Reporting Technology (SMART) system built into most ATA/SATA and SCSI/SAS +hard drives and solid-state drives. +The purpose of SMART is to monitor the reliability of the hard drive +and predict drive failures, and to carry out different types of drive +self-tests. +\fBsmartctl\fP also supports some features not related to SMART. +This version of \fBsmartctl\fP is compatible with +ACS-2, ATA8-ACS, ATA/ATAPI-7 and earlier standards +(see \fBREFERENCES\fP below). \fBsmartctl\fP also provides support for polling TapeAlert messages from SCSI tape drives and changers. @@ -78,7 +71,14 @@ below. For disks behind HighPoint RocketRAID controllers you may need \fB"/dev/sd[a\-z]"\fP. For disks behind Areca SATA RAID controllers, you need \fB"/dev/sg[2\-9]"\fP (note that smartmontools interacts with the Areca controllers via a SCSI generic device which is different -than the SCSI device used for reading and writing data)! +than the SCSI device used for reading and writing data)! For HP Smart +Array RAID controllers, there are three currently supported drivers: cciss, +hpsa, and hpahcisr. For disks accessed via the cciss driver the device nodes +are of the form \fB"/dev/cciss/c[0\-9]d0"\fP. For disks accessed via +the hpahcisr and hpsa drivers, the device nodes you need are \fB"/dev/sg[0\-9]*"\fP. +("lsscsi \-g" is helpful in determining which scsi generic device node corresponds +to which device.) Use the nodes corresponding to the RAID controllers, +not the nodes corresponding to logical drives. See the \fB\-d\fP option below, as well. .\" %ENDIF OS Linux .\" %IF OS Darwin .IP \fBDARWIN\fP: 9 @@ -93,7 +93,9 @@ Firewire devices (see INSTALL file). .IP \fBFREEBSD\fP: 9 Use the forms \fB"/dev/ad[0\-9]+"\fP for IDE/ATA devices and \fB"/dev/da[0\-9]+"\fP or \fB"/dev/pass[0\-9]+"\fP for SCSI devices. -For SATA devices on AHCI bus use \fB"/dev/ada[0\-9]+"\fP format. +For SATA devices on AHCI bus use \fB"/dev/ada[0\-9]+"\fP format. For HP Smart +Array RAID controllers, use \fB"/dev/ciss[0\-9]"\fP (and see the \fB-d\fP option, +below). .\" %ENDIF OS FreeBSD .\" %IF OS NetBSD OpenBSD .IP \fBNETBSD/OPENBSD\fP: 9 @@ -108,18 +110,11 @@ your architecture. Use the forms \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk devices, and \fB"/dev/rmt/*"\fP for SCSI tape devices. .\" %ENDIF OS Solaris -.\" %IF OS Windows -.IP \fBWINDOWS\ 9x/ME\fP: 9 -Use the forms \fB"/dev/hd[a\-d]"\fP for standard IDE/ATA devices -accessed via SMARTVSD.VXD, and \fB"/dev/hd[e\-h]"\fP for additional devices -accessed via a patched SMARTVSE.VXD (see INSTALL file for details). -Use the form \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices via an aspi dll -on ASPI adapter 0\-9, ID 0\-15. The prefix \fB"/dev/"\fP is optional. -.\" %ENDIF OS Windows .\" %IF OS Windows Cygwin -.IP \fBWINDOWS\ NT4/2000/XP/2003/Vista/Win7/2008\fP: 9 +.IP \fBWINDOWS\fP: 9 Use the forms \fB"/dev/sd[a\-z]"\fP for IDE/(S)ATA and SCSI disks "\\\\.\\PhysicalDrive[0\-25]" (where "a" maps to "0"). +Use \fB"/dev/sd[a\-z][a\-z]"\fP for "\\\\.\\PhysicalDrive[26\-...]". These disks can also be referred to as \fB"/dev/pd[0\-255]"\fP for "\\\\.\\PhysicalDrive[0\-255]". ATA disks can also be referred to as \fB"/dev/hd[a\-z]"\fP for @@ -145,15 +140,11 @@ For disks behind an Intel ICHxR controller with RST driver use \fB"/dev/csmi[0\-9],N"\fP where N specifies the port behind the logical scsi controller "\\\\.\\Scsi[0\-9]:". -[NEW EXPERIMENTAL SMARTCTL FEATURE] For SATA disks behind an Areca SATA -or SAS controller use \fB"/dev/arcmsr[0\-9]"\fP, see \'\-d areca,N[/E]\' below. +[NEW EXPERIMENTAL SMARTCTL FEATURE] For SATA or SAS disks behind an Areca +controller use \fB"/dev/arcmsr[0\-9]"\fP, see \'\-d areca,N[/E]\' below. The prefix \fB"/dev/"\fP is optional. .\" %ENDIF OS Windows Cygwin -.\" %IF OS Cygwin -.IP \fBCYGWIN\fP: 9 -See "WINDOWS NT4/2000/XP/2003/Vista/Win7/2008" above. -.\" %ENDIF OS Cygwin .\" %IF OS OS2 .IP \fBOS/2,eComStation\fP: 9 Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices. @@ -164,7 +155,7 @@ interprets it's own debug output from standard input. See \'\-r ataioctl\' below for details. .PP Based on the device path, \fBsmartctl\fP will guess the device type -(ATA or SCSI). If necessary, the \'\-d\' option can be used to over\-ride +(ATA or SCSI). If necessary, the \'\-d\' option can be used to override this guess Note that the printed output of \fBsmartctl\fP displays most numerical @@ -203,6 +194,20 @@ in the smartmontools database (see \'\-v\' options below). If so, the drive model family may also be printed. If \'\-n\' (see below) is specified, the power mode of the drive is printed. .TP +.B \-\-identify[=[w][nvb]] +[ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] Prints an annotated +table of the IDENTIFY DEVICE data. +By default, only valid words (words not equal to 0x0000 or 0xffff) +and nonzero bits and bit fields are printed. +This can be changed by the optional argument which consists of one or +two characters from the set \'wnvb\'. +The character \'w\' enables printing of all 256 words. The character +\'n\' suppresses printing of bits, \'v\' enables printing of all bits +from valid words, \'b\' enables printing of all bits. +For example \'\-\-identify=n\' (valid words, no bits) produces the +shortest output and \'\-\-identify=wb\' (all words, all bits) produces +the longest output. +.TP .B \-a, \-\-all Prints all SMART information about the disk, or TapeAlert information about the tape drive or changer. For ATA devices this is equivalent @@ -222,7 +227,7 @@ Prints all SMART and non-SMART information about the device. For ATA devices this is equivalent to .nf \'\-H \-i \-g all \-c \-A \-f brief \-l xerror,error \-l xselftest,selftest -\-l selective \-l directory \-l scttemp \-l scterc \-l sataphy\'. +\-l selective \-l directory \-l scttemp \-l scterc \-l devstat \-l sataphy\'. .fi and for SCSI, this is equivalent to .nf @@ -245,14 +250,14 @@ This option can be used to create a draft \fBsmartd.conf\fP file. All options after \'\-\-\' are appended to each output line. For example: .nf -smartctl --scan-open -- -a -W 4,45,50 -m admin@work > smartd.conf +smartctl \-\-scan\-open \-\- \-a \-W 4,45,50 \-m admin@work > smartd.conf .fi .TP .B \-g NAME, \-\-get=NAME -Get non\-SMART device settings. See \'\-s, \-\-set\' below for further info. +Get non-SMART device settings. See \'\-s, \-\-set\' below for further info. .TP -.B RUN\-TIME BEHAVIOR OPTIONS: +.B RUN-TIME BEHAVIOR OPTIONS: .TP .B \-q TYPE, \-\-quietmode=TYPE Specifies that \fBsmartctl\fP should run in one of the two quiet modes @@ -260,11 +265,11 @@ described here. The valid arguments to this option are: .I errorsonly \- only print: For the \'\-l error\' option, if nonzero, the number -of errors recorded in the SMART error log and the power\-on time when +of errors recorded in the SMART error log and the power-on time when they occurred; For the \'\-l selftest\' option, errors recorded in the device -self\-test log; For the \'\-H\' option, SMART "disk failing" status or device -Attributes (pre\-failure or usage) which failed either now or in the -past; For the \'\-A\' option, device Attributes (pre\-failure or usage) +self-test log; For the \'\-H\' option, SMART "disk failing" status or device +Attributes (pre-failure or usage) which failed either now or in the +past; For the \'\-A\' option, device Attributes (pre-failure or usage) which failed either now or in the past. .I silent @@ -279,13 +284,13 @@ Specifies the type of the device. The valid arguments to this option are: .I auto -- attempt to guess the device type from the device name or from +\- attempt to guess the device type from the device name or from controller type info provided by the operating system or from a matching USB ID entry in the drive database. This is the default. .I test -- prints the guessed type, then opens the device and prints the +\- prints the guessed type, then opens the device and prints the (possibly changed) TYPE name and then exists without performing any further commands. @@ -308,8 +313,7 @@ SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and the other 16 bytes long. The default is the 16 byte variant which can be overridden with either \'\-d sat,12\' or \'\-d sat,16\'. -[NEW EXPERIMENTAL SMARTCTL FEATURE] If \'-d sat,auto\' is specified, -device type SAT (for ATA/SATA disks) is +If \'\-d sat,auto\' is specified, device type SAT (for ATA/SATA disks) is only used if the SCSI INQUIRY data reports a SATL (VENDOR: "ATA "). Otherwise device type SCSI (for SCSI/SAS disks) is used. @@ -320,8 +324,8 @@ The default SCSI operation code is 0x24, but although it can be overridden with \'\-d usbcypress,0xN\', where N is the scsi operation code, you're running the risk of damage to the device or filesystems on it. -.I usbjmicron -- this device type is for SATA disks that are behind a JMicron USB to +.I usbjmicron[,p][,x][,PORT] +\- this device type is for SATA disks that are behind a JMicron USB to PATA/SATA bridge. The 48-bit ATA commands (required e.g. for \'\-l xerror\', see below) do not work with all of these bridges and are therefore disabled by default. These commands can be enabled by \'\-d usbjmicron,x\'. @@ -335,6 +339,13 @@ CAUTION: Specifying \',x\' for a device which does not support it results in I/O errors and may disconnect the drive. The same applies if the specified PORT does not exist or is not connected to a disk. +[NEW EXPERIMENTAL SMARTCTL FEATURE] +The Prolific PL2507/3507 USB bridges with older firmware support a pass-through +command similar to JMicron and work with \'\-d usbjmicron,0\'. +Newer Prolific firmware requires a modified command which can be selected by +\'\-d usbjmicron,p\'. +Note that this does not yet support the SMART status command. + .I usbsunplus \- this device type is for SATA disks that are behind a SunplusIT USB to SATA bridge. @@ -356,12 +367,18 @@ Use syntax such as: .nf \fBsmartctl \-a \-d megaraid,0 /dev/sdb\fP .fi +.nf +\fBsmartctl \-a \-d megaraid,0 /dev/bus/0\fP +.fi This interface will also work for Dell PERC controllers. -The following /dev/XXX entry must exist: +It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus +number. + +The following entry in /proc/devices must exist: .fi -For PERC2/3/4 controllers: \fB/dev/megadev0\fP +For PERC2/3/4 controllers: \fBmegadevN\fP .fi -For PERC5/6 controllers: \fB/dev/megaraid_sas_ioctl_node\fP +For PERC5/6 controllers: \fBmegaraid_sas_ioctlN\fP .\" %ENDIF OS Linux .\" %IF OS FreeBSD Linux @@ -372,7 +389,7 @@ connected to a 3ware RAID controller. The non-negative integer N is monitored. Use syntax such as: .nf -\fBsmartctl \-a \-d 3ware,2 /dev/sda\fP +\fBsmartctl \-a \-d 3ware,2 /dev/sda\fP [Linux only] .fi .nf \fBsmartctl \-a \-d 3ware,0 /dev/twe0\fP @@ -381,28 +398,31 @@ Use syntax such as: \fBsmartctl \-a \-d 3ware,1 /dev/twa0\fP .fi .nf -\fBsmartctl \-a \-d 3ware,1 /dev/twl0\fP +\fBsmartctl \-a \-d 3ware,1 /dev/twl0\fP [Linux only] +.fi +.nf +\fBsmartctl \-a \-d 3ware,1 /dev/tws0\fP [FreeBSD only] .fi The first two forms, which refer to devices /dev/sda\-z and /dev/twe0\-15, may be used with 3ware series 6000, 7000, and 8000 series controllers -that use the 3x\-xxxx driver. +that use the 3x-xxxx driver. \fBNote that the /dev/sda\-z form is deprecated\fP starting with the Linux 2.6 kernel series and may not be supported by the Linux kernel in the near future. The final form, which refers to devices /dev/twa0\-15, must be used with 3ware 9000 series controllers, which use the 3w\-9xxx driver. -The devices /dev/twl0\-15 must be used with the 3ware/LSI 9750 series +The devices /dev/twl0\-15 [Linux] or /dev/tws0\-15 [FreeBSD] must be used with the 3ware/LSI 9750 series controllers which use the 3w-sas driver. -Note that if the special character device nodes /dev/twl?, /dev/twa? +Note that if the special character device nodes /dev/tw[ls]?, /dev/twa? and /dev/twe? do not exist, or exist with the incorrect major or minor numbers, smartctl will recreate them on the fly. Typically /dev/twa0 -refers to the first 9000\-series controller, /dev/twa1 refers to the +refers to the first 9000-series controller, /dev/twa1 refers to the second 9000 series controller, and so on. The /dev/twl0 devices refers to the first 9750 series controller, /dev/twl1 resfers to the second 9750 series controller, and so on. Likewise /dev/twe0 refers to -the first 6/7/8000\-series controller, /dev/twe1 refers to the second +the first 6/7/8000-series controller, /dev/twe1 refers to the second 6/7/8000 series controller, and so on. Note that for the 6/7/8000 controllers, \fBany\fP of the physical @@ -424,19 +444,19 @@ disk attached to it, the behavior of \fBsmartctl\fP depends upon the specific controller model, firmware, Linux kernel and platform. In some cases you will get a warning message that the device does not exist. In other cases you will be presented with \'void\' data for a -non\-existent device. +non-existent device. -Note that if the /dev/sd? addressing form is used, then older 3w\-xxxx +Note that if the /dev/sd? addressing form is used, then older 3w-xxxx drivers do not pass the "Enable Autosave" (\'\fB\-S on\fP\') and "Enable Automatic Offline" (\'\fB\-o on\fP\') commands to the disk, and produce these types of harmless syslog error -messages instead: "\fB3w\-xxxx: tw_ioctl(): Passthru size (123392) too +messages instead: "\fB3w-xxxx: tw_ioctl(): Passthru size (123392) too big\fP". This can be fixed by upgrading to version 1.02.00.037 or -later of the 3w\-xxxx driver, or by applying a patch to older +later of the 3w-xxxx driver, or by applying a patch to older versions. Alternatively, use the character device /dev/twe0\-15 interface. -The selective self\-test functions (\'\-t select,A\-B\') are only supported -using the character device interface /dev/twl0\-15, /dev/twa0\-15 and /dev/twe0\-15. +The selective self-test functions (\'\-t select,A\-B\') are only supported +using the character device interface /dev/twl0\-15, /dev/tws0\-15, /dev/twa0\-15 and /dev/twe0\-15. The necessary WRITE LOG commands can not be passed through the SCSI interface. @@ -498,21 +518,35 @@ error messages and no SMART information. .I areca,N/E \- [FreeBSD, Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTCTL FEATURE] the -device consists of one or more SATA disks connected to an Areca SAS RAID controller. +device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller. The integer N (range 1 to 128) denotes the channel (slot) and E (range 1 to 8) denotes the enclosure. -Important: This requires upcoming Areca SAS controller firmware version 1.51 or a -recent beta version. +Important: This requires Areca SAS controller firmware version 1.51 or later. .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS FreeBSD Linux .I cciss,N -\- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS disks +\- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS or SATA disks connected to a cciss RAID controller. The non-negative integer N (in the range from 0 to 15 inclusive) denotes which disk on the controller is monitored. -If the controller firmware or driver provides a SAT Layer it may be possible -to monitor also SATA disks by specifiying \'\-d sat+cciss,N\'. +To look at disks behind HP Smart Array controllers, use syntax +such as: +.\" %ENDIF OS FreeBSD Linux +.\" %IF OS Linux +.nf +\fBsmartctl \-a \-d cciss,0 /dev/cciss/c0d0\fP (cciss driver under Linux) +.fi +.nf +\fBsmartctl \-a \-d cciss,0 /dev/sg2\fP (hpsa or hpahcisr drivers under Linux) +.fi +.\" %ENDIF OS Linux +.\" %IF OS FreeBSD +.nf +\fBsmartctl \-a \-d cciss,0 /dev/ciss0\fP (under FreeBSD) +.fi +.\" %ENDIF OS FreeBSD +.\" %IF OS FreeBSD Linux .I hpt,L/M/N \- [FreeBSD and Linux only] the device consists of one or more ATA disks @@ -553,9 +587,9 @@ command failures. The behavior of \fBsmartctl\fP depends upon whether the command is "\fBoptional\fP" or "\fBmandatory\fP". Here "\fBmandatory\fP" means -"required by the ATA/ATAPI\-5 Specification if the device implements +"required by the ATA Specification if the device implements the SMART command set" and "\fBoptional\fP" means "not required by the -ATA/ATAPI\-5 Specification even if the device implements the SMART +ATA Specification even if the device implements the SMART command set." The "\fBmandatory\fP" ATA and SMART commands are: (1) ATA IDENTIFY DEVICE, (2) SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE, (3) SMART ENABLE/DISABLE, and (4) SMART RETURN STATUS. @@ -578,8 +612,8 @@ final message, Feature X is \fBnot\fP enabled. \- ignore failure(s) of \fBmandatory\fP SMART commands. This option may be given more than once. Each additional use of this option will cause one more additional failure to be ignored. Note that the use of -this option can lead to messages like "Feature X not implemented", -followed shortly by "Error: unable to enable Feature X". In a few +this option can lead to messages like "Feature X not supported", +followed shortly by "Feature X enable failed". In a few such cases, contrary to the final message, Feature X \fBis\fP enabled. .I verypermissive @@ -590,7 +624,7 @@ Please see the note above. .B \-b TYPE, \-\-badsum=TYPE [ATA only] Specifies the action \fBsmartctl\fP should take if a checksum error is detected in the: (1) Device Identity Structure, (2) SMART -Self\-Test Log Structure, (3) SMART Attribute Value Structure, (4) SMART +Self-Test Log Structure, (3) SMART Attribute Value Structure, (4) SMART Attribute Threshold Structure, or (5) ATA Error Log Structure. The valid arguments to this option are: @@ -607,7 +641,7 @@ default. .TP .B \-r TYPE, \-\-report=TYPE Intended primarily to help \fBsmartmontools\fP developers understand -the behavior of \fBsmartmontools\fP on non\-conforming or poorly +the behavior of \fBsmartmontools\fP on non-conforming or poorly conforming hardware. This option reports details of \fBsmartctl\fP transactions with the device. The option can be used multiple times. When used just once, it shows a record of the ioctl() transactions @@ -643,13 +677,13 @@ behaviour. This is does not work for SCSI devices yet. .TP .B \-n POWERMODE, \-\-nocheck=POWERMODE [ATA only] Specifies if \fBsmartctl\fP should exit before performing any -checks when the device is in a low\-power mode. It may be used to prevent -a disk from being spun\-up by \fBsmartctl\fP. The power mode is ignored by +checks when the device is in a low-power mode. It may be used to prevent +a disk from being spun-up by \fBsmartctl\fP. The power mode is ignored by default. A nonzero exit status is returned if the device is in one of the -specified low\-power modes (see RETURN VALUES below). +specified low-power modes (see RETURN VALUES below). Note: If this option is used it may also be necessary to specify the device -type with the \'-d\' option. Otherwise the device may spin up due to +type with the \'\-d\' option. Otherwise the device may spin up due to commands issued during device type autodetection. The valid arguments to this option are: @@ -687,9 +721,9 @@ the corresponding disable command. Enables or disables SMART on device. The valid arguments to this option are \fIon\fP and \fIoff\fP. Note that the command \'\-s on\' (perhaps used with with the \'\-o on\' and \'\-S on\' options) should be -placed in a start\-up script for your machine, for example in rc.local or +placed in a start-up script for your machine, for example in rc.local or rc.sysinit. In principle the SMART feature settings are preserved over -power\-cycling, but it doesn\'t hurt to be sure. It is not necessary (or +power-cycling, but it doesn\'t hurt to be sure. It is not necessary (or useful) to enable SMART to see the TapeAlert messages. .TP .B \-o VALUE, \-\-offlineauto=VALUE @@ -700,13 +734,13 @@ and \fIoff\fP. Note that the SMART automatic offline test command is listed as "Obsolete" in every version of the ATA and ATA/ATAPI Specifications. -It was originally part of the SFF\-8035i Revision 2.0 specification, +It was originally part of the SFF-8035i Revision 2.0 specification, but was never part of any ATA specification. However it is implemented and used by many vendors. [Good documentation can be found in IBM\'s Official Published Disk Specifications. For example the IBM Travelstar 40GNX Hard Disk Drive Specifications (Revision 1.1, 22 -April 2002, Publication # 1541, Document S07N\-7715\-02) page 164. You -can also read the SFF\-8035i Specification \-\- see REFERENCES below.] +April 2002, Publication # 1541, Document S07N-7715-02) page 164. You +can also read the SFF-8035i Specification -- see REFERENCES below.] You can tell if automatic offline testing is supported by seeing if this command enables and disables it, as indicated by the \'Auto Offline Data Collection\' part of the SMART capabilities report @@ -722,12 +756,12 @@ type of test can, in principle, degrade the device performance. The automatically, on a regular scheduled basis. Normally, the disk will suspend offline testing while disk accesses are taking place, and then automatically resume it when the disk would otherwise be idle, so in -practice it has little effect. Note that a one\-time offline test can +practice it has little effect. Note that a one-time offline test can also be carried out immediately upon receipt of a user command. See -the \'\-t offline\' option below, which causes a one\-time offline test +the \'\-t offline\' option below, which causes a one-time offline test to be carried out immediately. -The choice (made by the SFF\-8035i and ATA specification authors) of +The choice (made by the SFF-8035i and ATA specification authors) of the word \fItesting\fP for these first two categories is unfortunate, and often leads to confusion. In fact these first two categories of online and offline testing could have been more accurately described @@ -740,9 +774,9 @@ Attributes will go below their failure thresholds; some types of errors may also appear in the SMART error log. These are visible with the \'\-A\' and \'\-l error\' options respectively. -Some SMART attribute values are updated only during off\-line data +Some SMART attribute values are updated only during off-line data collection activities; the rest are updated during normal operation of -the device or during both normal operation and off\-line testing. The +the device or during both normal operation and off-line testing. The Attribute value table produced by the \'\-A\' option indicates this in the UPDATED column. Attributes of the first type are labeled "Offline" and Attributes of the second type are labeled "Always". @@ -751,20 +785,20 @@ The \fBthird\fP category of testing (and the \fIonly\fP category for which the word \'testing\' is really an appropriate choice) is "self" testing. This third type of test is only performed (immediately) when a command to run it is issued. The \'\-t\' and \'\-X\' options can be -used to carry out and abort such self\-tests; please see below for +used to carry out and abort such self-tests; please see below for further details. Any errors detected in the self testing will be shown in the -SMART self\-test log, which can be examined using the \'\-l selftest\' +SMART self-test log, which can be examined using the \'\-l selftest\' option. \fBNote:\fP in this manual page, the word \fB"Test"\fP is used in connection with the second category just described, e.g. for the -"offline" testing. The words \fB"Self\-test"\fP are used in +"offline" testing. The words \fB"Self-test"\fP are used in connection with the third category. .TP .B \-S VALUE, \-\-saveauto=VALUE -[ATA] Enables or disables SMART autosave of device vendor\-specific +[ATA] Enables or disables SMART autosave of device vendor-specific Attributes. The valid arguments to this option are \fIon\fP and \fIoff\fP. Note that this feature is preserved across disk power cycles, so you should only need to issue it once. @@ -776,16 +810,16 @@ a warning if autosave is disabled. [SCSI] For SCSI devices this toggles the value of the Global Logging Target Save Disabled (GLTSD) bit in the Control Mode Page. Some disk manufacturers set this bit by default. This prevents error counters, -power\-up hours and other useful data from being placed in non\-volatile +power-up hours and other useful data from being placed in non-volatile storage, so these values may be reset to zero the next time the device -is power\-cycled. If the GLTSD bit is set then \'smartctl \-a\' will +is power-cycled. If the GLTSD bit is set then \'smartctl \-a\' will issue a warning. Use \fIon\fP to clear the GLTSD bit and thus enable -saving counters to non\-volatile storage. For extreme streaming\-video +saving counters to non-volatile storage. For extreme streaming-video type applications you might consider using \fIoff\fP to set the GLTSD bit. .TP .B \-g NAME, \-\-get=NAME, \-s NAME[,VALUE], \-\-set=NAME[,VALUE] -[NEW EXPERIMENTAL SMARTCTL FEATURE] Gets/sets non\-SMART device settings. +Gets/sets non-SMART device settings. Note that the \'\-\-set\' option shares its short option \'\-s\' with \'\-\-smart\'. Valid arguments are: @@ -800,7 +834,7 @@ Note that the \'\-\-set\' option shares its short option \'\-s\' with (if supported). A value of 128 sets the most quiet (slowest) mode and 254 the fastest (loudest) mode, \'off\' disables AAM. Devices may support intermediate levels. Values below 128 are defined as vendor specific (0) -or retired (1\-127). Note that the AAM feature was declared obsolete in +or retired (1 to 127). Note that the AAM feature was declared obsolete in ATA ACS-2 Revision 4a (Dec 2010). .I apm[,N|off] @@ -842,9 +876,20 @@ because ATA standards do not specify a method to read the standby timer. the drive. The setting of the standby timer is not affected. .I wcache[,on|off] -\- [ATA only] Gets/sets the volatile write cache feature (if supported). +\- [ATA] Gets/sets the volatile write cache feature (if supported). The write cache is usually enabled by default. +.I wcache[,on|off] +\- [SCSI] [NEW EXPERIMENTAL SMARTCTL FEATURE] +Gets/sets the \'Write Cache Enable\' (WCE) bit (if supported). +The write cache is usually enabled by default. + +.I rcache[,on|off] +\- [SCSI only] [NEW EXPERIMENTAL SMARTCTL FEATURE] +Gets/sets the \'Read Cache Disable\' (RCE) bit. \'Off\' value disables read cache +(if supported). +The read cache is usually enabled by default. + .TP .B SMART READ AND DISPLAY DATA OPTIONS: .TP @@ -853,7 +898,7 @@ Check: Ask the device to report its SMART health status or pending TapeAlert messages. SMART status is based on information that it has gathered from online and offline tests, which were used to determine/update its -SMART vendor\-specific Attribute values. TapeAlert status is obtained +SMART vendor-specific Attribute values. TapeAlert status is obtained by reading the TapeAlert log page. If the device reports failing health status, this means @@ -869,10 +914,10 @@ this happens, use the \'\-a\' option to get more information, and show what SMART features are implemented and how the device will respond to some of the different SMART commands. For example it shows if the device logs errors, if it supports offline surface -scanning, and so on. If the device can carry out self\-tests, this +scanning, and so on. If the device can carry out self-tests, this option also shows the estimated time required to run those tests. -Note that the time required to run the Self\-tests (listed in minutes) +Note that the time required to run the Self-tests (listed in minutes) are fixed. However the time required to run the Immediate Offline Test (listed in seconds) is variable. This means that if you issue a command to perform an Immediate Offline test with the \'\-t offline\' option, @@ -889,9 +934,9 @@ disk been powered up. Each Attribute has a "Raw" value, printed under the heading "RAW_VALUE", and a "Normalized" value printed under the heading -"VALUE". [Note: \fBsmartctl\fP prints these values in base\-10.] In +"VALUE". [Note: \fBsmartctl\fP prints these values in base-10.] In the example just given, the "Raw Value" for Attribute 12 would be the -actual number of times that the disk has been power\-cycled, for +actual number of times that the disk has been power-cycled, for example 365 if the disk has been turned on once per day for exactly one year. Each vendor uses their own algorithm to convert this "Raw" value to a "Normalized" value in the range from 1 to 254. Please keep @@ -905,30 +950,30 @@ not specified by the SMART standard. In most cases, the values printed by \fBsmartctl\fP are sensible. For example the temperature Attribute generally has its raw value equal to the temperature in Celsius. However in some cases vendors use unusual conventions. For example -the Hitachi disk on my laptop reports its power\-on hours in minutes, +the Hitachi disk on my laptop reports its power-on hours in minutes, not hours. Some IBM disks track three temperatures rather than one, in their raw values. And so on. Each Attribute also has a Threshold value (whose range is 0 to 255) which is printed under the heading "THRESH". If the Normalized value is \fBless than or equal to\fP the Threshold value, then the Attribute -is said to have failed. If the Attribute is a pre\-failure Attribute, +is said to have failed. If the Attribute is a pre-failure Attribute, then disk failure is imminent. Each Attribute also has a "Worst" value shown under the heading "WORST". This is the smallest (closest to failure) value that the disk has recorded at any time during its lifetime when SMART was enabled. [Note however that some vendors firmware may actually -\fBincrease\fP the "Worst" value for some "rate\-type" Attributes.] +\fBincrease\fP the "Worst" value for some "rate-type" Attributes.] The Attribute table printed out by \fBsmartctl\fP also shows the "TYPE" of the Attribute. Attributes are one of two possible types: -Pre\-failure or Old age. Pre\-failure Attributes are ones which, if +Pre-failure or Old age. Pre-failure Attributes are ones which, if less than or equal to their threshold values, indicate pending disk failure. Old age, or usage Attributes, are ones which indicate -end\-of\-product life from old\-age or normal aging and wearout, if +end-of-product life from old-age or normal aging and wearout, if the Attribute value is less than or equal to the threshold. \fBPlease -note\fP: the fact that an Attribute is of type 'Pre\-fail' does +note\fP: the fact that an Attribute is of type 'Pre-fail' does \fBnot\fP mean that your disk is about to fail! It only has this meaning if the Attribute\'s current Normalized value is less than or equal to the threshold value. @@ -942,13 +987,13 @@ a dash: \'\-\') then this Attribute is OK now (not failing) and has also never failed in the past. The table column labeled "UPDATED" shows if the SMART Attribute values -are updated during both normal operation and off\-line testing, or +are updated during both normal operation and off-line testing, or only during offline testing. The former are labeled "Always" and the latter are labeled "Offline". So to summarize: the Raw Attribute values are the ones that might have a real physical interpretation, such as "Temperature Celsius", -"Hours", or "Start\-Stop Cycles". Each manufacturer converts these, +"Hours", or "Start-Stop Cycles". Each manufacturer converts these, using their detailed knowledge of the disk\'s operations and failure modes, to Normalized Attribute values in the range 1\-254. The current and worst (lowest measured) of these Normalized Attribute @@ -959,13 +1004,17 @@ fail, or that it has exceeded its design age or aging limit. thresholds, or types, it merely reports them from the SMART data on the device. -Note that starting with ATA/ATAPI\-4, revision 4, the meaning of these -Attribute fields has been made entirely vendor\-specific. However most -ATA/ATAPI\-5 disks seem to respect their meaning, so we have retained +Note that starting with ATA/ATAPI-4, revision 4, the meaning of these +Attribute fields has been made entirely vendor-specific. However most +newer ATA/SATA disks seem to respect their meaning, so we have retained the option of printing the Attribute values. +Solid-state drives use different meanings for some of the attributes. +In this case the attribute name printed by smartctl is incorrect unless +the drive is already in the smartmontools drive database. + [SCSI] For SCSI devices the "attributes" are obtained from the temperature -and start\-stop cycle counter log pages. Certain vendor specific +and start-stop cycle counter log pages. Certain vendor specific attributes are listed if recognised. The attributes are output in a relatively free format (compared with ATA disk attributes). .TP @@ -991,15 +1040,15 @@ This is the default if the '\-x\' option is specified. \- Same as \'\-f hex,id \-f hex,val\'. .TP .B \-l TYPE, \-\-log=TYPE -Prints either the SMART Error Log, the SMART Self\-Test Log, the SMART -Selective Self\-Test Log [ATA only], the Log Directory [ATA only], or +Prints either the SMART Error Log, the SMART Self-Test Log, the SMART +Selective Self-Test Log [ATA only], the Log Directory [ATA only], or the Background Scan Results Log [SCSI only]. The valid arguments to this option are: .I error \- [ATA] prints the Summary SMART error log. SMART disks maintain a log -of the most recent five non\-trivial errors. For each of these errors, the -disk power\-on lifetime at which the error occurred is recorded, as is +of the most recent five non-trivial errors. For each of these errors, the +disk power-on lifetime at which the error occurred is recorded, as is the device status (idle, standby, etc) at the time of the error. For some common types of errors, the Error Register (ER) and Status Register (SR) values are decoded and printed as text. The meanings of these @@ -1011,7 +1060,7 @@ are: \fBEOM\fP: \fBE\fPnd \fBO\fPf \fBM\fPedia \fBICRC\fP: \fBI\fPnterface \fBC\fPyclic \fBR\fPedundancy \fBC\fPode (CRC) error \fBIDNF\fP: \fBID\fPentity \fBN\fPot \fBF\fPound - \fBILI\fP: (packet command\-set specific) + \fBILI\fP: (packet command-set specific) \fBMC\fP: \fBM\fPedia \fBC\fPhanged \fBMCR\fP: \fBM\fPedia \fBC\fPhange \fBR\fPequest \fBNM\fP: \fBN\fPo \fBM\fPedia @@ -1027,18 +1076,18 @@ Dd+HH:MM:SS.msec where D is the number of days, HH is hours, MM is minutes, SS is seconds and msec is milliseconds. [Note: this time stamp wraps after 2^32 milliseconds, or 49 days 17 hours 2 minutes and 47.296 seconds.] The key ATA disk registers are also recorded in the -log. The final column of the error log is a text\-string description +log. The final column of the error log is a text-string description of the ATA command defined by the Command Register (CR) and Feature Register (FR) values. Commands that are obsolete in the most current -(ATA\-7) spec are listed like this: \fBREAD LONG (w/ retry) [OBS\-4]\fP, -indicating that the command became obsolete with or in the ATA\-4 +spec are listed like this: \fBREAD LONG (w/ retry) [OBS-4]\fP, +indicating that the command became obsolete with or in the ATA-4 specification. Similarly, the notation \fB[RET\-\fP\fIN\fP\fB]\fP is -used to indicate that a command was retired in the ATA\-\fIN\fP +used to indicate that a command was retired in the ATA-\fIN\fP specification. Some commands are not defined in any version of the ATA specification but are in common use nonetheless; these are marked -\fB[NS]\fP, meaning non\-standard. +\fB[NS]\fP, meaning non-standard. -The ATA Specification (ATA\-5 Revision 1c, Section 8.41.6.8.2) says: +The ATA Specification (ATA-5 Revision 1c, Section 8.41.6.8.2) says: \fB"Error log structures shall include UNC errors, IDNF errors for which the address requested was valid, servo errors, write fault errors, etc. Error log data structures shall not include errors @@ -1051,14 +1100,14 @@ to data which has been read from the disk, but for which the Error Checking and Correction (ECC) codes are inconsistent. In effect, this means that the data can not be read. .br -\fBIDNF\fP (\fBID N\fPot \fBF\fPound): user\-accessible address could +\fBIDNF\fP (\fBID N\fPot \fBF\fPound): user-accessible address could not be found. For READ LOG type commands, \fBIDNF\fP can also indicate that a device data log structure checksum was incorrect. If the command that caused the error was a READ or WRITE command, then the Logical Block Address (LBA) at which the error occurred will be printed in base 10 and base 16. The LBA is a linear address, which -counts 512\-byte sectors on the disk, starting from zero. (Because of +counts 512-byte sectors on the disk, starting from zero. (Because of the limitations of the SMART error log, if the LBA is greater than 0xfffffff, then either no error log entry will be made, or the error log entry will have an incorrect LBA. This may happen for drives with @@ -1083,7 +1132,7 @@ the contents of the 48-bit LBA register set introduced with ATA-6. It also supports logs with more than one sector. Each sector holds up to 4 log entries. The actual number of log sectors is vendor specific, typical values for HDD are 2 (Samsung), 5 (Seagate) or -6 (WD). Some recent SSD devices have much larger error logs. +6 (WD). Only the 8 most recent error log entries are printed by default. This number can be changed by the optional parameter NUM. @@ -1096,11 +1145,11 @@ Comprehensive SMART error log. The Summary SMART error log may be reported as supported but is always empty then. .I selftest -\- [ATA] prints the SMART self\-test log. The disk maintains a self\-test +\- [ATA] prints the SMART self-test log. The disk maintains a self-test log showing the results of the self tests, which can be run using the \'\-t\' option described below. For each of the most recent -twenty\-one self\-tests, the log shows the type of test (short or -extended, off\-line or captive) and the final status of the test. If +twenty-one self-tests, the log shows the type of test (short or +extended, off-line or captive) and the final status of the test. If the test did not complete successfully, then the percentage of the test remaining is shown. The time at which the test took place, measured in hours of disk lifetime, is also printed. [Note: this time @@ -1111,18 +1160,18 @@ smartmontools web page has instructions about how to convert this LBA address to the name of the disk file containing the erroneous block. .I selftest -\- [SCSI] the self\-test log for a SCSI device has a slightly different +\- [SCSI] the self-test log for a SCSI device has a slightly different format than for an ATA device. For each of the most recent twenty -self\-tests, it shows the type of test and the status (final or in +self-tests, it shows the type of test and the status (final or in progress) of the test. SCSI standards use the terms "foreground" and "background" (rather than ATA\'s corresponding "captive" and -"off\-line") and "short" and "long" (rather than ATA\'s corresponding +"off-line") and "short" and "long" (rather than ATA\'s corresponding "short" and "extended") to describe the type of the test. The printed segment number is only relevant when a test fails in the third or later test segment. It identifies the 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 +which the test was run, using a vendor-specific method of putting both numbers into a single byte. The Logical Block Address (LBA) of the first error is printed in hexadecimal notation. On Linux systems the smartmontools web page has instructions about how to convert this LBA @@ -1133,8 +1182,8 @@ can be run using the \'\-t\' option described below (using the ATA test terminology). .I xselftest[,NUM][,selftest] -\- [ATA only] prints the Extended SMART self\-test log (General Purpose -Log address 0x07). Unlike the SMART self\-test log (see \'\-l selftest\' +\- [ATA only] prints the Extended SMART self-test log (General Purpose +Log address 0x07). Unlike the SMART self-test log (see \'\-l selftest\' above), it supports 48-bit LBA and logs with more than one sector. Each sector holds up to 19 log entries. The actual number of log sectors is vendor specific, typical values are 1 (Seagate) or 2 (Samsung). @@ -1147,26 +1196,24 @@ supported, the old SMART self-test log is printed. .I selective \- [ATA only] Please see the \'\-t select\' option below for a -description of selective self\-tests. The selective self\-test log +description of selective self-tests. The selective self-test log shows the start/end Logical Block Addresses (LBA) of each of the five test spans, and their current test status. If the span is being -tested or the remainder of the disk is being read\-scanned, the -current 65536\-sector block of LBAs being tested is also displayed. -The selective self\-test log also shows if a read\-scan of the +tested or the remainder of the disk is being read-scanned, the +current 65536-sector block of LBAs being tested is also displayed. +The selective self-test log also shows if a read-scan of the remainder of the disk will be carried out after the selective -self\-test has completed (see \'\-t afterselect\' option) and the time -delay before restarting this read\-scan if it is interrupted (see -\'\-t pending\' option). This is a new smartmontools feature; please -report unusual or incorrect behavior to the smartmontools\-support -mailing list. +self-test has completed (see \'\-t afterselect\' option) and the time +delay before restarting this read-scan if it is interrupted (see +\'\-t pending\' option). .I directory[,gs] \- [ATA only] if the device supports the General Purpose Logging feature -set (ATA\-6 and above) then this prints the Log Directory (the log at +set (ATA-6 and above) then this prints the Log Directory (the log at address 0). The Log Directory shows what logs are available and their length in sectors (512 bytes). The contents of the logs at address 1 -[Summary SMART error log] and at address 6 [SMART self\-test log] may -be printed using the previously\-described +[Summary SMART error log] and at address 6 [SMART self-test log] may +be printed using the previously-described .I error and .I selftest @@ -1178,7 +1225,7 @@ SL directory by \'\-l directory,q\' or \'\-l directory,s\' respectively. .I background \- [SCSI only] the background scan results log outputs information derived -from Background Media Scans (BMS) done after power up and/or periodocally +from Background Media Scans (BMS) done after power up and/or periodically (e.g. every 24 hours) on recent SCSI disks. If supported, the BMS status is output first, indicating whether a background scan is currently underway (and if so a progress percentage), the amount of time the disk @@ -1186,7 +1233,7 @@ has been powered up and the number of scans already completed. Then there is a header and a line for each background scan "event". These will typically be either recovered or unrecoverable errors. That latter group may need some attention. There is a description of the background scan -mechansim in section 4.18 of SBC\-3 revision 6 (see www.t10.org ). +mechanism in section 4.18 of SBC-3 revision 6 (see www.t10.org ). .I scttemp, scttempsts, scttemphist \- [ATA only] prints the disk temperature information provided by the @@ -1198,8 +1245,8 @@ the SCT Data Table command, and \'scttemp\' prints both. The temperature values are preserved across power cycles. The logging interval can be configured with the \'\-l scttempint,N[,p]\' option, see below. -The SCT commands were introduced in ATA\-8 ACS and were also -supported by in many ATA\-7 disks. +The SCT commands were introduced in ATA8-ACS and were also +supported by many ATA-7 disks. .I scttempint,N[,p] \- [ATA only] clears the SCT temperature history table and sets the @@ -1219,19 +1266,20 @@ than 65 are probably not supported. For RAID configurations, this is typically set to 70,70 deciseconds. .I devstat[,PAGE] -\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values -and descriptions of the ATA Device Statistics log pages -(General Purpose Log address 0x04). If no PAGE number is specified, +\- [ATA only] prints values and descriptions of the ATA Device Statistics +log pages (General Purpose Log address 0x04). If no PAGE number is specified, entries from all supported pages are printed. If PAGE 0 is specified, the list of supported pages is printed. Device Statistics was -introduced in ATA\-8 ACS and is only supported by some recent devices -(e.g. Hitachi 7K3000, Intel 320, 330 and 710 Series SSDs, Crucial/Micron +introduced in ACS-2 and is only supported by some recent devices +(e.g. Hitachi 7K3000, Intel 320, 330, 520 and 710 Series SSDs, Crucial/Micron m4 SSDs). .I sataphy[,reset] \- [SATA only] prints values and descriptions of the SATA Phy Event Counters (General Purpose Log address 0x11). If \'\-l sataphy,reset\' is specified, all counters are reset after reading the values. +This also works for SATA devices with Packet interface like CD/DVD +drives. .I sasphy[,reset] \- [SAS (SCSI) only] prints values and descriptions of the SAS (SSP) @@ -1277,7 +1325,7 @@ indicates the device is at the end of its lifetime as projected by the manufacturer. The value may reach 255. .TP .B \-v ID,FORMAT[:BYTEORDER][,NAME], \-\-vendorattribute=ID,FORMAT[:BYTEORDER][,NAME] -[ATA only] Sets a vendor\-specific raw value print FORMAT, an optional +[ATA only] Sets a vendor-specific raw value print FORMAT, an optional BYTEORDER and an optional NAME for Attribute ID. This option may be used multiple times. @@ -1286,11 +1334,11 @@ ID, the settings for all Attributes are changed. The optional BYTEORDER consists of 1 to 8 characters from the set \'012345rvwz\'. The characters \'0\' to \'5\' select the byte 0 -to 5 from the 48\-bit raw value, \'r\' selects the reserved byte of +to 5 from the 48-bit raw value, \'r\' selects the reserved byte of the attribute data block, \'v\' selects the normalized value, \'w\' selects the worst value and \'z\' inserts a zero byte. -The default BYTEORDER is \'543210\' for all 48\-bit formats, \'r543210\' -for the 54\-bit formats, and \'543210wv\' for the 64\-bit formats. +The default BYTEORDER is \'543210\' for all 48-bit formats, \'r543210\' +for the 54-bit formats, and \'543210wv\' for the 64-bit formats. For example, \'\-v 5,raw48:012345\' prints the raw value of attribute 5 with big endian instead of little endian byte ordering. @@ -1306,15 +1354,15 @@ then exits. Valid arguments for FORMAT are: .I raw8 -\- Print the Raw value as six 8\-bit unsigned base\-10 integers. +\- Print the Raw value as six 8-bit unsigned base-10 integers. This may be useful for decoding the meaning of the Raw value. .I raw16 -\- Print the Raw value as three 16\-bit unsigned base\-10 integers. +\- Print the Raw value as three 16-bit unsigned base-10 integers. This may be useful for decoding the meaning of the Raw value. .I raw48 -\- Print the Raw value as a 48\-bit unsigned base\-10 integer. +\- Print the Raw value as a 48-bit unsigned base-10 integer. This is the default for most attributes. .I hex48 @@ -1322,15 +1370,15 @@ This is the default for most attributes. This may be useful for decoding the meaning of the Raw value. .I raw56 -\- Print the Raw value as a 54\-bit unsigned base\-10 integer. -This includes the reserved byte which follows the 48\-bit raw value. +\- Print the Raw value as a 54-bit unsigned base-10 integer. +This includes the reserved byte which follows the 48-bit raw value. .I hex56 \- Print the Raw value as a 14 digit hexadecimal number. -This includes the reserved byte which follows the 48\-bit raw value. +This includes the reserved byte which follows the 48-bit raw value. .I raw64 -\- Print the Raw value as a 64\-bit unsigned base\-10 integer. +\- Print the Raw value as a 64-bit unsigned base-10 integer. This includes two bytes from the normalized and worst attribute value. This raw format is used by some SSD devices with Indilinx controller. @@ -1340,27 +1388,27 @@ This includes two bytes from the normalized and worst attribute value. This raw format is used by some SSD devices with Indilinx controller. .I min2hour -\- Raw Attribute is power\-on time in minutes. Its raw value +\- Raw Attribute is power-on time in minutes. Its raw value will be displayed in the form "Xh+Ym". Here X is hours, and Y is minutes in the range 0\-59 inclusive. Y is always printed with two digits, for example "06" or "31" or "00". .I sec2hour -\- Raw Attribute is power\-on time in seconds. Its raw value +\- Raw Attribute is power-on time in seconds. Its raw value will be displayed in the form "Xh+Ym+Zs". Here X is hours, Y is minutes in the range 0\-59 inclusive, and Z is seconds in the range 0\-59 inclusive. Y and Z are always printed with two digits, for example "06" or "31" or "00". .I halfmin2hour -\- Raw Attribute is power\-on time, measured in units of 30 +\- Raw Attribute is power-on time, measured in units of 30 seconds. This format is used by some Samsung disks. Its raw value will be displayed in the form "Xh+Ym". Here X is hours, and Y is minutes in the range 0\-59 inclusive. Y is always printed with two digits, for example "06" or "31" or "00". .I msec24hour32 -\- Raw Attribute is power\-on time measured in 32\-bit hours and 24\-bit +\- Raw Attribute is power-on time measured in 32-bit hours and 24-bit milliseconds since last hour update. It will be displayed in the form "Xh+Ym+Z.Ms". Here X is hours, Y is minutes, Z is seconds and M is milliseconds. @@ -1376,8 +1424,8 @@ specific. \- Raw Attribute is ten times the disk temperature in Celsius. .I raw16(raw16) -\- Print the raw attribute as a 16\-bit value and two optional -16\-bit values if these words are nonzero. This is the default +\- Print the raw attribute as a 16-bit value and two optional +16-bit values if these words are nonzero. This is the default for Attributes 5 and 196. .I raw16(avg16) @@ -1386,12 +1434,12 @@ and an optional "Average" 16-bit value if the word is nonzero. This is the default for Attribute 3. .I raw24(raw8) -\- Print the raw attribute as a 24\-bit value and three optional -8\-bit values if these bytes are nonzero. This is the default +\- Print the raw attribute as a 24-bit value and three optional +8-bit values if these bytes are nonzero. This is the default for Attribute 9. .I raw24/raw24 -\- Raw Attribute contains two 24\-bit values. The first is the +\- Raw Attribute contains two 24-bit values. The first is the number of load cycles. The second is the number of unload cycles. The difference between these two values is the number of times that the drive was unexpectedly powered off (also called an emergency @@ -1400,8 +1448,8 @@ emergency unload is equivalent to that created by one hundred normal unloads. .I raw24/raw32 -\- Raw attribute is an error rate which consists of a 24\-bit error -count and a 32\-bit total count. +\- Raw attribute is an error rate which consists of a 24-bit error +count and a 32-bit total count. The following old arguments to \'\-v\' are also still valid: @@ -1465,7 +1513,7 @@ is not reset if uncorrectable sectors are reallocated .I 220,temp \- same as: -.I 220,raw48,Temperature_Celsius. +.I 220,tempminmax,Temperature_Celsius. Note: a table of hard drive models, listing which Attribute corresponds to temperature, can be found at: @@ -1473,43 +1521,49 @@ corresponds to temperature, can be found at: .TP .B \-F TYPE, \-\-firmwarebug=TYPE [ATA only] Modifies the behavior of \fBsmartctl\fP to compensate for some -known and understood device firmware or driver bug. Except \'swapid\', -the arguments to this option are exclusive, so that only the final -option given is used. The valid values are: +known and understood device firmware or driver bug. This option may be used +multiple times. The valid arguments are: .I none \- Assume that the device firmware obeys the ATA specifications. This is the default, unless the device has presets for \'\-F\' in the -device database (see note below). +drive database. Using this option on the command line will over-ride any +preset values. + +.I nologdir +\- Suppresses read attempts of SMART or GP Log Directory. +Support for all standard logs is assumed without an actual check. +Some Intel SSDs may freeze if log address 0 is read. .I samsung \- In some Samsung disks (example: model SV4012H Firmware Version: -RM100\-08) some of the two\- and four\-byte quantities in the SMART data -structures are byte\-swapped (relative to the ATA specification). +RM100-08) some of the two- and four-byte quantities in the SMART data +structures are byte-swapped (relative to the ATA specification). Enabling this option tells \fBsmartctl\fP to evaluate these quantities -in byte\-reversed order. Some signs that your disk needs this option -are (1) no self\-test log printed, even though you have run self\-tests; +in byte-reversed order. Some signs that your disk needs this option +are (1) no self-test log printed, even though you have run self-tests; (2) very large numbers of ATA errors reported in the ATA error log; (3) strange and impossible values for the ATA error log timestamps. .I samsung2 \- In some Samsung disks the number of ATA errors reported is byte swapped. Enabling this option tells \fBsmartctl\fP to evaluate this quantity in -byte\-reversed order. An indication that your Samsung disk needs this -option is that the self\-test log is printed correctly, but there are a +byte-reversed order. An indication that your Samsung disk needs this +option is that the self-test log is printed correctly, but there are a very large number of errors in the SMART error log. This is because the error count is byte swapped. Thus a disk with five errors (0x0005) will appear to have 20480 errors (0x5000). .I samsung3 -\- Some Samsung disks (at least SP2514N with Firmware VF100\-37) report -a self\-test still in progress with 0% remaining when the test was already -completed. Enabling this option modifies the output of the self\-test +\- Some Samsung disks (at least SP2514N with Firmware VF100-37) report +a self-test still in progress with 0% remaining when the test was already +completed. Enabling this option modifies the output of the self-test execution status (see options \'\-c\' or \'\-a\' above) accordingly. -Note that an explicit \'\-F\' option on the command line will -over\-ride any preset values for \'\-F\' (see the \'\-P\' option -below). +.I xerrorlba +\- Fixes LBA byte ordering in Extended Comprehensive SMART error log. +Some disk use little endian byte ordering instead of ATA register +ordering to specifiy the LBA addresses in the log entries. .I swapid \- Fixes byte swapped ATA identify strings (device name, serial number, @@ -1522,8 +1576,8 @@ in the \fBsmartmontools\fP database, then the presets are used. \fBsmartctl\fP can automatically set appropriate options for known drives. For example, the Maxtor 4D080H4 uses Attribute 9 to stores -power\-on time in minutes whereas most drives use that Attribute to -store the power\-on time in hours. The command\-line option \'\-v +power-on time in minutes whereas most drives use that Attribute to +store the power-on time in hours. The command-line option \'\-v 9,minutes\' ensures that \fBsmartctl\fP correctly interprets Attribute 9 in this case, but that option is preset for the Maxtor 4D080H4 and so need not be specified by the user on the \fBsmartctl\fP command @@ -1545,9 +1599,9 @@ The valid arguments to this option are: .I use \- if a drive is recognized, then use the stored presets for it. This -is the default. Note that presets will NOT over\-ride additional -Attribute interpretation (\'\-v N,something\') command\-line options or -explicit \'\-F\' command\-line options.. +is the default. Note that presets will NOT override additional +Attribute interpretation (\'\-v N,something\') command-line options or +explicit \'\-F\' command-line options.. .I ignore \- do not use presets. @@ -1652,19 +1706,28 @@ Example: .fi .TP -.B SMART RUN/ABORT OFFLINE TEST AND SELF\-TEST OPTIONS: +.B SMART RUN/ABORT OFFLINE TEST AND self-test OPTIONS: .TP .B \-t TEST, \-\-test=TEST Executes TEST immediately. The \'\-C\' option can be used in conjunction with this option to run the short or long (and also for -ATA devices, selective or conveyance) self\-tests in captive mode +ATA devices, selective or conveyance) self-tests in captive mode (known as "foreground mode" for SCSI devices). Note that only one test type can be run at a time, so only one test type should be specified per command line. Note also that if a computer is shutdown -or power cycled during a self\-test, no harm should result. The -self\-test will either be aborted or will resume automatically. +or power cycled during a self-test, no harm should result. The +self-test will either be aborted or will resume automatically. + +All \'\-t TEST\' commands can be given during normal system operation +unless captive mode (\'\-C\' option) is used. +A running self-test can, however, degrade performance of the drive. +Frequent I/O requests from the operating system increase the duration +of a test. These impacts may vary from device to device. -The valid arguments to this option are: +If a test failure occurs then the device may discontinue the testing +and report the result immediately. + +The valid arguments to this option are: .I offline \- [ATA] runs SMART Immediate Offline Test. This immediately @@ -1696,11 +1759,11 @@ tests. The "Self" tests check the electrical and mechanical performance as well as the read performance of the disk. Their results are reported in the Self Test Error Log, readable with the \'\-l selftest\' option. Note that on some disks the progress of the -self\-test can be monitored by watching this log during the self\-test; with other disks +self-test can be monitored by watching this log during the self-test; with other disks use the \'\-c\' option to monitor progress. .I short -\- [SCSI] runs the "Background short" self\-test. +\- [SCSI] runs the "Background short" self-test. .I long \- [ATA] runs SMART Extended Self Test (tens of minutes). This is a @@ -1709,12 +1772,12 @@ above. Note that this command can be given during normal system operation (unless run in captive mode \- see the \'\-C\' option below). .I long -\- [SCSI] runs the "Background long" self\-test. +\- [SCSI] runs the "Background long" self-test. .I conveyance \- [ATA only] runs a SMART Conveyance Self Test (minutes). This -self\-test routine is intended to identify damage incurred during -transporting of the device. This self\-test routine should take on the +self-test routine is intended to identify damage incurred during +transporting of the device. This self-test routine should take on the order of minutes to complete. Note that this command can be given during normal system operation (unless run in captive mode \- see the \'\-C\' option below). @@ -1749,21 +1812,21 @@ overlap partially or completely, for example: .nf smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/hda .fi -The results of the selective self\-test can be obtained (both during -and after the test) by printing the SMART self\-test log, using the +The results of the selective self-test can be obtained (both during +and after the test) by printing the SMART self-test log, using the \'\-l selftest\' option to smartctl. Selective self tests are particularly useful as disk capacities increase: an extended self test (smartctl \-t long) can take several -hours. Selective self\-tests are helpful if (based on SYSLOG error -messages, previous failed self\-tests, or SMART error log entries) you +hours. Selective self-tests are helpful if (based on SYSLOG error +messages, previous failed self-tests, or SMART error log entries) you suspect that a disk is having problems at a particular range of Logical Block Addresses (LBAs). -Selective self\-tests can be run during normal system operation (unless +Selective self-tests can be run during normal system operation (unless done in captive mode \- see the \'\-C\' option below). -The following variants of the selective self\-test command use spans based +The following variants of the selective self-test command use spans based on the ranges from past tests already stored on the disk: .I select,redo[+SIZE] @@ -1815,30 +1878,30 @@ that the last test was aborted by the host. Otherwise it run the \'next\' (above) test. .I afterselect,on -\- [ATA only] perform an offline read scan after a Selective Self\-test +\- [ATA only] perform an offline read scan after a Selective self-test has completed. This option must be used together with one or more of the \fIselect,N\-M\fP options above. If the LBAs that have been -specified in the Selective self\-test pass the test with no errors +specified in the Selective self-test pass the test with no errors found, then read scan the \fBremainder\fP of the disk. If the device -is powered\-cycled while this read scan is in progress, the read scan +is powered-cycled while this read scan is in progress, the read scan will be automatically resumed after a time specified by the pending timer (see below). The value of this option is preserved between -selective self\-tests. +selective self-tests. .I afterselect,off \- [ATA only] do not read scan the remainder of the disk after a -Selective self\-test has completed. This option must be use together +Selective self-test has completed. This option must be use together with one or more of the \fIselect,N\-M\fP options above. The value of this -option is preserved between selective self\-tests. +option is preserved between selective self-tests. .I pending,N \- [ATA only] set the pending offline read scan timer to N minutes. Here N is an integer in the range from 0 to 65535 inclusive. If the -device is powered off during a read scan after a Selective self\-test, -then resume the test automatically N minutes after power\-up. This +device is powered off during a read scan after a Selective self-test, +then resume the test automatically N minutes after power-up. This option must be use together with one or more of the \fIselect,N\-M\fP options above. The value of this option is preserved between selective -self\-tests. +self-tests. .I vendor,N \- [ATA only] issues the ATA command SMART EXECUTE OFF-LINE IMMEDIATE @@ -1852,32 +1915,29 @@ T13/1699-D Revision 6a (ATA8-ACS). Note that the subcommands \fBWARNING: Only run subcommands documented by the vendor of the device.\fP -Example for Intel (X18/X25\-M G2, 320, 520 and 710 Series) SSDs only: +Example for Intel (X18/X25-M G2, 320, 520 and 710 Series) SSDs only: The subcommand 0x40 (\'\-t vendor,0x40\') clears the timed workload related SMART attributes (226, 227, 228). Note that the raw values of these attributes are held at 65535 (0xffff) until the workload timer reaches 60 minutes. .I force -\- [ATA only] start new self\-test even if another test is already running. -By default a running self\-test will not be interrupted to begin another +\- start new self-test even if another test is already running. +By default a running self-test will not be interrupted to begin another test. - -.I scttempint,N[,p] -\- is no longer supported, use \'\-l scttempint,N[,p]\' instead, see above. .TP .B \-C, \-\-captive -[ATA] Runs self\-tests in captive mode. This has no effect with \'\-t +[ATA] Runs self-tests in captive mode. This has no effect with \'\-t offline\' or if the \'\-t\' option is not used. \fBWARNING: Tests run in captive mode may busy out the drive for the length of the test. Only run captive tests on drives without any mounted partitions!\fP -[SCSI] Runs the self\-test in "Foreground" mode. +[SCSI] Runs the self-test in "Foreground" mode. .TP .B \-X, \-\-abort -Aborts non\-captive SMART Self Tests. Note that this +Aborts non-captive SMART Self Tests. Note that this command will abort the Offline Immediate Test routine only if your disk has the "Abort Offline collection upon new command" capability. .PP @@ -1948,14 +2008,14 @@ Disable SMART monitoring and data log collection on drive /dev/hdd . .fi Enable SMART on drive /dev/hda, enable automatic offline testing every four hours, and enable autosaving of -SMART Attributes. This is a good start\-up line for your system\'s +SMART Attributes. This is a good start-up line for your system\'s init files. You can issue this command on a running system. .PP .nf .B smartctl \-t long /dev/hdc .fi -Begin an extended self\-test of drive /dev/hdc. You can issue this -command on a running system. The results can be seen in the self\-test +Begin an extended self-test of drive /dev/hdc. You can issue this +command on a running system. The results can be seen in the self-test log visible with the \'\-l selftest\' option after it has completed. .PP .nf @@ -1970,14 +2030,14 @@ the SMART error log, which can be seen with the \'\-l error\' option. .nf .B smartctl \-A \-v 9,minutes /dev/hda .fi -Shows the vendor Attributes, when the disk stores its power\-on time +Shows the vendor Attributes, when the disk stores its power-on time internally in minutes rather than hours. .PP .nf .B smartctl \-q errorsonly \-H \-l selftest /dev/hda .fi Produces output only if the device returns failing SMART status, -or if some of the logged self\-tests ended with errors. +or if some of the logged self-tests ended with errors. .PP .nf .B smartctl \-q silent \-a /dev/hda @@ -1987,7 +2047,7 @@ printed output. You must use the exit status (the .B $? shell variable) to learn if any Attributes are out of bound, if the SMART status is failing, if there are errors recorded in the -self\-test log, or if there are errors recorded in the disk error log. +self-test log, or if there are errors recorded in the disk error log. .PP .nf .B smartctl \-a \-d 3ware,0 /dev/sda @@ -2016,13 +2076,13 @@ Examine all SMART data for the first SATA (not SAS) disk connected to a .nf .B smartctl \-t short \-d 3ware,3 /dev/sdb .fi -Start a short self\-test on the fourth ATA disk connected to the 3ware RAID +Start a short self-test on the fourth ATA disk connected to the 3ware RAID controller card which is the second SCSI device /dev/sdb. .PP .nf .B smartctl \-t long \-d areca,4 /dev/sg2 .fi -Start a long self\-test on the fourth SATA disk connected to an Areca RAID +Start a long self-test on the fourth SATA disk connected to an Areca RAID controller addressed by /dev/sg2. .PP .nf @@ -2037,15 +2097,15 @@ first HighPoint RocketRAID controller card. .B smartctl \-t short \-d hpt,1/1/2 /dev/sda (under Linux) .B smartctl \-t short \-d hpt,1/1/2 /dev/hptrr (under FreeBSD) .fi -Start a short self\-test on the (S)ATA disk connected to second pmport on the +Start a short self-test on the (S)ATA disk connected to second pmport on the first channel of the first HighPoint RocketRAID controller card. .PP .nf .B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/hda .fi -Run a selective self\-test on LBAs 10 to 100 and 30 to 300. After the -these LBAs have been tested, read\-scan the remainder of the disk. If the disk is -power\-cycled during the read\-scan, resume the scan 45 minutes after power to the +Run a selective self-test on LBAs 10 to 100 and 30 to 300. After the +these LBAs have been tested, read-scan the remainder of the disk. If the disk is +power-cycled during the read-scan, resume the scan 45 minutes after power to the device is restored. .PP .nf @@ -2058,7 +2118,7 @@ RAID controller card. The return values of \fBsmartctl\fP are defined by a bitmask. If all is well with the disk, the return value (exit status) of \fBsmartctl\fP is 0 (all bits turned off). If a problem occurs, or an -error, potential error, or fault is detected, then a non\-zero status +error, potential error, or fault is detected, then a non-zero status is returned. In this case, the eight different bits in the return value have the following meanings for ATA disks; some of these values may also be returned for SCSI disks. @@ -2089,9 +2149,9 @@ past. The device error log contains records of errors. .TP .B Bit 7: -The device self\-test log contains records of errors. +The device self-test log contains records of errors. [ATA only] Failed self-tests outdated by a newer successful extended -self\-test are ignored. +self-test are ignored. .PP To test within the shell for whether or not the different bits are turned on or off, you can use the following type of construction (this @@ -2121,17 +2181,21 @@ once by \fBsmartctl\fP for each initiator for each activation of the condition. .PP -.SH AUTHOR -\fBBruce Allen\fP smartmontools\-support@lists.sourceforge.net -.fi +.SH AUTHORS +\fBBruce Allen\fP +.br University of Wisconsin \- Milwaukee Physics Department +.br +\fBChristian Franke\fP (Windows interface, C++ redesign, most enhancements +since 2009) +.br +\fBsmartmontools\-support@lists.sourceforge.net\fP .PP .SH CONTRIBUTORS The following have made large contributions to smartmontools: .nf \fBCasper Dik\fP (Solaris SCSI interface) -\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...) \fBDouglas Gilbert\fP (SCSI subsystem) \fBGuido Guenther\fP (Autoconf/Automake packaging) \fBGeoffrey Keating\fP (Darwin ATA interface) @@ -2153,7 +2217,7 @@ Many other individuals have made smaller contributions and corrections. .fi This code was derived from the smartsuite package, written by Michael Cornwell, and from the previous UCSC smartsuite package. It extends -these to cover ATA\-5 disks. This code was originally developed as a +these to cover ATA-5 disks. 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 @@ -2172,18 +2236,18 @@ REFERENCES FOR SMART .fi An introductory article about smartmontools is \fIMonitoring Hard Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004, -pages 74\-77. This is \fBhttp://www.linuxjournal.com/article/6983\fP +pages 74-77. This is \fBhttp://www.linuxjournal.com/article/6983\fP online. If you would like to understand better how SMART works, and what it does, a good place to start is with Sections 4.8 and 6.54 of the first -volume of the \'AT Attachment with Packet Interface\-7\' (ATA/ATAPI\-7) +volume of the \'AT Attachment with Packet Interface-7\' (ATA/ATAPI-7) specification Revision 4b. This documents the SMART functionality which the \fBsmartmontools\fP utilities provide access to. .fi -The functioning of SMART was originally defined by the SFF\-8035i -revision 2 and the SFF\-8055i revision 1.4 specifications. These are +The functioning of SMART was originally defined by the SFF-8035i +revision 2 and the SFF-8055i revision 1.4 specifications. These are publications of the Small Form Factors (SFF) Committee. Links to these and other documents may be found on the Links page of the @@ -2192,4 +2256,4 @@ Links to these and other documents may be found on the Links page of the .SH SVN ID OF THIS PAGE: -$Id: smartctl.8.in 3561 2012-06-05 19:49:31Z chrfranke $ +$Id: smartctl.8.in 3799 2013-03-15 17:47:25Z chrfranke $ diff --git a/smartctl.cpp b/smartctl.cpp index 9b414e0..190f9f3 100644 --- a/smartctl.cpp +++ b/smartctl.cpp @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -41,10 +40,6 @@ #include #endif -#if defined(__QNXNTO__) -#include // TODO: Why is this include necessary on QNX ? -#endif - #include "int64.h" #include "atacmds.h" #include "dev_interface.h" @@ -55,7 +50,7 @@ #include "smartctl.h" #include "utility.h" -const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3531 2012-03-27 20:02:25Z chrfranke $" +const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3726 2012-12-12 20:02:48Z chrfranke $" CONFIG_H_CVSID SMARTCTL_H_CVSID; // Globals to control printing @@ -87,8 +82,10 @@ static void Usage() " Print license, copyright, and version information and exit\n\n" " -i, --info\n" " Show identity information for device\n\n" +" --identify[=[w][nvb]]\n" +" Show words and bits from IDENTIFY DEVICE data (ATA)\n\n" " -g NAME, --get=NAME\n" -" Get device setting: all, aam, apm, lookahead, security, wcache\n\n" +" Get device setting: all, aam, apm, lookahead, security, wcache, rcache\n\n" " -a, --all\n" " Show all SMART information for device\n\n" " -x, --xall\n" @@ -124,7 +121,7 @@ static void Usage() " -s NAME[,VALUE], --set=NAME[,VALUE]\n" " Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n" " lookahead,[on|off], security-freeze, standby,[N|off|now],\n" -" wcache,[on|off]\n\n" +" wcache,[on|off], rcache,[on|off]\n\n" ); printf( "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n" @@ -146,13 +143,14 @@ static void Usage() " -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" -" samsung3, swapid\n\n" +" Use firmware bug workaround:\n" +" %s, swapid\n\n" " -P TYPE, --presets=TYPE (ATA)\n" " Drive-specific presets: use, ignore, show, showall\n\n" " -B [+]FILE, --drivedb=[+]FILE (ATA)\n" " Read and replace [add] drive database from FILE\n" " [default is +%s", + get_valid_firmwarebug_args(), get_drivedb_path_add() ); #ifdef SMARTMONTOOLS_DRIVEDBDIR @@ -179,7 +177,7 @@ static void Usage() } // Values for --long only options, see parse_options() -enum { opt_scan = 1000, opt_scan_open, opt_set, opt_smart }; +enum { opt_identify = 1000, opt_scan, opt_scan_open, opt_set, opt_smart }; /* Returns a string containing a formatted list of the valid arguments to the option opt or empty on failure. Note 'v' case different */ @@ -214,18 +212,20 @@ static std::string getvalidarglist(int opt) return "offline, short, long, conveyance, force, vendor,N, select,M-N, " "pending,N, afterselect,[on|off]"; case 'F': - return "none, samsung, samsung2, samsung3, swapid"; + return std::string(get_valid_firmwarebug_args()) + ", swapid"; case 'n': return "never, sleep, standby, idle"; case 'f': return "old, brief, hex[,id|val]"; case 'g': - return "aam, apm, lookahead, security, wcache"; + return "aam, apm, lookahead, security, wcache, rcache"; case opt_set: return "aam,[N|off], apm,[N|off], lookahead,[on|off], security-freeze, " - "standby,[N|off|now], wcache,[on|off]"; + "standby,[N|off|now], wcache,[on|off], rcache,[on|off]"; case 's': return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set); + case opt_identify: + return "n, wn, w, v, wv, wb"; case 'v': default: return ""; @@ -300,6 +300,7 @@ static const char * parse_options(int argc, char** argv, { "drivedb", required_argument, 0, 'B' }, { "format", required_argument, 0, 'f' }, { "get", required_argument, 0, 'g' }, + { "identify", optional_argument, 0, opt_identify }, { "set", required_argument, 0, opt_set }, { "scan", no_argument, 0, opt_scan }, { "scan-open", no_argument, 0, opt_scan_open }, @@ -439,19 +440,10 @@ static const char * parse_options(int argc, char** argv, scsiopts.smart_ss_media_log = true; break; case 'F': - if (!strcmp(optarg,"none")) { - ataopts.fix_firmwarebug = FIX_NONE; - } else if (!strcmp(optarg,"samsung")) { - ataopts.fix_firmwarebug = FIX_SAMSUNG; - } else if (!strcmp(optarg,"samsung2")) { - ataopts.fix_firmwarebug = FIX_SAMSUNG2; - } else if (!strcmp(optarg,"samsung3")) { - ataopts.fix_firmwarebug = FIX_SAMSUNG3; - } else if (!strcmp(optarg,"swapid")) { + if (!strcmp(optarg, "swapid")) ataopts.fix_swapped_id = true; - } else { + else if (!parse_firmwarebug_def(optarg, ataopts.firmwarebugs)) badarg = true; - } break; case 'c': ataopts.smart_general_values = true; @@ -498,7 +490,7 @@ static const char * parse_options(int argc, char** argv, unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg); if (!( sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1 && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) { - strcpy(extraerror, "Option -l scttempint,N[,p] must have positive integer N\n"); + snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n"); badarg = true; } ataopts.sct_temp_int = interval; @@ -554,7 +546,7 @@ static const char * parse_options(int argc, char** argv, ataopts.sct_erc_writetime = wt; } else { - sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); + snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); badarg = true; } } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) @@ -570,14 +562,14 @@ static const char * parse_options(int argc, char** argv, const char * erropt = (gpl ? "gplog" : "smartlog"); if (!( n1 == len || n2 == len || (n3 == len && (sign == '+' || sign == '-')))) { - sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt); + snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt); badarg = true; } else if (!( logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU) && 0 < nsectors && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U) && (sign != '-' || page <= nsectors) )) { - sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt); + snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt); badarg = true; } else { @@ -593,6 +585,22 @@ static const char * parse_options(int argc, char** argv, case 'i': ataopts.drive_info = scsiopts.drive_info = true; break; + + case opt_identify: + ataopts.identify_word_level = ataopts.identify_bit_level = 0; + if (optarg) { + for (int i = 0; optarg[i]; i++) { + switch (optarg[i]) { + case 'w': ataopts.identify_word_level = 1; break; + case 'n': ataopts.identify_bit_level = -1; break; + case 'v': ataopts.identify_bit_level = 1; break; + case 'b': ataopts.identify_bit_level = 2; break; + default: badarg = true; + } + } + } + break; + case 'a': ataopts.drive_info = scsiopts.drive_info = true; ataopts.smart_check_status = scsiopts.smart_check_status = true; @@ -618,11 +626,13 @@ static const char * parse_options(int argc, char** argv, ataopts.smart_logdir = ataopts.gp_logdir = true; ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; ataopts.sct_erc_get = true; + ataopts.devstat_all_pages = true; ataopts.sataphy = true; ataopts.get_set_used = true; ataopts.get_aam = ataopts.get_apm = true; ataopts.get_security = true; ataopts.get_lookahead = ataopts.get_wcache = true; + scsiopts.get_rcd = scsiopts.get_wce = true; scsiopts.smart_background_log = true; scsiopts.smart_ss_media_log = true; scsiopts.sasphy = true; @@ -680,6 +690,7 @@ static const char * parse_options(int argc, char** argv, ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST; } else if (!strcmp(optarg,"force")) { ataopts.smart_selftest_force = true; + scsiopts.smart_selftest_force = true; } else if (!strcmp(optarg,"afterselect,on")) { // scan remainder of disk after doing selected segment ataopts.smart_selective_args.scan_after_select = 2; @@ -693,10 +704,10 @@ static const char * parse_options(int argc, char** argv, 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"); + snprintf(extraerror, sizeof(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); + snprintf(extraerror, sizeof(extraerror), "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); badarg = true; } else { ataopts.smart_selective_args.pending_time = i+1; @@ -707,15 +718,15 @@ static const char * parse_options(int argc, char** argv, // parse range of LBAs to test uint64_t start, stop; int mode; if (split_selective_arg(optarg, &start, &stop, &mode)) { - sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n"); + snprintf(extraerror, sizeof(extraerror), "Option -t select,M-N must have non-negative integer M and N\n"); badarg = true; } else { if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) { if (start > stop) { - sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n", + snprintf(extraerror, sizeof(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" + snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be" " defined\n"); } badarg = true; @@ -727,13 +738,13 @@ static const char * parse_options(int argc, char** argv, ataopts.smart_selftest_type = SELECTIVE_SELF_TEST; } } else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) { - strcpy(extraerror, "-t scttempint is no longer supported, use -l scttempint instead\n"); + snprintf(extraerror, sizeof(extraerror), "-t scttempint is no longer supported, use -l scttempint instead\n"); badarg = true; } else if (!strncmp(optarg, "vendor,", sizeof("vendor,")-1)) { unsigned subcmd = ~0U; int n = -1; if (!( sscanf(optarg, "%*[a-z],0x%x%n", &subcmd, &n) == 1 && subcmd <= 0xff && n == (int)strlen(optarg))) { - strcpy(extraerror, "Option -t vendor,0xNN syntax error\n"); + snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n"); badarg = true; } else @@ -819,6 +830,7 @@ static const char * parse_options(int argc, char** argv, ataopts.get_aam = ataopts.get_apm = true; ataopts.get_security = true; ataopts.get_lookahead = ataopts.get_wcache = true; + scsiopts.get_rcd = scsiopts.get_wce = true; } else if (!strcmp(name, "aam")) { if (get) @@ -828,7 +840,7 @@ static const char * parse_options(int argc, char** argv, else if (val <= 254) ataopts.set_aam = val + 1; else { - sprintf(extraerror, "Option -s aam,N must have 0 <= N <= 254\n"); + snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n"); badarg = true; } } @@ -840,13 +852,14 @@ static const char * parse_options(int argc, char** argv, else if (1 <= val && val <= 254) ataopts.set_apm = val + 1; else { - sprintf(extraerror, "Option -s apm,N must have 1 <= N <= 254\n"); + snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n"); badarg = true; } } else if (!strcmp(name, "lookahead")) { - if (get) + if (get) { ataopts.get_lookahead = true; + } else if (off) ataopts.set_lookahead = -1; else if (on) @@ -854,6 +867,16 @@ static const char * parse_options(int argc, char** argv, else badarg = true; } + else if (!strcmp(name, "rcache")) { + if (get) + scsiopts.get_rcd = true; + else if (off) + scsiopts.set_rcd = -1; + else if (on) + scsiopts.set_rcd = 1; + else + badarg = true; + } else if (get && !strcmp(name, "security")) { ataopts.get_security = true; } @@ -869,17 +892,23 @@ static const char * parse_options(int argc, char** argv, else if (val <= 255) ataopts.set_standby = val + 1; else { - sprintf(extraerror, "Option -s standby,N must have 0 <= N <= 255\n"); + snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n"); badarg = true; } } else if (!strcmp(name, "wcache")) { - if (get) + if (get) { ataopts.get_wcache = true; - else if (off) + scsiopts.get_wce = true; + } + else if (off) { ataopts.set_wcache = -1; - else if (on) + scsiopts.set_wce = -1; + } + else if (on) { ataopts.set_wcache = 1; + scsiopts.set_wce = 1; + } else badarg = true; } @@ -941,7 +970,8 @@ static const char * parse_options(int argc, char** argv, // a clean way to do it. char optstr[] = { (char)optchar, 0 }; pout("=======> INVALID ARGUMENT TO -%s: %s\n", - (optchar == opt_set ? "-set" : + (optchar == opt_identify ? "-identify" : + optchar == opt_set ? "-set" : optchar == opt_smart ? "-smart" : optstr), optarg); printvalidarglistmessage(optchar); if (extraerror[0]) @@ -1190,7 +1220,7 @@ static int main_worker(int argc, char **argv) if (!strcmp(name,"-")) { // Parse "smartctl -r ataioctl,2 ..." output from stdin if (type || print_type_only) { - pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n"); + pout("-d option is not allowed in conjunction with device name \"-\".\n"); UsageSummary(); return FAILCMD; } @@ -1205,7 +1235,7 @@ static int main_worker(int argc, char **argv) if (type) printvalidarglistmessage('d'); else - pout("Smartctl: please specify device type with the -d option.\n"); + pout("Please specify device type with the -d option.\n"); UsageSummary(); return FAILCMD; } diff --git a/smartctl.h b/smartctl.h index 3ec3e08..8820287 100644 --- a/smartctl.h +++ b/smartctl.h @@ -13,8 +13,8 @@ * 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. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -26,7 +26,7 @@ #ifndef SMARTCTL_H_ #define SMARTCTL_H_ -#define SMARTCTL_H_CVSID "$Id: smartctl.h 3196 2010-10-28 21:31:49Z chrfranke $\n" +#define SMARTCTL_H_CVSID "$Id: smartctl.h 3727 2012-12-13 17:23:06Z samm2 $\n" // Return codes (bitmask) diff --git a/smartd.8.in b/smartd.8.in index d234974..f90f3b6 100644 --- a/smartd.8.in +++ b/smartd.8.in @@ -1,21 +1,22 @@ .ig Copyright (C) 2002-10 Bruce Allen - -$Id: smartd.8.in 3561 2012-06-05 19:49:31Z chrfranke $ +Copyright (C) 2004-13 Christian Franke + +$Id: smartd.8.in 3799 2013-03-15 17:47:25Z 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 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/ + +You should have received a copy of the GNU General Public License +(for example COPYING); If not, see . + +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/ + .. .TH SMARTD 8 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE .SH NAME @@ -38,13 +39,15 @@ CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL -\fBsmartd\fP is a daemon that monitors the Self-Monitoring, Analysis -and Reporting Technology (SMART) system built into many ATA-3 and -later ATA, IDE and SCSI-3 hard drives. The purpose of SMART is to -monitor the reliability of the hard drive and predict drive failures, -and to carry out different types of drive self-tests. This version of -\fBsmartd\fP is compatible with ATA/ATAPI-7 and earlier standards (see -\fBREFERENCES\fP below). +\fBsmartd\fP is a daemon that monitors the Self-Monitoring, Analysis and +Reporting Technology (SMART) system built into most ATA/SATA and SCSI/SAS +hard drives and solid-state drives. +The purpose of SMART is to monitor the reliability of the hard drive +and predict drive failures, and to carry out different types of drive +self-tests. +This version of \fBsmartd\fP is compatible with +ACS-2, ATA8-ACS, ATA/ATAPI-7 and earlier standards +(see \fBREFERENCES\fP below). \fBsmartd\fP will attempt to enable SMART monitoring on ATA devices (equivalent to \fBsmartctl -s on\fP) and polls these and SCSI devices @@ -57,7 +60,7 @@ command-line option described below. In addition to logging to a file, \fBsmartd\fP can also be configured to send email warnings if problems are detected. Depending upon the -type of problem, you may want to run self\-tests on the disk, back up +type of problem, you may want to run self-tests on the disk, back up the disk, replace the disk, or use a manufacturer\'s utility to force reallocation of bad or unreadable disk sectors. If disk problems are detected, please see the \fBsmartctl\fP manual page and the @@ -89,11 +92,11 @@ the contents of the (faulty) configuration file, as if the \fBHUP\fP signal had never been received. When \fBsmartd\fP is running in debug mode, the \fBINT\fP signal -(normally generated from a shell with CONTROL\-C) is treated in the +(normally generated from a shell with CONTROL-C) is treated in the same way as a \fBHUP\fP signal: it makes \fBsmartd\fP reload its configuration file. To exit \fBsmartd\fP use CONTROL-\e .\" %IF OS Windows -(Windows: CONTROL\-Break). +(Windows: CONTROL-Break). .\" %ENDIF OS Windows On startup, in the absence of the configuration file @@ -104,10 +107,12 @@ devices that support SMART. The scanning is done as follows: Examine all entries \fB"/dev/hd[a-t]"\fP for IDE/ATA devices, and \fB"/dev/sd[a-z]"\fP, \fB"/dev/sd[a-c][a-z]"\fP for SCSI or SATA devices. +Disks behind RAID controllers are not included. .\" %ENDIF OS Linux .\" %IF OS FreeBSD .IP \fBFREEBSD:\fP 9 Authoritative list of disk devices is obtained from SCSI (CAM) and ATA subsystems. +Disks behind RAID controllers are not included. .\" %ENDIF OS FreeBSD .\" %IF OS NetBSD OpenBSD .IP \fBNETBSD/OPENBSD:\fP 9 @@ -123,17 +128,11 @@ devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices. .IP \fBDARWIN:\fP 9 The IOService plane is scanned for ATA block storage devices. .\" %ENDIF OS Darwin -.\" %IF OS Windows -.IP \fBWINDOWS\ 9x/ME\fP: 9 -Examine all entries \fB"/dev/hd[a-d]"\fP (bitmask -from "\\\\.\\SMARTVSD") for IDE/ATA devices. -Examine all entries \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices -on ASPI adapter 0\-9, ID 0\-15. -.\" %ENDIF OS Windows .\" %IF OS Windows Cygwin -.IP \fBWINDOWS\ NT4/2000/XP/2003/Vista/Win7/2008\fP: 9 -Examine all entries \fB"/dev/sd[a-j]"\fP ("\\\\.\\PhysicalDrive[0-9]") -for IDE/(S)ATA and SCSI disk devices +.IP \fBWINDOWS\fP: 9 +Examine all entries \fB"/dev/sd[a\-z]"\fP, \fB"/dev/sd[a\-c][a\-z]"\fP +and \fB"/dev/sdd[a\-x]"\fP ("\\\\.\\PhysicalDrive[0\-127]") for +IDE/(S)ATA and SCSI disk devices. If a 3ware 9000 controller is installed, examine all entries \fB"/dev/sdX,N"\fP for the first logical drive (\'unit\' @@ -143,30 +142,24 @@ detected behind this controller. Same for a second controller if present. If directive \'\-d csmi\' or no \'\-d\' directive is specified, examine all entries \fB"/dev/csmi[0\-9],N"\fP for drives behind an Intel ICHxR controller with RST driver. + +Disks behind Areca RAID controllers are not included. .\" %ENDIF OS Windows Cygwin -.\" %IF OS Cygwin -.IP \fBCYGWIN\fP: 9 -See "WINDOWS NT4/2000/XP/2003/Vista/Win7/2008" above. -.\" %ENDIF OS Cygwin -.\" %IF OS OS2 -.IP \fBOS/2,eComStation\fP: 9 -Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices. -.\" %ENDIF OS OS2 .PP \fBsmartd\fP then monitors for \fIall\fP possible SMART errors (corresponding to the \fB\'\-a\'\fP -Directive in the configuration file; see \fBCONFIGURATION FILE\fP -below). +Directive in the configuration file; see the \fBsmartd.conf\fP(5) man page). .SH OPTIONS .TP .B \-A PREFIX, \-\-attributelog=PREFIX -[ATA only] Writes \fBsmartd\fP attribute information (normalized and raw -attribute values) to files \'PREFIX\'\'MODEL\-SERIAL.ata.csv\'. At each +Writes \fBsmartd\fP attribute information (normalized and raw +attribute values) to files \'PREFIX\'\'MODEL\-SERIAL.ata.csv\' or \'PREFIX\'\'VENDOR\-MODEL\-SERIAL.scsi.csv\'. At each check cycle attributes are logged as a line of semicolon separated triplets of the form "attribute-ID;attribute-norm-value;attribute-raw-value;". +For SCSI devices error counters and temperature recorded in the form "counter-name;counter-value;" Each line is led by a date string of the form "yyyy-mm-dd HH:MM:SS" (in UTC). .\" %IF ENABLE_ATTRIBUTELOG @@ -218,10 +211,10 @@ information to STDOUT rather than logging it to SYSLOG and does not terminal. In this mode, \fBsmartd\fP also prints more verbose information about what it is doing than when operating in "daemon" mode. In this mode, the \fBINT\fP signal (normally generated from a -terminal with CONTROL\-C) makes \fBsmartd\fP reload its configuration +terminal with CONTROL-C) makes \fBsmartd\fP reload its configuration file. Please use CONTROL-\e to exit .\" %IF OS Windows -(Windows: CONTROL\-Break). +(Windows: CONTROL-Break). [Windows only] The "debug" mode can be toggled by the command \fBsmartd sigusr2\fP. A new console for debug output is opened when @@ -308,18 +301,12 @@ Windows: Some \fBsyslog\fP functionality is implemented internally in \fBsmartd\fP as follows: If no \'\-l\' option (or \'\-l daemon\') is specified, messages are written to Windows event log or to file \fB./smartd.log\fP if event log is not available -(Win9x/ME or access denied). By specifying other values of FACILITY, +(access denied). By specifying other values of FACILITY, log output is redirected as follows: \'\-l local0\' to file \fB./smartd.log\fP, \'\-l local1\' to standard output (redirect with \'>\' to any file), \'\-l local2\' to standard error, \'\-l local[3-7]\': to file \fB./smartd[1-5].log\fP. - -When using the event log, the enclosed utility \fBsyslogevt.exe\fP -should be registered as an event message file to avoid error -messages from the event viewer. Use \'\fBsyslogevt -r smartd\fP\' -to register, \'\fBsyslogevt -u smartd\fP\' to unregister and -\'\fBsyslogevt\fP\' for more help. .\" %ENDIF OS Windows .TP .B \-n, \-\-no\-fork @@ -392,7 +379,7 @@ a list of future scheduled self tests to stdout, and then exit with zero exit status if all of these steps worked correctly. Device's SMART status is not checked. -This option is intended to test whether the '-s REGEX' directives in +This option is intended to test whether the '\-s REGEX' directives in smartd.conf will have the desired effect. The output lists the next test schedules, limited to 5 tests per type and device. This is followed by a summary of all tests of each device within the next 90 days. @@ -427,17 +414,19 @@ The default level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent. .TP .B \-s PREFIX, \-\-savestates=PREFIX -[ATA only] Reads/writes \fBsmartd\fP state information from/to files -\'PREFIX\'\'MODEL\-SERIAL.ata.state\'. This preserves SMART attributes, drive -min and max temperatures (\-W directive), info about last sent warning email +Reads/writes \fBsmartd\fP state information from/to files +\'PREFIX\'\'MODEL\-SERIAL.ata.state\' or \'PREFIX\'\'VENDOR\-MODEL\-SERIAL.scsi.state\'. +This preserves SMART attributes, drive min and max temperatures (\-W directive), +info about last sent warning email (\-m directive), and the time of next check of the self-test REGEXP (\-s directive) across boot cycles. .\" %IF ENABLE_SAVESTATES If this option is not specified, state information is maintained in files -\'/usr/local/var/lib/smartmontools/smartd.MODEL\-SERIAL.ata.state\'. +\'/usr/local/var/lib/smartmontools/smartd.MODEL\-SERIAL.ata.state\' for ATA devices and +\'/usr/local/var/lib/smartmontools/smartd.VENDOR\-MODEL\-SERIAL.scsi.state\' for SCSI devices. To disable state files, specify this option with an empty string -argument: \'-s ""\'. +argument: \'\-s ""\'. .\" %ENDIF ENABLE_SAVESTATES MODEL and SERIAL are build from drive identify information, invalid characters are replaced by underline. @@ -453,6 +442,22 @@ always (re)written after reading the configuration file, before rereading the configuration file (SIGHUP), before smartd shutdown, and after a check forced by SIGUSR1. After a normal check cycle, a file is only rewritten if an important change (which usually results in a SYSLOG output) occurred. +.TP +.B \-w PATH, \-\-warnexec=PATH +[NEW EXPERIMENTAL SMARTD FEATURE] +Run the executable PATH instead of the default script when smartd +needs to send warning messages. PATH must point to an executable binary +file or script. +The default script is +.\" %IF NOT OS Windows +\fB/usr/local/etc/smartd_warning.sh\fP. +.\" %ENDIF NOT OS Windows +.\" %IF OS ALL +(Windows: EXEDIR/smartd_warning.cmd) +.\" %ENDIF OS ALL +.\" %IF OS Windows +.\"! \fBEXEDIR/smartd_warning.cmd\fP. +.\" %ENDIF OS Windows .\" %IF OS Windows .TP .B \-\-service @@ -570,7 +575,7 @@ to read: ...;daemon.info;... /var/adm/messages .fi Alternatively, you can use a local facility to log messages: please -see the \fBsmartd\fP '-l' command-line option described above. +see the \fBsmartd\fP '\-l' command-line option described above. .\" %ENDIF OS Solaris .\" %IF OS Cygwin @@ -587,7 +592,7 @@ The service can be started and stopped by the start-up script as usual .\" %ENDIF OS Cygwin .\" %IF OS Windows On Windows, the log messages are written to the event log or to a file. -See documentation of the '-l FACILITY' option above for details. +See documentation of the '\-l FACILITY' option above for details. On Windows, the following built-in commands can be used to control \fBsmartd\fP, if running as a daemon: @@ -609,22 +614,24 @@ The Windows Version of \fBsmartd\fP has buildin support for services: \'\fBsmartd install [options]\fP\' installs a service named "smartd" (display name "SmartD Service") using the command line \'/INSTALLPATH/smartd.exe \-\-service [options]\'. +This also installs smartd.exe as a event message file for the Windows +event viewer. -\'\fBsmartd remove\fP\' can later be used to remove the service entry -from registry. +\'\fBsmartd remove\fP\' can later be used to remove the service and +event message entries from the registry. Upon startup, the smartd service changes the working directory to its own installation path. If smartd.conf and blat.exe are stored in this directory, no \'-c\' option and \'-M exec\' directive is needed. -The debug mode (\'-d\', \'-q onecheck\') does not work if smartd is +The debug mode (\'\-d\', \'\-q onecheck\') does not work if smartd is running as service. The service can be controlled as usual with Windows commands \'net\' or \'sc\' (\'\fBnet start smartd\fP\', \'\fBnet stop smartd\fP\'). Pausing the service (\'\fBnet pause smartd\fP\') sets the interval between -disk checks (\'-i N\') to infinite. +disk checks (\'\-i N\') to infinite. Continuing the paused service (\'\fBnet continue smartd\fP\') resets the interval and rereads the configuration file immediately (like \fBSIGHUP\fP): @@ -671,7 +678,7 @@ Forking the daemon failed. Couldn\'t create PID file. .TP .B 5: -Config file does not exist (only returned in conjunction with the \'-c\' option). +Config file does not exist (only returned in conjunction with the \'\-c\' option). .TP .B 6: Config file exists, but cannot be read. @@ -716,17 +723,21 @@ status is then 128 plus the signal number. For example if is killed by SIGKILL (signal 9) then the exit status is 137. .PP -.SH AUTHOR -\fBBruce Allen\fP smartmontools\-support@lists.sourceforge.net -.fi +.SH AUTHORS +\fBBruce Allen\fP +.br University of Wisconsin \- Milwaukee Physics Department +.br +\fBChristian Franke\fP (Windows interface, C++ redesign, most enhancements +since 2009) +.br +\fBsmartmontools\-support@lists.sourceforge.net\fP .PP .SH CONTRIBUTORS The following have made large contributions to smartmontools: .nf \fBCasper Dik\fP (Solaris SCSI interface) -\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...) \fBDouglas Gilbert\fP (SCSI subsystem) \fBGuido Guenther\fP (Autoconf/Automake packaging) \fBGeoffrey Keating\fP (Darwin ATA interface) @@ -747,7 +758,7 @@ Many other individuals have made smaller contributions and corrections. .fi This code was derived from the smartsuite package, written by Michael Cornwell, and from the previous UCSC smartsuite package. It extends -these to cover ATA\-5 disks. This code was originally developed as a +these to cover ATA-5 disks. 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 @@ -768,18 +779,18 @@ REFERENCES FOR SMART .fi An introductory article about smartmontools is \fIMonitoring Hard Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004, -pages 74\-77. This is \fBhttp://www.linuxjournal.com/article/6983\fP +pages 74-77. This is \fBhttp://www.linuxjournal.com/article/6983\fP online. If you would like to understand better how SMART works, and what it does, a good place to start is with Sections 4.8 and 6.54 of the first -volume of the \'AT Attachment with Packet Interface\-7\' (ATA/ATAPI\-7) +volume of the \'AT Attachment with Packet Interface-7\' (ATA/ATAPI-7) specification Revision 4b. This documents the SMART functionality which the \fBsmartmontools\fP utilities provide access to. .fi -The functioning of SMART was originally defined by the SFF\-8035i -revision 2 and the SFF\-8055i revision 1.4 specifications. These are +The functioning of SMART was originally defined by the SFF-8035i +revision 2 and the SFF-8055i revision 1.4 specifications. These are publications of the Small Form Factors (SFF) Committee. Links to these and other documents may be found on the Links page of the @@ -788,4 +799,4 @@ Links to these and other documents may be found on the Links page of the .SH SVN ID OF THIS PAGE: -$Id: smartd.8.in 3561 2012-06-05 19:49:31Z chrfranke $ +$Id: smartd.8.in 3799 2013-03-15 17:47:25Z chrfranke $ diff --git a/smartd.conf b/smartd.conf index 7605d5c..5ef85b1 100644 --- a/smartd.conf +++ b/smartd.conf @@ -2,7 +2,7 @@ # Home page is: http://smartmontools.sourceforge.net -# $Id: smartd.conf 3128 2010-07-27 13:08:31Z chrfranke $ +# $Id: smartd.conf 3651 2012-10-18 15:11:36Z samm2 $ # smartd will re-read the configuration file if it receives a HUP # signal @@ -83,10 +83,11 @@ DEVICESCAN #/dev/twa0 -d 3ware,1 -a -s L/../../2/03 # Monitor 2 SATA (not SAS) disks connected to a 3ware 9000 controller which -# uses the 3w-sas driver (Linux, FreeBSD). Start long self-tests Tuesdays +# uses the 3w-sas driver (Linux). Start long self-tests Tuesdays # between 1-2 and 3-4 am. +# On FreeBSD /dev/tws0 should be used instead #/dev/twl0 -d 3ware,0 -a -s L/../../2/01 -#/dev/twa0 -d 3ware,1 -a -s L/../../2/03 +#/dev/twl0 -d 3ware,1 -a -s L/../../2/03 # Same as above for Windows. Option '-d 3ware,N' is not necessary, # disk (port) number is specified in device name. diff --git a/smartd.conf.5.in b/smartd.conf.5.in index 06425ee..a4b093c 100644 --- a/smartd.conf.5.in +++ b/smartd.conf.5.in @@ -1,21 +1,22 @@ .ig Copyright (C) 2002-10 Bruce Allen +Copyright (C) 2004-13 Christian Franke -$Id: smartd.conf.5.in 3561 2012-06-05 19:49:31Z chrfranke $ +$Id: smartd.conf.5.in 3741 2013-01-02 17:06:54Z 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 -Software Foundation; either version 2, or (at your option) any later -version. +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. +You should have received a copy of the GNU General Public License +(for example COPYING); If not, see . 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/ + .. .TH SMARTD.CONF 5 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE .SH NAME @@ -36,9 +37,7 @@ CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .\"! .PP .\" %ENDIF NOT OS ALL \fB/usr/local/etc/smartd.conf\fP is the configuration file for the \fBsmartd\fP -daemon, which monitors the Self-Monitoring, Analysis and Reporting -Technology (SMART) system built into many ATA-3 and later ATA, IDE and -SCSI-3 hard drives. +daemon. If the configuration file \fB/usr/local/etc/smartd.conf\fP is present, \fBsmartd\fP reads it at startup, before \fBfork\fP(2)ing into the @@ -49,50 +48,16 @@ re-read the configuration file. This signal can be generated by typing \fB\\fP in the terminal window where \fBsmartd\fP is running. -.SH CONFIGURATION FILE /usr/local/etc/smartd.conf In the absence of a configuration file -\fBsmartd\fP will try to open all available devices. -.\" %IF OS Linux -Under linux \fBsmartd\fP will try to open the 20 ATA devices -.B /dev/hd[a-t] -and the 26 SCSI devices -.B /dev/sd[a-z]. -.\" %ENDIF OS Linux -.\" %IF OS FreeBSD -Under FreeBSD, -\fBsmartd\fP -will try to open all existing ATA/SATA devices (using ATA subsystem) -.B /dev/ad[0-9]+ -and all existing SCSI/SAS/AHCI devices (using CAM subsystem). -.\" %ENDIF OS FreeBSD -.\" %IF OS NetBSD OpenBSD -Under NetBSD/OpenBSD, -\fBsmartd\fP -will try to open all existing ATA devices (with entries in /dev) -.B /dev/wd[0-9]+c -and all existing SCSI devices -.B /dev/sd[0-9]+c. -.\" %ENDIF OS NetBSD OpenBSD -.\" %IF OS Solaris -Under Solaris \fBsmartd\fP will try to open all entries \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk -devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices. -.\" %ENDIF OS Solaris -.\" %IF OS Windows -Under Windows \fBsmartd\fP will try to open all entries \fB"/dev/hd[a-j]"\fP ("\\\\.\\PhysicalDrive[0-9]") -for IDE/ATA devices on WinNT4/2000/XP, \fB"/dev/hd[a-d]"\fP -(bitmask from "\\\\.\\SMARTVSD") for IDE/ATA devices on Win95/98/98SE/ME, -and \fB"/dev/scsi[0-9][0-7]"\fP (ASPI adapter 0-9, ID 0-7) for SCSI -devices on all versions of Windows. -.\" %ENDIF OS Windows -.\" %IF OS Darwin -Under Darwin, \fBsmartd\fP will open any ATA block storage device. -.\" %ENDIF OS Darwin +\fBsmartd\fP will try to open all available devices +(see \fBsmartd\fP(8) man page). +A configuration file with a single line \fB\'DEVICESCAN \-a'\fP +would have the same effect. This can be annoying if you have an ATA or SCSI device that hangs or misbehaves when receiving SMART commands. Even if this causes no -problems, you may be annoyed by the string of error log messages about -block-major devices that can\'t be found, and SCSI devices that can\'t -be opened. +problems, you may be annoyed by the string of error log messages about devices +that can\'t be opened. One can avoid this problem, and gain more control over the types of events monitored by @@ -176,6 +141,7 @@ Section below! .B \ \ /dev/sda -d megaraid,0 -a -s S/../.././01 .B \ \ /dev/sda -d megaraid,1 -a -s S/../.././02 .B \ \ /dev/sda -d megaraid,2 -a -s S/../.././03 +.B \ \ /dev/bus/0 -d megaraid,2 -a -s S/../.././03 .B .B # .\" %ENDIF OS Linux @@ -202,8 +168,14 @@ Section below! .B # Two SATA (not SAS) disks on a 3ware 9750 controller. .B # Start long self-tests Sundays between midnight and .B # 1am and 2-3 am +.\" %IF OS Linux .B \ \ /dev/twl0 -d 3ware,0 -a -s L/../../7/00 .B \ \ /dev/twl0 -d 3ware,1 -a -s L/../../7/02 +.\" %ENDIF OS Linux +.\" %IF OS FreeBSD +.B \ \ /dev/tws0 -d 3ware,0 -a -s L/../../7/00 +.B \ \ /dev/tws0 -d 3ware,1 -a -s L/../../7/02 +.\" %ENDIF OS FreeBSD .B # .nf .B # Three SATA disks on a HighPoint RocketRAID controller. @@ -341,7 +313,7 @@ status fails, or if new errors appear in the self-test log. .B If a 3ware controller is used then the corresponding SCSI (/dev/sd?) or character device (/dev/twe?, -/dev/twa? or /dev/twl?) must be listed, along with the \'\-d 3ware,N\' +/dev/twa?, /dev/twl? or /dev/tws?) must be listed, along with the \'\-d 3ware,N\' Directive (see below). The individual ATA disks hosted by the 3ware controller appear to \fBsmartd\fP as normal ATA devices. Hence all the ATA directives can be used for these disks (but see note below). @@ -362,7 +334,7 @@ Specifies the type of the device. The valid arguments to this directive are: .I auto -- attempt to guess the device type from the device name or from +\- attempt to guess the device type from the device name or from controller type info provided by the operating system or from a matching USB ID entry in the drive database. This is the default. @@ -386,8 +358,7 @@ SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and the other 16 bytes long. The default is the 16 byte variant which can be overridden with either \'\-d sat,12\' or \'\-d sat,16\'. -[NEW EXPERIMENTAL SMARTD FEATURE] If \'-d sat,auto\' is specified, -device type SAT (for ATA/SATA disks) is +If \'\-d sat,auto\' is specified, device type SAT (for ATA/SATA disks) is only used if the SCSI INQUIRY data reports a SATL (VENDOR: "ATA "). Otherwise device type SCSI (for SCSI/SAS disks) is used. @@ -398,8 +369,8 @@ The default SCSI operation code is 0x24, but although it can be overridden with \'\-d usbcypress,0xN\', where N is the scsi operation code, you're running the risk of damage to the device or filesystems on it. -.I usbjmicron -- this device type is for SATA disks that are behind a JMicron USB to +.I usbjmicron[,p][,x][,PORT] +\- this device type is for SATA disks that are behind a JMicron USB to PATA/SATA bridge. The 48-bit ATA commands (required e.g. for \'\-l xerror\', see below) do not work with all of these bridges and are therefore disabled by default. These commands can be enabled by \'\-d usbjmicron,x\'. @@ -413,6 +384,13 @@ CAUTION: Specifying \',x\' for a device which does not support it results in I/O errors and may disconnect the drive. The same applies if the specified PORT does not exist or is not connected to a disk. +[NEW EXPERIMENTAL SMARTD FEATURE] +The Prolific PL2507/3507 USB bridges with older firmware support a pass-through +command similar to JMicron and work with \'\-d usbjmicron,0\'. +Newer Prolific firmware requires a modified command which can be selected by +\'\-d usbjmicron,p\'. +Note that this does not yet support the SMART status command. + .I usbsunplus \- this device type is for SATA disks that are behind a SunplusIT USB to SATA bridge. @@ -430,6 +408,8 @@ to a MegaRAID controller. The non-negative integer N (in the range of 0 to This interface will also work for Dell PERC controllers. In log files and email messages this disk will be identified as megaraid_disk_XXX with XXX in the range from 000 to 127 inclusive. +It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus +number. Please see the \fBsmartctl\fP(8) man page for further details. .\" %ENDIF OS Linux @@ -460,16 +440,15 @@ Please see the \fBsmartctl\fP(8) man page for further details. .I areca,N/E \- [FreeBSD, Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTD FEATURE] the -device consists of one or more SATA disks connected to an Areca SAS RAID controller. +device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller. The integer N (range 1 to 128) denotes the channel (slot) and E (range 1 to 8) denotes the enclosure. -Important: This requires upcoming Areca SAS controller firmware version 1.51 or a -recent beta version. +Important: This requires Areca SAS controller firmware version 1.51 or later. .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS FreeBSD Linux .I cciss,N -\- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS disks +\- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS or SATA disks connected to a cciss RAID controller. The non-negative integer N (in the range from 0 to 15 inclusive) denotes which disk on the controller is monitored. In log files and email messages this disk will be identified as cciss_disk_XX @@ -490,6 +469,14 @@ to the default value 1. Please see the \fBsmartctl\fP(8) man page for further details. .\" %ENDIF OS FreeBSD Linux +.I ignore +\- [NEW EXPERIMENTAL SMARTD FEATURE] +the device specified by this configuration entry should be ignored. +This allows to ignore specific devices which are detected by a following +DEVICESCAN configuration line. +It may also be used to temporary disable longer multi-line configuration entries. +This Directive may be used in conjunction with the other \'\-d\' Directives. + .I removable \- the device or its media is removable. This indicates to \fBsmartd\fP @@ -507,25 +494,25 @@ power consumption they are: \'OFF\', \'SLEEP\', \'STANDBY\', \'IDLE\', and \'ACTIVE\'. Typically in the OFF, SLEEP, and STANDBY modes the disk\'s platters are not spinning. But usually, in response to SMART commands issued by \fBsmartd\fP, the disk platters are spun up. So if -this option is not used, then a disk which is in a low\-power mode may -be spun up and put into a higher\-power mode when it is periodically +this option is not used, then a disk which is in a low-power mode may +be spun up and put into a higher-power mode when it is periodically polled by \fBsmartd\fP. Note that if the disk is in SLEEP mode when \fBsmartd\fP is started, then it won't respond to \fBsmartd\fP commands, and so the disk won't be registered as a device for \fBsmartd\fP to monitor. If a disk is in -any other low\-power mode, then the commands issued by \fBsmartd\fP to -register the disk will probably cause it to spin\-up. +any other low-power mode, then the commands issued by \fBsmartd\fP to +register the disk will probably cause it to spin-up. The \'\fB\-n\fP\' (nocheck) Directive specifies if \fBsmartd\fP\'s periodic checks should still be carried out when the device is in a -low\-power mode. It may be used to prevent a disk from being spun\-up +low-power mode. It may be used to prevent a disk from being spun-up by periodic \fBsmartd\fP polling. The allowed values of POWERMODE are: .I never \- \fBsmartd\fP will poll (check) the device regardless of its power -mode. This may cause a disk which is spun\-down to be spun\-up when +mode. This may cause a disk which is spun-down to be spun-up when \fBsmartd\fP checks it. This is the default behavior if the '\-n' Directive is not given. @@ -568,12 +555,8 @@ continue if an optional SMART command fails. This is the default. .I permissive \- try to monitor the disk even if it appears to lack SMART capabilities. This may be required for some old disks (prior to -ATA\-3 revision 4) that implemented SMART before the SMART standards -were incorporated into the ATA/ATAPI Specifications. This may also be -needed for some Maxtor disks which fail to comply with the ATA -Specifications and don't properly indicate support for error\- or -self\-test logging. - +ATA-3 revision 4) that implemented SMART before the SMART standards +were incorporated into the ATA/ATAPI Specifications. [Please see the \fBsmartctl \-T\fP command-line option.] .TP .B \-o VALUE @@ -638,9 +621,9 @@ command-line option. options.] [ATA only] Failed self-tests outdated by a newer successful extended -self\-test are ignored. The warning email counter is reset if the +self-test are ignored. The warning email counter is reset if the number of failed self tests dropped to 0. This typically happens when -an extended self\-test is run after all bad sectors have been reallocated. +an extended self-test is run after all bad sectors have been reallocated. .I offlinests[,ns] \- [ATA only] report if the Offline Data Collection status has changed @@ -686,9 +669,9 @@ not supported. For RAID configurations, this is typically set to [Please see the \fBsmartctl \-l scterc\fP command-line option.] .TP -.B -e NAME[,VALUE] -[NEW EXPERIMENTAL SMARTD FEATURE] Sets non\-SMART device settings -when \fBsmartd\fP starts up and has no further effect. +.B \-e NAME[,VALUE] +Sets non-SMART device settings when \fBsmartd\fP starts up and has no +further effect. [Please see the \fBsmartctl \-\-set\fP command-line option.] Valid arguments are: @@ -837,7 +820,7 @@ disk is active again. Unix users: please beware that the rules for extended regular expressions [regex(7)] are \fBnot\fP the same as the rules for -file\-name pattern matching by the shell [glob(7)]. \fBsmartd\fP will +file-name pattern matching by the shell [glob(7)]. \fBsmartd\fP will issue harmless informational warning messages if it detects characters in \fBREGEXP\fP that appear to indicate that you have made this mistake. @@ -903,20 +886,32 @@ sending mail, this should help you to understand and fix them. If you have mail problems, we recommend running \fBsmartd\fP in debug mode with the \'-d\' flag, using the \'-M test\' Directive described below. +.\" %IF NOT OS Windows + +[NEW EXPERIMENTAL SMARTD FEATURE] +If a word of the comma separated list has the form \'@plugin\', a custom +script /usr/local/etc/smartd_warning.d/plugin is run and the word is +removed from the list before sending mail. The string \'plugin\' may be any +valid name except \'ALL\'. +If \'@ALL\' is specified, all scripts in /usr/local/etc/smartd_warning.d/* +are run instead. +This is handled by the script /usr/local/etc/smartd_warning.sh +(see also \'\-M exec\' below). +.\" %ENDIF NOT OS Windows .\" %IF OS Windows -The following extension is available on Windows: -By specifying \'\fBmsgbox\fP\' as a mail address, a warning -"email" is displayed as a message box on the screen. -Using both \'\fBmsgbox\fP\' and regular mail addresses is possible, -if \'\fBmsgbox\fP\' is the first word in the comma separated list. -With \'\fBsysmsgbox\fP\', a system modal (always on top) message box -is used. - -If running as a service, a service notification message box -(always shown on current visible desktop) is used. Please note that -service notification message boxes are no longer supported on Windows -Vista/2008 or later. +[Windows only] [NEW EXPERIMENTAL SMARTD FEATURE] +If one of the following words are used as the first address in the +comma separated list, warning messages are sent via WTSSendMessage(). +This displays message boxes on the desktops of the selected sessions. +Address \'\fBconsole\fP\' specifies the console session only, +\'\fBactive\fP\' specifies the console session and all active remote +sessions, and \'\fBconnected\fP\' specifies the console session and +all connected (active or waiting for login) remote sessions. +This is handled by the script EXEDIR/smartd_warning.cmd which runs +the tool EXEDIR/wtssendmsg.exe (see also \'\-M exec\' below). +The addresses \'\fBmsgbox\fP\' and \'\fBsysmsgbox\fP\' are now +deprecated and have the same effect as \'\fBconsole\fP\'. .\" %ENDIF OS Windows .TP .B \-M TYPE @@ -968,15 +963,22 @@ in addition to the single test email! \fBsmartd\fP needs to send email. PATH must point to an executable binary file or script. +.\" %IF OS Windows + +[Windows only] The PATH may contain space characters. +Then it must be included in double quotes. +.\" %ENDIF OS Windows By setting PATH to point to a customized script, you can make \fBsmartd\fP perform useful tricks when a disk problem is detected (beeping the console, shutting down the machine, broadcasting warnings to all logged-in users, etc.) But please be careful. \fBsmartd\fP will \fBblock\fP until the executable PATH returns, so if your -executable hangs, then \fBsmartd\fP will also hang. Some sample -scripts are included in +executable hangs, then \fBsmartd\fP will also hang. +.\" %IF NOT OS Windows +Some sample scripts are included in /usr/local/share/doc/smartmontools/examplescripts/. +.\" %ENDIF NOT OS Windows The return status of the executable is recorded by \fBsmartd\fP in SYSLOG. The executable is not expected to write to STDOUT or @@ -1008,6 +1010,11 @@ or \'/dev/hptrr [hpt_1/1/1]\' under FreeBSD. For Areca controllers, the form is \'/dev/sg2 [areca_disk_09]\' on Linux or \'/dev/arcmsr0 [areca_disk_09]\' on FreeBSD. In these cases the device string contains a space and is NOT quoted. So to use $SMARTD_DEVICESTRING in a bash script you should probably enclose it in double quotes. +.IP \fBSMARTD_DEVICEINFO\fP 4 +is set to device identify information. It includes most of the info printed +by \fBsmartctl \-i\fP but uses a brief single line format. +This device info is also logged when \fBsmartd\fP starts up. +The string contains space characters and is NOT quoted. .IP \fBSMARTD_FAILTYPE\fP 4 gives the reason for the warning or message email. The possible values that it takes and their meanings are: @@ -1032,7 +1039,7 @@ it takes and their meanings are: read and are marked to be reallocated (replaced with spare sectors). .nf .fi -\fIOfflineUncorrectableSector\fP: during off\-line testing, or self\-testing, +\fIOfflineUncorrectableSector\fP: during off-line testing, or self-testing, one or more disk sectors could not be read. .nf .fi @@ -1061,18 +1068,32 @@ given by the argument ADD, with the commas replaced by spaces given, then this string will contain space characters and is NOT quoted, so to use it in a bash script you may want to enclose it in double quotes. +.\" %IF OS Windows +.IP \fBSMARTD_ADDRCSV\fP 4 +[Windows only] is set to a comma-separated list of the addresses from +SMARTD_ADDRESS. +.\" %ENDIF OS Windows .IP \fBSMARTD_MESSAGE\fP 4 is set to the one sentence summary warning email message string from \fBsmartd\fP. This message string contains space characters and is NOT quoted. So to use $SMARTD_MESSAGE in a bash script you should probably enclose it in double quotes. +.\" %IF NOT OS Windows .IP \fBSMARTD_FULLMESSAGE\fP 4 is set to the contents of the entire email warning message string from \fBsmartd\fP. This message string contains space and return characters and is NOT quoted. So to use $SMARTD_FULLMESSAGE in a bash script you should probably enclose it in double quotes. +.\" %ENDIF NOT OS Windows +.\" %IF OS Windows +.IP \fBSMARTD_FULLMSGFILE\fP 4 +[Windows only] is the path to a temporary file containing the full message. +The path may contain space characters and is NOT quoted. +The file is created by the smartd_warning.cmd script and removed when +the mailer or command exits. +.\" %ENDIF OS Windows .IP \fBSMARTD_TFIRST\fP 4 is a text string giving the time and date at which the first problem of this type was reported. This text string contains space characters @@ -1083,6 +1104,12 @@ Sun Feb 9 14:58:19 2003 CST .IP \fBSMARTD_TFIRSTEPOCH\fP 4 is an integer, which is the unix epoch (number of seconds since Jan 1, 1970) for \fBSMARTD_TFIRST\fP. +.IP \fBSMARTD_PREVCNT\fP 4 +is an integer specifying the number of previous messages sent. +It is set to \'0\' for the first message. +.IP \fBSMARTD_NEXTDAYS\fP 4 +is an integer specifying the number of days until the next message will be sent. +It it set to empty on \'\-M once\' and set to \'1\' on \'\-M daily\'. .RE .\" The following two lines are a workaround for a man2html bug. Please leave them. .\" They define a non-existent option; useful because man2html can't correctly reset the margins. @@ -1107,10 +1134,10 @@ that would normally be provided to \'mail\'. Examples include: .fi .\" %IF OS Windows -Note that on Windows, the syntax of the \'\fBBlat\fP\' mailer is +[Windows only] On Windows, the syntax of the \'\fBBlat\fP\' mailer is used: .nf -- -q -subject "$SMARTD_SUBJECT" -to "$SMARTD_ADDRESS" +- -q -subject "%SMARTD_SUBJECT%" -to %SMARTD_ADDRCSV% .fi .\" %ENDIF OS Windows @@ -1130,8 +1157,36 @@ will be copied to SYSLOG. The remainder of the output is then discarded. Some EXAMPLES of scripts that can be used with the \'\-M exec\' -Directive are given below. Some sample scripts are also included in +Directive are given below. +.\" %IF NOT OS Windows +Some sample scripts are also included in /usr/local/share/doc/smartmontools/examplescripts/. +.\" %ENDIF NOT OS Windows + +[NEW EXPERIMENTAL SMARTD FEATURE] The executable is run by the script +.\" %IF NOT OS Windows +/usr/local/etc/smartd_warning.sh. +.\" %ENDIF NOT OS Windows +.\" %IF OS ALL +(Windows: EXEDIR/smartd_warning.cmd) +.\" %ENDIF OS ALL +.\" %IF OS Windows +.\"! EXEDIR/smartd_warning.cmd. +.\" %ENDIF OS Windows +This script formats subject and full message based on SMARTD_MESSAGE and other +environment variables set by \fBsmartd\fP. +The environment variables +.\" %IF NOT OS Windows +SMARTD_SUBJECT and SMARTD_FULLMESSAGE +.\" %ENDIF NOT OS Windows +.\" %IF OS ALL +(Windows: SMARTD_SUBJECT, SMARTD_FULLMSGFILE and SMARTD_ADDRCSV) +.\" %ENDIF OS ALL +.\" %IF OS Windows +.\"! SMARTD_SUBJECT, SMARTD_FULLMSGFILE and SMARTD_ADDRCSV +.\" %ENDIF OS Windows +are set by the script before running the executable. + .TP .B \-f [ATA only] Check for \'failure\' of any Usage Attributes. If these @@ -1193,7 +1248,7 @@ A common use of this Directive is to track the device Temperature If the optional flag \'!\' is appended, a change of the Normalized value is considered critical. The report will be logged as LOG_CRIT -and a warning email will be sent if \'-m\' is specified. +and a warning email will be sent if \'\-m\' is specified. .TP .B \-R ID[!] [ATA only] When tracking, report whenever the \fIRaw\fP value of Attribute @@ -1215,7 +1270,7 @@ Attributes. If the optional flag \'!\' is appended, a change of the Raw value is considered critical. The report will be logged as -LOG_CRIT and a warning email will be sent if \'-m\' is specified. +LOG_CRIT and a warning email will be sent if \'\-m\' is specified. An example is \'-R 5!\' to warn when new sectors are reallocated. .TP .B \-C ID[+] @@ -1271,7 +1326,7 @@ sectors dropped to 0. This typically happens when all offline uncorrectable sectors have been reallocated or could be read again. An offline uncorrectable sector is a disk sector which was not -readable during an off\-line scan or a self\-test. This is important +readable during an off-line scan or a self-test. This is important to know, because if you have data stored in this disk sector, and you need to read it, the read will fail. Please see the previous \'\-C\' option for more details. @@ -1283,7 +1338,7 @@ Report or Warn if the temperature is greater or equal than one of \fBINFO\fP or \fBCRIT\fP degrees Celsius. If the limit \fBCRIT\fP is reached, a message with loglevel \fB\'LOG_CRIT\'\fP will be logged to syslog and a warning email -will be send if '-m' is specified. If only the limit \fBINFO\fP is +will be send if \'\-m\' is specified. If only the limit \fBINFO\fP is reached, a message with loglevel \fB\'LOG_INFO\'\fP will be logged. The warning email counter is reset if the temperature dropped below @@ -1315,50 +1370,53 @@ To combine all of the above reports, use: .B \-W 2,40,45 .fi -For ATA devices, smartd interprets Attribute 194 as Temperature Celsius +For ATA devices, smartd interprets Attribute 194 or 190 as Temperature Celsius by default. This can be changed to Attribute 9 or 220 by the drive -database or by the \'-v\' directive, see below. +database or by the \'\-v 9,temp\' or \'\-v 220,temp\' directive. .TP .B \-F TYPE -[ATA only] Modifies the behavior of \fBsmartd\fP to compensate for -some known and understood device firmware bug. The arguments to this -Directive are exclusive, so that only the final Directive given is -used. The valid values are: +[ATA only] Modifies the behavior of \fBsmartd\fP to compensate for some +known and understood device firmware bug. This directive may be used +multiple times. The valid arguments are: .I none \- Assume that the device firmware obeys the ATA specifications. This is the default, unless the device has presets for \'\-F\' in the -device database. +drive database. Using this directive will over-ride any preset values. + +.I nologdir +\- Suppresses read attempts of SMART or GP Log Directory. +Support for all standard logs is assumed without an actual check. +Some Intel SSDs may freeze if log address 0 is read. .I samsung \- In some Samsung disks (example: model SV4012H Firmware Version: -RM100\-08) some of the two\- and four\-byte quantities in the SMART data -structures are byte\-swapped (relative to the ATA specification). +RM100-08) some of the two- and four-byte quantities in the SMART data +structures are byte-swapped (relative to the ATA specification). Enabling this option tells \fBsmartd\fP to evaluate these quantities -in byte\-reversed order. Some signs that your disk needs this option -are (1) no self\-test log printed, even though you have run self\-tests; +in byte-reversed order. Some signs that your disk needs this option +are (1) no self-test log printed, even though you have run self-tests; (2) very large numbers of ATA errors reported in the ATA error log; (3) strange and impossible values for the ATA error log timestamps. .I samsung2 \- In some Samsung disks the number of ATA errors reported is byte swapped. Enabling this option tells \fBsmartd\fP to evaluate this quantity in -byte\-reversed order. +byte-reversed order. .I samsung3 -\- Some Samsung disks (at least SP2514N with Firmware VF100\-37) report -a self\-test still in progress with 0% remaining when the test was already +\- Some Samsung disks (at least SP2514N with Firmware VF100-37) report +a self-test still in progress with 0% remaining when the test was already completed. If this directive is specified, \fBsmartd\fP will not skip the -next scheduled self\-test (see Directive \'\-s\' above) in this case. - -Note that an explicit \'\-F\' Directive will over\-ride any preset -values for \'\-F\' (see the \'\-P\' option below). +next scheduled self-test (see Directive \'\-s\' above) in this case. +.I xerrorlba +\- This only affects \fBsmartctl\fP. [Please see the \fBsmartctl \-F\fP command-line option.] .TP .B \-v ID,FORMAT[:BYTEORDER][,NAME] -[ATA only] Sets a vendor\-specific raw value print FORMAT, an optional +[ATA only] Sets a vendor-specific raw value print FORMAT, an optional BYTEORDER and an optional NAME for Attribute ID. This directive may be used multiple times. Please see \fBsmartctl -v\fP command-line option for further details. @@ -1444,10 +1502,7 @@ If you want more frequent information, use: 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. - -Configuration entries for devices not found by the platform\-specific -device scanning may precede the \fBDEVICESCAN\fP entry. +for devices (see also \fBsmartd\fP(8) man page). If \fBDEVICESCAN\fP is not followed by any Directives, then smartd will scan for both ATA and SCSI devices, and will monitor all possible @@ -1472,6 +1527,20 @@ will do the same, but only monitors the SMART health status of the devices, (rather than the default \-a, which monitors all SMART properties). +[NEW EXPERIMENTAL SMARTD FEATURE] +Configuration entries for specific devices may precede the \fBDEVICESCAN\fP entry. +For example +.nf +.B DEFAULT -m root@example.com +.B /dev/sda -s S/../.././02 +.B /dev/sdc -d ignore +.B DEVICESCAN -s L/../.././02 +.fi +will scan for all devices except /dev/sda and /dev/sdc, monitor them, and run a long +test between 2-3am every morning. Device /dev/sda will also be monitored, but +only a short test will be run. Device /dev/sdc will be ignored. +Warning emails will be sent for all monitored devices. + .TP .B EXAMPLES OF SHELL SCRIPTS FOR \'\-M exec\' These are two examples of shell scripts that can be used with the \'\-M @@ -1534,17 +1603,21 @@ within the script, and a snippet of STDOUT/STDERR is logged to SYSLOG. The remainder is flushed. .PP -.SH AUTHOR -\fBBruce Allen\fP smartmontools\-support@lists.sourceforge.net -.fi +.SH AUTHORS +\fBBruce Allen\fP +.br University of Wisconsin \- Milwaukee Physics Department +.br +\fBChristian Franke\fP (Windows interface, C++ redesign, most enhancements +since 2009) +.br +\fBsmartmontools\-support@lists.sourceforge.net\fP .PP .SH CONTRIBUTORS The following have made large contributions to smartmontools: .nf \fBCasper Dik\fP (Solaris SCSI interface) -\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...) \fBDouglas Gilbert\fP (SCSI subsystem) \fBGuido Guenther\fP (Autoconf/Automake packaging) \fBGeoffrey Keating\fP (Darwin ATA interface) @@ -1565,7 +1638,7 @@ Many other individuals have made smaller contributions and corrections. .fi This code was derived from the smartsuite package, written by Michael Cornwell, and from the previous UCSC smartsuite package. It extends -these to cover ATA\-5 disks. This code was originally developed as a +these to cover ATA-5 disks. 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 @@ -1583,4 +1656,4 @@ SEE ALSO: .SH SVN ID OF THIS PAGE: -$Id: smartd.conf.5.in 3561 2012-06-05 19:49:31Z chrfranke $ +$Id: smartd.conf.5.in 3741 2013-01-02 17:06:54Z chrfranke $ diff --git a/smartd.cpp b/smartd.cpp index 0e124ec..fec1235 100644 --- a/smartd.cpp +++ b/smartd.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2000 Michael Cornwell * Copyright (C) 2008 Oliver Bock - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 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 @@ -21,10 +21,8 @@ * */ -#ifndef _GNU_SOURCE -// TODO: Why is this define necessary? -#define _GNU_SOURCE -#endif +#include "config.h" +#include "int64.h" // unconditionally included files #include @@ -46,9 +44,6 @@ #include #include // std::replace() -// see which system files to conditionally include -#include "config.h" - // conditionally included files #ifndef _WIN32 #include @@ -79,7 +74,6 @@ typedef int pid_t; #endif // LIBCAP_NG // locally included files -#include "int64.h" #include "atacmds.h" #include "dev_interface.h" #include "knowndrives.h" @@ -95,9 +89,6 @@ typedef int pid_t; #endif #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 @@ -115,9 +106,7 @@ typedef int pid_t; extern "C" int getdomainname(char *, int); // no declaration in header files! #endif -#define ARGUSED(x) ((void)(x)) - -const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3513 2012-02-15 21:57:21Z chrfranke $" +const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3802 2013-03-24 18:36:21Z chrfranke $" CONFIG_H_CVSID; // smartd exit codes @@ -168,6 +157,9 @@ static const char * const configfile_stdin = ""; // path of alternate configuration file static std::string configfile_alt; +// warning script file +static std::string warning_script; + // command-line: when should we exit? static int quit=0; @@ -184,10 +176,8 @@ static bool do_fork=true; static bool enable_capabilities = false; #endif -#if defined(_WIN32) || defined(__CYGWIN__) // TODO: This smartctl only variable is also used in os_win32.cpp unsigned char failuretest_permissive = 0; -#endif // set to one if we catch a USR1 (check devices now) static volatile int caughtsigUSR1=0; @@ -248,8 +238,10 @@ struct dev_config std::string name; // Device name (with optional extra info) std::string dev_name; // Device name (plain, for SMARTD_DEVICE variable) std::string dev_type; // Device type argument from -d directive, empty if none + std::string dev_idinfo; // Device identify info for warning emails std::string state_file; // Path of the persistent state file, empty if none std::string attrlog_file; // Path of the persistent attrlog file, empty if none + bool ignore; // Ignore this entry bool smartcheck; // Check SMART status bool usagefailed; // Check for failed Usage Attributes bool prefail; // Track changes in Prefail Attributes @@ -264,7 +256,7 @@ struct dev_config bool permissive; // Ignore failed SMART commands char autosave; // 1=disable, 2=enable Autosave Attributes char autoofflinetest; // 1=disable, 2=enable Auto Offline Test - unsigned char fix_firmwarebug; // FIX_*, see atacmds.h + firmwarebug_defs firmwarebugs; // -F directives from drivedb or smartd.conf bool ignorepresets; // Ignore database of -v options bool showpresets; // Show database entry for this device bool removable; // Device may disappear (not be present) @@ -282,6 +274,7 @@ struct dev_config bool emailtest; // Send test email? // ATA ONLY + int dev_rpm; // rotation rate, 0 = unknown, 1 = SSD, >1 = HDD int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management int set_lookahead; // disable(-1), enable(1) read look-ahead @@ -307,6 +300,7 @@ struct dev_config dev_config::dev_config() : lineno(0), + ignore(false), smartcheck(false), usagefailed(false), prefail(false), @@ -319,7 +313,6 @@ dev_config::dev_config() permissive(false), autosave(0), autoofflinetest(0), - fix_firmwarebug(FIX_NOTSPECIFIED), ignorepresets(false), showpresets(false), removable(false), @@ -330,6 +323,7 @@ dev_config::dev_config() tempinfo(0), tempcrit(0), emailfreq(0), emailtest(false), + dev_rpm(0), set_aam(0), set_apm(0), set_lookahead(0), set_standby(0), @@ -388,6 +382,22 @@ struct persistent_dev_state ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { } }; ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES]; + + // SCSI ONLY + + struct scsi_error_counter { + struct scsiErrorCounter errCounter; + unsigned char found; + scsi_error_counter() : found(0) { } + }; + scsi_error_counter scsi_error_counters[3]; + + struct scsi_nonmedium_error { + struct scsiNonMediumError nme; + unsigned char found; + scsi_nonmedium_error() : found(0) { } + }; + scsi_nonmedium_error scsi_nonmedium_error; persistent_dev_state(); }; @@ -423,10 +433,13 @@ struct temp_dev_state // SCSI ONLY unsigned char SmartPageSupported; // has log sense IE page (0x2f) unsigned char TempPageSupported; // has log sense temperature page (0xd) + unsigned char ReadECounterPageSupported; + unsigned char WriteECounterPageSupported; + unsigned char VerifyECounterPageSupported; + unsigned char NonMediumErrorPageSupported; unsigned char SuppressReport; // minimize nuisance reports unsigned char modese_len; // mode sense/select cmd len: 0 (don't // know yet) 6 or 10 - // ATA ONLY uint64_t num_sectors; // Number of sectors ata_smart_values smartval; // SMART data @@ -450,6 +463,10 @@ temp_dev_state::temp_dev_state() powerskipcnt(0), SmartPageSupported(false), TempPageSupported(false), + ReadECounterPageSupported(false), + WriteECounterPageSupported(false), + VerifyECounterPageSupported(false), + NonMediumErrorPageSupported(false), SuppressReport(false), modese_len(0), num_sectors(0), @@ -721,7 +738,7 @@ static bool write_dev_state(const char * path, const persistent_dev_state & stat } // Write to the attrlog file -static bool write_dev_attrlog(const char * path, const persistent_dev_state & state) +static bool write_dev_attrlog(const char * path, const dev_state & state) { stdio_file f(path, "a"); if (!f) { @@ -729,20 +746,48 @@ static bool write_dev_attrlog(const char * path, const persistent_dev_state & st return false; } - // ATA ONLY + time_t now = time(0); struct tm * tms = gmtime(&now); fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;", 1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec); + // ATA ONLY for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i]; if (!pa.id) continue; fprintf(f, "\t%d;%d;%"PRIu64";", pa.id, pa.val, pa.raw); } + // SCSI ONLY + const struct scsiErrorCounter * ecp; + const char * pageNames[3] = {"read", "write", "verify"}; + for (int k = 0; k < 3; ++k) { + if ( !state.scsi_error_counters[k].found ) continue; + ecp = &state.scsi_error_counters[k].errCounter; + fprintf(f, "\t%s-corr-by-ecc-fast;%"PRIu64";" + "\t%s-corr-by-ecc-delayed;%"PRIu64";" + "\t%s-corr-by-retry;%"PRIu64";" + "\t%s-total-err-corrected;%"PRIu64";" + "\t%s-corr-algorithm-invocations;%"PRIu64";" + "\t%s-gb-processed;%.3f;" + "\t%s-total-unc-errors;%"PRIu64";", + pageNames[k], ecp->counter[0], + pageNames[k], ecp->counter[1], + pageNames[k], ecp->counter[2], + pageNames[k], ecp->counter[3], + pageNames[k], ecp->counter[4], + pageNames[k], (ecp->counter[5] / 1000000000.0), + pageNames[k], ecp->counter[6]); + } + if(state.scsi_nonmedium_error.found && state.scsi_nonmedium_error.nme.gotPC0) { + fprintf(f, "\tnon-medium-errors;%"PRIu64";", state.scsi_nonmedium_error.nme.counterPC0); + } + // write SCSI current temperature if it is monitored + if(state.TempPageSupported && state.temperature) + fprintf(f, "\ttemperature;%d;", state.temperature); + // end of line fprintf(f, "\n"); - return true; } @@ -849,56 +894,43 @@ static int Goodbye(int status) return status; } -#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. -static int exportenv(char *stackspace, const char *name, const char *value) +// string can be freed if the environment variable is redefined via +// another call to putenv(). There is no portable way to unset a variable +// with putenv(). So we manage the buffer in a static object. +// Using setenv() if available is not considered because some +// implementations may produce memory leaks. + +class env_buffer { - snprintf(stackspace,ENVLENGTH, "%s=%s", name, value); - return putenv(stackspace); -} +public: + env_buffer() + : m_buf((char *)0) { } + + void set(const char * name, const char * value); + +private: + char * m_buf; + + env_buffer(const env_buffer &); + void operator=(const env_buffer &); +}; -static char *dnsdomain(const char *hostname) +void env_buffer::set(const char * name, const char * value) { - char *p = NULL; -#ifdef HAVE_GETADDRINFO - static char canon_name[NI_MAXHOST]; - struct addrinfo *info = NULL; - struct addrinfo hints; - int err; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_CANONNAME; - if ((err = getaddrinfo(hostname, NULL, &hints, &info)) || (!info)) { - PrintOut(LOG_CRIT, "Error retrieving getaddrinfo(%s): %s\n", hostname, gai_strerror(err)); - return NULL; - } - if (info->ai_canonname) { - strncpy(canon_name, info->ai_canonname, sizeof(canon_name)); - canon_name[NI_MAXHOST - 1] = '\0'; - p = canon_name; - if ((p = strchr(canon_name, '.'))) - p++; - } - freeaddrinfo(info); -#elif 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; + int size = strlen(name) + 1 + strlen(value) + 1; + char * newbuf = new char[size]; + snprintf(newbuf, size, "%s=%s", name, value); + + if (putenv(newbuf)) + throw std::runtime_error("putenv() failed"); + + // This assumes that the same NAME is passed on each call + delete [] m_buf; + m_buf = newbuf; } #define EBUFLEN 1024 @@ -908,15 +940,9 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co // If either address or executable path is non-null then send and log // a warning email, or execute executable -static void MailWarning(const dev_config & cfg, dev_state & state, int which, const 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; - const char * const whichfail[]={ +static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) +{ + static const char * const whichfail[] = { "EmailTest", // 0 "Health", // 1 "Usage", // 2 @@ -932,8 +958,6 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co "Temperature" // 12 }; - const char *unknown="[Unknown]"; - // See if user wants us to send mail if (cfg.emailaddress.empty() && cfg.emailcmdline.empty()) return; @@ -964,15 +988,16 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co return; // To decide if to send mail, we need to know what time it is. - epoch=time(NULL); + time_t epoch = time(0); // Return if less than one day has gone by + const int day = 24*3600; if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) return; // Return if less than 2^(logged-1) days have gone by if (cfg.emailfreq==3 && mail->logged) { - days=0x01<<(mail->logged-1); + int days = 0x01 << (mail->logged - 1); days*=day; if (epoch<(mail->lastsent+days)) return; @@ -990,125 +1015,57 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co 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 + char message[256]; + va_list ap; va_start(ap, fmt); - vsnprintf(message, 256, fmt, ap); + vsnprintf(message, sizeof(message), 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 (cfg.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)<logged); - break; - } - if (cfg.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 - -#ifndef _WIN32 // blat mailer needs comma // replace commas by spaces to separate recipients std::replace(address.begin(), address.end(), ',', ' '); -#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); + static env_buffer env[12]; + env[0].set("SMARTD_MAILER", executable); + env[1].set("SMARTD_MESSAGE", message); + char dates[DATEANDEPOCHLEN]; + snprintf(dates, sizeof(dates), "%d", mail->logged); + env[2].set("SMARTD_PREVCNT", dates); dateandtimezoneepoch(dates, mail->firstsent); - exportenv(environ_strings[3], "SMARTD_TFIRST", dates); + env[3].set("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.empty()) - exportenv(environ_strings[6], "SMARTD_ADDRESS", address.c_str()); - exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg.name.c_str()); + env[4].set("SMARTD_TFIRSTEPOCH", dates); + env[5].set("SMARTD_FAILTYPE", whichfail[which]); + env[6].set("SMARTD_ADDRESS", address.c_str()); + env[7].set("SMARTD_DEVICESTRING", cfg.name.c_str()); // Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE' - exportenv(environ_strings[8], "SMARTD_DEVICETYPE", - (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto")); - exportenv(environ_strings[9], "SMARTD_DEVICE", cfg.dev_name.c_str()); - - 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.\n\n" - "%s%s%s", - hostname, domainname, nisdomain, message, further, original, additional); - exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage); + env[8].set("SMARTD_DEVICETYPE", + (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto")); + env[9].set("SMARTD_DEVICE", cfg.dev_name.c_str()); + + env[10].set("SMARTD_DEVICEINFO", cfg.dev_idinfo.c_str()); + dates[0] = 0; + if (which) switch (cfg.emailfreq) { + case 2: dates[0] = '1'; dates[1] = 0; break; + case 3: snprintf(dates, sizeof(dates), "%d", (0x01)<logged); + } + env[11].set("SMARTD_NEXTDAYS", dates); // now construct a command to send this as EMAIL -#ifndef _WIN32 - if (!address.empty()) - snprintf(command, 2048, - "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n" - "%sENDMAIL\n", subject, address.c_str(), fullmessage); - else - snprintf(command, 2048, "%s 2>&1", executable); - - // tell SYSLOG what we are about to do... + char command[2048]; + if (!*executable) + executable = ""; const char * newadd = (!address.empty()? address.c_str() : ""); const char * newwarn = (which? "Warning via" : "Test of"); +#ifndef _WIN32 + snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str()); + + // tell SYSLOG what we are about to do... PrintOut(LOG_INFO,"%s %s to %s ...\n", which?"Sending warning via":"Executing test of", executable, newadd); @@ -1182,65 +1139,15 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co } #else // _WIN32 + { + snprintf(command, sizeof(command), "cmd /c \"%s\"", warning_script.c_str()); - // No "here-documents" on Windows, so must use separate commandline and stdin - char stdinbuf[1024]; - command[0] = stdinbuf[0] = 0; - int boxtype = -1, boxmsgoffs = 0; - const char * newadd = ""; - if (!address.empty()) { - // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox - char addr1[9+1+13] = ""; int n1 = -1, n2 = -1; - if (sscanf(address.c_str(), "%9[a-z]%n,%n", addr1, &n1, &n2) == 1 && (n1 == (int)address.size() || n2 > 0)) { - if (!strcmp(addr1, "msgbox")) - boxtype = 0; - else if (!strcmp(addr1, "sysmsgbox")) - boxtype = 1; - if (boxtype >= 0) - address.erase(0, (n2 > n1 ? n2 : n1)); - } - - if (!address.empty()) { - // Use "blat" parameter syntax (TODO: configure via -M for other mailers) - snprintf(command, sizeof(command), - "%s - -q -subject \"%s\" -to \"%s\"", - executable, subject, address.c_str()); - newadd = address.c_str(); - } - - // 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", - hostname, /*domainname, */ nisdomain); - boxmsgoffs = strlen(stdinbuf); - snprintf(stdinbuf+boxmsgoffs, sizeof(stdinbuf)-boxmsgoffs, - "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", - message, further, original, additional); - } - else - snprintf(command, sizeof(command), "%s", executable); - - const char * 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)); + rc = daemon_spawn(command, "", 0, 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, (int)strlen(stdoutbuf), stdoutbuf); @@ -1321,13 +1228,15 @@ void pout(const char *fmt, ...){ // initialize variable argument list va_start(ap,fmt); // in debugmode==1 mode we will print the output from the ataprint.o functions! - if (debugmode && debugmode!=2) + if (debugmode && debugmode != 2) { + FILE * f = stdout; #ifdef _WIN32 - if (facility == LOG_LOCAL1) // logging to stdout - vfprintf(stderr,fmt,ap); - else + if (facility == LOG_LOCAL1) // logging to stdout + f = stderr; #endif - vprintf(fmt,ap); + vfprintf(f, fmt, ap); + fflush(f); + } // in debugmode==2 mode we print output from knowndrives.o functions else if (debugmode==2 || ata_debugmode || scsi_debugmode) { openlog("smartd", LOG_PID, facility); @@ -1335,7 +1244,6 @@ void pout(const char *fmt, ...){ closelog(); } va_end(ap); - fflush(NULL); return; } @@ -1347,13 +1255,15 @@ static void PrintOut(int priority, const char *fmt, ...){ FixGlibcTimeZoneBug(); // initialize variable argument list va_start(ap,fmt); - if (debugmode) + if (debugmode) { + FILE * f = stdout; #ifdef _WIN32 - if (facility == LOG_LOCAL1) // logging to stdout - vfprintf(stderr,fmt,ap); - else + if (facility == LOG_LOCAL1) // logging to stdout + f = stderr; #endif - vprintf(fmt,ap); + vfprintf(f, fmt, ap); + fflush(f); + } else { openlog("smartd", LOG_PID, facility); vsyslog_lines(priority, fmt, ap); @@ -1506,7 +1416,8 @@ static void Directives() { PrintOut(LOG_INFO, "Configuration file (%s) Directives (after device name):\n" - " -d TYPE Set the device type: %s, auto, removable\n" + " -d TYPE Set the device type: auto, ignore, removable,\n" + " %s\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" @@ -1534,14 +1445,16 @@ static void Directives() " -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 -l selfteststs -C 197 -U 198\n" - " -F TYPE Firmware bug workaround: none, samsung, samsung2, samsung3\n" + " -F TYPE Use firmware bug workaround:\n" + " %s\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, smi()->get_valid_dev_types_str().c_str()); - return; + "Use ID = 0 to turn off -C and/or -U Directives\n" + "Example: /dev/sda -a\n", + configfile, + smi()->get_valid_dev_types_str().c_str(), + get_valid_firmwarebug_args()); } /* Returns a pointer to a static string containing a formatted list of the valid @@ -1562,6 +1475,7 @@ static const char *GetValidArgList(char opt) return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; case 'B': case 'p': + case 'w': return ""; case 'i': return ""; @@ -1626,6 +1540,13 @@ static void Usage() PrintOut(LOG_INFO," [default is "SMARTMONTOOLS_SAVESTATES"MODEL-SERIAL.TYPE.state]\n"); #endif PrintOut(LOG_INFO,"\n"); + PrintOut(LOG_INFO," -w NAME, --warnexec=NAME\n"); + PrintOut(LOG_INFO," Run executable NAME on warnings\n"); +#ifndef _WIN32 + PrintOut(LOG_INFO," [default is "SMARTMONTOOLS_SYSCONFDIR"/smartd_warning.sh]\n\n"); +#else + PrintOut(LOG_INFO," [default is %s/smartd_warning.cmd]\n\n", get_exe_dir().c_str()); +#endif #ifdef _WIN32 PrintOut(LOG_INFO," --service\n"); PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n"); @@ -1658,11 +1579,11 @@ static bool not_allowed_in_filename(char c) // Read error count from Summary or Extended Comprehensive SMART error log // Return -1 on error static int read_ata_error_count(ata_device * device, const char * name, - unsigned char fix_firmwarebug, bool extended) + firmwarebug_defs firmwarebugs, bool extended) { if (!extended) { ata_smart_errorlog log; - if (ataReadErrorLog(device, &log, fix_firmwarebug)){ + if (ataReadErrorLog(device, &log, firmwarebugs)){ PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name); return -1; } @@ -1670,7 +1591,7 @@ static int read_ata_error_count(ata_device * device, const char * name, } else { ata_smart_exterrlog logx; - if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/)) { + if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/, firmwarebugs)) { PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name); return -1; } @@ -1682,17 +1603,17 @@ static int read_ata_error_count(ata_device * device, const char * name, // 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. static int SelfTestErrorCount(ata_device * device, const char * name, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { struct ata_smart_selftestlog log; - if (ataReadSelfTestLog(device, &log, fix_firmwarebug)){ + if (ataReadSelfTestLog(device, &log, firmwarebugs)){ 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, false, fix_firmwarebug); + return ataPrintSmartSelfTestlog(&log, false, firmwarebugs); } #define SELFTEST_ERRORCOUNT(x) (x & 0xff) @@ -1845,7 +1766,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade return 2; } - // Log drive identity and size + // Get drive identity, size and rotation rate (HDD/SSD) char model[40+1], serial[20+1], firmware[8+1]; ata_format_id_string(model, drive.model, sizeof(model)-1); ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1); @@ -1854,6 +1775,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade ata_size_info sizes; ata_get_size_info(&drive, sizes); state.num_sectors = sizes.sectors; + cfg.dev_rpm = ata_get_rotation_rate(&drive); char wwn[30]; wwn[0] = 0; unsigned oui = 0; uint64_t unique_id = 0; @@ -1861,10 +1783,12 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade if (naa >= 0) snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09"PRIx64", ", naa, oui, unique_id); + // Format device id string for warning emails char cap[32]; - PrintOut(LOG_INFO, "Device: %s, %s, S/N:%s, %sFW:%s, %s\n", name, - model, serial, wwn, firmware, - format_capacity(cap, sizeof(cap), sizes.capacity, ".")); + cfg.dev_idinfo = strprintf("%s, S/N:%s, %sFW:%s, %s", model, serial, wwn, firmware, + format_capacity(cap, sizeof(cap), sizes.capacity, ".")); + + PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str()); // Show if device in database, and use preset vendor attribute // options unless user has requested otherwise. @@ -1873,7 +1797,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade else { // Apply vendor specific presets, print warning if present const drive_settings * dbentry = lookup_drive_apply_presets( - &drive, cfg.attribute_defs, cfg.fix_firmwarebug); + &drive, cfg.attribute_defs, cfg.firmwarebugs); if (!dbentry) PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); else { @@ -1997,7 +1921,8 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade if ( (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) && !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) { - PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name); + PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", + name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; } @@ -2044,12 +1969,13 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade bool smart_logdir_ok = false, gp_logdir_ok = false; if ( isGeneralPurposeLoggingCapable(&drive) - && (cfg.errorlog || cfg.selftest) ) { + && (cfg.errorlog || cfg.selftest) + && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { if (!ataReadLogDirectory(atadev, &smart_logdir, false)) smart_logdir_ok = true; } - if (cfg.xerrorlog) { + if (cfg.xerrorlog && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { if (!ataReadLogDirectory(atadev, &gp_logdir, true)) gp_logdir_ok = true; } @@ -2064,7 +1990,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name); cfg.selftest = false; } - else if ((retval = SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)) < 0) { + else if ((retval = SelfTestErrorCount(atadev, name, cfg.firmwarebugs)) < 0) { PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name); cfg.selftest = false; } @@ -2084,7 +2010,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name); cfg.errorlog = false; } - else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false)) < 0) { + else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false)) < 0) { PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name); cfg.errorlog = false; } @@ -2094,12 +2020,13 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade if (cfg.xerrorlog) { int errcnt2; - if (!(cfg.permissive || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors))) { + if (!( cfg.permissive || cfg.firmwarebugs.is_set(BUG_NOLOGDIR) + || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors) )) { PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n", name); cfg.xerrorlog = false; } - else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true)) < 0) { + else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true)) < 0) { PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name); cfg.xerrorlog = false; } @@ -2235,7 +2162,7 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs UINT8 tBuf[64]; UINT8 inqBuf[96]; UINT8 vpdBuf[252]; - char lu_id[64]; + char lu_id[64], serial[256], vendor[40], model[40]; // Device must be open memset(inqBuf, 0, 96); @@ -2249,7 +2176,8 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs return 2; } } - version = inqBuf[2]; + version = (inqBuf[2] & 0x7f); /* Accept old ISO/IEC 9316:1995 variants */ + avail_len = inqBuf[4] + 5; len = (avail_len < req_len) ? avail_len : req_len; if (len < 36) { @@ -2266,28 +2194,50 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs "skip\n", device, pdt); return 2; } + + if (supported_vpd_pages_p) { + delete supported_vpd_pages_p; + supported_vpd_pages_p = NULL; + } + supported_vpd_pages_p = new supported_vpd_pages(scsidev); + lu_id[0] = '\0'; - if ((version >= 0x4) && (version < 0x8)) { - /* SPC-2 to SPC-5 */ - if (0 == (err = scsiInquiryVpd(scsidev, 0x83, vpdBuf, sizeof(vpdBuf)))) { + if ((version >= 0x3) && (version < 0x8)) { + /* SPC to SPC-5 */ + if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_DEVICE_IDENTIFICATION, + vpdBuf, sizeof(vpdBuf))) { len = vpdBuf[3]; scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL); } - } + } + serial[0] = '\0'; + if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_UNIT_SERIAL_NUMBER, + vpdBuf, sizeof(vpdBuf))) { + len = vpdBuf[3]; + vpdBuf[4 + len] = '\0'; + scsi_format_id_string(serial, (const unsigned char *)&vpdBuf[4], len); + } unsigned int lb_size; char si_str[64]; - uint64_t capacity = scsiGetSize(scsidev, &lb_size); + uint64_t capacity = scsiGetSize(scsidev, &lb_size, NULL); if (capacity) format_capacity(si_str, sizeof(si_str), capacity); else si_str[0] = '\0'; - PrintOut(LOG_INFO, "Device: %s, [%.8s %.16s %.4s]%s%s%s%s\n", - device, (char *)&inqBuf[8], (char *)&inqBuf[16], - (char *)&inqBuf[32], - (lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""), - (si_str[0] ? ", " : ""), (si_str[0] ? si_str : "")); + + // Format device id string for warning emails + cfg.dev_idinfo = strprintf("[%.8s %.16s %.4s]%s%s%s%s%s%s", + (char *)&inqBuf[8], (char *)&inqBuf[16], (char *)&inqBuf[32], + (lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""), + (serial[0] ? ", S/N: " : ""), (serial[0] ? serial : ""), + (si_str[0] ? ", " : ""), (si_str[0] ? si_str : "")); + + // format "model" string + scsi_format_id_string(vendor, (const unsigned char *)&inqBuf[8], 8); + scsi_format_id_string(model, (const unsigned char *)&inqBuf[16], 16); + PrintOut(LOG_INFO, "Device: %s, %s\n", device, cfg.dev_idinfo.c_str()); // check that device is ready for commands. IE stores its stuff on // the media. @@ -2343,6 +2293,18 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs case IE_LPAGE: state.SmartPageSupported = 1; break; + case READ_ERROR_COUNTER_LPAGE: + state.ReadECounterPageSupported = 1; + break; + case WRITE_ERROR_COUNTER_LPAGE: + state.WriteECounterPageSupported = 1; + break; + case VERIFY_ERROR_COUNTER_LPAGE: + state.VerifyECounterPageSupported = 1; + break; + case NON_MEDIUM_ERROR_LPAGE: + state.NonMediumErrorPageSupported = 1; + break; default: break; } @@ -2361,7 +2323,8 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); state.SuppressReport = 1; if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) { - PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", device); + PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", + device, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; } } @@ -2403,21 +2366,29 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs // tell user we are registering device PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device); - // TODO: Build file name for state file - if (!state_path_prefix.empty()) { - PrintOut(LOG_INFO, "Device: %s, persistence not yet supported for SCSI; ignoring -s option.\n", device); - } - // TODO: Build file name for attribute log file - if (!attrlog_path_prefix.empty()) { - PrintOut(LOG_INFO, "Device: %s, attribute log not yet supported for SCSI; ignoring -A option.\n", device); - } - // Make sure that init_standby_check() ignores SCSI devices cfg.offlinests_ns = cfg.selfteststs_ns = false; // close file descriptor CloseDevice(scsidev, device); + if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) { + // Build file name for state file + std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_'); + std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_'); + if (!state_path_prefix.empty()) { + cfg.state_file = strprintf("%s%s-%s-%s.scsi.state", state_path_prefix.c_str(), vendor, model, serial); + // Read previous state + if (read_dev_state(cfg.state_file.c_str(), state)) { + PrintOut(LOG_INFO, "Device: %s, state read from %s\n", device, cfg.state_file.c_str()); + // Copy ATA attribute values to temp state + state.update_temp_state(); + } + } + if (!attrlog_path_prefix.empty()) + cfg.attrlog_file = strprintf("%s%s-%s-%s.scsi.csv", attrlog_path_prefix.c_str(), vendor, model, serial); + } + finish_device_scan(cfg, state); return 0; @@ -2461,7 +2432,7 @@ static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int new // new failure. PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n", name, newh); - MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n", + MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d", name, newh); state.must_write = true; } @@ -2755,7 +2726,7 @@ static int DoATASelfTest(const dev_config & cfg, dev_state & state, ata_device * // If currently running a self-test, do not interrupt it to start another. if (15==(data.self_test_exec_status >> 4)) { - if (cfg.fix_firmwarebug == FIX_SAMSUNG3 && data.self_test_exec_status == 0xf0) { + if (cfg.firmwarebugs.is_set(BUG_SAMSUNG3) && data.self_test_exec_status == 0xf0) { PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest " "despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname); } else { @@ -2834,17 +2805,16 @@ static void check_pending(const dev_config & cfg, dev_state & state, s += strprintf(" (changed %+"PRId64")", rawval - prev_rawval); PrintOut(LOG_CRIT, "%s\n", s.c_str()); - MailWarning(cfg, state, mailtype, "%s\n", s.c_str()); + MailWarning(cfg, state, mailtype, "%s", s.c_str()); state.must_write = true; } // Format Temperature value -static const char * fmt_temp(unsigned char x, char * buf) +static const char * fmt_temp(unsigned char x, char (& buf)[20]) { if (!x) // unset - strcpy(buf, "??"); - else - sprintf(buf, "%u", x); + return "??"; + snprintf(buf, sizeof(buf), "%u", x); return buf; } @@ -2908,7 +2878,7 @@ static void CheckTemperature(const dev_config & cfg, dev_state & state, unsigned if (cfg.tempcrit && currtemp >= cfg.tempcrit) { PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); - MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", + MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)", cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); } else if (cfg.tempinfo && currtemp >= cfg.tempinfo) { @@ -2937,7 +2907,7 @@ static void check_attribute(const dev_config & cfg, dev_state & state, // If requested, check for usage attributes that have failed. if ( cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW && !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) { - std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs); + std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm); PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str()); MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str()); state.must_write = true; @@ -3004,7 +2974,7 @@ static void check_attribute(const dev_config & cfg, dev_state & state, // Format message std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s", cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id, - ata_get_smart_attr_name(attr.id, cfg.attribute_defs).c_str(), + ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm).c_str(), prevstr.c_str(), currstr.c_str()); // Report this change as critical ? @@ -3195,16 +3165,16 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device // check if number of selftest errors has increased (note: may also DECREASE) if (cfg.selftest) - CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)); + CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.firmwarebugs)); // check if number of ATA errors has increased if (cfg.errorlog || cfg.xerrorlog) { int errcnt1 = -1, errcnt2 = -1; if (cfg.errorlog) - errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false); + errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false); if (cfg.xerrorlog) - errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true); + errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true); // new number of errors is max of both logs int newc = (errcnt1 >= errcnt2 ? errcnt1 : errcnt2); @@ -3251,6 +3221,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic UINT8 asc, ascq; UINT8 currenttemp; UINT8 triptemp; + UINT8 tBuf[252]; const char * name = cfg.name.c_str(); const char *cp; @@ -3266,6 +3237,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic return 1; } else if (debugmode) PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name); + reset_warning_mail(cfg, state, 9, "open device worked again"); currenttemp = 0; asc = 0; ascq = 0; @@ -3283,6 +3255,8 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic if (cp) { PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp); + } else if (asc == 4 && ascq == 9) { + PrintOut(LOG_INFO,"Device: %s, self-test in progress\n", name); } else if (debugmode) PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n", name, (int)asc, (int)ascq); @@ -3290,7 +3264,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name); // check temperature limits - if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) + if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || !cfg.attrlog_file.empty()) CheckTemperature(cfg, state, currenttemp, triptemp); // check if number of selftest errors has increased (note: may also DECREASE) @@ -3302,6 +3276,29 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic if (testtype) DoSCSISelfTest(cfg, state, scsidev, testtype); } + if (!cfg.attrlog_file.empty()){ + // saving error counters to state + if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev, + READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { + scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter); + state.scsi_error_counters[0].found=1; + } + if (state.WriteECounterPageSupported && (0 == scsiLogSense(scsidev, + WRITE_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { + scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[1].errCounter); + state.scsi_error_counters[1].found=1; + } + if (state.VerifyECounterPageSupported && (0 == scsiLogSense(scsidev, + VERIFY_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { + scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[2].errCounter); + state.scsi_error_counters[2].found=1; + } + if (state.NonMediumErrorPageSupported && (0 == scsiLogSense(scsidev, + NON_MEDIUM_ERROR_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { + scsiDecodeNonMediumErrPage(tBuf, &state.scsi_nonmedium_error.nme); + state.scsi_nonmedium_error.found=1; + } + } CloseDevice(scsidev, name); return 0; } @@ -3561,7 +3558,8 @@ static void printoutvaliddirectiveargs(int priority, char d) PrintOut(priority, "use, ignore, show, showall"); break; case 'F': - PrintOut(priority, "none, samsung, samsung2, samsung3"); + PrintOut(priority, "%s", get_valid_firmwarebug_args()); + break; case 'e': PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], " "security-freeze, standby,[N|off], wcache,[on|off]"); @@ -3627,6 +3625,35 @@ static int Get3Integers(const char *arg, const char *name, const char *token, in } +#ifdef _WIN32 + +// Concatenate strtok() results if quoted with "..." +static const char * strtok_dequote(const char * delimiters) +{ + const char * t = strtok(0, delimiters); + if (!t || t[0] != '"') + return t; + + static std::string token; + token = t+1; + for (;;) { + t = strtok(0, delimiters); + if (!t || !*t) + return "\""; + token += ' '; + int len = strlen(t); + if (t[len-1] == '"') { + token += std::string(t, len-1); + break; + } + token += t; + } + return token.c_str(); +} + +#endif // _WIN32 + + // 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. @@ -3696,6 +3723,8 @@ static int ParseToken(char * token, dev_config & cfg) // specify the device type if ((arg = strtok(NULL, delim)) == NULL) { missingarg = 1; + } else if (!strcmp(arg, "ignore")) { + cfg.ignore = true; } else if (!strcmp(arg, "removable")) { cfg.removable = true; } else if (!strcmp(arg, "auto")) { @@ -3706,19 +3735,10 @@ static int ParseToken(char * token, dev_config & cfg) break; case 'F': // fix firmware bug - if ((arg = strtok(NULL, delim)) == NULL) { + if (!(arg = strtok(0, delim))) missingarg = 1; - } else if (!strcmp(arg, "none")) { - cfg.fix_firmwarebug = FIX_NONE; - } else if (!strcmp(arg, "samsung")) { - cfg.fix_firmwarebug = FIX_SAMSUNG; - } else if (!strcmp(arg, "samsung2")) { - cfg.fix_firmwarebug = FIX_SAMSUNG2; - } else if (!strcmp(arg, "samsung3")) { - cfg.fix_firmwarebug = FIX_SAMSUNG3; - } else { + else if (!parse_firmwarebug_def(arg, cfg.firmwarebugs)) badarg = 1; - } break; case 'H': // check SMART status @@ -3894,6 +3914,18 @@ static int ParseToken(char * token, dev_config & cfg) if (!cfg.emailaddress.empty()) PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", configfile, lineno, name, cfg.emailaddress.c_str()); +#ifdef _WIN32 + if ( !strcmp(arg, "msgbox") || !strcmp(arg, "sysmsgbox") + || str_starts_with(arg, "msgbox,") || str_starts_with(arg, "sysmsgbox,")) { + cfg.emailaddress = "console"; + const char * arg2 = strchr(arg, ','); + if (arg2) + cfg.emailaddress += arg2; + PrintOut(LOG_INFO, "File %s line %d (drive %s): Deprecated -m %s changed to -m %s\n", + configfile, lineno, name, arg, cfg.emailaddress.c_str()); + } + else +#endif cfg.emailaddress = arg; } break; @@ -3911,7 +3943,18 @@ static int ParseToken(char * token, dev_config & cfg) cfg.emailtest = 1; else if (!strcmp(arg, "exec")) { // Get the next argument (the command line) - if (!(arg = strtok(NULL, delim))) { +#ifdef _WIN32 + // Allow "/path name/with spaces/..." on Windows + arg = strtok_dequote(delim); + if (arg && arg[0] == '"') { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument: missing closing quote\n", + configfile, lineno, name, token); + return -1; + } +#else + arg = strtok(0, delim); +#endif + if (!arg) { 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; @@ -4166,7 +4209,7 @@ static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & defaul cfg.name.c_str(), cfg.lineno, configfile); return -2; } - // From here on the sign of is address.empty() and !cfg.emailcmdline.empty() + // From here on the sign of is cfg.emailaddress.empty() and !cfg.emailcmdline.empty() cfg.emailaddress.clear(); } @@ -4289,7 +4332,7 @@ static int ParseConfigFile(dev_config_vector & conf_entries) } // copy string so far into fullline, and increment length - strcpy(fullline+cont,line); + snprintf(fullline+cont, sizeof(fullline)-cont, "%s" ,line); cont+=len; // is this a continuation line. If so, replace \ by space and look at next line @@ -4350,16 +4393,19 @@ static void check_abs_path(char option, const std::string & path) // version/license/copyright messages static void ParseOpts(int argc, char **argv) { - // Init default configfile path + // Init default path names #ifndef _WIN32 configfile = SMARTMONTOOLS_SYSCONFDIR"/smartd.conf"; + warning_script = SMARTMONTOOLS_SYSCONFDIR"/smartd_warning.sh"; #else - static std::string configfile_str = get_exe_dir() + "/smartd.conf"; + std::string exedir = get_exe_dir(); + static std::string configfile_str = exedir + "/smartd.conf"; configfile = configfile_str.c_str(); + warning_script = exedir + "/smartd_warning.cmd"; #endif // Please update GetValidArgList() if you edit shortopts - static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:Vh?" + static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:w:Vh?" #ifdef HAVE_LIBCAP_NG "C" #endif @@ -4382,6 +4428,7 @@ static void ParseOpts(int argc, char **argv) { "savestates", required_argument, 0, 's' }, { "attributelog", required_argument, 0, 'A' }, { "drivedb", required_argument, 0, 'B' }, + { "warnexec", required_argument, 0, 'w' }, { "version", no_argument, 0, 'V' }, { "license", no_argument, 0, 'V' }, { "copyright", no_argument, 0, 'V' }, @@ -4543,6 +4590,9 @@ static void ParseOpts(int argc, char **argv) debugmode = savedebug; } break; + case 'w': + warning_script = optarg; + break; case 'V': // print version and CVS info debugmode = 1; @@ -4744,6 +4794,43 @@ static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_devic return conf_entries.size(); } +// Return true if TYPE contains a RAID drive number +static bool is_raid_type(const char * type) +{ + if (str_starts_with(type, "sat,")) + return false; + int i; + if (sscanf(type, "%*[^,],%d", &i) != 1) + return false; + return true; +} + +// Return true if DEV is already in DEVICES[0..NUMDEVS) or IGNORED[*] +static bool is_duplicate_device(const smart_device * dev, + const smart_device_list & devices, unsigned numdevs, + const dev_config_vector & ignored) +{ + const smart_device::device_info & info1 = dev->get_info(); + bool is_raid1 = is_raid_type(info1.dev_type.c_str()); + + for (unsigned i = 0; i < numdevs; i++) { + const smart_device::device_info & info2 = devices.at(i)->get_info(); + // -d TYPE options must match if RAID drive number is specified + if ( info1.dev_name == info2.dev_name + && ( info1.dev_type == info2.dev_type + || !is_raid1 || !is_raid_type(info2.dev_type.c_str()))) + return true; + } + + for (unsigned i = 0; i < ignored.size(); i++) { + const dev_config & cfg2 = ignored.at(i); + if ( info1.dev_name == cfg2.dev_name + && ( info1.dev_type == cfg2.dev_type + || !is_raid1 || !is_raid_type(cfg2.dev_type.c_str()))) + return true; + } + return false; +} // This function tries devices from conf_entries. Each one that can be // registered is moved onto the [ata|scsi]devices lists and removed @@ -4757,10 +4844,22 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device states.clear(); // Register entries + dev_config_vector ignored_entries; + unsigned numnoscan = 0; for (unsigned i = 0; i < conf_entries.size(); i++){ dev_config cfg = conf_entries[i]; + if (cfg.ignore) { + // Store for is_duplicate_device() check and ignore + PrintOut(LOG_INFO, "Device: %s%s%s%s, ignored\n", cfg.name.c_str(), + (!cfg.dev_type.empty() ? " [" : ""), + cfg.dev_type.c_str(), + (!cfg.dev_type.empty() ? "]" : "")); + ignored_entries.push_back(cfg); + continue; + } + // get device of appropriate type smart_device_auto_ptr dev; bool scanning = false; @@ -4768,8 +4867,15 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device // Device may already be detected during devicescan if (i < scanned_devs.size()) { dev = scanned_devs.release(i); - if (dev) + if (dev) { + // Check for a preceding non-DEVICESCAN entry for the same device + if ( (numnoscan || !ignored_entries.empty()) + && is_duplicate_device(dev.get(), devices, numnoscan, ignored_entries)) { + PrintOut(LOG_INFO, "Device: %s, duplicate, ignored\n", dev->get_info_name()); + continue; + } scanning = true; + } } if (!dev) { @@ -4834,6 +4940,8 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device configs.push_back(cfg); states.push_back(state); devices.push_back(dev); + if (!scanning) + numnoscan = devices.size(); } // if device is explictly listed and we can't register it, then // exit unless the user has specified that the device is removable diff --git a/smartd.initd.in b/smartd.initd.in index 7e0fe60..0c1e52c 100644 --- a/smartd.initd.in +++ b/smartd.initd.in @@ -2,7 +2,7 @@ # smartmontools init file for smartd # Copyright (C) 2002-8 Bruce Allen -# $Id: smartd.initd.in 3360 2011-06-06 19:25:36Z chrfranke $ +# $Id: smartd.initd.in 3727 2012-12-13 17:23:06Z samm2 $ # For RedHat and cousins: # chkconfig: 2345 40 40 @@ -27,8 +27,8 @@ # 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. +# example COPYING); if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 @@ -227,6 +227,15 @@ elif [ -f /etc/SuSE-release ] ; then if test -n "$SMARTD_DRIVEDB" ; then smartd_opts="$smartd_opts -B $SMARTD_DRIVEDB" fi + if test "$SMARTD_SAVESTATES" = "no" ; then + smartd_opts="$smartd_opts -s \"\"" + fi + if test "$SMARTD_ATTRLOG" = "no" ; then + smartd_opts="$smartd_opts -A \"\"" + fi + if test -n "$SMARTD_EXTRA_OPTS" ; then + smartd_opts="$smartd_opts $SMARTD_EXTRA_OPTS" + fi # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status @@ -269,7 +278,7 @@ elif [ -f /etc/SuSE-release ] ; then # We don't use startproc - we need to check for return code 17. if ! /sbin/checkproc $SMARTD_BIN ; then - $SMARTD_BIN $smartd_opts + eval $SMARTD_BIN$smartd_opts # Remember status and be verbose if test $? -ne 17 ; then rc_status -v diff --git a/smartd_warning.sh.in b/smartd_warning.sh.in new file mode 100644 index 0000000..a077255 --- /dev/null +++ b/smartd_warning.sh.in @@ -0,0 +1,209 @@ +#! /bin/sh +# +# smartd warning script +# +# Copyright (C) 2012-13 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: smartd_warning.sh.in 3809 2013-04-18 19:41:40Z chrfranke $ +# + +set -e + +# Set by config.status +PACKAGE="@PACKAGE@" +VERSION="@VERSION@" +prefix="@prefix@" +sysconfdir="@sysconfdir@" + +# Default mailer +os_mailer="@os_mailer@" + +# Plugin directory +plugindir="$sysconfdir/smartd_warning.d" + +# Parse options +dryrun= +case $1 in + --dryrun) dryrun=t; shift ;; +esac + +if [ $# != 0 ]; then + cat <&2 + exit 1 +fi + +# Get host and domain names +for cmd in @os_hostname@ 'echo "[Unknown]"'; do + hostname=`eval $cmd 2>/dev/null` || continue + test -n "$hostname" || continue + break +done + +dnsdomain=${hostname#*.} +if [ "$dnsdomain" != "$hostname" ]; then + # hostname command printed FQDN + hostname=${hostname%%.*} +else + for cmd in @os_dnsdomainname@ 'echo'; do + dnsdomain=`eval $cmd 2>/dev/null` || continue + break + done + test "$dnsdomain" != "(none)" || dnsdomain= +fi + +for cmd in @os_nisdomainname@ 'echo'; do + nisdomain=`eval $cmd 2>/dev/null` || continue + break +done +test "$nisdomain" != "(none)" || nisdomain= + +# Format subject +export SMARTD_SUBJECT="SMART error (${SMARTD_FAILTYPE-[SMARTD_FAILTYPE]}) detected on host: $hostname" + +# Format message +fullmessage=` + echo "This message was generated by the smartd daemon running on:" + echo + echo " host name: $hostname" + echo " DNS domain: ${dnsdomain:-[Empty]}" + test -z "$nisdomain" || + echo " NIS domain: $nisdomain" + @OS_WIN32_TRUE@test -z "$USERDOMAIN" || + @OS_WIN32_TRUE@ echo " Win domain: $USERDOMAIN" + echo + echo "The following warning/error was logged by the smartd daemon:" + echo + echo "${SMARTD_MESSAGE-[SMARTD_MESSAGE]}" + echo + echo "Device info:" + echo "${SMARTD_DEVICEINFO-[SMARTD_DEVICEINFO]}" + echo + echo "For details see host's SYSLOG." + if [ "$SMARTD_FAILTYPE" != "EmailTest" ]; then + echo + echo "You can also use the smartctl utility for further investigation." + test "$SMARTD_PREVCNT" = "0" || + echo "The original message about this issue was sent at ${SMARTD_TFIRST-[SMARTD_TFIRST]}" + case $SMARTD_NEXTDAYS in + '') echo "No additional messages about this problem will be sent." ;; + 1) echo "Another message will be sent in 24 hours if the problem persists." ;; + *) echo "Another message will be sent in $SMARTD_NEXTDAYS days if the problem persists." ;; + esac + fi +` + +# Export message with trailing newline +export SMARTD_FULLMESSAGE="$fullmessage +" + +# Run plugin scripts if requested +case " $SMARTD_ADDRESS" in + *\ @*) + if [ -n "$dryrun" ]; then + echo "export SMARTD_SUBJECT='$SMARTD_SUBJECT'" + echo "export SMARTD_FULLMESSAGE='$SMARTD_FULLMESSAGE'" + fi + + # Run ALL scripts if requested + case " $SMARTD_ADDRESS " in + *\ @ALL\ *) + for cmd in "$plugindir"/*; do + if [ -f "$cmd" ] && [ -x "$cmd" ]; then + if [ -n "$dryrun" ]; then + echo "$cmd &2 + fi + ;; + *) + SMARTD_ADDRESS="${SMARTD_ADDRESS:+ }$ad" + ;; + esac + done + + # Send email to remaining addresses + test -n "$SMARTD_ADDRESS" || exit 0 + ;; +esac + +# Send mail or run command +if [ -n "$SMARTD_ADDRESS" ]; then + + # Send mail, use platform mailer by default + test -n "$SMARTD_MAILER" || SMARTD_MAILER=$os_mailer + if [ -n "$dryrun" ]; then + echo "exec '$SMARTD_MAILER' -s '$SMARTD_SUBJECT' $SMARTD_ADDRESS < - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 Christian Franke * Copyright (C) 2000 Michael Cornwell * * This program is free software; you can redistribute it and/or modify @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -53,7 +52,7 @@ #include "atacmds.h" #include "dev_interface.h" -const char * utility_cpp_cvsid = "$Id: utility.cpp 3500 2012-01-01 18:03:36Z chrfranke $" +const char * utility_cpp_cvsid = "$Id: utility.cpp 3739 2013-01-01 16:32:48Z chrfranke $" UTILITY_H_CVSID INT64_H_CVSID; const char * packet_types[] = { @@ -91,7 +90,7 @@ std::string format_version_info(const char * prog_name, bool full /*= false*/) "(build date "__DATE__")" // checkout without expansion of Id keywords #endif " [%s] "BUILD_INFO"\n" - "Copyright (C) 2002-12 by Bruce Allen, http://smartmontools.sourceforge.net\n", + "Copyright (C) 2002-13, Bruce Allen, Christian Franke, www.smartmontools.org\n", prog_name, smi()->get_os_version_str().c_str() ); if (!full) @@ -503,27 +502,6 @@ int split_report_arg(char *s, int *i) 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) // Functionality reduced to requirements of smartd and split_selective_arg(). @@ -617,6 +595,8 @@ int split_selective_arg(char *s, uint64_t *start, return 0; } } + + errno = 0; *stop = strtoull(s+1, &tailptr, 0); if (errno || *tailptr != '\0') return 1; @@ -704,33 +684,6 @@ bool nonempty(const void * data, int size) return false; } - -// 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){ - 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); - } - - sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec); - return; -} - // Format integer with thousands separator const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, const char * thousands_sep /* = 0 */) diff --git a/utility.h b/utility.h index 13d5ac9..72764ee 100644 --- a/utility.h +++ b/utility.h @@ -13,8 +13,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -26,7 +25,7 @@ #ifndef UTILITY_H_ #define UTILITY_H_ -#define UTILITY_H_CVSID "$Id: utility.h 3558 2012-06-05 16:42:05Z chrfranke $" +#define UTILITY_H_CVSID "$Id: utility.h 3719 2012-12-03 21:19:33Z chrfranke $" #include #include // for regex.h (according to POSIX) @@ -95,8 +94,6 @@ void syserror(const char *message); // Function for processing -r option in smartctl and smartd int split_report_arg(char *s, int *i); -// Function for processing -c option in smartctl and smartd -int split_report_arg2(char *s, int *i); // Function for processing -t selective... option in smartctl int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode); @@ -183,9 +180,6 @@ bool nonempty(const void * data, int size); // needed to fix glibc bug void FixGlibcTimeZoneBug(); -// convert time in msec to a text string -void MsecToText(unsigned int msec, char *txt); - // Format integer with thousands separator const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, const char * thousands_sep = 0); -- 2.39.2