From 832b75ede04683f4f493716791e44174e2d3694d Mon Sep 17 00:00:00 2001 From: Guido Guenther Date: Sun, 23 Apr 2006 17:49:33 +0200 Subject: [PATCH 1/1] import upstream version 5.36 --- AUTHORS | 32 + CHANGELOG | 2138 +++++++ COPYING | 340 + INSTALL | 637 ++ Makefile.am | 436 ++ Makefile.in | 1311 ++++ NEWS | 230 + README | 125 + TODO | 142 + WARNINGS | 132 + aclocal.m4 | 1116 ++++ atacmdnames.c | 520 ++ atacmdnames.h | 32 + atacmds.c | 1887 ++++++ atacmds.h | 537 ++ ataprint.c | 1864 ++++++ ataprint.h | 61 + autogen.sh | 88 + config.guess | 1450 +++++ config.h.in | 148 + config.sub | 1555 +++++ configure | 8268 +++++++++++++++++++++++++ configure.in | 220 + depcomp | 522 ++ examplescripts/Example1 | 44 + examplescripts/Example2 | 22 + examplescripts/Example3 | 25 + examplescripts/Makefile.am | 10 + examplescripts/Makefile.in | 369 ++ examplescripts/README | 50 + extern.h | 102 + install-sh | 323 + int64.h | 94 + knowndrives.c | 1258 ++++ knowndrives.h | 88 + missing | 353 ++ os_darwin.c | 404 ++ os_darwin.h | 31 + os_darwin/English_Localizable.strings | 12 + os_darwin/SMART.in | 56 + os_darwin/StartupParameters.plist | 5 + os_freebsd.c | 1069 ++++ os_freebsd.h | 582 ++ os_generic.c | 255 + os_generic.h | 35 + os_linux.c | 1527 +++++ os_linux.h | 390 ++ os_netbsd.c | 447 ++ os_netbsd.h | 55 + os_openbsd.c | 448 ++ os_openbsd.h | 55 + os_solaris.c | 434 ++ os_solaris.h | 57 + os_solaris_ata.s | 697 +++ os_win32.c | 1619 +++++ os_win32/daemon_win32.c | 1266 ++++ os_win32/daemon_win32.h | 71 + os_win32/hostname_win32.c | 186 + os_win32/hostname_win32.h | 27 + os_win32/syslog.h | 62 + os_win32/syslog_win32.c | 435 ++ posix/regcomp.c | 3544 +++++++++++ posix/regex.c | 61 + posix/regex.h | 574 ++ posix/regex_internal.c | 1263 ++++ posix/regex_internal.h | 742 +++ posix/regexec.c | 3977 ++++++++++++ scsicmds.c | 2097 +++++++ scsicmds.h | 355 ++ scsiprint.c | 1233 ++++ scsiprint.h | 37 + smartctl.8.in | 1290 ++++ smartctl.c | 896 +++ smartctl.h | 74 + smartd.8.in | 1844 ++++++ smartd.c | 4127 ++++++++++++ smartd.conf | 102 + smartd.conf.5.in | 1253 ++++ smartd.h | 291 + smartd.initd.in | 478 ++ smartmontools.spec | 1821 ++++++ utility.c | 681 ++ utility.h | 179 + 83 files changed, 63673 insertions(+) create mode 100644 AUTHORS create mode 100644 CHANGELOG create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 WARNINGS create mode 100644 aclocal.m4 create mode 100644 atacmdnames.c create mode 100644 atacmdnames.h create mode 100644 atacmds.c create mode 100644 atacmds.h create mode 100644 ataprint.c create mode 100644 ataprint.h create mode 100755 autogen.sh create mode 100755 config.guess create mode 100644 config.h.in create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.in create mode 100755 depcomp create mode 100755 examplescripts/Example1 create mode 100755 examplescripts/Example2 create mode 100755 examplescripts/Example3 create mode 100644 examplescripts/Makefile.am create mode 100644 examplescripts/Makefile.in create mode 100644 examplescripts/README create mode 100644 extern.h create mode 100755 install-sh create mode 100644 int64.h create mode 100644 knowndrives.c create mode 100644 knowndrives.h create mode 100755 missing create mode 100644 os_darwin.c create mode 100644 os_darwin.h create mode 100644 os_darwin/English_Localizable.strings create mode 100644 os_darwin/SMART.in create mode 100644 os_darwin/StartupParameters.plist create mode 100644 os_freebsd.c create mode 100644 os_freebsd.h create mode 100644 os_generic.c create mode 100644 os_generic.h create mode 100644 os_linux.c create mode 100644 os_linux.h create mode 100644 os_netbsd.c create mode 100644 os_netbsd.h create mode 100644 os_openbsd.c create mode 100644 os_openbsd.h create mode 100644 os_solaris.c create mode 100644 os_solaris.h create mode 100644 os_solaris_ata.s create mode 100644 os_win32.c create mode 100644 os_win32/daemon_win32.c create mode 100644 os_win32/daemon_win32.h create mode 100644 os_win32/hostname_win32.c create mode 100644 os_win32/hostname_win32.h create mode 100644 os_win32/syslog.h create mode 100644 os_win32/syslog_win32.c create mode 100644 posix/regcomp.c create mode 100644 posix/regex.c create mode 100644 posix/regex.h create mode 100644 posix/regex_internal.c create mode 100644 posix/regex_internal.h create mode 100644 posix/regexec.c create mode 100644 scsicmds.c create mode 100644 scsicmds.h create mode 100644 scsiprint.c create mode 100644 scsiprint.h create mode 100644 smartctl.8.in create mode 100644 smartctl.c create mode 100644 smartctl.h create mode 100644 smartd.8.in create mode 100644 smartd.c create mode 100644 smartd.conf create mode 100644 smartd.conf.5.in create mode 100644 smartd.h create mode 100755 smartd.initd.in create mode 100644 smartmontools.spec create mode 100644 utility.c create mode 100644 utility.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1acac05 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,32 @@ +$Id: AUTHORS,v 1.15 2004/11/11 17:56:07 ydario Exp $ + +This code was originally developed as a Senior Thesis by Michael +Cornwell at the Concurrent Systems Laboratory (now part of the Storage +Systems Research Center), Jack Baskin School of Engineering, University +of California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + +This package is meant to be an up-to-date replacement for the +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 +David Snyder +Sergey Svishchev +Phil Williams +Richard Zybert +Yuri Dario diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..7857883 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,2138 @@ +CHANGELOG for smartmontools + +$Id: CHANGELOG,v 1.537 2006/04/12 16:11:44 ballen4705 Exp $ + +The most recent version of this file is: +http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?sortby=date&view=markup + +Maintainers / Developers Key: +[BA] Bruce Allen +[EB] Erik Inge Bolsø +[SB] Stanislav Brabec +[PC] Peter Cassidy +[YD] Yuri Dario +[CD] Casper Dik +[CF] Christian Franke +[GF] Guilhem Frézou +[DG] Douglas Gilbert +[GG] Guido Guenther +[GK] Geoff Keating +[DK] Dr. David Kirkby +[KM] Kai Mäkisara +[EM] Eduard Martinescu +[FM] Frédéric L. W. Meunier +[KS] Keiji Sawada +[DS] David Snyder +[SS] Sergey Svishchev +[PW] Phil Williams +[LW] Leon Woestenberg +[RZ] Richard Zybert + +NOTES FOR FUTURE RELEASES: see TODO file. + + + +smartmontools 5.36 Stable Release + + [BA] Linux: smartd/smartctl issue syntax hints to user if 3ware + disk controller present with EITHER 3ware OR AMCC vendor + name, and user syntax incorrect. + + [BA] Update copyright dates to 2006. + + [DG] [SCSI] Loosen sanity check on Seagate/Hitachi factory information + log page so it is not skipped on recent Seagate SCSI disks. + + [CF] Added command 'smartd -q showtests' to list test schedules. + + [CF] Added command 'smartctl -P showall MODEL [FIRMWARE]' to list + database entries for specific drives and firmware. + + [PW] Automatically set -v 9,minutes and -v 194,unknown for Maxtor + DiamondMax D540X-4G drives. + + [DG] [SCSI] suppress various outputs when data fails sanity checks. + Correct 'last n error events' log page indexing. + + [DG] [SCSI] changed smartctl exit status to reflect any problems in + the most recent 20 self test logs [Leandro Santi] + + [DG] [SCSI] Fix process return value when scsiGetSmartData() fails + in smartctl [Leandro Santi] + + [BA] Updated docs and error message to reflect Linux libata + support for smartmontools starting with the 2.6.15 kernel + series. Also init script support for the 'tinysofa' release. + + [DG] [SCSI] Mask dpofua bit when changing mode pages. Fix failure + of 'smartctl -l error'. + + [EM] Fixed a problem with FreeBSD and 3Ware 'twe' devices + + [CF] Fixed a regexp in knowndrives table, added regexp syntax check + via 'smartctl -P showall'. + + [CF] Cygwin & Windows: Fixed memory leak in function calling + IOCTL_IDE_PASS_THROUGH. Thanks to Fred Schmidt for the problem + report. + + [CF] Cygwin: added cygrunsrv support and commands "install", "remove" + and "status" to smartd.initd. + + [SS] Fix runtime problems on big-engian NetBSD platforms (patch provided + by Martin Husemann) + + [CF] Cygwin smartd: Open smartd.conf in textmode to allow use of + Windows editors. + + [CF] Cygwin smartd: Added option '--service' to allow smartd running + as windows service via cygrunsrv. Useful in conjunction with new + syslogd support added in Cygwin 1.5.15. + + [CF] Windows: Added patch to avoid output of non-ascii timezone names. + + [EM] Incorporate various patches to provide TWE support and support for + multiple 3Ware cards, Power Check Support, and FreeBSD 6.x support. + Thanks to Rudolf Cejka, Frank Behrens, and Jung-uk Kim. + + [DG] Silence gcc 4.0.1 compile warning concerning the difference in + "signedness" in pointer assignments. Changes to SCSI code + and os_linux.c . + + [PW] Additions to knowndrives table: added missing drive from Quantum + Fireball Plus LM series, added QUANTUM BIGFOOT TS10.0A, added + ExcelStor J680 and J880, added Western Digital Caviar RE Serial ATA + series, added missing drives from Western Digital Caviar SE series, + added Seagate Momentus 4200.2 series, added missing drives from + Maxtor DiamondMax 10 series, added Fujitsu MHG and MHH series, and + added Hitachi Travelstar 5K100 series. + + [PW] Additions to knowndrives table: added Fujitsu MHU2100AT, added + Fujitsu M1623TAU, added missing drives from Seagate Barracuda + 7200.8 series, added Seagate Momentus 5400.2 series, and added + QUANTUM FIREBALL CR8.4A. + + [PW] Additions to knowndrives table: added missing drive from Maxtor + MaxLine II series, added Maxtor DiamondMax 2880 Ultra ATA series, + added Maxtor DiamondMax 17 VL series, added Hitachi Deskstar 7K80 + series, and added Hitachi Deskstar 7K400 series. + + [CF] Windows: Fixed unsupported 'smartctl -X' on Win2000/XP by using + IOCTL_IDE_PASS_THROUGH instead. + +smartmontools 5.34 Stable Release [NOTE: never officially released] + + [CF] Cygwin & Windows smartd: Increased SCSI DEVICESCAN range + from ASPI adapter 0-3 to 0-9. Added diagnostic messages. + + [CF] Windows smartd: Added ability to run .bat files via '-M exec' + directive. + + [CF] Cygwin smartd: Added FreeConsole() after fork() to avoid hang + of terminated shell console window. + + [DG] [SCSI] Add code so 'smartctl -A' outputs the number of elements + in the grown defect list. When this number is increasing a + disk has problems. N.B. Similar logic should be added to smartd. + + [CF] Windows smartd: Fixed event handling to allow start of another + smartd process when service is already running. Useful for testing + service configuration changes in debug mode. + + [PW] Added following drives to knowndrives table: Western Digital Raptor + family, Seagate Barracuda 7200.8 family, Maxtor DiamondMax 2160 + Ultra ATA and DiamondMax 10 families, Hitachi Travelstar E7K60 + family, Seagate Medalist 17240, 13030, 10231, 8420, and 4310, + TOSHIBA MK4018GAP and MK6022GAX, ExcelStor Technology J360, and + Western Digital Caviar AC14300. + + [PW] Added missing Fujitsu MHTxxxxAT and Seagate Barracuda 7200.7 drives + to knowndrives table. + + [PW] Added QUANTUM FIREBALLP LM10.2 to knowndrives table. Thanks to + Mike Fleetwood for submitting the patch. + + [KS] Solaris/SPARC: fixed not to disable automatic offline test and + automatic save attributes incorrectly. Thanks to Roy Badami. + + [BA] Linux: smartd init script now recognizes 'trustix' distro. + + [DG] [SCSI] Medium and hardware errors were slipping through + unreported. Fix linux SCSI sense reporting via SG_IO ioctl. + + [DG] [SCSI] Change lba of first failure in selftest output to + decimal (was hex) to conform with ATA output. + + [GK] smartd: Detect most self-test failures even if the hour counter + has wrapped. + + [BA] smartctl: list 'marvell' as option if user give invalid + -d argument + + [CF] Windows: fixed SCSI timeout handling to allow long timeouts + for selftests. + + [CF] Fixed buffer overflow issues in printone() and safe_vsnprintf() + which results in crash on -V option (at least on Windows). + + [DG] [SCSI] Add explicit timeouts to INQUIRY and REQUEST SENSE (that + were missed in an earlier patch). Could have impacted freebsd. + + [DG] When linux detects a sata_via_libata disk suggest that user try + '-d ata' (rather then '-d libata). Anticipate kernel change. + + [YD] Added OS/2 and eComStation platform support. + + [PW] Added Seagate U4 family, Fujitsu MHJ and MHK families, Seagate + Barracuda 5400.1, QUANTUM FIREBALLP KX27.3, QUANTUM FIREBALLP KA10.1, + and ExcelStor J340 to knowndrives table. + + [DG] [SCSI] After report of Hitachi IC35L073UCDY10 disks locking up + on log page 0x7 (last n error events), check log page (and some + others) is supported (via log page 0x0) before probing. + + [CF] Added safe_v?snprintf() for platforms using v?snprintf() + with non standard behaviour on overflow (Windows, old Linux) + + [CF] smartd: Added message if check power mode spins up disk. + + [CF] Windows: Added support for READ_LOG on WinNT4 using undocumented + pseudo SCSI command via IOCTL_SCSI_PASS_THROUGH. + + [CF] smartd: Added ',q' option for '-n' directive to suppress 'skipping + checks' log message. This prevents a laptop disk from spinning up + due to this message. Thanks to Rob MacLachlan and Manfred Schwarb + for pointing out problem & solution. + + [CF] Windows: Added function get_os_version_str() to show OS flavor in + copyright message. + + [CF] Windows: Added function ata_identify_is_cached() to check for outdated + SMART enabled bit in identify data. + + [CF] Windows: Added fix to prevent linkage of smartd specific win32 modules + to smartctl. + + [PW] Added Fujitsu MPG3153AH, Hitachi Endurastar J4K20/N4K20 (formerly + DK23FA-20J), Seagate Momentus family, and Maxtor Fireball 3 family + to knowndrives table. + + [PW] Added missing Maxtor DiamondMax 16, Seagate Barracuda ATA IV, and + Western Digital Caviar WDxxxAA/WDxxxBA drives to knowndrives table. + + [CF] Windows: Added ATA check power mode for smartd -n directive. + + [CF] Windows: Fixed use of new service status flag which causes hang + of smartd service on WinNT4. + + [CF] Windows: Fixed error checking of IOCTL_IDE_PASS_THROUGH (used + for READ_LOG on 2000/XP). Added some diagnostic messages on + -r ataioctl[,2]. Thanks to Manfred Schwarb for bug report and testing. + + [BA] Fixed code bug that made it impossible to enable SMART on + disks with failing health status. This would happen if the + os_*.c author made STATUS and STATUS_CHECK work the same way. + I have corrected this at a higher level; we now handle the + case where STATUS and STATUS_CHECK are identical without + issues. + + [LW] Make os_linux.c/marvell_command_interface() always return 0 on STATUS. + Needed for a disk having bad SMART status. + + [CF] smartctl: Added drive family printing. + + [CF] autogen.sh: Allow automake 1.9, added message if automake + version is unknown. + + [BA] smartctl: use locale-specific separators for printing disk + capacity. Also use AC_CHECK_HEADERS not AC_CHECK_HEADER in + configure.in. + + [BA] clean-up of #include structure so that -V options to smartd + and smartctl work correctly. Please, don't #include header + files into other header files. + +smartmontools 5.33 Experimental Release + + [BA] smartctl: ATA disks, if SMART ATTRIBUTE THRESHOLDS page has ID + errors with some Attributes having NULL IDs, print Attribute + info anyway (but issuing a warning to the user). + + [DG] [SCSI] Decode Last n error events log page; decode track following + and positioning errors [Hitachi] + + [EM] FreeBSD: another tweak, __packed__ introduced in Version 5.0040 + + [EM] Cleaner tweak of fixes for FreeBSD 4.x. + + [EM] Fix compilation errors under FreeBSD 4.x, as it is still using + and old GCC + + [EM] Remove 3ware/FreeBSD specific files and just include pieces we need + + [DG] Add logic in smartd to detect 3ware, Marvell controllers and SATA + disks behind an ATA-SCSI simulator (in Linux). If specific device + types are not given and they are picked in a general SCSI device + scan then warn and skip. + + [GG] insert correct path to smartd into smartd's init script + + [BA] Changed all default paths in documentation to reflect /usr/local as + default path prefix. This affects on-line man pages, primarily. + + [DS] Added support for OpenBSD + + [BA] Added another environment variable SMART_FULLMESSAGE set by + the smartd mailing feature, and modified examplescripts/Example1 + to illustrate it. + + [BA] Fixed potentially misleading messages of the form: + XXX failed: success + + [DG] emit warning if SATA disk detected using libata in Linux; then exit + + [PW] Added Seagate U10 family, Hitachi Travelstar 7K60, Fujitsu MHR2020AT, + and QUANTUM FIREBALLP AS20.5 to knowndrives table. + + [DG] Detect 3ware and Marvell controllers from SCSI INQUIRY vendor string + and suggest usage of appropriate '-d' argument in smartctl. + + [LW] Tested the RELEASE_5_33_WITH_MARVELL_SUPPORT branch on + actual Marvell 88SX5041 hardware, with success. + Merged into HEAD. + + [BA] Fixed nasty DEVICESCAN bug + + [BA] Checked in RELEASE_5_33_WITH_MARVELL_SUPPORT branch with + some Marvell support. + + [BA] Additional modifications of Ed's controller scheme. Fixed + broken 3ware support under linux, problems with scanning + devices in smartd, and other small problems. + + [CF] Added make targets to build formatted man pages (htmlman, txtman), + Windows distribution (dist-win32) and MSVC6 config.h (config-vc6). + + [EM] Minor change to FreeBSD inclusion of 'twe' include files. Add + code to check if they exising in /usr/include/sys to use those + in preference to ones added here + + [EM] Very preliminary support attempt for 3Ware controllers under + FreeBSD. Also, switched 'escalade_type/escalade_port' to + 'controler_type/controller_port' and moved away from + 'tryata/tryscsi' to using new 'controller*' variables to + determine which controller type (ATA/SCSI/3Ware) to use. + + [GK] Added initscript support for Darwin. + + [CF] Windows smartd: Added ability to run smartd as a windows service, + including new commands "smartd install ..." and "smartd remove" + to install and remove the service registry entry. + + [BA] smartd: warn user if -s regexp regular expression contains + characters other than 0123456789.*()|+?[-]{}:=SLCO since such + characters are 'suspicous' and may indicate a poorly formed + regexp. Extended regular expression gurus: can this list be + reduced somewhat? + + [CF] Fixed bug in Windows smartd: Missing close of config file when + configuration is reloaded by smartd daemon. + + [CF] Windows smartd: Added mail warning feature using the "Blat" + (http://blat.sourceforge.net/) mailer as a default. + + [PW] Added Maxtor DiamondMax Plus 5120 Ultra ATA 33 series and TOSHIBA + MK3017GAP to knowndrives table. + + [CF] Added fixes to build smartmontools on old Linux systems + (libc < 6, Kernel 2.0.x). + + [BA] Added ATA minor version identity strings for latest ATA specification + updates: ATA/ATAPI-7 T13 1532D revision 4a and ATA/ATAPI-6 published, + ANSI INCITS 361-2002 + + [PW] Added Hitachi Travelstar 5K80 family and Fujitsu MHTxxxxAH family to + knowndrives table. + + [EM] Fix up compilation under FreeBSD < 5.x + + [PW] Added QUANTUM FIREBALL EX3.2A and missing Western Digital Caviar SE + drives to knowndrives table. + + [BA] Modified Hitachi Travelstar 80GN family regexp in drive database. + Thanks to [GK/CF] for problem & solution. + + [GK] Added os_darwin.[ch] + + [PW] Added the following drives to the knowndrives table: IBM Travelstar + 48GH, 30GN, and 15GN family; IBM Deskstar 37GP and 34GXP family; + Western Digital WDC WD272AA; Maxtor DiamondMax D540X-4D family; + TOSHIBA MK2016GAP, MK2018GAP, MK2018GAS, MK2023GAS; and + QUANTUM FIREBALL ST3.2A + + [BA] smartd/smarctl now print build HOST/OS information as part + of startup slogan. This should make it slightly easier to + read bug reports from users. + + [RZ] Fixed the DEVICESCAN to do what it was supposed to do - give + error message unless scanning is in progress. + + [BA] Update documentation to describe 3ware character devices. Better + error detection for missing/malfunctioning devices behind 3ware + controllers. Now pack 3ware ioctl structures explicitly. + + [BA] For ATA devices that support LBA mode, print capacity as part + of smartctl --info + + [RZ] Made DEVICESCAN quiet about non-existing devices unless debug + is on. + + [DG] treat "unit attention" SCSI warning as try again in some contexts + (test unit ready and mode sense) + + [BA] on drives that store max/min rather than min/max, get order + correct in printing temp. + + [BA] fixed typo in 'smartctl -h' output. Thanks to Gabor Z. Papp. + + [BA] linux: clean-up to 3ware/AMCC support; dynamically create + or fix /dev/tw[ae][0-15] device node entries if they don't + exist or are incorrect. One can now use the character devices + /dev/twe[0-15] OR /dev/sd? for 3ware 6000/7000/8000 series + cards. One must use /dev/twa[0-15] for 3ware 9000 series cards. + Note that selective self-tests now work via /dev/tw[ae] devices. + Next step: documentation. + + [BA] linux: experimental "support" for 3ware/AMCC 9000 series + controllers that use the 3w-9xxx driver. This will be in a + state of flux for a few days. Note that this requires the + character interface /dev/twa[0-15]. + + [DG] linux: extend general SCSI OS interface to use the SG_IO ioctl. If + not available, use the older SCSI_IOCTL_SEND_COMMAND ioctl. + + [KS] Solaris/x86: fixed system identification problem in configure + script. Thanks to Stuart Swales. + +smartmontools 5.32 + + [BA] Update link to revised/updated IBM Deskstar Firmware + + [CF] Cygwin & Windows: Added missing ASPI manager initialization + with GetASPI32SupportInfo(). Thanks to Nikolai SAOUKH for pointing + this out and providing a patch. + + [BA] modified smartd init script to work on whitebox (thanks to + Michael Falzon) + + [BA] removed (reverted) additional Attribute definitions from + http://smart.friko.pl/attributes.php. All (or most?) of these + appear to be return code values for the WD Digital Life Guard Utility. + + [PW] Added Seagate Medalist 17242, 13032, 10232, 8422, and 4312 to + knowndrives table. Added missing Seagate U Series 5 drives. + + [PW] Added the following QUANTUM models to knowndrives table: + FIREBALL EX6.4A, FIREBALLP AS10.2, FIREBALLP AS40.0, FIREBALL CR4.3A, + FIREBALLP LM15, FIREBALLP LM30, and FIREBALLlct20 30 + + [PW] Added missing Western Digital Protege drives to knowndrives table. + + [PW] Added Maxtor DiamondMax 40 ATA 66 series and DiamondMax 40 VL Ultra + ATA 100 series to knowndrives table. + + [PW] Added the following Hitachi/IBM drives to knowndrives table: + HITACHI_DK14FA-20B, Travelstar 40GNX series, Travelstar 4LP series, + and Travelstar DK23XXB series. Added the missing Travelstar 80GN + drives. + + [PW] Added Fujitsu MPB series and MPG series to knowndrives table. Added + the missing Fujitsu MHSxxxxAT drives. + + [KS] Solaris: added workaround for dynamic change of time-zone. + + [KS] Solaris: fixed problem that autogen.sh cannot detect absence of + auto* tools. + + [BA] smartd: added time-zone bug information to man page. + Reverted CF code for _WIN32 case. + + [CF] Cygwin & Windows: Added better error messages on IDE/ATA device + open error. + + [BA] added additional Attribute definitions from + http://smart.friko.pl/attributes.php + + [BA] smartd: reworked TimeZone bug workaround so it is only invoked + for glibc. Note: this might not be right -- a similar bug may + exist in other platform's libcs. + + [DG] SCSI smartmontools documentation updated [2004/5/6]. See: + http://smartmontools.sourceforge.net/smartmontools_scsi.html + + [CF] Windows: Fixed reset of TZ=GMT in glibc timezone bug workaround. + +smartmontools 5.31 + + [DG] move SCSI device temperature and start-stop log page output + (smartctl) into --attributes section (was in --info section). + + [GG] change default installation location to /usr/local + + [CF] Cygwin smartd: Fixed crash on access of SCSI devices after fork(). + + [PW] Added TOSHIBA MK4018GAS and the following Maxtor drive families + to knowndrives table: DiamondMax D540X-4G, Fireball 541DX, + DiamondMax 3400 Ultra ATA, DiamondMax Plus 6800 Ultra ATA 66. + + [PW] Added missing Maxtor DiamondMax 16, DiamondMax D540X-4K, and + DiamondMax Plus 45 Ulta ATA 100 drives to knowndrives table. + + [PW] Added ExcelStor J240, Hitachi Travelstar 80GN family, Fujitsu + MHTxxxxAT family, and IBM Deskstar 25GP and 22GXP families to + knowndrives table. + + [CF] Cygwin smartd: Added workaround for missing SIGQUIT via keyboard: + To exit smartd in debug mode, type CONTROL-C twice. + + [BA] smartctl: printing of the selective self-test log is now + controlled by a new option: -l selective + + [BA] Added entries for Samsung firmware versions -25 to -39 based + on latest info about firmware bug fixes. + + [PW] Added Seagate U Series X family, Seagate U8 family, and Seagate + Medalist 8641 family to knowndrives table. + + [CF] smartd: Added exit values 5/6 for missing/unreadable config file. + + [BA] smartd: now monitor the Current Pending Sector count (Attribute 197) + and the Offline Pending Sector Count (Attribute 198). Log a + warning (and send an email, if so configured) if the raw count + is nonzero. These are controlled by new Directives: -C and -U. + Currently they are enabled by default. + + [CF] Added option -c FILE, --configfile=FILE to smartd to specify + an alternate configuration FILE or '-' for standard input. + + [KS] configure.in now searches for -lnsl and -lsocket for Solaris. + + [CF] Win32/native smartd: Added thread to combine several syslog output + lines into one single event log entry. + + [CF] Win32 smartd: Added DEVICESCAN for SCSI/ASPI devices. + + [GG] Use gethostbyname() the get the DNS domain since getdomainname() + returns the NIS domain when sending mails from smartd. + + [GG] smartd.init.in: pass smartd_opts to smartd on startup, read distribution + specific configuration files if found + + [SS] smartctl: added NetBSD support for Selective Self-tests. + + [BA] smartd.conf example configuration file now has all examples + commented out except for 'DEVICESCAN'. + + [CF] Win32/native smartd: Added ability to display warning "emails" + as message box by "-m msgbox" directive. With "-m sysmsgbox", + a system modal (always on top) message box is shown. + + [BA] smartctl: printing of self-test log for disks that support + Selective self-testing now shows the status of the (optional) + read-scan after the selective self test. Also, changed format + in printing self-test log to print failing LBA in base 10 not + base 16 (more compatible with kernel error messages). Also, + in printing SMART error log, print timestamps in format + days+hours+minutes+seconds. + + [CF] Win32 smartd: Added ability to log to stdout/stderr + (-l local1/2). Toggling debug console still works + if stdout is redirected. + + [BA] smartctl: selective self-test log, print current status + in a more detailed way. Allow writing of selective self-test + log provided that no other self-test is underway. + + [BA] Linux: eliminated dependency on kernel tree hdreg.h. + + [BA] smartctl: -l selftest option now prints Selective self-test + log in addition to the normal self-test log. + Added additional options (-t pending, -t afterselect) to + control remaining Selective Self-test capabilities. Tested + with several Maxtor disks. Modified error message printing + so that munged option messages print at the end not the + start of output. + + [CF] Added daemon support to Win32 native version of smartd. + The daemon can be controlled by commands similar to initd + scripts: "smartd status|stop|reload|restart|sigusr1|sigusr2". + + [CF] Added minor support for option "-l local[0-7]" to Win32 native + (not Cygwin) version of smartd. If specified, the log output + is written to file "./smartd[1-7]?.log" instead of event log. + + [BA] Added Selective Self-test to smartctl (-t selective,M-N). + Currently only supported under Linux; Solaris, NetBSD, FreeBSD + and Windows developers must add WRITE LOG functionality to + os_*.c + + [BA] Added workaround for an annoying glibc bug: if you change + timezones, (eg, flying with a laptop from USA to Europe) + localtime() does not notice this in a running + executable, so time that appears in the system log (syslog!) + will be incorrect. See + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48184 + for additional examples of this bug. + + [DG] Set explicit timeouts for SCSI commands (most default to 6 seconds). + Previously a 0 second timeout was meant to be interpreted as a + default timeout but the FreeBSD port had a problem in this area. + + [CF] Fixed un-thread-safe exit signal handler for Win32 + + [BA] Fixed un-thread-safe exit signal handler pointed out + by CF. + + [BA] Changed configure script to eliminate warnings under + Solaris from sys/int_type.h conflicts with int64.h + Added header files for umask to smartd.c. + + [BA] Man page format change from Werner LEMBERG. " " changed to \& + + [CF] Added os_win32/syslogevt.* event message file tool for Win32 + smartd (native+cygwin). May also be useful for other cygwin + programs writing to syslog(). + + [CF] Added Win32 version of smartd + + [CF] Merged RELEASE_5_26_WIN32_BRANCH + + [BA] Made some changes to man page markup suggested by + Richard Verhoeven to work around bugs in man2html. + Tested not to break anything under Linux and Solaris. + + [CF] Moved PrintOut() from utility.c to smart{ctl,d}.c to avoid + syslog() output of smartctl. + + [BA] Grew worried that some time-zone names could be very long (eg, + Mitteleuropaische Zeit) and put date string lengths into a + single macro in utility.c + + [EM] Updated os_freebsd.c to handle older versions of FreeBSD in a + more appropriate/obvious fashion. + + [EM] Modified autogen.sh as FreeBSD installs automake 1.7 as + 'automake17' and NOT 'automake-1.7' + +smartmontools 5.30 + + [PW] Added QUANTUM FIREBALLlct15 30, QUANTUM FIREBALLlct20 40, and + Maxtor 6Y060P0 (DiamondMax Plus 9 60GB) to knowndrives table. + + [PW] Added Maxtor MaXLine II family to knowndrives table (thanks to + Brett Russ for submitting the patch). + + [BA] Added remaining read/write commands to detailed list of + error log commands that have text descriptions of problem + printed. For commands that support it, print number of failed + sectors at problem LBA. + + [BA] Made SuSE section of smartd init script more SuSE 9 compatible. + Thanks to Hans-Peter Jansen. + + [CF] Windows smartd: Added IDE/ATA device scan + Added windows device names to smartctl.8.in, smartd.8.in + + [BA] smartctl/smartd: user-provided '-F samsung' and '-F samsung2' + command line options/Directives did NOT over-ride preset values + unless user specified '-P ignore'. Now they will always over-ride + preset values from the database. + + [BA] Added error decoding for a few more READ and WRITE commands. + + [PW] Added Maxtor MaXLine Plus II, Western Digital Caviar SE (Serial ATA) + series, Hitachi Deskstar 7K250 series, and Ultra ATA 66 models of + the Maxtor DiamondMax Plus 40 series to knowndrives table. + + [BA] Added Maxtor Diamondmax 250 GB drives to database. Note that + these model numbers are not listed in Maxtor documentation, but + they exist. + + [BA] Removed the 'contact developers' phrase from the Samsung disk + warning messages. + + [PW] Added TOSHIBA MK2017GAP, IBM Deskstar 14GXP and 16GP series, + Fujitsu MPC series, Seagate Barracuda ATA III family, and missing + Seagate Barracuda U Series drives to knowndrives table + + [BA] smartd: wrong loglevel for message: Configuration file + /etc/smartd.conf parsed. Changed to LOG_INFO from LOG_CRIT. + Thanks to Emmanuel CHANTREAU for the report. + + [CF] Checked in development version of windows code base. + +smartmontools 5.29 (note: there was NO 5.28 release) + + [BA] smartd: configure script did not set correct directory to search for + smartd.conf based on --prefix argument to ./configure. Thanks to + GG for identifying the problem and fix. + + [BA] make clean now removes man pages (generated from *.in) files as well + as object files. + + [EM] Correct copying of sense data in FreeBSD SCSI implementation. Thanks + to Sergey Svishchev for noticing the bug. + + [BA] On solaris, wrong warning message if no ATA support. Warning message + concerns 3ware controller, not ATA. + + [SS] Added SCSI support for NetBSD. + + [BA] on big-endian linux machines, fixed interpretation of HDIO_GET_IDENTITY + to correctly identify ATAPI bit (was byte swapped). This should + eliminate some SYSLOG noise if user queries a packet device (eg, CD + ROM or DVD reader). + + [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives with + A5AA/A6AA firmware. Thanks to Gerald Schnabel. + + [PW] Added Toshiba TOS MK3019GAXB SUN30G to knowndrives table + + [PW] Added Western Digital Caviar AC12500, AC24300, AC25100, AC36400, + and AC38400 to knowndrives table + + [BA] When printing ATA error log, print the LBA at which READ + or WRITE commands failed. + + [BA] Changed syntax of error message in smartctl + + [BA] Added versioning info (-V options to smartd/smartctl) for + Solaris ATA module. + +smartmontools 5.27 + + [KS] Added ATA/IDE support for Solaris/SPARC (ATA/IDE not yet for + Solaris/x86). + + [BA] 3ware controllers: documented that one can monitor any of the + physical disks from any of the 3ware /dev/sd? logical devices. + Better warnings if querying a disk that does not exist. + + [PW] Added Hitachi Travelstar DK23DA series, Maxtor DiamondMax Plus 40 + series, Western Digital Caviar WDxxxAA, WDxxxBA, and WDxxxAB series + to knowndrives table + + [BA] missing 'pragma pack' on ATA IDENTIFY DEVICE structure may have + caused odd or incorrect results on 64-bit machines. + + [BA] smartctl/smartd allow inspection of self-test and error logs even + if disk firmware claims that these don't exist. This is needed + for some Maxtor disks whose firmware does not indicate log support + even though the disk DOES support it. + + [BA] Improved porting instructions and documentation in os_generic.c + + [PW] Add Western Digital Caviar WD136AA and SAMSUNG SP40A2H (RR100-07 + firmware) to knowndrives table. + + [EM] FreeBSD: remove extra definition of FreeNonZero + + [BA] smartctl: the -q silent option was printing output for some + error conditions. Fixed. Will rename relevant variables to help + avoid these errors in the future. + + [SS] NetBSD port added. + + [BA] more sensible error messages for devfs and devfs-like systems. + Instead of saying that the DIRECTORY does not exist, say that + the DEVICE does not exist. + + [BA] smartd: added -n Directive, to prevent disk spin-up depending + upon the power mode (SLEEP, STANDBY, or IDLE). + + [PW] Added Maxtor DiamondMax 20 VL series, Fujitsu MPF series, + Maxtor DiamondMax 36 series, Maxtor DiamondMax 4320 series, and + Maxtor DiamondMax 536DX series to knowndrives table. + + [BA] many warning messages now give the file name AND VERSION + + [BA] smartd: when the user provides multiple address recipients + to the '-m' Directive in a comma-delineated list, the commas + are stripped out before passing the list of addresses to the + mailer program. (Thanks to Calin A. Culianu for pointing this out + and providing a patch.) + + [BA] smartd: when the '-M exec path' Directive is used, any stdout OR + stderr output from the executable "path" is assumed to indicate a + problem, and is echoed to SYSLOG. + + [BA] Added all missing IBM/Hitachi Deskstar 180GXP models to knowndrives + table. + + [PW] Added some missing IBM/Hitachi Deskstar 120GXP models to knowndrives + table. + + [PW] Added IBM Travelstar 14GS to knowndrives table. + + [PW] Modified knowndrives table to match entire Hitachi Travelstar + DK23BA and DK23EA series of drives (thanks to Norikatsu Shigemura + for submitting the patch). + + [PW] Added some missing Fujitsu MPE series drives to knowndrives table. + + [PW] Added TOSHIBA MK4019GAX, TOSHIBA MK6409MAV, and QUANTUM + FIREBALLlct15 20 to knowndrives table. + + [EM] Fixup example command output for FreeBSD + + [PW] Added Maxtor DiamondMax 80 family to knowndrives table. + + [EM] Catch up FreeBSD code to switch PROJECTHOME to PACKAGE_HOMEPAGE + macros. + + [BA] smartd: now watches stdout/stderr when trying to run mail, mailx + or mail warning script, and reports any output to SYSLOG. This + gives a clearer error message if something is wrong. + + [BA] smartd: Solaris init script modified to accomodate grep that + lacks '-q' quiet option. Also check for running process to kill + on stop. + + [PW] Added some missing Seagate Barracuda 7200.7 and 7200.7 Plus drives + to knowndrives table. + + [PW] Added Maxtor DiamondMax Plus 60 family and Seagate U Series 5 20413 + to knowndrives table. + + [BA] smartd: under Solaris, made default mailer be 'mailx' not + 'mail', since Solaris 'mail' does not accept a '-s' argument. + A workaround for Solaris users of earlier versions is to + have '-M exec /bin/mailx' in their smartd.conf config file. + + [DG] some SCSI controllers don't like odd length transfers so make + sure LOG SENSE transfers are rounded up to an even number when + and odd length is reported (i.e. there is a double fetch, the + first to find the length, the second gets the data) + + [BA] smartd man pages: under Solaris, correct section numbers in the + 'See also' section. + + [KS/BA] smartd man page: describe how to set Solaris syslog.conf + file to catch all messages. Give correct Solaris SYSLOG default + path /var/adm/messages in man pages. + + [BA] smartd: incorporated Debian startup script submitted by user. + + [BA] smartctl: modified printing of self-test log entry number. Seagate + firmware can leave 'holes' in the self-test log while a test is + actually running. We now print entry numbers consistently in this + case, not assuming that entries are contiguous. + + [PW] Added QUANTUM FIREBALL CX10.2A and Western Digital Caviar AC23200L + to knowndrives table. + + [PW] Added QUANTUM FIREBALLlct20 20 to knowndrives table. + + [PW] Added Maxtor DiamondMax Plus D740X family to knowndrives table. + + [PW] Added IBM Travelstar 32GH, 30GT, and 20GN family to knowndrives + table. + + [BA] Slackware init script modified to search for /etc/slackware-version + rather than /etc/slackware-release. + + [PW] Added Seagate Barracuda ATA II family and TOSHIBA MK4019GAXB to + knowndrives table. + + [GG] explain howto use autoreconf in autogen.sh + + [KS] Makefile.am/configure.in: changed manual page sections for + Solaris. + + [BA] smartd: reduced number of scheduled self-test messages if + test already run in current hour. + + [PW] Added Maxtor DiamondMax Plus 8 family to knowndrives table. + + [BA] linux: check for linux/hdreg.h. If it's there, use it. If + not, provide the necessary definitions ourselves. + + [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives + with TXAOA5AA firmware + + [PW] Added IBM Travelstar 25GS, 18GT, and 12GN family to knowndrives + table. + + [PW] Added IBM/Hitachi Travelstar 60GH & 40GN family to knowndrives + table. + + [BA] smartd: made '-s' Directive more efficient. Now store + compiled regex, and re-use. If device lacks certain self-test + capabilities, track it and don't try again. + + [BA] smartd: made memory allocation for device lists completely + dynamic (eliminating compile-time maximum length constants). + + [PW] Removed warning for SAMSUNG SP0802N with TK100-23 firmware + + [PW] Added Seagate Barracuda ATA IV family to knowndrives table. + + [BA] smartd: reduce per-device memory footprint by making + mail-warning info dynamically allocated. Also remove + potential memory leak if use has -m Directive twice and + keeps reloading the config file (highly unlikely this would + ever be noticed!) + + [DG] smartd: added SCSI scheduled self-tests (Background + short or extended). + + [BA] smartd: can now run scheduled offline immediate and + self-tests. See man page and -s Directive for details. + + [GG] don't include manpages in make-dist-tarball. + + [BA] smartctl: on-line examples given with -h are now correct + for solaris and linux, but wrong for freebsd. Ed? + + [BA] smartd: man page now explains device scanning for solaris as + well as linux and freebsd. + + [BA] smartd/smartctl: man pages now report correct CVS tag release + date, and executables '-V' options reports more build info. + +smartmontools 5.26 + + [BA] Improved user messages that appear from 'make install' + + [PW] Removed warning for SAMSUNG SP1213N with firmware TL100-23 + + [BA] incorporated SuSE init script from user. + + [DG] if SCSI device is read only, then open it read only. + + [BA] when compiled on non-supported system (NOT linux, freebsd or solaris) then + the run-time error messages now clearly say 'your system is not supported' + and give clear directions. + + [BA] ./configure script now works correctly on SuSE linux boxes + + [BA] minor improvements to man pages + + [BA] simplified detection of packet (ATAPI, CD) devices. + + [BA] init script (redhat, mandrake, yellowdog) now uses correct + strings for translation and is slightly more standard. + + [DG] smartctl: output scsi Seagate vendor pages for disks (not tapes) + +smartmontools 5.25 + +Note: there was no '5.24' release. From this point on, even numbered +releases will be 'stable' ones and odd numbered releases will be +unstable/testing/development ones. + + [DG] smartd/smartctl: changed scsiClearControlGLTSD() to + scsiSetControlGLTSD() with an 'enabled' argument so '-S on' + and '-S off' work for SCSI devices (if changing GLTSD supported). + + [BA] smartd/smartctl: wired in scsiClearControlGLTSD(). Could still + use a corresponding Set function. Left stubs for this purpose. + + [DG] scsicmds: added scsiClearControlGLTSD() [still to be wired in] + + [BA] smartctl: make SCSI -T options behave the same way as the + ATA ones. + + [DG] smartctl: output scsi transport protocol if available + + [DG] scsi: stop device scan in smartd and smartctl if badly formed + mode response [heuristic to filter out USB devices before we + (potentially) lock them up]. + + [BA] smartd: deviceclose()->CloseDevice(). Got rid of SCSIDEVELOPMENT + macro-enabled code. Added -W to list of gcc specific options to + always enable. Made code clean for -W warnings. + + [PW] Added Maxtor DiamondMax VL 30 family to knowndrives table. + + [DG] scsi: add warning (when '-l error' active) if Control mode page + GLTSD bit is set (global disable of saving log counters) + + [DG] scsi: remember mode sense cmd length. Output trip temperature + from IE lpage (IBM extension) when unavailable from temp lpage. + + [BA] smartd: for both SCSI and ATA now warns user if either + the number of self-test errors OR timestamp of most + recent self-test error have increased. + + [DG] smartctl: output Seagate scsi Cache and Factory log pages (if + available) when vendor attributes chosen + + [DG] smartd: add scsiCountFailedSelfTests() function. + + [DG] Do more sanity checking of scsi log page responses. + + [BA] smartd: now warns user if number of self-test errors has + increased for SCSI devices. + + [BA] smartd: warn user if number of ATA self-test errors increases + (as before) OR if hour time stamp of most recent self-test + error changes. + + [DG] More checks for well formed mode page responses. This has the side + effect of stopping scans on bad SCSI implementations (e.g. some + USB disks) prior to sending commands (typically log sense) that + locks them up. + + [PW] Added Western Digital Caviar family and Caviar SE family to + knowndrives table. + + [BA] smartd: added -l daemon (which is the default value if -l + is not used). + + [PW] Added Seagate Barracuda ATA V family to knowndrives table. + + [BA] smartd: added additional command line argument -l FACILITY + or --logfacility FACILITY. This can be used to redirect + messages from smartd to a different file than the one used + by other system daemons. + + [PW] Added Seagate Barracuda 7200.7, Western Digital Protege WD400EB, + and Western Digital Caviar AC38400 to knowndrives table. + + [BA] smartd: scanning should now also work correctly for + devfs WITHOUT traditional links /dev/hd[a-t] or /dev/sd[a-z]. + + [PW] Added Maxtor 4W040H3, Seagate Barracuda 7200.7 Plus, + IBM Deskstar 120GXP (40GB), Seagate U Series 20410, + Fujitsu MHM2100AT, MHL2300AT, MHM2150AT, and IBM-DARA-212000 + to knowndrives table. + + [PW] Added remaining Maxtor DiamondMax Plus 9 models to knowndrives + table. + + [EM] smartd: If no matches found, then return 0, rather than an error + indication, as it just means no devices of the given type exist. + Adjust FreeBSD scan code to mirror Linux version. + + [BA] smartd: made device scan code simpler and more robust. If + too many devices detected, warn user but scan as many + as possible. If error in scanning, warn user but don't + die right away. + + [EM] smartd: To keep as consistent as possible, migrate FreeBSD + devicescan code to also use glob(3). Also verified clean + compile on a 4.7 FreeBSD system. + + [BA] smartd: Modified device scan code to use glob(3). Previously + it appeared to have trouble when scanning devices on an XFS + file system, and used non-public interface to directory + entries. Problems were also reported when /dev/ was on an + ext2/3 file system, but there was a JFS partition on the same + disk. + + [BA] Clearer error messages when device scanning finds no suitable + devices. + + [EM] FreeBSD: Fixup code to allow for proper compilation under + -STABLE branch. + +smartmontools 5.23 + + [BA] smartd: didn't close file descriptors of ATA packet devices + that are scanned. Fixed. + + [BA] Added reload/report targets to the smartmontools init script. + reload: reloads config file + report: send SIGUSR1 to check devices now + +smartmontools 5.22 + + [EM] Fix compile issues for FreeBSD < 5-CURRENT. + + [PW] Added Fujitsu MHM2200AT to knowndrives table. + + [BA] To help catch bugs, clear ATA error structures before all + ioctl calls. Disable code that attempted to time-out on SCSI + devices when they hung (doesn't work). + + [BA] Documented STATUS/ERROR flags added by [PW] below. + + [BA] Improved algorithm to recognize ATA packet devices. Should + no longer generate SYSLOG kernel noise when user tries either + smartd or smartctl on packet device (CD-ROM or DVD). Clearer + warning messages from smartd when scanning ATA packet device. + + [PW] Added TOSHIBA MK4025GAS to knowndrives table. + + [PW] Added a textual interpretation of the status and error registers + in the SMART error log (ATA). The interpretation is + command-dependent and currently only eight commands are supported + (those which produced errors in the error logs that I happen to + have seen). + + [BA] added memory allocation tracking to solaris code. + Fixed solaris signal handling (reset handler to default + after first call to handler) by using sigset. Added + HAVE_SIGSET to configure.in + + [CD] solaris port: added SCSI functionality to solaris + stubs. + + [BA] smartd: attempt to address bug report about smartd + hanging on USB devices when scanning: + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=107615 + Set a timeout of SCSITIMEOUT (nominally 7 seconds) before + giving up. + + [EM] smartd: DEVICESCAN will follow links in a devfs filesystem and + make sure the end point is a disc. Update documentation, added + note about FreeBSD scanning + + [BA] smartd: DEVICESCAN also looks for block devices in + /dev. Updated documentation. Now scans for up to + 20 ATA devices /dev/hda-t rather than previous 12 + /dev/hda-l. + + [EM] smartd: mirror the FreeBSD DEVICESCAN logic for Linux, + so that smartd now scans only devices found in /dev/. Also, + make utility memory functions take a line number and file so + that we report errors with the correct location. + + [GG] add a note about Debian bug #208964 to WARNINGS. + + [BA] smartctl: -T verypermissive option broken. Use + -T verpermissive until the next release, please. + + [BA] Syntax mods so that code also compiles on Solaris using + Sun Workshop compiler. Need -xmemalign 1i -xCC flags + for cc. + +smartmontools 5.21 + + [DK] Changed configure.in so -Wall is only included if gcc + is used (this is a gcc specific flag) and -fsignedchar + is not used at all (this is a gcc specific compiler + flag). + + [BA] Modifications so that code now compiles under solaris. Now + all that's needed (:-) is to fill in os_solaris.[hc]. Added + os_generic.[hc] as guide to future ports. Fixed -D option + of smartd (no file name). Modified -h opt of smartd/smartctl + to work properly with solaris getopt(). + + [EM] Update MAN pages with notes that 3ware drives are NOT supported + under FreeBSD. Cleanup FreeBSD warning message handling. + + [EM] FreeBSD only: Fix first user found bug....I guess I was making + the wrong assumption on how to convert ATA devnames to + channel/unit numbers. + + [EM] Allow for option --enable-sample to append '.sample' to installed + smartd.conf and rc script files. Also, let rc script shell setting + be determined by configure + + [EM] Minor autoconf update to include -lcam for FreeBSD + + [EM] Add conditional logic to allow FreeBSD to compile pre-ATAng. + -- note, not tested + Add some documentation to INSTALL for FreeBSD. + + [EM] Implement SCSI CAM support for FreeBSD. NOTE: I am not an expert + in the use of CAM. It seems to work for me, but I may be doing + something horribly wrong, so please exercise caution. + + [EM] Switch over to using 'atexit' rather than 'on_exit' routine. This also + meant we needed to save the exit status elsewhere so our 'Goodbye' + routine could examine it. + + [EM] Move the DEVICESCAN code to os specific files. Also moved some of the + smartd Memory functions to utility.c to make available to smartctl. + + [EM] Code janitor work on os_freebsd.c. + + [EM] Added os_freebsd.[hc] code. Additional code janitor + work. + + [BA] Code janitor working, moving OS dependent code into + os_linux.[hc]. + + [GG] conditionally compile os_{freebsd,linux}.o depending on + host architecture + + [PW] Print estimated completion time for tests + + [BA] Added -F samsung2 flag to correct firmware byte swap. + All samsung drives with *-23 firmware revision string. + +smartmontools 5.20 + + [GG] Fixed broken Makefile.am (zero length smartd.conf.5 + was being created), fix broken uninstall/distcheck targets + + [FM] Improved Slackware init script added to /etc/smartd.initd + +smartmontools 5.19 [NOTE CHANGE OF RELEASE NUMBERING] + + [BA] smartctl: added '-T verypermissive' option which is + equivalent to giving '-T permissive' many times. + + [BA] Try harder to identify from IDENTIFY DEVICE structure + if SMART supported/enabled. smartd now does a more + thorough job of trying to assess this before sending + a SMART status command to find out for sure. + + [BA] smartctl: it's now possible to override the program's + guess of the device type (ATA or SCSI) with -d option. + + [BA] try hard to avoid sending IDENTIFY DEVICE to packet + devices (CDROMS). They can't do SMART, and this generates + annoying syslog messages. At the same time, identify type + of Packet device. + + [BA] smartctl: Can now use permissive option more + than once, to control how far to go before giving up. + + [BA] smartd: if user asked to monitor either error or self-test + logs (-l error or -l selftest) WITHOUT monitoring any of the + Attribute values, code will SEGV. For 5.1-18 and earlier, + a good workaround is to enable Auto offline (-o on). + + [BA] smartctl: If enable auto offline command given, update auto + offline status before printing capabilities. + + [GG] Make autotools build the default, remove autotools.diff + + [GG] Add auto{conf,make} support, not enabled by default. + + [BA] Eliminated #include from code. This + should simplify porting to solaris, FreeBSD, etc. The + only linux-specific code is now isolated to three routines, + one for SCSI, one for Escalade, one for ATA. + +smartmontools 5.1-18 + + [BA] smartd: fixed serious bug - Attributes not monitored unless + user told smartd to ignore at least one of them! + +smartmontools 5.1-17 + + [BA] Default runlevels for smartd changed from 3 and 5 to + 2, 3, 4, and 5. + + [BA] Removed as much dynamic memory allocation as possible from + configuration file parsing. Reloading config file, even in + presence of syntax errors etc. should not cause memory leaks. + + [PW] It is no longer permissible for the integer part (if any) of + arguments to --report and --device to be followed by non-digits. + For example, the "foo" in --report=ioctl,2foo was previously + ignored, but now causes an error. + + [BA] smartd: added -q/--quit command line option to specify + under what circumstances smartd should exit. The old + -c/--checkonce option is now obsoleted by this more + general-purpose option. + + [BA] smartd now responds to a HUP signal by re-reading its + configuration file /etc/smartd.conf. If there are + errors in this file, then the configuration file is + ignored and smartd continues to monitor the devices that + it was monitoring prior to receiving the HUP signal. + + [BA] Now correctly get SMART status from disks behind 3ware + controllers, thanks to Adam Radford. Need 3w-xxxx driver + version 1.02.00.037 or later. Previously the smartmontools + SMART status always returned "OK" for 3ware controllers. + + [BA] Additional work on dynamic memory allocation/deallocation. + This should have no effect on smartctl, but clears that way + for smartd to dynamically add and remove entries. It should + also now be easier to modify smartd to re-read its config + file on HUP (which is easy) without leaking memory (which is + harder). The philosophy is that memory for data structures in + smartd is now allocated only on demand, the first time it + is needed. + + [BA] smartd: finished cleanup. Now use create/rm functions for + cfgentries and dynamic memory allocation almost everywhere. + Philosophy: aggresively try and provoke SEGV to help find + bad code. + + [BA] Added SAMSUNG SV0412H to knowndrives table. + + [BA] smartd: if DEVICESCAN used then knowndrives table might not set + the -v attributes correctly -- may have been the same for all + the drives. Cleaned up some data structures and memory + allocation to try and ensure segvs if such problems are + introduced again. + + [BA] Now allow -S on and -o on for the 3ware device type. For these + commands to be passed through, the stock 3ware 3w-xxxx driver + must be patched (8 lines). I'll post a patch on the smartmontools + home page after it's been tested by a few other people and 3ware + have had a chance to look it over. + +smartmontools-5.1-16 + + [BA] smartd - can now monitor ATA drives behind 3ware controllers. + + [BA] smartd - changed some FATAL out of memory error messages from + syslog level LOG_INFO to LOG_CRIT. + + [BA] smartctl - added code to look at ATA drives behind 3ware RAID + controllers using the 3w-xxxx driver. Note that for technical + reasons related to the 3w-xxxx driver, the "Enable Autosave", + "Enable Automatic Offline" commands are not implemented. + I will add this to smartd shortly. + + [BA] smartd - modified sleep loop, so that smartd no longer comes + on the run queue every second. Instead, unless interrupted, + it sleeps until the next polling time, when it wakes up. Now + smartd also tries to wake up at exactly the right + intervals (nominally 30 min) even if the user has been sending + signals to it. + + [GG] add Fujitsu MHN2300AT to vendoropts_9_seconds. + + [EB] Fujitsu change in knowndrives ... match the whole MPD and + MPE series for vendoropts_9_seconds. + + [BA] smartd bug, might cause segv if a device can not be opened. Was + due to missing comma in char* list. Consequence is that email + failure messages might have had the wrong Subject: heading for + errorcount, FAILEDhealthcheck, FAILEDreadsmartdata, FAILEDreadsmarterrorlog, + FAILEDreadsmartsefltestlog, FAILEDopendevice were all displaced by + one. And FAILEDopendevice might have caused a segv if -m was being + used as a smartd Directive. + +smartmontools-5.1-15 + + [BA] Cleaned up smartmontools.spec so that upgrading, removing + and other such operations correctly preserve running behavior + and booting behavior of smartd. + + [BA] Improved formatting of ATA Error Log printout, and added + listing of names of commands that caused the error. Added + obsolete ATA-4 SMART feature commands to table, along with + obsolete SFF-8035i SMART feature command. + + [PW] Added atacmdnames.[hc], which turn command register & + feature register pairs into ATA command names. + + [BA] Added conveyance self-test. Some code added for selective + self-tests, but #ifdefed out. + + [BA] Modified smartd exit status and log levels. If smartd is + "cleanly" terminated, for example with SIGTERM, then its + exit messages are now logged at LOG_INFO not LOG_CRIT + + [BA] Added Attribute IDs (Fujitsu) 0xCA - 0xCE. This is decimal + 202-206. Added -v switches for interpretation of Attributes + 192, 198 and 201. + + [BA] Made smartmontools work with any endian order machine for: + - SMART selftest log + - SMART ATA error log + - SMART Attributes values + - SMART Attributes thesholds + - IDENTIFY DEVICE information + - LOG DIRECTORY + Smartmontools is now free of endian bias and works correctly + on both little- and big-endian hardware. This has been tested by + three independent PPC users on a variety of ATA and SCSI hardware. + + [DG] Check that certain SCSI command responses are well formed. If + IEC mode page response is not well formed exit smartctl. This + is to protect aacraid. smartd should ignore a aacraid device. + +smartmontools-5.1-14 + + [BA] smartctl: added column to -A output to show if Attributes are + updated only during off-line testing or also during normal + operation. + +smartmontools-5.1-13 + + [BA] smartd: attempt to enable/disable automatic offline testing even + if the disk appears not to support it. Now the same logic + as smartctl. + + [BA] Added definition of Attribute 201, soft read error rate. + + [BA] Added IBM/Hitachi IC35L120AVV207-1 (GXP-180) and corresponding + 8MB Cache GXP-120 to drive database. + + [BA] smartd: if DEVICESCAN Directive used in smartd.conf, and + -I, -R or -r Directives used in conjunction with this, got + segv errors. Fixed by correcting memory allocation calls. + + [BA] smartd: enable automatic offline testing was broken due + to cut-and-paste error that disabled it instead of + enabling it. Thanks to Maciej W. Rozycki for pointing + out the problem and solution. + + [BA] Fixed "spelling" of some Attribute names to replace spaces + in names by underscores. (Fixed field width easier for awk + style parsing.) + + [BA,GF] Added mods submitted by [GF] to support Attribute 193 being + load/unload cycles. Add -v 193,loadunload option, useful for + Hitachi drive DK23EA-30, and add this drive to knowndrive.c + Add meaning of attribute 250 : Read error retry rate + +smartmontools-5.1-12 + + [BA] Added another entry for Samsung drives to knowndrive table. + + [DG] Refine SCSI log sense command to do a double fetch in most cases + (but not for the TapeAlert log page). Fix TapeAlert and Self Test + log page response truncation. + + [PW] Added 'removable' argument to -d Directive for smartd. This indicates + that smartd should continue (rather than exit) if the device does not + appear to be present. + + [BA] Modified smartmontools.spec [Man pages location] and + smartd.initd [Extra space kills chkconfig!] for Redhat 6.x + compatibility (thanks to Gerald Schnabel). + +smartmontools-5.1-11 + + [EB] Add another Fujitsu disk to knowndrives.c + + [GG] match for scsi/ and ide/ in case of devfs to exclude false postives + + [BA] If SCSI device listed in /etc/smartd.conf fails to open or do + SMART stuff correctly, or not enough space + to list all SCSI devices, fail with error unless + -DSCSIDEVELOPMENT set during compile-time. + + [BA] Added automatic recognition of /dev/i* (example: /dev/ide/...) + as an ATA device. + + [DG] Add "Device type: [disk | tape | medium changer | ...] line to + smartctl -i output for SCSI devices. + + [PW] Fixed bug in smartd where test email would be sent regularly (for + example, daily if the user had specified -M daily) instead of just + once on startup. + + [KM] More TapeAlert work. Added translations for media changer + alerts. TapeAlert support reported according to the log page + presence. ModeSense not attempted for non-ready tapes (all + drives do not support this after all). Get peripheral type from + Inquiry even if drive info is not printed. Add QUIETON() + QUIETOFF() to TapeAlert log check. + + [BA] Stupid bug in atacmds.c minor_str[] affected ataVersionInfo(). + Two missing commas meant that minor_str[] had two few elements, + leading to output like this: + Device Model: Maxtor 6Y120L0 + Serial Number: Y40BF74E + Firmware Version: YAR41VW0 + Device is: Not in smartctl database [for details use: -P showall] + ATA Version is: 7 + ATA Standard is: 9,minutes + ^^^^^^^^^ + Missing commas inserted. + + [BA] Fixed smartd bug. On device registration, if ATA device did + not support SMART error or self-test logs but user had asked to + monitor them, an attempt would be made to read them anyway, + possibly generating "Drive Seek" errors. We now check that + the self-test and error logs are supported before trying to + access them the first time. + + [GG/BA] Fixed bug where if SMART ATA error log not supported, + command was tried anyway. Changed some error printing to use + print handlers. + + [GG] Makefile modifications to ease packaging + + [DG] Did work for TapeAlerts (SCSI). Now can detect /dev/nst0 as a + SCSI device. Also open SCSI devices O_NONBLOCK so they don't + hang on open awaiting media. The ATA side should worry about + this also: during a DEVICESCAN a cd/dvd device without media + will hang. Added some TapeAlert code suggested by Kai Makisara. + +smartmontools-5.1-10 + + [PW] Extended the -F option/Directive to potentially fix other firmware + bugs in addition to the Samsung byte-order bug. Long option name is + now --firmwarebug and the option/Directive accepts an argument + indicating the type of firmware bug to fix. + + [BA] Fixed a bug that prevented the enable automatic off-line + test feature from enabling. It also prevented the enable Attribute + autosave from working. See CVS entry for additional details. + + [PW] Modified the -r/--report option (smartctl and smartd) to allow the + user to specify the debug level as a positive integer. + + [BA] Added --log directory option to smartctl. If the disk + supports the general-purpose logging feature set (ATA-6/7) + then this option enables the Log Directory to be printed. + This Log Directory shows which device logs are available, and + their lengths in sectors. + + [PW] Added -P/--presets option to smartctl and -P Directive to smartd. + + [GG] Introduce different exit codes indicating the type of problem + encountered for smartd. + + [DG] Add non-medium error count to '-l error' and extended self test + duration to '-l selftest'. Get scsi IEs and temperature changes + working in smartd. Step over various scsi disk problems rather + than abort smartd startup. + + [DG] Support -l error for SCSI disks (and tapes). Output error counter + log pages. + + [BA] Added -F/--fixbyteorder option to smartctl. This allows us to read + SMART data from some disks that have byte-reversed two- and four- + byte quantities in their SMART data structures. + + [BA] Fixed serious bug: the -v options in smartd.conf were all put + together and used together, not drive-by-drive. + + [PW] Added knowndrives.h and knowndrives.c. The knowndrives array + supersedes the drivewarnings array. + + [GG] add {-p,--pidfile} option to smartd to write a PID file on + startup. Update the manpage accordingly. + + [DG] Fix scsi smartd problem detecting SMART support. More cleaning + and fix (and rename) scsiTestUnitReady(). More scsi renaming. + + [BA] Fixed smartd so that if a disk that is explictily listed is not + found, then smartd will exit with nonzero status BEFORE forking. + If a disk can't be registered, this will also be detected before + forking, so that init scripts can react correctly. + + [BA] Replaced all linux-specific ioctl() calls in atacmds.c with + a generic handler smartcommandhandler(). Now the only routine + that needs to be implemented for a given OS is os_specific_handler(). + Also implemented the --report ataioctl. This provides + two levels of reporting. Using the option once gives a summary + report of device IOCTL transactions. Using the option twice give + additional info (a printout of ALL device raw 512 byte SMART + data structures). This is useful for debugging. + + [DG] more scsi cleanup. Output scsi device serial number (VPD page + 0x80) if available as part of '-i'. Implement '-t offline' as + default self test (only self test older disks support). + + [BA] Changed crit to info in loglevel of smartd complaint to syslog + if DEVICESCAN enabled and device not found. + + [BA] Added -v 194,10xCelsius option/Directive. Raw Attribute number + 194 is ten times the disk temperature in Celsius. + + [DG] scsicmds.[hc] + scsiprint.c: clean up indentation, remove tabs. + Introduce new intermediate interface based on "struct scsi_cmnd_io" + to isolate SCSI generic commands + responses from Linux details; + should help port to FreeBSD of SCSI part of smartmontools. + Make SCSI command builders more parametric. + +smartmontools-5.1-9 + + [BA] smartctl: if HDIO_DRIVE_TASK ioctl() is not implemented (no + kernel support, then try to assess drive health by examining + Attribute values/thresholds directly. + + [BA] smartd/smartctl: added -v 200,writeerrorcount option/Directive + for Fujitsu disks. + + [BA] smartd: Now send email if any of the SMART commands fails, + or if open()ing the device fails. This is often noted + as a common disk failure mode. + + [BA] smartd/smartctl: Added -v N,raw8 -v N,raw16 and -v N,raw48 + Directives/Options for printing Raw Attributes in different + Formats. + + [BA] smartd: Added -r ID and -R ID for reporting/tracking Raw + values of Attributes. + + [BA] smartd/smartctl: Changed printing of spin-up-time attribute + raw value to reflect current/average as per IBM standard. + + [BA] smartd/smartctl: Added -v 9,seconds option for disks which + use Attribute 9 for power-on lifetime in seconds. + + [BA] smartctl: Added a warning message so that users of some IBM + disks are warned to update their firmware. Note: we may want + to add a command-line flag to disable the warning messages. + I have done this in a general way, using regexp, so that we + can add warnings about any type of disk that we wish... + +smartmontools-5.1-7 + + [BA] smartd: Created a subdirectory examplescripts/ of source + directory that contains executable scripts for the -M exec PATH + Directive of smartd. + +smartmontools-5.1-5 + + [BA] smartd: DEVICESCAN in /etc/smartd.conf + can now be followed by all the same Directives as a regular + device name like /dev/hda takes. This allows one to use + (for example): + DEVICESCAN -m root@example.com + in the /etc/smartd.conf file. + + [BA] smartd: Added -c (--checkonce) command-line option. This checks + all devices once, then exits. The exit status can be + used to learn if devices were detected, and if smartd is + functioning correctly. This is primarily for Distribution + scripters. + + [BA] smartd: Implemented -M exec Directive for + smartd.conf. This makes it possible to run an + arbitrary script or mailing program with the + -m option. + + [PW] smartd: Modified -M Directive so that it can be given + multiple times. Added -M exec Directive. + +smartmontools-5.1-4 + + [BA] Fixed bug in smartctl pointed out by Pierre Gentile. + -d scsi didn't work because tryata and tryscsi were + reversed -- now works on /devfs SCSI devices. + + [BA] Fixed bug in smartctl pointed out by Gregory Goddard + . Manual says that bit 6 of return + value turned on if errors found in smart error log. But + this wasn't implemented. + +smartmontools-5.1-3 + + [BA] Modified printing format for 9,minutes to read + Xh+Ym not X h + Y m, so that fields are fixed width. + + [BA] Added Attribute 240 "head flying hours" + +smartmontools-5.1.1 + + [BA] As requested, local time/date now printed by smartctl -i + + [PW] Added "help" argument to -v for smartctl + + [PW] Added -D, --showdirectives option to smartd + + [DG] add '-l selftest' capability for SCSI devices (update smartctl.8) + + [BA] smartd,smartctl: added additional Attribute modification option + -v 220,temp and -v 9,temp. + + [PW] Renamed smartd option -X to -d + +START OF SMARTMONTOOLS 5.1 series + +smartmontools-5.0.50 + + [PW] Changed smartd.conf Directives -- see man page + + [BA/DG] Fixed uncommented comment in smartd.conf + + [DG] Correct 'Recommended start stop count' for SCSI devices + + [PW] Replaced smartd.conf directive -C with smartd option -i + + [PW] Changed options for smartctl -- see man page. + + [BA] Use strerror() to generate system call error messages. + + [BA] smartd: fflush() all open streams before fork(). + + [BA] smartctl, smartd simplified internal handling of checksums + for simpler porting and less code. + +smartmontools-5.0.49 + + [PW] smartd --debugmode changed to --debug + + [BA] smartd/smartctl added attribute 230 Head Amplitude from + IBM DPTA-353750. + + [PW] Added list of proposed new options for smartctl to README. + + [PW] smartd: ParseOpts() now uses getopt_long() if HAVE_GETOPT_LONG is + defined and uses getopt() otherwise. This is controlled by CPPFLAGS in + the Makefile. + + [BA] smartd: Fixed a couple of error messages done with perror() + to redirect them as needed. + +smartmontools-5.0.48 + + [BA] smartctl: The -O option to enable an Immediate off-line test + did not print out the correct time that the test would take to + complete. This is because the test timer is volatile and not + fixed. This has been fixed, and the smartctl.8 man page has been + updated to explain how to track the Immediate offline test as it + progresses, and to further emphasize the differences between the + off-line immediate test and the self-tests. + + [BA] smartd/smartctl: Added new attribute (200) Multi_Zone_Error_Rate + + [BA] smartctl: modified so that arguments could have either a single - + as in -ea or multiple ones as in -e -a. Improved warning message for + device not opened, and fixed error in redirection of error output of + HD identity command. + + [PW] smartd: added support for long options. All short options are still + supported; see manpage for available long options. + + [BA] smartctl. When raw Attribute value was 2^31 or larger, did + not print correctly. + +smartmontools-5.0.46 + + [BA] smartd: added smartd.conf Directives -T and -s. The -T Directive + enables/disables Automatic Offline Testing. The -s Directive + enables/disables Attribute Autosave. Documentation and + example configuration file updated to agree. + + [BA] smartd: user can make smartd check the disks at any time + (ie, interrupt sleep) by sending signal SIGUSR1 to smartd. This + can be done for example with: + kill -USR1 + where is the process ID number of smartd. + + [EB] scsi: don't trust the data we receive from the drive too + much. It very well might have errors (like zero response length). + Seen on Megaraid logical drive, and verified in the driver source. + + [BA] smartd: added Directive -m for sending test email and + for modifying email reminder behavior. Updated manual, and sample + configuration file to illustrate & explain this. + + [BA] smartd: increased size of a continued smartd.conf line to + 1023 characters. + + [BA] Simplified Directive parsers and improved warning/error + messages. + +smartmontools-5.0.45 + + [EB] Fixed bug in smartd where testunitready logic inverted + prevented functioning on scsi devices. + The bug in question only affects smartd users with scsi devices. + To see if your version of smartd has the testunitready() bug, do + smartd -V + If the version of the module smartd.c in a line like: + Module: smartd.c revision: 1.66 date: 2002/11/17 + has a revision greater than or equal to 1.30, and less than or equal to + 1.64, then your version of the code has this problem. + This problem affected releases starting with RELEASE_5_0_16 up to and + including RELEASE_5_0_43. + + [BA] Added testunitnotready to smartctl for symmetry with smartd. + + [SB] added Czech descriptions to .spec file + [SB] corrected comment in smartd.conf example + + [BA] Changed way that entries in the ATA error log are printed, + to make it clearer which is the most recent error and + which is the oldest one. + + NOTE: All changes made prior to this point were done by Bruce Allen + [BA] although several of them had been suggested by earlier postings + by Stanislav Brabec [SB]. + +smartmontools-5.0.43 + + Changed Temperature_Centigrade to Temperature_Celsius. + The term "Centigrade" ceased to exist in 1948. (c.f + http://www.bartleby.com/64/C004/016.html). + +smartmontools-5.0.42 + + Modified SCSI device check to also send warning emails if + requested in directives file. + + Added a new smartd configuration file Directive: -M ADDRESS. + This sends a single warning email to ADDRESS for failures or + errors detected with the -c, -L, -l, or -f Directives. + +smartmontools-5.0.38 + + Modified perror() statements in atacmds.c so that printout for SMART + commands errors is properly suppressed or queued depending upon users + choices for error reporting modes. + + Added Italian descriptions to smartmontools.spec file. + + Started impementing send-mail-on-error for smartd; not yet enabled. + + Added -P (Permissive) Directive to smartd.conf file to allow SMART + monitoring of pre-ATA-3 Rev 4 disks that have SMART but do not have + a SMART capability bit. + + Removed charset encodings from smartmontools.spec file for non-English + fields. + +smartmontools-5.0.32 + + Added manual page smartd.conf.5 for configuration file. + + smartctl: Missing ANSI prototype in failuretest(); fixed. + + smartctl: Checksum warnings now printed on stdout, or are silent, + depending upon -q and -Q settings. + +smartmontools-5.0.31 + + Changed Makefile so that the -V option does not reflect file state + before commit! + + smartctl: added new options -W, -U, and -P to control if and how the + smartctl exits if an error is detected in either a SMART data + structure checksum, or a SMART command returns an error. + + modified manual page to break options into slightly more logical + categories. + + reformatted 'usage' message order to agree with man page ordering + + modified .spec file so that locale information now contains + character set definition. Changed pt_BR to pt since we do not use any + aspect other than language. See man setlocale. + +smartmontools-5.0.30 + smartctl: added new options -n and -N to force device to be ATA or SCSI + smartctl: no longer dies silently if device path does not start/dev/X + smartctl: now handles arbitrary device paths + +smartmontools-5.0.29 + Modified .spec file and Makefile to make them more compliant with + the "right" way of doing things. + +smartmontools-5.0.26 + Fixed typesetting error in man page smartd.8 + + Removed redundant variable (harmless) from smartd.c + +smartmontools-5.0.25 + + Added a new directive for the configuration file. If the word + DEVICESCAN appears before any non-commented material in the + configuration file, then the confi file will be ignored and the + devices wil be scanned. + +smartmontools-5.0.24 + + Note: it has now been confirmed that the code modifications between + 5.0.23 and 5.0.24 have eliminated the GCC 3.2 problems. Note that + there is a GCC bug howerver, see #8404 at + http://gcc.gnu.org/cgi-bin/gnatsweb.pl?database=gcc&cmd=query + http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8404 + + Added new Directive for Configuration file: + -C This sets the time in between disk checks to be + seconds apart. Note that although you can give + this Directive multiple times on different lines of + the configuration file, only the final value that + is given has an effect, and applies to all the + disks. The default value of is 1800 sec, and + the minimum allowed value is ten seconds. + + Problem wasn't the print format. F.L.W. Meunier <0@pervalidus.net> + sent me a gcc 3.2 build and I ran it under a debugger. The + problem seems to be with passing the very large (2x512+4) byte + data structures as arguments. I never liked this anyway; it was + inherited from smartsuite. So I've changed all the heavyweight + functions (ATA ones, anyone) to just passing pointers, not hideous + kB size structures on the stack. Hopefully this will now build OK + under gcc 3.2 with any sensible compilation options. + +smartmontools-5.0.23 + + Because of reported problems with GCC 3.2 compile, I have gone + thorough the code and explicitly changed all print format + parameters to correspond EXACTLY to int unless they have to be + promoted to long longs. To quote from the glibc bible: [From + GLIBC Manual: Since the prototype doesn't specify types for + optional arguments, in a call to a variadic function the default + argument promotions are performed on the optional argument + values. This means the objects of type char or short int (whether + signed or not) are promoted to either int or unsigned int, as + appropriate.] + +smartmontools-5.0.22 + + smartd, smartctl now warn if they find an attribute whose ID + number does not match between Data and Threshold structures. + + Fixed nasty bug which led to wrong number of arguments for a + varargs statement, with attendent stack corruption. Sheesh! + Have added script to CVS attic to help find such nasties in the + future. + +smartmontools-5.0.21 + + Eliminated some global variables out of header files and other + minor cleanup of smartd. + +smartmontools-5.0.20 + + Did some revision of the man page for smartd and made the usage + messages for Directives 100% consistent. + +smartmontools-5.0-19 + + smartd: prints warning message when it gets SIGHUP, saying that it + is NOT re-reading the config file. + + smartctl: updated man page to say self-test commands -O,x,X,s,S,A + appear to be supported in the code. [I can't test these, can anyone + report?] + +smartmontools-5.0-18 + + smartctl: smartctl would previously print the LBA of a self-test + if it completed, and the LBA was not 0 or 0xff...f However + according to the specs this is not correct. According to the + specs, if the self-test completed without error then LBA is + undefined. This version fixes that. LBA value only printed if + self-test encountered an error. + +smartmontools-5.0-17 + + smartd has changed significantly. This is the first CVS checkin of + code that extends the options available for smartd. The following + options can be placed into the /etc/smartd.conf file, and control the + behavior of smartd. + Configuration file Directives (following device name): + -A Device is an ATA device + -S Device is a SCSI device + -c Monitor SMART Health Status + -l Monitor SMART Error Log for changes + -L Monitor SMART Self-Test Log for new errors + -f Monitor for failure of any 'Usage' Attributes + -p Report changes in 'Prefailure' Attributes + -u Report changes in 'Usage' Attributes + -t Equivalent to -p and -u Directives + -a Equivalent to -c -l -L -f -t Directives + -i ID Ignore Attribute ID for -f Directive + -I ID Ignore Attribute ID for -p, -u or -t Directive + # Comment: text after a hash sign is ignored + \ Line continuation character + + cleaned up functions used for printing CVS IDs. Now use string + library, as it should be. + + modified length of device name string in smartd internal structure + to accomodate max length device name strings + + removed un-implemented (-e = Email notification) option from + command line arg list. We'll put it back on when implemeneted. + + smartd now logs serious (fatal) conditions in its operation at + loglevel LOG_CRIT rather than LOG_INFO before exiting with error. + + smartd used to open a file descriptor for each SMART enabled + device, and then keep it open the entire time smartd was running. + This meant that some commands, like IOREADBLKPART did not work, + since the fd to the device was open. smartd now opens the device + when it needs to read values, then closes it. Also, if one time + around it can't open the device, it simply prints a warning + message but does not give up. Have eliminated the .fd field from + data structures -- no longer gets used. + + smartd now opens SCSI devices as well using O_RDONLY rather than + O_RDWR. If someone can no longer monitor a SCSI device that used + to be readable, this may well be the reason why. + + smartd never checked if the number of ata or scsi devices detected + was greater than the max number it could monitor. Now it does. + +smartmontools-5.0-16 + + smartd on startup now looks in the configuration file /etc/smartd.conf for + a list of devices which to include in its monitoring list. See man page + (man smartd) for syntax. + + smartd: close file descriptors of SCSI device if not SMART capable + Closes ALL file descriptors after forking to daemon. + + added new temperature attribute (231, temperature) + + smartd: now open ATA disks using O_RDONLY + +smartmontools-5.0-11 + + smartd now prints the name of a failed or changed attribute + into logfile, not just ID number + + Changed name of -p (print version) option to -V + + Minor change in philosophy: if a SMART command fails or the device + appears incapable of a SMART command that the user has asked for, + complain by printing an error message, but go ahead and try + anyway. Since unimplemented SMART commands should just return an + error but not cause disk problems, this should't cause any + difficulty. + + Added two new flags: q and Q. q is quiet mode - only print: For + the -l option, errors recorded in the SMART error log; For the -L + option, errors recorded in the device self-test log; For the -c + SMART "disk failing" status or device attributes (pre-failure or + usage) which failed either now or in the past; For the -v option + device attributes (pre-failure or usage) which failed either now + or in the past. Q is Very Quiet mode: Print no ouput. The only + way to learn about what was found is to use the exit status of + smartctl. + + smartctl now returns sensible values (bitmask). See smartctl.h + for the values, and the man page for documentation. + + The SMART status check now uses the correct ATA call. If failure + is detected we search through attributes to list the failed ones. + If the SMART status check shows GOOD, we then look to see if their + are any usage attributes or prefail attributes have failed at any + time. If so we print them. + + Modified function that prints vendor attributes to say if the + attribute has currently failed or has ever failed. + + -p option now prints out license info and CVS strings for all + modules in the code, nicely formatted. + + Previous versions of this code (and Smartsuite) only generate + SMART failure errors if the value of an attribute is below the + threshold and the prefailure bit is set. However the ATA Spec + (ATA4 <=Rev 4) says that it is a SMART failure if the value of an + attribute is LESS THAN OR EQUAL to the threshold and the + prefailure bit is set. This is now fixed in both smartctl and + smartd. Note that this is a troubled subject -- the original + SFF 8035i specification defining SMART was inconsistent about + this. One section says that Attribute==Threshold is pass, + and another section says it is fail. However the ATA specs are + consistent and say Attribute==Threshold is a fail. + + smartd did not print the correct value of any failing SMART attribute. It + printed the index in the attribute table, not the attribute + ID. This is fixed. + + when starting self-tests in captive mode ioctl returns EIO because + the drive has been busied out. Detect this and don't return an eror + in this case. Check this this is correct (or how to fix it?) + + fixed possible error in how to determine ATA standard support + for devices with no ATA minor revision number. + + device opened only in read-only not read-write mode. Don't need R/W + access to get smart data. Check this with Andre. + + smartctl now handles all possible choices of "multiple options" + gracefully. It goes through the following phases of operation, + in order: INFORMATION, ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS. + Documentation has bee updated to explain the different phases of + operation. Control flow through ataPrintMain() + simplified. + + If reading device identity information fails, try seeing if the info + can be accessed using a "DEVICE PACKET" command. This way we can + at least get device info. + + Modified Makefile to automatically tag CVS archive on issuance of + a release + + Modified drive detection so minor device ID code showing ATA-3 rev + 0 (no SMART) is known to not be SMART capable. + + Now verify the checksum of the device ID data structure, and of the + attributes threshold structure. Before neither of these + structures had their checksums verified. + + New behavior vis-a-vis checksums. If they are wrong, we log + warning messages to stdout, stderr, and syslog, but carry on + anyway. All functions now call a checksumwarning routine if the + checksum doesn't vanish as it should. + + Changed Read Hard Disk Identity function to get fresh info from + the disk on each call rather than to use the values that were read + upon boot-up into the BIOS. This is the biggest change in this + release. The ioctl(device, HDIO_GET_IDENTITY, buf ) call should + be avoided in such code. Note that if people get garbled strings + for the model, serial no and firmware versions of their drives, + then blame goes here (the BIOS does the byte swapping for you, + apparently!) + + Function ataSmartSupport now looks at correct bits in drive + identity structure to verify first that these bits are valid, + before using them. + + Function ataIsSmartEnabled() written which uses the Drive ID state + information to tell if SMART is enabled or not. We'll carry this + along for the moment without using it. + + Function ataDoesSmartWork() guaranteed to work if the device + supports SMART. + + Replace some numbers by #define MACROS + + Wrote Function TestTime to return test time associated with each + different type of test. + + Thinking of the future, have added a new function called + ataSmartStatus2(). Eventually when I understand how to use the + TASKFILE API and am sure that this works correctly, it will + replace ataSmartStatus(). This queries the drive directly to + see if the SMART status is OK, rather than comparing thresholds to + attribute values ourselves. But I need to get some drives that fail + their SMART status to check it. + + +smartmontools-5.0-10 + Removed extraneous space before printing in some error messages + Fixed additional typos in documentation + Fixed some character buffers that were too short for their contents. + +smartmontools-5.0-9 + + Put project home path into all source files near the top + Corrected typos in the documentation + Modified Makefile so that Mandrake Cooker won't increment version number + (unless they happen to be working on my machine, which I doubt!) + +smartmontools-5.0-8: + + For IBM disks whose raw temp data includes three temps. print all + three + + print timestamps for error log to msec precision + + added -m option for Hitachi disks that store power on life in + minutes + + added -L option for printing self-test error logs + + in -l option, now print power on lifetime, so that one can see + when the error took place + + updated SMART structure definitions to ATA-5 spec + + added -p option + + added -f and -F options to enable/disable autosave threshold + parameters + + changed argv parsing to use getops -- elminate buffer overflow + vulnerability + + expanded and corrected documentation + + fixed problem with smartd. It did not actually call + ataSmartEnable()! Since the argument was left out, the test + always suceeded because it evaluated to a pointer to the function. + + smartd: closed open file descriptors if device does not support + smart. Note: this still needs to be fixed for SCSI devices + + +smartmontools-5.0-0 STARTED with smartsuite-2.1-2 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c1ca8ca --- /dev/null +++ b/INSTALL @@ -0,0 +1,637 @@ +Smartmontools installation instructions +======================================= + +$Id: INSTALL,v 1.62 2005/10/22 17:11:39 chrfranke Exp $ + +Please also see the smartmontools home page: +http://smartmontools.sourceforge.net/ + +Table of contents: + +[1] System requirements +[2] Installing from CVS +[3] Installing from source tarball +[4] Guidelines for different Linux distributions +[5] Guidelines for FreeBSD +[6] Guidelines for Darwin +[7] Guidelines for NetBSD +[8] Guidelines for Solaris +[9] Guidelines for Cygwin +[10] Guidelines for Windows +[11] Guidelines for OS/2, eComStation +[12] Guidelines for OpenBSD +[13] Comments +[14] Detailed description of ./configure options + +[1] System requirements +======================= + + A) Linux + + Any Linux distribution will support smartmontools if it has a + kernel version greater than or equal to 2.2.14. So any recent + Linux distribution should support smartmontools. + + There are two parts of smartmontools that may require a patched or + nonstandard kernel: + + (1) To get the ATA RETURN SMART STATUS command, the kernel needs + to support the HDIO_DRIVE_TASK ioctl(). + + (2) To run Selective Self-tests, the kernel needs to support the + HDIO_DRIVE_TASKFILE ioctl(). + + If your kernel does not support one or both of these ioctls, then + smartmontools will "mostly" work. The things that don't work will + give you harmless warning messages. + + Although "not officially supported" by the developers, smartmontools + has also been successfully build and run on a legacy Linux system + with kernel 2.0.33 and libc.so.5. On such systems, the restrictions + above apply. + + For item (1) above, any 2.4 or 2.6 series kernel will provide + HDIO_DRIVE_TASK support. Some 2.2.20 and later kernels also + provide this support IF they're properly patched and + configured. [Andre Hedrick's IDE patches may be found at + http://www.funet.fi/pub/linux/kernel/people/hedrick/ide-2.2.20/ or + are available from your local kernel.org mirror. They are not + updated for 2.2.21 or later, and may contain a few bugs.]. + If the configuration option CONFIG_IDE_TASK_IOCTL + exists in your 2.2.X kernel source code tree, then your 2.2.X + kernel will probably support this ioctl. [Note that this kernel + configuration option does NOT need to be enabled. Its presence + merely indicates that the required HDIO_DRIVE_TASK ioctl() is + supported.] + + For item (2) above, your kernel must be configured with the kernel + configuration option CONFIG_IDE_TASKFILE_IO enabled. This + configuration option is present in all 2.4 and 2.6 series + kernels. Some 2.2.20 and later kernels also provide this support + IF they're properly patched and configured as described above. + + Please see FAQ section of the URL above for additional details. + + If you are using 3ware controllers, for full functionality you + must either use version 1.02.00.037 or greater of the 3w-xxxx + driver, or patch earlier 3ware 3w-xxxx drivers. See + http://smartmontools.sourceforge.net/3w-xxxx.txt + for the patch. The version 1.02.00.037 3w-xxxx.c driver was + incorporated into kernel 2.4.23-bk2 on 3 December 2003 and into + kernel 2.6.0-test5-bk11 on 23 September 2003. + + B) FreeBSD + + For FreeBSD support, a 5-current kernel that includes ATAng is + required in order to support ATA drives. Even current versions of + ATAng will not support 100% operation, as the SMART status can not + be reliably retrieved. There is patch pending approval of the + ATAng driver maintainer that will address this issue. + + C) Solaris + + The SCSI code has been tested on a variety of Solaris 8 and 9 + systems. ATA/IDE code only works on SPARC platform. All tested + kernels worked correctly. + + D) NetBSD/OpenBSD + + The code was tested on a 1.6ZG (i.e., 1.6-current) system. It should + also function under 1.6.1 and later releases (unverified). Currently + it doesn't support ATA devices on 3ware RAID controllers. + + E) Cygwin + + The code was tested on Cygwin 1.5.7, 1.5.11 and 1.5.18. It should also + work on other recent releases. + + Release 1.5.15 or later is recommended for Cygwin smartd. Older versions + do not provide syslogd support. + + Both Cygwin and Windows versions of smartmontools share the same code + to access the IDE/ATA or SCSI devices. The information in the "Windows" + section below also applies to the Cygwin version. + + F) Windows + + The code was tested on Windows 98SE, NT4(SP5,SP6), 2000(SP4) and + XP(no SP,SP1a,SP2). It should also work on Windows 95(OSR2), 98, + ME and 2003. + + 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/default.aspx?scid=kb;en-us;265854). + + 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 on 9x/ME. This is the case if e.g. the optional + "IDE miniport driver" is installed on a system with VIA chipset. + + On NT4/2000/XP, also other ATA or SATA devices are supported if + the device driver implements the SMART IOCTL. + + The IDE/ATA read log command (smartctl -l, --log, -a, --all) is + not supported by the SMART IOCTL of NT4/2000/XP. Undocumented + and possibly buggy system calls are used for this purpose, + see WARNINGS file for details. + + SCSI devices are supported on all versions of Windows. An installed + ASPI interface (WNASPI32.DLL) is required to access SCSI devices. + The code was tested with Adaptec Windows ASPI drivers 4.71.2. + (http://www.adaptec.com/worldwide/support/drivers_by_product.jsp?cat=/Product/ASPI-4.70) + + G) MacOS/Darwin + + The code was tested on MacOS 10.3.4. It should work from 10.3 + forwards. It doesn't support 10.2. + + There's an important limitation (see WARNINGS); due to bugs in + the libraries used, you cannot run a short test or switch SMART + support off on a drive; if you try, you will just run an extended + test or switch SMART support on. So don't panic when your "short" + test seems to be taking hours. + + It's also not possible at present to control when the offline + routine runs. If your drive doesn't have it running automatically by + default, you can't run it at all. + + SCSI devices are not currently supported. Detecting the power + status of a drive is also not currently supported. + + To summarize this, from another point of view, the things that + are not supported fall into two categories: + + * Can't be implemented easily without more kernel-level support, + so far as I know: + - running immediate offline, conveyance, or selective tests + - running any test in captive mode + - aborting tests + - switching automatic offline testing on or off + - support for SCSI + - checking the power mode [-n Directive of smartd] (this is not + completely impossible, but not by using a documented API) + + * Are implemented, but don't work due to OS bugs: + - switching off SMART (switching *on* works fine) + - switching off auto-save (but why would you want to?) + - running the short test (that leaves you with only the extended test) + + The last set have been filed in Apple's bug tracking system and + hopefully will be fixed in the next major version of Mac OS. + + However, some things do work well. For ATA devices, all the + informational output is available, unless you want something that only + an offline test updates. + + H) OS/2, eComStation + + The code was tested on eComStation 1.1, but it should work on all versions + of OS/2. + Innotek LibC 0.5 runtime is required. + Currently only ATA disks are supported, SCSI support will be added. + + +[2] Installing from CVS +======================= + Get the sources from the CVS repository: + cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login + cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5 + (when prompted for a password, just press Enter) + + Then type: + ./autogen.sh + and continue with step [3] below, skipping the "unpack the tarball" step. + + Further details of using CVS can be found at the URL above. + + The autogen.sh command is ONLY required when installing from + CVS. You need GNU Autoconf (version 2.50 or greater), GNU Automake + (version 1.6 or greater) and their dependencies installed in order + to run it. You can get these here: + http://www.gnu.org/directory/GNU/autoconf.html + http://www.gnu.org/directory/GNU/automake.html + +[3] Installing from the source tarball +====================================== + + If you are NOT installing from CVS, then unpack the tarball: + tar zxvf smartmontools-5.VERSION.tar.gz + + Then: + ./configure + make + make install (you may need to be root to do this) + + As shown (with no options to ./configure) this defaults to the + following set of installation directories: + --prefix=/usr/local + --sbindir=/usr/local/sbin + --sysconfdir=/usr/local/etc + --mandir=/usr/local/share/man + --with-docdir=/usr/local/share/doc/smartmontools-VERSION + --with-initscriptdir=/usr/local/etc/rc.d/init.d + --disable-sample + + These will usually not overwrite existing "distribution" installations on + Linux Systems since the FHS reserves this area for use by the system + administrator. + + For different installation locations or distributions, simply add + arguments to ./configure as shown in [4] below. + + If you wish to alter the default C compiler flags, set an + environment variable CFLAGS='your options' before doing + ./configure, or else do: + make CFLAGS='your options' + +[4] Guidelines for different Linux distributions +================================================ + +Note: Please send corrections/additions to: +smartmontools-support@lists.sourceforge.net + +Debian: + If you don't want to overwrite any distribution package, use: + ./configure + +Filesystem Hierarchy Standard (FHS, http://www.pathname.com/fhs/): + ./configure --sbindir=/usr/local/sbin \ + --sysconfdir=/usr/local/etc \ + --mandir=/usr/local/man \ + --with-initscriptdir=/usr/local/etc/rc.d/init.d \ + --with-docdir=/usr/local/share/doc/smartmontools-VERSION + +Red Hat: + ./configure --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --with-initscriptdir=/etc/rc.d/init.d \ + --with-docdir=/usr/share/doc/smartmontools-VERSION + +Slackware: + If you don't want to overwrite any "distribution" package, use: + ./configure + + Otherwise use: + ./configure --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --with-initscriptdir=/etc/rc.d \ + --with-docdir=/usr/share/doc/smartmontools-VERSION + + And + removepkg smartmontools smartsuite (only root can do this) + before make install + + The init script works on Slackware. You just have to add an entry like + the following in /etc/rc.d/rc.M or /etc/rc.d/rc.local: + + if [ -x /etc/rc.d/smartd ]; then + . /etc/rc.d/smartd start + fi + + To disable it: + chmod 644 /etc/rc.d/smartd + + For a list of options: + /etc/rc.d/smartd + +SuSE: + ./configure --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --with-initscriptdir=/etc/init.d \ + --with-docdir=/usr/share/doc/packages/smartmontools-VERSION + +[5] Guidelines for FreeBSD +========================== + To match the way it will installed when it becomes available as a PORT, use + the following: + + ./configure --prefix=/usr/local \ + --with-initscriptdir=/usr/local/etc/rc.d/ \ + --with-docdir=/usr/local/share/doc/smartmontools-VERSION \ + --enable-sample + + Also, it is important that you use GNU make (gmake from /usr/ports/devel/gmake) + to build smartmontools, as the default FreeBSD make doesn't know how to build + the man pages. + + NOTE: --enable-sample will cause the smartd.conf and smartd RC files to + be installed with the string '.sample' append to the name, so you will end + up with the following: + /usr/local/etc/smartd.conf.sample + /usr/local/etc/rc.d/smartd.sample + + +[6] Guidelines for Darwin +========================= + ./configure --with-initscriptdir=/Library/StartupItems + + +[7] Guidelines for NetBSD/OpenBSD +================================= + ./configure --prefix=/usr/pkg \ + --with-docdir=/usr/pkg/share/doc/smartmontools + + On OpenBSD, it is important that you use GNU make (gmake from + /usr/ports/devel/gmake) to build smartmontools, as the BSD make doesn't + know how to make the manpages. + +[8] Guidelines for Solaris +========================== + + smartmontools has been partially but not completely ported to + Solaris. It includes complete SCSI support but no ATA or 3ware + support. It can be compiled with either cc or gcc. To compile + with gcc: + + ./configure [args] + make + + To compile with Sun cc: + + setenv CC cc [csh syntax], or + CC=cc [sh syntax] + ./configure [args] + make + + The correct arguments [args] to configure are: + --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --with-docdir=/usr/share/doc/smartmontools-VERSION \ + --with-initscriptdir=/etc/init.d + + To start the script automatically on bootup, create hardlinks that + indicate when to start/stop in: + /etc/rc[S0123].d/ + pointing to /etc/init.d/smartd. Create: + Ksmartd in rcS.d, rc0.d, rc1.d, rc2.d + Ssmartd in rc3.d + where is related to such that the higher snum is the + lower knum must be. + + On usual configuration, '95' would be suitable for and '05' + for respectively. If you choose these value, you can + create hardlinks by: + + cd /etc + sh -c 'for n in S 0 1 2; do ln init.d/smartd rc$n.d/K05smartd; done' + sh -c 'for n in 3 ; do ln init.d/smartd rc$n.d/S95smartd; done' + +[9] Guidelines for Cygwin +========================= + +Same as Red Hat: + ./configure --prefix=/usr \ + --sysconfdir=/etc \ + --mandir='${prefix}/share/man' + + OR EQUIVALENTLY + ./configure --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --with-initscriptdir=/etc/rc.d/init.d \ + --with-docdir=/usr/share/doc/smartmontools-VERSION + + Using DOS text file type as default for the working directories ("textmode" + mount option) is not recommended. Building the binaries and man pages using + "make" is possible, but "make dist" and related targets work only with UNIX + file type ("binmode" mount option) set. The "autogen.sh" script prints a + warning if DOS type is selected. + +[10] Guidelines for Windows +========================== + +To compile the Windows release with MinGW, use the following on Cygwin: + + ./configure --build=mingw32 + make + + Instead of using "make install", copy the .exe files into + some directory in the PATH. + +To build the Windows binary distribution, use: + + make dist-win32 + + This builds the distribution in directory + + ./smartmontools-VERSION.win32/ + + and packs it into + + ./smartmontools-VERSION.win32.zip + + Additional make targets are distdir-win32 to build the directory + only and cleandist-win32 for cleanup. + + The binary distribution includes all documentation files converted + to DOS text file format and *.html and *.txt preformatted man pages. + The tools unix2dos.exe (package cygutils) and zip.exe (package zip + or a native Win32 release of Info-ZIP, http://www.info-zip.org) are + necessary but may be not installed by Cygwin's default settings. + + It is also possible to compile smartmontools with MSVC 6.0. + The project files (smartmontools_vc6.dsw, smart{ctl,d}_vc6.dsp) are + included in CVS (but not in source tarball). The config_vc6.h is no + longer maintained in CVS. The command: + + make config-vc6 + + builds config_vc6.h from MinGW's config.h. Unlike MinGW, MSVC 6.0 + can also be used to build the syslog message file tool syslogevt.exe. + See smartd man page for usage information about this tool. + + +[11] Guidelines for OS/2, eComStation +===================================== + +To compile the OS/2 code, please run + + ./os_os2/configure.os2 + make + make install + +[12] Guidelines for OpenBSD +========================== + To match the way it will installed when it becomes available as a PORT, use + the following: + + ./configure --prefix=/usr/local \ + --sysconfdir=/etc + --with-initscriptdir=/usr/local/share/doc/smartmontools-VERSION \ + --with-docdir=/usr/local/share/doc/smartmontools-VERSION \ + --enable-sample + + It is important that you use GNU make (gmake from /usr/ports/devel/gmake) + to build smartmontools, as the default OpenBSD make doesn't know how to build + the man pages. + + NOTE1: --with-initscriptdir installs a SystemV startup script. It really + should be --without-initscriptdir, but the Makefile code is incorrect and + trys to install the initscript (smartd) to /no. So, an interim fix it to + set the initscript dir to the doc dir. + + NOTE2: --enable-sample will cause the smartd.conf and smartd RC files to + be installed with the string '.sample' append to the name, so you will end + up with the following: + /usr/local/etc/smartd.conf.sample + /usr/local/etc/rc.d/smartd.sample + +[13] Comments +============ + +To compile from another directory, you can replace the step + ./configure [options] +by the following: + mkdir objdir + cd objdir + ../configure [options] + +To install to another destination (used mainly by package maintainers, +or to examine the package contents without risk of modifying any +system files) you can replace the step: + make install +with: + make DESTDIR=/home/myself/smartmontools-package install + +Use a full path. Paths like ~/smartmontools-package may not work. + +After installing smartmontools, you can read the man pages, and try +out the commands: + +man smartd.conf +man smartctl +man smartd + +/usr/sbin/smartctl -s on -o on -S on /dev/hda (only root can do this) +/usr/sbin/smartctl -a /dev/hda (only root can do this) + +Note that the default location for the manual pages are +/usr/share/man/man5 and /usr/share/man/man8. If "man" doesn't find +them, you may need to add /usr/share/man to your MANPATH environment +variable. + +Source and binary RPM packages are available at +http://sourceforge.net/project/showfiles.php?group_id=64297 + +Refer to http://smartmontools.sourceforge.net/index.html#howtodownload +for any additional download and installation instructions. + +The following files are installed if ./configure has no options: + +/usr/local/sbin/smartd [Executable daemon] +/usr/local/sbin/smartctl [Executable command-line utility] +/usr/local/etc/smartd.conf [Configuration file for smartd daemon] +/usr/local/etc/rc.d/init.d/smartd [Init/Startup script for smartd] +/usr/local/share/man/man5/smartd.conf.5 [Manual page] +/usr/local/share/man/man8/smartctl.8 [Manual page] +/usr/local/share/man/man8/smartd.8 [Manual page] +/usr/local/share/doc/smartmontools-5.X/AUTHORS [Information about the authors and developers] +/usr/local/share/doc/smartmontools-5.X/CHANGELOG [A log of changes. Also see CVS] +/usr/local/share/doc/smartmontools-5.X/COPYING [GNU General Public License Version 2] +/usr/local/share/doc/smartmontools-5.X/INSTALL [Installation instructions: what you're reading!] +/usr/local/share/doc/smartmontools-5.X/NEWS [Significant bugs discovered in old versions] +/usr/local/share/doc/smartmontools-5.X/README [Overview] +/usr/local/share/doc/smartmontools-5.X/TODO [Things that need to be done/fixed] +/usr/local/share/doc/smartmontools-5.X/WARNINGS [Systems where lockups or other serious problems were reported] +/usr/local/share/doc/smartmontools-5.X/smartd.conf [Example configuration file for smartd] +/usr/local/share/doc/smartmontools-5.X/examplescripts [Executable scripts for -M exec of smartd.conf (4 files)] + +The commands: + +make htmlman +make txtman + +may be used to build .html and .txt preformatted man pages. +These are used by the dist-win32 make target to build the Windows +distribution. +The commands also work on other operating system configurations +if suitable versions of man2html, groff and grotty are installed. +On systems without man2html, the following command should work +if groff is available: + +make MAN2HTML='groff -man -Thtml' htmlman + + +[14] Detailed description of arguments to configure command +=========================================================== + +When you type: +./configure [options] +there are six particularly important variables that affect where the +smartmontools software is installed. The variables are listed here, +with their default values in square brackets, and the quantities that +they affect described following that. This is a very wide table: please read +it in a wide window. + +OPTIONS DEFAULT AFFECTS +------- ------- ------- +--prefix /usr/local Please see below +--sbindir ${prefix}/sbin Directory for smartd/smartctl executables; + Contents of smartd/smartctl man pages +--mandir ${prefix}/share/man Directory for smartctl/smartd/smartd.conf man pages +--sysconfdir ${prefix}/etc Directory for smartd.conf; + Contents of smartd executable; + Contents of smartd/smartd.conf man pages; + Directory for rc.d/init.d/smartd init script +--with-initscriptdir ${sysconfdir}/init.d/rc.d Location of init scripts +--with-docdir ${prefix}/share/doc/smartmontools-5.X Location of the documentation +--enable-sample --disable-sample Adds the string '.sample' to the names of the smartd.conf file and the smartd RC file + +Here's an example: +If you set --prefix=/home/joe and none of the other four +variables then the different directories that are used would be: +--sbindir /home/joe/sbin +--mandir /home/joe/share/man +--sysconfdir /home/joe/etc +--with-initscriptdir /home/joe/etc/init.d/rc.d +--with-docdir /home/joe/doc/smartmontools-5.X + +This is useful for test installs in a harmless subdirectory somewhere. + +Here are the four possible cases for the four variables above: + +Case 1: +--prefix not set +--variable not set +===> VARIABLE gets default value above + +Case 2: +--prefix set +--variable not set +===> VARIABLE gets PREFIX/ prepended to default value above + +Case 3: +--prefix not set +--variable set +===> VARIABLE gets value that is set + +Case 4: +--prefix is set +--variable is set +===> PREFIX is IGNORED, VARIABLE gets value that is set + + +Here are the differences with and without --enable-sample, assuming +no other options specified (see above for details) + +Case 1: +--enable-sample provided +==> Files installed are: + /usr/local/etc/smartd.conf.sample + /usr/local/etc/rc.d/init.d/smartd.sample + +Case 2: +--disable-sample provided or parameter left out +==> Files installed are: + /usr/local/etc/smartd.conf + /usr/local/etc/rc.d/init.d/smartd + +Additional information about using configure can be found here: +http://www.gnu.org/software/autoconf/manual/autoconf-2.57/html_mono/autoconf.html#SEC139 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9bc740d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,436 @@ +## Process this file with automake to produce Makefile.in +# +# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $ +# + +@SET_MAKE@ + +AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\" + +sbin_PROGRAMS = smartd \ + smartctl + +smartd_SOURCES = smartd.c \ + smartd.h \ + atacmdnames.c \ + atacmdnames.h \ + atacmds.c \ + atacmds.h \ + ataprint.c \ + ataprint.h \ + extern.h \ + int64.h \ + knowndrives.c \ + knowndrives.h \ + scsicmds.c \ + scsicmds.h \ + scsiprint.c \ + scsiprint.h \ + utility.c \ + utility.h + +smartd_LDADD = @os_deps@ @os_libs@ +smartd_DEPENDENCIES = @os_deps@ +EXTRA_smartd_SOURCES = os_darwin.c \ + os_darwin.h \ + os_linux.c \ + os_linux.h \ + os_freebsd.c \ + os_freebsd.h \ + os_netbsd.c \ + os_netbsd.h \ + os_openbsd.c \ + os_openbsd.h \ + os_solaris.c \ + os_solaris.h \ + os_solaris_ata.s \ + os_win32.c \ + os_generic.c \ + os_generic.h + + +if OS_WIN32_MINGW + +smartd_SOURCES += \ + posix/regex.h \ + posix/regex.c \ + os_win32/daemon_win32.h \ + os_win32/daemon_win32.c \ + os_win32/hostname_win32.h \ + os_win32/hostname_win32.c \ + os_win32/syslog.h \ + os_win32/syslog_win32.c + +# Included by regex.c: +EXTRA_smartd_SOURCES += \ + posix/regcomp.c \ + posix/regexec.c \ + posix/regex_internal.c \ + posix/regex_internal.h + +endif + +smartctl_SOURCES= smartctl.c \ + smartctl.h \ + atacmdnames.c \ + atacmdnames.h \ + atacmds.c \ + atacmds.h \ + ataprint.c \ + ataprint.h \ + extern.h \ + int64.h \ + knowndrives.c \ + knowndrives.h \ + scsicmds.c \ + scsicmds.h \ + scsiprint.c \ + scsiprint.h \ + utility.c \ + utility.h + +smartctl_LDADD = @os_deps@ @os_libs@ +smartctl_DEPENDENCIES = @os_deps@ +EXTRA_smartctl_SOURCES = os_linux.c \ + os_linux.h \ + os_freebsd.c \ + os_freebsd.h \ + os_netbsd.c \ + os_netbsd.h \ + os_openbsd.c \ + os_openbsd.h \ + os_solaris.c \ + os_solaris.h \ + os_win32.c \ + os_generic.c \ + os_generic.h + +if OS_WIN32_MINGW + +smartctl_SOURCES += \ + posix/regex.h \ + posix/regex.c \ + os_win32/syslog.h + +# Included by regex.c: +EXTRA_smartctl_SOURCES += \ + posix/regcomp.c \ + posix/regexec.c \ + posix/regex_internal.c \ + posix/regex_internal.h + +endif + +if OS_SOLARIS +# This block is required because Solaris uses manual page section 1m +# for administrative command (linux/freebsd use section 8) and Solaris +# uses manual page section 4 for file formats (linux/freebsd use +# section 5). Automake can deal cleanly with man page sections 1-8 +# and n, but NOT with sections of the form 1m. +extra_MANS = smartd.conf.4 \ + smartctl.1m \ + smartd.1m +install-man: $(extra_MANS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(mandir)/man4 + $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m + for i in $(extra_MANS); do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \ + done +uninstall-man: + @$(NORMAL_UNINSTALL) + for i in $(extra_MANS); do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst"; \ + rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \ + done +%.1m: %.8 + awk '/^.TH/ {$$3="1m"} {print}' < $< | \ + sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \ + -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \ + -e 's/smartctl\(.*\)(8)/smartctl\1(1m)/g' \ + -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \ + -e 's|/var/log/messages|/var/adm/messages|g' \ + -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@ +%.4: %.5 + awk '/^.TH/ {$$3="4"} {print}' < $< | \ + sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \ + -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \ + -e 's/smartctl\(.*\)(8)/smartdctl\1(1m)/g' \ + -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \ + -e 's|/var/log/messages|/var/adm/messages|g' \ + -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@ +else +# For systems that adopts traditional manner +man_MANS = smartd.conf.5 \ + smartctl.8 \ + smartd.8 +endif + +docsdir=$(docdir) +docs_DATA = AUTHORS \ + CHANGELOG \ + COPYING \ + INSTALL \ + NEWS \ + README \ + TODO \ + WARNINGS \ + smartd.conf + +sysconf_DATA = smartd.conf$(smartd_suffix) + +if SMARTD_SUFFIX +smartd.conf$(smartd_suffix): smartd.conf + cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix) +endif + +EXTRA_DIST = smartmontools.spec \ + smartd.initd.in \ + smartd.8.in \ + smartctl.8.in \ + smartd.conf.5.in \ + smartd.conf \ + autogen.sh \ + os_darwin/SMART.in \ + os_darwin/StartupParameters.plist \ + os_darwin/English_Localizable.strings \ + $(docs_DATA) + +CLEANFILES = smartd.conf.5 \ + smartd.conf.4 \ + smartd.8 \ + smartd.1m \ + smartd.8.html \ + smartd.8.txt \ + smartctl.8 \ + smartctl.1m \ + smartctl.8.html \ + smartctl.8.txt \ + smartd.conf.5.html \ + smartd.conf.5.txt \ + smartd.initd \ + SMART + +if SMARTD_SUFFIX +CLEANFILES += smartd.conf$(smartd_suffix) +endif + + +smartd.conf.5.in: smartd.8.in + sed '1,/STARTINCLUDE/ D;/ENDINCLUDE/,$$D' < $(srcdir)/smartd.8.in > $(top_builddir)/tmp.directives + sed '/STARTINCLUDE/,$$D' < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.head + sed '1,/ENDINCLUDE/D' < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.tail + cat $(top_builddir)/tmp.head > $(srcdir)/smartd.conf.5.in + echo '.\" STARTINCLUDE' >> $(srcdir)/smartd.conf.5.in + cat $(top_builddir)/tmp.directives >> $(srcdir)/smartd.conf.5.in + echo '.\" ENDINCLUDE' >> $(srcdir)/smartd.conf.5.in + cat $(top_builddir)/tmp.tail >> $(srcdir)/smartd.conf.5.in + rm -f $(top_builddir)/tmp.head $(top_builddir)/tmp.tail $(top_builddir)/tmp.directives + +if OS_DARWIN +initd_DATA = SMART \ + os_darwin/StartupParameters.plist \ + os_darwin/English_Localizable.strings + +initd_install_name = SMART + +initd_DATA_install = install-initdDATA-darwin + +SMART : os_darwin/SMART.in + sed "s|/usr/sbin/|$(sbindir)/|" $< > $@ + +install-initdDATA-darwin: $(initd_DATA) + $(mkinstalldirs) $(DESTDIR)$(initddir) + $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART + $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources + $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART + $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \ + $(DESTDIR)$(initddir)/SMART/StartupParameters.plist + for i in English ; do \ + RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \ + $(mkinstalldirs) $$RDIR ;\ + $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \ + $$RDIR/Localizable.strings ; \ + done + @echo -e "\n\n####################################################################\n#" + @echo -e "# PLEASE READ THIS BOX!\n#" + @echo -e "# To manually start the smartd daemon, run:\n# ${initddir}/SMART/SMART start\n#" + @echo -e "# To automatically start smartd on bootup, add the line:\n# SMARTd=-YES-\n# to /etc/hostconfig\n#" + @echo -e "# smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n# man smartd" + @echo -e "# to learn about it. A sample configuration file can be found in:\n# ${docdir}\n#" + @echo -e "####################################################################\n\n" + +else + +initd_DATA = smartd.initd + +smartd.initd: $(srcdir)/smartd.initd.in Makefile + sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/smartd.initd.in > $@ + +initd_install_name = smartd$(smartd_suffix) + +initd_DATA_install = install-initdDATA-generic + +install-initdDATA-generic: $(initd_DATA) + $(mkinstalldirs) $(DESTDIR)$(initddir) + $(INSTALL_SCRIPT) $(top_builddir)/smartd.initd $(DESTDIR)$(initddir)/smartd$(smartd_suffix) + @echo -e "\n\n####################################################################\n#" + @echo -e "# PLEASE READ THIS BOX!\n#" + @echo -e "# To manually start the smartd daemon, run:\n# ${initddir}/smartd start\n#" + @echo -e "# To automatically start smartd on bootup, run:\n# /sbin/chkconfig --add smartd\n#" + @echo -e "# smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n# man smartd" + @echo -e "# to learn about it. A sample configuration file can be found in:\n# ${docdir}\n#" + @echo -e "####################################################################\n\n" + +endif + +install-initdDATA : $(initd_DATA_install) + +uninstall-initdDATA: + rm -rf $(DESTDIR)$(initddir)/$(initd_install_name) + +uninstall-docsDATA: + rm -rf $(DESTDIR)$(docsdir) + +smart%: $(srcdir)/smart%.in Makefile + sed "s|CURRENT_CVS_VERSION|$(releaseversion)|g" $< | \ + sed "s|CURRENT_CVS_DATE|$(smartmontools_release_date)|g" | \ + sed "s|CURRENT_CVS_TIME|$(smartmontools_release_time)|g" | \ + sed "s|/usr/local/share/man/|$(mandir)/|g" | \ + sed "s|/usr/local/sbin/|$(sbindir)/|g" | \ + sed "s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g" | \ + sed "s|/usr/local/share/doc/smartmontools-5.1/|$(docsdir)/|g" | \ + sed "s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g" > $@ + + +# Commands to convert man pages into .html and .txt +# TODO: configure +MAN2HTML = man2html +#MAN2HTML = groff -man -Thtml +MAN2TXT = groff -man -Tascii -P'-bcou' + +# Fix links in man2html output +FIXHTML = sed 's,,,g' \ + | sed 's,,,g' \ + | sed 's,\([^<]*\),\1,g' \ + | sed 's,\([^<]*\),\1,g' + +# Convert man pages into .html and .txt + +htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html + +txtman: smartctl.8.txt smartd.8.txt smartd.conf.5.txt + +%.5.html: %.5 + $(MAN2HTML) $< | $(FIXHTML) > $@ + +%.8.html: %.8 + $(MAN2HTML) $< | $(FIXHTML) > $@ + +%.5.txt: %.5 + $(MAN2TXT) $< > $@ + +%.8.txt: %.8 + $(MAN2TXT) $< > $@ + + + +if OS_WIN32_MINGW +# Definitions for Windows distribution + +distdir_win32 = $(PACKAGE)-$(VERSION).win32 +distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip + +exedir_win32 = $(distdir_win32)/bin +docdir_win32 = $(distdir_win32)/doc + +FILES_WIN32 = $(exedir_win32)/smartctl.exe \ + $(exedir_win32)/smartd.exe \ + $(docdir_win32)/AUTHORS.txt \ + $(docdir_win32)/CHANGELOG.txt \ + $(docdir_win32)/COPYING.txt \ + $(docdir_win32)/INSTALL.txt \ + $(docdir_win32)/NEWS.txt \ + $(docdir_win32)/README.txt \ + $(docdir_win32)/TODO.txt \ + $(docdir_win32)/WARNINGS.txt \ + $(docdir_win32)/smartd.conf \ + $(docdir_win32)/smartctl.8.html \ + $(docdir_win32)/smartctl.8.txt \ + $(docdir_win32)/smartd.8.html \ + $(docdir_win32)/smartd.8.txt \ + $(docdir_win32)/smartd.conf.5.html \ + $(docdir_win32)/smartd.conf.5.txt + +CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check + +# Textfile converter from cygutils +UNIX2DOS = unix2dos -D + +# Build Windows distribution + +dist-win32: $(distzip_win32) + +distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check + +$(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check + @rm -fv $(distzip_win32) + cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) . + +cleandist-win32: + rm -rf $(distdir_win32) distdir.mkdir syslogevt.check + +distdir.mkdir: + @test -d $(exedir_win32) || mkdir -pv $(exedir_win32) + @test -d $(docdir_win32) || mkdir -pv $(docdir_win32) + touch $@ + +syslogevt.check: + @if [ -f $(srcdir)/os_win32/syslogevt.exe ]; then \ + cp -pv $(srcdir)/os_win32/syslogevt.exe $(exedir_win32)/syslogevt.exe; \ + else echo "Warning: $(srcdir)/os_win32/syslogevt.exe missing."; fi + touch $@ + +$(exedir_win32)/%.exe: %.exe + cp -p $< $@ + strip -s $@ + touch -r $< $@ + +$(docdir_win32)/%: % + $(UNIX2DOS) < $< > $@ + touch -r $< $@ + +$(docdir_win32)/%.txt: $(srcdir)/% + $(UNIX2DOS) < $< > $@ + touch -r $< $@ + +$(docdir_win32)/%.conf: $(srcdir)/%.conf + $(UNIX2DOS) < $< > $@ + touch -r $< $@ + + +# Build config_vc6.h for MSVC 6 from MinGW config.h + +config-vc6: $(srcdir)/os_win32/config_vc6.h + +$(srcdir)/os_win32/config_vc6.h: config.h + sed '1i/* config_vc6.h. Generated by Makefile. */' $< | \ + sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \ + sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@ + +endif + +SUBDIRS= . examplescripts diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5d65d9f --- /dev/null +++ b/Makefile.in @@ -0,0 +1,1311 @@ +# Makefile.in generated by automake 1.9.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $ +# + + +SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) $(smartd_SOURCES) $(EXTRA_smartd_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +sbin_PROGRAMS = smartd$(EXEEXT) smartctl$(EXEEXT) +@OS_WIN32_MINGW_TRUE@am__append_1 = \ +@OS_WIN32_MINGW_TRUE@ posix/regex.h \ +@OS_WIN32_MINGW_TRUE@ posix/regex.c \ +@OS_WIN32_MINGW_TRUE@ os_win32/daemon_win32.h \ +@OS_WIN32_MINGW_TRUE@ os_win32/daemon_win32.c \ +@OS_WIN32_MINGW_TRUE@ os_win32/hostname_win32.h \ +@OS_WIN32_MINGW_TRUE@ os_win32/hostname_win32.c \ +@OS_WIN32_MINGW_TRUE@ os_win32/syslog.h \ +@OS_WIN32_MINGW_TRUE@ os_win32/syslog_win32.c + + +# Included by regex.c: +@OS_WIN32_MINGW_TRUE@am__append_2 = \ +@OS_WIN32_MINGW_TRUE@ posix/regcomp.c \ +@OS_WIN32_MINGW_TRUE@ posix/regexec.c \ +@OS_WIN32_MINGW_TRUE@ posix/regex_internal.c \ +@OS_WIN32_MINGW_TRUE@ posix/regex_internal.h + +@OS_WIN32_MINGW_TRUE@am__append_3 = \ +@OS_WIN32_MINGW_TRUE@ posix/regex.h \ +@OS_WIN32_MINGW_TRUE@ posix/regex.c \ +@OS_WIN32_MINGW_TRUE@ os_win32/syslog.h + + +# Included by regex.c: +@OS_WIN32_MINGW_TRUE@am__append_4 = \ +@OS_WIN32_MINGW_TRUE@ posix/regcomp.c \ +@OS_WIN32_MINGW_TRUE@ posix/regexec.c \ +@OS_WIN32_MINGW_TRUE@ posix/regex_internal.c \ +@OS_WIN32_MINGW_TRUE@ posix/regex_internal.h + +@SMARTD_SUFFIX_TRUE@am__append_5 = smartd.conf$(smartd_suffix) +@OS_WIN32_MINGW_TRUE@am__append_6 = $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(top_srcdir)/configure AUTHORS COPYING INSTALL NEWS TODO \ + config.guess config.sub depcomp install-sh missing +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \ + "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" \ + "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)" +sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(sbin_PROGRAMS) +am__smartctl_SOURCES_DIST = smartctl.c smartctl.h atacmdnames.c \ + atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \ + extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \ + scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \ + posix/regex.h posix/regex.c os_win32/syslog.h +@OS_WIN32_MINGW_TRUE@am__objects_1 = regex.$(OBJEXT) +am_smartctl_OBJECTS = smartctl.$(OBJEXT) atacmdnames.$(OBJEXT) \ + atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \ + scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \ + $(am__objects_1) +am__EXTRA_smartctl_SOURCES_DIST = os_linux.c os_linux.h os_freebsd.c \ + os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \ + os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \ + posix/regcomp.c posix/regexec.c posix/regex_internal.c \ + posix/regex_internal.h +smartctl_OBJECTS = $(am_smartctl_OBJECTS) +am__smartd_SOURCES_DIST = smartd.c smartd.h atacmdnames.c \ + atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \ + extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \ + scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \ + posix/regex.h posix/regex.c os_win32/daemon_win32.h \ + os_win32/daemon_win32.c os_win32/hostname_win32.h \ + os_win32/hostname_win32.c os_win32/syslog.h \ + os_win32/syslog_win32.c +@OS_WIN32_MINGW_TRUE@am__objects_3 = regex.$(OBJEXT) \ +@OS_WIN32_MINGW_TRUE@ daemon_win32.$(OBJEXT) \ +@OS_WIN32_MINGW_TRUE@ hostname_win32.$(OBJEXT) \ +@OS_WIN32_MINGW_TRUE@ syslog_win32.$(OBJEXT) +am_smartd_OBJECTS = smartd.$(OBJEXT) atacmdnames.$(OBJEXT) \ + atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \ + scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \ + $(am__objects_3) +am__EXTRA_smartd_SOURCES_DIST = os_darwin.c os_darwin.h os_linux.c \ + os_linux.h os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h \ + os_openbsd.c os_openbsd.h os_solaris.c os_solaris.h \ + os_solaris_ata.s os_win32.c os_generic.c os_generic.h \ + posix/regcomp.c posix/regexec.c posix/regex_internal.c \ + posix/regex_internal.h +smartd_OBJECTS = $(am_smartd_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I. +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CCASCOMPILE = $(CCAS) $(AM_CCASFLAGS) $(CCASFLAGS) +SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) \ + $(smartd_SOURCES) $(EXTRA_smartd_SOURCES) +DIST_SOURCES = $(am__smartctl_SOURCES_DIST) \ + $(am__EXTRA_smartctl_SOURCES_DIST) $(am__smartd_SOURCES_DIST) \ + $(am__EXTRA_smartd_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +man5dir = $(mandir)/man5 +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +docsDATA_INSTALL = $(INSTALL_DATA) +initdDATA_INSTALL = $(INSTALL_DATA) +sysconfDATA_INSTALL = $(INSTALL_DATA) +DATA = $(docs_DATA) $(initd_DATA) $(sysconf_DATA) +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +ASFLAGS = @ASFLAGS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OS_DARWIN_FALSE = @OS_DARWIN_FALSE@ +OS_DARWIN_TRUE = @OS_DARWIN_TRUE@ +OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@ +OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@ +OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@ +OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@ +SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +docdir = @docdir@ +exampledir = @exampledir@ +exec_prefix = @exec_prefix@ +gcc_have_attr_packed = @gcc_have_attr_packed@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +initddir = @initddir@ +install_sh = @install_sh@ +libc_have_working_snprintf = @libc_have_working_snprintf@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +os_deps = @os_deps@ +os_libs = @os_libs@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +releaseversion = @releaseversion@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +smartd_suffix = @smartd_suffix@ +smartmontools_release_date = @smartmontools_release_date@ +smartmontools_release_time = @smartmontools_release_time@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\" +smartd_SOURCES = smartd.c smartd.h atacmdnames.c atacmdnames.h \ + atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \ + knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \ + scsiprint.h utility.c utility.h $(am__append_1) +smartd_LDADD = @os_deps@ @os_libs@ +smartd_DEPENDENCIES = @os_deps@ +EXTRA_smartd_SOURCES = os_darwin.c os_darwin.h os_linux.c os_linux.h \ + os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c \ + os_openbsd.h os_solaris.c os_solaris.h os_solaris_ata.s \ + os_win32.c os_generic.c os_generic.h $(am__append_2) +smartctl_SOURCES = smartctl.c smartctl.h atacmdnames.c atacmdnames.h \ + atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \ + knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \ + scsiprint.h utility.c utility.h $(am__append_3) +smartctl_LDADD = @os_deps@ @os_libs@ +smartctl_DEPENDENCIES = @os_deps@ +EXTRA_smartctl_SOURCES = os_linux.c os_linux.h os_freebsd.c \ + os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \ + os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \ + $(am__append_4) + +# This block is required because Solaris uses manual page section 1m +# for administrative command (linux/freebsd use section 8) and Solaris +# uses manual page section 4 for file formats (linux/freebsd use +# section 5). Automake can deal cleanly with man page sections 1-8 +# and n, but NOT with sections of the form 1m. +@OS_SOLARIS_TRUE@extra_MANS = smartd.conf.4 \ +@OS_SOLARIS_TRUE@ smartctl.1m \ +@OS_SOLARIS_TRUE@ smartd.1m + +# For systems that adopts traditional manner +@OS_SOLARIS_FALSE@man_MANS = smartd.conf.5 \ +@OS_SOLARIS_FALSE@ smartctl.8 \ +@OS_SOLARIS_FALSE@ smartd.8 + +docsdir = $(docdir) +docs_DATA = AUTHORS \ + CHANGELOG \ + COPYING \ + INSTALL \ + NEWS \ + README \ + TODO \ + WARNINGS \ + smartd.conf + +sysconf_DATA = smartd.conf$(smartd_suffix) +EXTRA_DIST = smartmontools.spec \ + smartd.initd.in \ + smartd.8.in \ + smartctl.8.in \ + smartd.conf.5.in \ + smartd.conf \ + autogen.sh \ + os_darwin/SMART.in \ + os_darwin/StartupParameters.plist \ + os_darwin/English_Localizable.strings \ + $(docs_DATA) + +CLEANFILES = smartd.conf.5 smartd.conf.4 smartd.8 smartd.1m \ + smartd.8.html smartd.8.txt smartctl.8 smartctl.1m \ + smartctl.8.html smartctl.8.txt smartd.conf.5.html \ + smartd.conf.5.txt smartd.initd SMART $(am__append_5) \ + $(am__append_6) +@OS_DARWIN_FALSE@initd_DATA = smartd.initd +@OS_DARWIN_TRUE@initd_DATA = SMART \ +@OS_DARWIN_TRUE@ os_darwin/StartupParameters.plist \ +@OS_DARWIN_TRUE@ os_darwin/English_Localizable.strings + +@OS_DARWIN_FALSE@initd_install_name = smartd$(smartd_suffix) +@OS_DARWIN_TRUE@initd_install_name = SMART +@OS_DARWIN_FALSE@initd_DATA_install = install-initdDATA-generic +@OS_DARWIN_TRUE@initd_DATA_install = install-initdDATA-darwin + +# Commands to convert man pages into .html and .txt +# TODO: configure +MAN2HTML = man2html +#MAN2HTML = groff -man -Thtml +MAN2TXT = groff -man -Tascii -P'-bcou' + +# Fix links in man2html output +FIXHTML = sed 's,,,g' \ + | sed 's,,,g' \ + | sed 's,\([^<]*\),\1,g' \ + | sed 's,\([^<]*\),\1,g' + + +# Definitions for Windows distribution +@OS_WIN32_MINGW_TRUE@distdir_win32 = $(PACKAGE)-$(VERSION).win32 +@OS_WIN32_MINGW_TRUE@distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip +@OS_WIN32_MINGW_TRUE@exedir_win32 = $(distdir_win32)/bin +@OS_WIN32_MINGW_TRUE@docdir_win32 = $(distdir_win32)/doc +@OS_WIN32_MINGW_TRUE@FILES_WIN32 = $(exedir_win32)/smartctl.exe \ +@OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartd.exe \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/AUTHORS.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/CHANGELOG.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/COPYING.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/INSTALL.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/NEWS.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/README.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/TODO.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/WARNINGS.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.conf \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartctl.8.html \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartctl.8.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.8.html \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.8.txt \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.conf.5.html \ +@OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.conf.5.txt + + +# Textfile converter from cygutils +@OS_WIN32_MINGW_TRUE@UNIX2DOS = unix2dos -D +SUBDIRS = . examplescripts +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .o .obj .s +am--refresh: + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \ + cd $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ + rm -f "$(DESTDIR)$(sbindir)/$$f"; \ + done + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +smartctl$(EXEEXT): $(smartctl_OBJECTS) $(smartctl_DEPENDENCIES) + @rm -f smartctl$(EXEEXT) + $(LINK) $(smartctl_LDFLAGS) $(smartctl_OBJECTS) $(smartctl_LDADD) $(LIBS) +smartd$(EXEEXT): $(smartd_OBJECTS) $(smartd_DEPENDENCIES) + @rm -f smartd$(EXEEXT) + $(LINK) $(smartd_LDFLAGS) $(smartd_OBJECTS) $(smartd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmdnames.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmds.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ataprint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon_win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname_win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knowndrives.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_darwin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_freebsd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_generic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_linux.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_netbsd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_openbsd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_solaris.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsicmds.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsiprint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartctl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog_win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utility.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +regex.o: posix/regex.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.o -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regex.c' object='regex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c + +regex.obj: posix/regex.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.obj -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regex.c' object='regex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi` + +regcomp.o: posix/regcomp.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.o -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regcomp.c' object='regcomp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c + +regcomp.obj: posix/regcomp.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.obj -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regcomp.c' object='regcomp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi` + +regexec.o: posix/regexec.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.o -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regexec.c' object='regexec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c + +regexec.obj: posix/regexec.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.obj -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regexec.c' object='regexec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi` + +regex_internal.o: posix/regex_internal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.o -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regex_internal.c' object='regex_internal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c + +regex_internal.obj: posix/regex_internal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.obj -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='posix/regex_internal.c' object='regex_internal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi` + +daemon_win32.o: os_win32/daemon_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.o -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/daemon_win32.c' object='daemon_win32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c + +daemon_win32.obj: os_win32/daemon_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.obj -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/daemon_win32.c' object='daemon_win32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi` + +hostname_win32.o: os_win32/hostname_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.o -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/hostname_win32.c' object='hostname_win32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c + +hostname_win32.obj: os_win32/hostname_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.obj -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/hostname_win32.c' object='hostname_win32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi` + +syslog_win32.o: os_win32/syslog_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.o -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/syslog_win32.c' object='syslog_win32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c + +syslog_win32.obj: os_win32/syslog_win32.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.obj -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os_win32/syslog_win32.c' object='syslog_win32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi` + +.s.o: + $(CCASCOMPILE) -c $< + +.s.obj: + $(CCASCOMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: +install-man5: $(man5_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man5dir)" || $(mkdir_p) "$(DESTDIR)$(man5dir)" + @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 5*) ;; \ + *) ext='5' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \ + done +uninstall-man5: + @$(NORMAL_UNINSTALL) + @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 5*) ;; \ + *) ext='5' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man5dir)/$$inst"; \ + done +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done +install-docsDATA: $(docs_DATA) + @$(NORMAL_INSTALL) + test -z "$(docsdir)" || $(mkdir_p) "$(DESTDIR)$(docsdir)" + @list='$(docs_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(docsDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docsdir)/$$f'"; \ + $(docsDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docsdir)/$$f"; \ + done +install-sysconfDATA: $(sysconf_DATA) + @$(NORMAL_INSTALL) + test -z "$(sysconfdir)" || $(mkdir_p) "$(DESTDIR)$(sysconfdir)" + @list='$(sysconf_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \ + $(sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \ + done + +uninstall-sysconfDATA: + @$(NORMAL_UNINSTALL) + @list='$(sysconf_DATA)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \ + rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkdir_p) $(distdir)/os_darwin + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: install-docsDATA install-initdDATA install-man + +install-exec-am: install-sbinPROGRAMS install-sysconfDATA + +install-info: install-info-recursive + +@OS_SOLARIS_FALSE@install-man: install-man5 install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-docsDATA uninstall-info-am uninstall-initdDATA \ + uninstall-man uninstall-sbinPROGRAMS uninstall-sysconfDATA + +uninstall-info: uninstall-info-recursive + +@OS_SOLARIS_FALSE@uninstall-man: uninstall-man5 uninstall-man8 + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-recursive \ + clean-sbinPROGRAMS ctags ctags-recursive dist dist-all \ + dist-bzip2 dist-gzip dist-shar dist-tarZ dist-zip distcheck \ + distclean distclean-compile distclean-generic distclean-hdr \ + distclean-recursive distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-docsDATA install-exec install-exec-am install-info \ + install-info-am install-initdDATA install-man install-man5 \ + install-man8 install-sbinPROGRAMS install-strip \ + install-sysconfDATA installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-docsDATA \ + uninstall-info-am uninstall-initdDATA uninstall-man \ + uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS \ + uninstall-sysconfDATA + + +@SET_MAKE@ +@OS_SOLARIS_TRUE@install-man: $(extra_MANS) +@OS_SOLARIS_TRUE@ @$(NORMAL_INSTALL) +@OS_SOLARIS_TRUE@ $(mkinstalldirs) $(DESTDIR)$(mandir)/man4 +@OS_SOLARIS_TRUE@ $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m +@OS_SOLARIS_TRUE@ for i in $(extra_MANS); do \ +@OS_SOLARIS_TRUE@ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ +@OS_SOLARIS_TRUE@ else file=$$i; fi; \ +@OS_SOLARIS_TRUE@ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$inst | sed -e 's/^.*\///'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ +@OS_SOLARIS_TRUE@ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst"; \ +@OS_SOLARIS_TRUE@ $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \ +@OS_SOLARIS_TRUE@ done +@OS_SOLARIS_TRUE@uninstall-man: +@OS_SOLARIS_TRUE@ @$(NORMAL_UNINSTALL) +@OS_SOLARIS_TRUE@ for i in $(extra_MANS); do \ +@OS_SOLARIS_TRUE@ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ +@OS_SOLARIS_TRUE@ else file=$$i; fi; \ +@OS_SOLARIS_TRUE@ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$inst | sed -e 's/^.*\///'`; \ +@OS_SOLARIS_TRUE@ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ +@OS_SOLARIS_TRUE@ echo " rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst"; \ +@OS_SOLARIS_TRUE@ rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \ +@OS_SOLARIS_TRUE@ done +@OS_SOLARIS_TRUE@%.1m: %.8 +@OS_SOLARIS_TRUE@ awk '/^.TH/ {$$3="1m"} {print}' < $< | \ +@OS_SOLARIS_TRUE@ sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \ +@OS_SOLARIS_TRUE@ -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \ +@OS_SOLARIS_TRUE@ -e 's/smartctl\(.*\)(8)/smartctl\1(1m)/g' \ +@OS_SOLARIS_TRUE@ -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \ +@OS_SOLARIS_TRUE@ -e 's|/var/log/messages|/var/adm/messages|g' \ +@OS_SOLARIS_TRUE@ -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@ +@OS_SOLARIS_TRUE@%.4: %.5 +@OS_SOLARIS_TRUE@ awk '/^.TH/ {$$3="4"} {print}' < $< | \ +@OS_SOLARIS_TRUE@ sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \ +@OS_SOLARIS_TRUE@ -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \ +@OS_SOLARIS_TRUE@ -e 's/smartctl\(.*\)(8)/smartdctl\1(1m)/g' \ +@OS_SOLARIS_TRUE@ -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \ +@OS_SOLARIS_TRUE@ -e 's|/var/log/messages|/var/adm/messages|g' \ +@OS_SOLARIS_TRUE@ -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@ + +@SMARTD_SUFFIX_TRUE@smartd.conf$(smartd_suffix): smartd.conf +@SMARTD_SUFFIX_TRUE@ cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix) + +smartd.conf.5.in: smartd.8.in + sed '1,/STARTINCLUDE/ D;/ENDINCLUDE/,$$D' < $(srcdir)/smartd.8.in > $(top_builddir)/tmp.directives + sed '/STARTINCLUDE/,$$D' < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.head + sed '1,/ENDINCLUDE/D' < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.tail + cat $(top_builddir)/tmp.head > $(srcdir)/smartd.conf.5.in + echo '.\" STARTINCLUDE' >> $(srcdir)/smartd.conf.5.in + cat $(top_builddir)/tmp.directives >> $(srcdir)/smartd.conf.5.in + echo '.\" ENDINCLUDE' >> $(srcdir)/smartd.conf.5.in + cat $(top_builddir)/tmp.tail >> $(srcdir)/smartd.conf.5.in + rm -f $(top_builddir)/tmp.head $(top_builddir)/tmp.tail $(top_builddir)/tmp.directives + +@OS_DARWIN_TRUE@SMART : os_darwin/SMART.in +@OS_DARWIN_TRUE@ sed "s|/usr/sbin/|$(sbindir)/|" $< > $@ + +@OS_DARWIN_TRUE@install-initdDATA-darwin: $(initd_DATA) +@OS_DARWIN_TRUE@ $(mkinstalldirs) $(DESTDIR)$(initddir) +@OS_DARWIN_TRUE@ $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART +@OS_DARWIN_TRUE@ $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources +@OS_DARWIN_TRUE@ $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART +@OS_DARWIN_TRUE@ $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \ +@OS_DARWIN_TRUE@ $(DESTDIR)$(initddir)/SMART/StartupParameters.plist +@OS_DARWIN_TRUE@ for i in English ; do \ +@OS_DARWIN_TRUE@ RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \ +@OS_DARWIN_TRUE@ $(mkinstalldirs) $$RDIR ;\ +@OS_DARWIN_TRUE@ $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \ +@OS_DARWIN_TRUE@ $$RDIR/Localizable.strings ; \ +@OS_DARWIN_TRUE@ done +@OS_DARWIN_TRUE@ @echo -e "\n\n####################################################################\n#" +@OS_DARWIN_TRUE@ @echo -e "# PLEASE READ THIS BOX!\n#" +@OS_DARWIN_TRUE@ @echo -e "# To manually start the smartd daemon, run:\n# ${initddir}/SMART/SMART start\n#" +@OS_DARWIN_TRUE@ @echo -e "# To automatically start smartd on bootup, add the line:\n# SMARTd=-YES-\n# to /etc/hostconfig\n#" +@OS_DARWIN_TRUE@ @echo -e "# smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n# man smartd" +@OS_DARWIN_TRUE@ @echo -e "# to learn about it. A sample configuration file can be found in:\n# ${docdir}\n#" +@OS_DARWIN_TRUE@ @echo -e "####################################################################\n\n" + +@OS_DARWIN_FALSE@smartd.initd: $(srcdir)/smartd.initd.in Makefile +@OS_DARWIN_FALSE@ sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/smartd.initd.in > $@ + +@OS_DARWIN_FALSE@install-initdDATA-generic: $(initd_DATA) +@OS_DARWIN_FALSE@ $(mkinstalldirs) $(DESTDIR)$(initddir) +@OS_DARWIN_FALSE@ $(INSTALL_SCRIPT) $(top_builddir)/smartd.initd $(DESTDIR)$(initddir)/smartd$(smartd_suffix) +@OS_DARWIN_FALSE@ @echo -e "\n\n####################################################################\n#" +@OS_DARWIN_FALSE@ @echo -e "# PLEASE READ THIS BOX!\n#" +@OS_DARWIN_FALSE@ @echo -e "# To manually start the smartd daemon, run:\n# ${initddir}/smartd start\n#" +@OS_DARWIN_FALSE@ @echo -e "# To automatically start smartd on bootup, run:\n# /sbin/chkconfig --add smartd\n#" +@OS_DARWIN_FALSE@ @echo -e "# smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n# man smartd" +@OS_DARWIN_FALSE@ @echo -e "# to learn about it. A sample configuration file can be found in:\n# ${docdir}\n#" +@OS_DARWIN_FALSE@ @echo -e "####################################################################\n\n" + +install-initdDATA : $(initd_DATA_install) + +uninstall-initdDATA: + rm -rf $(DESTDIR)$(initddir)/$(initd_install_name) + +uninstall-docsDATA: + rm -rf $(DESTDIR)$(docsdir) + +smart%: $(srcdir)/smart%.in Makefile + sed "s|CURRENT_CVS_VERSION|$(releaseversion)|g" $< | \ + sed "s|CURRENT_CVS_DATE|$(smartmontools_release_date)|g" | \ + sed "s|CURRENT_CVS_TIME|$(smartmontools_release_time)|g" | \ + sed "s|/usr/local/share/man/|$(mandir)/|g" | \ + sed "s|/usr/local/sbin/|$(sbindir)/|g" | \ + sed "s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g" | \ + sed "s|/usr/local/share/doc/smartmontools-5.1/|$(docsdir)/|g" | \ + sed "s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g" > $@ + +# Convert man pages into .html and .txt + +htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html + +txtman: smartctl.8.txt smartd.8.txt smartd.conf.5.txt + +%.5.html: %.5 + $(MAN2HTML) $< | $(FIXHTML) > $@ + +%.8.html: %.8 + $(MAN2HTML) $< | $(FIXHTML) > $@ + +%.5.txt: %.5 + $(MAN2TXT) $< > $@ + +%.8.txt: %.8 + $(MAN2TXT) $< > $@ + +# Build Windows distribution + +@OS_WIN32_MINGW_TRUE@dist-win32: $(distzip_win32) + +@OS_WIN32_MINGW_TRUE@distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check + +@OS_WIN32_MINGW_TRUE@$(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check +@OS_WIN32_MINGW_TRUE@ @rm -fv $(distzip_win32) +@OS_WIN32_MINGW_TRUE@ cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) . + +@OS_WIN32_MINGW_TRUE@cleandist-win32: +@OS_WIN32_MINGW_TRUE@ rm -rf $(distdir_win32) distdir.mkdir syslogevt.check + +@OS_WIN32_MINGW_TRUE@distdir.mkdir: +@OS_WIN32_MINGW_TRUE@ @test -d $(exedir_win32) || mkdir -pv $(exedir_win32) +@OS_WIN32_MINGW_TRUE@ @test -d $(docdir_win32) || mkdir -pv $(docdir_win32) +@OS_WIN32_MINGW_TRUE@ touch $@ + +@OS_WIN32_MINGW_TRUE@syslogevt.check: +@OS_WIN32_MINGW_TRUE@ @if [ -f $(srcdir)/os_win32/syslogevt.exe ]; then \ +@OS_WIN32_MINGW_TRUE@ cp -pv $(srcdir)/os_win32/syslogevt.exe $(exedir_win32)/syslogevt.exe; \ +@OS_WIN32_MINGW_TRUE@ else echo "Warning: $(srcdir)/os_win32/syslogevt.exe missing."; fi +@OS_WIN32_MINGW_TRUE@ touch $@ + +@OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.exe: %.exe +@OS_WIN32_MINGW_TRUE@ cp -p $< $@ +@OS_WIN32_MINGW_TRUE@ strip -s $@ +@OS_WIN32_MINGW_TRUE@ touch -r $< $@ + +@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%: % +@OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ +@OS_WIN32_MINGW_TRUE@ touch -r $< $@ + +@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.txt: $(srcdir)/% +@OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ +@OS_WIN32_MINGW_TRUE@ touch -r $< $@ + +@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.conf: $(srcdir)/%.conf +@OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ +@OS_WIN32_MINGW_TRUE@ touch -r $< $@ + +# Build config_vc6.h for MSVC 6 from MinGW config.h + +@OS_WIN32_MINGW_TRUE@config-vc6: $(srcdir)/os_win32/config_vc6.h + +@OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/config_vc6.h: config.h +@OS_WIN32_MINGW_TRUE@ sed '1i/* config_vc6.h. Generated by Makefile. */' $< | \ +@OS_WIN32_MINGW_TRUE@ sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \ +@OS_WIN32_MINGW_TRUE@ sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@ +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..21aaf9e --- /dev/null +++ b/NEWS @@ -0,0 +1,230 @@ +smartmontools NEWS +------------------ +CVS ID: $Id: NEWS,v 1.29 2006/04/12 15:46:30 ballen4705 Exp $ + +The most up-to-date version of this file is: +http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/NEWS?sortby=date&view=markup + +Date 2006-04-12 +Summary: smartmontools release 5.36 (STABLE) +----------------------------------------------------------- +This is a stable smartmontools release. The 5.34 version +described just below was never officially released because +Bruce Allen decided to wait until Linux support for +accessing SATA devices through libata was in the official +kernel.org sources. Changes include: + +- Win 2000/XP:ability to cancel drive self-tests +- Additions to the table of known drives +- FreeBSD support for 3ware char device interface and + multiple 3ware cards +- Various cygwin improvements for running as service +- Works 'out of the box' with Linux SATA libata +- smartd option added to list test schedules +- smartctl option added to list part of drive database +- various improvements for SCSI disks and logs + + +Date 2005-04-19 +Summary: smartmontools release 5.34 (STABLE) +----------------------------------------------------------- +This is a stable smartmontools release. It includes: +- OS/2 and eComStation support +All Platforms: + - Printing of drive family info + - SCSI disks: output size of grown defect list + - Added info about drive family to 'smartctl -i' output. + - Added option ',q' for smartd '-n' directive to suppress + 'skipping checks' message which may spin up laptop disk. + - Added message if smartd '-n' check power mode spins up disk. +Cygwin and Windows: + - Added info about Windows version and service pack to banner line. + - Added support for smartd '-n' directive for Win2000/XP. + - Added support for READ_LOG for WinNT4 also. + - Fixed bug that prevents display of empty logs on Win2000/XP + - Fixed use of cached smart enabled state in 'smartctl -i' output. +Windows: + - Fixed bug that prevents running smartd as service on WinNT4. + + +Date 2004-9-5 +Summary: smartmontools release 5.33 (UNSTABLE/EXPERIMENTAL) +----------------------------------------------------------- +This is an unstable/experimental release of smartmontools. It includes + - support for Darwin/Mac OSX + - support for OpenBSD + - support for 3ware ATA RAID controllers under FreeBSD + - support for 3ware 9500 series SATA RAID controllers under + Linux. Use /dev/twa[0-15] devices to access these. + - support for 3ware character device interfaces /dev/twe[0-15] + under Linux. This allows (for example) Selective Self-tests. + - support for Marvell chip-set based SATA controllers under Linux. + - smartd mail now works also under Windows (using "Blat" mailer). + - smartd can now be run as a Windows service. +Please report sucess/failure with these items to the +smartmontools-support mailing list. + + +Date 2004-7-5 +Summary: smartmontools release 5.32 (STABLE) +----------------------------------------------------------- +This is an stable release of smartmontools. +Note added 2004/7/7: users building a Solaris/Intel version of the code should +modify the 'configure' file, changing "pc-*-solaris*" on line 106 +to read "*-pc-solaris*". Reference: +http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/configure.in?r1=1.83&r2=1.84 + + +Date: 2004-5-4 +Summary: smartmontools release 5.31 (UNSTABLE/EXPERIMENTAL) +----------------------------------------------------------- +This is an unstable/experimental release of smartmontools. It includes +several new features: +- Windows smartd daemon +- smartd now monitors current and pending sector counts +- Support for ATA-7 selective self-test features (Linux/NetBSD only) + Please report sucess/failure with this option to the smartmontools-support + mailing list. + +Date: 2004-3-6 +Summary: smartmontools release 5.30 (STABLE) +-------------------------------------------- +This is a stable release of smartmontools: the first stable release +since 5.26. +- KNOWN BUG (identified/fixed by CF): smartd will segv and crash if + the configuration file /etc/smartd.conf contains NO valid entries. + This bug was introduced in version 1.259 of smartd.c by BA and + is present in smartmontools releases 5.27-5.30 inclusive. This can + be fixed by editing line 3165 of smartd.c, and changing: + "else if (cfgentries[0]) {" + to read: + "else if (cfgentries && cfgentries[0]) {" + + +Date: 2004-2-24 +Summary: smartmontools release 5.29 (Experimental, not STABLE) +-------------------------------------------------------------- +This is another experimental release, to replace the 5.27 release that +had a damaged configure script. The next stable release will be 5.30 +- This release has SCSI support for NetBSD + + +Date: 2004-2-12 +Summary: smartmontools release 5.27 (Experimental, not STABLE) +-------------------------------------------------------------- +- WARNING: this release has a broken --prefix=/a/path option to the + configure script. The consequence is that smartd will not look for the + configuration file (smartd.conf) at the desired location. +- NetBSD support added +- A new Directive (-s) for smartd.conf now enables flexible automatic + scheduled self-testing for both ATA and SCSI devices. +- Solaris now has ATA device support (SPARC only) +- A new Directive (-n) for smartd.conf to avoid spinning up disks +- Errors when smartd sends mail are now logged to SYSLOG +- Solaris smartd mail now works correctly (uses mailx not mail) + + +Date: 2003-11-29 +Summary: smartmontools release 5.26 +----------------------------------- +This is a stable smartmontools release. The only known problem is +that under Solaris, the email features of smartd do not work 'out of +the box'. Three workarounds are: + [1] use '-M exec mailx' in /etc/smartd.conf + [2] in the start script for smartd, put /usr/ucb into PATH before + /bin + [3] upgrade to release 5.27 or later, or the latest CVS snapshot + + +Date: 2003-11-19 +Summary: smartmontools release 5.25 +----------------------------------- +This release should not hang when accessing USB devices. It provides +smartd SCSI self-test log monitoring for self-test errors, and a +larger table of known ATA drives. DEVICESCAN should work correctly +even on file systems containing XFS or JFS partitions, and on machines +that use devfs, even without traditional links. + +From this time on, even numbered releases will be 'stable' ones and +odd numbered releases (like 5.25) will be unstable/testing/development +releases. + + +Date: 2003-10-30 +Summary: smartmontools release 5.23 +----------------------------------- +This release has one known problem: DEVICESCAN device scanning does +not work correctly if the disk with the /dev directory also has XFS +or JFS file systems on it. + + +Date: 2003-10-28 +Summary: smartmontools release 5.22 +----------------------------------- +Replaces flawed 5.21 release: the -T verypermissive option had to be +entered as -T verpermissive. First experimental solaris support (SCSI +only). This release had a serious flaw: smartd left open file descriptors +for devices that it couldn't monitor. + + +Date: 2003-10-14 +Summary: smartmontools release 5.21 +----------------------------------- +Preliminary support for FreeBSD added to smartmontools. For FreeBSD, +ATA support requires a 5.1-CURRENT kernel while SCSI support should +work across multiple versions (any that support CAM). + + +Date: 2003-10-04 +Summary: smartmontools release 5.20 +----------------------------------- +Replaces flawed 5.19 release (which had a zero-length man page +smartd.conf.5). + + +Date: 2003-10-03 +Summary: smartmontools release 5.19 +----------------------------------- +This is the first release of smartmontools based on autoconf/automake. +For this reason, it is a very experimental release. Please let us +know in particular about documenation errors/omissions, missing or +unneccesary files, and similar oversights. The major changes are: + [1] installation scripts based on autoconfig/automake + [2] ./configure [options] lets you set arbitrary paths + [3] supports FHS with ./configure --prefix=/usr/local + [4] correct paths are inserted into all man pages, binaries, etc. + [5] tarballs and RPMs are now GPG-signed + + +Date: 2003-10-02 11:35 +Summary: smartd SEGV +-------------------- +Some versions of smartd, including smartmontools release 5.1-18, will +SEGV if the combination of Directives in /etc/smartd.conf contains +-l error +AND/OR +-l selftest +without any Attribute monitoring Directives. This is fixed in 5.19 +and above. + +A good workaround is to add: +-o on +OR +-o off +to enable or disable automatic offline data collection. + + +Date: 2002-11-17 07:41 +Summary: testunitready bug in smartd +------------------------------------ +A bug in smartd prevented functioning on scsi devices. +The bug in question only affects smartd users with scsi devices. +To see if your version of smartd has the testunitready() bug, do +smartd -V +If the version of the module smartd.c in a line like: +Module: smartd.c revision: 1.66 date: 2002/11/17 +has a revision greater than or equal to 1.30, and less than or +equal to 1.64, then your version of the code has this problem. + +This problem affected releases starting with RELEASE_5_0_16 up to and +including RELEASE_5_0_43. diff --git a/README b/README new file mode 100644 index 0000000..6d6e1c8 --- /dev/null +++ b/README @@ -0,0 +1,125 @@ +========================================================== +smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac +OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows. +========================================================== + +$Id: README,v 1.55 2006/04/12 14:54:28 ballen4705 Exp $ + +== HOME == +The home for smartmontools is located at: + + http://smartmontools.sourceforge.net/ + +Please see this web site for updates, documentation, and for submitting +patches and bug reports. + +You will find a mailing list for support and other questions at: + + http://lists.sourceforge.net/lists/listinfo/smartmontools-support + + +== COPYING == +Copyright (C) 2002-6 Bruce Allen + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +You should have received a copy of the GNU General Public License (for +example COPYING); if not, write to the Free Software Foundation, Inc., 675 +Mass Ave, Cambridge, MA 02139, USA. + + +== CREDITS == +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/ + + +== OVERVIEW == +smartmontools contains utilities that control and monitor storage +devices using the Self-Monitoring, Analysis and Reporting Technology +(S.M.A.R.T.) system build into ATA and SCSI Hard Drives. This is used +to check the reliability of the hard drive and to predict drive +failures. smartmontools Version 5.x is designed to comply to the +ATA/ATAPI-5 specification (Revision 1). Future releases of +smartmontools (Versions 6.x and 7.x) will comply with the ATA/ATAPI-6 +and ATA/ATAPI-7 specifications. + +This package is meant to be an up-to-date replacement for the +ucsc-smartsuite and smartsuite packages, and is derived from that +code. + + +== CONTENTS == +The suite contains two utilities: + +smartctl is a command line utility designed to perform S.M.A.R.T. tasks + such as disk self-checks, and to report the S.M.A.R.T. status of + the disk. + +smartd is a daemon that periodically monitors S.M.A.R.T. status and + reports errors and changes in S.M.A.R.T. attributes to syslog. + + +== OBTAINING SMARTMONTOOLS == + +Source tarballs +--------------- + +http://sourceforge.net/project/showfiles.php?group_id=64297 + +CVS +--- + +cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login (when prompted for a password, just press Enter) +cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5 + +This will create a subdirectory called sm5/ containing the code. + +To instead get the 5.1-16 release: + +cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co -r RELEASE_5_1_16 sm5 + +To update your sources to the 5.1-18 release: + +cd sm5 +cvs up -r RELEASE_5_1_18 + +To update any tagged release to the latest development code: + +cd sm5 +cvs up -A + +You can see what the different tags are by looking at +http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/smartmontools/sm5/ . +You'll see the tag names in the little scroll window where it says "Show +only files with tag". + +== BUILDING/INSTALLING SMARTMONTOOLS == + +Refer to the "INSTALL" file for detailed installation instructions. + +See the "WARNINGS" file for reports of hardware where these utilities +might cause serious problems such as lockups. + +== GETTING STARTED == + +To examine SMART data from a disk, try: + smartctl -a /dev/hda +for ATA disks, or + smartctl -a /dev/sda +for SCSI disks. See the manual page 'man smartctl' for more +information. + +To start automatic monitoring of your disks with the smartd daemon, +try: + smartd -d +to start the daemon in foreground (debug) mode, or + smartd +to start the daemon in background mode. This will log messages to +SYSLOG. If you would like to get email warning messages, please set +up the configuration file smartd.conf with the '-m' mail warning +Directive. See the manual page 'man smartd' for more information. diff --git a/TODO b/TODO new file mode 100644 index 0000000..1a5945d --- /dev/null +++ b/TODO @@ -0,0 +1,142 @@ +TODO list for smartmontools: + +$Id: TODO,v 1.59 2005/12/11 18:40:35 ballen4705 Exp $ + +SATA devices under Linux +------------------------ +These work OK if you use the standard IDe drivers in drivers/ide. + +The situation is more complicated if you use libata. + +Prior to Linux kernel version 2.6.15-rc1, libata does not support the +HDIO_DRIVE_TASK, HDIO_DRIVE_CMD, and HDIO_DRIVE_TASKFILE ioctl()s that +are needed by smartmontools. Support for HDIO_DRIVE_TASK and +HDIO_DRIVE_CMD was added into libata by Jeff Garzik starting with +Linux kernel version 2.6.15-rc1. Starting with this version, you can +use all the smartmontools commands apart from initiating selective +self-tests (which also requires HDIO_DRIVE_TASKFILE). A typical +command line might look like this: + smartctl -a -d ata /dev/sda +The '-d ata' is required, since otherwise smartmontools will assume +that the device is SCSI, not ATA/SATA. Similar syntax will work with +smartd. + +You may be able to patch earlier versions of libata. Please search the +Linux Kernel Mailing list to find this patch, or look at the thread: +http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&threadm=2yYBY-4HB-55%40gated-at.bofh.it&rnum=3&prev=/groups%3Fq%3Dsmartmontools%26hl%3Den%26lr%3D%26ie%3DUTF-8%26scoring%3Dd%26selm%3D2yYBY-4HB-55%2540gated-at.bofh.it%26rnum%3D3 +To use this, just use (for example) 'smartctl -a -d ata /dev/sda'. + +Since this looks like this patch will become standard, we need to add something +to smartmontools to automatically recognize the libata, and add the '-d ata' +automatically. + +SATA devices +------------ +The ATA PASS THROUGH SCSI command (12 and 16 byte cdb) as defined in +SAT: http://www.t10.org/ftp/t10/drafts/sat/sat-r07.pdf (section 12.2) +provides a general way to pass ATA SMART commands through to SATA +devices. + +Doug Gilbert will in the future add a '-d sat' type (note, this is not +a typo, we do not mean '-d sata') which will instruct the generic +smartmontools code to assume an ATA device but use those ATA PASS +THROUGH commands. + +This should provide additional support for SATA devices under most or +all operating systems. + +USB devices under Linux +----------------------- +Some USB devices can hang smartctl or smartd. This is because these +devices fail to comply with SCSI specifications for their packet +command sets. Work on improving the detection and bail-out procedures +for these flawed devices, so that the user sees an informative error +message and smartd/smartctl don't hang. + +ATA-4 (no kidding!) +------------------- +smartctl: add another -t TESTTYPE option to accomodate old-style ATA-4 +IBM disks (ATA-4 has no self-test commands). See IBM S25L-2426-02 OEM +HARD DISK DRIVE SPECIFICATIONS for DBCA-203240/204860/206480 2.5-Inch +Hard Disk Drive with ATA Interface Revision (1.0) +http://www.hgst.com/tech/techlib.nsf/techdocs/85256AB8006A31E587256A7D00642A1D/$file/dbca_sp.pdf +section 12.30.1.5 for details. These disks offer no self-test option, +and the -t offline command only tests a small part of the disk (a +'segment'). We need a -t multioffline that: + (1) issues auto offline immediate command (tests ONE segment) + (2) waits until estimated completion time + (3) tests if off-line data collection status is set to 0x02 (all + segments completed) + (4) if not, return to (1) + +ATA-6/7 +------- +Support extended error logs +Support extended self-test logs + +smartctl/smartd +--------------- +Add additional -v options (corresponding to comments in +atacmds.c:ataPrintSmartAttribName(). + +Add interface to Megaraid ATA RAID controllers (Erik) + +smartctl: +--------- +Add command line option to issue SMART SAVE ATTRIBUTE VALUES command +Feature Register value ATA_SMART_SAVE 0xd3 + +Perhaps modify the -q option (quiet mode) so that it only warns of ATA +errors if they have (say) taken place in the last 168 hours (week). + +Parse and print additional Attribute flag meanings (IBM ones, eg +performance etc). These are now documented in atacmds.h -- we just +need to modify the format of the Attribute table. + +Modify the SMART self-test log table printing so that we ALSO print +the value of the self-test failure checkpoint byte, if it's one of the +recognized values. See routine SelfTestFailureCodeName and +documentation in atacmds.h. + +smartd: +------- +Perhaps change special argument to -m to have also + which would actually work with -M exec to run the +executable/script in the background rather than in the foreground. +But let's wait for someone to request this. At that point we should +probably use fork/exec rather than system(). + +Perhaps change smartd to look in /proc/ide and /proc/scsi to see what +exists? If something doesn't exit then don't try to open it? This +should probably be the default option if there is no configuration +file. + +Add ability to monitor "worst" value from attributes (sometimes it +gets larger!) and to monitor the threshold value (sometimes it +changes!). + +Add command line option that scans devices then WRITES +/etc/smartd.conf, perhaps as /etc/smartd.conf.output, just for devices +that can be monitored. + +FreeBSD +------- + +Add support for 3ware 9000 series SATA controllers. + +Cygwin and Windows +------------------ +Add IDE/ATA selective self test. + +Access SCSI devices via IOCTL_SCSI_PASS_THROUGH on 2000/XP +to support systems with missing ASPI driver. + +Windows +------- +Provide some installer. + +Packaging +--------- +Under freebsd and solaris, the following are wrong: +smartd.conf: has linux device paths +smart*.in : man pages have (mostly) linux device paths diff --git a/WARNINGS b/WARNINGS new file mode 100644 index 0000000..21e23d8 --- /dev/null +++ b/WARNINGS @@ -0,0 +1,132 @@ +$Id: WARNINGS,v 1.32 2005/04/20 19:17:33 geoffk1 Exp $ + +The most recent version of this file can be found here: +http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/WARNINGS?view=markup + +The following are reports of serious problems (eg system lockup) which +were due to smartmontools. There are DARWIN, LINUX, FREEBSD, SOLARIS +and WINDOWS sections below. + + +LINUX +----- + +You may also wish to search the linux-kernel mailing list for problem +reports concerning smartmontools. Here is the URL: +http://groups.google.com/groups?as_q=smartmontools&safe=images&ie=UTF-8&oe=UTF-8&as_ugroup=linux.kernel&lr=&num=100&hl=en + +SYSTEM: Any system with USB ports and USB storage devices +PROBLEM: Using smartd/smartctl on USB "SCSI" storage devices can cause kernel hang +REPORTER: see link below +LINK: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=107615 +NOTE: USB storage devices are handled as SCSI devices by the kernel. But many of these + devices do not comply with SCSI specs, and can cause the kernel to hang. + Avoid using smartd/smartctl on these devices (they don't do SMART anyway). + In particular, the use of smartd DEVICESCAN in /etc/smartd.conf can cause + these devices (typically represented by /dev/sda or /dev/sdb) to hang, and + the kernel to lock up. +FIXED: This problem should be fixed in smartmontools-5.25 and greater. + + +SYSTEM: Intel 875WP1-E motherboard with SATA drives on motherboard's SATA ports +PROBLEM: smartd makes NTP time drift +REPORTER: nohez@cmie.com +LINK: http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=Pine.LNX.4.33.0310111545530.1047-100000%40venus.cmie.ernet.in.lucky.linux.kernel +NOTE: When using SATA disks, linux kernel k_smp-2.4.21-108 (SMP because + of hyper-threading) and xntp-4.1.1-177, the server time went + out of sync with system time. Problem goes away when SATA + disks removed. + + +SYSTEM: Dell servers using AACRAID (SCSI) +PROBLEM: Locked up, needed to be rebooted +REPORTER: drew@eastvan.bc.ca +LINK: http://sourceforge.net/mailarchive/forum.php?thread_id=1311313&forum_id=12495 + + +SYSTEM: Box with Promise 20265 IDE-controller (pdc202xx-driver) and > 2.4.18 kernel with ide-taskfile support +PROBLEM: Smartctl locks system solid when used on /dev/hd[ef]. +REPORTER: Georg Acher +LINK: http://sourceforge.net/mailarchive/forum.php?thread_id=1457979&forum_id=12495 +NOTE: Lockup doesn't happen with 2.4.18 kernel, and doesn't affect /dev/hd[a-d] + This appears to be a problem with the pdc202xx-driver and has been reported + to the pdcx maintainers. If you enable the Promise-BIOS (ATA100-BIOS) then + everything will work fine. But if you disable it, then the machine will hang. + + +SYSTEM: Box with Promise 20262 IDE-controller +PROBLEM: Smartctl locks system solid +REPORTER: Ben Low +LINK: http://sourceforge.net/mailarchive/message.php?msg_id=5074201 +NOTE: Similar to previous report: Promise Ultra66 2-port card (20262) which, with + linux 2.4.20, suffers from the lockups reported above. But it was + impossible to enable the Promiste BIOS. A kernel patch is referenced + to fix the problem. + + +SYSTEM: Promise 20265 IDE-controller +PROBLEM: Smartctl locks system solid when used on CDROM/DVD device +REPORTER: see link below +LINK: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208964 +NOTE: Problem seems to affect kernel 2.4.21 only. + + +SYSTEM: Promise IDE-controllers and perhaps others also +PROBLEM: System freezes under heavy load, perhaps when running SMART commands +REPORTER: Mario 'BitKoenig' Holbe Mario.Holbe@RZ.TU-Ilmenau.DE +LINK: http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=1wUXW-2FA-9%40gated-at.bofh.it +NOTE: Before freezing, SYSLOG shows the following message(s) + kernel: hdf: dma timer expiry: dma status == 0xXX + where XX is two hexidecimal digits. This may be a kernel bug + or an underlying hardware problem. It's not clear if + smartmontools plays a role in provoking this problem. FINAL + NOTE: Problem was COMPLETELY resolved by replacing the power + supply. See URL above, entry on May 29, 2004 by Holbe. Other + things to try are exchanging cables, and cleaning PCI slots. + +FREEBSD +------- + +[No problem reports yet.] + + +SOLARIS +------- + +[No problem reports yet.] + + +CYGWIN and WINDOWS +------------------ + +SYSTEM: Any Windows NT4, 2000 or XP system. +PROBLEM: Use of undocumented system calls for IDE/ATA read log + (smartctl -l, --log, -a, --all) may affect system stability. +REPORTER: Christian Franke +NOTE: The IOCTL call SMART_RCV_DRIVE_DATA does not support + ATA_SMART_READ_LOG_SECTOR on NT4/2000/XP. The Win32 + implementation of smartctl/smartd uses undocumented + and possibly buggy system calls for this purpose: + NT4: IOCTL_SCSI_PASS_THROUGH.with undocumented pseudo SCSI + command SCSIOP_ATA_PASSTHROUGH (0xCC). + 2000/XP: Undocumented IOCTL_IDE_PASS_THROUGH. + + +DARWIN +------ + +SYSTEM: Any system before Tiger +PROBLEM: Can't switch off SMART, can't switch off auto-save, can't run + short tests. +REPORTER: Geoff Keating +NOTE: There's a bug in the system library: when you ask it to + do any of these things, it does the inverse (switches on, + runs extended tests). Radar 3727283. + +SYSTEM: All known systems +PROBLEM: When drive is asleep, SMART commands fail +REPORTER: Geoff Keating +NOTE: You can prevent the drive from sleeping by saying + pmset -a disksleep 0 + or by unchecking the 'Put the hard disk(s) to sleep when possible' + checkbox in the Energy Saver preferences. Radar 4094403. diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..cf94d1b --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1116 @@ +# generated automatically by aclocal 1.9.3 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# -*- Autoconf -*- +# Copyright (C) 2002, 2003 Free Software Foundation, Inc. +# Generated from amversion.in; do not edit by hand. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.9.3])]) + +# Figure out how to run the assembler. -*- Autoconf -*- + +# serial 3 + +# Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# AM_PROG_AS +# ---------- +AC_DEFUN([AM_PROG_AS], +[# By default we simply use the C compiler to build assembly code. +AC_REQUIRE([AC_PROG_CC]) +test "${CCAS+set}" = set || CCAS=$CC +test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS +AC_ARG_VAR([CCAS], [assembler compiler command (defaults to CC)]) +AC_ARG_VAR([CCASFLAGS], [assembler compiler flags (defaults to CFLAGS)]) +]) + +# AM_AUX_DIR_EXPAND + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 6 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# serial 7 -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +#serial 2 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 7 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# This macro actually does too much some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 11 + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# -*- Autoconf -*- +# Copyright (C) 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 1 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. +# From Jim Meyering + +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# -*- Autoconf -*- + + +# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. + +# Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# +# Check to make sure that the build environment is sane. +# + +# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# AM_PROG_INSTALL_STRIP + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 1 + + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/atacmdnames.c b/atacmdnames.c new file mode 100644 index 0000000..52b68f1 --- /dev/null +++ b/atacmdnames.c @@ -0,0 +1,520 @@ +/* + * atacmdnames.c + * + * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7) + * specification, which is available from http://www.t13.org/#FTP_site + * + * Home page of code is: http://smartmontools.sourceforge.net + * Address of support mailing list: smartmontools-support@lists.sourceforge.net + * + * Copyright (C) 2003-6 Philip Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "atacmdnames.h" +#include +#include + +#define COMMAND_TABLE_SIZE 256 + +const char *atacmdnames_c_cvsid="$Id: atacmdnames.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" ATACMDNAMES_H_CVSID; + +const char cmd_reserved[] = "[RESERVED]"; +const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]"; +const char cmd_reserved_sa[] = "[RESERVED FOR SERIAL ATA]"; +const char cmd_reserved_cf[] = "[RESERVED FOR COMPACTFLASH ASSOCIATION]"; +const char cmd_reserved_mcpt[] = "[RESERVED FOR MEDIA CARD PASS THROUGH]"; +const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]"; +const char cmd_seek_ret4[] = "SEEK [RET-4]"; + +const char *command_table[COMMAND_TABLE_SIZE] = { +/*-------------------------------------------------- 00h-0Fh -----*/ + "NOP", + cmd_reserved, + cmd_reserved, + "CFA REQUEST EXTENDED ERROR CODE", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + "DEVICE RESET", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 10h-1Fh -----*/ + "RECALIBRATE [OBS-4]", + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, + cmd_recalibrate_ret4, +/*-------------------------------------------------- 20h-2Fh -----*/ + "READ SECTOR(S)", + "READ SECTOR(S) [OBS-5]", + "READ LONG (w/ retry) [OBS-4]", + "READ LONG (w/o retry) [OBS-4]", + "READ SECTOR(S) EXT", + "READ DMA EXT", + "READ DMA QUEUED EXT", + "READ NATIVE MAX ADDRESS EXT", + cmd_reserved, + "READ MULTIPLE EXT", + "READ STREAM DMA", + "READ STREAM PIO", + cmd_reserved, + cmd_reserved, + cmd_reserved, + "READ LOG EXT", +/*-------------------------------------------------- 30h-3Fh -----*/ + "WRITE SECTOR(S)", + "WRITE SECTOR(S) [OBS-5]", + "WRITE LONG(w/ retry) [OBS-4]", + "WRITE LONG(w/o retry) [OBS-4]", + "WRITE SECTORS(S) EXT", + "WRITE DMA EXT", + "WRITE DMA QUEUED EXT", + "SET MAX ADDRESS EXT", + "CFA WRITE SECTORS WITHOUT ERASE", + "WRITE MULTIPLE EXT", + "WRITE STREAM DMA", + "WRITE STREAM PIO", + "WRITE VERIFY [OBS-4]", + "WRITE DMA FUA EXT", + "WRITE DMA QUEUED FUA EXT", + "WRITE LOG EXT", +/*-------------------------------------------------- 40h-4Fh -----*/ + "READ VERIFY SECTOR(S)", + "READ VERIFY SECTOR(S) [OBS-5]", + "READ VERIFY SECTOR(S) EXT", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 50h-5Fh -----*/ + "FORMAT TRACK [OBS-4]", + "CONFIGURE STREAM", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 60h-6Fh -----*/ + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved_sa, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- 70h-7Fh -----*/ + "SEEK [OBS-7]", + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, + cmd_seek_ret4, +/*-------------------------------------------------- 80h-8Fh -----*/ + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + "CFA TRANSLATE SECTOR [VS IF NO CFA]", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, +/*-------------------------------------------------- 90h-9Fh -----*/ + "EXECUTE DEVICE DIAGNOSTIC", + "INITIALIZE DEVICE PARAMETERS [OBS-6]", + "DOWNLOAD MICROCODE", + cmd_reserved, + "STANDBY IMMEDIATE [RET-4]", + "IDLE IMMEDIATE [RET-4]", + "STANDBY [RET-4]", + "IDLE [RET-4]", + "CHECK POWER MODE [RET-4]", + "SLEEP [RET-4]", + cmd_vendor_specific, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- A0h-AFh -----*/ + "PACKET", + "IDENTIFY PACKET DEVICE", + "SERVICE", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, +/*-------------------------------------------------- B0h-BFh -----*/ + "SMART", + "DEVICE CONFIGURATION", + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, + cmd_reserved_cf, +/*-------------------------------------------------- C0h-CFh -----*/ + "CFA ERASE SECTORS [VS IF NO CFA]", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + "READ MULTIPLE", + "WRITE MULTIPLE", + "SET MULTIPLE MODE", + "READ DMA QUEUED", + "READ DMA", + "READ DMA [OBS-5]", + "WRITE DMA", + "WRITE DMA [OBS-5]", + "WRITE DMA QUEUED", + "CFA WRITE MULTIPLE WITHOUT ERASE", + "WRITE MULTIPLE FUA EXT", + cmd_reserved, +/*-------------------------------------------------- D0h-DFh -----*/ + cmd_reserved, + "CHECK MEDIA CARD TYPE", + cmd_reserved_mcpt, + cmd_reserved_mcpt, + cmd_reserved_mcpt, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + cmd_reserved, + "GET MEDIA STATUS", + "ACKNOWLEDGE MEDIA CHANGE [RET-4]", + "BOOT POST-BOOT [RET-4]", + "BOOT PRE-BOOT [RET-4]", + "MEDIA LOCK", + "MEDIA UNLOCK", +/*-------------------------------------------------- E0h-EFh -----*/ + "STANDBY IMMEDIATE", + "IDLE IMMEDIATE", + "STANDBY", + "IDLE", + "READ BUFFER", + "CHECK POWER MODE", + "SLEEP", + "FLUSH CACHE", + "WRITE BUFFER", + "WRITE SAME [RET-4]", /* Warning! This command is retired but the value of + f_reg is used in look_up_ata_command(). If this + command code is reclaimed in a future standard then + be sure to update look_up_ata_command(). */ + "FLUSH CACHE EXIT", + cmd_reserved, + "IDENTIFY DEVICE", + "MEDIA EJECT", + "IDENTIFY DEVICE DMA [OBS-4]", + "SET FEATURES", +/*-------------------------------------------------- F0h-FFh -----*/ + cmd_vendor_specific, + "SECURITY SET PASSWORD", + "SECURITY UNLOCK", + "SECURITY ERASE PREPARE", + "SECURITY ERASE UNIT", + "SECURITY FREEZE LOCK", + "SECURITY DISABLE PASSWORD", + cmd_vendor_specific, + "READ NATIVE MAX ADDRESS", + "SET MAX", + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific, + cmd_vendor_specific +}; + +/* Returns the name of the command (and possibly sub-command) with the given + command code and feature register values. For most command codes this + simply returns the corresponding entry in the command_table array, but for + others the value of the feature register specifies a subcommand or + distinguishes commands. */ +const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) { + + // check that command table not messed up. The compiler will issue + // warnings if there are too many array elements, but won't issue + // warnings if there are not enough of them. + if (sizeof(command_table) != sizeof(char *)*COMMAND_TABLE_SIZE){ + fprintf(stderr, + "Problem in atacmdnames.c. Command Table command_table[] does\n" + "not have %d entries! It has %d entries. Please fix it.\n", + COMMAND_TABLE_SIZE, (int)(sizeof(command_table)/sizeof(char *))); + abort(); + } + + switch (c_code) { + case 0x00: /* NOP */ + switch (f_reg) { + case 0x00: + return "NOP [Abort queued commands]"; + case 0x01: + return "NOP [Don't abort queued commands]"; + default: + return "NOP [Reserved subcommand]"; + } + case 0x92: /* DOWNLOAD MICROCODE */ + switch (f_reg) { + case 0x01: + return "DOWNLOAD MICROCODE [Temporary]"; + case 0x07: + return "DOWNLOAD MICROCODE [Save]"; + default: + return "DOWNLOAD MICROCODE [Reserved subcommand]"; + } + case 0xB0: /* SMART */ + switch (f_reg) { + case 0xD0: + return "SMART READ DATA"; + case 0xD1: + return "SMART READ ATTRIBUTE THRESHOLDS [OBS-4]"; + case 0xD2: + return "SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE"; + case 0xD3: + return "SMART SAVE ATTRIBUTE VALUES [OBS-6]"; + case 0xD4: + return "SMART EXECUTE OFF-LINE IMMEDIATE"; + case 0xD5: + return "SMART READ LOG"; + case 0xD6: + return "SMART WRITE LOG"; + case 0xD7: + return "SMART WRITE ATTRIBUTE THRESHOLDS [NS, OBS-4]"; + case 0xD8: + return "SMART ENABLE OPERATIONS"; + case 0xD9: + return "SMART DISABLE OPERATIONS"; + case 0xDA: + return "SMART RETURN STATUS"; + case 0xDB: + return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]"; + default: + if (f_reg >= 0xE0) + return "[Vendor specific SMART command]"; + else + return "[Reserved SMART command]"; + } + case 0xB1: /* DEVICE CONFIGURATION */ + switch (f_reg) { + case 0xC0: + return "DEVICE CONFIGURATION RESTORE"; + case 0xC1: + return "DEVICE CONFIGURATION FREEZE LOCK"; + case 0xC2: + return "DEVICE CONFIGURATION IDENTIFY"; + case 0xC3: + return "DEVICE CONFIGURATION SET"; + default: + return "DEVICE CONFIGURATION [Reserved command]"; + } + case 0xE9: /* WRITE SAME */ + switch (f_reg) { + case 0x22: + return "WRITE SAME [Start specified] [RET-4]"; + case 0xDD: + return "WRITE SAME [Start unspecified] [RET-4]"; + default: + return "WRITE SAME [Invalid subcommand] [RET-4]"; + } + case 0xEF: /* SET FEATURES */ + switch (f_reg) { + case 0x01: + return "SET FEATURES [Enable 8-bit PIO]"; + case 0x02: + return "SET FEATURES [Enable write cache]"; + case 0x03: + return "SET FEATURES [Set transfer mode]"; + case 0x04: + return "SET FEATURES [Enable auto DR] [OBS-4]"; + case 0x05: + return "SET FEATURES [Enable APM]"; + case 0x06: + return "SET FEATURES [Enable Pwr-Up In Standby]"; + case 0x07: + return "SET FEATURES [Set device spin-up]"; + case 0x09: + return "SET FEATURES [Reserved (address offset)]"; + case 0x0A: + return "SET FEATURES [Enable CFA power mode 1]"; + case 0x10: + return "SET FEATURES [Reserved for Serial ATA]"; + case 0x20: + return "SET FEATURES [Set Time-ltd R/W WCT]"; + case 0x21: + return "SET FEATURES [Set Time-ltd R/W EH]"; + case 0x31: + return "SET FEATURES [Disable Media Status Notf]"; + case 0x33: + return "SET FEATURES [Disable retry] [OBS-4]"; + case 0x42: + return "SET FEATURES [Enable AAM]"; + case 0x43: + return "SET FEATURES [Set Max Host I/F S Times]"; + case 0x44: + return "SET FEATURES [Length of VS data] [OBS-4]"; + case 0x54: + return "SET FEATURES [Set cache segs] [OBS-4]"; + case 0x55: + return "SET FEATURES [Disable read look-ahead]"; + case 0x5D: + return "SET FEATURES [Enable release interrupt]"; + case 0x5E: + return "SET FEATURES [Enable SERVICE interrupt]"; + case 0x66: + return "SET FEATURES [Disable revert defaults]"; + case 0x77: + return "SET FEATURES [Disable ECC] [OBS-4]"; + case 0x81: + return "SET FEATURES [Disable 8-bit PIO]"; + case 0x82: + return "SET FEATURES [Disable write cache]"; + case 0x84: + return "SET FEATURES [Disable auto DR] [OBS-4]"; + case 0x85: + return "SET FEATURES [Disable APM]"; + case 0x86: + return "SET FEATURES [Disable Pwr-Up In Standby]"; + case 0x88: + return "SET FEATURES [Disable ECC] [OBS-4]"; + case 0x89: + return "SET FEATURES [Reserved (address offset)]"; + case 0x8A: + return "SET FEATURES [Disable CFA power mode 1]"; + case 0x90: + return "SET FEATURES [Reserved for Serial ATA]"; + case 0x95: + return "SET FEATURES [Enable Media Status Notf]"; + case 0x99: + return "SET FEATURES [Enable retries] [OBS-4]"; + case 0x9A: + return "SET FEATURES [Set max avg curr] [OBS-4]"; + case 0xAA: + return "SET FEATURES [Enable read look-ahead]"; + case 0xAB: + return "SET FEATURES [Set max prefetch] [OBS-4]"; + case 0xBB: + return "SET FEATURES [4 bytes VS data] [OBS-4]"; + case 0xC2: + return "SET FEATURES [Disable AAM]"; + case 0xCC: + return "SET FEATURES [Enable revert to defaults]"; + case 0xDD: + return "SET FEATURES [Disable release interrupt]"; + case 0xDE: + return "SET FEATURES [Disable SERVICE interrupt]"; + case 0xE0: + return "SET FEATURES [Obsolete subcommand]"; + default: + if (f_reg >= 0xF0) + return "SET FEATURES [Reserved for CFA]"; + else + return "SET FEATURES [Reserved subcommand]"; + } + case 0xF9: /* SET MAX */ + switch (f_reg) { + case 0x00: + return "SET MAX ADDRESS [OBS-6]"; + case 0x01: + return "SET MAX SET PASSWORD"; + case 0x02: + return "SET MAX LOCK"; + case 0x03: + return "SET MAX UNLOCK"; + case 0x04: + return "SET MAX FREEZE LOCK"; + default: + return "[Reserved SET MAX command]"; + } + default: + return command_table[c_code]; + } +} diff --git a/atacmdnames.h b/atacmdnames.h new file mode 100644 index 0000000..810cf94 --- /dev/null +++ b/atacmdnames.h @@ -0,0 +1,32 @@ +/* + * atacmdnames.h + * + * 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-6 Philip Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef ATACMDNAMES_H_ +#define ATACMDNAMES_H_ + +#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $\n" + +/* Returns the name of the command (and possibly sub-command) with the given + command code and feature register values. */ +const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg); + +#endif diff --git a/atacmds.c b/atacmds.c new file mode 100644 index 0000000..4a74f40 --- /dev/null +++ b/atacmds.c @@ -0,0 +1,1887 @@ +/* + * atacmds.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * Copyright (C) 2000 Andre Hedrick + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "extern.h" +#include "utility.h" + +const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.168 2006/04/12 17:01:46 ballen4705 Exp $" +ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// for passing global control variables +extern smartmonctrl *con; + +// These Drive Identity tables are taken from hdparm 5.2, and are also +// given in the ATA/ATAPI specs for the IDENTIFY DEVICE command. Note +// that SMART was first added into the ATA/ATAPI-3 Standard with +// Revision 3 of the document, July 25, 1995. Look at the "Document +// Status" revision commands at the beginning of +// http://www.t13.org/project/d2008r6.pdf to see this. +#define NOVAL_0 0x0000 +#define NOVAL_1 0xffff +/* word 81: minor version number */ +#define MINOR_MAX 0x22 +const char *minor_str[] = { /* word 81 value: */ + "Device does not report version", /* 0x0000 */ + "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */ + "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */ + "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */ + "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */ + "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ + "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ /* SMART NOT INCLUDED */ + "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ + "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ + "ATA-2 X3T10 948D revision 3", /* 0x0009 */ + "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ + "ATA-3 X3T10 2008D revision 6", /* 0x000b */ /* 1st VERSION WITH SMART */ + "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */ + "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */ + "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */ + "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */ + "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */ + "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */ + "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012 */ + "ATA/ATAPI-5 T13 1321D revision 3", /* 0x0013 */ + "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */ + "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */ + "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016 */ + "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */ + "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */ + "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */ + "ATA/ATAPI-7 T13 1532D revision 1", /* 0x001a */ + "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ + "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ + "reserved", /* 0x001d */ + "ATA/ATAPI-7 T13 1532D revision 0", /* 0x001e */ + "reserved", /* 0x001f */ + "reserved", /* 0x0020 */ + "ATA/ATAPI-7 T13 1532D revision 4a", /* 0x0021 */ + "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022 */ +}; + +// NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device +// attribute structures were NOT completely vendor specific. So any +// disk that is ATA/ATAPI-4 or above can not be trusted to show the +// vendor values in sensible format. + +// Negative values below are because it doesn't support SMART +const int actual_ver[] = { + /* word 81 value: */ + 0, /* 0x0000 WARNING: */ + 1, /* 0x0001 WARNING: */ + 1, /* 0x0002 WARNING: */ + 1, /* 0x0003 WARNING: */ + 2, /* 0x0004 WARNING: This array */ + 2, /* 0x0005 WARNING: corresponds */ + -3, /*<== */ /* 0x0006 WARNING: *exactly* */ + 2, /* 0x0007 WARNING: to the ATA/ */ + -3, /*<== */ /* 0x0008 WARNING: ATAPI version */ + 2, /* 0x0009 WARNING: listed in */ + 3, /* 0x000a WARNING: the */ + 3, /* 0x000b WARNING: minor_str */ + 3, /* 0x000c WARNING: array */ + 4, /* 0x000d WARNING: above. */ + 4, /* 0x000e WARNING: */ + 4, /* 0x000f WARNING: If you change */ + 4, /* 0x0010 WARNING: that one, */ + 4, /* 0x0011 WARNING: change this one */ + 4, /* 0x0012 WARNING: too!!! */ + 5, /* 0x0013 WARNING: */ + 4, /* 0x0014 WARNING: */ + 5, /* 0x0015 WARNING: */ + 5, /* 0x0016 WARNING: */ + 4, /* 0x0017 WARNING: */ + 6, /* 0x0018 WARNING: */ + 6, /* 0x0019 WARNING: */ + 7, /* 0x001a WARNING: */ + 6, /* 0x001b WARNING: */ + 6, /* 0x001c WARNING: */ + 0, /* 0x001d WARNING: */ + 7, /* 0x001e WARNING: */ + 0, /* 0x001f WARNING: */ + 0, /* 0x0020 WARNING: */ + 7, /* 0x0021 WARNING: */ + 6 /* 0x0022 WARNING: */ +}; + +// When you add additional items to this list, you should then: +// 0 -- update this list +// 1 -- modify the following function parse_attribute_def() +// 2 -- if needed, modify ataPrintSmartAttribRawValue() +// 3 - if needed, modify ataPrintSmartAttribName() +// 4 -- add #define PRESET_N_DESCRIPTION at top of knowndrives.c +// 5 -- add drive in question into knowndrives[] table in knowndrives.c +// 6 -- update smartctl.8 +// 7 -- update smartd.8 +// 8 -- do "make smartd.conf.5" to update smartd.conf.5 +// 9 -- update CHANGELOG file +const char *vendorattributeargs[] = { + // 0 defs[9]=1 + "9,minutes", + // 1 defs[9]=3 + "9,seconds", + // 2 defs[9]=2 + "9,temp", + // 3 defs[220]=1 + "220,temp", + // 4 defs[*]=253 + "N,raw8", + // 5 defs[*]=254 + "N,raw16", + // 6 defs[*]=255 + "N,raw48", + // 7 defs[200]=1 + "200,writeerrorcount", + // 8 defs[9]=4 + "9,halfminutes", + // 9 defs[194]=1 + "194,10xCelsius", + // 10 defs[194]=2 + "194,unknown", + // 11 defs[193]=1 + "193,loadunload", + // 12 defs[201]=1 + "201,detectedtacount", + // 13 defs[192]=1 + "192,emergencyretractcyclect", + // 14 defs[198]=1 + "198,offlinescanuncsectorct", + // NULL should always terminate the array + NULL +}; + +// This are the meanings of the Self-test failure checkpoint byte. +// This is in the self-test log at offset 4 bytes into the self-test +// descriptor and in the SMART READ DATA structure at byte offset +// 371. These codes are not well documented. The meanings returned by +// this routine are used (at least) by Maxtor and IBM. Returns NULL if +// not recognized. Currently the maximum length is 15 bytes. +const char *SelfTestFailureCodeName(unsigned char which){ + + switch (which) { + case 0: + return "Write_Test"; + case 1: + return "Servo_Basic"; + case 2: + return "Servo_Random"; + case 3: + return "G-list_Scan"; + case 4: + return "Handling_Damage"; + case 5: + return "Read_Scan"; + default: + return NULL; + } +} + +// This is a utility function for parsing pairs like "9,minutes" or +// "220,temp", and putting the correct flag into the attributedefs +// array. Returns 1 if problem, 0 if pair has been recongized. +int parse_attribute_def(char *pair, unsigned char **defsptr){ + int i,j; + char temp[32]; + unsigned char *defs; + + // If array does not exist, allocate it + if (!*defsptr && !(*defsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM, 1))){ + pout("Out of memory in parse_attribute_def\n"); + EXIT(1); + } + + defs=*defsptr; + + // look along list and see if we find the pair + for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++); + + switch (i) { + case 0: + // attribute 9 is power on time in minutes + defs[9]=1; + return 0; + case 1: + // attribute 9 is power-on-time in seconds + defs[9]=3; + return 0; + case 2: + // attribute 9 is temperature in celsius + defs[9]=2; + return 0; + case 3: + // attribute 220 is temperature in celsius + defs[220]=1; + return 0; + case 4: + // print all attributes in raw 8-bit form + for (j=0; j255) + return 1; + + // check for recognized strings + if (!strcmp(temp, "raw8")) { + defs[i]=253; + return 0; + } + + // check for recognized strings + if (!strcmp(temp, "raw16")) { + defs[i]=254; + return 0; + } + + // check for recognized strings + if (!strcmp(temp, "raw48")) { + defs[i]=255; + return 0; + } + + // didn't recognize the string + return 1; +} + +// Structure used in sorting the array vendorattributeargs[]. +typedef struct vaa_pair_s { + const char *vaa; + const char *padded_vaa; +} vaa_pair; + +// Returns a copy of s with all numbers of less than three digits padded with +// leading zeros. Returns NULL if there isn't enough memory available. The +// memory for the string is dynamically allocated and should be freed by the +// caller. +char *pad_numbers(const char *s) +{ + char c, *t, *u; + const char *r; + int i, len, ndigits = 0; + + // Allocate the maximum possible amount of memory needed. + if (!(t = (char *)malloc(strlen(s)*2+2))) + return NULL; + + // Copy the string s to t, padding any numbers of less than three digits + // with leading zeros. The string is copied backwards to simplify the code. + r = s + strlen(s); + u = t; + while (( r-- >= s)) { + if (isdigit((int)*r)) + ndigits++; + else if (ndigits > 0) { + while (ndigits++ < 3) + *u++ = '0'; + ndigits = 0; + } + *u++ = *r; + } + *u = '\0'; + + // Reverse the string in t. + len = strlen(t); + for (i = 0; i < len/2; i++) { + c = t[i]; + t[i] = t[len-1-i]; + t[len-1-i] = c; + } + + return t; +} + +// Comparison function for qsort(). Used by sort_vendorattributeargs(). +int compare_vaa_pairs(const void *a, const void *b) +{ + vaa_pair *p = (vaa_pair *)a; + vaa_pair *q = (vaa_pair *)b; + + return strcmp(p->padded_vaa, q->padded_vaa); +} + +// Returns a sorted list of vendorattributeargs or NULL if there is not enough +// memory available. The memory for the list is allocated dynamically and +// should be freed by the caller. +// To perform the sort, any numbers in the strings are padded out to three +// digits by adding leading zeros. For example, +// +// "9,minutes" becomes "009,minutes" +// "N,raw16" becomes "N,raw016" +// +// and the original strings are paired with the padded strings. The list of +// pairs is then sorted by comparing the padded strings (using strcmp) and the +// result is then the list of unpadded strings. +// +const char **sort_vendorattributeargs(void) { + const char **ps, **sorted_list = NULL; + vaa_pair *pairs, *pp; + int count, i; + + // Figure out how many strings are in vendorattributeargs[] (not including + // the terminating NULL). + count = (sizeof vendorattributeargs) / sizeof(char *) - 1; + + // Construct a list of pairs of strings from vendorattributeargs[] and their + // padded equivalents. + if (!(pairs = (vaa_pair *)malloc(sizeof(vaa_pair) * count))) + goto END; + for (ps = vendorattributeargs, pp = pairs; *ps; ps++, pp++) { + pp->vaa = *ps; + if (!(pp->padded_vaa = pad_numbers(*ps))) + goto END; + } + + // Sort the array of vaa_pair structures by comparing the padded strings + // using strcmp(). + qsort(pairs, count, sizeof(vaa_pair), compare_vaa_pairs); + + // Construct the sorted list of strings. + if (!(sorted_list = (const char **)malloc(sizeof vendorattributeargs))) + goto END; + for (ps = sorted_list, pp = pairs, i = 0; i < count; ps++, pp++, i++) + *ps = pp->vaa; + *ps = NULL; + +END: + if (pairs) { + for (i = 0; i < count; i++) + if (pairs[i].padded_vaa) + free((void *)pairs[i].padded_vaa); + free((void *)pairs); + } + + // If there was a problem creating the list then sorted_list should now + // contain NULL. + return sorted_list; +} + +// Function to return a multiline string containing a list of the arguments in +// vendorattributeargs[]. The strings are preceeded by tabs and followed +// (except for the last) by newlines. +// This function allocates the required memory for the string and the caller +// must use free() to free it. It returns NULL if the required memory can't +// be allocated. +char *create_vendor_attribute_arg_list(void){ + const char **ps, **sorted; + char *s; + int len; + + // Get a sorted list of vendor attribute arguments. If the sort fails + // (which should only happen if the system is really low on memory) then just + // use the unordered list. + if (!(sorted = (const char **) sort_vendorattributeargs())) + sorted = vendorattributeargs; + + // Calculate the required number of characters + len = 1; // At least one char ('\0') + for (ps = sorted; *ps != NULL; ps++) { + len += 1; // For the tab + len += strlen(*ps); // For the actual argument string + if (*(ps+1)) + len++; // For the newline if required + } + + // Attempt to allocate memory for the string + if (!(s = (char *)malloc(len))) + return NULL; + + // Construct the string + *s = '\0'; + for (ps = sorted; *ps != NULL; ps++) { + strcat(s, "\t"); + strcat(s, *ps); + if (*(ps+1)) + strcat(s, "\n"); + } + + free((char **)sorted); + + // Return a pointer to the string + return s; +} + +// swap two bytes. Point to low address +void swap2(char *location){ + char tmp=*location; + *location=*(location+1); + *(location+1)=tmp; + return; +} + +// swap four bytes. Point to low address +void swap4(char *location){ + char tmp=*location; + *location=*(location+3); + *(location+3)=tmp; + swap2(location+1); + return; +} + +// swap eight bytes. Points to low address +void swap8(char *location){ + char tmp=*location; + *location=*(location+7); + *(location+7)=tmp; + tmp=*(location+1); + *(location+1)=*(location+6); + *(location+6)=tmp; + swap4(location+2); + return; +} + +static char *commandstrings[]={ + "SMART ENABLE", + "SMART DISABLE", + "SMART AUTOMATIC ATTRIBUTE SAVE", + "SMART IMMEDIATE OFFLINE", + "SMART AUTO OFFLINE", + "SMART STATUS", + "SMART STATUS CHECK", + "SMART READ ATTRIBUTE VALUES", + "SMART READ ATTRIBUTE THRESHOLDS", + "SMART READ LOG", + "IDENTIFY DEVICE", + "IDENTIFY PACKET DEVICE", + "CHECK POWER MODE", + "SMART WRITE LOG", + "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n" +}; + +void prettyprint(unsigned char *stuff, char *name){ + int i,j; + pout("\n===== [%s] DATA START (BASE-16) =====\n", name); + for (i=0; i<32; i++){ + pout("%03d-%03d: ", 16*i, 16*(i+1)-1); + for (j=0; j<15; j++) + pout("%02x ",*stuff++); + pout("%02x\n",*stuff++); + } + pout("===== [%s] DATA END (512 Bytes) =====\n\n", name); +} + +// This function provides the pretty-print reporting for SMART +// commands: it implements the various -r "reporting" options for ATA +// ioctls. +int smartcommandhandler(int device, smart_command_set command, int select, char *data){ + int retval; + + // This conditional is true for commands that return data + int getsdata=(command==PIDENTIFY || + command==IDENTIFY || + command==READ_LOG || + command==READ_THRESHOLDS || + command==READ_VALUES || + command==CHECK_POWER_MODE); + + int sendsdata=(command==WRITE_LOG); + + // If reporting is enabled, say what the command will be before it's executed + if (con->reportataioctl){ + // conditional is true for commands that use parameters + int usesparam=(command==READ_LOG || + command==AUTO_OFFLINE || + command==AUTOSAVE || + command==IMMEDIATE_OFFLINE || + command==WRITE_LOG); + + pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]); + if (usesparam) + pout(" InputParameter=%d\n", select); + else + pout("\n"); + } + + if ((getsdata || sendsdata) && !data){ + pout("REPORT-IOCTL: Unable to execute command %s : data destination address is NULL\n", commandstrings[command]); + return -1; + } + + // The reporting is cleaner, and we will find coding bugs faster, if + // the commands that failed clearly return empty (zeroed) data + // structures + if (getsdata) { + if (command==CHECK_POWER_MODE) + data[0]=0; + else + memset(data, '\0', 512); + } + + + // If reporting is enabled, say what input was sent to the command + if (con->reportataioctl && sendsdata){ + pout("REPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]); + // if requested, pretty-print the output data structure + if (con->reportataioctl>1) + prettyprint((unsigned char *)data, commandstrings[command]); + } + + // In case the command produces an error, we'll want to know what it is: + errno=0; + + // now execute the command + switch (con->controller_type) { + case CONTROLLER_3WARE_678K: + case CONTROLLER_3WARE_678K_CHAR: + case CONTROLLER_3WARE_9000_CHAR: + retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data); + if (retval && con->controller_port<=0) + pout("WARNING: apparently missing '-d 3ware,N' disk specification\n"); + break; + case CONTROLLER_MARVELL_SATA: + retval=marvell_command_interface(device, command, select, data); + break; + default: + retval=ata_command_interface(device, command, select, data); + } + + // If reporting is enabled, say what output was produced by the command + if (con->reportataioctl){ + if (errno) + pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", + device, commandstrings[command], retval, errno, strerror(errno)); + else + pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n", + device, commandstrings[command], retval); + + // if requested, pretty-print the output data structure + if (con->reportataioctl>1 && getsdata) { + if (command==CHECK_POWER_MODE) + pout("Sector Count Register (BASE-16): %02x\n", (unsigned char)(*data)); + else + prettyprint((unsigned char *)data, commandstrings[command]); + } + } + return retval; +} + + +// This function computes the checksum of a single disk sector (512 +// bytes). Returns zero if checksum is OK, nonzero if the checksum is +// incorrect. The size (512) is correct for all SMART structures. +unsigned char checksum(unsigned char *buffer){ + unsigned char sum=0; + int i; + + for (i=0; i<512; i++) + sum+=buffer[i]; + + return sum; +} + +// returns -1 if command fails or the device is in Sleep mode, else +// value of Sector Count register. Sector Count result values: +// 00h device is in Standby mode. +// 80h device is in Idle mode. +// FFh device is in Active mode or Idle mode. + +int ataCheckPowerMode(int device) { + unsigned char result; + + if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result))) + return -1; + + if (result!=0 && result!=0x80 && result!=0xff) + pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result); + + return (int)result; +} + + + + +// Reads current Device Identity info (512 bytes) into buf. Returns 0 +// if all OK. Returns -1 if no ATA Device identity can be +// established. Returns >0 if Device is ATA Packet Device (not SMART +// capable). The value of the integer helps identify the type of +// Packet device, which is useful so that the user can connect the +// formal device number with whatever object is inside their computer. +int ataReadHDIdentity (int device, struct ata_identify_device *buf){ + unsigned short *rawshort=(unsigned short *)buf; + unsigned char *rawbyte =(unsigned char *)buf; + + // See if device responds either to IDENTIFY DEVICE or IDENTIFY + // PACKET DEVICE + if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){ + if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){ + return -1; + } + } + +#ifndef __NetBSD__ + // if machine is big-endian, swap byte order as needed + // (the NetBSD kernel does deliver the results in host byte order) + if (isbigendian()){ + int i; + + // swap various capability words that are needed + for (i=0; i<33; i++) + swap2((char *)(buf->words047_079+i)); + + for (i=80; i<=87; i++) + swap2((char *)(rawshort+i)); + + for (i=0; i<168; i++) + swap2((char *)(buf->words088_255+i)); + } +#endif + + // If there is a checksum there, validate it + if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte)) + checksumwarning("Drive Identity Structure"); + + // If this is a PACKET DEVICE, return device type + if (rawbyte[1] & 0x80) + return 1+(rawbyte[1] & 0x1f); + + // Not a PACKET DEVICE + return 0; +} + +// Returns ATA version as an integer, and a pointer to a string +// describing which revision. Note that Revision 0 of ATA-3 does NOT +// support SMART. For this one case we return -3 rather than +3 as +// the version number. See notes above. +int ataVersionInfo (const char** description, struct ata_identify_device *drive, unsigned short *minor){ + unsigned short major; + int i; + + // check that arrays at the top of this file are defined + // consistently + if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){ + pout("Internal error in ataVersionInfo(). minor_str[] size %d\n" + "is not consistent with value of MINOR_MAX+1 = %d\n", + (int)(sizeof(minor_str)/sizeof(char *)), MINOR_MAX+1); + fflush(NULL); + abort(); + } + if (sizeof(actual_ver) != sizeof(int)*(1+MINOR_MAX)){ + pout("Internal error in ataVersionInfo(). actual_ver[] size %d\n" + "is not consistent with value of MINOR_MAX = %d\n", + (int)(sizeof(actual_ver)/sizeof(int)), MINOR_MAX+1); + fflush(NULL); + abort(); + } + + // get major and minor ATA revision numbers + major=drive->major_rev_num; + *minor=drive->minor_rev_num; + + // First check if device has ANY ATA version information in it + if (major==NOVAL_0 || major==NOVAL_1) { + *description=NULL; + return -1; + } + + // The minor revision number has more information - try there first + if (*minor && (*minor<=MINOR_MAX)){ + int std = actual_ver[*minor]; + if (std) { + *description=minor_str[*minor]; + return std; + } + } + + // HDPARM has a very complicated algorithm from here on. Since SMART only + // exists on ATA-3 and later standards, let's punt on this. If you don't + // like it, please fix it. The code's in CVS. + for (i=15; i>0; i--) + if (major & (0x1<command_set_1; + unsigned short word83=drive->command_set_2; + + // check if words 82/83 contain valid info + if ((word83>>14) == 0x01) + // return value of SMART support bit + return word82 & 0x0001; + + // since we can're rely on word 82, we don't know if SMART supported + return -1; +} + +// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell +int ataIsSmartEnabled(struct ata_identify_device *drive){ + unsigned short word85=drive->cfs_enable_1; + unsigned short word87=drive->csf_default; + + // check if words 85/86/87 contain valid info + if ((word87>>14) == 0x01) + // return value of SMART enabled bit + return word85 & 0x0001; + + // Since we can't rely word85, we don't know if SMART is enabled. + return -1; +} + + +// Reads SMART attributes into *data +int ataReadSmartValues(int device, struct ata_smart_values *data){ + + if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){ + syserror("Error SMART Values Read failed"); + return -1; + } + + // compute checksum + if (checksum((unsigned char *)data)) + checksumwarning("SMART Attribute Data Structure"); + + // byte swap if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->revnumber)); + swap2((char *)&(data->total_time_to_complete_off_line)); + swap2((char *)&(data->smart_capability)); + for (i=0; ivendor_attributes+i; + swap2((char *)&(x->flags)); + } + } + + return 0; +} + + +// This corrects some quantities that are byte reversed in the SMART +// SELF TEST LOG +void fixsamsungselftestlog(struct ata_smart_selftestlog *data){ + int i; + + // bytes 508/509 (numbered from 0) swapped (swap of self-test index + // with one byte of reserved. + swap2((char *)&(data->mostrecenttest)); + + // LBA low register (here called 'selftestnumber", containing + // information about the TYPE of the self-test) is byte swapped with + // Self-test execution status byte. These are bytes N, N+1 in the + // entries. + for (i=0; i<21; i++) + swap2((char *)&(data->selftest_struct[i].selftestnumber)); + + return; +} + +// Reads the Self Test Log (log #6) +int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){ + syserror("Error SMART Error Self-Test Log Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Self-Test Log Structure"); + + // fix firmware bugs in self-test log + if (con->fixfirmwarebug == FIX_SAMSUNG) + fixsamsungselftestlog(data); + + // fix endian order, if needed + if (isbigendian()){ + int i; + swap2((char*)&(data->revnumber)); + for (i=0; i<21; i++){ + struct ata_smart_selftestlog_struct *x=data->selftest_struct+i; + swap2((char *)&(x->timestamp)); + swap4((char *)&(x->lbafirstfailure)); + } + } + + return 0; +} + + +// Reads the Log Directory (log #0). Note: NO CHECKSUM!! +int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){ + return -1; + } + + // swap endian order if needed + if (isbigendian()){ + swap2((char *)&(data->logversion)); + } + + return 0; +} + + +// Reads the selective self-test log (log #9) +int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){ + syserror("Error SMART Read Selective Self-Test Log failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Selective Self-Test Log Structure"); + + // swap endian order if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->logversion)); + for (i=0;i<5;i++){ + swap8((char *)&(data->span[i].start)); + swap8((char *)&(data->span[i].end)); + } + swap8((char *)&(data->currentlba)); + swap2((char *)&(data->currentspan)); + swap2((char *)&(data->flags)); + swap2((char *)&(data->pendingtime)); + } + + if (data->logversion != 1) + pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion); + + return 0; +} + +// Writes the selective self-test log (log #9) +int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv){ + int i; + struct ata_selective_self_test_log sstlog, *data=&sstlog; + unsigned char cksum=0; + unsigned char *ptr=(unsigned char *)data; + + // Read log + if (ataReadSelectiveSelfTestLog(device, data)) { + pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n"); + return -1; + } + + // Fix logversion if needed + if (data->logversion !=1) { + pout("Error SMART Selective Self-Test Log Data Structure Revision not recognized\n" + "Revision number should be 1 but is %d. To be safe, aborting WRITE LOG\n", data->logversion); + return -2; + } + + // Host is NOT allowed to write selective self-test log if a selective + // self-test is in progress. + if (0currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) { + pout("Error SMART Selective or other Self-Test in progress.\n"); + return -4; + } + + // Clear spans + for (i=0; i<5; i++) + memset(data->span+i, 0, sizeof(struct test_span)); + + // Set spans for testing + for (i=0; ismartselectivenumspans; i++){ + data->span[i].start = con->smartselectivespan[i][0]; + data->span[i].end = con->smartselectivespan[i][1]; + } + + // host must initialize to zero before initiating selective self-test + data->currentlba=0; + data->currentspan=0; + + // Perform off-line scan after selective test? + if (1 == con->scanafterselect) + // NO + data->flags &= ~SELECTIVE_FLAG_DOSCAN; + else if (2 == con->scanafterselect) + // YES + data->flags |= SELECTIVE_FLAG_DOSCAN; + + // Must clear active and pending flags before writing + data->flags &= ~(SELECTIVE_FLAG_ACTIVE); + data->flags &= ~(SELECTIVE_FLAG_PENDING); + + // modify pending time? + if (con->pendingtime) + data->pendingtime=(unsigned short)(con->pendingtime-1); + + // Set checksum to zero, then compute checksum + data->checksum=0; + for (i=0; i<512; i++) + cksum+=ptr[i]; + cksum=~cksum; + cksum+=1; + data->checksum=cksum; + + // swap endian order if needed + if (isbigendian()){ + int i; + swap2((char *)&(data->logversion)); + for (i=0;i<5;i++){ + swap8((char *)&(data->span[i].start)); + swap8((char *)&(data->span[i].end)); + } + swap8((char *)&(data->currentlba)); + swap2((char *)&(data->currentspan)); + swap2((char *)&(data->flags)); + swap2((char *)&(data->pendingtime)); + } + + // write new selective self-test log + if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){ + syserror("Error Write Selective Self-Test Log failed"); + return -3; + } + + return 0; +} + +// This corrects some quantities that are byte reversed in the SMART +// ATA ERROR LOG. +void fixsamsungerrorlog(struct ata_smart_errorlog *data){ + int i,j; + + // FIXED IN SAMSUNG -25 FIRMWARE??? + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + + // FIXED IN SAMSUNG -22a FIRMWARE + // step through 5 error log data structures + for (i=0; i<5; i++){ + // step through 5 command data structures + for (j=0; j<5; j++) + // Command data structure 4-byte millisec timestamp. These are + // bytes (N+8, N+9, N+10, N+11). + swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp)); + // Error data structure two-byte hour life timestamp. These are + // bytes (N+28, N+29). + swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp)); + } + return; +} + +// NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE +void fixsamsungerrorlog2(struct ata_smart_errorlog *data){ + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + return; +} + +// Reads the Summary SMART Error Log (log #1). The Comprehensive SMART +// Error Log is #2, and the Extended Comprehensive SMART Error log is +// #3 +int ataReadErrorLog (int device, struct ata_smart_errorlog *data){ + + // get data from device + if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){ + syserror("Error SMART Error Log Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART ATA Error Log Structure"); + + // Some disks have the byte order reversed in some SMART Summary + // Error log entries + if (con->fixfirmwarebug == FIX_SAMSUNG) + fixsamsungerrorlog(data); + else if (con->fixfirmwarebug == FIX_SAMSUNG2) + fixsamsungerrorlog2(data); + + // Correct endian order if necessary + if (isbigendian()){ + int i,j; + + // Device error count in bytes 452-3 + swap2((char *)&(data->ata_error_count)); + + // step through 5 error log data structures + for (i=0; i<5; i++){ + // step through 5 command data structures + for (j=0; j<5; j++) + // Command data structure 4-byte millisec timestamp + swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp)); + // Error data structure life timestamp + swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp)); + } + } + + return 0; +} + +int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){ + + // get data from device + if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){ + syserror("Error SMART Thresholds Read failed"); + return -1; + } + + // compute its checksum, and issue a warning if needed + if (checksum((unsigned char *)data)) + checksumwarning("SMART Attribute Thresholds Structure"); + + // byte swap if needed + if (isbigendian()) + swap2((char *)&(data->revnumber)); + + return 0; +} + +int ataEnableSmart (int device ){ + if (smartcommandhandler(device, ENABLE, 0, NULL)){ + syserror("Error SMART Enable failed"); + return -1; + } + return 0; +} + +int ataDisableSmart (int device ){ + + if (smartcommandhandler(device, DISABLE, 0, NULL)){ + syserror("Error SMART Disable failed"); + return -1; + } + return 0; +} + +int ataEnableAutoSave(int device){ + if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){ + syserror("Error SMART Enable Auto-save failed"); + return -1; + } + return 0; +} + +int ataDisableAutoSave(int device){ + + if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){ + syserror("Error SMART Disable Auto-save failed"); + return -1; + } + return 0; +} + +// In *ALL* ATA standards the Enable/Disable AutoOffline command is +// marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most +// vendors still support it for backwards compatibility. IBM documents +// it for some drives. +int ataEnableAutoOffline (int device ){ + + /* timer hard coded to 4 hours */ + if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ + syserror("Error SMART Enable Automatic Offline failed"); + return -1; + } + return 0; +} + +// Another Obsolete Command. See comments directly above, associated +// with the corresponding Enable command. +int ataDisableAutoOffline (int device ){ + + if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){ + syserror("Error SMART Disable Automatic Offline failed"); + return -1; + } + return 0; +} + +// If SMART is enabled, supported, and working, then this call is +// guaranteed to return 1, else zero. Note that it should return 1 +// regardless of whether the disk's SMART status is 'healthy' or +// 'failing'. +int ataDoesSmartWork(int device){ + int retval=smartcommandhandler(device, STATUS, 0, NULL); + + if (-1 == retval) + return 0; + + return 1; +} + +// This function uses a different interface (DRIVE_TASK) than the +// other commands in this file. +int ataSmartStatus2(int device){ + return smartcommandhandler(device, STATUS_CHECK, 0, NULL); +} + +// This is the way to execute ALL tests: offline, short self-test, +// extended self test, with and without captive mode, etc. +int ataSmartTest(int device, int testtype, struct ata_smart_values *sv) { + char cmdmsg[128],*type,*captive; + int errornum, cap, retval, select=0; + + // Boolean, if set, says test is captive + cap=testtype & CAPTIVE_MASK; + + // Set up strings that describe the type of test + if (cap) + captive="captive"; + else + captive="off-line"; + + if (testtype==OFFLINE_FULL_SCAN) + type="off-line"; + else if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST) + type="Short self-test"; + else if (testtype==EXTEND_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) + type="Extended self-test"; + else if (testtype==CONVEYANCE_SELF_TEST || testtype==CONVEYANCE_CAPTIVE_SELF_TEST) + type="Conveyance self-test"; + else if ((select=(testtype==SELECTIVE_SELF_TEST || testtype==SELECTIVE_CAPTIVE_SELF_TEST))) + type="Selective self-test"; + else + type="[Unrecognized] self-test"; + + // If doing a selective self-test, first use WRITE_LOG to write the + // selective self-test log. + if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv))) { + if (retval==-4) + pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n"); + return retval; + } + + // Print ouf message that we are sending the command to test + if (testtype==ABORT_SELF_TEST) + sprintf(cmdmsg,"Abort SMART off-line mode self-test routine"); + else + sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive); + pout("Sending command: \"%s\".\n",cmdmsg); + + if (select) { + int i; + pout("SPAN STARTING_LBA ENDING_LBA\n"); + for (i = 0; i < con->smartselectivenumspans; i++) + pout(" %d %20"PRId64" %20"PRId64"\n", i, + con->smartselectivespan[i][0], + con->smartselectivespan[i][1]); + } + + // Now send the command to test + errornum=smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL); + + if (errornum && !(cap && errno==EIO)){ + char errormsg[128]; + sprintf(errormsg,"Command \"%s\" failed",cmdmsg); + syserror(errormsg); + pout("\n"); + return -1; + } + + // Since the command succeeded, tell user + if (testtype==ABORT_SELF_TEST) + pout("Self-testing aborted!\n"); + else + pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg); + return 0; +} + +/* Test Time Functions */ +int TestTime(struct ata_smart_values *data,int testtype){ + switch (testtype){ + case OFFLINE_FULL_SCAN: + return (int) data->total_time_to_complete_off_line; + case SHORT_SELF_TEST: + case SHORT_CAPTIVE_SELF_TEST: + return (int) data->short_test_completion_time; + case EXTEND_SELF_TEST: + case EXTEND_CAPTIVE_SELF_TEST: + return (int) data->extend_test_completion_time; + case CONVEYANCE_SELF_TEST: + case CONVEYANCE_CAPTIVE_SELF_TEST: + return (int) data->conveyance_test_completion_time; + default: + return 0; + } +} + +// This function tells you both about the ATA error log and the +// self-test error log capability (introduced in ATA-5). The bit is +// poorly documented in the ATA/ATAPI standard. Starting with ATA-6, +// SMART error logging is also indicated in bit 0 of DEVICE IDENTIFY +// word 84 and 87. Top two bits must match the pattern 01. BEFORE +// ATA-6 these top two bits still had to match the pattern 01, but the +// remaining bits were reserved (==0). +int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){ + + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + int isata6=identity->major_rev_num & (0x01<<6); + int isata7=identity->major_rev_num & (0x01<<7); + + if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01)) + return 1; + + if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01)) + return 1; + + // otherwise we'll use the poorly documented capability bit + return data->errorlog_capability & 0x01; +} + +// See previous function. If the error log exists then the self-test +// log should (must?) also exist. +int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){ + + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + int isata6=identity->major_rev_num & (0x01<<6); + int isata7=identity->major_rev_num & (0x01<<7); + + if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02)) + return 1; + + if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02)) + return 1; + + + // otherwise we'll use the poorly documented capability bit + return data->errorlog_capability & 0x01; +} + + +int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){ + unsigned short word84=identity->command_set_extension; + unsigned short word87=identity->csf_default; + + // If bit 14 of word 84 is set to one and bit 15 of word 84 is + // cleared to zero, the contents of word 84 contains valid support + // information. If not, support information is not valid in this + // word. + if ((word84>>14) == 0x01) + // If bit 5 of word 84 is set to one, the device supports the + // General Purpose Logging feature set. + return (word84 & (0x01 << 5)); + + // If bit 14 of word 87 is set to one and bit 15 of word 87 is + // cleared to zero, the contents of words (87:85) contain valid + // information. If not, information is not valid in these words. + if ((word87>>14) == 0x01) + // If bit 5 of word 87 is set to one, the device supports + // the General Purpose Logging feature set. + return (word87 & (0x01 << 5)); + + // not capable + return 0; +} + + +// SMART self-test capability is also indicated in bit 1 of DEVICE +// IDENTIFY word 87 (if top two bits of word 87 match pattern 01). +// However this was only introduced in ATA-6 (but self-test log was in +// ATA-5). +int isSupportExecuteOfflineImmediate(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x01; +} +// Note in the ATA-5 standard, the following bit is listed as "Vendor +// Specific". So it may not be reliable. The only use of this that I +// have found is in IBM drives, where it is well-documented. See for +// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX +// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002. +int isSupportAutomaticTimer(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x02; +} +int isSupportOfflineAbort(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x04; +} +int isSupportOfflineSurfaceScan(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x08; +} +int isSupportSelfTest (struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x10; +} +int isSupportConveyanceSelfTest(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x20; +} +int isSupportSelectiveSelfTest(struct ata_smart_values *data){ + return data->offline_data_collection_capability & 0x40; +} + + + +// Loop over all valid attributes. If they are prefailure attributes +// and are at or below the threshold value, then return the ID of the +// first failing attribute found. Return 0 if all prefailure +// attributes are in bounds. The spec says "Bit 0 +// -Pre-failure/advisory - If the value of this bit equals zero, an +// attribute value less than or equal to its corresponding attribute +// threshold indicates an advisory condition where the usage or age of +// the device has exceeded its intended design life period. If the +// value of this bit equals one, an atribute value less than or equal +// to its corresponding attribute threshold indicates a pre-failure +// condition where imminent loss of data is being predicted." + + +// onlyfailed=0 : are or were any age or prefailure attributes <= threshold +// onlyfailed=1: are any prefailure attributes <= threshold now +int ataCheckSmart(struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds, + int onlyfailed){ + int i; + + // loop over all attributes + for (i=0; ivendor_attributes+i; + struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i; + + // consider only valid attributes + if (disk->id && thre->id){ + int failednow,failedever; + + failednow =disk->current <= thre->threshold; + failedever=disk->worst <= thre->threshold; + + if (!onlyfailed && failedever) + return disk->id; + + if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) + return disk->id; + } + } + return 0; +} + + + +// This checks the n'th attribute in the attribute list, NOT the +// attribute with id==n. If the attribute does not exist, or the +// attribute is > threshold, then returns zero. If the attribute is +// <= threshold (failing) then we the attribute number if it is a +// prefail attribute. Else we return minus the attribute number if it +// is a usage attribute. +int ataCheckAttribute(struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds, + int n){ + struct ata_smart_attribute *disk; + struct ata_smart_threshold_entry *thre; + + if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds) + return 0; + + // pointers to disk's values and vendor's thresholds + disk=data->vendor_attributes+n; + thre=thresholds->thres_entries+n; + + if (!disk || !thre) + return 0; + + // consider only valid attributes, check for failure + if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold) + return 0; + + // We have found a failed attribute. Return positive or negative? + if (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) + return disk->id; + else + return -1*(disk->id); +} + + +// This routine prints the raw value of an attribute as a text string +// into out. It also returns this 48-bit number as a long long. The +// array defs[] contains non-zero values if particular attributes have +// non-default interpretations. + +int64_t ataPrintSmartAttribRawValue(char *out, + struct ata_smart_attribute *attribute, + unsigned char *defs){ + int64_t rawvalue; + unsigned word[3]; + int j; + unsigned char select; + + // convert the six individual bytes to a long long (8 byte) integer. + // This is the value that we'll eventually return. + rawvalue = 0; + for (j=0; j<6; j++) { + // This looks a bit roundabout, but is necessary. Don't + // succumb to the temptation to use raw[j]<<(8*j) since under + // the normal rules this will be promoted to the native type. + // On a 32 bit machine this might then overflow. + int64_t temp; + temp = attribute->raw[j]; + temp <<= 8*j; + rawvalue |= temp; + } + + // convert quantities to three two-byte words + for (j=0; j<3; j++){ + word[j] = attribute->raw[2*j+1]; + word[j] <<= 8; + word[j] |= attribute->raw[2*j]; + } + + // if no data array, Attributes have default interpretations + if (defs) + select=defs[attribute->id]; + else + select=0; + + // Print six one-byte quantities. + if (select==253){ + for (j=0; j<5; j++) + out+=sprintf(out, "%d ", attribute->raw[5-j]); + out+=sprintf(out, "%d ", attribute->raw[0]); + return rawvalue; + } + + // Print three two-byte quantities + if (select==254){ + out+=sprintf(out, "%d %d %d", word[2], word[1], word[0]); + return rawvalue; + } + + // Print one six-byte quantity + if (select==255){ + out+=sprintf(out, "%"PRIu64, rawvalue); + return rawvalue; + } + + // This switch statement is where we handle Raw attributes + // that are stored in an unusual vendor-specific format, + switch (attribute->id){ + // Spin-up time + case 3: + out+=sprintf(out, "%d", word[0]); + // if second nonzero then it stores the average spin-up time + if (word[1]) + out+=sprintf(out, " (Average %d)", word[1]); + break; + // Power on time + case 9: + if (select==1){ + // minutes + int64_t tmp1=rawvalue/60; + int64_t tmp2=rawvalue%60; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2); + } + else if (select==3){ + // seconds + int64_t hours=rawvalue/3600; + int64_t minutes=(rawvalue-3600*hours)/60; + int64_t seconds=rawvalue%60; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m+%02"PRIu64"s", hours, minutes, seconds); + } + else if (select==4){ + // 30-second counter + int64_t tmp1=rawvalue/120; + int64_t tmp2=(rawvalue-120*tmp1)/2; + out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2); + } + else + // hours + out+=sprintf(out, "%"PRIu64, rawvalue); //stored in hours + break; + // Load unload cycles + case 193: + if (select==1){ + // loadunload + long load =attribute->raw[0] + (attribute->raw[1]<<8) + (attribute->raw[2]<<16); + long unload=attribute->raw[3] + (attribute->raw[4]<<8) + (attribute->raw[5]<<16); + out+=sprintf(out, "%lu/%lu", load, unload); + } + else + // associated + out+=sprintf(out, "%"PRIu64, rawvalue); + break; + // Temperature + case 194: + if (select==1){ + // ten times temperature in Celsius + int deg=word[0]/10; + int tenths=word[0]%10; + out+=sprintf(out, "%d.%d", deg, tenths); + } + else if (select==2) + // unknown attribute + out+=sprintf(out, "%"PRIu64, rawvalue); + else { + out+=sprintf(out, "%d", word[0]); + if (!(rawvalue==word[0])) { + int min=word[1]word[2]?word[1]:word[2]; + // The other bytes are in use. Try IBM's model + out+=sprintf(out, " (Lifetime Min/Max %d/%d)", min, max); + } + } + break; + default: + out+=sprintf(out, "%"PRIu64, rawvalue); + } + + // Return the full value + return rawvalue; +} + + +// Note some attribute names appear redundant because different +// manufacturers use different attribute IDs for an attribute with the +// same name. The variable val should contain a non-zero value if a particular +// attributes has a non-default interpretation. +void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definitions){ + char *name; + unsigned char val; + + // If no data array, use default interpretations + if (definitions) + val=definitions[id]; + else + val=0; + + switch (id){ + + case 1: + name="Raw_Read_Error_Rate"; + break; + case 2: + name="Throughput_Performance"; + break; + case 3: + name="Spin_Up_Time"; + break; + case 4: + name="Start_Stop_Count"; + break; + case 5: + name="Reallocated_Sector_Ct"; + break; + case 6: + name="Read_Channel_Margin"; + break; + case 7: + name="Seek_Error_Rate"; + break; + case 8: + name="Seek_Time_Performance"; + break; + case 9: + switch (val) { + case 1: + name="Power_On_Minutes"; + break; + case 2: + name="Temperature_Celsius"; + break; + case 3: + name="Power_On_Seconds"; + break; + case 4: + name="Power_On_Half_Minutes"; + break; + default: + name="Power_On_Hours"; + break; + } + break; + case 10: + name="Spin_Retry_Count"; + break; + case 11: + name="Calibration_Retry_Count"; + break; + case 12: + name="Power_Cycle_Count"; + break; + case 13: + name="Read_Soft_Error_Rate"; + break; + case 191: + name="G-Sense_Error_Rate"; + break; + case 192: + switch (val) { + case 1: + // Fujitsu + name="Emergency_Retract_Cycle_Ct"; + break; + default: + name="Power-Off_Retract_Count"; + break; + } + break; + case 193: + name="Load_Cycle_Count"; + break; + case 194: + switch (val){ + case 1: + // Samsung SV1204H with RK100-13 firmware + name="Temperature_Celsius_x10"; + break; + case 2: + // for disks with no temperature Attribute + name="Unknown_Attribute"; + break; + default: + name="Temperature_Celsius"; + break; + } + break; + case 195: + // Fujitsu name="ECC_On_The_Fly_Count"; + name="Hardware_ECC_Recovered"; + break; + case 196: + name="Reallocated_Event_Count"; + break; + case 197: + name="Current_Pending_Sector"; + break; + case 198: + switch (val){ + case 1: + // Fujitsu + name="Off-line_Scan_UNC_Sector_Ct"; + break; + default: + name="Offline_Uncorrectable"; + break; + } + break; + case 199: + name="UDMA_CRC_Error_Count"; + break; + case 200: + switch (val) { + case 1: + // Fujitsu MHS2020AT + name="Write_Error_Count"; + break; + default: + // Western Digital + name="Multi_Zone_Error_Rate"; + break; + } + break; + case 201: + switch (val) { + case 1: + // Fujitsu + name="Detected_TA_Count"; + break; + default: + name="Soft_Read_Error_Rate"; + break; + } + break; + case 202: + // Fujitsu + name="TA_Increase_Count"; + // Maxtor: Data Address Mark Errors + break; + case 203: + // Fujitsu + name="Run_Out_Cancel"; + // Maxtor: ECC Errors + break; + case 204: + // Fujitsu + name="Shock_Count_Write_Opern"; + // Maxtor: Soft ECC Correction + break; + case 205: + // Fujitsu + name="Shock_Rate_Write_Opern"; + // Maxtor: Thermal Aspirates + break; + case 206: + // Fujitsu + name="Flying_Height"; + break; + case 207: + // Maxtor + name="Spin_High_Current"; + break; + case 208: + // Maxtor + name="Spin_Buzz"; + break; + case 209: + // Maxtor + name="Offline_Seek_Performnce"; + break; + case 220: + switch (val) { + case 1: + name="Temperature_Celsius"; + break; + default: + name="Disk_Shift"; + break; + } + break; + case 221: + name="G-Sense_Error_Rate"; + break; + case 222: + name="Loaded_Hours"; + break; + case 223: + name="Load_Retry_Count"; + break; + case 224: + name="Load_Friction"; + break; + case 225: + name="Load_Cycle_Count"; + break; + case 226: + name="Load-in_Time"; + break; + case 227: + name="Torq-amp_Count"; + break; + case 228: + name="Power-off_Retract_Count"; + break; + case 230: + // seen in IBM DTPA-353750 + name="Head_Amplitude"; + break; + case 231: + name="Temperature_Celsius"; + break; + case 240: + name="Head_Flying_Hours"; + break; + case 250: + name="Read_Error_Retry_Rate"; + break; + default: + name="Unknown_Attribute"; + break; + } + sprintf(out,"%3hu %s",(short int)id,name); + return; +} + +// Returns raw value of Attribute with ID==id. This will be in the +// range 0 to 2^48-1 inclusive. If the Attribute does not exist, +// return -1. +int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data) { + int i; + + // valid Attribute IDs are in the range 1 to 255 inclusive. + if (!id || !data) + return -1; + + // loop over Attributes to see if there is one with the desired ID + for (i=0; ivendor_attributes + i; + if (this->id == id) { + // we've found the desired Attribute. Return its value + int64_t rawvalue=0; + int j; + + for (j=0; j<6; j++) { + // This looks a bit roundabout, but is necessary. Don't + // succumb to the temptation to use raw[j]<<(8*j) since under + // the normal rules this will be promoted to the native type. + // On a 32 bit machine this might then overflow. + int64_t temp; + temp = this->raw[j]; + temp <<= 8*j; + rawvalue |= temp; + } // loop over j + return rawvalue; + } // found desired Attribute + } // loop over Attributes + + // fall-through: no such Attribute found + return -1; +} + + diff --git a/atacmds.h b/atacmds.h new file mode 100644 index 0000000..01698d2 --- /dev/null +++ b/atacmds.h @@ -0,0 +1,537 @@ +/* + * atacmds.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef ATACMDS_H_ +#define ATACMDS_H_ + +#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.81 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// Macro to check expected size of struct at compile time using a +// dummy typedef. On size mismatch, compiler reports a negative array +// size. If you see an error message of this form, it means that the +// #pragma pack(1) pragma below is not having the desired effect on +// your compiler. +#define ASSERT_SIZEOF_STRUCT(s, n) \ + typedef char assert_sizeof_struct_##s[(sizeof(struct s) == (n)) ? 1 : -1] + +// Add __attribute__((packed)) if compiler supports it +// because some gcc versions (at least ARM) lack support of #pragma pack() +#ifdef HAVE_ATTR_PACKED +#define ATTR_PACKED __attribute__((packed)) +#else +#define ATTR_PACKED +#endif + +typedef enum { + // returns no data, just succeeds or fails + ENABLE, + DISABLE, + AUTOSAVE, + IMMEDIATE_OFFLINE, + AUTO_OFFLINE, + STATUS, // just says if SMART is working or not + STATUS_CHECK, // says if disk's SMART status is healthy, or failing + // return 512 bytes of data: + READ_VALUES, + READ_THRESHOLDS, + READ_LOG, + IDENTIFY, + PIDENTIFY, + // returns 1 byte of data + CHECK_POWER_MODE, + // writes 512 bytes of data: + WRITE_LOG +} smart_command_set; + +// ATA Specification Command Register Values (Commands) +#define ATA_IDENTIFY_DEVICE 0xec +#define ATA_IDENTIFY_PACKET_DEVICE 0xa1 +#define ATA_SMART_CMD 0xb0 +#define ATA_CHECK_POWER_MODE 0xe5 + +// ATA Specification Feature Register Values (SMART Subcommands). +// Note that some are obsolete as of ATA-7. +#define ATA_SMART_READ_VALUES 0xd0 +#define ATA_SMART_READ_THRESHOLDS 0xd1 +#define ATA_SMART_AUTOSAVE 0xd2 +#define ATA_SMART_SAVE 0xd3 +#define ATA_SMART_IMMEDIATE_OFFLINE 0xd4 +#define ATA_SMART_READ_LOG_SECTOR 0xd5 +#define ATA_SMART_WRITE_LOG_SECTOR 0xd6 +#define ATA_SMART_WRITE_THRESHOLDS 0xd7 +#define ATA_SMART_ENABLE 0xd8 +#define ATA_SMART_DISABLE 0xd9 +#define ATA_SMART_STATUS 0xda +// SFF 8035i Revision 2 Specification Feature Register Value (SMART +// Subcommand) +#define ATA_SMART_AUTO_OFFLINE 0xdb + +// Sector Number values for ATA_SMART_IMMEDIATE_OFFLINE Subcommand +#define OFFLINE_FULL_SCAN 0 +#define SHORT_SELF_TEST 1 +#define EXTEND_SELF_TEST 2 +#define CONVEYANCE_SELF_TEST 3 +#define SELECTIVE_SELF_TEST 4 +#define ABORT_SELF_TEST 127 +#define SHORT_CAPTIVE_SELF_TEST 129 +#define EXTEND_CAPTIVE_SELF_TEST 130 +#define CONVEYANCE_CAPTIVE_SELF_TEST 131 +#define SELECTIVE_CAPTIVE_SELF_TEST 132 +#define CAPTIVE_MASK (0x01<<7) + +// Maximum allowed number of SMART Attributes +#define NUMBER_ATA_SMART_ATTRIBUTES 30 + +// Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled +// word* are NOT used. +#pragma pack(1) +struct ata_identify_device { + unsigned short words000_009[10]; + unsigned char serial_no[20]; + unsigned short words020_022[3]; + unsigned char fw_rev[8]; + unsigned char model[40]; + unsigned short words047_079[33]; + unsigned short major_rev_num; + unsigned short minor_rev_num; + unsigned short command_set_1; + unsigned short command_set_2; + unsigned short command_set_extension; + unsigned short cfs_enable_1; + unsigned short word086; + unsigned short csf_default; + unsigned short words088_255[168]; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_identify_device, 512); + +/* ata_smart_attribute is the vendor specific in SFF-8035 spec */ +#pragma pack(1) +struct ata_smart_attribute { + unsigned char id; + // meaning of flag bits: see MACROS just below + // WARNING: MISALIGNED! + unsigned short flags; + unsigned char current; + unsigned char worst; + unsigned char raw[6]; + unsigned char reserv; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_attribute, 12); + +// MACROS to interpret the flags bits in the previous structure. +// These have not been implemented using bitflags and a union, to make +// it portable across bit/little endian and different platforms. + +// 0: Prefailure bit + +// From SFF 8035i Revision 2 page 19: Bit 0 (pre-failure/advisory bit) +// - If the value of this bit equals zero, an attribute value less +// than or equal to its corresponding attribute threshold indicates an +// advisory condition where the usage or age of the device has +// exceeded its intended design life period. If the value of this bit +// equals one, an attribute value less than or equal to its +// corresponding attribute threshold indicates a prefailure condition +// where imminent loss of data is being predicted. +#define ATTRIBUTE_FLAGS_PREFAILURE(x) (x & 0x01) + +// 1: Online bit + +// From SFF 8035i Revision 2 page 19: Bit 1 (on-line data collection +// bit) - If the value of this bit equals zero, then the attribute +// value is updated only during off-line data collection +// activities. If the value of this bit equals one, then the attribute +// value is updated during normal operation of the device or during +// both normal operation and off-line testing. +#define ATTRIBUTE_FLAGS_ONLINE(x) (x & 0x02) + + +// The following are (probably) IBM's, Maxtors and Quantum's definitions for the +// vendor-specific bits: +// 2: Performance type bit +#define ATTRIBUTE_FLAGS_PERFORMANCE(x) (x & 0x04) + +// 3: Errorrate type bit +#define ATTRIBUTE_FLAGS_ERRORRATE(x) (x & 0x08) + +// 4: Eventcount bit +#define ATTRIBUTE_FLAGS_EVENTCOUNT(x) (x & 0x10) + +// 5: Selfpereserving bit +#define ATTRIBUTE_FLAGS_SELFPRESERVING(x) (x & 0x20) + + +// Last ten bits are reserved for future use + +/* ata_smart_values is format of the read drive Attribute command */ +/* see Table 34 of T13/1321D Rev 1 spec (Device SMART data structure) for *some* info */ +#pragma pack(1) +struct ata_smart_values { + unsigned short int revnumber; + struct ata_smart_attribute vendor_attributes [NUMBER_ATA_SMART_ATTRIBUTES]; + unsigned char offline_data_collection_status; + unsigned char self_test_exec_status; //IBM # segments for offline collection + unsigned short int total_time_to_complete_off_line; // IBM different + unsigned char vendor_specific_366; // Maxtor & IBM curent segment pointer + unsigned char offline_data_collection_capability; + unsigned short int smart_capability; + unsigned char errorlog_capability; + unsigned char vendor_specific_371; // Maxtor, IBM: self-test failure checkpoint see below! + unsigned char short_test_completion_time; + unsigned char extend_test_completion_time; + unsigned char conveyance_test_completion_time; + unsigned char reserved_375_385[11]; + unsigned char vendor_specific_386_510[125]; // Maxtor bytes 508-509 Attribute/Threshold Revision # + unsigned char chksum; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_values, 512); + +/* Maxtor, IBM: self-test failure checkpoint byte meaning: + 00 - write test + 01 - servo basic + 02 - servo random + 03 - G-list scan + 04 - Handling damage + 05 - Read scan +*/ + +/* Vendor attribute of SMART Threshold (compare to ata_smart_attribute above) */ +#pragma pack(1) +struct ata_smart_threshold_entry { + unsigned char id; + unsigned char threshold; + unsigned char reserved[10]; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_threshold_entry, 12); + +/* Format of Read SMART THreshold Command */ +/* Compare to ata_smart_values above */ +#pragma pack(1) +struct ata_smart_thresholds_pvt { + unsigned short int revnumber; + struct ata_smart_threshold_entry thres_entries[NUMBER_ATA_SMART_ATTRIBUTES]; + unsigned char reserved[149]; + unsigned char chksum; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_thresholds_pvt, 512); + + +// Table 42 of T13/1321D Rev 1 spec (Error Data Structure) +#pragma pack(1) +struct ata_smart_errorlog_error_struct { + unsigned char reserved; + unsigned char error_register; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + unsigned char drive_head; + unsigned char status; + unsigned char extended_error[19]; + unsigned char state; + unsigned short timestamp; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_error_struct, 30); + + +// Table 41 of T13/1321D Rev 1 spec (Command Data Structure) +#pragma pack(1) +struct ata_smart_errorlog_command_struct { + unsigned char devicecontrolreg; + unsigned char featuresreg; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + unsigned char drive_head; + unsigned char commandreg; + unsigned int timestamp; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_command_struct, 12); + +// Table 40 of T13/1321D Rev 1 spec (Error log data structure) +#pragma pack(1) +struct ata_smart_errorlog_struct { + struct ata_smart_errorlog_command_struct commands[5]; + struct ata_smart_errorlog_error_struct error_struct; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_struct, 90); + +// Table 39 of T13/1321D Rev 1 spec (SMART error log sector) +#pragma pack(1) +struct ata_smart_errorlog { + unsigned char revnumber; + unsigned char error_log_pointer; + struct ata_smart_errorlog_struct errorlog_struct[5]; + unsigned short int ata_error_count; + unsigned char reserved[57]; + unsigned char checksum; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_errorlog, 512); + +// Table 45 of T13/1321D Rev 1 spec (Self-test log descriptor entry) +#pragma pack(1) +struct ata_smart_selftestlog_struct { + unsigned char selftestnumber; // Sector number register + unsigned char selfteststatus; + unsigned short int timestamp; + unsigned char selftestfailurecheckpoint; + unsigned int lbafirstfailure; + unsigned char vendorspecific[15]; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_selftestlog_struct, 24); + +// Table 44 of T13/1321D Rev 1 spec (Self-test log data structure) +#pragma pack(1) +struct ata_smart_selftestlog { + unsigned short int revnumber; + struct ata_smart_selftestlog_struct selftest_struct[21]; + unsigned char vendorspecific[2]; + unsigned char mostrecenttest; + unsigned char reserved[2]; + unsigned char chksum; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_selftestlog, 512); + +// SMART LOG DIRECTORY Table 52 of T13/1532D Vol 1 Rev 1a +#pragma pack(1) +struct ata_smart_log_entry { + unsigned char numsectors; + unsigned char reserved; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_log_entry, 2); + +#pragma pack(1) +struct ata_smart_log_directory { + unsigned short int logversion; + struct ata_smart_log_entry entry[255]; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_smart_log_directory, 512); + +// SMART SELECTIVE SELF-TEST LOG Table 61 of T13/1532D Volume 1 +// Revision 3 +#pragma pack(1) +struct test_span { + uint64_t start; + uint64_t end; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(test_span, 16); + +#pragma pack(1) +struct ata_selective_self_test_log { + unsigned short logversion; + struct test_span span[5]; + unsigned char reserved1[337-82+1]; + unsigned char vendor_specific1[491-338+1]; + uint64_t currentlba; + unsigned short currentspan; + unsigned short flags; + unsigned char vendor_specific2[507-504+1]; + unsigned short pendingtime; + unsigned char reserved2; + unsigned char checksum; +} ATTR_PACKED; +#pragma pack() +ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512); + +#define SELECTIVE_FLAG_DOSCAN (0x0002) +#define SELECTIVE_FLAG_PENDING (0x0008) +#define SELECTIVE_FLAG_ACTIVE (0x0010) + +// Get information from drive +int ataReadHDIdentity(int device, struct ata_identify_device *buf); +int ataCheckPowerMode(int device); + +/* Read S.M.A.R.T information from drive */ +int ataReadSmartValues(int device,struct ata_smart_values *); +int ataReadSmartThresholds(int device, struct ata_smart_thresholds_pvt *); +int ataReadErrorLog(int device, struct ata_smart_errorlog *); +int ataReadSelfTestLog(int device, struct ata_smart_selftestlog *); +int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data); +int ataSmartStatus(int device); +int ataSetSmartThresholds(int device, struct ata_smart_thresholds_pvt *); +int ataReadLogDirectory(int device, struct ata_smart_log_directory *); + +/* Enable/Disable SMART on device */ +int ataEnableSmart ( int device ); +int ataDisableSmart (int device ); +int ataEnableAutoSave(int device); +int ataDisableAutoSave(int device); + +/* Automatic Offline Testing */ +int ataEnableAutoOffline ( int device ); +int ataDisableAutoOffline (int device ); + +/* S.M.A.R.T. test commands */ +int ataSmartOfflineTest (int device); +int ataSmartExtendSelfTest (int device); +int ataSmartShortSelfTest (int device); +int ataSmartShortCapSelfTest (int device); +int ataSmartExtendCapSelfTest (int device); +int ataSmartSelfTestAbort (int device); + +// Returns the latest compatibility of ATA/ATAPI Version the device +// supports. Returns -1 if Version command is not supported +int ataVersionInfo (const char **description, struct ata_identify_device *drive, unsigned short *minor); + +// If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0. +int ataDoesSmartWork(int device); + +// returns 1 if SMART supported, 0 if not supported or can't tell +int ataSmartSupport ( struct ata_identify_device *drive); + +// Return values: +// 1: SMART enabled +// 0: SMART disabled +// -1: can't tell if SMART is enabled -- try issuing ataDoesSmartWork command to see +int ataIsSmartEnabled(struct ata_identify_device *drive); + +/* Check SMART for Threshold failure */ +// onlyfailed=0 : are or were any age or prefailure attributes <= threshold +// onlyfailed=1: are any prefailure attributes <= threshold now +int ataCheckSmart ( struct ata_smart_values *data, struct ata_smart_thresholds_pvt *thresholds, int onlyfailed); + +int ataSmartStatus2(int device); + +// int isOfflineTestTime ( struct ata_smart_values data) +// returns S.M.A.R.T. Offline Test Time in seconds +int isOfflineTestTime ( struct ata_smart_values *data); + +int isShortSelfTestTime ( struct ata_smart_values *data); + +int isExtendedSelfTestTime ( struct ata_smart_values *data); + +int isSmartErrorLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity); + +int isSmartTestLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity); + +int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity); + +int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data); + +int isSupportAutomaticTimer ( struct ata_smart_values *data); + +int isSupportOfflineAbort ( struct ata_smart_values *data); + +int isSupportOfflineSurfaceScan ( struct ata_smart_values *data); + +int isSupportSelfTest (struct ata_smart_values *data); + +int isSupportConveyanceSelfTest(struct ata_smart_values *data); + +int isSupportSelectiveSelfTest(struct ata_smart_values *data); + +int ataSmartTest(int device, int testtype, struct ata_smart_values *data); + +int TestTime(struct ata_smart_values *data,int testtype); + +// Prints the raw value (with appropriate formatting) into the +// character string out. +int64_t ataPrintSmartAttribRawValue(char *out, + struct ata_smart_attribute *attribute, + unsigned char *defs); + +// Prints Attribute Name for standard SMART attributes. Writes a +// 30 byte string with attribute name into output +void ataPrintSmartAttribName(char *output, unsigned char id, unsigned char *definitions); + +// This checks the n'th attribute in the attribute list, NOT the +// attribute with id==n. If the attribute does not exist, or the +// attribute is > threshold, then returns zero. If the attribute is +// <= threshold (failing) then we the attribute number if it is a +// prefail attribute. Else we return minus the attribute number if it +// is a usage attribute. +int ataCheckAttribute(struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds, + int n); + +// External handler function, for when a checksum is not correct. Can +// simply return if no action is desired, or can print error messages +// as needed, or exit. Is passed a string with the name of the Data +// Structure with the incorrect checksum. +void checksumwarning(const char *string); + +// Returns raw value of Attribute with ID==id. This will be in the +// range 0 to 2^48-1 inclusive. If the Attribute does not exist, +// return -1. +int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data); + + +// 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 + +extern const char *vendorattributeargs[]; + +// function to parse pairs like "9,minutes" or "220,temp". See end of +// extern.h for definition of defs[]. Returns 0 if pair recognized, +// else 1 if there is a problem. Allocates memory for array if the +// array address is *defs==NULL. +int parse_attribute_def(char *pair, unsigned char **defs); + +// Function to return a string containing a list of the arguments in +// vendorattributeargs[]. Returns NULL if the required memory can't +// be allocated. +char *create_vendor_attribute_arg_list(void); + + +// These are two of the functions that are defined in os_*.c and need +// to be ported to get smartmontools onto another OS. +int ata_command_interface(int device, smart_command_set command, int select, char *data); +int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data); +int marvell_command_interface(int device, 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(int device, smart_command_set command, int select, char *data); + +// Utility routines. +void swap2(char *location); +void swap4(char *location); +void swap8(char *location); +#endif /* ATACMDS_H_ */ diff --git a/ataprint.c b/ataprint.c new file mode 100644 index 0000000..09cbe4d --- /dev/null +++ b/ataprint.c @@ -0,0 +1,1864 @@ +/* + * ataprint.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif // #ifdef HAVE_LOCALE_H + +#include "int64.h" +#include "atacmdnames.h" +#include "atacmds.h" +#include "ataprint.h" +#include "smartctl.h" +#include "extern.h" +#include "utility.h" +#include "knowndrives.h" + +const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.164 2006/04/12 14:54:28 ballen4705 Exp $" +ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; + +// for passing global control variables +extern smartmonctrl *con; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents +// bytes. +void swapbytes(char *out, const char *in, size_t n) +{ + size_t i; + + for (i = 0; i < n; i += 2) { + out[i] = in[i+1]; + out[i+1] = in[i]; + } +} + +// Copies in to out, but removes leading and trailing whitespace. +void trim(char *out, const char *in) +{ + int i, first, last; + + // Find the first non-space character (maybe none). + first = -1; + for (i = 0; in[i]; i++) + if (!isspace((int)in[i])) { + first = i; + break; + } + + if (first == -1) { + // There are no non-space characters. + out[0] = '\0'; + return; + } + + // Find the last non-space character. + for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--) + ; + last = i; + + strncpy(out, in+first, last-first+1); + out[last-first+1] = '\0'; +} + +// Convenience function for formatting strings from ata_identify_device +void formatdriveidstring(char *out, const char *in, int n) +{ + char tmp[65]; + + n = n > 64 ? 64 : n; +#ifndef __NetBSD__ + swapbytes(tmp, in, n); +#else + strncpy(tmp, in, n); /* NetBSD delivers host byte order strings */ +#endif + tmp[n] = '\0'; + trim(out, tmp); +} + +void infofound(char *output) { + if (*output) + pout("%s\n", output); + else + pout("[No Information Found]\n"); +} + + +/* For the given Command Register (CR) and Features Register (FR), attempts + * to construct a string that describes the contents of the Status + * Register (ST) and Error Register (ER). The string is dynamically allocated + * memory and the return value is a pointer to this string. It is up to the + * caller to free this memory. If there is insufficient memory or if the + * meanings of the flags of the error register are not known for the given + * command then it returns NULL. + * + * The meanings of the flags of the error register for all commands are + * described in the ATA spec and could all be supported here in theory. + * Currently, only a few commands are supported (those that have been seen + * to produce errors). If many more are to be added then this function + * should probably be redesigned. + */ +char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) { + unsigned char CR=data->commands[4].commandreg; + unsigned char FR=data->commands[4].featuresreg; + unsigned char ST=data->error_struct.status; + unsigned char ER=data->error_struct.error_register; + char *s; + const char *error_flag[8]; + int i, print_lba=0, print_sector=0; + + // Set of character strings corresponding to different error codes. + // Please keep in alphabetic order if you add more. + const char *abrt = "ABRT"; // ABORTED + const char *amnf = "AMNF"; // ADDRESS MARK NOT FOUND + const char *ccto = "CCTO"; // COMMAND COMPLETTION TIMED OUT + const char *eom = "EOM"; // END OF MEDIA + const char *icrc = "ICRC"; // INTERFACE CRC ERROR + const char *idnf = "IDNF"; // ID NOT FOUND + const char *ili = "ILI"; // MEANING OF THIS BIT IS COMMAND-SET SPECIFIC + const char *mc = "MC"; // MEDIA CHANGED + const char *mcr = "MCR"; // MEDIA CHANGE REQUEST + const char *nm = "NM"; // NO MEDIA + const char *obs = "obs"; // OBSOLETE + const char *tk0nf = "TK0NF"; // TRACK 0 NOT FOUND + const char *unc = "UNC"; // UNCORRECTABLE + const char *wp = "WP"; // WRITE PROTECTED + + /* If for any command the Device Fault flag of the status register is + * not used then used_device_fault should be set to 0 (in the CR switch + * below) + */ + int uses_device_fault = 1; + + /* A value of NULL means that the error flag isn't used */ + for (i = 0; i < 8; i++) + error_flag[i] = NULL; + + switch (CR) { + case 0x10: // RECALIBRATE + error_flag[2] = abrt; + error_flag[1] = tk0nf; + break; + case 0x20: /* READ SECTOR(S) */ + case 0x21: // READ SECTOR(S) + case 0x24: // READ SECTOR(S) EXT + case 0xC4: /* READ MULTIPLE */ + case 0x29: // READ MULTIPLE EXT + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x22: // READ LONG (with retries) + case 0x23: // READ LONG (without retries) + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x2a: // READ STREAM DMA + case 0x2b: // READ STREAM PIO + if (CR==0x2a) + error_flag[7] = icrc; + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = ccto; + print_lba=1; + print_sector=(int)data->error_struct.sector_count; + break; + case 0x3A: // WRITE STREAM DMA + case 0x3B: // WRITE STREAM PIO + if (CR==0x3A) + error_flag[7] = icrc; + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = ccto; + print_lba=1; + print_sector=(int)data->error_struct.sector_count; + break; + case 0x25: /* READ DMA EXT */ + case 0x26: // READ DMA QUEUED EXT + case 0xC7: // READ DMA QUEUED + case 0xC8: /* READ DMA */ + case 0xC9: + error_flag[7] = icrc; + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + if (CR==0x25 || CR==0xC8) + print_sector=(int)data->error_struct.sector_count; + break; + case 0x30: /* WRITE SECTOR(S) */ + case 0x31: // WRITE SECTOR(S) + case 0x34: // WRITE SECTOR(S) EXT + case 0xC5: /* WRITE MULTIPLE */ + case 0x39: // WRITE MULTIPLE EXT + case 0xCE: // WRITE MULTIPLE FUA EXT + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + print_lba=1; + break; + case 0x32: // WRITE LONG (with retries) + case 0x33: // WRITE LONG (without retries) + error_flag[4] = idnf; + error_flag[2] = abrt; + print_lba=1; + break; + case 0x3C: // WRITE VERIFY + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = amnf; + print_lba=1; + break; + case 0x40: // READ VERIFY SECTOR(S) with retries + case 0x41: // READ VERIFY SECTOR(S) without retries + case 0x42: // READ VERIFY SECTOR(S) EXT + error_flag[6] = unc; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + break; + case 0xA0: /* PACKET */ + /* Bits 4-7 are all used for sense key (a 'command packet set specific error + * indication' according to the ATA/ATAPI-7 standard), so "Sense key" will + * be repeated in the error description string if more than one of those + * bits is set. + */ + error_flag[7] = "Sense key (bit 3)", + error_flag[6] = "Sense key (bit 2)", + error_flag[5] = "Sense key (bit 1)", + error_flag[4] = "Sense key (bit 0)", + error_flag[2] = abrt; + error_flag[1] = eom; + error_flag[0] = ili; + break; + case 0xA1: /* IDENTIFY PACKET DEVICE */ + case 0xEF: /* SET FEATURES */ + case 0x00: /* NOP */ + case 0xC6: /* SET MULTIPLE MODE */ + error_flag[2] = abrt; + break; + case 0x2F: // READ LOG EXT + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0x3F: // WRITE LOG EXT + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xB0: /* SMART */ + switch(FR) { + case 0xD0: // SMART READ DATA + case 0xD1: // SMART READ ATTRIBUTE THRESHOLDS + case 0xD5: /* SMART READ LOG */ + error_flag[6] = unc; + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xD6: /* SMART WRITE LOG */ + error_flag[4] = idnf; + error_flag[2] = abrt; + error_flag[0] = obs; + break; + case 0xD2: // Enable/Disable Attribute Autosave + case 0xD3: // SMART SAVE ATTRIBUTE VALUES (ATA-3) + case 0xD8: // SMART ENABLE OPERATIONS + case 0xD9: /* SMART DISABLE OPERATIONS */ + case 0xDA: /* SMART RETURN STATUS */ + case 0xDB: // Enable/Disable Auto Offline (SFF) + error_flag[2] = abrt; + break; + case 0xD4: // SMART EXECUTE IMMEDIATE OFFLINE + error_flag[4] = idnf; + error_flag[2] = abrt; + break; + default: + return NULL; + break; + } + break; + case 0xB1: /* DEVICE CONFIGURATION */ + switch (FR) { + case 0xC0: /* DEVICE CONFIGURATION RESTORE */ + error_flag[2] = abrt; + break; + default: + return NULL; + break; + } + break; + case 0xCA: /* WRITE DMA */ + case 0xCB: + case 0x35: // WRITE DMA EXT + case 0x3D: // WRITE DMA FUA EXT + case 0xCC: // WRITE DMA QUEUED + case 0x36: // WRITE DMA QUEUED EXT + case 0x3E: // WRITE DMA QUEUED FUA EXT + error_flag[7] = icrc; + error_flag[6] = wp; + error_flag[5] = mc; + error_flag[4] = idnf; + error_flag[3] = mcr; + error_flag[2] = abrt; + error_flag[1] = nm; + error_flag[0] = amnf; + print_lba=1; + if (CR==0x35) + print_sector=(int)data->error_struct.sector_count; + break; + case 0xE4: // READ BUFFER + case 0xE8: // WRITE BUFFER + error_flag[2] = abrt; + break; + default: + return NULL; + } + + /* 256 bytes -- that'll be plenty (OK, this is lazy!) */ + if (!(s = (char *)malloc(256))) + return s; + + s[0] = '\0'; + + /* We ignore any status flags other than Device Fault and Error */ + + if (uses_device_fault && (ST & (1 << 5))) { + strcat(s, "Device Fault"); + if (ST & 1) // Error flag + strcat(s, "; "); + } + if (ST & 1) { // Error flag + int count = 0; + + strcat(s, "Error: "); + for (i = 7; i >= 0; i--) + if ((ER & (1 << i)) && (error_flag[i])) { + if (count++ > 0) + strcat(s, ", "); + strcat(s, error_flag[i]); + } + } + + // If the error was a READ or WRITE error, print the Logical Block + // Address (LBA) at which the read or write failed. + if (print_lba) { + char tmp[128]; + int lba; + + // bits 24-27: bits 0-3 of DH + lba = 0xf & data->error_struct.drive_head; + lba <<= 8; + // bits 16-23: CH + lba |= data->error_struct.cylinder_high; + lba <<= 8; + // bits 8-15: CL + lba |= data->error_struct.cylinder_low; + lba <<= 8; + // bits 0-7: SN + lba |= data->error_struct.sector_number; + + // print number of sectors, if known, and append to print string + if (print_sector) { + snprintf(tmp, 128, " %d sectors", print_sector); + strcat(s, tmp); + } + + // print LBA, and append to print string + snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba); + strcat(s, tmp); + } + + return s; +} + +// This returns the capacity of a disk drive and also prints this into +// a string, using comma separators to make it easier to read. If the +// drive doesn't support LBA addressing or has no user writable +// sectors (eg, CDROM or DVD) then routine returns zero. +uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){ + + unsigned short command_set_2 = drive->command_set_2; + unsigned short capabilities_0 = drive->words047_079[49-47]; + unsigned short sects_16 = drive->words047_079[60-47]; + unsigned short sects_32 = drive->words047_079[61-47]; + unsigned short lba_16 = drive->words088_255[100-88]; + unsigned short lba_32 = drive->words088_255[101-88]; + unsigned short lba_48 = drive->words088_255[102-88]; + unsigned short lba_64 = drive->words088_255[103-88]; + uint64_t capacity_short=0, capacity=0, threedigits, power_of_ten; + int started=0,k=1000000000; + char separator=','; + + // get correct character to use as thousands separator +#ifdef HAVE_LOCALE_H + struct lconv *currentlocale=NULL; + setlocale (LC_ALL, ""); + currentlocale=localeconv(); + if (*(currentlocale->thousands_sep)) + separator=*(currentlocale->thousands_sep); +#endif // #ifdef HAVE_LOCALE_H + + // if drive supports LBA addressing, determine 32-bit LBA capacity + if (capabilities_0 & 0x0200) { + capacity_short = (unsigned int)sects_32 << 16 | + (unsigned int)sects_16 << 0 ; + + // if drive supports 48-bit addressing, determine THAT capacity + if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400)) + capacity = (uint64_t)lba_64 << 48 | + (uint64_t)lba_48 << 32 | + (uint64_t)lba_32 << 16 | + (uint64_t)lba_16 << 0 ; + + // choose the larger of the two possible capacities + if (capacity_short>capacity) + capacity=capacity_short; + } + + // turn sectors into bytes + capacity_short = (capacity *= 512); + + // print with locale-specific separators (default is comma) + power_of_ten = k; + power_of_ten *= k; + + for (k=0; k<7; k++) { + threedigits = capacity/power_of_ten; + capacity -= threedigits*power_of_ten; + if (started) + // we have already printed some digits + pstring += sprintf(pstring, "%c%03"PRIu64, separator, threedigits); + else if (threedigits || k==6) { + // these are the first digits that we are printing + pstring += sprintf(pstring, "%"PRIu64, threedigits); + started = 1; + } + if (k!=6) + power_of_ten /= 1000; + } + + return capacity_short; +} + +void ataPrintDriveInfo (struct ata_identify_device *drive){ + int version, drivetype; + const char *description; + char unknown[64], timedatetz[DATEANDEPOCHLEN]; + unsigned short minorrev; + char model[64], serial[64], firm[64], capacity[64]; + + // format drive information (with byte swapping as needed) + formatdriveidstring(model, (char *)drive->model,40); + formatdriveidstring(serial, (char *)drive->serial_no,20); + formatdriveidstring(firm, (char *)drive->fw_rev,8); + + // print out model, serial # and firmware versions (byte-swap ASCI strings) + drivetype=lookupdrive(model, firm); + + // Print model family if known + if (drivetype>=0 && knowndrives[drivetype].modelfamily) + pout("Model Family: %s\n", knowndrives[drivetype].modelfamily); + + pout("Device Model: "); + infofound(model); + pout("Serial Number: "); + infofound(serial); + pout("Firmware Version: "); + infofound(firm); + + if (determine_capacity(drive, capacity)) + pout("User Capacity: %s bytes\n", capacity); + + // See if drive is recognized + pout("Device is: %s\n", drivetype<0? + "Not in smartctl database [for details use: -P showall]": + "In smartctl database [for details use: -P show]"); + + // now get ATA version info + version=ataVersionInfo(&description,drive, &minorrev); + + // unrecognized minor revision code + if (!description){ + if (!minorrev) + sprintf(unknown, "Exact ATA specification draft version not indicated"); + else + sprintf(unknown,"Not recognized. Minor revision code: 0x%02hx", minorrev); + description=unknown; + } + + + // SMART Support was first added into the ATA/ATAPI-3 Standard with + // Revision 3 of the document, July 25, 1995. Look at the "Document + // Status" revision commands at the beginning of + // http://www.t13.org/project/d2008r6.pdf to see this. So it's not + // enough to check if we are ATA-3. Version=-3 indicates ATA-3 + // BEFORE Revision 3. + pout("ATA Version is: %d\n",(int)abs(version)); + pout("ATA Standard is: %s\n",description); + + // print current time and date and timezone + dateandtimezone(timedatetz); + pout("Local Time is: %s\n", timedatetz); + + // Print warning message, if there is one + if (drivetype>=0 && knowndrives[drivetype].warningmsg) + pout("\n==> WARNING: %s\n\n", knowndrives[drivetype].warningmsg); + + if (version>=3) + return; + + pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n"); + pout("We will try to proceed in spite of this.\n"); + return; +} + + +const char *OfflineDataCollectionStatus(unsigned char status_byte){ + unsigned char stat=status_byte & 0x7f; + + switch(stat){ + case 0x00: + return "was never started"; + case 0x02: + return "was completed without error"; + case 0x03: + if (status_byte == 0x03) + return "is in progress"; + else + return "is in a Reserved state"; + case 0x04: + return "was suspended by an interrupting command from host"; + case 0x05: + return "was aborted by an interrupting command from host"; + case 0x06: + return "was aborted by the device with a fatal error"; + default: + if (stat >= 0x40) + return "is in a Vendor Specific state\n"; + else + return "is in a Reserved state\n"; + } +} + + + /* prints verbose value Off-line data collection status byte */ + void PrintSmartOfflineStatus(struct ata_smart_values *data){ + + pout("Offline data collection status: (0x%02x)\t", + (int)data->offline_data_collection_status); + + // Off-line data collection status byte is not a reserved + // or vendor specific value + pout("Offline data collection activity\n" + "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status)); + + // Report on Automatic Data Collection Status. Only IBM documents + // this bit. See SFF 8035i Revision 2 for details. + if (data->offline_data_collection_status & 0x80) + pout("\t\t\t\t\tAuto Offline Data Collection: Enabled.\n"); + else + pout("\t\t\t\t\tAuto Offline Data Collection: Disabled.\n"); + + return; +} + +void PrintSmartSelfExecStatus(struct ata_smart_values *data) +{ + pout("Self-test execution status: "); + + switch (data->self_test_exec_status >> 4) + { + case 0: + pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n"); + break; + case 1: + pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the host.\n"); + break; + case 2: + pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("by the host with a hard or soft reset.\n"); + break; + case 3: + pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("occurred while the device was executing\n\t\t\t\t\t"); + pout("its self-test routine and the device \n\t\t\t\t\t"); + pout("was unable to complete the self-test \n\t\t\t\t\t"); + pout("routine.\n"); + break; + case 4: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("a test element that failed and the test\n\t\t\t\t\t"); + pout("element that failed is not known.\n"); + break; + case 5: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the electrical element of the test\n\t\t\t\t\t"); + pout("failed.\n"); + break; + case 6: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the servo (and/or seek) element of the \n\t\t\t\t\t"); + pout("test failed.\n"); + break; + case 7: + pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("the read element of the test failed.\n"); + break; + case 15: + pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t", + (int)data->self_test_exec_status); + pout("%1d0%% of test remaining.\n", + (int)(data->self_test_exec_status & 0x0f)); + break; + default: + pout("(%4d)\tReserved.\n", + (int)data->self_test_exec_status); + break; + } + +} + + + +void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){ + pout("Total time to complete Offline \n"); + pout("data collection: \t\t (%4d) seconds.\n", + (int)data->total_time_to_complete_off_line); +} + + + +void PrintSmartOfflineCollectCap(struct ata_smart_values *data){ + pout("Offline data collection\n"); + pout("capabilities: \t\t\t (0x%02x) ", + (int)data->offline_data_collection_capability); + + if (data->offline_data_collection_capability == 0x00){ + pout("\tOffline data collection not supported.\n"); + } + else { + pout( "%s\n", isSupportExecuteOfflineImmediate(data)? + "SMART execute Offline immediate." : + "No SMART execute Offline immediate."); + + pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? + "Auto Offline data collection on/off support.": + "No Auto Offline data collection support."); + + pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? + "Abort Offline collection upon new\n\t\t\t\t\tcommand.": + "Suspend Offline collection upon new\n\t\t\t\t\tcommand."); + + pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? + "Offline surface scan supported.": + "No Offline surface scan supported."); + + pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? + "Self-test supported.": + "No Self-test supported."); + + pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? + "Conveyance Self-test supported.": + "No Conveyance Self-test supported."); + + pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? + "Selective Self-test supported.": + "No Selective Self-test supported."); + } +} + + + +void PrintSmartCapability ( struct ata_smart_values *data) +{ + pout("SMART capabilities: "); + pout("(0x%04x)\t", (int)data->smart_capability); + + if (data->smart_capability == 0x00) + { + pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n"); + } + else + { + + pout( "%s\n", (data->smart_capability & 0x01)? + "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.": + "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode."); + + if ( data->smart_capability & 0x02 ) + { + pout("\t\t\t\t\tSupports SMART auto save timer.\n"); + } + } +} + +void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity) +{ + + pout("Error logging capability: "); + + if ( isSmartErrorLogCapable(data, identity) ) + { + pout(" (0x%02x)\tError logging supported.\n", + (int)data->errorlog_capability); + } + else { + pout(" (0x%02x)\tError logging NOT supported.\n", + (int)data->errorlog_capability); + } +} + +void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){ + pout("Short self-test routine \n"); + if (isSupportSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->short_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){ + pout("Extended self-test routine\n"); + if (isSupportSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->extend_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){ + pout("Conveyance self-test routine\n"); + if (isSupportConveyanceSelfTest(data)) + pout("recommended polling time: \t (%4d) minutes.\n", + (int)data->conveyance_test_completion_time); + else + pout("recommended polling time: \t Not Supported.\n"); +} + +// onlyfailed=0 : print all attribute values +// onlyfailed=1: just ones that are currently failed and have prefailure bit set +// onlyfailed=2: ones that are failed, or have failed with or without prefailure bit set +void PrintSmartAttribWithThres (struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds, + int onlyfailed){ + int i; + int needheader=1; + char rawstring[64]; + + // step through all vendor attributes + for (i=0; ivendor_attributes+i; + struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i; + + // consider only valid attributes (allowing some screw-ups in the + // thresholds page data to slip by) + if (disk->id){ + char *type, *update; + int failednow,failedever; + char attributename[64]; + + failednow = (disk->current <= thre->threshold); + failedever= (disk->worst <= thre->threshold); + + // These break out of the loop if we are only printing certain entries... + if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow)) + continue; + + if (onlyfailed==2 && !failedever) + continue; + + // print header only if needed + if (needheader){ + if (!onlyfailed){ + pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber); + pout("Vendor Specific SMART Attributes with Thresholds:\n"); + } + pout("ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n"); + needheader=0; + } + + // is this Attribute currently failed, or has it ever failed? + if (failednow) + status="FAILING_NOW"; + else if (failedever) + status="In_the_past"; + else + status=" -"; + + // Print name of attribute + ataPrintSmartAttribName(attributename,disk->id, con->attributedefs); + pout("%-28s",attributename); + + // printing line for each valid attribute + type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age"; + update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline"; + + pout("0x%04x %.3d %.3d %.3d %-10s%-9s%-12s", + (int)disk->flags, (int)disk->current, (int)disk->worst, + (int)thre->threshold, type, update, status); + + // print raw value of attribute + ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs); + pout("%s\n", rawstring); + + // print a warning if there is inconsistency here! + if (disk->id != thre->id){ + char atdat[64],atthr[64]; + ataPrintSmartAttribName(atdat, disk->id, con->attributedefs); + ataPrintSmartAttribName(atthr, thre->id, con->attributedefs); + pout("%-28s<== Data Page | WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat); + pout("%-28s<== Threshold Page | INCONSISTENT IDENTITIES IN THE DATA\n",atthr); + } + } + } + if (!needheader) pout("\n"); +} + +void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){ + pout("General SMART Values:\n"); + + PrintSmartOfflineStatus(data); + + if (isSupportSelfTest(data)){ + PrintSmartSelfExecStatus (data); + } + + PrintSmartTotalTimeCompleteOffline(data); + PrintSmartOfflineCollectCap(data); + PrintSmartCapability(data); + + PrintSmartErrorLogCapability(data, drive); + + pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)? + "General Purpose Logging supported.": + "No General Purpose Logging support."); + + if (isSupportSelfTest(data)){ + PrintSmartShortSelfTestPollingTime (data); + PrintSmartExtendedSelfTestPollingTime (data); + } + if (isSupportConveyanceSelfTest(data)) + PrintSmartConveyanceSelfTestPollingTime (data); + + pout("\n"); +} + +int ataPrintLogDirectory(struct ata_smart_log_directory *data){ + int i; + char *name; + + pout("SMART Log Directory Logging Version %d%s\n", + data->logversion, data->logversion==1?" [multi-sector log support]":""); + for (i=0; i<=255; i++){ + int numsect; + + // Directory log length + numsect = i? data->entry[i-1].numsectors : 1; + + // If the log is not empty, what is it's name + if (numsect){ + switch (i) { + case 0: + name="Log Directory"; break; + case 1: + name="Summary SMART error log"; break; + case 2: + name="Comprehensive SMART error log"; break; + case 3: + name="Extended Comprehensive SMART error log"; break; + case 6: + name="SMART self-test log"; break; + case 7: + name="Extended self-test log"; break; + case 9: + name="Selective self-test log"; break; + case 0x20: + name="Streaming performance log"; break; + case 0x21: + name="Write stream error log"; break; + case 0x22: + name="Read stream error log"; break; + case 0x23: + name="Delayed sector log"; break; + default: + if (0xa0<=i && i<=0xbf) + name="Device vendor specific log"; + else if (0x80<=i && i<=0x9f) + name="Host vendor specific log"; + else + name="Reserved log"; + break; + } + + // print name and length of log + pout("Log at address 0x%02x has %03d sectors [%s]\n", + i, numsect, name); + } + } + return 0; +} + +// returns number of errors +int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){ + int k; + + pout("SMART Error Log Version: %d\n", (int)data->revnumber); + + // if no errors logged, return + if (!data->error_log_pointer){ + pout("No Errors Logged\n\n"); + return 0; + } + PRINT_ON(con); + // If log pointer out of range, return + if (data->error_log_pointer>5){ + pout("Invalid Error Log index = 0x%02x (T13/1321D rev 1c " + "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n", + (int)data->error_log_pointer); + return 0; + } + + // Some internal consistency checking of the data structures + if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) { + pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n", + data->ata_error_count,data->error_log_pointer); + } + + // starting printing error log info + if (data->ata_error_count<=5) + pout( "ATA Error Count: %d\n", (int)data->ata_error_count); + else + pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n", + (int)data->ata_error_count); + PRINT_OFF(con); + pout("\tCR = Command Register [HEX]\n" + "\tFR = Features Register [HEX]\n" + "\tSC = Sector Count Register [HEX]\n" + "\tSN = Sector Number Register [HEX]\n" + "\tCL = Cylinder Low Register [HEX]\n" + "\tCH = Cylinder High Register [HEX]\n" + "\tDH = Device/Head Register [HEX]\n" + "\tDC = Device Command Register [HEX]\n" + "\tER = Error register [HEX]\n" + "\tST = Status register [HEX]\n" + "Powered_Up_Time is measured from power on, and printed as\n" + "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n" + "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n"); + + // now step through the five error log data structures (table 39 of spec) + for (k = 4; k >= 0; k-- ) { + char *st_er_desc; + + // The error log data structure entries are a circular buffer + int j, i=(data->error_log_pointer+k)%5; + struct ata_smart_errorlog_struct *elog=data->errorlog_struct+i; + struct ata_smart_errorlog_error_struct *summary=&(elog->error_struct); + + // Spec says: unused error log structures shall be zero filled + if (nonempty((unsigned char*)elog,sizeof(*elog))){ + // Table 57 of T13/1532D Volume 1 Revision 3 + char *msgstate; + int bits=summary->state & 0x0f; + int days = (int)summary->timestamp/24; + + switch (bits){ + case 0x00: msgstate="in an unknown state";break; + case 0x01: msgstate="sleeping"; break; + case 0x02: msgstate="in standby mode"; break; + case 0x03: msgstate="active or idle"; break; + case 0x04: msgstate="doing SMART Offline or Self-test"; break; + default: + if (bits<0x0b) + msgstate="in a reserved state"; + else + msgstate="in a vendor specific state"; + } + + // See table 42 of ATA5 spec + PRINT_ON(con); + pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n", + (int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days)); + PRINT_OFF(con); + pout(" When the command that caused the error occurred, the device was %s.\n\n",msgstate); + pout(" After command completion occurred, registers were:\n" + " ER ST SC SN CL CH DH\n" + " -- -- -- -- -- -- --\n" + " %02x %02x %02x %02x %02x %02x %02x", + (int)summary->error_register, + (int)summary->status, + (int)summary->sector_count, + (int)summary->sector_number, + (int)summary->cylinder_low, + (int)summary->cylinder_high, + (int)summary->drive_head); + // Add a description of the contents of the status and error registers + // if possible + st_er_desc = construct_st_er_desc(elog); + if (st_er_desc) { + pout(" %s", st_er_desc); + free(st_er_desc); + } + pout("\n\n"); + pout(" Commands leading to the command that caused the error were:\n" + " CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name\n" + " -- -- -- -- -- -- -- -- ---------------- --------------------\n"); + for ( j = 4; j >= 0; j--){ + struct ata_smart_errorlog_command_struct *thiscommand=elog->commands+j; + + // Spec says: unused data command structures shall be zero filled + if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand))) { + char timestring[32]; + + // Convert integer milliseconds to a text-format string + MsecToText(thiscommand->timestamp, timestring); + + pout(" %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n", + (int)thiscommand->commandreg, + (int)thiscommand->featuresreg, + (int)thiscommand->sector_count, + (int)thiscommand->sector_number, + (int)thiscommand->cylinder_low, + (int)thiscommand->cylinder_high, + (int)thiscommand->drive_head, + (int)thiscommand->devicecontrolreg, + timestring, + look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg)); + } + } + pout("\n"); + } + } + PRINT_ON(con); + if (con->printing_switchable) + pout("\n"); + PRINT_OFF(con); + return data->ata_error_count; +} + +void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) { + int i,field1,field2; + char *msg; + char tmp[64]; + uint64_t maxl=0,maxr=0; + uint64_t current=log->currentlba; + uint64_t currentend=current+65535; + + // print data structure revision number + pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion); + if (1 != log->logversion) + pout("Warning: ATA Specification requires selective self-test log data structure revision number = 1\n"); + + switch((sv->self_test_exec_status)>>4){ + case 0:msg="Completed"; + break; + case 1:msg="Aborted_by_host"; + break; + case 2:msg="Interrupted"; + break; + case 3:msg="Fatal_error"; + break; + case 4:msg="Completed_unknown_failure"; + break; + case 5:msg="Completed_electrical_failure"; + break; + case 6:msg="Completed_servo/seek_failure"; + break; + case 7:msg="Completed_read_failure"; + break; + case 8:msg="Completed_handling_damage??"; + break; + case 15:msg="Self_test_in_progress"; + break; + default:msg="Unknown_status "; + break; + } + + // find the number of columns needed for printing. If in use, the + // start/end of span being read-scanned... + if (log->currentspan>5) { + maxl=current; + maxr=currentend; + } + for (i=0; i<5; i++) { + uint64_t start=log->span[i].start; + uint64_t end =log->span[i].end; + // ... plus max start/end of each of the five test spans. + if (start>maxl) + maxl=start; + if (end > maxr) + maxr=end; + } + + // we need at least 7 characters wide fields to accomodate the + // labels + if ((field1=snprintf(tmp,64, "%"PRIu64, maxl))<7) + field1=7; + if ((field2=snprintf(tmp,64, "%"PRIu64, maxr))<7) + field2=7; + + // now print the five test spans + pout(" SPAN %*s %*s CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA"); + + for (i=0; i<5; i++) { + uint64_t start=log->span[i].start; + uint64_t end=log->span[i].end; + + if ((i+1)==(int)log->currentspan) + // this span is currently under test + pout(" %d %*"PRIu64" %*"PRIu64" %s [%01d0%% left] (%"PRIu64"-%"PRIu64")\n", + i+1, field1, start, field2, end, msg, + (int)(sv->self_test_exec_status & 0x7), current, currentend); + else + // this span is not currently under test + pout(" %d %*"PRIu64" %*"PRIu64" Not_testing\n", + i+1, field1, start, field2, end); + } + + // if we are currently read-scanning, print LBAs and the status of + // the read scan + if (log->currentspan>5) + pout("%5d %*"PRIu64" %*"PRIu64" Read_scanning %s\n", + (int)log->currentspan, field1, current, field2, currentend, + OfflineDataCollectionStatus(sv->offline_data_collection_status)); + + /* Print selective self-test flags. Possible flag combinations are + (numbering bits from 0-15): + Bit-1 Bit-3 Bit-4 + Scan Pending Active + 0 * * Don't scan + 1 0 0 Will carry out scan after selective test + 1 1 0 Waiting to carry out scan after powerup + 1 0 1 Currently scanning + 1 1 1 Currently scanning + */ + + pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags); + if (log->flags & SELECTIVE_FLAG_DOSCAN) { + if (log->flags & SELECTIVE_FLAG_ACTIVE) + pout(" Currently read-scanning the remainder of the disk.\n"); + else if (log->flags & SELECTIVE_FLAG_PENDING) + pout(" Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n", + (int)log->pendingtime); + else + pout(" After scanning selected spans, read-scan remainder of disk.\n"); + } + else + pout(" After scanning selected spans, do NOT read-scan remainder of disk.\n"); + + // print pending time + pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n", + (int)log->pendingtime); + + return; +} + +// return value is: +// bottom 8 bits: number of entries found where self-test showed an error +// remaining bits: if nonzero, power on hours of last self-test where error was found +int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){ + int i,j,noheaderprinted=1; + int retval=0, hours=0, testno=0; + + if (allentries) + pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber); + if ((data->revnumber!=0x0001) && allentries && con->fixfirmwarebug != FIX_SAMSUNG) + pout("Warning: ATA Specification requires self-test log structure revision number = 1\n"); + if (data->mostrecenttest==0){ + if (allentries) + pout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n\n"); + return 0; + } + + // print log + for (i=20;i>=0;i--){ + struct ata_smart_selftestlog_struct *log; + + // log is a circular buffer + j=(i+data->mostrecenttest)%21; + log=data->selftest_struct+j; + + if (nonempty((unsigned char*)log,sizeof(*log))){ + char *msgtest,*msgstat,percent[64],firstlba[64]; + int errorfound=0; + + // count entry based on non-empty structures -- needed for + // Seagate only -- other vendors don't have blank entries 'in + // the middle' + testno++; + + // test name + switch(log->selftestnumber){ + case 0: msgtest="Offline "; break; + case 1: msgtest="Short offline "; break; + case 2: msgtest="Extended offline "; break; + case 3: msgtest="Conveyance offline "; break; + case 4: msgtest="Selective offline "; break; + case 127: msgtest="Abort offline test "; break; + case 129: msgtest="Short captive "; break; + case 130: msgtest="Extended captive "; break; + case 131: msgtest="Conveyance captive "; break; + case 132: msgtest="Selective captive "; break; + default: + if ( log->selftestnumber>=192 || + (log->selftestnumber>= 64 && log->selftestnumber<=126)) + msgtest="Vendor offline "; + else + msgtest="Reserved offline "; + } + + // test status + switch((log->selfteststatus)>>4){ + case 0:msgstat="Completed without error "; break; + case 1:msgstat="Aborted by host "; break; + case 2:msgstat="Interrupted (host reset) "; break; + case 3:msgstat="Fatal or unknown error "; errorfound=1; break; + case 4:msgstat="Completed: unknown failure "; errorfound=1; break; + case 5:msgstat="Completed: electrical failure"; errorfound=1; break; + case 6:msgstat="Completed: servo/seek failure"; errorfound=1; break; + case 7:msgstat="Completed: read failure "; errorfound=1; break; + case 8:msgstat="Completed: handling damage?? "; errorfound=1; break; + case 15:msgstat="Self-test routine in progress"; break; + default:msgstat="Unknown/reserved test status "; + } + + retval+=errorfound; + sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf); + + // T13/1321D revision 1c: (Data structure Rev #1) + + //The failing LBA shall be the LBA of the uncorrectable sector + //that caused the test to fail. If the device encountered more + //than one uncorrectable sector during the test, this field + //shall indicate the LBA of the first uncorrectable sector + //encountered. If the test passed or the test failed for some + //reason other than an uncorrectable sector, the value of this + //field is undefined. + + // This is true in ALL ATA-5 specs + + if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000) + sprintf(firstlba,"%s","-"); + else + sprintf(firstlba,"%u",log->lbafirstfailure); + + // print out a header if needed + if (noheaderprinted && (allentries || errorfound)){ + pout("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); + noheaderprinted=0; + } + + // print out an entry, either if we are printing all entries OR + // if an error was found + if (allentries || errorfound) + pout("#%2d %s %s %s %8d %s\n", testno, msgtest, msgstat, percent, (int)log->timestamp, firstlba); + + // keep track of time of most recent error + if (errorfound && !hours) + hours=log->timestamp; + } + } + if (!allentries && retval) + pout("\n"); + + hours = hours << 8; + return (retval | hours); +} + +void ataPseudoCheckSmart ( struct ata_smart_values *data, + struct ata_smart_thresholds_pvt *thresholds) { + int i; + int failed = 0; + for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) { + if (data->vendor_attributes[i].id && + thresholds->thres_entries[i].id && + ATTRIBUTE_FLAGS_PREFAILURE(data->vendor_attributes[i].flags) && + (data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) && + (thresholds->thres_entries[i].threshold != 0xFE)){ + pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id); + failed = 1; + } + } + pout("%s\n", ( failed )? + "SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA": + "SMART overall-health self-assessment test result: PASSED"); +} + + +// Compares failure type to policy in effect, and either exits or +// simply returns to the calling routine. +void failuretest(int type, int returnvalue){ + + // If this is an error in an "optional" SMART command + if (type==OPTIONAL_CMD){ + if (con->conservative){ + pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n"); + EXIT(returnvalue); + } + return; + } + + // If this is an error in a "mandatory" SMART command + if (type==MANDATORY_CMD){ + if (con->permissive--) + return; + pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n"); + EXIT(returnvalue); + } + + pout("Smartctl internal error in failuretest(type=%d). Please contact developers at " PACKAGE_HOMEPAGE "\n",type); + EXIT(returnvalue|FAILCMD); +} + +// Used to warn users about invalid checksums. Action to be taken may be +// altered by the user. +void checksumwarning(const char *string){ + // user has asked us to ignore checksum errors + if (con->checksumignore) + return; + + pout("Warning! %s error: invalid SMART checksum.\n",string); + + // user has asked us to fail on checksum errors + if (con->checksumfail) + EXIT(FAILSMART); + + return; +} + +// Initialize to zero just in case some SMART routines don't work +struct ata_identify_device drive; +struct ata_smart_values smartval; +struct ata_smart_thresholds_pvt smartthres; +struct ata_smart_errorlog smarterror; +struct ata_smart_selftestlog smartselftest; + +int ataPrintMain (int fd){ + int timewait,code; + int returnval=0, retid=0, supported=0, needupdate=0; + + // Start by getting Drive ID information. We need this, to know if SMART is supported. + if ((retid=ataReadHDIdentity(fd,&drive))<0){ + pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILID); + } + + // If requested, show which presets would be used for this drive and exit. + if (con->showpresets) { + showpresets(&drive); + EXIT(0); + } + + // Use preset vendor attribute options unless user has requested otherwise. + if (!con->ignorepresets){ + unsigned char *charptr; + if ((charptr=con->attributedefs)) + applypresets(&drive, &charptr, con); + else { + pout("Fatal internal error in ataPrintMain()\n"); + EXIT(returnval|=FAILCMD); + } + } + + // Print most drive identity information if requested + if (con->driveinfo){ + pout("=== START OF INFORMATION SECTION ===\n"); + ataPrintDriveInfo(&drive); + } + + // Was this a packet device? + if (retid>0){ + pout("SMART support is: Unavailable - Packet Interface Devices [this device: %s] don't support ATA SMART\n", packetdevicetype(retid-1)); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + + // if drive does not supports SMART it's time to exit + supported=ataSmartSupport(&drive); + if (supported != 1){ + if (supported==0) { + pout("SMART support is: Unavailable - device lacks SMART capability.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + pout(" Checking to be sure by trying SMART ENABLE command.\n"); + } + else { + pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + pout(" Checking for SMART support by trying SMART ENABLE command.\n"); + } + + if (ataEnableSmart(fd)){ + pout(" SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + supported=0; + } + else { + pout(" SMART ENABLE appeared to work! Continuing.\n"); + supported=1; + } + if (!con->driveinfo) pout("\n"); + } + + // Now print remaining drive info: is SMART enabled? + if (con->driveinfo){ + int ison=ataIsSmartEnabled(&drive),isenabled=ison; + + if (ison==-1) { + pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + // check SMART support by trying a command + pout(" Checking to be sure by trying SMART RETURN STATUS command.\n"); + isenabled=ataDoesSmartWork(fd); + } + else { + pout("SMART support is: Available - device has SMART capability.\n"); +#ifdef HAVE_ATA_IDENTIFY_IS_CACHED + if (ata_identify_is_cached(fd)) { + pout(" %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n", + (isenabled?"En":"Dis")); + isenabled=ataDoesSmartWork(fd); + } +#endif + } + + if (isenabled) + pout("SMART support is: Enabled\n"); + else { + if (ison==-1) + pout("SMART support is: Unavailable\n"); + else + pout("SMART support is: Disabled\n"); + } + pout("\n"); + } + + // START OF THE ENABLE/DISABLE SECTION OF THE CODE + if (con->smartenable || con->smartdisable || + con->smartautosaveenable || con->smartautosavedisable || + con->smartautoofflineenable || con->smartautoofflinedisable) + pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); + + // Enable/Disable SMART commands + if (con->smartenable){ + if (ataEnableSmart(fd)) { + pout("Smartctl: SMART Enable Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Enabled.\n"); + } + + // From here on, every command requires that SMART be enabled... + if (!ataDoesSmartWork(fd)) { + pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); + return returnval; + } + + // Turn off SMART on device + if (con->smartdisable){ + if (ataDisableSmart(fd)) { + pout( "Smartctl: SMART Disable Failed.\n\n"); + failuretest(MANDATORY_CMD,returnval|=FAILSMART); + } + pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); + return returnval; + } + + // Let's ALWAYS issue this command to get the SMART status + code=ataSmartStatus2(fd); + if (code==-1) + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + + // Enable/Disable Auto-save attributes + if (con->smartautosaveenable){ + if (ataEnableAutoSave(fd)){ + pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Attribute Autosave Enabled.\n"); + } + if (con->smartautosavedisable){ + if (ataDisableAutoSave(fd)){ + pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + else + pout("SMART Attribute Autosave Disabled.\n"); + } + + // for everything else read values and thresholds are needed + if (ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if (ataReadSmartThresholds(fd, &smartthres)){ + pout("Smartctl: SMART Read Thresholds failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + + // Enable/Disable Off-line testing + if (con->smartautoofflineenable){ + if (!isSupportAutomaticTimer(&smartval)){ + pout("Warning: device does not support SMART Automatic Timers.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + needupdate=1; + if (ataEnableAutoOffline(fd)){ + pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + pout("SMART Automatic Offline Testing Enabled every four hours.\n"); + } + if (con->smartautoofflinedisable){ + if (!isSupportAutomaticTimer(&smartval)){ + pout("Warning: device does not support SMART Automatic Timers.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + needupdate=1; + if (ataDisableAutoOffline(fd)){ + pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + pout("SMART Automatic Offline Testing Disabled.\n"); + } + + if (needupdate && ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + + // all this for a newline! + if (con->smartenable || con->smartdisable || + con->smartautosaveenable || con->smartautosavedisable || + con->smartautoofflineenable || con->smartautoofflinedisable) + pout("\n"); + + // START OF READ-ONLY OPTIONS APART FROM -V and -i + if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog) + pout("=== START OF READ SMART DATA SECTION ===\n"); + + // Check SMART status (use previously returned value) + if (con->checksmart){ + switch (code) { + + case 0: + // The case where the disk health is OK + pout("SMART overall-health self-assessment test result: PASSED\n"); + if (ataCheckSmart(&smartval, &smartthres,0)){ + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Please note the following marginal Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,2); + } + returnval|=FAILAGE; + } + else + pout("\n"); + break; + + case 1: + // The case where the disk health is NOT OK + PRINT_ON(con); + pout("SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); + PRINT_OFF(con); + if (ataCheckSmart(&smartval, &smartthres,1)){ + returnval|=FAILATTR; + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for failed Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Failed Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,1); + } + } + else + pout("No failed Attributes found.\n\n"); + returnval|=FAILSTATUS; + PRINT_OFF(con); + break; + + case -1: + default: + // The case where something went wrong with HDIO_DRIVE_TASK ioctl() + if (ataCheckSmart(&smartval, &smartthres,1)){ + PRINT_ON(con); + pout("SMART overall-health self-assessment test result: FAILED!\n" + "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); + PRINT_OFF(con); + returnval|=FAILATTR; + returnval|=FAILSTATUS; + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for failed Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Failed Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,1); + } + } + else { + pout("SMART overall-health self-assessment test result: PASSED\n"); + if (ataCheckSmart(&smartval, &smartthres,0)){ + if (con->smartvendorattrib) + pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); + else { + PRINT_ON(con); + pout("Please note the following marginal Attributes:\n"); + PrintSmartAttribWithThres(&smartval, &smartthres,2); + } + returnval|=FAILAGE; + } + else + pout("\n"); + } + PRINT_OFF(con); + break; + } // end of switch statement + + PRINT_OFF(con); + } // end of checking SMART Status + + // Print general SMART values + if (con->generalsmartvalues) + ataPrintGeneralSmartValues(&smartval, &drive); + + // Print vendor-specific attributes + if (con->smartvendorattrib){ + PRINT_ON(con); + PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0); + PRINT_OFF(con); + } + + // Print SMART log Directory + if (con->smartlogdirectory){ + struct ata_smart_log_directory smartlogdirectory; + if (!isGeneralPurposeLoggingCapable(&drive)){ + pout("Warning: device does not support General Purpose Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + pout("Log Directory Supported\n"); + if (ataReadLogDirectory(fd, &smartlogdirectory)){ + PRINT_OFF(con); + pout("Read Log Directory failed.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + ataPrintLogDirectory( &smartlogdirectory); + } + PRINT_OFF(con); + } + + // Print SMART error log + if (con->smarterrorlog){ + if (!isSmartErrorLogCapable(&smartval, &drive)){ + pout("Warning: device does not support Error Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if (ataReadErrorLog(fd, &smarterror)){ + pout("Smartctl: SMART Error Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + // quiet mode is turned on inside ataPrintSmartErrorLog() + if (ataPrintSmartErrorlog(&smarterror)) + returnval|=FAILERR; + PRINT_OFF(con); + } + } + + // Print SMART self-test log + if (con->smartselftestlog){ + if (!isSmartTestLogCapable(&smartval, &drive)){ + pout("Warning: device does not support Self Test Logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if(ataReadSelfTestLog(fd, &smartselftest)){ + pout("Smartctl: SMART Self Test Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable)) + returnval|=FAILLOG; + PRINT_OFF(con); + pout("\n"); + } + } + + // Print SMART selective self-test log + if (con->selectivetestlog){ + struct ata_selective_self_test_log log; + + if (!isSupportSelectiveSelfTest(&smartval)) + pout("Device does not support Selective Self Tests/Logging\n"); + else if(ataReadSelectiveSelfTestLog(fd, &log)) { + pout("Smartctl: SMART Selective Self Test Log Read Failed\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else { + PRINT_ON(con); + ataPrintSelectiveSelfTestLog(&log, &smartval); + PRINT_OFF(con); + pout("\n"); + } + } + + // START OF THE TESTING SECTION OF THE CODE. IF NO TESTING, RETURN + if (con->testcase==-1) + return returnval; + + pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n"); + // if doing a self-test, be sure it's supported by the hardware + switch (con->testcase){ + case OFFLINE_FULL_SCAN: + if (!isSupportExecuteOfflineImmediate(&smartval)){ + pout("Warning: device does not support Execute Offline Immediate function.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case ABORT_SELF_TEST: + case SHORT_SELF_TEST: + case EXTEND_SELF_TEST: + case SHORT_CAPTIVE_SELF_TEST: + case EXTEND_CAPTIVE_SELF_TEST: + if (!isSupportSelfTest(&smartval)){ + pout("Warning: device does not support Self-Test functions.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case CONVEYANCE_SELF_TEST: + case CONVEYANCE_CAPTIVE_SELF_TEST: + if (!isSupportConveyanceSelfTest(&smartval)){ + pout("Warning: device does not support Conveyance Self-Test functions.\n\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + break; + case SELECTIVE_SELF_TEST: + case SELECTIVE_CAPTIVE_SELF_TEST: + if (!isSupportSelectiveSelfTest(&smartval)){ + pout("Warning: device does not support Selective Self-Test functions.\n\n"); + failuretest(MANDATORY_CMD, returnval|=FAILSMART); + } + break; + default: + pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase); + pout("Please contact smartmontools developers at %s.\n", PACKAGE_BUGREPORT); + EXIT(returnval|=FAILCMD); + } + + // Now do the test. Note ataSmartTest prints its own error/success + // messages + if (ataSmartTest(fd, con->testcase, &smartval)) + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + else { + // Tell user how long test will take to complete. This is tricky + // because in the case of an Offline Full Scan, the completion + // timer is volatile, and needs to be read AFTER the command is + // given. If this will interrupt the Offline Full Scan, we don't + // do it, just warn user. + if (con->testcase==OFFLINE_FULL_SCAN){ + if (isSupportOfflineAbort(&smartval)) + pout("Note: giving further SMART commands will abort Offline testing\n"); + else if (ataReadSmartValues(fd, &smartval)){ + pout("Smartctl: SMART Read Values failed.\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + } + + // Now say how long the test will take to complete + if ((timewait=TestTime(&smartval,con->testcase))){ + time_t t=time(NULL); + if (con->testcase==OFFLINE_FULL_SCAN) { + t+=timewait; + pout("Please wait %d seconds for test to complete.\n", (int)timewait); + } else { + t+=timewait*60; + pout("Please wait %d minutes for test to complete.\n", (int)timewait); + } + pout("Test will complete after %s\n", ctime(&t)); + + if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && + con->testcase!=EXTEND_CAPTIVE_SELF_TEST && + con->testcase!=CONVEYANCE_CAPTIVE_SELF_TEST && + con->testcase!=SELECTIVE_CAPTIVE_SELF_TEST) + pout("Use smartctl -X to abort test.\n"); + } + } + + return returnval; +} diff --git a/ataprint.h b/ataprint.h new file mode 100644 index 0000000..d2e1e46 --- /dev/null +++ b/ataprint.h @@ -0,0 +1,61 @@ +/* + * ataprint.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef ATAPRINT_H_ +#define ATAPRINT_H_ + +#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.28 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include +#include + +/* Prints ATA Drive Information and S.M.A.R.T. Capability */ +void ataPrintDriveInfo(struct ata_identify_device *); + +void ataPrintGeneralSmartValues(struct ata_smart_values *, struct ata_identify_device *); + +void ataPrintSmartThresholds(struct ata_smart_thresholds_pvt *); + +// returns number of errors in Errorlog +int ataPrintSmartErrorlog(struct ata_smart_errorlog *); + +int ataPrintLogDirectory(struct ata_smart_log_directory *); + +void PrintSmartAttributes(struct ata_smart_values *); + +void PrintSmartAttribWithThres(struct ata_smart_values *, + struct ata_smart_thresholds_pvt *, + int onlyfailed); + +// returns number of entries that had logged errors +int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *, int allentries); + +void ataPseudoCheckSmart(struct ata_smart_values *, struct ata_smart_thresholds_pvt *); + +// Convenience function for formatting strings from ata_identify_device. +void formatdriveidstring(char *out, const char *in, int n); + +int ataPrintMain(int fd); + +#endif diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..3fc1c45 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# $Id: autogen.sh,v 1.13 2005/09/19 09:28:11 chrfranke Exp $ +# +# Generate ./configure from config.in and Makefile.in from Makefile.am. +# This also adds files like missing,depcomp,install-sh to the source +# direcory. To update these files at a later date use: +# autoreconf -f -i -v + + +# Cygwin? +test -x /usr/bin/uname && /usr/bin/uname | grep -i CYGWIN >/dev/null && +{ + # Enable strict case checking + # (to avoid e.g. "DIST_COMMON = ... ChangeLog ..." in Makefile.in) + export CYGWIN="${CYGWIN}${CYGWIN:+ }check_case:strict" + + # Check for Unix text file type + echo > dostest.tmp + test "`wc -c < dostest.tmp`" -eq 1 || + echo "Warning: DOS text file type set, 'make dist' and related targets will not work." + rm -f dostest.tmp +} + +typep() +{ + cmd=$1 ; TMP=$IFS ; IFS=: ; set $PATH + for dir + do + if [ -x "$dir/$cmd" ]; then + echo "$dir/$cmd" + IFS=$TMP + return 0 + fi + done + IFS=$TMP + return 1 +} + +test -x "$AUTOMAKE" || AUTOMAKE=`typep automake-1.9` || AUTOMAKE=`typep automake-1.8` || AUTOMAKE=`typep automake-1.7` || AUTOMAKE=`typep automake17` || +{ +echo +echo "You must have at least GNU Automake 1.7 (up to 1.9.x) installed" +echo "in order to bootstrap smartmontools from CVS. Download the" +echo "appropriate package for your distribution, or the source tarball" +echo "from ftp://ftp.gnu.org/gnu/automake/ ." +echo +echo "Also note that support for new Automake series (anything newer" +echo "than 1.9.x) is only added after extensive tests. If you live in" +echo "the bleeding edge, you should know what you're doing, mainly how" +echo "to test it before the developers. Be patient." +exit 1; +} + +test -x "$ACLOCAL" || ACLOCAL="aclocal`echo "$AUTOMAKE" | sed 's/.*automake//'`" && ACLOCAL=`typep "$ACLOCAL"` || +{ +echo +echo "autogen.sh found automake-1.7, automake-1.8, or automake-1.9 in" +echo "your PATH, but not the respective aclocal-1.7, aclocal-1.8, or" +echo "aclocal-1.9. Your installation of GNU Automake is broken or" +echo "incomplete." +exit 2; +} + +# Warn if Automake version is unknown +ver= +case "$AUTOMAKE" in + *automake-1.[78]|*automake17) + ;; + *) + ver="`$AUTOMAKE --version | head -1 | sed -n 's,^.*\([12]\.[.0-9]*[-pl0-9]*\).*$,\1,p'`" + ver="${ver:-?.?.?}" + case "$ver" in + 1.[78]*|1.9.[1-6]) ver= ;; + esac ;; +esac + +test -z "$ver" || +{ +echo "Note: GNU Automake version ${ver} was not tested by the developers." +echo "Please report success/failure to the smartmontools-support mailing list." +} + +set -e # stops on error status + +${ACLOCAL} +autoheader +${AUTOMAKE} --add-missing --copy --foreign +autoconf diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..c28419d --- /dev/null +++ b/config.guess @@ -0,0 +1,1450 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +timestamp='2004-10-25' + +# This file 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 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + luna88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit 0 ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit 0 ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + *86) UNAME_PROCESSOR=i686 ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms && exit 0 ;; + I*) echo ia64-dec-vms && exit 0 ;; + V*) echo vax-dec-vms && exit 0 ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..348c066 --- /dev/null +++ b/config.h.in @@ -0,0 +1,148 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* smartmontools CVS Tag */ +#undef CONFIG_H_CVSID + +/* use mailx as default mailer */ +#undef DEFAULT_MAILER + +/* Define to 1 if you have the `ata_identify_is_cached' function in os_*.c. */ +#undef HAVE_ATA_IDENTIFY_IS_CACHED + +/* Define to 1 if C compiler supports __attribute__((packed)) */ +#undef HAVE_ATTR_PACKED + +/* Define to 1 if you have the header file. */ +#undef HAVE_DEV_ATA_ATAVAR_H + +/* Define to 1 if you have the `getdomainname' function. */ +#undef HAVE_GETDOMAINNAME + +/* Define to 1 if you have the `gethostbyname' function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the `get_os_version_str' function in os_*.c. */ +#undef HAVE_GET_OS_VERSION_STR + +/* Define to 1 if the system has the type `int64_t'. */ +#undef HAVE_INT64_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the `sigset' function. */ +#undef HAVE_SIGSET + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strtoull' function. */ +#undef HAVE_STRTOULL + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_INT_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TWEIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TWEREG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TW_OSL_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if the system has the type `uint64_t'. */ +#undef HAVE_UINT64_T + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the `snprintf' function is sane */ +#undef HAVE_WORKING_SNPRINTF + +/* need assembly code os_solaris_ata.s */ +#undef NEED_SOLARIS_ATA_CODE + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* smartmontools Home Page */ +#undef PACKAGE_HOMEPAGE + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* smartmontools Build Host */ +#undef SMARTMONTOOLS_BUILD_HOST + +/* smartmontools Configure Arguments */ +#undef SMARTMONTOOLS_CONFIGURE_ARGS + +/* smartmontools Configure Date */ +#undef SMARTMONTOOLS_CONFIGURE_DATE + +/* smartmontools Release Date */ +#undef SMARTMONTOOLS_RELEASE_DATE + +/* smartmontools Release Time */ +#undef SMARTMONTOOLS_RELEASE_TIME + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..edb6b66 --- /dev/null +++ b/config.sub @@ -0,0 +1,1555 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +timestamp='2004-08-29' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..c5f73d5 --- /dev/null +++ b/configure @@ -0,0 +1,8268 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59 for smartmontools 5.36. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='smartmontools' +PACKAGE_TARNAME='smartmontools' +PACKAGE_VERSION='5.36' +PACKAGE_STRING='smartmontools 5.36' +PACKAGE_BUGREPORT='smartmontools-support@lists.sourceforge.net' + +ac_unique_file="smartctl.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CCAS CCASFLAGS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CPP EGREP libc_have_working_snprintf gcc_have_attr_packed ASFLAGS exampledir initddir docdir smartd_suffix SMARTD_SUFFIX_TRUE SMARTD_SUFFIX_FALSE releaseversion smartmontools_release_date smartmontools_release_time os_deps os_libs OS_DARWIN_TRUE OS_DARWIN_FALSE OS_SOLARIS_TRUE OS_SOLARIS_FALSE OS_WIN32_MINGW_TRUE OS_WIN32_MINGW_FALSE LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CCAS_set=${CCAS+set} +ac_env_CCAS_value=$CCAS +ac_cv_env_CCAS_set=${CCAS+set} +ac_cv_env_CCAS_value=$CCAS +ac_env_CCASFLAGS_set=${CCASFLAGS+set} +ac_env_CCASFLAGS_value=$CCASFLAGS +ac_cv_env_CCASFLAGS_set=${CCASFLAGS+set} +ac_cv_env_CCASFLAGS_value=$CCASFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures smartmontools 5.36 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of smartmontools 5.36:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-sample Enables appending .sample to the installed smartd rc + script and configuration file + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-initscriptdir=dir + Location of init scripts (default is + ${sysconfdir}/rc.d/init.d) + --with-docdir=dir Location of documentation (default is + ${prefix}/share/doc/smartmontools-5.X) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CCAS assembler compiler command (defaults to CC) + CCASFLAGS assembler compiler flags (defaults to CFLAGS) + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +smartmontools configure 5.36 +generated by GNU Autoconf 2.59 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by smartmontools $as_me 5.36, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"` +smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.113 2005/11/27 20:01:29 chrfranke Exp $'` +smartmontools_release_date=2006/04/12 +smartmontools_release_time="17:39:01 UTC" + + +cat >>confdefs.h <<_ACEOF +#define SMARTMONTOOLS_CONFIGURE_ARGS "$ac_configure_args" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SMARTMONTOOLS_CONFIGURE_DATE "$smartmontools_configure_date" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SMARTMONTOOLS_RELEASE_DATE "$smartmontools_release_date" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SMARTMONTOOLS_RELEASE_TIME "$smartmontools_release_time" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define CONFIG_H_CVSID "$smartmontools_cvs_tag" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_HOMEPAGE "http://smartmontools.sourceforge.net/" +_ACEOF + + + ac_config_headers="$ac_config_headers config.h" + + +am__api_version="1.9" +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='smartmontools' + VERSION='5.36' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + + + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then + enableval="$enable_maintainer_mode" + USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6 + + +if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + + ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +# By default we simply use the C compiler to build assembly code. + +test "${CCAS+set}" = set || CCAS=$CC +test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS + + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +case "${host}" in + *-*-mingw*) + CPPFLAGS="$CPPFLAGS -mno-cygwin" + LDFLAGS="$LDFLAGS -mno-cygwin" + CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32" +esac + +# AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES]) +echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5 +echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_search_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_gethostbyname=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_gethostbyname" = no; then + for ac_lib in nsl; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6 +if test "$ac_cv_search_gethostbyname" != no; then + test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS" + +else + echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5 +echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_search_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_gethostbyname=no + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_gethostbyname" = no; then + for ac_lib in nsl; do + LIBS="-l$ac_lib -lsocket $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_gethostbyname="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6 +if test "$ac_cv_search_gethostbyname" != no; then + test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS" + +fi + +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in locale.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in getopt.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dev/ata/atavar.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in netdb.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in inttypes.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in stdint.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in sys/inttypes.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in sys/int_types.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_header in sys/tweio.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in sys/twereg.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in sys/tw_osl_ioctl.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------------------------- ## +## Report this to smartmontools-support@lists.sourceforge.net ## +## ---------------------------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for int64_t" >&5 +echo $ECHO_N "checking for int64_t... $ECHO_C" >&6 +if test "${ac_cv_type_int64_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((int64_t *) 0) + return 0; +if (sizeof (int64_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_int64_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_int64_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5 +echo "${ECHO_T}$ac_cv_type_int64_t" >&6 +if test $ac_cv_type_int64_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_INT64_T 1 +_ACEOF + + +fi +echo "$as_me:$LINENO: checking for uint64_t" >&5 +echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6 +if test "${ac_cv_type_uint64_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((uint64_t *) 0) + return 0; +if (sizeof (uint64_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_uint64_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_uint64_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5 +echo "${ECHO_T}$ac_cv_type_uint64_t" >&6 +if test $ac_cv_type_uint64_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_UINT64_T 1 +_ACEOF + + +fi + + + +for ac_func in getopt +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in getopt_long +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in getdomainname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in gethostname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in gethostbyname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in sigset +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in strtoull +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in uname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Check whether snprintf appends null char and returns expected length on overflow + + +echo "$as_me:$LINENO: checking for working snprintf" >&5 +echo $ECHO_N "checking for working snprintf... $ECHO_C" >&6 +if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + char buf[]="ABCDEFGHI"; + int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + libc_have_working_snprintf=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +libc_have_working_snprintf=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +if test "$libc_have_working_snprintf" = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_SNPRINTF 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $libc_have_working_snprintf" >&5 +echo "${ECHO_T}$libc_have_working_snprintf" >&6 + +# check for __attribute__((packed)) + + +echo "$as_me:$LINENO: checking whether C compiler supports __attribute__((packed))" >&5 +echo $ECHO_N "checking whether C compiler supports __attribute__((packed))... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +struct a { int b; } __attribute__((packed)); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gcc_have_attr_packed=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gcc_have_attr_packed=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test "$gcc_have_attr_packed" = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_ATTR_PACKED 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $gcc_have_attr_packed" >&5 +echo "${ECHO_T}$gcc_have_attr_packed" >&6 + + + + + +exampledir='${docdir}/examplescripts' + + + +# Check whether --with-initscriptdir or --without-initscriptdir was given. +if test "${with_initscriptdir+set}" = set; then + withval="$with_initscriptdir" + initddir="$withval" +else + initddir='${sysconfdir}/rc.d/init.d' +fi; + + + +# Check whether --with-docdir or --without-docdir was given. +if test "${with_docdir+set}" = set; then + withval="$with_docdir" + docdir="$withval" +else + docdir='${prefix}/share/doc/${PACKAGE}-${VERSION}' +fi; + + +# Check whether --enable-sample or --disable-sample was given. +if test "${enable_sample+set}" = set; then + enableval="$enable_sample" + smartd_suffix='.sample' +else + smartd_suffix='' +fi; + + + +if test $smartd_suffix; then + SMARTD_SUFFIX_TRUE= + SMARTD_SUFFIX_FALSE='#' +else + SMARTD_SUFFIX_TRUE='#' + SMARTD_SUFFIX_FALSE= +fi + + +if test "$prefix" = "NONE"; then + if test "$mandir" = '${prefix}/man'; then + mandir='${prefix}/share/man' + + fi +fi + +releaseversion='${PACKAGE}-${VERSION}' + + + + +case "${host}" in + *-*-linux-gnu*) + os_deps='os_linux.o' + + os_libs='' + ;; + *-*-linux*) + os_deps='os_linux.o' + + os_libs='' + ;; + *-*-freebsd*) + os_deps='os_freebsd.o' + + os_libs='-lcam' +;; + sparc-*-solaris*) + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_MAILER "mailx" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NEED_SOLARIS_ATA_CODE "os_solaris_ata.s" +_ACEOF + + os_deps='os_solaris.o os_solaris_ata.o' + + os_libs='' + ;; + *-pc-solaris*) + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_MAILER "mailx" +_ACEOF + + os_deps='os_solaris.o' + + os_libs='' + ;; + *-*-netbsd*) + os_deps='os_netbsd.o' + + os_libs='-lutil' + ;; + *-*-openbsd*) + os_deps='os_openbsd.o' + + os_libs='-lutil' + ;; + *-*-cygwin*) + os_deps='os_win32.o' + + os_libs='' + ;; + *-*-mingw*) + os_deps='os_win32.o' + + os_libs='' + ;; + *-*-darwin*) + os_deps='os_darwin.o' + + os_libs='-framework CoreFoundation -framework IOKit' + ;; + *) + os_deps='os_generic.o' + + os_libs='' + ;; +esac + +# Define symbols for optional functions in OS specific module +case "${os_deps}" in + os_win32*) + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ATA_IDENTIFY_IS_CACHED 1 +_ACEOF + ;; +esac +case "${os_deps}" in + os_win32*) + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GET_OS_VERSION_STR 1 +_ACEOF + ;; +esac + + + +if echo $host_os | grep '^darwin' > /dev/null; then + OS_DARWIN_TRUE= + OS_DARWIN_FALSE='#' +else + OS_DARWIN_TRUE='#' + OS_DARWIN_FALSE= +fi + + + +if echo $host_os | grep '^solaris' > /dev/null; then + OS_SOLARIS_TRUE= + OS_SOLARIS_FALSE='#' +else + OS_SOLARIS_TRUE='#' + OS_SOLARIS_FALSE= +fi + + + +if echo $host_os | grep '^mingw' > /dev/null; then + OS_WIN32_MINGW_TRUE= + OS_WIN32_MINGW_FALSE='#' +else + OS_WIN32_MINGW_TRUE='#' + OS_WIN32_MINGW_FALSE= +fi + + +if test "x$GCC" = "xyes"; then + if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then + CFLAGS="$CFLAGS -Wall" + fi +# In the next line, do NOT delete the 2 spaces inside double quotes. + if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then + CFLAGS="$CFLAGS -W" + fi + case "${host}" in + *-*-mingw*) + # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t + CFLAGS="$CFLAGS -Wno-format";; + esac +else + case "${host}" in + *-*-solaris*) + if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then + CFLAGS="-xmemalign=1i $CFLAGS" + fi + if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then + CFLAGS="-xCC $CFLAGS" + fi + if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then + CFLAGS="-xO2 $CFLAGS" + fi + esac +fi + + +cat >>confdefs.h <<_ACEOF +#define SMARTMONTOOLS_BUILD_HOST "${host}" +_ACEOF + + + + + ac_config_files="$ac_config_files Makefile examplescripts/Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${SMARTD_SUFFIX_TRUE}" && test -z "${SMARTD_SUFFIX_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"SMARTD_SUFFIX\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"SMARTD_SUFFIX\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${OS_DARWIN_TRUE}" && test -z "${OS_DARWIN_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"OS_DARWIN\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"OS_DARWIN\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${OS_SOLARIS_TRUE}" && test -z "${OS_SOLARIS_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"OS_SOLARIS\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"OS_SOLARIS\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${OS_WIN32_MINGW_TRUE}" && test -z "${OS_WIN32_MINGW_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"OS_WIN32_MINGW\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"OS_WIN32_MINGW\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by smartmontools $as_me 5.36, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +smartmontools config.status 5.36 +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "examplescripts/Makefile" ) CONFIG_FILES="$CONFIG_FILES examplescripts/Makefile" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@mkdir_p@,$mkdir_p,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@AMTAR@,$AMTAR,;t t +s,@am__tar@,$am__tar,;t t +s,@am__untar@,$am__untar,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@CCAS@,$CCAS,;t t +s,@CCASFLAGS@,$CCASFLAGS,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@libc_have_working_snprintf@,$libc_have_working_snprintf,;t t +s,@gcc_have_attr_packed@,$gcc_have_attr_packed,;t t +s,@ASFLAGS@,$ASFLAGS,;t t +s,@exampledir@,$exampledir,;t t +s,@initddir@,$initddir,;t t +s,@docdir@,$docdir,;t t +s,@smartd_suffix@,$smartd_suffix,;t t +s,@SMARTD_SUFFIX_TRUE@,$SMARTD_SUFFIX_TRUE,;t t +s,@SMARTD_SUFFIX_FALSE@,$SMARTD_SUFFIX_FALSE,;t t +s,@releaseversion@,$releaseversion,;t t +s,@smartmontools_release_date@,$smartmontools_release_date,;t t +s,@smartmontools_release_time@,$smartmontools_release_time,;t t +s,@os_deps@,$os_deps,;t t +s,@os_libs@,$os_libs,;t t +s,@OS_DARWIN_TRUE@,$OS_DARWIN_TRUE,;t t +s,@OS_DARWIN_FALSE@,$OS_DARWIN_FALSE,;t t +s,@OS_SOLARIS_TRUE@,$OS_SOLARIS_TRUE,;t t +s,@OS_SOLARIS_FALSE@,$OS_SOLARIS_FALSE,;t t +s,@OS_WIN32_MINGW_TRUE@,$OS_WIN32_MINGW_TRUE,;t t +s,@OS_WIN32_MINGW_FALSE@,$OS_WIN32_MINGW_FALSE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..e427448 --- /dev/null +++ b/configure.in @@ -0,0 +1,220 @@ +# +# $Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $ +# +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.50) +AC_INIT(smartmontools, 5.36, smartmontools-support@lists.sourceforge.net) +AC_CONFIG_SRCDIR(smartctl.c) + +smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"` +smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $'` +smartmontools_release_date=2006/04/12 +smartmontools_release_time="17:39:01 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]) +AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_DATE, "$smartmontools_release_date", [smartmontools Release Date]) +AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_TIME, "$smartmontools_release_time", [smartmontools Release Time]) +AC_DEFINE_UNQUOTED(CONFIG_H_CVSID, "$smartmontools_cvs_tag", [smartmontools CVS Tag]) +AC_DEFINE_UNQUOTED(PACKAGE_HOMEPAGE, "http://smartmontools.sourceforge.net/", [smartmontools Home Page]) + +AM_CONFIG_HEADER(config.h) + +AM_INIT_AUTOMAKE + +AM_MAINTAINER_MODE + +AC_LANG_C +dnl Checks for programs. +AC_PROG_CC +AM_PROG_AS +AC_PROG_INSTALL + +AC_CANONICAL_HOST +dnl Set flags which may affect AC_CHECK_*. +case "${host}" in + *-*-mingw*) + CPPFLAGS="$CPPFLAGS -mno-cygwin" + LDFLAGS="$LDFLAGS -mno-cygwin" + CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32" +esac + +dnl Checks for libraries.needed for gethostbyname (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(gethostbyname, nsl, , AC_SEARCH_LIBS(gethostbyname, nsl, , , -lsocket), , ) + +dnl Checks for header files. +AC_CHECK_HEADERS([locale.h]) +AC_CHECK_HEADERS([getopt.h]) +AC_CHECK_HEADERS([dev/ata/atavar.h]) +AC_CHECK_HEADERS([netdb.h]) +dnl we need [u]int64_t and friends. +AC_CHECK_HEADERS([inttypes.h]) dnl C99, UNIX98, solaris 2.6+ +AC_CHECK_HEADERS([stdint.h]) dnl C99 +AC_CHECK_HEADERS([sys/inttypes.h]) dnl pre-UNIX98 +AC_CHECK_HEADERS([sys/int_types.h]) dnl pre-UNIX98, solaris 2.6+ +dnl Check for FreeBSD twe include files...currently missing on 5.2, but should be there +AC_CHECK_HEADERS([sys/tweio.h]) +AC_CHECK_HEADERS([sys/twereg.h]) +dnl Check for FreeBSD twa include files... +AC_CHECK_HEADERS([sys/tw_osl_ioctl.h]) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_TYPES([int64_t, uint64_t]) + +dnl Checks for library functions. +AC_CHECK_FUNCS([getopt]) +AC_CHECK_FUNCS([getopt_long]) +AC_CHECK_FUNCS([getdomainname]) +AC_CHECK_FUNCS([gethostname]) +AC_CHECK_FUNCS([gethostbyname]) +AC_CHECK_FUNCS([sigset]) +AC_CHECK_FUNCS([strtoull]) +AC_CHECK_FUNCS([uname]) + +# Check whether snprintf appends null char and returns expected length on overflow +AH_TEMPLATE(HAVE_WORKING_SNPRINTF, [Define to 1 if the `snprintf' function is sane]) +AC_MSG_CHECKING([for working snprintf]) +AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ char buf[]="ABCDEFGHI"; + int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8); ]])], + [libc_have_working_snprintf=yes], [libc_have_working_snprintf=no]) +AC_SUBST(libc_have_working_snprintf) +if test "$libc_have_working_snprintf" = "yes"; then + AC_DEFINE(HAVE_WORKING_SNPRINTF) +fi +AC_MSG_RESULT([$libc_have_working_snprintf]) + +# check for __attribute__((packed)) +AH_TEMPLATE(HAVE_ATTR_PACKED, [Define to 1 if C compiler supports __attribute__((packed))]) +AC_MSG_CHECKING([whether C compiler supports __attribute__((packed))]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[struct a { int b; } __attribute__((packed));]])], + [gcc_have_attr_packed=yes], [gcc_have_attr_packed=no]) +AC_SUBST(gcc_have_attr_packed) +if test "$gcc_have_attr_packed" = "yes"; then + AC_DEFINE(HAVE_ATTR_PACKED) +fi +AC_MSG_RESULT([$gcc_have_attr_packed]) + +AC_SUBST(CPPFLAGS) +AC_SUBST(LDFLAGS) +AC_SUBST(ASFLAGS) + +AC_SUBST([exampledir], ['${docdir}/examplescripts']) + +AC_ARG_WITH(initscriptdir,[AC_HELP_STRING([--with-initscriptdir=dir],[Location of init scripts (default is ${sysconfdir}/rc.d/init.d)])],[initddir="$withval"],[initddir='${sysconfdir}/rc.d/init.d']) +AC_SUBST(initddir) + +AC_ARG_WITH(docdir,[AC_HELP_STRING([--with-docdir=dir],[Location of documentation (default is ${prefix}/share/doc/smartmontools-5.X)])],[docdir="$withval"],[docdir='${prefix}/share/doc/${PACKAGE}-${VERSION}']) +AC_SUBST(docdir) + +AC_ARG_ENABLE(sample,[AC_HELP_STRING([--enable-sample],[Enables appending .sample to the installed smartd rc script and configuration file])],[smartd_suffix='.sample'],[smartd_suffix='']) +AC_SUBST(smartd_suffix) +AM_CONDITIONAL(SMARTD_SUFFIX, test $smartd_suffix) + +if test "$prefix" = "NONE"; then + dnl no prefix and no mandir, so use ${prefix}/share/man as default + if test "$mandir" = '${prefix}/man'; then + AC_SUBST([mandir], ['${prefix}/share/man']) + fi +fi + +AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}']) +AC_SUBST(smartmontools_release_date) +AC_SUBST(smartmontools_release_time) + +dnl if OS not recognized, then use the os_generic modules +case "${host}" in + *-*-linux-gnu*) + AC_SUBST([os_deps], ['os_linux.o']) + AC_SUBST([os_libs], ['']) ;; + *-*-linux*) + AC_SUBST([os_deps], ['os_linux.o']) + AC_SUBST([os_libs], ['']) ;; + *-*-freebsd*) + AC_SUBST([os_deps], ['os_freebsd.o']) + AC_SUBST([os_libs], ['-lcam']);; + sparc-*-solaris*) + AC_DEFINE_UNQUOTED(DEFAULT_MAILER, "mailx", [use mailx as default mailer]) + AC_DEFINE_UNQUOTED(NEED_SOLARIS_ATA_CODE, "os_solaris_ata.s", [need assembly code os_solaris_ata.s]) + AC_SUBST([os_deps], ['os_solaris.o os_solaris_ata.o']) + AC_SUBST([os_libs], ['']) ;; + *-pc-solaris*) + AC_DEFINE_UNQUOTED(DEFAULT_MAILER, "mailx", [use mailx as default mailer]) + AC_SUBST([os_deps], ['os_solaris.o']) + AC_SUBST([os_libs], ['']) ;; + *-*-netbsd*) + AC_SUBST([os_deps], ['os_netbsd.o']) + AC_SUBST([os_libs], ['-lutil']) ;; + *-*-openbsd*) + AC_SUBST([os_deps], ['os_openbsd.o']) + AC_SUBST([os_libs], ['-lutil']) ;; + *-*-cygwin*) + AC_SUBST([os_deps], ['os_win32.o']) + AC_SUBST([os_libs], ['']) ;; + *-*-mingw*) + AC_SUBST([os_deps], ['os_win32.o']) + AC_SUBST([os_libs], ['']) ;; + *-*-darwin*) + AC_SUBST([os_deps], ['os_darwin.o']) + AC_SUBST([os_libs], ['-framework CoreFoundation -framework IOKit']) ;; + *) + AC_SUBST([os_deps], ['os_generic.o']) + AC_SUBST([os_libs], ['']) ;; +esac + +# Define symbols for optional functions in OS specific module +case "${os_deps}" in + os_win32*) + AC_DEFINE(HAVE_ATA_IDENTIFY_IS_CACHED, 1, [Define to 1 if you have the `ata_identify_is_cached' function in os_*.c.]) ;; +esac +case "${os_deps}" in + os_win32*) + AC_DEFINE(HAVE_GET_OS_VERSION_STR, 1, [Define to 1 if you have the `get_os_version_str' function in os_*.c.]) ;; +esac + +dnl Define platform-specific symbol. +AM_CONDITIONAL(OS_DARWIN, [echo $host_os | grep '^darwin' > /dev/null]) +AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null]) +AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null]) + +dnl Add -Wall and -W if using gcc and its not already specified. +if test "x$GCC" = "xyes"; then + if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then + CFLAGS="$CFLAGS -Wall" + fi +# In the next line, do NOT delete the 2 spaces inside double quotes. + if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then + CFLAGS="$CFLAGS -W" + fi + case "${host}" in + *-*-mingw*) + # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t + CFLAGS="$CFLAGS -Wno-format";; + esac +else + dnl We are NOT using gcc, so enable host-specific compiler flags + case "${host}" in + *-*-solaris*) + dnl set CFLAGS for Solaris C compiler + if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then + dnl we have to tell the compilers about packed ATA structures + CFLAGS="-xmemalign=1i $CFLAGS" + fi + if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then + dnl we have to tell the compiler to ignore C++ style comments + CFLAGS="-xCC $CFLAGS" + fi + if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then + dnl turn on optimization if user has not explicitly set its value + CFLAGS="-xO2 $CFLAGS" + fi + esac +fi + +AC_DEFINE_UNQUOTED(SMARTMONTOOLS_BUILD_HOST, "${host}", [smartmontools Build Host]) + +AC_SUBST(CFLAGS) + +AC_OUTPUT(Makefile examplescripts/Makefile) +AC_PROG_MAKE_SET diff --git a/depcomp b/depcomp new file mode 100755 index 0000000..11e2d3b --- /dev/null +++ b/depcomp @@ -0,0 +1,522 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2004-05-31.23 + +# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit 0 + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit 0 + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # Dependencies are output in .lo.d with libtool 1.4. + # With libtool 1.5 they are output both in $dir.libs/$base.o.d + # and in $dir.libs/$base.o.d and $dir$base.o.d. We process the + # latter, because the former will be cleaned when $dir.libs is + # erased. + tmpdepfile1="$dir.libs/$base.lo.d" + tmpdepfile2="$dir$base.o.d" + tmpdepfile3="$dir.libs/$base.d" + "$@" -Wc,-MD + else + tmpdepfile1="$dir$base.o.d" + tmpdepfile2="$dir$base.d" + tmpdepfile3="$dir$base.d" + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + elif test -f "$tmpdepfile2"; then + tmpdepfile="$tmpdepfile2" + else + tmpdepfile="$tmpdepfile3" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/examplescripts/Example1 b/examplescripts/Example1 new file mode 100755 index 0000000..9f4726f --- /dev/null +++ b/examplescripts/Example1 @@ -0,0 +1,44 @@ +#!/bin/bash +# +# This is a script from the smartmontools examplescripts/ directory. +# It can be used as an argument to the -M exec Directive in +# /etc/smartd.conf, in a line like +# -m root@localhost -M exec /path/to/this/file +# +# Please see man 8 smartd or man 5 smartd.conf for further +# information. +# +# $Id: Example1,v 1.7 2004/08/29 02:33:17 ballen4705 Exp $ + +# Save standard input into a temp file +cat > /root/tempfile + +# Echo command line arguments into temp file +echo "Command line argument 1:" >> /root/tempfile +echo $1 >> /root/tempfile +echo "Command line argument 2:" >> /root/tempfile +echo $2 >> /root/tempfile +echo "Command line argument 3:" >> /root/tempfile +echo $3 >> /root/tempfile + +# Echo environment variables into a temp file +echo "Variables are": >> /root/tempfile +echo "$SMARTD_DEVICE" >> /root/tempfile +echo "$SMARTD_DEVICESTRING" >> /root/tempfile +echo "$SMARTD_DEVICETYPE" >> /root/tempfile +echo "$SMARTD_MESSAGE" >> /root/tempfile +echo "$SMARTD_FULLMESSAGE" >> /root/tempfile +echo "$SMARTD_ADDRESS" >> /root/tempfile +echo "$SMARTD_SUBJECT" >> /root/tempfile +echo "$SMARTD_TFIRST" >> /root/tempfile +echo "$SMARTD_TFIRSTEPOCH" >> /root/tempfile + +# Run smartctl -a and save output in temp file +/usr/sbin/smartctl -a -d $SMARTD_DEVICETYPE $SMARTD_DEVICE >> /root/tempfile + +# Email the contents of the temp file. Solaris and other OSes +# may need to use /bin/mailx not /bin/mail. +/bin/mail -s "SMART errors detected on host: `hostname`" $SMARTD_ADDRESS < /root/tempfile + +# And exit +exit 0 diff --git a/examplescripts/Example2 b/examplescripts/Example2 new file mode 100755 index 0000000..f653433 --- /dev/null +++ b/examplescripts/Example2 @@ -0,0 +1,22 @@ +#! /bin/bash +# +# This is a script from the smartmontools examplescripts/ directory. +# It can be used as an argument to the -M exec Directive in +# /etc/smartd.conf, in a line like +# -m root@localhost -M exec /path/to/this/file +# +# Please see man 8 smartd or man 5 smartd.conf for further +# information. +# +# $Id: Example2,v 1.4 2004/01/07 16:49:56 ballen4705 Exp $ + +# Save the email message (STDIN) to a file: +cat > /root/msg + +# Append the output of smartctl -a to the message: +/usr/sbin/smartctl -a -d $SMARTD_DEVICETYPE $SMARTD_DEVICE >> /root/msg + +# Now email the message to the user at address ADD. Solaris and +# other OSes may need to use /bin/mailx below. +/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg + diff --git a/examplescripts/Example3 b/examplescripts/Example3 new file mode 100755 index 0000000..6a1b11c --- /dev/null +++ b/examplescripts/Example3 @@ -0,0 +1,25 @@ +#! /bin/bash +# +# This is a script from the smartmontools examplescripts/ directory. +# It can be used as an argument to the -M exec Directive in +# /etc/smartd.conf, in a line like +# -m -M exec /path/to/this/file +# +# Please see man 8 smartd or man 5 smartd.conf for further +# information. +# +# $Id: Example3,v 1.4 2003/08/17 09:15:56 ballen4705 Exp $ + +# Warn all users of a problem +wall 'Problem detected with disk: ' "$SMARTD_DEVICESTRING" +wall 'Warning message from smartd is: ' "$SMARTD_MESSAGE" +wall 'Shutting down machine in 30 seconds... ' + +# Wait half a minute +sleep 30 + +# Power down the machine (uncomment the shutdown command if you really +# want to do this!) + +# /sbin/shutdown -hf now + diff --git a/examplescripts/Makefile.am b/examplescripts/Makefile.am new file mode 100644 index 0000000..d37c17d --- /dev/null +++ b/examplescripts/Makefile.am @@ -0,0 +1,10 @@ +## Process this file with automake to produce Makefile.in +examplesdir=$(exampledir) + +examples_DATA = README + +examples_SCRIPTS = Example1 \ + Example2 \ + Example3 + +EXTRA_DIST = $(examples_SCRIPTS) diff --git a/examplescripts/Makefile.in b/examplescripts/Makefile.in new file mode 100644 index 0000000..01ea4fa --- /dev/null +++ b/examplescripts/Makefile.in @@ -0,0 +1,369 @@ +# Makefile.in generated by automake 1.9.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = examplescripts +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(examplesdir)" \ + "$(DESTDIR)$(examplesdir)" +examplesSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(examples_SCRIPTS) +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +examplesDATA_INSTALL = $(INSTALL_DATA) +DATA = $(examples_DATA) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +ASFLAGS = @ASFLAGS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OS_DARWIN_FALSE = @OS_DARWIN_FALSE@ +OS_DARWIN_TRUE = @OS_DARWIN_TRUE@ +OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@ +OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@ +OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@ +OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@ +SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +docdir = @docdir@ +exampledir = @exampledir@ +exec_prefix = @exec_prefix@ +gcc_have_attr_packed = @gcc_have_attr_packed@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +initddir = @initddir@ +install_sh = @install_sh@ +libc_have_working_snprintf = @libc_have_working_snprintf@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +os_deps = @os_deps@ +os_libs = @os_libs@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +releaseversion = @releaseversion@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +smartd_suffix = @smartd_suffix@ +smartmontools_release_date = @smartmontools_release_date@ +smartmontools_release_time = @smartmontools_release_time@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +examplesdir = $(exampledir) +examples_DATA = README +examples_SCRIPTS = Example1 \ + Example2 \ + Example3 + +EXTRA_DIST = $(examples_SCRIPTS) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign examplescripts/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign examplescripts/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-examplesSCRIPTS: $(examples_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)" + @list='$(examples_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(examplesSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \ + $(examplesSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \ + else :; fi; \ + done + +uninstall-examplesSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(examples_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \ + rm -f "$(DESTDIR)$(examplesdir)/$$f"; \ + done +uninstall-info-am: +install-examplesDATA: $(examples_DATA) + @$(NORMAL_INSTALL) + test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)" + @list='$(examples_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(examplesDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \ + $(examplesDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \ + done + +uninstall-examplesDATA: + @$(NORMAL_UNINSTALL) + @list='$(examples_DATA)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \ + rm -f "$(DESTDIR)$(examplesdir)/$$f"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(examplesdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-examplesDATA install-examplesSCRIPTS + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-examplesDATA uninstall-examplesSCRIPTS \ + uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-examplesDATA install-examplesSCRIPTS install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-examplesDATA uninstall-examplesSCRIPTS \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/examplescripts/README b/examplescripts/README new file mode 100644 index 0000000..404feca --- /dev/null +++ b/examplescripts/README @@ -0,0 +1,50 @@ +# Home page: http://smartmontools.sourceforge.net +# +# $Id: README,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $ +# +# Copyright (C) 2003-6 Bruce Allen +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2, or (at your option) any later +# version. +# +# You should have received a copy of the GNU General Public License (for +# example COPYING); if not, write to the Free Software Foundation, Inc., 675 +# Mass Ave, Cambridge, MA 02139, USA. +# +# This code was originally developed as a Senior Thesis by Michael Cornwell +# at the Concurrent Systems Laboratory (now part of the Storage Systems +# Research Center), Jack Baskin School of Engineering, University of +# California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + +This directory contains executable bash scripts, that are intended for +use with the + -m address -M exec /path/to/an/executable +Directive in /etc/smartd.conf. + +Details about how to use this Directive may be found in the man pages for +smartd and smartd.conf. + man 8 smartd + man 5 smartd.conf +should display those pages on your system. + +If you wish to contribute additional scripts to this collection, +please email them to , +and include a brief description to use below. + +The files contained in this directory are: + +Example1: appends values of $SMARTD_* environment variables and the output + of smartctl -a to the normal email message, and sends that + to the email address listed as the argument to the -m + Directive. + +Example2: Appends output of smartctl -a to the normal email message + and sends that to the email address listed as the argument + to the -m Directive. + +Example3: Uses wall(1) to send a warning message to all users, then powers + down the machine. + + diff --git a/extern.h b/extern.h new file mode 100644 index 0000000..5eabed7 --- /dev/null +++ b/extern.h @@ -0,0 +1,102 @@ +/* + * extern.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef EXTERN_H_ +#define EXTERN_H_ + +#define EXTERN_H_CVSID "$Id: extern.h,v 1.41 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// Possible values for fixfirmwarebug. If use has NOT specified -F at +// all, then value is 0. +#define FIX_NOTSPECIFIED 0 +#define FIX_NONE 1 +#define FIX_SAMSUNG 2 +#define FIX_SAMSUNG2 3 + +// Block used for global control/communications. If you need more +// global variables, this should be the only place that you need to +// add them. +typedef struct smartmonctrl_s { + // spans for selective self-test + uint64_t smartselectivespan[5][2]; + // number of spans + int smartselectivenumspans; + int testcase; + // one plus time in minutes to wait after powerup before restarting + // interrupted offline scan after selective self-test. + int pendingtime; + // run offline scan after selective self-test. 0: don't change, 1: + // turn off scan after selective self-test, 2: turn on scan after + // selective self-test. + unsigned char scanafterselect; + unsigned char driveinfo; + unsigned char checksmart; + unsigned char smartvendorattrib; + unsigned char generalsmartvalues; + unsigned char smartlogdirectory; + unsigned char smartselftestlog; + unsigned char selectivetestlog; + unsigned char smarterrorlog; + unsigned char smartdisable; + unsigned char smartenable; + unsigned char smartstatus; + unsigned char smartexeoffimmediate; + unsigned char smartshortselftest; + unsigned char smartextendselftest; + unsigned char smartconveyanceselftest; + unsigned char smartselectiveselftest; + unsigned char smartshortcapselftest; + unsigned char smartextendcapselftest; + unsigned char smartconveyancecapselftest; + unsigned char smartselectivecapselftest; + unsigned char smartselftestabort; + unsigned char smartautoofflineenable; + unsigned char smartautoofflinedisable; + unsigned char smartautosaveenable; + unsigned char smartautosavedisable; + unsigned char printing_switchable; + unsigned char dont_print; + unsigned char permissive; + unsigned char conservative; + unsigned char checksumfail; + unsigned char checksumignore; + unsigned char reportataioctl; + unsigned char reportscsiioctl; + unsigned char fixfirmwarebug; + // 3Ware controller type, but also extensible to other contoller types + unsigned char controller_type; + // For 3Ware controllers, nonzero value is 1 plus the disk number + unsigned char controller_port; + unsigned char ignorepresets; + unsigned char showpresets; + // The i'th entry in this array will modify the printed meaning of + // the i'th SMART attribute. The default definitions of the + // Attributes are obtained by having the array be all zeros. If + // attributedefs[i] is nonzero, it means that the i'th attribute has + // a non-default meaning. See the ataPrintSmartAttribName and + // and parse_attribute_def functions. + unsigned char attributedefs[256]; +} smartmonctrl; + +#endif diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..0b65ee8 --- /dev/null +++ b/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2004-10-22.00 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit 0;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit 0;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/int64.h b/int64.h new file mode 100644 index 0000000..64aa28d --- /dev/null +++ b/int64.h @@ -0,0 +1,94 @@ +/* + * int64.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2004-6 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 INT64_H_ +#define INT64_H_ + +#define INT64_H_CVSID "$Id: int64.h,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// 64 bit integer typedefs + +#ifdef HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#else +#ifdef HAVE_SYS_INTTYPES_H +#include +#else +#ifdef HAVE_SYS_INT_TYPES_H +#include +#else +#if defined(_WIN32) && defined(_MSC_VER) +// for MSVC 6.0 +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +// for systems with above includes missing (like ix86-pc-linux-gnulibc1), +// default to GCC if types are undefined in types.h +#include +#ifndef HAVE_INT64_T +typedef long long int64_t; +#endif +#ifndef HAVE_UINT64_T +typedef unsigned long long uint64_t; +#endif +#endif // _WIN32 && _MSC_VER +#endif // HAVE_SYS_INT_TYPES_H +#endif // HAVE_SYS_INTTYPES_H +#endif // HAVE_STDINT_H +#endif // HAVE_INTTYPES_H + +// 64 bit integer format strings + +#if defined(_WIN32) && defined(_MSC_VER) +// for MSVC 6.0 +#define PRId64 "I64d" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#endif // _WIN32 && _MSC_VER + +// If macros not defined in inttypes.h, fix here. Default is GCC +// style +#ifndef PRId64 +#define PRId64 "lld" +#endif // ndef PRId64 + +#ifndef PRIu64 +#define PRIu64 "llu" +#endif // ndef PRIu64 + +#ifndef PRIx64 +#define PRIx64 "llx" +#endif // ndef PRIx64 + + +#if defined(_WIN32) && defined(_MSC_VER) +// for MSVC 6.0: "unsigned __int64 -> double" conversion not implemented (why?-) +__inline double uint64_to_double(uint64_t ull) { + return ((int64_t)ull >= 0 ? (double)(int64_t)ull : + ((double)(int64_t)(ull - 9223372036854775808UI64)) + 9223372036854775808.0); +} +#else +#define uint64_to_double(ull) ((double)(ull)) +#endif // _WIN32 && _MSC_VER + + +#endif // INT64_H diff --git a/knowndrives.c b/knowndrives.c new file mode 100644 index 0000000..9494300 --- /dev/null +++ b/knowndrives.c @@ -0,0 +1,1258 @@ +/* + * knowndrives.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * Address of support mailing list: smartmontools-support@lists.sourceforge.net + * + * Copyright (C) 2003-6 Philip Williams, Bruce Allen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "config.h" +#include "int64.h" +#include +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "knowndrives.h" +#include "utility.h" // includes + +const char *knowndrives_c_cvsid="$Id: knowndrives.c,v 1.139 2006/04/05 19:50:07 chrfranke Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID; + +#define MODEL_STRING_LENGTH 40 +#define FIRMWARE_STRING_LENGTH 8 +#define TABLEPRINTWIDTH 19 + +// See vendorattributeargs[] array in atacmds.c for definitions. +#define PRESET_9_MINUTES { 9, 1 } +#define PRESET_9_TEMP { 9, 2 } +#define PRESET_9_SECONDS { 9, 3 } +#define PRESET_9_HALFMINUTES { 9, 4 } +#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192, 1 } +#define PRESET_193_LOADUNLOAD { 193, 1 } +#define PRESET_194_10XCELSIUS { 194, 1 } +#define PRESET_194_UNKNOWN { 194, 2 } +#define PRESET_198_OFFLINESCANUNCSECTORCT { 198, 1 } +#define PRESET_200_WRITEERRORCOUNT { 200, 1 } +#define PRESET_201_DETECTEDTACOUNT { 201, 1 } +#define PRESET_220_TEMP { 220, 1 } + +/* Arrays of preset vendor-specific attribute options for use in + * knowndrives[]. */ + +extern int64_t bytes; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// These three are common to several models. +const unsigned char vendoropts_9_minutes[][2] = { + PRESET_9_MINUTES, + {0,0} +}; +const unsigned char vendoropts_9_halfminutes[][2] = { + PRESET_9_HALFMINUTES, + {0,0} +}; +const unsigned char vendoropts_9_seconds[][2] = { + PRESET_9_SECONDS, + {0,0} +}; + +const unsigned char vendoropts_Maxtor_4D080H4[][2] = { + PRESET_9_MINUTES, + PRESET_194_UNKNOWN, + {0,0} +}; + +const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = { + PRESET_9_SECONDS, + PRESET_192_EMERGENCYRETRACTCYCLECT, + PRESET_198_OFFLINESCANUNCSECTORCT, + PRESET_200_WRITEERRORCOUNT, + PRESET_201_DETECTEDTACOUNT, + {0,0} +}; + +const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = { + PRESET_9_SECONDS, + PRESET_192_EMERGENCYRETRACTCYCLECT, + PRESET_198_OFFLINESCANUNCSECTORCT, + PRESET_200_WRITEERRORCOUNT, + {0,0} +}; + +const unsigned char vendoropts_Samsung_SV4012H[][2] = { + PRESET_9_HALFMINUTES, + {0,0} +}; + +const unsigned char vendoropts_Samsung_SV1204H[][2] = { + PRESET_9_HALFMINUTES, + PRESET_194_10XCELSIUS, + {0,0} +}; + +const unsigned char vendoropts_Hitachi_DK23XX[][2] = { + PRESET_9_MINUTES, + PRESET_193_LOADUNLOAD, + {0,0} +}; + +const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)"; +const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)"; + +const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details."; +const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details."; +const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details."; +const char may_need_minus_F_enabled[] ="May need -F samsung or -F samsung2 enabled; see manual for details."; + +/* Special-purpose functions for use in knowndrives[]. */ +void specialpurpose_reverse_samsung(smartmonctrl *con) +{ + if (con->fixfirmwarebug==FIX_NOTSPECIFIED) + con->fixfirmwarebug = FIX_SAMSUNG; +} +void specialpurpose_reverse_samsung2(smartmonctrl *con) +{ + if (con->fixfirmwarebug==FIX_NOTSPECIFIED) + con->fixfirmwarebug = FIX_SAMSUNG2; +} + +/* Table of settings for known drives terminated by an element containing all + * zeros. The drivesettings structure is described in knowndrives.h. Note + * that lookupdrive() will search knowndrives[] from the start to end or + * until it finds the first match, so the order in knowndrives[] is important + * for distinct entries that could match the same drive. */ + +// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS. +// A good on-line reference for these is: +// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html + +const drivesettings knowndrives[] = { + { "IBM Deskstar 60GXP series", // ER60A46A firmware + "(IBM-|Hitachi )?IC35L0[12346]0AVER07", + "^ER60A46A$", + NULL, NULL, NULL, NULL + }, + { "IBM Deskstar 60GXP series", // All other firmware + "(IBM-|Hitachi )?IC35L0[12346]0AVER07", + ".*", + "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n" + "Please see http://www.geocities.com/dtla_update/index.html#rel and\n" + "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" + "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", + NULL, NULL, NULL + }, + { "IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware)", + "(IBM-)?DTLA-30[57]0[123467][05]", + "^T[WX][123468AG][OF]A[56]AA$", + NULL, NULL, NULL, NULL + }, + { "IBM Deskstar 40GV & 75GXP series (all other firmware)", + "(IBM-)?DTLA-30[57]0[123467][05]", + ".*", + "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n" + "Please see http://www.geocities.com/dtla_update/ and\n" + "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n" + "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215", + NULL, NULL, NULL + }, + { NULL, // ExcelStor J240, J340, J360, J680, and J880 + "^ExcelStor Technology J(24|34|36|68|88)0$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // Fujitsu M1623TAU + "^FUJITSU M1623TAU$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MHG and MHH series", + "^FUJITSU MH(G2102|H20(64|48|32))AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MHJ and MHK series", + "^FUJITSU MH[JK]....ATU?$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MPB series", + "^FUJITSU MPB....ATU?$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MPD and MPE series", + "^FUJITSU MP[DE]....A[HTE]$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MPF series", + "^FUJITSU MPF3(102A[HT]|153A[HT]|204A[HT])$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MPG series", + "^FUJITSU MPG3(102A(H|T E)|153AH|204A(H|[HT] E)|307A(H E|T)|409A[HT] E)$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MPC series", + "^FUJITSU MPC3(032AT|043AT|045AH|064A[HT]|084AT|096AT|102AT)$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { NULL, // Fujitsu MHN2300AT + "^FUJITSU MHN2300AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { NULL, // Fujitsu MHR2040AT + "^FUJITSU MHR2040AT$", + ".*", // Tested on 40BA + NULL, + vendoropts_Fujitsu_MHR2040AT, + NULL, NULL + }, + { NULL, // Fujitsu MHR2020AT + "^FUJITSU MHR2020AT$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MHSxxxxAT family", + "^FUJITSU MHS20[6432]0AT( .)?$", + ".*", + NULL, + vendoropts_Fujitsu_MHS2020AT, + NULL, NULL + }, + { NULL, // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT + "^FUJITSU MH(L230|M2(20|10|15))0AT$", + ".*", + "This drive's firmware has a harmless Drive Identity Structure\n" + "checksum error bug.", + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MHT2xxxAT/MHU2100AT series", + "^FUJITSU MH(T20[23468]0AT( PL)?|U2100AT)$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { "Fujitsu MHTxxxxAH family", + "^FUJITSU MHT20[468]0AH$", + ".*", + NULL, + vendoropts_9_seconds, + NULL, NULL + }, + { NULL, // Samsung SV4012H (known firmware) + "^SAMSUNG SV4012H$", + "^RM100-08$", + NULL, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // Samsung SV4012H (all other firmware) + "^SAMSUNG SV4012H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // Samsung SV0412H (known firmware) + "^SAMSUNG SV0412H$", + "^SK100-01$", + NULL, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // Samsung SV0412H (all other firmware) + "^SAMSUNG SV0412H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // Samsung SV1204H (known firmware) + "^SAMSUNG SV1204H$", + "^RK100-1[3-5]$", + NULL, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // Samsung SV1204H (all other firmware) + "^SAMSUNG SV1204H$", + ".*", + may_need_minus_F_disabled, + vendoropts_Samsung_SV1204H, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { NULL, // SAMSUNG SV0322A tested with FW JK200-35 + "^SAMSUNG SV0322A$", + ".*", + NULL, + NULL, + NULL, + NULL + }, + { NULL, // SAMSUNG SP40A2H with RR100-07 firmware + "^SAMSUNG SP40A2H$", + "^RR100-07$", + NULL, + vendoropts_9_halfminutes, + specialpurpose_reverse_samsung, + same_as_minus_F + }, + { + NULL, // Any other Samsung disk with *-23 *-24 firmware + // SAMSUNG SP1213N (TL100-23 firmware) + // SAMSUNG SP0802N (TK100-23 firmware) + // Samsung SP1604N, tested with FW TM100-23 and TM100-24 + "^SAMSUNG .*$", + ".*-2[34]$", + NULL, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung2, + same_as_minus_F2 + }, + { NULL, // All Samsung drives with '.*-25' firmware + "^SAMSUNG.*", + ".*-25$", + may_need_minus_F2_disabled, + vendoropts_Samsung_SV4012H, + specialpurpose_reverse_samsung2, + same_as_minus_F2 + }, + { NULL, // All Samsung drives with '.*-26 or later (currently to -39)' firmware + "^SAMSUNG.*", + ".*-(2[6789]|3[0-9])$", + NULL, + vendoropts_Samsung_SV4012H, + NULL, + NULL + }, + { NULL, // Samsung ALL OTHER DRIVES + "^SAMSUNG.*", + ".*", + may_need_minus_F_enabled, + NULL, NULL, NULL + }, + { "Maxtor Fireball 541DX family", + "^Maxtor 2B0(0[468]|1[05]|20)H1$", + ".*", + NULL, + vendoropts_Maxtor_4D080H4, + NULL, NULL + }, + { "Maxtor Fireball 3 family", + "^Maxtor 2F0[234]0[JL]0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 2160 Ultra ATA family", + "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 2880 Ultra ATA family", + "^Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 3400 Ultra ATA family", + "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax D540X-4G family", + "^Maxtor 4G(120J6|160J[68])$", + ".*", + NULL, + vendoropts_Maxtor_4D080H4, + NULL, NULL + }, + { "Maxtor DiamondMax D540X-4K family", + "^MAXTOR 4K(020H1|040H2|060H3|080H4)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Maxtor DiamondMax Plus D740X family", + "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Maxtor DiamondMax Plus 5120 Ultra ATA 33 family", + "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 6800 Ultra ATA 66 family", + "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax D540X-4D", + "^Maxtor 4D0(20H1|40H2|60H3|80H4)$", + ".*", + NULL, + vendoropts_Maxtor_4D080H4, + NULL, NULL + }, + { "Maxtor DiamondMax 16 family", + "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 4320 family", + "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 17 VL family", + "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 20 VL family", + "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax VL 30 family", + "^Maxtor (33073U4|32049U3|31536U2|30768U1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 36 family", + "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 40 ATA 66 series", + "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100)", + "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 40 VL Ultra ATA 100 series", + "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family", + "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 60 family", + "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 80 family", + "^Maxtor (98196H8|96147H6)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 536DX family", + "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 8 family", + "^Maxtor 6E0[234]0L0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax 10 family", + "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(100P|120[MP]|160M|200[MPRS]|250[RS]|300[RS]))0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor DiamondMax Plus 9 family", + "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor MaXLine Plus II", + "^Maxtor 7Y250[PM]0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { "Maxtor MaXLine II family", + "^Maxtor [45]A(25|30|32)0[JN]0$", + ".*", + NULL, + vendoropts_9_minutes, + NULL, NULL + }, + { NULL, // HITACHI_DK14FA-20B + "^HITACHI_DK14FA-20B$", + ".*", + NULL, + vendoropts_Hitachi_DK23XX, + NULL, NULL + }, + { "HITACHI Travelstar DK23XX/DK23XXB series", + "^HITACHI_DK23..-..B?$", + ".*", + NULL, + vendoropts_Hitachi_DK23XX, + NULL, NULL + }, + { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)", + "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$", + ".*", + NULL, + vendoropts_Hitachi_DK23XX, + NULL, NULL + }, + { "IBM Deskstar 14GXP and 16GP series", + "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Deskstar 25GP and 22GXP family", + "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Travelstar 25GS, 18GT, and 12GN family", + "^IBM-DARA-2(25|18|15|12|09|06)000$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Travelstar 48GH, 30GN, and 15GN family", + "^(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Travelstar 32GH, 30GT, and 20GN family", + "^IBM-DJSA-2(32|30|20|10|05)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Deskstar 37GP and 34GXP family", + "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM/Hitachi Travelstar 60GH and 40GN family", + "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM/Hitachi Travelstar 40GNX family", + "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Travelstar 80GN family", + "^(Hitachi )?IC25N0[23468]0ATMR04-.$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Travelstar 5K80 family", + "^HTS5480[8642]0M9AT00$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Travelstar 5K100 series", + "^HTS5410[1864]0G9(AT|SA)00$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Travelstar 7K60", + "^HTS726060M9AT00$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Travelstar E7K60 family", + "^HTE7260[46]0M9AT00$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM/Hitachi Deskstar 120GXP family", + "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM/Hitachi Deskstar GXP-180 family", + "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Travelstar 14GS", + "^IBM-DCYA-214000$", + ".*", + NULL, NULL, NULL, NULL + }, + { "IBM Travelstar 4LP", + "^IBM-DTNA-2(180|216)0$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Deskstar 7K80 series", + "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Deskstar 7K250 series", + "^(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Hitachi Deskstar 7K400 series", + "^(Hitachi )?HDS724040KL(AT|SA)80$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK4025GAS + "^TOSHIBA MK4025GAS$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Toshiba 2.5\" HDD series", // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop] + "^TOSHIBA MK6021GAS$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK6022GAX + "^TOSHIBA MK6022GAX$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK4019GAX/MK4019GAXB + "^TOSHIBA MK4019GAXB?$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK6409MAV + "^TOSHIBA MK6409MAV$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOS MK3019GAXB SUN30G + "^TOS MK3019GAXB SUN30G$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS + "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK4018GAS, MK4018GAP + "^TOSHIBA MK4018GA[SP]$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK3017GAP + "^TOSHIBA MK3017GAP$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // TOSHIBA MK8026GAX + "^TOSHIBA MK8026GAX$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Momentus family", + "^ST9(20|28|40|48)11A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Momentus 4200.2 Series", + "^ST9(100822|808210|60821|50212|402113|30219)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Momentus 5400.2 series", + "^ST9(100823|808211|60822|408114|308110)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Medalist 8641 family", + "^ST3(2110|3221|4312|6531|8641)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U Series X family", + "^ST3(10014A(CE)?|20014A)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U Series 6 family", + "^ST3(8002|6002|4081|3061|2041)0A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U Series 5 family", + "^ST3(40823|30621|20413|15311|10211)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U4 family", + "^ST3(2112|4311|6421|8421)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U8 family", + "^ST3(8410|4313|17221|13021)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate U10 family", + "^ST3(20423|15323|10212)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda ATA II family", + "^ST3(3063|2042|1532|1021)0A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda ATA III family", + "^ST3(40824|30620|20414|15310|10215)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda ATA IV family", + "^ST3(20011|30011|40016|60021|80021)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda ATA V family", + "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda 5400.1", + "^ST340015A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda 7200.7 and 7200.7 Plus family", + "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]0827AS|8001[13]AS?|80817AS|60014A|40014AS?)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Barracuda 7200.8 family", + "^ST3(400832|300831|250823|200826)AS?$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310", + "^ST3(17240|13030|10231|8420|4310)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312", + "^ST3(1724|1303|1023|842|431)2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Protege", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD([2468]00E|1[26]00A)B-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar family", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar WDxxxAB series", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD(3|4|6)00AB-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar WDxxxAA series", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD...?AA(-.*)?$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar WDxxxBA series", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD...BA$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // Western Digital Caviar AC12500, AC14300, AC23200, AC24300, AC25100, + // AC36400, AC38400 + "^WDC AC(125|143|232|243|251|364|384)00.?", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar SE family", + /* Western Digital drives with this comment all appear to use Attribute 9 in + * a non-standard manner. These entries may need to be updated when it + * is understood exactly how Attribute 9 should be interpreted. + * UPDATE: this is probably explained by the WD firmware bug described in the + * smartmontools FAQ */ + "^WDC WD((4|6|8|10|12|16|18|20|25|30|32)00JB|(12|20|25)00PB)-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar SE (Serial ATA) family", + "^WDC WD(4|8|12|16|20|25)00JD-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Caviar RE Serial ATA series", + "^WDC WD((12|16|25|32)00SD|4000YR)-.*$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Western Digital Raptor family", + "^WDC WD(360|740)GD", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM BIGFOOT TS10.0A + "^QUANTUM BIGFOOT TS10.0A$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30 + "^QUANTUM FIREBALLlct15 [23]0$", + ".*", + NULL, NULL, NULL, NULL + }, + { "QUANTUM FIREBALLlct20 series", + "^QUANTUM FIREBALLlct20 [234]0$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALL CX10.2A + "^QUANTUM FIREBALL CX10.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Quantum Fireball Plus LM series", + "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$", + ".*", + NULL, NULL, NULL, NULL + }, + { "Quantum Fireball CR series", + "^QUANTUM FIREBALL CR(4.3|8.4)A$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0 + "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALL EX6.4A + "^QUANTUM FIREBALL EX6.4A$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALL ST3.2A + "^QUANTUM FIREBALL ST3.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALL EX3.2A + "^QUANTUM FIREBALL EX3.2A$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALLP KX27.3 + "^QUANTUM FIREBALLP KX27.3$", + ".*", + NULL, NULL, NULL, NULL + }, + { NULL, // QUANTUM FIREBALLP KA10.1 + "^QUANTUM FIREBALLP KA10.1$", + ".*", + NULL, NULL, NULL, NULL + }, + /*------------------------------------------------------------ + * End of table. Do not add entries below this marker. + *------------------------------------------------------------ */ + {NULL, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +// Searches knowndrives[] for a drive with the given model number and firmware +// string. If either the drive's model or firmware strings are not set by the +// manufacturer then values of NULL may be used. Returns the index of the +// first match in knowndrives[] or -1 if no match if found. +int lookupdrive(const char *model, const char *firmware) +{ + regex_t regex; + int i, index; + const char *empty = ""; + + model = model ? model : empty; + firmware = firmware ? firmware : empty; + + for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) { + // Attempt to compile regular expression. + if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) + goto CONTINUE; + + // Check whether model matches the regular expression in knowndrives[i]. + if (!regexec(®ex, model, 0, NULL, 0)) { + // model matches, now check firmware. + if (!knowndrives[i].firmwareregexp) + // The firmware regular expression in knowndrives[i] is NULL, which is + // considered a match. + index = i; + else { + // Compare firmware against the regular expression in knowndrives[i]. + regfree(®ex); // Recycle regex. + if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) + goto CONTINUE; + if (!regexec(®ex, firmware, 0, NULL, 0)) + index = i; + } + } + CONTINUE: + regfree(®ex); + } + + return index; +} + + +// Shows all presets for drives in knowndrives[]. +void showonepreset(const drivesettings *drivetable){ + + const unsigned char (* presets)[2] = drivetable->vendoropts; + int first_preset = 1; + + // Basic error check + if (!drivetable || !drivetable->modelregexp){ + pout("Null known drive table pointer. Please report\n" + "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n"); + return; + } + + // print model and firmware regular expressions + pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp); + pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ? + drivetable->firmwareregexp : ""); + pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", drivetable->modelfamily ? + drivetable->modelfamily : ""); + + // if there are any presets, then show them + if (presets && (*presets)[0]) while (1) { + char out[256]; + const int attr = (*presets)[0], val = (*presets)[1]; + unsigned char fakearray[MAX_ATTRIBUTE_NUM]; + + // if we are at the end of the attribute list, break out + if (!attr) + break; + + // This is a hack. ataPrintSmartAttribName() needs a pointer to an + // "array" to dereference, so we provide such a pointer. + fakearray[attr]=val; + ataPrintSmartAttribName(out, attr, fakearray); + + // Use leading zeros instead of spaces so that everything lines up. + out[0] = (out[0] == ' ') ? '0' : out[0]; + out[1] = (out[1] == ' ') ? '0' : out[1]; + pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out); + first_preset = 0; + presets++; + } + else + pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); + + + // Is a special purpose function defined? If so, describe it + if (drivetable->specialpurpose){ + pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:"); + pout("%s\n", drivetable->functiondesc ? + drivetable->functiondesc : "A special purpose function " + "is defined for this drive"); + } + + // Print any special warnings + if (drivetable->warningmsg){ + pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:"); + pout("%s\n", drivetable->warningmsg); + } + + return; +} + +// Shows all presets for drives in knowndrives[]. +// Returns <0 on syntax error in regular expressions. +int showallpresets(void){ + int i; + int rc = 0; + regex_t regex; + + // loop over all entries in the knowndrives[] table, printing them + // out in a nice format + for (i=0; knowndrives[i].modelregexp; i++){ + showonepreset(&knowndrives[i]); + pout("\n"); + } + + // Check all regular expressions + for (i=0; knowndrives[i].modelregexp; i++){ + if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) + rc = -1; + if (knowndrives[i].firmwareregexp) { + if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) + rc = -1; + } + } + pout("For information about adding a drive to the database see the FAQ on the\n"); + pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n"); + return rc; +} + +// Shows all matching presets for a drive in knowndrives[]. +// Returns # matching entries. +int showmatchingpresets(const char *model, const char *firmware){ + int i; + int cnt = 0; + const char * firmwaremsg = (firmware ? firmware : "(any)"); + regex_t regex; + + for (i=0; knowndrives[i].modelregexp; i++){ + if (i > 0) + regfree(®ex); + if (compileregex(®ex, knowndrives[i].modelregexp, REG_EXTENDED)) + continue; + if (regexec(®ex, model, 0, NULL, 0)) + continue; + if (firmware && knowndrives[i].firmwareregexp) { + regfree(®ex); + if (compileregex(®ex, knowndrives[i].firmwareregexp, REG_EXTENDED)) + continue; + if (regexec(®ex, firmware, 0, NULL, 0)) + continue; + } + if (++cnt == 1) + pout("Drive found in smartmontools Database. Drive identity strings:\n" + "%-*s %s\n" + "%-*s %s\n" + "match smartmontools Drive Database entry:\n", + TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg); + else if (cnt == 2) + pout("and match these additional entries:\n"); + showonepreset(&knowndrives[i]); + pout("\n"); + } + regfree(®ex); + if (cnt == 0) + pout("No presets are defined for this drive. Its identity strings:\n" + "MODEL: %s\n" + "FIRMWARE: %s\n" + "do not match any of the known regular expressions.\n", + model, firmwaremsg); + return cnt; +} + +// Shows the presets (if any) that are available for the given drive. +void showpresets(const struct ata_identify_device *drive){ + int i; + char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; + + // get the drive's model/firmware strings + formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); + formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); + + // and search to see if they match values in the table + if ((i = lookupdrive(model, firmware)) < 0) { + // no matches found + pout("No presets are defined for this drive. Its identity strings:\n" + "MODEL: %s\n" + "FIRMWARE: %s\n" + "do not match any of the known regular expressions.\n" + "Use -P showall to list all known regular expressions.\n", + model, firmware); + return; + } + + // We found a matching drive. Print out all information about it. + pout("Drive found in smartmontools Database. Drive identity strings:\n" + "%-*s %s\n" + "%-*s %s\n" + "match smartmontools Drive Database entry:\n", + TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware); + showonepreset(&knowndrives[i]); + return; +} + +// Sets preset vendor attribute options in opts by finding the entry +// (if any) for the given drive in knowndrives[]. Values that have +// already been set in opts will not be changed. Returns <0 if drive +// not recognized else index >=0 into drive database. +int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr, + smartmonctrl *con) { + int i; + unsigned char *opts; + char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; + + if (*optsptr==NULL) + bytes+=MAX_ATTRIBUTE_NUM; + + if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){ + pout("Unable to allocate memory in applypresets()"); + bytes-=MAX_ATTRIBUTE_NUM; + EXIT(1); + } + + opts=*optsptr; + + // get the drive's model/firmware strings + formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH); + formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH); + + // Look up the drive in knowndrives[]. + if ((i = lookupdrive(model, firmware)) >= 0) { + + // if vendoropts is non-NULL then Attribute interpretation presets + if (knowndrives[i].vendoropts) { + const unsigned char (* presets)[2]; + + // For each attribute in list of attribute/val pairs... + presets = knowndrives[i].vendoropts; + while (1) { + const int attr = (*presets)[0]; + const int val = (*presets)[1]; + + if (!attr) + break; + + // ... set attribute if user hasn't already done so. + if (!opts[attr]) + opts[attr] = val; + presets++; + } + } + + // If a special-purpose function is defined for this drive then + // call it. Note that if command line arguments or Directives + // over-ride this choice, then the specialpurpose function that is + // called must deal with this. + if (knowndrives[i].specialpurpose) + (*knowndrives[i].specialpurpose)(con); + } + + // return <0 if drive wasn't recognized, or index>=0 into database + // if it was + return i; +} diff --git a/knowndrives.h b/knowndrives.h new file mode 100644 index 0000000..1d1e751 --- /dev/null +++ b/knowndrives.h @@ -0,0 +1,88 @@ +/* + * knowndrives.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * Address of support mailing list: smartmontools-support@lists.sourceforge.net + * + * Copyright (C) 2003-6 Philip Williams, Bruce Allen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef KNOWNDRIVES_H_ +#define KNOWNDRIVES_H_ + +#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.16 2006/04/05 19:50:07 chrfranke Exp $\n" + +/* Structure used to store settings for specific drives in knowndrives[]. The + * elements are used in the following ways: + * + * modelfamily Informal string about the model family/series of a + * device. Set to NULL if no info (apart from device id) + * known. + * modelregexp POSIX regular expression to match the model of a device. + * This should never be NULL (except to terminate the + * knowndrives array). + * firmwareregexp POSIX regular expression to match a devices's firmware + * version. This is optional and should be NULL if it is not + * to be used. If it is non-NULL then it will be used to + * narrow the set of devices matched by modelregexp. + * warningmsg A message that may be displayed for matching drives. For + * example, to inform the user that they may need to apply a + * firmware patch. + * vendoropts Pointer to first element of an array of vendor-specific + * option attribute/value pairs that should be set for a + * matching device unless the user has requested otherwise. + * The user's own settings override these. The array should + * be terminated with the entry {0,0}. + * specialpurpose Pointer to a function that defines some additional action + * that may be taken for matching devices. + * functiondesc A description of the effect of the specialpurpose + * function. Used by showpresets() and showallpresets() to + * make the output more informative. + */ +typedef struct drivesettings_s { + const char * const modelfamily; + const char * const modelregexp; + const char * const firmwareregexp; + const char * const warningmsg; + const unsigned char (* const vendoropts)[2]; + void (* const specialpurpose)(smartmonctrl *); + const char * const functiondesc; +} drivesettings; + +/* Table of settings for known drives. Defined in knowndrives.c. */ +extern const drivesettings knowndrives[]; + +// Searches knowndrives[] for a drive with the given model number and firmware +// string. +int lookupdrive(const char *model, const char *firmware); + +// Shows the presets (if any) that are available for the given drive. +void showpresets(const struct ata_identify_device *drive); + +// Shows all presets for drives in knowndrives[]. +// Returns <0 on syntax error in regular expressions. +int showallpresets(void); + +// Shows all matching presets for a drive in knowndrives[]. +// Returns # matching entries. +int showmatchingpresets(const char *model, const char *firmware); + +// Sets preset vendor attribute options in opts by finding the entry +// (if any) for the given drive in knowndrives[]. Values that have +// already been set in opts will not be changed. Also sets options in +// con. Returns <0 if drive not recognized else index of drive in +// database. +int applypresets(const struct ata_identify_device *drive, unsigned char **opts, + smartmonctrl *con); + +#endif diff --git a/missing b/missing new file mode 100755 index 0000000..64b5f90 --- /dev/null +++ b/missing @@ -0,0 +1,353 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2004-09-07.08 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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 distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit 0 + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit 0 + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/os_darwin.c b/os_darwin.c new file mode 100644 index 0000000..d733290 --- /dev/null +++ b/os_darwin.c @@ -0,0 +1,404 @@ +/* + * os_darwin.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 Geoffrey Keating + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + // No, I don't know why there isn't a header for this. +#define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice" + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +#include "os_darwin.h" + +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_XXXX_c_cvsid="$Id: os_darwin.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// Print examples for smartctl. +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); + printf( + " smartctl -a disk0 (Prints all SMART information)\n\n" + " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n" +#ifdef HAVE_GETOPT_LONG + " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n" + " (Prints Self-Test & Attribute errors)\n\n" +#else + " smartctl -s on -S on /dev/rdisk0 (Enables SMART on first disk)\n\n" + " smartctl -A -l selftest -q errorsonly /dev/disk0\n" + " (Prints Self-Test & Attribute errors)\n\n" +#endif + " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n" + " (You can use IOService: ...)\n\n" + " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n" + " (... Or IODeviceTree:)\n" + ); + return; +} + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char* dev_name) { + // Only ATA is supported right now, so that's what it'd better be. + dev_name = dev_name; // suppress unused warning. + return CONTROLLER_ATA; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist); the +// other N arrays each contain null-terminated character strings. In +// the case N==0, no arrays are allocated because the array of 0 +// pointers has zero length, equivalent to calling malloc(0). +int make_device_names (char*** devlist, const char* name) { + IOReturn err; + io_iterator_t i; + io_object_t device; + int result; + int index; + const char * cls; + + if (strcmp (name, "ATA") == 0) + cls = kIOATABlockStorageDeviceClass; + else // only ATA supported right now. + return 0; + + err = IOServiceGetMatchingServices (kIOMasterPortDefault, + IOServiceMatching (cls), + &i); + if (err != kIOReturnSuccess) + return -1; + + // Count the devices. + for (result = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; result++) + IOObjectRelease (device); + + // Create an array of service names. + IOIteratorReset (i); + *devlist = Calloc (result, sizeof (char *)); + if (! *devlist) + goto error; + for (index = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; index++) + { + io_string_t devName; + IORegistryEntryGetPath(device, kIOServicePlane, devName); + IOObjectRelease (device); + + (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__); + if (! (*devlist)[index]) + goto error; + } + IOObjectRelease (i); + + return result; + + error: + IOObjectRelease (i); + if (*devlist) + { + for (index = 0; index < result; index++) + if ((*devlist)[index]) + FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__); + FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__); + } + return -1; +} + +// Information that we keep about each device. + +static struct { + io_object_t ioob; + bool hassmart; + IOCFPlugInInterface **plugin; + IOATASMARTInterface **smartIf; +} devices[20]; + +// Like open(). Return non-negative integer handle, only used by the +// functions below. type=="ATA" or "SCSI". The return value is +// an index into the devices[] array. If the device can't be opened, +// sets errno and returns -1. +// Acceptable device names are: +// /dev/disk* +// /dev/rdisk* +// disk* +// IOService:* +// IODeviceTree:* +int deviceopen(const char *pathname, char *type){ + size_t devnum; + const char *devname; + io_object_t disk; + + if (strcmp (type, "ATA") != 0) + { + errno = EINVAL; + return -1; + } + + // Find a free device number. + for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++) + if (! devices[devnum].ioob) + break; + if (devnum == sizeof (devices) / sizeof (devices[0])) + { + errno = EMFILE; + return -1; + } + + devname = NULL; + if (strncmp (pathname, "/dev/rdisk", 10) == 0) + devname = pathname + 6; + else if (strncmp (pathname, "/dev/disk", 9) == 0) + devname = pathname + 5; + else if (strncmp (pathname, "disk", 4) == 0) + // allow user to just say 'disk0' + devname = pathname; + + // Find the device. + if (devname) + { + CFMutableDictionaryRef matcher; + matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); + disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); + } + else + { + disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname); + } + + if (! disk) + { + errno = ENOENT; + return -1; + } + + // Find the ATA block storage driver that is the parent of this device + while (! IOObjectConformsTo (disk, kIOATABlockStorageDeviceClass)) + { + IOReturn err; + io_object_t notdisk = disk; + + err = IORegistryEntryGetParentEntry (notdisk, kIOServicePlane, &disk); + if (err != kIOReturnSuccess || ! disk) + { + errno = ENODEV; + IOObjectRelease (notdisk); + return -1; + } + } + + devices[devnum].ioob = disk; + + { + CFDictionaryRef diskChars = NULL; + CFNumberRef diskFeatures = NULL; + UInt32 ataFeatures; + + // Determine whether the drive actually supports SMART. + if ((diskChars = IORegistryEntryCreateCFProperty (disk, + CFSTR (kIOPropertyDeviceCharacteristicsKey), + kCFAllocatorDefault, + kNilOptions)) != NULL + && CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"), + (const void **)&diskFeatures) + && CFNumberGetValue (diskFeatures, kCFNumberLongType, &ataFeatures) + && (ataFeatures & kIOATAFeatureSMART)) + devices[devnum].hassmart = true; + else + devices[devnum].hassmart = false; + if (diskChars) + CFRelease (diskChars); + } + + { + SInt32 dummy; + + devices[devnum].plugin = NULL; + devices[devnum].smartIf = NULL; + + // Create an interface to the ATA SMART library. + if (devices[devnum].hassmart + && IOCreatePlugInInterfaceForService (disk, + kIOATASMARTUserClientTypeID, + kIOCFPlugInInterfaceID, + &devices[devnum].plugin, + &dummy) == kIOReturnSuccess) + (*devices[devnum].plugin)->QueryInterface + (devices[devnum].plugin, + CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID), + (LPVOID) &devices[devnum].smartIf); + } + + return devnum; +} + +// Like close(). Acts only on integer handles returned by +// deviceopen() above. +int deviceclose(int fd){ + if (devices[fd].smartIf) + (*devices[fd].smartIf)->Release (devices[fd].smartIf); + if (devices[fd].plugin) + IODestroyPlugInInterface (devices[fd].plugin); + IOObjectRelease (devices[fd].ioob); + devices[fd].ioob = MACH_PORT_NULL; + return 0; +} + +// Interface to ATA devices. See os_linux.c for the cannonical example. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the integer handle provided by deviceopen() +// command: defines the different operations, see atacmds.h +// select: additional input data IF NEEDED (which log, which type of +// self-test). +// data: location to write output data, IF NEEDED (1 or 512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES (for all commands BUT command==STATUS_CHECK) +// -1 if the command failed +// 0 if the command succeeded, +// RETURN VALUES if command==STATUS_CHECK +// -1 if the command failed OR the disk SMART status can't be determined +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + +// Things that aren't available in the Darwin interfaces: +// - Tests other than short and extended (in particular, can't run +// an immediate offline test) +// - Captive-mode tests, aborting tests +// - ability to switch automatic offline testing on or off + +// Note that some versions of Darwin, at least 7H63 and earlier, +// have a buggy library that treats the boolean value in +// SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and +// SMARTExecuteOffLineImmediate as always being true. +int marvell_command_interface(int fd, smart_command_set command, + int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, + int select, char *data) +{ + IOATASMARTInterface **ifp = devices[fd].smartIf; + IOATASMARTInterface *smartIf; + IOReturn err; + + if (! ifp) + return -1; + smartIf = *ifp; + + switch (command) + { + case STATUS: + return 0; + case STATUS_CHECK: + { + Boolean is_failing; + err = smartIf->SMARTReturnStatus (ifp, &is_failing); + if (err == kIOReturnSuccess && is_failing) + return 1; + break; + } + case ENABLE: + case DISABLE: + err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE); + break; + case AUTOSAVE: + err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0); + break; + case IMMEDIATE_OFFLINE: + if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST) + { + errno = EINVAL; + return -1; + } + err = smartIf->SMARTExecuteOffLineImmediate (ifp, + select == EXTEND_SELF_TEST); + break; + case READ_VALUES: + err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data); + break; + case READ_THRESHOLDS: + err = smartIf->SMARTReadDataThresholds (ifp, + (ATASMARTDataThresholds *)data); + break; + case READ_LOG: + err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512); + break; + case WRITE_LOG: + err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512); + break; + case IDENTIFY: + { + UInt32 dummy; + err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy); + if (err == kIOReturnSuccess && isbigendian()) + { + int i; + /* The system has already byte-swapped, undo it. */ + for (i = 0; i < 256; i+=2) + swap2 (data + i); + } + } + break; + case CHECK_POWER_MODE: + // The information is right there in the device registry, but how + // to get to it portably? + default: + errno = ENOTSUP; + return -1; + } + if (err == kIOReturnExclusiveAccess) + errno = EBUSY; + return err == kIOReturnSuccess ? 0 : -1; +} + +// There's no special handling needed for hidden devices, the kernel +// must deal with them. +int escalade_command_interface(int fd, int escalade_port, int escalade_type, + smart_command_set command, int select, + char *data) +{ + fd = fd; + escalade_port = escalade_port; + escalade_type = escalade_type; + command = command; + select = select; + data = data; + return -1; +} + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + return -ENOSYS; +} diff --git a/os_darwin.h b/os_darwin.h new file mode 100644 index 0000000..115d704 --- /dev/null +++ b/os_darwin.h @@ -0,0 +1,31 @@ +/* + * os_generic.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 Geoff Keating + * + * 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/ + * + */ + +#ifndef OS_DARWIN_H_ +#define OS_DARWIN_H_ + +#define OS_DARWIN_H_CVSID "$Id: os_darwin.h,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// There isn't actually any content here yet. + +#endif /* OS_DARWIN_H_ */ diff --git a/os_darwin/English_Localizable.strings b/os_darwin/English_Localizable.strings new file mode 100644 index 0000000..4f5d3d6 --- /dev/null +++ b/os_darwin/English_Localizable.strings @@ -0,0 +1,12 @@ + + + + + SMART disk monitoring + SMART disk monitoring + Starting SMART disk monitoring + Starting SMART disk monitoring + Stopping SMART disk monitoring + Stopping SMART disk monitoring + + diff --git a/os_darwin/SMART.in b/os_darwin/SMART.in new file mode 100644 index 0000000..c37b5b2 --- /dev/null +++ b/os_darwin/SMART.in @@ -0,0 +1,56 @@ +#!/bin/sh + +# Darwin init file for smartd +# +# Home page of code is: http://smartmontools.sourceforge.net +# +# Copyright (C) 2004-6 Geoffrey Keating +# +# 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. + +# $Id: SMART.in,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $ + +## +# SMART monitoring +## + +. /etc/rc.common + +StartService () +{ + if [ "${SMARTd:=-YES-}" = "-YES-" ] && + ! GetPID smartd > /dev/null; then + + ConsoleMessage "Starting SMART disk monitoring" + + /usr/sbin/smartd -p /var/run/smartd.pid + fi +} + +StopService () +{ + if pid=$(GetPID smartd); then + ConsoleMessage "Stopping SMART disk monitoring" + kill -TERM "${pid}" + else + echo "smartd is not running." + fi +} + +RestartService () +{ + if pid=$(GetPID smartd); then + kill -HUP "${pid}" + else + StartService + fi +} + +RunService "$1" diff --git a/os_darwin/StartupParameters.plist b/os_darwin/StartupParameters.plist new file mode 100644 index 0000000..174d831 --- /dev/null +++ b/os_darwin/StartupParameters.plist @@ -0,0 +1,5 @@ +{ + Description = "SMART disk monitoring"; + Provides = ("SMART"); + Requires = ("System Log"); +} diff --git a/os_freebsd.c b/os_freebsd.c new file mode 100644 index 0000000..19bbef1 --- /dev/null +++ b/os_freebsd.c @@ -0,0 +1,1069 @@ +/* + * os_freebsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 Eduard Martinescu + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_freebsd.h" + +static const char *filenameandversion="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Private table of open devices: guaranteed zero on startup since +// part of static data. +struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; + +// forward declaration +static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch); + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n" + " (Prints Self-Test & Attribute errors)\n" + " (Prints Self-Test & Attribute errors)\n\n" + " smartctl -a --device=3ware,2 /dev/twa0\n" + " smartctl -a --device=3ware,2 /dev/twe0\n" + " (Prints all SMART information for ATA disk on\n" + " third port of first 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/ad0 (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/ad0 (Enables SMART on first disk)\n" + " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/ad0\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a -d 3ware,2 /dev/twa0\n" + " smartctl -a -d 3ware,2 /dev/twe0\n" + ); +#endif + return; +} + +// Like open(). Return positive integer handle, used by functions below only. mode=="ATA" or "SCSI". +int deviceopen (const char* dev, char* mode __unused) { + struct freebsd_dev_channel *fdchan; + int parse_ok, i; + + // Search table for a free entry + for (i=0; idevice = open(dev,O_RDONLY))<0) { +#else + if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) { +#endif + int myerror = errno; //preserve across free call + free (fdchan); + errno = myerror; + return -1; + } + } + + if (parse_ok == CONTROLLER_3WARE_678K_CHAR) { + char buf[512]; + sprintf(buf,"/dev/twe%d",fdchan->device); +#ifdef IOCATAREQUEST + if ((fdchan->device = open(buf,O_RDWR))<0) { +#else + if ((fdchan->atacommand = open(buf,O_RDWR))<0) { +#endif + int myerror = errno; // preserver across free call + free(fdchan); + errno=myerror; + return -1; + } + } + + if (parse_ok == CONTROLLER_3WARE_9000_CHAR) { + char buf[512]; + sprintf(buf,"/dev/twa%d",fdchan->device); +#ifdef IOCATAREQUEST + if ((fdchan->device = open(buf,O_RDWR))<0) { +#else + if ((fdchan->atacommand = open(buf,O_RDWR))<0) { +#endif + int myerror = errno; // preserver across free call + free(fdchan); + errno=myerror; + return -1; + } + } + + if (parse_ok == CONTROLLER_SCSI) { + // this is really a NO-OP, as the parse takes care + // of filling in correct details + } + + // return pointer to "file descriptor" table entry, properly offset. + devicetable[i]=fdchan; + return i+FREEBSD_FDOFFSET; +} + +// Returns 1 if device not available/open/found else 0. Also shifts fd into valid range. +static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) { + // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1 + *fd -= FREEBSD_FDOFFSET; + + // check for validity of "file descriptor". + if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) { + errno = ENODEV; + return 1; + } + + return 0; +} + +// Like close(). Acts on handles returned by above function. +int deviceclose (int fd) { + struct freebsd_dev_channel *fdchan; + int failed = 0; + + // check for valid file descriptor + if (isnotopen(&fd, &fdchan)) + return -1; + + + // did we allocate a SCSI device name? + if (fdchan->devname) + free(fdchan->devname); + + // close device, if open +#ifdef IOCATAREQUEST + if (fdchan->device) + failed=close(fdchan->device); +#else + if (fdchan->atacommand) + failed=close(fdchan->atacommand); +#endif + + if (fdchan->scsicontrol) + failed=close(fdchan->scsicontrol); + + // if close succeeded, then remove from device list + // Eduard, should we also remove it from list if close() fails? I'm + // not sure. Here I only remove it from list if close() worked. + if (!failed) { + free(fdchan); + devicetable[fd]=NULL; + } + + return failed; +} + +#define NO_RETURN 0 +#define BAD_SMART 1 +#define NO_DISK_3WARE 2 +#define BAD_KERNEL 3 +#define MAX_MSG 3 + +// Utility function for printing warnings +void printwarning(int msgNo, const char* extra) { + static int printed[] = {0,0,0,0}; + static const char* message[]={ + "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n", + + "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + + "You must specify a DISK # for 3ware drives with -d 3ware, where begins with 1 for first disk drive\n", + + "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s",extra); + } + } + return; +} + + +// Interface to ATA devices. See os_linux.c +int marvell_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) { + return -1; +} + +int ata_command_interface(int fd, smart_command_set command, int select, char *data) { +#if !defined(ATAREQUEST) && !defined(IOCATAREQUEST) + // sorry, but without ATAng, we can't do anything here + printwarning(BAD_KERNEL,NULL); + errno = ENOSYS; + return -1; +#else + struct freebsd_dev_channel* con; + int retval, copydata=0; +#ifdef IOCATAREQUEST + struct ata_ioc_request request; +#else + struct ata_cmd iocmd; +#endif + unsigned char buff[512]; + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -1; + + bzero(buff,512); + +#ifdef IOCATAREQUEST + bzero(&request,sizeof(struct ata_ioc_request)); +#else + bzero(&iocmd,sizeof(struct ata_cmd)); +#endif + bzero(buff,512); + +#ifndef IOCATAREQUEST + iocmd.cmd=ATAREQUEST; + iocmd.channel=con->channel; + iocmd.device=con->device; +#define request iocmd.u.request +#endif + + request.u.ata.command=ATA_SMART_CMD; + request.timeout=600; + switch (command){ + case READ_VALUES: + request.u.ata.feature=ATA_SMART_READ_VALUES; + request.u.ata.lba=0xc24f<<8; + request.flags=ATA_CMD_READ; + request.data=buff; + request.count=512; + copydata=1; + break; + case READ_THRESHOLDS: + request.u.ata.feature=ATA_SMART_READ_THRESHOLDS; + request.u.ata.count=1; + request.u.ata.lba=1|(0xc24f<<8); + request.flags=ATA_CMD_READ; + request.data=buff; + request.count=512; + copydata=1; + break; + case READ_LOG: + request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR; + request.u.ata.lba=select|(0xc24f<<8); + request.u.ata.count=1; + request.flags=ATA_CMD_READ; + request.data=buff; + request.count=512; + copydata=1; + break; + case IDENTIFY: + request.u.ata.command=ATA_IDENTIFY_DEVICE; + request.flags=ATA_CMD_READ; + request.data=buff; + request.count=512; + copydata=1; + break; + case PIDENTIFY: + request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE; + request.flags=ATA_CMD_READ; + request.data=buff; + request.count=512; + copydata=1; + break; + case ENABLE: + request.u.ata.feature=ATA_SMART_ENABLE; + request.u.ata.lba=0xc24f<<8; + request.flags=ATA_CMD_CONTROL; + break; + case DISABLE: + request.u.ata.feature=ATA_SMART_DISABLE; + request.u.ata.lba=0xc24f<<8; + request.flags=ATA_CMD_CONTROL; + break; + case AUTO_OFFLINE: + // NOTE: According to ATAPI 4 and UP, this command is obsolete + request.u.ata.feature=ATA_SMART_AUTO_OFFLINE; + request.u.ata.lba=select|(0xc24f<<8); + request.flags=ATA_CMD_CONTROL; + break; + case AUTOSAVE: + request.u.ata.feature=ATA_SMART_AUTOSAVE; + request.u.ata.count=0xf1; // to enable autosave + request.u.ata.lba=0xc24f<<8; + request.flags=ATA_CMD_CONTROL; + break; + case IMMEDIATE_OFFLINE: + request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE; + request.u.ata.lba = select|(0xc24f<<8); // put test in sector + request.flags=ATA_CMD_CONTROL; + break; + case STATUS_CHECK: // same command, no HDIO in FreeBSD + case STATUS: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + request.u.ata.feature=ATA_SMART_STATUS; + request.u.ata.lba=0xc24f<<8; + request.flags=ATA_CMD_CONTROL; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno=ENOSYS; + return -1; + } + + if (command==STATUS_CHECK){ + unsigned const char normal_lo=0x4f, normal_hi=0xc2; + unsigned const char failed_lo=0xf4, failed_hi=0x2c; + unsigned char low,high; + +#ifdef IOCATAREQUEST + if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) +#else + if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) +#endif + return -1; + +#if __FreeBSD_version < 502000 + printwarning(NO_RETURN,NULL); +#endif + + high = (request.u.ata.lba >> 16) & 0xff; + low = (request.u.ata.lba >> 8) & 0xff; + + // Cyl low and Cyl high unchanged means "Good SMART status" + if (low==normal_lo && high==normal_hi) + return 0; + + // These values mean "Bad SMART status" + if (low==failed_lo && high==failed_hi) + return 1; + + // We haven't gotten output that makes sense; print out some debugging info + char buf[512]; + sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int)request.u.ata.command, + (int)request.u.ata.feature, + (int)request.u.ata.count, + (int)((request.u.ata.lba) & 0xff), + (int)((request.u.ata.lba>>8) & 0xff), + (int)((request.u.ata.lba>>16) & 0xff), + (int)request.error); + printwarning(BAD_SMART,buf); + return 0; + } + +#ifdef IOCATAREQUEST + if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) +#else + if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) +#endif + { + return -1; + } + // + if (copydata) + memcpy(data, buff, 512); + + return 0; +#endif +} + + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct freebsd_dev_channel* con = NULL; + struct cam_device* cam_dev = NULL; + union ccb *ccb; + + + if (report > 0) { + unsigned int k; + const unsigned char * ucp = iop->cmnd; + const char * np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : ""); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + pout("]"); + } + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -ENOTTY; + + + if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) { + warnx("%s",cam_errbuf); + return -1; + } + + if (!(ccb = cam_getccb(cam_dev))) { + warnx("error allocating ccb"); + return -ENOMEM; + } + + // clear out structure, except for header that was filled in for us + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + cam_fill_csio(&ccb->csio, + /*retrires*/ 1, + /*cbfcnp*/ NULL, + /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), + /* tagaction */ MSG_SIMPLE_Q_TAG, + /* dataptr */ iop->dxferp, + /* datalen */ iop->dxfer_len, + /* senselen */ iop->max_sense_len, + /* cdblen */ iop->cmnd_len, + /* timout (converted to seconds) */ iop->timeout*1000); + memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); + + if (cam_send_ccb(cam_dev,ccb) < 0) { + warn("error sending SCSI ccb"); + #if __FreeBSD_version > 500000 + cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + #endif + cam_freeccb(ccb); + return -1; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + #if __FreeBSD_version > 500000 + cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + #endif + cam_freeccb(ccb); + return -1; + } + + if (iop->sensep) { + memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); + iop->resp_sense_len = sizeof(struct scsi_sense_data); + } + + iop->scsi_status = ccb->csio.scsi_status; + + cam_freeccb(ccb); + + if (cam_dev) + cam_close_device(cam_dev); + + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + return 0; +} + +// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c + +#define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520 +#define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048 +#define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) ) + +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) { + // to hold true file descriptor + struct freebsd_dev_channel* con; + + // return value and buffer for ioctl() + int ioctlreturn, readdata=0; + struct twe_usercommand* cmd_twe = NULL; + TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; + TWE_Command_ATA* ata = NULL; + + // Used by both the SCSI and char interfaces + char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; + + if (disknum < 0) { + printwarning(NO_DISK_3WARE,NULL); + return -1; + } + + // check that "file descriptor" is valid + if (isnotopen(&fd,&con)) + return -1; + + memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); + + if (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; + ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; + } else if (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", escalade_type, disknum); + errno=ENOSYS; + return -1; + } + + ata->opcode = TWE_OP_ATA_PASSTHROUGH; + + // Same for (almost) all commands - but some reset below + ata->request_id = 0xFF; + ata->unit = disknum; + ata->host_id = 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; + + // Is this a command that reads or returns 512 bytes? + // passthru->param values are: + // 0x0 - non data command without TFR write check, + // 0x8 - non data command with TFR write check, + // 0xD - data command that returns data to host from device + // 0xF - data command that writes data from host to device + // passthru->size values are 0x5 for non-data and 0x07 for data + if (command == READ_VALUES || + command == READ_THRESHOLDS || + command == READ_LOG || + command == IDENTIFY || + command == WRITE_LOG ) { + readdata=1; + if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { + cmd_twe->tu_data = data; + cmd_twe->tu_size = 512; + } + ata->sgl_offset = 0x5; + ata->size = 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) + // ata->size++; + } + else { + // Non data command -- but doesn't use large sector + // count register values. + ata->sgl_offset = 0x0; + ata->size = 0x5; + ata->param = 0x8; + ata->sector_count = 0x0; + } + + // 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", 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, disknum); + errno=ENOSYS; + return -1; + } + + // Now send the command down through an ioctl() + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { +#ifdef IOCATAREQUEST + ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); +#else + ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); +#endif + } else { +#ifdef IOCATAREQUEST + ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe); +#else + ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe); +#endif + } + + // Deal with the different error cases + if (ioctlreturn) { + if (!errno) + errno=EIO; + return -1; + } + + // See if the ATA command failed. Now that we have returned from + // the ioctl() call, if passthru is valid, then: + // - ata->status contains the 3ware controller STATUS + // - ata->command contains the ATA STATUS register + // - ata->features contains the ATA ERROR register + // + // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS + // If bit 0 (error bit) is set, then ATA ERROR register is valid. + // While we *might* decode the ATA ERROR register, at the moment it + // doesn't make much sense: we don't care in detail why the error + // happened. + + if (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 this is a read data command, copy data to output buffer + if (readdata) { + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) + memcpy(data, cmd_twa->pdata, 512); + } + + // For STATUS_CHECK, we need to check register values + if (command==STATUS_CHECK) { + + // To find out if the SMART RETURN STATUS is good or failing, we + // need to examine the values of the Cylinder Low and Cylinder + // High Registers. + + unsigned short cyl_lo=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; + } + + // 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((unsigned char *)data, 512)) { + errno=ENODEV; + return -1; + } + + return 0; +} + +static int get_tw_channel_unit (const char* name, int* unit, int* dev) { + const char *p; + + /* device node sanity check */ + for (p = name + 3; *p; p++) + if (*p < '0' || *p > '9') + return -1; + if (strlen(name) > 4 && *(name + 3) == '0') + return -1; + + if (dev != NULL) + *dev=atoi(name + 3); + + /* no need for unit number */ + if (unit != NULL) + *unit=0; + return 0; +} + + +#ifndef IOCATAREQUEST +static int get_ata_channel_unit ( const char* name, int* unit, int* dev) { +#ifndef ATAREQUEST + *dev=0; + *unit=0; +return 0; +#else + // there is no direct correlation between name 'ad0, ad1, ...' and + // channel/unit number. So we need to iterate through the possible + // channels and check each unit to see if we match names + struct ata_cmd iocmd; + int fd,maxunit; + + bzero(&iocmd, sizeof(struct ata_cmd)); + + if ((fd = open("/dev/ata", O_RDWR)) < 0) + return -errno; + + iocmd.cmd = ATAGMAXCHANNEL; + if (ioctl(fd, IOCATA, &iocmd) < 0) { + return -errno; + close(fd); + } + maxunit = iocmd.u.maxchan; + for (*unit = 0; *unit < maxunit; (*unit)++) { + iocmd.channel = *unit; + iocmd.device = -1; + iocmd.cmd = ATAGPARM; + if (ioctl(fd, IOCATA, &iocmd) < 0) { + close(fd); + return -errno; + } + if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) { + *dev = 0; + break; + } + if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) { + *dev = 1; + break; + } + } + close(fd); + if (*unit == maxunit) + return -1; + else + return 0; +#endif +} +#endif + +// Guess device type (ata or scsi) based on device name (FreeBSD +// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst, +// osst, nosst and sg. +static const char * fbsd_dev_prefix = "/dev/"; +static const char * fbsd_dev_ata_disk_prefix = "ad"; +static const char * fbsd_dev_scsi_disk_plus = "da"; +static const char * fbsd_dev_scsi_tape1 = "sa"; +static const char * fbsd_dev_scsi_tape2 = "nsa"; +static const char * fbsd_dev_scsi_tape3 = "esa"; +static const char * fbsd_dev_twe_ctrl = "twe"; +static const char * fbsd_dev_twa_ctrl = "twa"; + +static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) { + int len; + int dev_prefix_len = strlen(fbsd_dev_prefix); + + // if dev_name null, or string length zero + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + // Remove the leading /dev/... if it's there + if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + // if nothing else in the string, unrecognized + return CONTROLLER_UNKNOWN; + // else advance pointer to following characters + dev_name += dev_prefix_len; + } + // form /dev/ad* or ad* + if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name, + strlen(fbsd_dev_ata_disk_prefix))) { +#ifndef IOCATAREQUEST + if (chan != NULL) { + if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { + return CONTROLLER_UNKNOWN; + } + } +#endif + return CONTROLLER_ATA; + } + + // form /dev/da* or da* + if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name, + strlen(fbsd_dev_scsi_disk_plus))) + goto handlescsi; + + // form /dev/sa* or sa* + if (!strncmp(fbsd_dev_scsi_tape1, dev_name, + strlen(fbsd_dev_scsi_tape1))) + goto handlescsi; + + // form /dev/nsa* or nsa* + if (!strncmp(fbsd_dev_scsi_tape2, dev_name, + strlen(fbsd_dev_scsi_tape2))) + goto handlescsi; + + // form /dev/esa* or esa* + if (!strncmp(fbsd_dev_scsi_tape3, dev_name, + strlen(fbsd_dev_scsi_tape3))) + goto handlescsi; + + if (!strncmp(fbsd_dev_twa_ctrl,dev_name, + strlen(fbsd_dev_twa_ctrl))) { + if (chan != NULL) { + if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { + return CONTROLLER_UNKNOWN; + } + } + else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { + return CONTROLLER_UNKNOWN; + } + return CONTROLLER_3WARE_9000_CHAR; + } + + if (!strncmp(fbsd_dev_twe_ctrl,dev_name, + strlen(fbsd_dev_twe_ctrl))) { + if (chan != NULL) { + if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { + return CONTROLLER_UNKNOWN; + } + } + else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { + return CONTROLLER_UNKNOWN; + } + return CONTROLLER_3WARE_678K_CHAR; + } + + // we failed to recognize any of the forms + return CONTROLLER_UNKNOWN; + + handlescsi: + if (chan != NULL) { + if (!(chan->devname = calloc(1,DEV_IDLEN+1))) + return CONTROLLER_UNKNOWN; + + if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1) + return CONTROLLER_UNKNOWN; + } + return CONTROLLER_SCSI; + +} + +int guess_device_type (const char* dev_name) { + return parse_ata_chan_dev(dev_name,NULL); +} + +// global variable holding byte count of allocated memory +extern long long bytes; + +// we are going to take advantage of the fact that FreeBSD's devfs will only +// have device entries for devices that exist. So if we get the equivilent of +// ls /dev/ad?, we have all the ATA devices on the system +// +// If any errors occur, leave errno set as it was returned by the +// system call, and return <0. + +// Return values: +// -1 out of memory +// -2 to -5 errors in glob + +int get_dev_names(char*** names, const char* prefix) { + int n = 0; + char** mp; + int retglob,lim; + glob_t globbuf; + int i; + char pattern1[128],pattern2[128]; + + bzero(&globbuf,sizeof(globbuf)); + // in case of non-clean exit + *names=NULL; + + // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV + sprintf(pattern1,"/dev/%s[0-9]",prefix); + sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix); + + // Use glob to look for any directory entries matching the patterns + // first call inits with first pattern match, second call appends + // to first list. Turn on NOCHECK for second call. This results in no + // error if no more matches found, however it does append the actual + // pattern to the list of paths.... + if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) || + (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) { + int retval = -1; + // glob failed + if (retglob==GLOB_NOSPACE) + pout("glob(3) ran out of memory matching patterns (%s),(%s)\n", + pattern1, pattern2); + else if (retglob==GLOB_ABORTED) + pout("glob(3) aborted matching patterns (%s),(%s)\n", + pattern1, pattern2); + else if (retglob==GLOB_NOMATCH) { + pout("glob(3) found no matches for patterns (%s),(%s)\n", + pattern1, pattern2); + retval = 0; + } + else if (retglob) + pout("Unexplained error in glob(3) of patterns (%s),(%s)\n", + pattern1, pattern2); + + // Free memory and return + globfree(&globbuf); + + return retval; + } + + // did we find too many paths? + // did we find too many paths? + lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV; + if (lim < globbuf.gl_pathc) + pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", + globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2, + globbuf.gl_pathc-MAX_NUM_DEV); + + // allocate space for up to lim number of ATA devices + if (!(mp = (char **)calloc(lim, sizeof(char*)))){ + pout("Out of memory constructing scan device list\n"); + return -1; + } + + // now step through the list returned by glob. No link checking needed + // in FreeBSD + for (i=0; i + * + * 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) 2000 Michael Smith + * Copyright (c) 2003 Paul Saab + * Copyright (c) 2003 Vinod Kashyap + * Copyright (c) 2000 BSDi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Copyright (c) 2004-05 Applied Micro Circuits Corporation. + * Copyright (c) 2004-05 Vinod Kashyap + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef OS_FREEBSD_H_ +#define OS_FREEBSD_H_ + +#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h,v 1.20 2006/04/12 14:54:28 ballen4705 Exp $\n" + +struct freebsd_dev_channel { + int channel; // the ATA channel to work with + int device; // the device on the channel + int atacommand; // the ATA Command file descriptor (/dev/ata) + char* devname; // the SCSI device name + int unitnum; // the SCSI unit number + int scsicontrol; // the SCSI control interface +}; + +#define FREEBSD_MAXDEV 64 +#define FREEBSD_FDOFFSET 16; +#define MAX_NUM_DEV 26 + +#ifdef HAVE_SYS_TWEREG_H +#include +#else +/** + * The following cut out of twereg.h + * + */ +#if __FreeBSD_version < 500040 +#define __packed __attribute__((__packed__)) +#endif + +#define TWE_MAX_SGL_LENGTH 62 +#define TWE_MAX_ATA_SGL_LENGTH 60 +#define TWE_OP_ATA_PASSTHROUGH 0x11 + +/* scatter/gather list entry */ +typedef struct +{ + u_int32_t address; + u_int32_t length; +} __packed TWE_SG_Entry; + +typedef struct { + u_int8_t opcode:5; /* TWE_OP_INITCONNECTION */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t res2:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int16_t message_credits; + u_int32_t response_queue_pointer; +} __packed TWE_Command_INITCONNECTION; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_READ/TWE_OP_WRITE */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int16_t block_count; + u_int32_t lba; + TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH]; +} __packed TWE_Command_IO; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_HOTSWAP */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int8_t action; +#define TWE_OP_HOTSWAP_REMOVE 0x00 /* remove assumed-degraded unit */ +#define TWE_OP_HOTSWAP_ADD_CBOD 0x01 /* add CBOD to empty port */ +#define TWE_OP_HOTSWAP_ADD_SPARE 0x02 /* add spare to empty port */ + u_int8_t aport; +} __packed TWE_Command_HOTSWAP; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_SETATAFEATURE */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int8_t feature; +#define TWE_OP_SETATAFEATURE_WCE 0x02 +#define TWE_OP_SETATAFEATURE_DIS_WCE 0x82 + u_int8_t feature_mode; + u_int16_t all_units; + u_int16_t persistence; +} __packed TWE_Command_SETATAFEATURE; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_CHECKSTATUS */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t res2:4; + u_int8_t status; + u_int8_t flags; + u_int16_t target_status; /* set low byte to target request's ID */ +} __packed TWE_Command_CHECKSTATUS; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_GETPARAM, TWE_OP_SETPARAM */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int16_t param_count; + TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH]; +} __packed TWE_Command_PARAM; + +typedef struct +{ + u_int8_t opcode:5; /* TWE_OP_REBUILDUNIT */ + u_int8_t res1:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t src_unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int8_t action:7; +#define TWE_OP_REBUILDUNIT_NOP 0 +#define TWE_OP_REBUILDUNIT_STOP 2 /* stop all rebuilds */ +#define TWE_OP_REBUILDUNIT_START 4 /* start rebuild with lowest unit */ +#define TWE_OP_REBUILDUNIT_STARTUNIT 5 /* rebuild src_unit (not supported) */ + u_int8_t cs:1; /* request state change on src_unit */ + u_int8_t logical_subunit; /* for RAID10 rebuild of logical subunit */ +} __packed TWE_Command_REBUILDUNIT; + +typedef struct +{ + u_int8_t opcode:5; + u_int8_t sgl_offset:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; + u_int16_t param; + u_int16_t features; + u_int16_t sector_count; + u_int16_t sector_num; + u_int16_t cylinder_lo; + u_int16_t cylinder_hi; + u_int8_t drive_head; + u_int8_t command; + TWE_SG_Entry sgl[TWE_MAX_ATA_SGL_LENGTH]; +} __packed TWE_Command_ATA; + +typedef struct +{ + u_int8_t opcode:5; + u_int8_t sgl_offset:3; + u_int8_t size; + u_int8_t request_id; + u_int8_t unit:4; + u_int8_t host_id:4; + u_int8_t status; + u_int8_t flags; +#define TWE_FLAGS_SUCCESS 0x00 +#define TWE_FLAGS_INFORMATIONAL 0x01 +#define TWE_FLAGS_WARNING 0x02 +#define TWE_FLAGS_FATAL 0x03 +#define TWE_FLAGS_PERCENTAGE (1<<8) /* bits 0-6 indicate completion percentage */ + u_int16_t count; /* block count, parameter count, message credits */ +} __packed TWE_Command_Generic; + +/* command packet - must be TWE_ALIGNMENT aligned */ +typedef union +{ + TWE_Command_INITCONNECTION initconnection; + TWE_Command_IO io; + TWE_Command_PARAM param; + TWE_Command_CHECKSTATUS checkstatus; + TWE_Command_REBUILDUNIT rebuildunit; + TWE_Command_SETATAFEATURE setatafeature; + TWE_Command_ATA ata; + TWE_Command_Generic generic; + u_int8_t pad[512]; +} TWE_Command; + +/* response queue entry */ +typedef union +{ + struct + { + u_int32_t undefined_1:4; + u_int32_t response_id:8; + u_int32_t undefined_2:20; + } u; + u_int32_t value; +} TWE_Response_Queue; + +#endif + +#ifdef HAVE_SYS_TWEIO_H +#include +#else +/* + * Following cut out of tweio.h + * + */ +/* + * User-space command + * + * Note that the command's scatter/gather list will be computed by the + * driver, and cannot be filled in by the consumer. + */ +struct twe_usercommand { + TWE_Command tu_command; /* command ready for the controller */ + void *tu_data; /* pointer to data in userspace */ + size_t tu_size; /* userspace data length */ +}; + +#define TWEIO_COMMAND _IOWR('T', 100, struct twe_usercommand) + +#endif + +#ifdef HAVE_SYS_TW_OSL_IOCTL_H +#include +#else +/* + * Following cut out of tw_osl_types.h + * + */ + +typedef void TW_VOID; +typedef char TW_INT8; +typedef unsigned char TW_UINT8; +typedef short TW_INT16; +typedef unsigned short TW_UINT16; +typedef int TW_INT32; +typedef unsigned int TW_UINT32; +typedef long long TW_INT64; +typedef unsigned long long TW_UINT64; + +/* + * Following cut out of tw_cl_share.h + * + */ + +#pragma pack(1) + +struct tw_cl_event_packet { + TW_UINT32 sequence_id; + TW_UINT32 time_stamp_sec; + TW_UINT16 aen_code; + TW_UINT8 severity; + TW_UINT8 retrieved; + TW_UINT8 repeat_count; + TW_UINT8 parameter_len; + TW_UINT8 parameter_data[98]; + TW_UINT32 event_src; + TW_UINT8 severity_str[20]; +}; + +#pragma pack() + +/* + * Following cut out of tw_cl_fwif.h + * + */ + +#define TWA_FW_CMD_ATA_PASSTHROUGH 0x11 + +#define TWA_SENSE_DATA_LENGTH 18 + +#pragma pack(1) +/* 7000 structures. */ +struct tw_cl_command_init_connect { + TW_UINT8 res1__opcode; /* 3:5 */ + TW_UINT8 size; + TW_UINT8 request_id; + TW_UINT8 res2; + TW_UINT8 status; + TW_UINT8 flags; + TW_UINT16 message_credits; + TW_UINT32 features; + TW_UINT16 fw_srl; + TW_UINT16 fw_arch_id; + TW_UINT16 fw_branch; + TW_UINT16 fw_build; + TW_UINT32 result; +}; + + +/* Structure for downloading firmware onto the controller. */ +struct tw_cl_command_download_firmware { + TW_UINT8 sgl_off__opcode;/* 3:5 */ + TW_UINT8 size; + TW_UINT8 request_id; + TW_UINT8 unit; + TW_UINT8 status; + TW_UINT8 flags; + TW_UINT16 param; + TW_UINT8 sgl[1]; +}; + + +/* Structure for hard resetting the controller. */ +struct tw_cl_command_reset_firmware { + TW_UINT8 res1__opcode; /* 3:5 */ + TW_UINT8 size; + TW_UINT8 request_id; + TW_UINT8 unit; + TW_UINT8 status; + TW_UINT8 flags; + TW_UINT8 res2; + TW_UINT8 param; +}; + + +/* Structure for sending get/set param commands. */ +struct tw_cl_command_param { + TW_UINT8 sgl_off__opcode;/* 3:5 */ + TW_UINT8 size; + TW_UINT8 request_id; + TW_UINT8 host_id__unit; /* 4:4 */ + TW_UINT8 status; + TW_UINT8 flags; + TW_UINT16 param_count; + TW_UINT8 sgl[1]; +}; + + +/* Generic command packet. */ +struct tw_cl_command_generic { + TW_UINT8 sgl_off__opcode;/* 3:5 */ + TW_UINT8 size; + TW_UINT8 request_id; + TW_UINT8 host_id__unit; /* 4:4 */ + TW_UINT8 status; + TW_UINT8 flags; + TW_UINT16 count; /* block cnt, parameter cnt, message credits */ +}; + + +/* Command packet header. */ +struct tw_cl_command_header { + TW_UINT8 sense_data[TWA_SENSE_DATA_LENGTH]; + struct { + TW_INT8 reserved[4]; + TW_UINT16 error; + TW_UINT8 padding; + TW_UINT8 res__severity; /* 5:3 */ + } status_block; + TW_UINT8 err_specific_desc[98]; + struct { + TW_UINT8 size_header; + TW_UINT16 reserved; + TW_UINT8 size_sense; + } header_desc; +}; + + +/* 7000 Command packet. */ +union tw_cl_command_7k { + struct tw_cl_command_init_connect init_connect; + struct tw_cl_command_download_firmware download_fw; + struct tw_cl_command_reset_firmware reset_fw; + struct tw_cl_command_param param; + struct tw_cl_command_generic generic; + TW_UINT8 padding[1024 - sizeof(struct tw_cl_command_header)]; +}; + + +/* 9000 Command Packet. */ +struct tw_cl_command_9k { + TW_UINT8 res__opcode; /* 3:5 */ + TW_UINT8 unit; + TW_UINT16 lun_l4__req_id; /* 4:12 */ + TW_UINT8 status; + TW_UINT8 sgl_offset; /* offset (in bytes) to sg_list, from the + end of sgl_entries */ + TW_UINT16 lun_h4__sgl_entries; + TW_UINT8 cdb[16]; + TW_UINT8 sg_list[872];/* total struct size = + 1024-sizeof(cmd_hdr) */ +}; + + +/* Full command packet. */ +struct tw_cl_command_packet { + struct tw_cl_command_header cmd_hdr; + union { + union tw_cl_command_7k cmd_pkt_7k; + struct tw_cl_command_9k cmd_pkt_9k; + } command; +}; + +#pragma pack() + +/* + * Following cut out of tw_cl_ioctl.h + * + */ + +#pragma pack(1) + +/* Structure used to handle GET/RELEASE LOCK ioctls. */ +struct tw_cl_lock_packet { + TW_UINT32 timeout_msec; + TW_UINT32 time_remaining_msec; + TW_UINT32 force_flag; +}; + + +/* Structure used to handle GET COMPATIBILITY INFO ioctl. */ +struct tw_cl_compatibility_packet { + TW_UINT8 driver_version[32];/* driver version */ + TW_UINT16 working_srl; /* driver & firmware negotiated srl */ + TW_UINT16 working_branch; /* branch # of the firmware that the + driver is compatible with */ + TW_UINT16 working_build; /* build # of the firmware that the + driver is compatible with */ +}; + + +/* Driver understandable part of the ioctl packet built by the API. */ +struct tw_cl_driver_packet { + TW_UINT32 control_code; + TW_UINT32 status; + TW_UINT32 unique_id; + TW_UINT32 sequence_id; + TW_UINT32 os_status; + TW_UINT32 buffer_length; +}; + +#pragma pack() + +/* + * Following cut out of tw_osl_ioctl.h + * + */ + +#pragma pack(1) +/* + * We need the structure below to ensure that the first byte of + * data_buf is not overwritten by the kernel, after we return + * from the ioctl call. Note that cmd_pkt has been reduced + * to an array of 1024 bytes even though it's actually 2048 bytes + * in size. This is because, we don't expect requests from user + * land requiring 2048 (273 sg elements) byte cmd pkts. + */ +typedef struct tw_osli_ioctl_no_data_buf { + struct tw_cl_driver_packet driver_pkt; + TW_VOID *pdata; /* points to data_buf */ + TW_INT8 padding[488 - sizeof(TW_VOID *)]; + struct tw_cl_command_packet cmd_pkt; +} TW_OSLI_IOCTL_NO_DATA_BUF; + +#pragma pack() + +#define TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH \ + _IOWR('T', 202, TW_OSLI_IOCTL_NO_DATA_BUF) + +#pragma pack(1) + +typedef struct tw_osli_ioctl_with_payload { + struct tw_cl_driver_packet driver_pkt; + TW_INT8 padding[488]; + struct tw_cl_command_packet cmd_pkt; + union { + struct tw_cl_event_packet event_pkt; + struct tw_cl_lock_packet lock_pkt; + struct tw_cl_compatibility_packet compat_pkt; + TW_INT8 data_buf[1]; + } payload; +} TW_OSLI_IOCTL_WITH_PAYLOAD; + +#pragma pack() + +#endif + + +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif + +#endif /* OS_FREEBSD_H_ */ diff --git a/os_generic.c b/os_generic.c new file mode 100644 index 0000000..3c5b5c4 --- /dev/null +++ b/os_generic.c @@ -0,0 +1,255 @@ +/* + * os_generic.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) YEAR YOUR_NAME + * Copyright (C) 2003-6 Bruce Allen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* PORTING NOTES AND COMMENTS + + To port smartmontools to the OS of your choice, please: + + [0] Contact smartmontools-support@lists.sourceforge.net to check + that it's not already been done. + + [1] Make copies of os_generic.[hc] called os_myOS.[hc]. + + [2] Modify configure.in so that case "${host}" includes myOS. + + [3] Verify that ./autogen.sh && ./configure && make compiles the + code. If not, fix any compilation problems. If your OS lacks + some function that is used elsewhere in the code, then add a + AC_CHECK_FUNCS([missingfunction]) line to configure.in, and + surround uses of the function with: + #ifdef HAVE_MISSINGFUNCTION + ... + #endif + where the macro HAVE_MISSINGFUNCTION is (or is not) defined in + config.h. + + [4] Provide the functions defined in this file by fleshing out the + skeletons below. You can entirely eliminate the function + 'unsupported()'. + + [5] Contact smartmontools-support@lists.sourceforge.net to see + about checking your code into the smartmontools CVS archive. +*/ + +/* + Developer's note: for testing this file, use an unsupported system, + for example: ./configure --build=rs6000-ibm-aix && make +*/ + + +// This is needed for the various HAVE_* macros and PROJECT_* macros. +#include "config.h" + +// These are needed to define prototypes and structures for the +// functions defined below +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +// This is to include whatever structures and prototypes you define in +// os_generic.h +#include "os_generic.h" + +// 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_generic.c,v 1.21 2006/04/12 14:54:28 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + + +// This is here to prevent compiler warnings for unused arguments of +// functions. +#define ARGUSED(x) ((void)(x)) + +// Please eliminate the following block: both the #include and +// the 'unsupported()' function. They are only here to warn +// unsuspecting users that their Operating System is not supported! If +// you wish, you can use a similar warning mechanism for any of the +// functions in this file that you can not (or choose not to) +// implement. + + +#ifdef HAVE_UNAME +#include +#endif + +static void unsupported(){ + static int warninggiven; + + if (!warninggiven) { + char *osname; + extern unsigned char debugmode; + unsigned char savedebugmode=debugmode; + +#ifdef HAVE_UNAME + struct utsname ostype; + uname(&ostype); + osname=ostype.sysname; +#else + osname="host's"; +#endif + + debugmode=1; + pout("\n" + "############################################################################\n" + "WARNING: smartmontools has not been ported to the %s Operating System.\n" + "Please see the files os_generic.c and os_generic.h for porting instructions.\n" + "############################################################################\n\n", + osname); + debugmode=savedebugmode; + warninggiven=1; + } + + return; +} +// End of the 'unsupported()' block that you should eliminate. + + +// print examples for smartctl. You should modify this function so +// that the device paths are sensible for your OS, and to eliminate +// unsupported commands (eg, 3ware controllers). +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a --device=3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a -d 3ware,2 /dev/sda\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#endif + return; +} + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char* dev_name) { + ARGUSED(dev_name); + unsupported(); + return CONTROLLER_UNKNOWN; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist); the +// other N arrays each contain null-terminated character strings. In +// the case N==0, no arrays are allocated because the array of 0 +// pointers has zero length, equivalent to calling malloc(0). +int make_device_names (char*** devlist, const char* name) { + ARGUSED(devlist); + ARGUSED(name); + unsupported(); + return 0; +} + +// Like open(). Return non-negative integer handle, only used by the +// functions below. type=="ATA" or "SCSI". If you need to store +// extra information about your devices, create a private internal +// array within this file (see os_freebsd.c for an example). If you +// can not open the device (permission denied, does not exist, etc) +// set errno as open() does and return <0. +int deviceopen(const char *pathname, char *type){ + ARGUSED(pathname); + ARGUSED(type); + unsupported(); + return -1; +} + +// Like close(). Acts only on integer handles returned by +// deviceopen() above. +int deviceclose(int fd){ + ARGUSED(fd); + unsupported(); + return 0; +} + +// Interface to ATA devices. See os_linux.c for the cannonical example. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the integer handle provided by deviceopen() +// command: defines the different operations, see atacmds.h +// select: additional input data IF NEEDED (which log, which type of +// self-test). +// data: location to write output data, IF NEEDED (1 or 512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES (for all commands BUT command==STATUS_CHECK) +// -1 if the command failed +// 0 if the command succeeded, +// RETURN VALUES if command==STATUS_CHECK +// -1 if the command failed OR the disk SMART status can't be determined +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" +int ata_command_interface(int fd, smart_command_set command, int select, char *data){ + ARGUSED(fd); + ARGUSED(command); + ARGUSED(select); + ARGUSED(data); + unsupported(); + return -1; +} + +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; +} + +// 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; +} + +#include +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + ARGUSED(fd); + ARGUSED(iop); + ARGUSED(report); + unsupported(); + return -ENOSYS; +} diff --git a/os_generic.h b/os_generic.h new file mode 100644 index 0000000..fae5209 --- /dev/null +++ b/os_generic.h @@ -0,0 +1,35 @@ +/* + * os_generic.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) YEAR YOUR_NAME + * Copyright (C) 2003-6 Bruce Allen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +// In the 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.6 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// Additional material should start here. Note: to keep the '-V' CVS +// reporting option working as intended, you should only #include +// system include files . Local #include files +// <"something.h"> should be #included in os_generic.c + +#endif /* OS_GENERIC_H_ */ diff --git a/os_linux.c b/os_linux.c new file mode 100644 index 0000000..26be586 --- /dev/null +++ b/os_linux.c @@ -0,0 +1,1527 @@ +/* + * os_linux.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 Bruce Allen + * Copyright (C) 2003-6 Doug Gilbert + * + * Parts of this file are derived from code that was + * + * Written By: Adam Radford + * Modifications By: Joel Jacobson + * Arnaldo Carvalho de Melo + * Brad Strand + * + * Copyright (C) 1999-2003 3ware Inc. + * + * Kernel compatablity By: Andre Hedrick + * Non-Copyright (C) 2000 Andre Hedrick + * + * Other ars of this file are derived from code that was + * + * Copyright (C) 1999-2000 Michael Cornwell + * Copyright (C) 2000 Andre Hedrick + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +// This file contains the linux-specific IOCTL parts of +// smartmontools. It includes one interface routine for ATA devices, +// one for SCSI devices, and one for ATA devices behind escalade +// controllers. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef makedev // old versions of types.h do not include sysmacros.h +#include +#endif + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "os_linux.h" +#include "scsicmds.h" +#include "utility.h" + +#ifndef ENOTSUP +#define ENOTSUP ENOSYS +#endif +typedef unsigned long long u8; + +#define ARGUSED(x) ((void)(x)) + +static const char *filenameandversion="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// global variable holding byte count of allocated memory +extern long long bytes; + + + +/* This function will setup and fix device nodes for a 3ware controller. */ +#define MAJOR_STRING_LENGTH 3 +#define DEVICE_STRING_LENGTH 32 +#define NODE_STRING_LENGTH 16 +int setup_3ware_nodes(char *nodename, char *driver_name) { + int tw_major = 0; + int index = 0; + char majorstring[MAJOR_STRING_LENGTH+1]; + char device_name[DEVICE_STRING_LENGTH+1]; + char nodestring[NODE_STRING_LENGTH]; + struct stat stat_buf; + FILE *file; + + /* First try to open up /proc/devices */ + if (!(file = fopen("/proc/devices", "r"))) { + pout("Error opening /proc/devices to check/create 3ware device nodes\n"); + syserror("fopen"); + return 0; // don't fail here: user might not have /proc ! + } + + /* Attempt to get device major number */ + while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) { + majorstring[MAJOR_STRING_LENGTH]='\0'; + device_name[DEVICE_STRING_LENGTH]='\0'; + if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) { + tw_major = atoi(majorstring); + break; + } + } + fclose(file); + + /* See if we found a major device number */ + if (!tw_major) { + pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name); + return 2; + } + + /* Now check if nodes are correct */ + for (index=0; index<16; index++) { + sprintf(nodestring, "/dev/%s%d", nodename, index); + + /* Try to stat the node */ + if ((stat(nodestring, &stat_buf))) { + /* Create a new node if it doesn't exist */ + if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) { + pout("problem creating 3ware device nodes %s", nodestring); + syserror("mknod"); + return 3; + } + } + + /* See if nodes major and minor numbers are correct */ + if ((tw_major != (int)(major(stat_buf.st_rdev))) || + (index != (int)(minor(stat_buf.st_rdev))) || + (!S_ISCHR(stat_buf.st_mode))) { + + /* Delete the old node */ + if (unlink(nodestring)) { + pout("problem unlinking stale 3ware device node %s", nodestring); + syserror("unlink"); + return 4; + } + + /* Make a new node */ + if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) { + pout("problem creating 3ware device nodes %s", nodestring); + syserror("mknod"); + return 5; + } + } + } + return 0; +} + +// equivalent to open(path, flags) +int deviceopen(const char *pathname, char *type){ + if (!strcmp(type,"SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } + else if (!strcmp(type,"ATA")) + return open(pathname, O_RDONLY | O_NONBLOCK); + else if (!strcmp(type,"ATA_3WARE_9000")) { + // the device nodes for this controller are dynamically assigned, + // so we need to check that they exist with the correct major + // numbers and if not, create them + if (setup_3ware_nodes("twa", "3w-9xxx")) { + if (!errno) + errno=ENXIO; + return -1; + } + return open(pathname, O_RDONLY | O_NONBLOCK); + } + else if (!strcmp(type,"ATA_3WARE_678K")) { + // the device nodes for this controller are dynamically assigned, + // so we need to check that they exist with the correct major + // numbers and if not, create them + if (setup_3ware_nodes("twe", "3w-xxxx")) { + if (!errno) + errno=ENXIO; + return -1; + } + return open(pathname, O_RDONLY | O_NONBLOCK); + } + else + return -1; +} + +// equivalent to close(file descriptor) +int deviceclose(int fd){ + return close(fd); +} + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl --all /dev/hda (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl --test=long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl --all --device=3ware,2 /dev/sda\n" + " smartctl --all --device=3ware,2 /dev/twe0\n" + " smartctl --all --device=3ware,2 /dev/twa0\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#else + printf( + " smartctl -a /dev/hda (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" + " smartctl -a -d 3ware,2 /dev/sda\n" + " smartctl -a -d 3ware,2 /dev/twa0\n" + " smartctl -a -d 3ware,2 /dev/twe0\n" + " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" + ); +#endif + return; +} + + +// we are going to take advantage of the fact that Linux's devfs will only +// have device entries for devices that exist. So if we get the equivalent of +// ls /dev/hd[a-t], we have all the ATA devices on the system +// +// If any errors occur, leave errno set as it was returned by the +// system call, and return <0. +int get_dev_names(char*** names, const char* pattern, const char* name, int max) { + int n = 0, retglob, i, lim; + char** mp; + glob_t globbuf; + + memset(&globbuf, 0, sizeof(globbuf)); + + // in case of non-clean exit + *names=NULL; + + // Use glob to look for any directory entries matching the pattern + if ((retglob=glob(pattern, GLOB_ERR, NULL, &globbuf))) { + + // glob failed: free memory and return + globfree(&globbuf); + + if (retglob==GLOB_NOMATCH){ + pout("glob(3) found no matches for pattern %s\n", pattern); + return 0; + } + + if (retglob==GLOB_NOSPACE) + pout("glob(3) ran out of memory matching pattern %s\n", pattern); +#ifdef GLOB_ABORTED // missing in old versions of glob.h + else if (retglob==GLOB_ABORTED) + pout("glob(3) aborted matching pattern %s\n", pattern); +#endif + else + pout("Unexplained error in glob(3) of pattern %s\n", pattern); + + return -1; + } + + // did we find too many paths? + lim = ((int)globbuf.gl_pathc < max) ? (int)globbuf.gl_pathc : max; + if (lim < (int)globbuf.gl_pathc) + pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", + (int)globbuf.gl_pathc, max, pattern, (int)(globbuf.gl_pathc-max)); + + // allocate space for up to lim number of ATA devices + if (!(mp = (char **)calloc(lim, sizeof(char*)))){ + pout("Out of memory constructing scan device list\n"); + return -1; + } + + // now step through the list returned by glob. If not a link, copy + // to list. If it is a link, evaluate it and see if the path ends + // in "disc". + for (i=0; i1023) + mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); + else { + // or if it's a link that points to a disc, follow it + char *p; + linkbuf[retlink]='\0'; + if ((p=strrchr(linkbuf,'/')) && !strcmp(p+1, "disc")) + // This is the branch of the code that gets followed if we are + // using devfs WITH traditional compatibility links. In this + // case, we add the traditional device name to the list that + // is returned. + mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); + else { + // This is the branch of the code that gets followed if we are + // using devfs WITHOUT traditional compatibility links. In + // this case, we check that the link to the directory is of + // the correct type, and then append "disc" to it. + char tmpname[1024]={0}; + char *type=strcmp(name,"ATA")?"scsi":"ide"; + if (strstr(linkbuf, type)){ + snprintf(tmpname, 1024, "%s/disc", globbuf.gl_pathv[i]); + mp[n++] = CustomStrDup(tmpname, 1, __LINE__, filenameandversion); + } + } + } + } + + // free memory, track memory usage + globfree(&globbuf); + mp = realloc(mp,n*(sizeof(char*))); + bytes += n*(sizeof(char*)); + + // and set up return values + *names=mp; + return n; +} + +// makes a list of device names to scan, for either ATA or SCSI +// devices. Return -1 if no memory remaining, else the number of +// devices on the list, which can be >=0. +int make_device_names (char*** devlist, const char* name) { + int retval, maxdev; + +#if 0 + // for testing case where no device names are found + return 0; +#endif + + if (!strcmp(name,"SCSI")) + retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26); + else if (!strcmp(name,"ATA")) + retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20); + else + // don't recognize disk type! + return 0; + + // if we found traditional links, we are done + if (retval>0) + return retval; + + // else look for devfs entries without traditional links + return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev); +} + + +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux one. +// DETAILED DESCRIPTION OF ARGUMENTS +// device: is the file descriptor provided by open() +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + + +// huge value of buffer size needed because HDIO_DRIVE_CMD assumes +// that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and +// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space. +// Otherwise a 4+512 byte buffer would be enough. +#define STRANGE_BUFFER_LENGTH (4+512*0xf8) + +int ata_command_interface(int device, smart_command_set command, int select, char *data){ + unsigned char buff[STRANGE_BUFFER_LENGTH]; + // positive: bytes to write to caller. negative: bytes to READ from + // caller. zero: non-data command + int copydata=0; + + const int HDIO_DRIVE_CMD_OFFSET = 4; + + // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl() + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER + // buff[2]: ATA FEATURES REGISTER + // buff[3]: ATA SECTOR COUNT REGISTER + + // Note that on return: + // buff[2] contains the ATA SECTOR COUNT REGISTER + + // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) + memset(buff, 0, STRANGE_BUFFER_LENGTH); + + buff[0]=ATA_SMART_CMD; + switch (command){ + case CHECK_POWER_MODE: + buff[0]=ATA_CHECK_POWER_MODE; + copydata=1; + break; + case READ_VALUES: + buff[2]=ATA_SMART_READ_VALUES; + buff[3]=1; + copydata=512; + break; + case READ_THRESHOLDS: + buff[2]=ATA_SMART_READ_THRESHOLDS; + buff[1]=buff[3]=1; + copydata=512; + break; + case READ_LOG: + buff[2]=ATA_SMART_READ_LOG_SECTOR; + buff[1]=select; + buff[3]=1; + copydata=512; + break; + case WRITE_LOG: + break; + case IDENTIFY: + buff[0]=ATA_IDENTIFY_DEVICE; + buff[3]=1; + copydata=512; + break; + case PIDENTIFY: + buff[0]=ATA_IDENTIFY_PACKET_DEVICE; + buff[3]=1; + copydata=512; + break; + case ENABLE: + buff[2]=ATA_SMART_ENABLE; + buff[1]=1; + break; + case DISABLE: + buff[2]=ATA_SMART_DISABLE; + buff[1]=1; + break; + case STATUS: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + buff[2]=ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + buff[2]=ATA_SMART_AUTO_OFFLINE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + buff[2]=ATA_SMART_AUTOSAVE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; + buff[1]=select; + break; + case STATUS_CHECK: + // This command uses HDIO_DRIVE_TASK and has different syntax than + // the other commands. + buff[1]=ATA_SMART_STATUS; + break; + default: + pout("Unrecognized command %d in linux_ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno=ENOSYS; + return -1; + } + + // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the + // only ioctl() that can be used to WRITE data to the disk. + if (command==WRITE_LOG) { + unsigned char task[sizeof(ide_task_request_t)+512]; + ide_task_request_t *reqtask=(ide_task_request_t *) task; + task_struct_t *taskfile=(task_struct_t *) reqtask->io_ports; + int retval; + + memset(task, 0, sizeof(task)); + + taskfile->data = 0; + taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; + taskfile->sector_count = 1; + taskfile->sector_number = select; + taskfile->low_cylinder = 0x4f; + taskfile->high_cylinder = 0xc2; + taskfile->device_head = 0; + taskfile->command = ATA_SMART_CMD; + + reqtask->data_phase = TASKFILE_OUT; + reqtask->req_cmd = IDE_DRIVE_TASK_OUT; + reqtask->out_size = 512; + reqtask->in_size = 0; + + // copy user data into the task request structure + memcpy(task+sizeof(ide_task_request_t), data, 512); + + if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) { + if (retval==-EINVAL) + pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); + return -1; + } + return 0; + } + + // There are two different types of ioctls(). The HDIO_DRIVE_TASK + // one is this: + if (command==STATUS_CHECK){ + int retval; + + // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You + // have to read the IDE driver source code. Sigh. + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA FEATURES REGISTER + // buff[2]: ATA SECTOR_COUNT + // buff[3]: ATA SECTOR NUMBER + // buff[4]: ATA CYL LO REGISTER + // buff[5]: ATA CYL HI REGISTER + // buff[6]: ATA DEVICE HEAD + + unsigned const char normal_lo=0x4f, normal_hi=0xc2; + unsigned const char failed_lo=0xf4, failed_hi=0x2c; + buff[4]=normal_lo; + buff[5]=normal_hi; + + if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) { + if (retval==-EINVAL) { + pout("Error SMART Status command via HDIO_DRIVE_TASK failed"); + pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n"); + } + else + syserror("Error SMART Status command failed"); + return -1; + } + + // Cyl low and Cyl high unchanged means "Good SMART status" + if (buff[4]==normal_lo && buff[5]==normal_hi) + return 0; + + // These values mean "Bad SMART status" + if (buff[4]==failed_lo && buff[5]==failed_hi) + return 1; + + // We haven't gotten output that makes sense; print out some debugging info + syserror("Error SMART Status command failed"); + pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); + pout("Register values returned from SMART Status command are:\n"); + pout("CMD=0x%02x\n",(int)buff[0]); + pout("FR =0x%02x\n",(int)buff[1]); + pout("NS =0x%02x\n",(int)buff[2]); + pout("SC =0x%02x\n",(int)buff[3]); + pout("CL =0x%02x\n",(int)buff[4]); + pout("CH =0x%02x\n",(int)buff[5]); + pout("SEL=0x%02x\n",(int)buff[6]); + return -1; + } + +#if 1 + // Note to people doing ports to other OSes -- don't worry about + // this block -- you can safely ignore it. I have put it here + // because under linux when you do IDENTIFY DEVICE to a packet + // device, it generates an ugly kernel syslog error message. This + // is harmless but frightens users. So this block detects packet + // devices and make IDENTIFY DEVICE fail "nicely" without a syslog + // error message. + // + // If you read only the ATA specs, it appears as if a packet device + // *might* respond to the IDENTIFY DEVICE command. This is + // misleading - it's because around the time that SFF-8020 was + // incorporated into the ATA-3/4 standard, the ATA authors were + // sloppy. See SFF-8020 and you will see that ATAPI devices have + // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their + // command set, and return 'Command Aborted' to IDENTIFY DEVICE. + if (command==IDENTIFY || command==PIDENTIFY){ + unsigned short deviceid[256]; + // check the device identity, as seen when the system was booted + // or the device was FIRST registered. This will not be current + // if the user has subsequently changed some of the parameters. If + // device is a packet device, swap the command interpretations. + if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000)) + buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE; + } +#endif + + // We are now doing the HDIO_DRIVE_CMD type ioctl. + if ((ioctl(device, HDIO_DRIVE_CMD, buff))) + return -1; + + // CHECK POWER MODE command returns information in the Sector Count + // register (buff[3]). Copy to return data buffer. + if (command==CHECK_POWER_MODE) + buff[HDIO_DRIVE_CMD_OFFSET]=buff[2]; + + // if the command returns data then copy it back + if (copydata) + memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata); + + return 0; +} + +// >>>>>> Start of general SCSI specific linux code + +/* Linux specific code. + * Historically smartmontools (and smartsuite before it) used the + * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device + * nodes that use the SCSI subsystem. A better interface has been available + * via the SCSI generic (sg) driver but this involves the extra step of + * mapping disk devices (e.g. /dev/sda) to the corresponding sg device + * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of + * the sg driver have become available via the SG_IO ioctl which is available + * on all SCSI devices (on SCSI tape devices from lk 2.6.6). + * So the strategy below is to find out if the SG_IO ioctl is available and + * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl. + * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */ + +#define MAX_DXFER_LEN 1024 /* can be increased if necessary */ +#define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */ +#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */ +#define LSCSI_DRIVER_MASK 0xf /* mask out "suggestions" */ +#define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */ +#define LSCSI_DRIVER_TIMEOUT 0x6 +#define LSCSI_DID_TIME_OUT 0x3 +#define LSCSI_DID_BUS_BUSY 0x2 +#define LSCSI_DID_NO_CONNECT 0x1 + +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +#define SG_IO_PRESENT_UNKNOWN 0 +#define SG_IO_PRESENT_YES 1 +#define SG_IO_PRESENT_NO 2 + +static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, + int unknown); +static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); + +static int sg_io_state = SG_IO_PRESENT_UNKNOWN; + +/* Preferred implementation for issuing SCSI commands in linux. This + * function uses the SG_IO ioctl. Return 0 if command issued successfully + * (various status values should still be checked). If the SCSI command + * cannot be issued then a negative errno value is returned. */ +static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, + int unknown) +{ +#ifndef SG_IO + ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report); + return -ENOTTY; +#else + struct sg_io_hdr io_hdr; + + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char *)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = iop->cmnd_len; + io_hdr.mx_sb_len = iop->max_sense_len; + io_hdr.dxfer_len = iop->dxfer_len; + io_hdr.dxferp = iop->dxferp; + io_hdr.cmdp = iop->cmnd; + io_hdr.sbp = iop->sensep; + /* sg_io_hdr interface timeout has millisecond units. Timeout of 0 + defaults to 60 seconds. */ + io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; + switch (iop->dxfer_dir) { + case DXFER_NONE: + io_hdr.dxfer_direction = SG_DXFER_NONE; + break; + case DXFER_FROM_DEVICE: + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case DXFER_TO_DEVICE: + io_hdr.dxfer_direction = SG_DXFER_TO_DEV; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) { + if (report && (! unknown)) + pout(" SG_IO ioctl failed, errno=%d [%s]\n", errno, + strerror(errno)); + return -errno; + } + if (report > 0) { + pout(" scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n" + " info=0x%x duration=%d milliseconds\n", io_hdr.status, + io_hdr.host_status, io_hdr.driver_status, io_hdr.info, + io_hdr.duration); + if (report > 1) { + if (DXFER_FROM_DEVICE == iop->dxfer_dir) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char*)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + } + } + iop->resid = io_hdr.resid; + iop->scsi_status = io_hdr.status; + + if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */ + int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status); + + if (0 != io_hdr.host_status) { + if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) || + (LSCSI_DID_BUS_BUSY == io_hdr.host_status) || + (LSCSI_DID_TIME_OUT == io_hdr.host_status)) + return -ETIMEDOUT; + else + return -EIO; /* catch all */ + } + if (0 != masked_driver_status) { + if (LSCSI_DRIVER_TIMEOUT == masked_driver_status) + return -ETIMEDOUT; + else if (LSCSI_DRIVER_SENSE != masked_driver_status) + return -EIO; + } + if (LSCSI_DRIVER_SENSE == masked_driver_status) + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + iop->resp_sense_len = io_hdr.sb_len_wr; + if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && + iop->sensep && (iop->resp_sense_len > 0)) { + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", + (int)iop->resp_sense_len); + dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1); + } + } + if (report) { + if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { + pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", + iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12], + iop->sensep[13]); + } + else + pout(" status=0x%x\n", iop->scsi_status); + } + } + return 0; +#endif +} + +struct linux_ioctl_send_command +{ + int inbufsize; + int outbufsize; + UINT8 buff[MAX_DXFER_LEN + 16]; +}; + +/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't + * support: CDB length (guesses it from opcode), resid and timeout. + * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout + * to 2 hours in order to allow long foreground extended self tests. */ +static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +{ + struct linux_ioctl_send_command wrk; + int status, buff_offset; + size_t len; + + memcpy(wrk.buff, iop->cmnd, iop->cmnd_len); + buff_offset = iop->cmnd_len; + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char *)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + switch (iop->dxfer_dir) { + case DXFER_NONE: + wrk.inbufsize = 0; + wrk.outbufsize = 0; + break; + case DXFER_FROM_DEVICE: + wrk.inbufsize = 0; + if (iop->dxfer_len > MAX_DXFER_LEN) + return -EINVAL; + wrk.outbufsize = iop->dxfer_len; + break; + case DXFER_TO_DEVICE: + if (iop->dxfer_len > MAX_DXFER_LEN) + return -EINVAL; + memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len); + wrk.inbufsize = iop->dxfer_len; + wrk.outbufsize = 0; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk); + if (-1 == status) { + if (report) + pout(" SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n", + errno, strerror(errno)); + return -errno; + } + if (0 == status) { + if (report > 0) + pout(" status=0\n"); + if (DXFER_FROM_DEVICE == iop->dxfer_dir) { + memcpy(iop->dxferp, wrk.buff, iop->dxfer_len); + if (report > 1) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((const char*)iop->dxferp, + (trunc ? 256 : iop->dxfer_len) , 1); + } + } + return 0; + } + iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */ + if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf)) + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ? + SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len; + if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && + iop->sensep && (len > 0)) { + memcpy(iop->sensep, wrk.buff, len); + iop->resp_sense_len = len; + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", (int)len); + dStrHex((const char *)wrk.buff, len , 1); + } + } + if (report) { + if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { + pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff, + wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]); + } + else + pout(" status=0x%x\n", status); + } + if (iop->scsi_status > 0) + return 0; + else { + if (report > 0) + pout(" ioctl status=0x%x but scsi status=0, fail with EIO\n", + status); + return -EIO; /* give up, assume no device there */ + } +} + +/* SCSI command transmission interface function, linux version. + * Returns 0 if SCSI command successfully launched and response + * received. Even when 0 is returned the caller should check + * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings + * (e.g. CHECK CONDITION). If the SCSI command could not be issued + * (e.g. device not present or timeout) or some other problem + * (e.g. timeout) then returns a negative errno value */ +int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +{ + int res; + + /* implementation relies on static sg_io_state variable. If not + * previously set tries the SG_IO ioctl. If that succeeds assume + * that SG_IO ioctl functional. If it fails with an errno value + * other than ENODEV (no device) or permission then assume + * SCSI_IOCTL_SEND_COMMAND is the only option. */ + switch (sg_io_state) { + case SG_IO_PRESENT_UNKNOWN: + /* ignore report argument */ + if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, 1))) { + sg_io_state = SG_IO_PRESENT_YES; + return 0; + } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res)) + return res; /* wait until we see a device */ + sg_io_state = SG_IO_PRESENT_NO; + /* drop through by design */ + case SG_IO_PRESENT_NO: + return sisc_cmnd_io(dev_fd, iop, report); + case SG_IO_PRESENT_YES: + return sg_io_cmnd_io(dev_fd, iop, report, 0); + default: + pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); + sg_io_state = SG_IO_PRESENT_UNKNOWN; + return -EIO; /* report error and reset state */ + } +} + +// >>>>>> End of general SCSI specific linux code + + +// prototype +void printwarning(smart_command_set command); + +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux interface to the 3ware 3w-xxxx driver. It allows ATA +// commands to be passed through the SCSI driver. +// DETAILED DESCRIPTION OF ARGUMENTS +// fd: is the file descriptor provided by open() +// disknum is the disk number (0 to 15) in the RAID array +// escalade_type indicates the type of controller type, and if scsi or char interface is used +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + + +/* 512 is the max payload size: increase if needed */ +#define BUFFER_LEN_678K ( sizeof(TW_Ioctl) ) // 1044 unpacked, 1041 packed +#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1 ) // 1539 unpacked, 1536 packed +#define BUFFER_LEN_9000 ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed +#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) ) + +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ + + // return value and buffer for ioctl() + int ioctlreturn, readdata=0; + + // Used by both the SCSI and char interfaces + TW_Passthru *passthru=NULL; + char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; + + // only used for SCSI device interface + TW_Ioctl *tw_ioctl=NULL; + TW_Output *tw_output=NULL; + + // only used for 6000/7000/8000 char device interface + TW_New_Ioctl *tw_ioctl_char=NULL; + + // only used for 9000 character device interface + TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL; + + memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); + + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { + tw_ioctl_apache = (TW_Ioctl_Buf_Apache *)ioctl_buffer; + tw_ioctl_apache->driver_command.control_code = TW_IOCTL_FIRMWARE_PASS_THROUGH; + tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */ + passthru = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand); + } + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { + tw_ioctl_char = (TW_New_Ioctl *)ioctl_buffer; + tw_ioctl_char->data_buffer_length = 512; + passthru = (TW_Passthru *)&(tw_ioctl_char->firmware_command); + } + else if (escalade_type==CONTROLLER_3WARE_678K) { + tw_ioctl = (TW_Ioctl *)ioctl_buffer; + tw_ioctl->cdb[0] = TW_IOCTL; + tw_ioctl->opcode = TW_ATA_PASSTHRU; + tw_ioctl->input_length = 512; // correct even for non-data commands + tw_ioctl->output_length = 512; // correct even for non-data commands + tw_output = (TW_Output *)tw_ioctl; + passthru = (TW_Passthru *)&(tw_ioctl->input_data); + } + else { + pout("Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum); + errno=ENOSYS; + return -1; + } + + // Same for (almost) all commands - but some reset below + passthru->byte0.opcode = TW_OP_ATA_PASSTHRU; + passthru->request_id = 0xFF; + passthru->byte3.aport = disknum; + passthru->byte3.host_id = 0; + passthru->status = 0; + passthru->flags = 0x1; + passthru->drive_head = 0x0; + passthru->sector_num = 0; + + // All SMART commands use this CL/CH signature. These are magic + // values from the ATA specifications. + passthru->cylinder_lo = 0x4F; + passthru->cylinder_hi = 0xC2; + + // SMART ATA COMMAND REGISTER value + passthru->command = ATA_SMART_CMD; + + // Is this a command that reads or returns 512 bytes? + // passthru->param values are: + // 0x0 - non data command without TFR write check, + // 0x8 - non data command with TFR write check, + // 0xD - data command that returns data to host from device + // 0xF - data command that writes data from host to device + // passthru->size values are 0x5 for non-data and 0x07 for data + if (command == READ_VALUES || + command == READ_THRESHOLDS || + command == READ_LOG || + command == IDENTIFY || + command == WRITE_LOG ) { + readdata=1; + passthru->byte0.sgloff = 0x5; + passthru->size = 0x7; + passthru->param = 0xD; + passthru->sector_count = 0x1; + // For 64-bit to work correctly, up the size of the command packet + // in dwords by 1 to account for the 64-bit single sgl 'address' + // field. Note that this doesn't agree with the typedefs but it's + // right (agree with kernel driver behavior/typedefs). + if (escalade_type==CONTROLLER_3WARE_9000_CHAR && sizeof(long)==8) + passthru->size++; + } + else { + // Non data command -- but doesn't use large sector + // count register values. + passthru->byte0.sgloff = 0x0; + passthru->size = 0x5; + passthru->param = 0x8; + passthru->sector_count = 0x0; + } + + // Now set ATA registers depending upon command + switch (command){ + case CHECK_POWER_MODE: + passthru->command = ATA_CHECK_POWER_MODE; + passthru->features = 0; + passthru->cylinder_lo = 0; + passthru->cylinder_hi = 0; + break; + case READ_VALUES: + passthru->features = ATA_SMART_READ_VALUES; + break; + case READ_THRESHOLDS: + passthru->features = ATA_SMART_READ_THRESHOLDS; + break; + case READ_LOG: + passthru->features = ATA_SMART_READ_LOG_SECTOR; + // log number to return + passthru->sector_num = select; + break; + case WRITE_LOG: + if (escalade_type == CONTROLLER_3WARE_9000_CHAR) + memcpy((unsigned char *)tw_ioctl_apache->data_buffer, data, 512); + else if (escalade_type == CONTROLLER_3WARE_678K_CHAR) + memcpy((unsigned char *)tw_ioctl_char->data_buffer, data, 512); + else { + // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE + // memcpy(tw_output->output_data, data, 512); + printwarning(command); + errno=ENOTSUP; + return -1; + } + readdata=0; + passthru->features = ATA_SMART_WRITE_LOG_SECTOR; + passthru->sector_count = 1; + passthru->sector_num = select; + passthru->param = 0xF; // PIO data write + break; + case IDENTIFY: + // ATA IDENTIFY DEVICE + passthru->command = ATA_IDENTIFY_DEVICE; + passthru->features = 0; + passthru->cylinder_lo = 0; + passthru->cylinder_hi = 0; + break; + case PIDENTIFY: + // 3WARE controller can NOT have packet device internally + pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum); + pout("Note: /dev/sdX many need to be replaced with /dev/tweN or /dev/twaN\n"); + errno=ENODEV; + return -1; + case ENABLE: + passthru->features = ATA_SMART_ENABLE; + break; + case DISABLE: + passthru->features = ATA_SMART_DISABLE; + break; + case AUTO_OFFLINE: + passthru->features = ATA_SMART_AUTO_OFFLINE; + // Enable or disable? + passthru->sector_count = select; + break; + case AUTOSAVE: + passthru->features = ATA_SMART_AUTOSAVE; + // Enable or disable? + passthru->sector_count = select; + break; + case IMMEDIATE_OFFLINE: + passthru->features = ATA_SMART_IMMEDIATE_OFFLINE; + // What test type to run? + passthru->sector_num = select; + break; + case STATUS_CHECK: + passthru->features = ATA_SMART_STATUS; + break; + case STATUS: + // This is JUST to see if SMART is enabled, by giving SMART status + // command. But it doesn't say if status was good, or failing. + // See below for the difference. + passthru->features = ATA_SMART_STATUS; + break; + default: + pout("Unrecognized command %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", command, disknum); + errno=ENOSYS; + return -1; + } + + // Now send the command down through an ioctl() + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) + ioctlreturn=ioctl(fd, TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache); + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) + ioctlreturn=ioctl(fd, TW_CMD_PACKET_WITH_DATA, tw_ioctl_char); + else + ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl); + + // Deal with the different error cases + if (ioctlreturn) { + if (CONTROLLER_3WARE_678K==escalade_type && ((command==AUTO_OFFLINE || command==AUTOSAVE) && select)){ + // error here is probably a kernel driver whose version is too old + printwarning(command); + errno=ENOTSUP; + } + if (!errno) + errno=EIO; + return -1; + } + + // The passthru structure is valid after return from an ioctl if: + // - we are using the character interface OR + // - we are using the SCSI interface and this is a NON-READ-DATA command + // For SCSI interface, note that we set passthru to a different + // value after ioctl(). + if (CONTROLLER_3WARE_678K==escalade_type) { + if (readdata) + passthru=NULL; + else + passthru=(TW_Passthru *)&(tw_output->output_data); + } + + // See if the ATA command failed. Now that we have returned from + // the ioctl() call, if passthru is valid, then: + // - passthru->status contains the 3ware controller STATUS + // - passthru->command contains the ATA STATUS register + // - passthru->features contains the ATA ERROR register + // + // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS + // If bit 0 (error bit) is set, then ATA ERROR register is valid. + // While we *might* decode the ATA ERROR register, at the moment it + // doesn't make much sense: we don't care in detail why the error + // happened. + + if (passthru && (passthru->status || (passthru->command & 0x21))) { + errno=EIO; + return -1; + } + + // If this is a read data command, copy data to output buffer + if (readdata) { + if (escalade_type==CONTROLLER_3WARE_9000_CHAR) + memcpy(data, (unsigned char *)tw_ioctl_apache->data_buffer, 512); + else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) + memcpy(data, (unsigned char *)tw_ioctl_char->data_buffer, 512); + else + memcpy(data, tw_output->output_data, 512); + } + + // For STATUS_CHECK, we need to check register values + if (command==STATUS_CHECK) { + + // To find out if the SMART RETURN STATUS is good or failing, we + // need to examine the values of the Cylinder Low and Cylinder + // High Registers. + + unsigned short cyl_lo=passthru->cylinder_lo; + unsigned short cyl_hi=passthru->cylinder_hi; + + // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. + if (cyl_lo==0x4F && cyl_hi==0xC2) + return 0; + + // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL + if (cyl_lo==0xF4 && cyl_hi==0x2C) + return 1; + + // Any other values mean that something has gone wrong with the command + if (CONTROLLER_3WARE_678K==escalade_type) { + printwarning(command); + errno=ENOSYS; + return 0; + } + else { + errno=EIO; + return -1; + } + } + + // copy sector count register (one byte!) to return data + if (command==CHECK_POWER_MODE) + *data=*(char *)&(passthru->sector_count); + + // look for nonexistent devices/ports + if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { + errno=ENODEV; + return -1; + } + + return 0; +} + + + +int marvell_command_interface(int device, + smart_command_set command, + int select, + char *data) { + typedef struct { + int inlen; + int outlen; + char cmd[540]; + } mvsata_scsi_cmd; + + int copydata = 0; + mvsata_scsi_cmd smart_command; + unsigned char *buff = (unsigned char *)&smart_command.cmd[6]; + // See struct hd_drive_cmd_hdr in hdreg.h + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA SECTOR NUMBER REGISTER + // buff[2]: ATA FEATURES REGISTER + // buff[3]: ATA SECTOR COUNT REGISTER + + // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) + memset(&smart_command, 0, sizeof(smart_command)); + smart_command.inlen = 540; + smart_command.outlen = 540; + smart_command.cmd[0] = 0xC; //Vendor-specific code + smart_command.cmd[4] = 6; //command length + + buff[0] = ATA_SMART_CMD; + switch (command){ + case CHECK_POWER_MODE: + buff[0]=ATA_CHECK_POWER_MODE; + break; + case READ_VALUES: + buff[2]=ATA_SMART_READ_VALUES; + copydata=buff[3]=1; + break; + case READ_THRESHOLDS: + buff[2]=ATA_SMART_READ_THRESHOLDS; + copydata=buff[1]=buff[3]=1; + break; + case READ_LOG: + buff[2]=ATA_SMART_READ_LOG_SECTOR; + buff[1]=select; + copydata=buff[3]=1; + break; + case IDENTIFY: + buff[0]=ATA_IDENTIFY_DEVICE; + copydata=buff[3]=1; + break; + case PIDENTIFY: + buff[0]=ATA_IDENTIFY_PACKET_DEVICE; + copydata=buff[3]=1; + break; + case ENABLE: + buff[2]=ATA_SMART_ENABLE; + buff[1]=1; + break; + case DISABLE: + buff[2]=ATA_SMART_DISABLE; + buff[1]=1; + break; + case STATUS: + case STATUS_CHECK: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + buff[2] = ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + buff[2]=ATA_SMART_AUTO_OFFLINE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + buff[2]=ATA_SMART_AUTOSAVE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; + buff[1]=select; + break; + default: + pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command); + exit(1); + break; + } + // There are two different types of ioctls(). The HDIO_DRIVE_TASK + // one is this: + // We are now doing the HDIO_DRIVE_CMD type ioctl. + if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command)) + return -1; + + if (command==CHECK_POWER_MODE) { + // LEON -- CHECK THIS PLEASE. THIS SHOULD BE THE SECTOR COUNT + // REGISTER, AND IT MIGHT BE buff[2] NOT buff[3]. Bruce + data[0]=buff[3]; + return 0; + } + + // Always succeed on a SMART status, as a disk that failed returned + // buff[4]=0xF4, buff[5]=0x2C, i.e. "Bad SMART status" (see below). + if (command == STATUS) + return 0; + //Data returned is starting from 0 offset + if (command == STATUS_CHECK) + { + // Cyl low and Cyl high unchanged means "Good SMART status" + if (buff[4] == 0x4F && buff[5] == 0xC2) + return 0; + // These values mean "Bad SMART status" + if (buff[4] == 0xF4 && buff[5] == 0x2C) + return 1; + // We haven't gotten output that makes sense; print out some debugging info + syserror("Error SMART Status command failed"); + pout("Please get assistance from %s\n",PACKAGE_BUGREPORT); + pout("Register values returned from SMART Status command are:\n"); + pout("CMD =0x%02x\n",(int)buff[0]); + pout("FR =0x%02x\n",(int)buff[1]); + pout("NS =0x%02x\n",(int)buff[2]); + pout("SC =0x%02x\n",(int)buff[3]); + pout("CL =0x%02x\n",(int)buff[4]); + pout("CH =0x%02x\n",(int)buff[5]); + pout("SEL=0x%02x\n",(int)buff[6]); + return -1; + } + + if (copydata) + memcpy(data, buff, 512); + return 0; +} + + +// Utility function for printing warnings +void printwarning(smart_command_set command){ + static int printed[4]={0,0,0,0}; + const char* message= + "can not be passed through the 3ware 3w-xxxx driver. This can be fixed by\n" + "applying a simple 3w-xxxx driver patch that can be found here:\n" + PACKAGE_HOMEPAGE "\n" + "Alternatively, upgrade your 3w-xxxx driver to version 1.02.00.037 or greater.\n\n"; + + if (command==AUTO_OFFLINE && !printed[0]) { + printed[0]=1; + pout("The SMART AUTO-OFFLINE ENABLE command (smartmontools -o on option/Directive)\n%s", message); + } + else if (command==AUTOSAVE && !printed[1]) { + printed[1]=1; + pout("The SMART AUTOSAVE ENABLE command (smartmontools -S on option/Directive)\n%s", message); + } + else if (command==STATUS_CHECK && !printed[2]) { + printed[2]=1; + pout("The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n%s", message); + } + else if (command==WRITE_LOG && !printed[3]) { + printed[3]=1; + pout("The SMART WRITE LOG command (smartmontools -t selective) only supported via char /dev/tw[ae] interface\n"); + } + + return; +} + +// Guess device type (ata or scsi) based on device name (Linux +// specific) SCSI device name in linux can be sd, sr, scd, st, nst, +// osst, nosst and sg. +static const char * lin_dev_prefix = "/dev/"; +static const char * lin_dev_ata_disk_plus = "h"; +static const char * lin_dev_ata_devfs_disk_plus = "ide/"; +static const char * lin_dev_scsi_devfs_disk_plus = "scsi/"; +static const char * lin_dev_scsi_disk_plus = "s"; +static const char * lin_dev_scsi_tape1 = "ns"; +static const char * lin_dev_scsi_tape2 = "os"; +static const char * lin_dev_scsi_tape3 = "nos"; +static const char * lin_dev_3ware_9000_char = "twa"; +static const char * lin_dev_3ware_678k_char = "twe"; + +int guess_device_type(const char * dev_name) { + int len; + int dev_prefix_len = strlen(lin_dev_prefix); + + // if dev_name null, or string length zero + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + // Remove the leading /dev/... if it's there + if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + // if nothing else in the string, unrecognized + return CONTROLLER_UNKNOWN; + // else advance pointer to following characters + dev_name += dev_prefix_len; + } + + // form /dev/h* or h* + if (!strncmp(lin_dev_ata_disk_plus, dev_name, + strlen(lin_dev_ata_disk_plus))) + return CONTROLLER_ATA; + + // form /dev/ide/* or ide/* + if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name, + strlen(lin_dev_ata_devfs_disk_plus))) + return CONTROLLER_ATA; + + // form /dev/s* or s* + if (!strncmp(lin_dev_scsi_disk_plus, dev_name, + strlen(lin_dev_scsi_disk_plus))) + return CONTROLLER_SCSI; + + // form /dev/scsi/* or scsi/* + if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name, + strlen(lin_dev_scsi_devfs_disk_plus))) + return CONTROLLER_SCSI; + + // form /dev/ns* or ns* + if (!strncmp(lin_dev_scsi_tape1, dev_name, + strlen(lin_dev_scsi_tape1))) + return CONTROLLER_SCSI; + + // form /dev/os* or os* + if (!strncmp(lin_dev_scsi_tape2, dev_name, + strlen(lin_dev_scsi_tape2))) + return CONTROLLER_SCSI; + + // form /dev/nos* or nos* + if (!strncmp(lin_dev_scsi_tape3, dev_name, + strlen(lin_dev_scsi_tape3))) + return CONTROLLER_SCSI; + + // form /dev/twa* + if (!strncmp(lin_dev_3ware_9000_char, dev_name, + strlen(lin_dev_3ware_9000_char))) + return CONTROLLER_3WARE_9000_CHAR; + + // form /dev/twe* + if (!strncmp(lin_dev_3ware_678k_char, dev_name, + strlen(lin_dev_3ware_678k_char))) + return CONTROLLER_3WARE_678K_CHAR; + + // we failed to recognize any of the forms + return CONTROLLER_UNKNOWN; +} + + +#if 0 + +[ed@firestorm ed]$ ls -l /dev/discs +total 0 +lr-xr-xr-x 1 root root 30 Dec 31 1969 disc0 -> ../ide/host2/bus0/target0/lun0/ +lr-xr-xr-x 1 root root 30 Dec 31 1969 disc1 -> ../ide/host2/bus1/target0/lun0/ +[ed@firestorm ed]$ ls -l dev/ide/host*/bus*/target*/lun*/disc +ls: dev/ide/host*/bus*/target*/lun*/disc: No such file or directory +[ed@firestorm ed]$ ls -l /dev/ide/host*/bus*/target*/lun*/disc +brw------- 1 root root 33, 0 Dec 31 1969 /dev/ide/host2/bus0/target0/lun0/disc +brw------- 1 root root 34, 0 Dec 31 1969 /dev/ide/host2/bus1/target0/lun0/disc +[ed@firestorm ed]$ ls -l /dev/ide/c*b*t*u* +ls: /dev/ide/c*b*t*u*: No such file or directory +[ed@firestorm ed]$ +Script done on Fri Nov 7 13:46:28 2003 + +#endif diff --git a/os_linux.h b/os_linux.h new file mode 100644 index 0000000..71a4102 --- /dev/null +++ b/os_linux.h @@ -0,0 +1,390 @@ +/* + * os_linux.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 Bruce Allen + * + * Derived from code that was + * + * Written By: Adam Radford + * Modifications By: Joel Jacobson + * Arnaldo Carvalho de Melo + * Brad Strand + * + * Copyright (C) 1999-2003 3ware Inc. + * + * Kernel compatablity By: Andre Hedrick + * Non-Copyright (C) 2000 Andre Hedrick + * + * + * 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/ + * + */ + + +#ifndef OS_LINUX_H_ +#define OS_LINUX_H_ + +#define OS_LINUX_H_CVSID "$Id: os_linux.h,v 1.24 2006/04/12 14:54:28 ballen4705 Exp $\n" + +/* + The following definitions/macros/prototypes are used for three + different interfaces, referred to as "the three cases" below. + CONTROLLER_3WARE_678K -- 6000, 7000, and 8000 controllers via /dev/sd? + CONTROLLER_3WARE_678K_CHAR -- 6000, 7000, and 8000 controllers via /dev/twe? + CONTROLLER_3WARE_9000_CHAR -- 9000 controllers via /dev/twa? +*/ + +// USED FOR ALL THREE CASES + +#define u32 unsigned int +#define TW_OP_ATA_PASSTHRU 0x11 +#define MAX(x,y) ( (x)>(y)?(x):(y) ) + +#pragma pack(1) +/* Scatter gather list entry */ +typedef struct TAG_TW_SG_Entry { + unsigned int address; + unsigned int length; +} TW_SG_Entry; + +/* Command header for ATA pass-thru. Note that for different + drivers/interfaces the length of sg_list (here TW_ATA_PASS_SGL_MAX) + is different. But it can be taken as same for all three cases + because it's never used to define any other structures, and we + never use anything in the sg_list or beyond! */ + +#define TW_ATA_PASS_SGL_MAX 60 + +typedef struct TAG_TW_Passthru { + struct { + unsigned char opcode:5; + unsigned char sgloff:3; + } byte0; + unsigned char size; + unsigned char request_id; + struct { + unsigned char aport:4; + unsigned char host_id:4; + } byte3; + unsigned char status; // On return, contains 3ware STATUS register + unsigned char flags; + unsigned short param; + unsigned short features; // On return, contains ATA ERROR register + unsigned short sector_count; + unsigned short sector_num; + unsigned short cylinder_lo; + unsigned short cylinder_hi; + unsigned char drive_head; + unsigned char command; // On return, contains ATA STATUS register + TW_SG_Entry sg_list[TW_ATA_PASS_SGL_MAX]; + unsigned char padding[12]; +} TW_Passthru; + +// the following are for the SCSI interface only + +// Ioctl buffer: Note that this defn has changed in kernel tree... +// Total size is 1041 bytes -- this is really weird + +#define TW_IOCTL 0x80 +#define TW_ATA_PASSTHRU 0x1e + +// Adam -- should this be #pramga packed? Otherwise table_id gets +// moved for byte alignment. Without packing, input passthru for SCSI +// ioctl is 31 bytes in. With packing it is 30 bytes in. +typedef struct TAG_TW_Ioctl { + int input_length; + int output_length; + unsigned char cdb[16]; + unsigned char opcode; + // This one byte of padding is missing from the typedefs in the + // kernel code, but it is indeed present. We put it explicitly + // here, so that the structure can be packed. Adam agrees with + // this. + unsigned char packing; + unsigned short table_id; + unsigned char parameter_id; + unsigned char parameter_size_bytes; + unsigned char unit_index; + // Size up to here is 30 bytes + 1 padding! + unsigned char input_data[499]; + // Reserve lots of extra space for commands that set Sector Count + // register to large values + unsigned char output_data[512]; // starts 530 bytes in! + // two more padding bytes here if structure NOT packed. +} TW_Ioctl; + +/* Ioctl buffer output -- SCSI interface only! */ +typedef struct TAG_TW_Output { + int padding[2]; + char output_data[512]; +} TW_Output; + +// What follows is needed for 9000 char interface only + +#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 +#define TW_MAX_SGL_LENGTH_9000 61 + +typedef struct TAG_TW_Ioctl_Driver_Command_9000 { + unsigned int control_code; + unsigned int status; + unsigned int unique_id; + unsigned int sequence_id; + unsigned int os_specific; + unsigned int buffer_length; +} TW_Ioctl_Driver_Command_9000; + +/* Command Packet */ +typedef struct TW_Command_9000 { + /* First DWORD */ + struct { + unsigned char opcode:5; + unsigned char sgl_offset:3; + } byte0; + unsigned char size; + unsigned char request_id; + struct { + unsigned char unit:4; + unsigned char host_id:4; + } byte3; + /* Second DWORD */ + unsigned char status; + unsigned char flags; + union { + unsigned short block_count; + unsigned short parameter_count; + unsigned short message_credits; + } byte6; + union { + struct { + u32 lba; + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH_9000]; + u32 padding; + } io; + struct { + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH_9000]; + u32 padding[2]; + } param; + struct { + u32 response_queue_pointer; + u32 padding[125]; /* pad entire structure to 512 bytes */ + } init_connection; + struct { + char version[504]; + } ioctl_miniport_version; + } byte8; +} TW_Command_9000; + +/* Command Packet for 9000+ controllers */ +typedef struct TAG_TW_Command_Apache { + struct { + unsigned char opcode:5; + unsigned char reserved:3; + } command; + unsigned char unit; + unsigned short request_id; + unsigned char sense_length; + unsigned char sgl_offset; + unsigned short sgl_entries; + unsigned char cdb[16]; + TW_SG_Entry sg_list[TW_MAX_SGL_LENGTH_9000]; +} TW_Command_Apache; + +/* New command packet header */ +typedef struct TAG_TW_Command_Apache_Header { + unsigned char sense_data[18]; + struct { + char reserved[4]; + unsigned short error; + unsigned char status; + struct { + unsigned char severity:3; + unsigned char reserved:5; + } substatus_block; + } status_block; + unsigned char err_specific_desc[102]; +} TW_Command_Apache_Header; + +/* This struct is a union of the 2 command packets */ +typedef struct TAG_TW_Command_Full_9000 { + TW_Command_Apache_Header header; + union { + TW_Command_9000 oldcommand; + TW_Command_Apache newcommand; + } command; + unsigned char padding[384]; /* Pad to 1024 bytes */ +} TW_Command_Full_9000; + +typedef struct TAG_TW_Ioctl_Apache { + TW_Ioctl_Driver_Command_9000 driver_command; + char padding[488]; + TW_Command_Full_9000 firmware_command; + char data_buffer[1]; + // three bytes of padding here if structure not packed! +} TW_Ioctl_Buf_Apache; + + + +// START OF DEFINITIONS FOR THE CHARACTER INTERFACE TO THE +// 6000/7000/8000 drivers + +#define TW_MAX_SGL_LENGTH 62 +#define TW_CMD_PACKET_WITH_DATA 0x1f + +/* Command Packet */ +typedef struct TW_Command { + /* First DWORD */ + struct { + unsigned char opcode:5; + unsigned char sgl_offset:3; + } byte0; + unsigned char size; + unsigned char request_id; + struct { + unsigned char unit:4; + unsigned char host_id:4; + } byte3; + /* Second DWORD */ + unsigned char status; + unsigned char flags; + union { + unsigned short block_count; + unsigned short parameter_count; + unsigned short message_credits; + } byte6; + union { + struct { + u32 lba; + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH]; + u32 padding; /* pad to 512 bytes */ + } io; + struct { + TW_SG_Entry sgl[TW_MAX_SGL_LENGTH]; + u32 padding[2]; + } param; + struct { + u32 response_queue_pointer; + u32 padding[125]; + } init_connection; + struct { + char version[504]; + } ioctl_miniport_version; + } byte8; +} TW_Command; + +typedef struct TAG_TW_New_Ioctl { + unsigned int data_buffer_length; + unsigned char padding [508]; + TW_Command firmware_command; + char data_buffer[1]; + // three bytes of padding here +} TW_New_Ioctl; +#pragma pack() + +#if 0 +// Useful for checking/understanding packing of 3ware data structures +// above. +void my(int x, char *y){ + printf("The size of %30s is: %5d\n",y, x); + return; +} + +int main() { + TW_Ioctl tmp; + my(sizeof(TW_SG_Entry),"TW_SG_Entry"); + my(sizeof(TW_Passthru),"TW_Passthru"); + my(sizeof(TW_Ioctl),"TW_Ioctl"); + my(sizeof(TW_Output),"TW_Output"); + my(sizeof(TW_Ioctl_Driver_Command_9000),"TW_Ioctl_Driver_Command_9000"); + my(sizeof(TW_Command_9000),"TW_Command_9000"); + my(sizeof(TW_Command_Apache),"TW_Command_Apache"); + my(sizeof(TW_Command_Apache_Header),"TW_Command_Apache_Header"); + my(sizeof(TW_Command_Full_9000),"TW_Command_Full_9000"); + my(sizeof(TW_Ioctl_Buf_Apache),"TW_Ioctl_Buf_Apache"); + my(sizeof(TW_Command),"TW_Command"); + my(sizeof(TW_New_Ioctl),"TW_New_Ioctl"); + printf("TW_Ioctl.table_id - start = %d (irrelevant)\n", + (void *)&tmp.table_id - (void *)&tmp); + printf("TW_Ioctl.input_data - start = %d (input passthru location)\n", + (void *)&tmp.input_data - (void *)&tmp); + printf("TW_Ioctl.output_data - start = %d (irrelevant)\n", + (void *)&tmp.output_data - (void *)&tmp); + return 0; +} +#endif + +// The following definitions are from hdreg.h in the kernel source +// tree. They don't carry any Copyright statements, but I think they +// are primarily from Mark Lord and Andre Hedrick. +typedef unsigned char task_ioreg_t; + +typedef struct hd_drive_task_hdr { + task_ioreg_t data; + task_ioreg_t feature; + task_ioreg_t sector_count; + task_ioreg_t sector_number; + task_ioreg_t low_cylinder; + task_ioreg_t high_cylinder; + task_ioreg_t device_head; + task_ioreg_t command; +} task_struct_t; + +typedef union ide_reg_valid_s { + unsigned all : 16; + struct { + unsigned data : 1; + unsigned error_feature : 1; + unsigned sector : 1; + unsigned nsector : 1; + unsigned lcyl : 1; + unsigned hcyl : 1; + unsigned select : 1; + unsigned status_command : 1; + unsigned data_hob : 1; + unsigned error_feature_hob : 1; + unsigned sector_hob : 1; + unsigned nsector_hob : 1; + unsigned lcyl_hob : 1; + unsigned hcyl_hob : 1; + unsigned select_hob : 1; + unsigned control_hob : 1; + } b; +} ide_reg_valid_t; + +typedef struct ide_task_request_s { + task_ioreg_t io_ports[8]; + task_ioreg_t hob_ports[8]; + ide_reg_valid_t out_flags; + ide_reg_valid_t in_flags; + int data_phase; + int req_cmd; + unsigned long out_size; + unsigned long in_size; +} ide_task_request_t; + +#define TASKFILE_NO_DATA 0x0000 +#define TASKFILE_IN 0x0001 +#define TASKFILE_OUT 0x0004 +#define HDIO_DRIVE_TASK_HDR_SIZE 8*sizeof(task_ioreg_t) +#define IDE_DRIVE_TASK_NO_DATA 0 +#define IDE_DRIVE_TASK_IN 2 +#define IDE_DRIVE_TASK_OUT 3 +#define HDIO_DRIVE_CMD 0x031f +#define HDIO_DRIVE_TASK 0x031e +#define HDIO_DRIVE_TASKFILE 0x031d +#define HDIO_GET_IDENTITY 0x030d + +#endif /* OS_LINUX_H_ */ diff --git a/os_netbsd.c b/os_netbsd.c new file mode 100644 index 0000000..4f6cee0 --- /dev/null +++ b/os_netbsd.c @@ -0,0 +1,447 @@ +/* + * os_netbsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 Sergey Svishchev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_netbsd.h" +#include + +const char *os_XXXX_c_cvsid = "$Id: os_netbsd.c,v 1.15 2006/04/12 14:54:28 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_NETBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* global variable holding byte count of allocated memory */ +extern long long bytes; + +enum warnings { + BAD_SMART, NO_3WARE, MAX_MSG +}; + +/* Utility function for printing warnings */ +void +printwarning(int msgNo, const char *extra) +{ + static int printed[] = {0, 0}; + static const char *message[] = { + "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n", + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s", extra); + } + } + return; +} + +static const char *net_dev_prefix = "/dev/"; +static const char *net_dev_ata_disk = "wd"; +static const char *net_dev_scsi_disk = "sd"; +static const char *net_dev_scsi_tape = "enrst"; + +/* Guess device type(ata or scsi) based on device name */ +int +guess_device_type(const char *dev_name) +{ + int len; + int dev_prefix_len = strlen(net_dev_prefix); + + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + return CONTROLLER_UNKNOWN; + else + dev_name += dev_prefix_len; + } + if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk))) + return CONTROLLER_ATA; + + if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk))) + return CONTROLLER_SCSI; + + if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape))) + return CONTROLLER_SCSI; + + return CONTROLLER_UNKNOWN; +} + +int +get_dev_names(char ***names, const char *prefix) +{ + char *disknames, *p, **mp; + int n = 0; + int sysctl_mib[2]; + size_t sysctl_len; + + *names = NULL; + + sysctl_mib[0] = CTL_HW; + sysctl_mib[1] = HW_DISKNAMES; + if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(disknames = malloc(sysctl_len))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) { + if (strncmp(p, prefix, strlen(prefix))) { + continue; + } + mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2); + if (!mp[n]) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition()); + bytes += strlen(mp[n]) + 1; + n++; + } + + mp = realloc(mp, n * (sizeof(char *))); + bytes += (n) * (sizeof(char *)); + *names = mp; + return n; +} + +int +make_device_names(char ***devlist, const char *name) +{ + if (!strcmp(name, "SCSI")) + return get_dev_names(devlist, net_dev_scsi_disk); + else if (!strcmp(name, "ATA")) + return get_dev_names(devlist, net_dev_ata_disk); + else + return 0; +} + +int +deviceopen(const char *pathname, char *type) +{ + if (!strcmp(type, "SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } else if (!strcmp(type, "ATA")) + return open(pathname, O_RDWR | O_NONBLOCK); + else + return -1; +} + +int +deviceclose(int fd) +{ + return close(fd); +} + +int +marvell_command_interface(int fd, smart_command_set command, int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, int select, char *data) +{ + struct atareq req; + unsigned char inbuf[DEV_BSIZE]; + int retval, copydata = 0; + + memset(&req, 0, sizeof(req)); + memset(&inbuf, 0, sizeof(inbuf)); + + switch (command) { + case READ_VALUES: + req.flags = ATACMD_READ; + req.features = WDSM_RD_DATA; + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.timeout = 1000; + copydata = 1; + break; + case READ_THRESHOLDS: + req.flags = ATACMD_READ; + req.features = WDSM_RD_THRESHOLDS; + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.timeout = 1000; + copydata = 1; + break; + case READ_LOG: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + copydata = 1; + break; + case WRITE_LOG: + memcpy(inbuf, data, 512); + req.flags = ATACMD_WRITE; + req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case IDENTIFY: + req.flags = ATACMD_READ; + req.command = WDCC_IDENTIFY; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case PIDENTIFY: + req.flags = ATACMD_READ; + req.command = ATAPI_IDENTIFY_DEVICE; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case ENABLE: + req.flags = ATACMD_READ; + req.features = WDSM_ENABLE_OPS; + req.command = WDCC_SMART; + req.cylinder = WDSMART_CYL; + req.timeout = 1000; + break; + case DISABLE: + req.flags = ATACMD_READ; + req.features = WDSM_DISABLE_OPS; + req.command = WDCC_SMART; + req.cylinder = WDSMART_CYL; + req.timeout = 1000; + break; + case AUTO_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case AUTOSAVE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.cylinder = WDSMART_CYL; + req.sec_count = 0xf1; + /* to enable autosave */ + req.timeout = 1000; + break; + case IMMEDIATE_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = WDCC_SMART; + req.databuf = inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = WDSMART_CYL; + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case STATUS_CHECK: + /* same command, no HDIO in NetBSD */ + case STATUS: + req.flags = ATACMD_READ; + req.features = WDSM_STATUS; + req.command = WDCC_SMART; + req.cylinder = WDSMART_CYL; + req.timeout = 1000; + break; + case CHECK_POWER_MODE: + req.flags = ATACMD_READREG; + req.command = WDCC_CHECK_PWR; + req.timeout = 1000; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n", command); + errno = ENOSYS; + return -1; + } + + if (command == STATUS_CHECK) { + char buf[512]; + + unsigned const short normal = WDSMART_CYL, failed = 0x2cf4; + + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + /* Cyl low and Cyl high unchanged means "Good SMART status" */ + if (req.cylinder == normal) + return 0; + + /* These values mean "Bad SMART status" */ + if (req.cylinder == failed) + return 1; + + /* We haven't gotten output that makes sense; + * print out some debugging info */ + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num, + (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff), + (int) req.error); + printwarning(BAD_SMART, buf); + return 0; + } + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + if (command == CHECK_POWER_MODE) + data[0] = req.sec_count; + + if (copydata) + memcpy(data, inbuf, 512); + + return 0; +} + +int +escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) +{ + printwarning(NO_3WARE, NULL); + return -1; +} + +int +do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct scsireq sc; + + if (report > 0) { + size_t k; + + const unsigned char *ucp = iop->cmnd; + const char *np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : ""); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } else + pout("]"); + } + memset(&sc, 0, sizeof(sc)); + memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); + sc.cmdlen = iop->cmnd_len; + sc.databuf = iop->dxferp; + sc.datalen = iop->dxfer_len; + sc.senselen = iop->max_sense_len; + sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout); + sc.flags = + (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ : /* XXX */ + (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); + + if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) { + warn("error sending SCSI ccb"); + return -1; + } + iop->resid = sc.datalen - sc.datalen_used; + iop->scsi_status = sc.status; + if (iop->sensep) { + memcpy(iop->sensep, sc.sense, sc.senselen_used); + iop->resp_sense_len = sc.senselen_used; + } + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } + return 0; +} + +/* print examples for smartctl */ +void +print_smartctl_examples() +{ + char p; + + p = 'a' + getrawpartition(); + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#else + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/wd0%c" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#endif + return; +} diff --git a/os_netbsd.h b/os_netbsd.h new file mode 100644 index 0000000..b5ed733 --- /dev/null +++ b/os_netbsd.h @@ -0,0 +1,55 @@ +/* + * os_netbsd.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 Sergey Svishchev + * + * 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/ + * + */ + +#ifndef OS_NETBSD_H_ +#define OS_NETBSD_H_ + +#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h,v 1.9 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include +#include +#include + +#include +#include + +#define ata_smart_selftestlog __netbsd_ata_smart_selftestlog +#include +#if HAVE_DEV_ATA_ATAVAR_H +#include +#endif +#include +#undef ata_smart_selftestlog + +#include +#include +#include + +#ifndef WDSM_RD_THRESHOLDS /* pre-1.6.2 system */ +#define WDSM_RD_THRESHOLDS 0xd1 +#endif +#ifndef WDSMART_CYL +#define WDSMART_CYL 0xc24f +#endif + +#endif /* OS_NETBSD_H_ */ diff --git a/os_openbsd.c b/os_openbsd.c new file mode 100644 index 0000000..33a7039 --- /dev/null +++ b/os_openbsd.c @@ -0,0 +1,448 @@ +/* + * os_openbsd.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 David Snyder + * + * Derived from os_netbsd.c by Sergey Svishchev , Copyright (C) 2003-6 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" +#include "os_openbsd.h" + +const char *os_XXXX_c_cvsid = "$Id: os_openbsd.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_OPENBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* global variable holding byte count of allocated memory */ +extern long long bytes; + +enum warnings { + BAD_SMART, NO_3WARE, MAX_MSG +}; + +/* Utility function for printing warnings */ +void +printwarning(int msgNo, const char *extra) +{ + static int printed[] = {0, 0}; + static const char *message[] = { + "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", + PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n", + }; + + if (msgNo >= 0 && msgNo <= MAX_MSG) { + if (!printed[msgNo]) { + printed[msgNo] = 1; + pout("%s", message[msgNo]); + if (extra) + pout("%s", extra); + } + } + return; +} + +static const char *net_dev_prefix = "/dev/"; +static const char *net_dev_ata_disk = "wd"; +static const char *net_dev_scsi_disk = "sd"; +static const char *net_dev_scsi_tape = "st"; + +/* Guess device type(ata or scsi) based on device name */ +int +guess_device_type(const char *dev_name) +{ + int len; + int dev_prefix_len = strlen(net_dev_prefix); + + if (!dev_name || !(len = strlen(dev_name))) + return CONTROLLER_UNKNOWN; + + if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) { + if (len <= dev_prefix_len) + return CONTROLLER_UNKNOWN; + else + dev_name += dev_prefix_len; + } + if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk))) + return CONTROLLER_ATA; + + if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk))) + return CONTROLLER_SCSI; + + if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape))) + return CONTROLLER_SCSI; + + return CONTROLLER_UNKNOWN; +} + +int +get_dev_names(char ***names, const char *prefix) +{ + char *disknames, *p, **mp; + int n = 0; + int sysctl_mib[2]; + size_t sysctl_len; + + *names = NULL; + + sysctl_mib[0] = CTL_HW; + sysctl_mib[1] = HW_DISKNAMES; + if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(disknames = malloc(sysctl_len))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) { + pout("Failed to get value of sysctl `hw.disknames'\n"); + return -1; + } + if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + for (p = strtok(disknames, ","); p; p = strtok(NULL, ",")) { + if (strncmp(p, prefix, strlen(prefix))) { + continue; + } + mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2); + if (!mp[n]) { + pout("Out of memory constructing scan device list\n"); + return -1; + } + sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition()); + bytes += strlen(mp[n]) + 1; + n++; + } + + mp = realloc(mp, n * (sizeof(char *))); + bytes += (n) * (sizeof(char *)); + *names = mp; + return n; +} + +int +make_device_names(char ***devlist, const char *name) +{ + if (!strcmp(name, "SCSI")) + return get_dev_names(devlist, net_dev_scsi_disk); + else if (!strcmp(name, "ATA")) + return get_dev_names(devlist, net_dev_ata_disk); + else + return 0; +} + +int +deviceopen(const char *pathname, char *type) +{ + if (!strcmp(type, "SCSI")) { + int fd = open(pathname, O_RDWR | O_NONBLOCK); + if (fd < 0 && errno == EROFS) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + return fd; + } else if (!strcmp(type, "ATA")) + return open(pathname, O_RDWR | O_NONBLOCK); + else + return -1; +} + +int +deviceclose(int fd) +{ + return close(fd); +} + +int +marvell_command_interface(int fd, smart_command_set command, int select, char *data) +{ return -1; } + +int +ata_command_interface(int fd, smart_command_set command, int select, char *data) +{ + struct atareq req; + unsigned char inbuf[DEV_BSIZE]; + int retval, copydata = 0; + + memset(&req, 0, sizeof(req)); + memset(&inbuf, 0, sizeof(inbuf)); + + switch (command) { + case READ_VALUES: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_VALUES; + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_THRESHOLDS: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_THRESHOLDS; + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + copydata = 1; + break; + case READ_LOG: + req.flags = ATACMD_READ; + req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + copydata = 1; + break; + case WRITE_LOG: + memcpy(inbuf, data, 512); + req.flags = ATACMD_WRITE; + req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case IDENTIFY: + req.flags = ATACMD_READ; + req.command = WDCC_IDENTIFY; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case PIDENTIFY: + req.flags = ATACMD_READ; + req.command = ATAPI_IDENTIFY_DEVICE; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.timeout = 1000; + copydata = 1; + break; + case ENABLE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_ENABLE; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case DISABLE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_DISABLE; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case AUTO_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case AUTOSAVE: + req.flags = ATACMD_READ; + req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.sec_count = 0xf1; + /* to enable autosave */ + req.timeout = 1000; + break; + case IMMEDIATE_OFFLINE: + /* NOTE: According to ATAPI 4 and UP, this command is obsolete */ + req.flags = ATACMD_READ; + req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */ + req.command = ATAPI_SMART; + req.databuf = (caddr_t) inbuf; + req.datalen = sizeof(inbuf); + req.cylinder = htole16(WDSMART_CYL); + req.sec_num = select; + req.sec_count = 1; + req.timeout = 1000; + break; + case STATUS_CHECK: + /* same command, no HDIO in NetBSD */ + case STATUS: + req.flags = ATACMD_READ; + req.features = ATA_SMART_STATUS; + req.command = ATAPI_SMART; + req.cylinder = htole16(WDSMART_CYL); + req.timeout = 1000; + break; + case CHECK_POWER_MODE: + req.flags = ATACMD_READREG; + req.command = WDCC_CHECK_PWR; + req.timeout = 1000; + break; + default: + pout("Unrecognized command %d in ata_command_interface()\n", command); + errno = ENOSYS; + return -1; + } + + if (command == STATUS_CHECK) { + char buf[512]; + + unsigned const short normal = WDSMART_CYL, failed = 0x2cf4; + + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + /* Cyl low and Cyl high unchanged means "Good SMART status" */ + if (letoh16(req.cylinder) == normal) + return 0; + + /* These values mean "Bad SMART status" */ + if (letoh16(req.cylinder) == failed) + return 1; + + /* We haven't gotten output that makes sense; + * print out some debugging info */ + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num, + (int) (letoh16(req.cylinder) & 0xff), (int) ((letoh16(req.cylinder) >> 8) & 0xff), + (int) req.error); + printwarning(BAD_SMART, buf); + return 0; + } + if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) { + perror("Failed command"); + return -1; + } + if (command == CHECK_POWER_MODE) + data[0] = req.sec_count; + + if (copydata) + memcpy(data, inbuf, 512); + + return 0; +} + +int +escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) +{ + printwarning(NO_3WARE, NULL); + return -1; +} + +int +do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + struct scsireq sc; + + if (report > 0) { + size_t k; + + const unsigned char *ucp = iop->cmnd; + const char *np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : ""); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } else + pout("]"); + } + memset(&sc, 0, sizeof(sc)); + memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); + sc.cmdlen = iop->cmnd_len; + sc.databuf = iop->dxferp; + sc.datalen = iop->dxfer_len; + sc.senselen = iop->max_sense_len; + sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout; /* XXX */ + sc.flags = + (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ : /* XXX */ + (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); + + if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) { + warn("error sending SCSI ccb"); + return -1; + } + iop->resid = sc.datalen - sc.datalen_used; + iop->scsi_status = sc.status; + if (iop->sensep) { + memcpy(iop->sensep, sc.sense, sc.senselen_used); + iop->resp_sense_len = sc.senselen_used; + } + if (report > 0) { + int trunc; + + pout(" status=0\n"); + trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1); + } + return 0; +} + +/* print examples for smartctl */ +void +print_smartctl_examples() +{ + char p; + + p = 'a' + getrawpartition(); + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#else + printf( + " smartctl -a /dev/wd0%c (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n" + " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/wd0%c" + " (Prints Self-Test & Attribute errors)\n", + p, p, p, p + ); +#endif + return; +} diff --git a/os_openbsd.h b/os_openbsd.h new file mode 100644 index 0000000..356343a --- /dev/null +++ b/os_openbsd.h @@ -0,0 +1,55 @@ +/* + * os_openbsd.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 David Snyder + * + * Derived from os_netbsd.c by Sergey Svishchev , Copyright (C) 2003-6 + * + * 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/ + * + */ + +#ifndef OS_OPENBSD_H_ +#define OS_OPENBSD_H_ + +#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n" + +/* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */ +/* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */ +/* Subcommands for SMART (features register) */ +#define WDSMART_CYL 0xc24f + +#include +#include +#include + +#include +#include + +#define ata_smart_selftestlog __openbsd_ata_smart_selftestlog +#include +#if HAVE_DEV_ATA_ATAVAR_H +#include +#endif +#include +#undef ata_smart_selftestlog + +#include +#include +#include + +#endif /* OS_OPENBSD_H_ */ diff --git a/os_solaris.c b/os_solaris.c new file mode 100644 index 0000000..d68197c --- /dev/null +++ b/os_solaris.c @@ -0,0 +1,434 @@ +/* + * os_solaris.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 SAWADA Keiji + * Copyright (C) 2003-6 Casper Dik + * + * 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 +#include +#include +#include +#include +#include +#include + +// These are needed to define prototypes for the functions defined below +#include "config.h" +#include "int64.h" +#include "atacmds.h" +#include "scsicmds.h" +#include "utility.h" + +// This is to include whatever prototypes you define in os_solaris.h +#include "os_solaris.h" + +#define ARGUSED(x) ((void)(x)) + +extern long long bytes; + +static const char *filenameandversion="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $"; + +const char *os_XXXX_c_cvsid="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $" \ +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 +int printedout[2]; +char *unimplemented[2]={ + "ATA command routine ata_command_interface()", + "3ware Escalade Controller command routine escalade_command_interface()", +}; + +int printwarning(int which){ + if (!unimplemented[which]) + return 0; + + if (printedout[which]) + return 1; + + printedout[which]=1; + + pout("\n" + "#######################################################################\n" + "%s NOT IMPLEMENTED under Solaris.\n" + "Please contact " PACKAGE_BUGREPORT " if\n" + "you want to help in porting smartmontools to Solaris.\n" + "#######################################################################\n" + "\n", + unimplemented[which]); + + return 1; +} + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( + " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n\n" + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n" + " (Prints Self-Test & Attribute errors)\n" + ); +#else + printf( + " smartctl -a /dev/rdsk/c0t0d0s0 (Prints all SMART information)\n" + " smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n" + " smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n" + " (Prints Self-Test & Attribute errors)\n" + ); +#endif + return; +} + +static const char *uscsidrvrs[] = { + "sd", + "ssd", + "st" +}; + +static const char *atadrvrs[] = { + "cmdk", + "dad", +}; + +static int +isdevtype(const char *dev_name, const char *table[], int tsize) +{ + char devpath[MAXPATHLEN]; + int i; + char *basename; + + if (realpath(dev_name, devpath) == NULL) + return 0; + + if ((basename = strrchr(devpath, '/')) == NULL) + return 0; + + basename++; + + for (i = 0; i < tsize; i++) { + int l = strlen(table[i]); + if (strncmp(basename, table[i], l) == 0 && basename[l] == '@') + return 1; + } + return 0; +} + +static int +isscsidev(const char *path) +{ + return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *)); +} + +static int +isatadev(const char *path) +{ + return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *)); +} + +// tries to guess device type given the name (a path) +int guess_device_type (const char* dev_name) { + if (isscsidev(dev_name)) + return CONTROLLER_SCSI; + else if (isatadev(dev_name)) + return CONTROLLER_ATA; + else + return CONTROLLER_UNKNOWN; +} + +struct pathlist { + char **names; + int nnames; + int maxnames; +}; + +static int +addpath(const char *path, struct pathlist *res) +{ + if (++res->nnames > res->maxnames) { + res->maxnames += 16; + res->names = realloc(res->names, res->maxnames * sizeof (char *)); + if (res->names == NULL) + return -1; + bytes += 16*sizeof(char *); + } + if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion))) + return -1; + return 0; +} + +static int +grokdir(const char *dir, struct pathlist *res, int testfun(const char *)) +{ + char pathbuf[MAXPATHLEN]; + size_t len; + DIR *dp; + struct dirent *de; + int isdisk = strstr(dir, "dsk") != NULL; + char *p; + + len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir); + if (len >= sizeof (pathbuf)) + return -1; + + dp = opendir(dir); + if (dp == NULL) + return 0; + + while ((de = readdir(dp)) != NULL) { + if (de->d_name[0] == '.') + continue; + + if (strlen(de->d_name) + len >= sizeof (pathbuf)) + continue; + + if (isdisk) { + /* Disk represented by slice 0 */ + p = strstr(de->d_name, "s0"); + /* String doesn't end in "s0\0" */ + if (p == NULL || p[2] != '\0') + continue; + } else { + /* Tape drive represented by the all-digit device */ + for (p = de->d_name; *p; p++) + if (!isdigit((int)(*p))) + break; + if (*p != '\0') + continue; + } + strcpy(&pathbuf[len], de->d_name); + if (testfun(pathbuf)) { + if (addpath(pathbuf, res) == -1) { + closedir(dp); + return -1; + } + } + } + closedir(dp); + + return 0; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number of devices, or -1 if out of memory. +int make_device_names (char*** devlist, const char* name) { + struct pathlist res; + + res.nnames = res.maxnames = 0; + res.names = NULL; + if (strcmp(name, "SCSI") == 0) { + if (grokdir("/dev/rdsk", &res, isscsidev) == -1) + return -1; + if (grokdir("/dev/rmt", &res, isscsidev) == -1) + return -1; + } else if (strcmp(name, "ATA") == 0) { + if (grokdir("/dev/rdsk", &res, isatadev) == -1) + return -1; + } else { + // non-SCSI and non-ATA case not implemented + *devlist=NULL; + return 0; + } + + // shrink array to min possible size + res.names = realloc(res.names, res.nnames * sizeof (char *)); + bytes -= sizeof(char *)*(res.maxnames-res.nnames); + + // pass list back + *devlist = res.names; + return res.nnames; +} + +// Like open(). Return integer handle, used by functions below only. +// type="ATA" or "SCSI". +int deviceopen(const char *pathname, char *type){ + if (!strcmp(type,"SCSI")) + return open(pathname, O_RDWR | O_NONBLOCK); + else if (!strcmp(type,"ATA")) + return open(pathname, O_RDONLY | O_NONBLOCK); + else + return -1; +} + +// Like close(). Acts on handles returned by above function. +int deviceclose(int fd){ + return close(fd); +} + +static void swap_sector(void *p) +{ + int i; + unsigned char t, *cp = p; + for(i = 0; i < 256; i++) { + t = cp[0]; cp[0] = cp[1]; cp[1] = t; + cp += 2; + } +} + +// Interface to ATA devices. See os_linux.c +int marvell_command_interface(int fd, smart_command_set command, int select, char *data){ + ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data); + return -1; +} + +int ata_command_interface(int fd, smart_command_set command, int select, char *data){ +#if defined(__sparc) + int err; + + switch (command){ + case CHECK_POWER_MODE: + /* currently not recognized */ + return -1; + case READ_VALUES: + return smart_read_data(fd, data); + case READ_THRESHOLDS: + return smart_read_thresholds(fd, data); + case READ_LOG: + return smart_read_log(fd, select, 1, data); + case IDENTIFY: + err = ata_identify(fd, data); + if(err) return err; + swap_sector(data); + return 0; + case PIDENTIFY: + err = ata_pidentify(fd, data); + if(err) return err; + swap_sector(data); + return 0; + case ENABLE: + return smart_enable(fd); + case DISABLE: + return smart_disable(fd); + case STATUS: + return smart_status(fd); + case AUTO_OFFLINE: + return smart_auto_offline(fd, select); + case AUTOSAVE: + return smart_auto_save(fd, select); + case IMMEDIATE_OFFLINE: + return smart_immediate_offline(fd, select); + case STATUS_CHECK: + return smart_status_check(fd); + default: + pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command); + exit(1); + break; + } +#else /* __sparc */ + // avoid gcc warnings// + fd=command=select=0; + data=NULL; + + /* Above smart_* routines uses undocumented ioctls of "dada" + * driver, which is specific to SPARC Solaris. See + * os_solaris_ata.s for further details. x86 Solaris seems not to + * provide similar or alternative interface... */ + if (printwarning(0)) + return -1; +#endif + return -1; +} + +// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c +int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ + // avoid gcc warnings// + fd=disknum=escalade_type=command=select=0; + data=NULL; + + if (printwarning(1)) + return -1; + return -1; +} + +#include +#include +#include +#include +#include + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { + struct uscsi_cmd uscsi; + + if (report > 0) { + int k; + const unsigned char * ucp = iop->cmnd; + const char * np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + pout("]"); + } + + + memset(&uscsi, 0, sizeof (uscsi)); + + uscsi.uscsi_cdb = (void *)iop->cmnd; + uscsi.uscsi_cdblen = iop->cmnd_len; + if (iop->timeout == 0) + uscsi.uscsi_timeout = 60; /* XXX */ + else + uscsi.uscsi_timeout = iop->timeout; + uscsi.uscsi_bufaddr = (void *)iop->dxferp; + uscsi.uscsi_buflen = iop->dxfer_len; + uscsi.uscsi_rqbuf = (void *)iop->sensep; + uscsi.uscsi_rqlen = iop->max_sense_len; + + switch (iop->dxfer_dir) { + case DXFER_NONE: + case DXFER_FROM_DEVICE: + uscsi.uscsi_flags = USCSI_READ; + break; + case DXFER_TO_DEVICE: + uscsi.uscsi_flags = USCSI_WRITE; + break; + default: + return -EINVAL; + } + uscsi.uscsi_flags |= USCSI_ISOLATE; + + if (ioctl(fd, USCSICMD, &uscsi)) + return -errno; + + iop->scsi_status = uscsi.uscsi_status; + iop->resid = uscsi.uscsi_resid; + iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid; + + if (report > 0) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + pout(" status=0\n"); + + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + + return (0); +} diff --git a/os_solaris.h b/os_solaris.h new file mode 100644 index 0000000..202dcac --- /dev/null +++ b/os_solaris.h @@ -0,0 +1,57 @@ +/* + * os_solaris.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2003-6 SAWADA Keiji + * Copyright (C) 2003-6 Casper Dik + * + * 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/ + * + */ + +#ifndef OS_SOLARIS_H_ +#define OS_SOLARIS_H_ + +#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h,v 1.12 2006/04/12 14:54:28 ballen4705 Exp $\n" + +// Additional material should start here. Note: to keep the '-V' CVS +// reporting option working as intended, you should only #include +// system include files . Local #include files +// <"something.h"> should be #included in os_solaris.c + +#include +#include +#include + +// function prototypes for functions defined in os_solaris_ata.s +int smart_read_data(int fd, void *data); +int smart_read_thresholds(int fd, void *data); +int smart_read_log(int fd, int s, int count, void *data); +int ata_identify(int fd, void *data); +int ata_pidentify(int fd, void *data); +int smart_enable(int fd); +int smart_disable(int fd); +int smart_status(int fd); +int smart_auto_offline(int fd, int s); +int smart_auto_save(int fd, int s); +int smart_immediate_offline(int fd, int s); +int smart_status_check(int fd); + +// wrapper macros +#define smart_enable_auto_save(fd) smart_auto_save(fd, 0xf1) +#define smart_disable_auto_save(fd) smart_auto_save(fd, 0x00) + +#endif /* OS_SOLARIS_H_ */ diff --git a/os_solaris_ata.s b/os_solaris_ata.s new file mode 100644 index 0000000..3199e17 --- /dev/null +++ b/os_solaris_ata.s @@ -0,0 +1,697 @@ +! +! os_solaris_ata.s +! +! Home page of code is: http://smartmontools.sourceforge.net +! +! Copyright (C) 2003-6 SAWADA Keiji +! +! 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 of the License, or +! (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but +! WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! 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. +! +! +! -------------------------------------------------------- +! direct access routines to ATA device under Solaris/SPARC +! -------------------------------------------------------- +! +! Information +! ----------- +! +! In Solaris, programmer can pass SCSI command to target device directly +! by using USCSI ioctl or using "scg" generic SCSI driver. But, such +! method does not exist for ATA devices. +! +! However, I can access Solaris kernel source because I am subscriber of +! Source Foundation Program of Solaris. So, I can find method of +! accessing ATA device directly. The method is to pack command in +! undocumented structure and issue ioctl that appears only in kernel +! source. Yes, that is the same way in using USCSI interface. +! +! But, I met difficulty in disclosing this technique. I have signed NDA +! with Sun that inhibits me not to violate their intellectual property. +! +! Fortunately, Sun allows licensees to publish "Interfaces" if: +! +! (1) he/she treats Solaris code as confidential +! +! (2) and he/she doesn't incorporate Sun's code into his/her code +! +! (3) and disclose enough information to use "Interface" to everyone. +! +! So, I publish that technique in assembly code or object code because: +! +! (1) I believe Sun's intellectural property is not invaded because I +! didn't reveal any struct member and ioctl to non-licensee. +! +! (2) no piece of kernel source is included in this code. +! +! (3) And finally, I publish enough information below in order to use +! this code. +! +! For last reason, please don't remove "Calling Interface" section from +! distribution. +! +! +! Calling Interface +! ----------------- +! +! Name of function/macro presents corresponding S.M.A.R.T. command. +! +! Parameters are described below. +! +! int fd +! +! File descriptor of ATA device. Device would be +! /dev/rdsk/cXtXdXsX. +! +! Device should be raw device serviced by "dada" driver. ATAPI +! CD-ROM/R/RW, DVD-ROM, and so on are not allowed because they are +! serviced by "sd" driver. On x86 Solaris, "cmdk" driver services +! them, this routines doesn't work. +! +! int s +! Select sector for service. For example, this indicates log sector +! number for smart_read_log() function. Probably you need to read +! ATA specification for this parameter. +! +! void *data +! Data going to be read/written. It don't have to be word aligned, +! But data shall points valid user memory space. +! +! This is very tiny routines, but if you feel this insufficient, please +! let me know. +! +! ksw / SAWADA Keiji +! + .file "solaris-ata-in.c" + .section ".rodata" + .align 8 +.LLC0: + .asciz "$Id: os_solaris_ata.s,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $" + .global os_solaris_ata_s_cvsid + .section ".data" + .align 4 + .type os_solaris_ata_s_cvsid, #object + .size os_solaris_ata_s_cvsid, 4 +os_solaris_ata_s_cvsid: + .long .LLC0 + .section ".text" + .align 4 + .type ata_cmd, #function + .proc 04 +ata_cmd: + !#PROLOGUE# 0 + save %sp, -184, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + st %i2, [%fp+76] + st %i3, [%fp+80] + st %i4, [%fp+84] + st %i5, [%fp+88] + ld [%fp+92], %g1 + st %g1, [%fp-76] + ld [%fp-76], %g1 + and %g1, 3, %g1 + cmp %g1, 0 + be .LL2 + nop + mov -2, %g1 + st %g1, [%fp-80] + b .LL1 + nop +.LL2: + add %fp, -56, %g1 + mov %g1, %o0 + mov 0, %o1 + mov 36, %o2 + call memset, 0 + nop + add %fp, -72, %g1 + mov %g1, %o0 + mov 0, %o1 + mov 16, %o2 + call memset, 0 + nop + ld [%fp+72], %g1 + stb %g1, [%fp-72] + mov 1, %g1 + stb %g1, [%fp-71] + mov 1, %g1 + stb %g1, [%fp-70] + ld [%fp+76], %g1 + stb %g1, [%fp-69] + ld [%fp+84], %g1 + sll %g1, 9, %g1 + st %g1, [%fp-68] + ld [%fp+80], %g1 + st %g1, [%fp-60] + mov 10, %g1 + sth %g1, [%fp-52] + ld [%fp+88], %g1 + cmp %g1, 0 + be .LL3 + nop + mov 14, %g1 + st %g1, [%fp-84] + b .LL4 + nop +.LL3: + mov 6, %g1 + st %g1, [%fp-84] +.LL4: + ld [%fp-84], %g1 + st %g1, [%fp-48] + ld [%fp+88], %g1 + sll %g1, 9, %g1 + st %g1, [%fp-44] + ld [%fp+88], %g1 + sll %g1, 9, %g1 + st %g1, [%fp-40] + ld [%fp+88], %g1 + cmp %g1, 0 + be .LL5 + nop + ld [%fp+92], %g1 + st %g1, [%fp-88] + b .LL6 + nop +.LL5: + st %g0, [%fp-88] +.LL6: + ld [%fp-88], %g1 + st %g1, [%fp-36] + add %fp, -72, %g1 + st %g1, [%fp-32] + add %fp, -56, %g1 + ld [%fp+68], %o0 + mov 1481, %o1 + mov %g1, %o2 + call ioctl, 0 + nop + mov %o0, %g1 + st %g1, [%fp-80] +.LL1: + ld [%fp-80], %i0 + ret + restore + .size ata_cmd, .-ata_cmd + .align 4 + .global ata_identify + .type ata_identify, #function + .proc 04 +ata_identify: + !#PROLOGUE# 0 + save %sp, -648, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + add %fp, -536, %g1 + st %g1, [%sp+92] + ld [%fp+68], %o0 + mov 236, %o1 + mov 0, %o2 + mov 0, %o3 + mov 1, %o4 + mov 1, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + add %fp, -536, %g1 + ld [%fp+72], %o0 + mov %g1, %o1 + mov 512, %o2 + call memcpy, 0 + nop + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL8 + nop + mov -1, %g1 + st %g1, [%fp-540] + b .LL9 + nop +.LL8: + st %g0, [%fp-540] +.LL9: + ld [%fp-540], %g1 + mov %g1, %i0 + ret + restore + .size ata_identify, .-ata_identify + .align 4 + .global ata_pidentify + .type ata_pidentify, #function + .proc 04 +ata_pidentify: + !#PROLOGUE# 0 + save %sp, -648, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + add %fp, -536, %g1 + st %g1, [%sp+92] + ld [%fp+68], %o0 + mov 161, %o1 + mov 0, %o2 + mov 0, %o3 + mov 1, %o4 + mov 1, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + add %fp, -536, %g1 + ld [%fp+72], %o0 + mov %g1, %o1 + mov 512, %o2 + call memcpy, 0 + nop + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL11 + nop + mov -1, %g1 + st %g1, [%fp-540] + b .LL12 + nop +.LL11: + st %g0, [%fp-540] +.LL12: + ld [%fp-540], %g1 + mov %g1, %i0 + ret + restore + .size ata_pidentify, .-ata_pidentify + .align 4 + .global smart_read_data + .type smart_read_data, #function + .proc 04 +smart_read_data: + !#PROLOGUE# 0 + save %sp, -648, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + add %fp, -536, %g1 + st %g1, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 208, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + mov 0, %o4 + mov 1, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + add %fp, -536, %g1 + ld [%fp+72], %o0 + mov %g1, %o1 + mov 512, %o2 + call memcpy, 0 + nop + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL14 + nop + mov -1, %g1 + st %g1, [%fp-540] + b .LL15 + nop +.LL14: + st %g0, [%fp-540] +.LL15: + ld [%fp-540], %g1 + mov %g1, %i0 + ret + restore + .size smart_read_data, .-smart_read_data + .align 4 + .global smart_read_thresholds + .type smart_read_thresholds, #function + .proc 04 +smart_read_thresholds: + !#PROLOGUE# 0 + save %sp, -648, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + add %fp, -536, %g1 + st %g1, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 209, %o2 + sethi %hi(12733440), %g1 + or %g1, 769, %o3 + mov 1, %o4 + mov 1, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + add %fp, -536, %g1 + ld [%fp+72], %o0 + mov %g1, %o1 + mov 512, %o2 + call memcpy, 0 + nop + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL17 + nop + mov -1, %g1 + st %g1, [%fp-540] + b .LL18 + nop +.LL17: + st %g0, [%fp-540] +.LL18: + ld [%fp-540], %g1 + mov %g1, %i0 + ret + restore + .size smart_read_thresholds, .-smart_read_thresholds + .align 4 + .global smart_auto_save + .type smart_auto_save, #function + .proc 04 +smart_auto_save: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 210, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + ld [%fp+72], %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL20 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL21 + nop +.LL20: + st %g0, [%fp-24] +.LL21: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_auto_save, .-smart_auto_save + .align 4 + .global smart_immediate_offline + .type smart_immediate_offline, #function + .proc 04 +smart_immediate_offline: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + ld [%fp+72], %g1 + and %g1, 255, %o5 + sethi %hi(12733440), %g1 + or %g1, 768, %g1 + or %o5, %g1, %g1 + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 212, %o2 + mov %g1, %o3 + mov 0, %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL23 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL24 + nop +.LL23: + st %g0, [%fp-24] +.LL24: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_immediate_offline, .-smart_immediate_offline + .align 4 + .global smart_read_log + .type smart_read_log, #function + .proc 04 +smart_read_log: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + st %i2, [%fp+76] + st %i3, [%fp+80] + ld [%fp+72], %g1 + and %g1, 255, %o5 + sethi %hi(12733440), %g1 + or %g1, 768, %g1 + or %o5, %g1, %o5 + ld [%fp+80], %g1 + st %g1, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 213, %o2 + mov %o5, %o3 + ld [%fp+76], %o4 + ld [%fp+76], %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL26 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL27 + nop +.LL26: + st %g0, [%fp-24] +.LL27: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_read_log, .-smart_read_log + .align 4 + .global smart_enable + .type smart_enable, #function + .proc 04 +smart_enable: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 216, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + mov 0, %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL29 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL30 + nop +.LL29: + st %g0, [%fp-24] +.LL30: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_enable, .-smart_enable + .align 4 + .global smart_disable + .type smart_disable, #function + .proc 04 +smart_disable: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 217, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + mov 0, %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL32 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL33 + nop +.LL32: + st %g0, [%fp-24] +.LL33: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_disable, .-smart_disable + .align 4 + .global smart_status + .type smart_status, #function + .proc 04 +smart_status: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 218, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + mov 0, %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL35 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL36 + nop +.LL35: + st %g0, [%fp-24] +.LL36: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_status, .-smart_status + .align 4 + .global smart_status_check + .type smart_status_check, #function + .proc 04 +smart_status_check: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 218, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + mov 0, %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL38 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL37 + nop +.LL38: + st %g0, [%fp-24] +.LL37: + ld [%fp-24], %i0 + ret + restore + .size smart_status_check, .-smart_status_check + .align 4 + .global smart_auto_offline + .type smart_auto_offline, #function + .proc 04 +smart_auto_offline: + !#PROLOGUE# 0 + save %sp, -128, %sp + !#PROLOGUE# 1 + st %i0, [%fp+68] + st %i1, [%fp+72] + st %g0, [%sp+92] + ld [%fp+68], %o0 + mov 176, %o1 + mov 219, %o2 + sethi %hi(12733440), %g1 + or %g1, 768, %o3 + ld [%fp+72], %o4 + mov 0, %o5 + call ata_cmd, 0 + nop + mov %o0, %g1 + st %g1, [%fp-20] + ld [%fp-20], %g1 + cmp %g1, 0 + be .LL40 + nop + mov -1, %g1 + st %g1, [%fp-24] + b .LL41 + nop +.LL40: + st %g0, [%fp-24] +.LL41: + ld [%fp-24], %g1 + mov %g1, %i0 + ret + restore + .size smart_auto_offline, .-smart_auto_offline + .ident "GCC: (GNU) 3.4.2" diff --git a/os_win32.c b/os_win32.c new file mode 100644 index 0000000..a210e39 --- /dev/null +++ b/os_win32.c @@ -0,0 +1,1619 @@ +/* + * os_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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 "config.h" +#include "int64.h" +#include "atacmds.h" +#include "extern.h" +extern smartmonctrl * con; // con->permissive,reportataioctl +#include "scsicmds.h" +#include "utility.h" +extern int64_t bytes; // malloc() byte count + +#include +#ifdef _DEBUG +#include +#else +#define assert(x) /**/ +#endif +#define WIN32_LEAN_AND_MEAN +#include +#include // offsetof() +#include // access() + +#define ARGUSED(x) ((void)(x)) + +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_XXXX_c_cvsid="$Id: os_win32.c,v 1.33 2006/04/07 15:02:19 chrfranke Exp $" +ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + + +#ifndef HAVE_GET_OS_VERSION_STR +#error define of HAVE_GET_OS_VERSION_STR missing in config.h +#endif + +// Return build host and OS version as static string +const char * get_os_version_str() +{ + static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2-sp2.1")+13]; + char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1; + const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3); + + OSVERSIONINFOEXA vi; + const char * w; + + // remove "-pc" to avoid long lines + assert(!strncmp(SMARTMONTOOLS_BUILD_HOST+5, "pc-", 3)); + strcpy(vstr, "i686-"); strcpy(vstr+5, SMARTMONTOOLS_BUILD_HOST+5+3); + assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr)); + + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) + return vstr; + } + + if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff) + return vstr; + + switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) { + case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0: + w = (vi.szCSDVersion[1] == 'B' || + vi.szCSDVersion[1] == 'C' ? "95-osr2" : "95"); break; + case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10: + w = (vi.szCSDVersion[1] == 'A' ? "98se" : "98"); break; + case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me"; break; + //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break; + case VER_PLATFORM_WIN32_NT <<16|0x0400| 0: w = "nt4"; break; + case VER_PLATFORM_WIN32_NT <<16|0x0500| 0: w = "2000"; break; + case VER_PLATFORM_WIN32_NT <<16|0x0500| 1: + w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp" + : "xp-mc"); break; + case VER_PLATFORM_WIN32_NT <<16|0x0500| 2: + w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003" + : "2003r2"); break; + case VER_PLATFORM_WIN32_NT <<16|0x0600| 0: w = "vista"; break; + default: w = 0; break; + } + + if (!w) + snprintf(vptr, vlen, "-%s%lu.%lu", + (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"), + vi.dwMajorVersion, vi.dwMinorVersion); + else if (vi.wServicePackMinor) + snprintf(vptr, vlen, "-%s-sp%u.%u", w, vi.wServicePackMajor, vi.wServicePackMinor); + else if (vi.wServicePackMajor) + snprintf(vptr, vlen, "-%s-sp%u", w, vi.wServicePackMajor); + else + snprintf(vptr, vlen, "-%s", w); + return vstr; +} + + +static int ata_open(int drive); +static void ata_close(int fd); +static int ata_scan(unsigned long * drives); + +static int aspi_open(unsigned adapter, unsigned id); +static void aspi_close(int fd); +static int aspi_scan(unsigned long * drives); + + +static int is_permissive() +{ + if (con->permissive <= 0) { + pout("To continue, add one or more '-T permissive' options.\n"); + return 0; + } + con->permissive--; + return 1; +} + +static const char * skipdev(const char * s) +{ + return (!strncmp(s, "/dev/", 5) ? s + 5 : s); +} + + +// tries to guess device type given the name (a path). See utility.h +// for return values. +int guess_device_type (const char * dev_name) +{ + dev_name = skipdev(dev_name); + if (!strncmp(dev_name, "hd", 2)) + return CONTROLLER_ATA; + if (!strncmp(dev_name, "scsi", 4)) + return CONTROLLER_SCSI; + return CONTROLLER_UNKNOWN; +} + + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist), the +// others each contain null-terminated character strings. +int make_device_names (char*** devlist, const char* type) +{ + unsigned long drives[3]; + int i, j, n, nmax, sz; + const char * path; + + drives[0] = drives[1] = drives[2] = 0; + if (!strcmp(type, "ATA")) { + // bit i set => drive i present + n = ata_scan(drives); + path = "/dev/hda"; + nmax = 10; + } + else if (!strcmp(type, "SCSI")) { + // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present + n = aspi_scan(drives); + path = "/dev/scsi00"; + nmax = 10*8; + } + else + return -1; + + if (n <= 0) + return 0; + + // Alloc devlist + sz = n * sizeof(char **); + *devlist = (char **)malloc(sz); bytes += sz; + + // Add devices + for (i = j = 0; i < n; i++) { + char * s; + sz = strlen(path)+1; + s = (char *)malloc(sz); bytes += sz; + strcpy(s, path); + while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f)))) + j++; + assert(j < nmax); + if (nmax <= 10) { + assert(j <= 9); + s[sz-2] += j; // /dev/hd[a-j] + } + else { + assert((j >> 3) <= 9); + s[sz-3] += (j >> 3); // /dev/scsi[0-9]..... + s[sz-2] += (j & 0x7); // .....[0-7] + } + (*devlist)[i] = s; + j++; + } + return n; +} + + +// Like open(). Return positive integer handle, only used by +// functions below. type="ATA" or "SCSI". If you need to store extra +// information about your devices, create a private internal array +// within this file (see os_freebsd.c for an example). +int deviceopen(const char * pathname, char *type) +{ + int len; + pathname = skipdev(pathname); + len = strlen(pathname); + + if (!strcmp(type, "ATA")) { + // hd[a-z] => ATA 0-9 + if (!(len == 3 && pathname[0] == 'h' && pathname[1] == 'd' + && 'a' <= pathname[2] && pathname[2] <= 'j')) { + errno = ENOENT; + return -1; + } + return ata_open(pathname[2] - 'a'); + } + + if (!strcmp(type, "SCSI")) { + // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0 + unsigned adapter = ~0, id = ~0; int n = -1; + if (!(sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len)) { + errno = ENOENT; + return -1; + } + return aspi_open(adapter, id); + } + errno = ENOENT; + return -1; +} + + +// Like close(). Acts only on handles returned by above function. +// (Never called in smartctl!) +int deviceclose(int fd) +{ + if (fd < 0x100) { + ata_close(fd); + } + else { + aspi_close(fd); + } + return 0; +} + + +// print examples for smartctl +void print_smartctl_examples(){ + printf("=================================================== SMARTCTL EXAMPLES =====\n\n" + " smartctl -a /dev/hda (Prints all SMART information)\n\n" +#ifdef HAVE_GETOPT_LONG + " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" + " (Enables SMART on first disk)\n\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" +#else + " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" + " smartctl -t long /dev/hda (Executes extended disk self-test)\n" + " smartctl -A -l selftest -q errorsonly /dev/hda\n" + " (Prints Self-Test & Attribute errors)\n" +#endif + " smartctl -a /dev/scsi21\n" + " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n" + ); +} + + +///////////////////////////////////////////////////////////////////////////// +// ATA Interface +///////////////////////////////////////////////////////////////////////////// + +// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) + +// Deklarations from: +// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3 + +#define FILE_READ_ACCESS 0x0001 +#define FILE_WRITE_ACCESS 0x0002 +#define METHOD_BUFFERED 0 +#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) + +#define FILE_DEVICE_DISK 7 +#define IOCTL_DISK_BASE FILE_DEVICE_DISK + +#define SMART_GET_VERSION \ + CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define SMART_RCV_DRIVE_DATA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define SMART_SEND_DRIVE_COMMAND \ + CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define SMART_CYL_LOW 0x4F +#define SMART_CYL_HI 0xC2 + +#pragma pack(1) + +typedef struct _GETVERSIONOUTPARAMS { + UCHAR bVersion; + UCHAR bRevision; + UCHAR bReserved; + UCHAR bIDEDeviceMap; + ULONG fCapabilities; + ULONG dwReserved[4]; +} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS; + +typedef struct _IDEREGS { + UCHAR bFeaturesReg; + UCHAR bSectorCountReg; + UCHAR bSectorNumberReg; + UCHAR bCylLowReg; + UCHAR bCylHighReg; + UCHAR bDriveHeadReg; + UCHAR bCommandReg; + UCHAR bReserved; +} IDEREGS, *PIDEREGS, *LPIDEREGS; + +typedef struct _SENDCMDINPARAMS { + ULONG cBufferSize; + IDEREGS irDriveRegs; + UCHAR bDriveNumber; + UCHAR bReserved[3]; + ULONG dwReserved[4]; + UCHAR bBuffer[1]; +} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS; + +/* DRIVERSTATUS.bDriverError constants (just for info, not used) +#define SMART_NO_ERROR 0 +#define SMART_IDE_ERROR 1 +#define SMART_INVALID_FLAG 2 +#define SMART_INVALID_COMMAND 3 +#define SMART_INVALID_BUFFER 4 +#define SMART_INVALID_DRIVE 5 +#define SMART_INVALID_IOCTL 6 +#define SMART_ERROR_NO_MEM 7 +#define SMART_INVALID_REGISTER 8 +#define SMART_NOT_SUPPORTED 9 +#define SMART_NO_IDE_DEVICE 10 +*/ + +typedef struct _DRIVERSTATUS { + UCHAR bDriverError; + UCHAR bIDEError; + UCHAR bReserved[2]; + ULONG dwReserved[2]; +} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS; + +typedef struct _SENDCMDOUTPARAMS { + ULONG cBufferSize; + DRIVERSTATUS DriverStatus; + UCHAR bBuffer[1]; +} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS; + +#pragma pack() + + +///////////////////////////////////////////////////////////////////////////// + +static void print_ide_regs(const IDEREGS * r, int out) +{ + pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", + (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, + r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); +} + +static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro) +{ + pout(" Input : "); print_ide_regs(ri, 0); + if (ro) { + pout(" Output: "); print_ide_regs(ro, 1); + } +} + +///////////////////////////////////////////////////////////////////////////// + +// call SMART_* ioctl + +static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize) +{ + SENDCMDINPARAMS inpar; + unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512]; + const SENDCMDOUTPARAMS * outpar; + DWORD code, num_out; + unsigned int size_out; + const char * name; + + assert(SMART_SEND_DRIVE_COMMAND == 0x07c084); + assert(SMART_RCV_DRIVE_DATA == 0x07c088); + assert(sizeof(SENDCMDINPARAMS)-1 == 32); + assert(sizeof(SENDCMDOUTPARAMS)-1 == 16); + + memset(&inpar, 0, sizeof(inpar)); + inpar.irDriveRegs = *regs; + // drive is set to 0-3 on Win9x only + inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4); + inpar.bDriveNumber = drive; + + assert(datasize == 0 || datasize == 512); + if (datasize) { + code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA"; + inpar.cBufferSize = size_out = 512; + } + else { + code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND"; + if (regs->bFeaturesReg == ATA_SMART_STATUS) + size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data + // Note: cBufferSize must be 0 on Win9x + else + size_out = 0; + } + + memset(&outbuf, 0, sizeof(outbuf)); + + if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1, + outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) { + // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface() + long err = GetLastError(); + if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) { + pout(" %s failed, Error=%ld\n", name, err); + print_ide_regs_io(regs, NULL); + } + errno = ( err == ERROR_INVALID_FUNCTION /*9x*/ + || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO); + return -1; + } + // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs + + outpar = (const SENDCMDOUTPARAMS *)outbuf; + + if (outpar->DriverStatus.bDriverError) { + if (con->reportataioctl) { + pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name, + outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError); + print_ide_regs_io(regs, NULL); + } + errno = EIO; + return -1; + } + + if (con->reportataioctl > 1) { + pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name, + num_out, outpar->cBufferSize); + print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ? + (const IDEREGS *)(outpar->bBuffer) : NULL)); + } + + if (datasize) + memcpy(data, outpar->bBuffer, 512); + else if (regs->bFeaturesReg == ATA_SMART_STATUS) + *regs = *(const IDEREGS *)(outpar->bBuffer); + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +// IDE PASS THROUGH for W2K/XP (does not work on W9x/NT4) +// Only used for SMART commands not supported by SMART_* IOCTLs +// +// Based on WinATA.cpp, 2002 c't/Matthias Withopf +// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip + +#define FILE_DEVICE_CONTROLLER 4 +#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER + +#define IOCTL_IDE_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#pragma pack(1) + +typedef struct { + IDEREGS IdeReg; + ULONG DataBufferSize; + UCHAR DataBuffer[1]; +} ATA_PASS_THROUGH; + +#pragma pack() + + +///////////////////////////////////////////////////////////////////////////// + +static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) +{ + unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize; + ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); + DWORD num_out; + const unsigned char magic = 0xcf; + assert(sizeof(ATA_PASS_THROUGH)-1 == 12); + assert(IOCTL_IDE_PASS_THROUGH == 0x04d028); + + if (!buf) { + errno = ENOMEM; + return -1; + } + + buf->IdeReg = *regs; + buf->DataBufferSize = datasize; + if (datasize) + buf->DataBuffer[0] = magic; + + if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH, + buf, size, buf, size, &num_out, NULL)) { + long err = GetLastError(); + if (con->reportataioctl) + pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err); + VirtualFree(buf, 0, MEM_RELEASE); + errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); + return -1; + } + + // Check ATA status + if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) { + if (con->reportataioctl) { + pout(" IOCTL_IDE_PASS_THROUGH command failed:\n"); + print_ide_regs_io(regs, &buf->IdeReg); + } + VirtualFree(buf, 0, MEM_RELEASE); + errno = EIO; + return -1; + } + + // Check and copy data + if (datasize) { + if ( num_out != size + || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) { + if (con->reportataioctl) { + pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n", + num_out, buf->DataBufferSize); + print_ide_regs_io(regs, &buf->IdeReg); + } + VirtualFree(buf, 0, MEM_RELEASE); + errno = EIO; + return -1; + } + memcpy(data, buf->DataBuffer, datasize); + } + + if (con->reportataioctl > 1) { + pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n", + num_out, buf->DataBufferSize); + print_ide_regs_io(regs, &buf->IdeReg); + } + *regs = buf->IdeReg; + + // Caution: VirtualFree() fails if parameter "dwSize" is nonzero + VirtualFree(buf, 0, MEM_RELEASE); + return 0; +} + + + +///////////////////////////////////////////////////////////////////////////// + +// ATA PASS THROUGH via SCSI PASS THROUGH for NT4 only +// Only used for SMART commands not supported by SMART_* IOCTLs + +// Declarations from: +// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddscsi.h?rev=1.2 + +#define IOCTL_SCSI_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 +// undocumented SCSI opcode to for ATA passthrough +#define SCSIOP_ATA_PASSTHROUGH 0xCC + +typedef struct _SCSI_PASS_THROUGH { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG/*_PTR*/ DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; + + +///////////////////////////////////////////////////////////////////////////// + +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; + + assert(sizeof(SCSI_PASS_THROUGH) == 44); + assert(IOCTL_SCSI_PASS_THROUGH == 0x04d004); + + 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 (con->reportataioctl) + pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err); + errno = (err == ERROR_INVALID_FUNCTION ? 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 (con->reportataioctl) { + 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 (con->reportataioctl > 1) { + pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out); + print_ide_regs_io(regs, NULL); + } + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static HANDLE h_ata_ioctl = 0; + + +// Print SMARTVSD error message, return errno + +static int smartvsd_error() +{ + char path[MAX_PATH]; + unsigned len; + if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2)) + return ENOENT; + // 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 { + // Some Windows versions install SMARTVSD.VXD in SYSTEM directory + // http://support.microsoft.com/default.aspx?scid=kb;en-us;265854 + strcpy(path+len, "\\SMARTVSD.VXD"); + if (!access(path, 0)) { + path[len] = 0; + pout("SMART driver is not properly installed,\n" + " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n" + " and reboot Windows.\n", path, path); + } + else { + path[len] = 0; + pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path); + } + return ENOSYS; + } +} + + +static int ata_open(int drive) +{ + int win9x; + char devpath[30]; + GETVERSIONOUTPARAMS vers; + DWORD num_out; + + assert(SMART_GET_VERSION == 0x074080); + assert(sizeof(GETVERSIONOUTPARAMS) == 24); + + // TODO: This version does not allow to open more than 1 ATA devices + if (h_ata_ioctl) { + errno = ENFILE; + return -1; + } + + win9x = ((GetVersion() & 0x80000000) != 0); + + if (!(0 <= drive && drive <= (win9x ? 3 : 9))) { + errno = ENOENT; + return -1; + } + // path depends on Windows Version + if (win9x) + strcpy(devpath, "\\\\.\\SMARTVSD"); + else + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive); + + // Open device + if ((h_ata_ioctl = CreateFileA(devpath, + GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { + long err = GetLastError(); + pout("Cannot open device %s, Error=%ld\n", devpath, err); + if (err == ERROR_FILE_NOT_FOUND) + errno = (win9x ? smartvsd_error() : ENOENT); + else if (err == ERROR_ACCESS_DENIED) { + if (!win9x) + pout("Administrator rights are necessary to access physical drives.\n"); + errno = EACCES; + } + else + errno = EIO; + h_ata_ioctl = 0; + return -1; + } + + // Get drive map + memset(&vers, 0, sizeof(vers)); + if (!DeviceIoControl(h_ata_ioctl, SMART_GET_VERSION, + NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { + pout("%s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError()); + if (!win9x) + pout("If this is a SCSI disk, try \"scsi\".\n"); + if (!is_permissive()) { + CloseHandle(h_ata_ioctl); h_ata_ioctl = 0; + errno = ENOSYS; + return -1; + } + } + + if (con->reportataioctl > 1) + pout("%s: SMART_GET_VERSION (%ld bytes):\n" + " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n", + devpath, num_out, vers.bVersion, vers.bRevision, + vers.fCapabilities, vers.bIDEDeviceMap); + + // TODO: Check vers.fCapabilities here? + + if (!win9x) + // NT4/2K/XP: Drive exists, Drive number not necessary for ioctl + return 0; + + // Win9x/ME: Check device presence & type + if (((vers.bIDEDeviceMap >> drive) & 0x11) != 0x01) { + unsigned char atapi = (vers.bIDEDeviceMap >> drive) & 0x10; + pout(( atapi + ? "Drive %d is an ATAPI device (IDEDeviceMap=0x%02x).\n" + : "Drive %d does not exist (IDEDeviceMap=0x%02x).\n"), + drive, vers.bIDEDeviceMap); + // Win9x Drive existence check may not work as expected + // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01 + // http://support.microsoft.com/support/kb/articles/Q196/1/20.ASP + if (!is_permissive()) { + CloseHandle(h_ata_ioctl); h_ata_ioctl = 0; + errno = (atapi ? ENOSYS : ENOENT); + return -1; + } + } + // Use drive number as fd for ioctl + return drive; +} + + +static void ata_close(int fd) +{ + ARGUSED(fd); + CloseHandle(h_ata_ioctl); + h_ata_ioctl = 0; +} + + +// Scan for ATA drives, fill bitmask of drives present, return #drives + +static int ata_scan(unsigned long * drives) +{ + int win9x = ((GetVersion() & 0x80000000) != 0); + int cnt = 0, i; + + for (i = 0; i <= 9; i++) { + char devpath[30]; + GETVERSIONOUTPARAMS vers; + DWORD num_out; + HANDLE h; + if (win9x) + strcpy(devpath, "\\\\.\\SMARTVSD"); + else + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i); + + // Open device + if ((h = CreateFileA(devpath, + GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { + if (con->reportataioctl > 1) + pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError()); + if (win9x) + break; // SMARTVSD.VXD missing or no ATA devices + continue; // Disk not found or access denied (break;?) + } + + // Get drive map + memset(&vers, 0, sizeof(vers)); + if (!DeviceIoControl(h, SMART_GET_VERSION, + NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { + if (con->reportataioctl) + pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError()); + CloseHandle(h); + if (win9x) + break; // Should not happen + continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk) + } + CloseHandle(h); + + if (con->reportataioctl) + pout(" %s: SMART_GET_VERSION (%ld bytes):\n" + " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n", + devpath, num_out, vers.bVersion, vers.bRevision, + vers.fCapabilities, vers.bIDEDeviceMap); + + if (win9x) { + // Check ATA device presence, remove ATAPI devices + drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf); + cnt = (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1); + break; + } + + // ATA drive exists and driver supports SMART ioctl + drives[0] |= (1L << i); + cnt++; + } + + return cnt; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Interface to ATA devices. See os_linux.c +int ata_command_interface(int fd, smart_command_set command, int select, char * data) +{ + IDEREGS regs; + int copydata, try_ioctl; + + if (!(0 <= fd && fd <= 3)) { + errno = EBADF; + return -1; + } + + // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE + memset(®s, 0, sizeof(regs)); + regs.bCommandReg = ATA_SMART_CMD; + regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW; + copydata = 0; + try_ioctl = 0x01; // 0x01=SMART_*, 0x02=IDE_PASS_THROUGH [, 0x04=ATA_PASS_THROUGH] + + switch (command) { + case WRITE_LOG: + // TODO. Not supported by SMART IOCTL (no data out ioctl available), + // also not supported by IOCTL_IDE_PASS_THROUGH (data out not working) + errno = ENOSYS; + return -1; + case CHECK_POWER_MODE: + regs.bCommandReg = ATA_CHECK_POWER_MODE; + regs.bCylLowReg = regs.bCylHighReg = 0; + try_ioctl = 0x02; // IOCTL_IDE_PASS_THROUGH + // Note: returns SectorCountReg in data[0] + break; + case READ_VALUES: + regs.bFeaturesReg = ATA_SMART_READ_VALUES; + regs.bSectorNumberReg = regs.bSectorCountReg = 1; + copydata = 1; + break; + case READ_THRESHOLDS: + regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS; + regs.bSectorNumberReg = regs.bSectorCountReg = 1; + copydata = 1; + break; + case READ_LOG: + regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR; + regs.bSectorNumberReg = select; + regs.bSectorCountReg = 1; + // Read log only supported on Win9x, retry with pass through command + try_ioctl = 0x03; // SMART_RCV_DRIVE_DATA, then IOCTL_IDE_PASS_THROUGH + copydata = 1; + break; + case IDENTIFY: + // Note: WinNT4/2000/XP return identify data cached during boot + // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH) + regs.bCommandReg = ATA_IDENTIFY_DEVICE; + regs.bCylLowReg = regs.bCylHighReg = 0; + regs.bSectorCountReg = 1; + copydata = 1; + break; + case PIDENTIFY: + regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE; + regs.bCylLowReg = regs.bCylHighReg = 0; + regs.bSectorCountReg = 1; + copydata = 1; + break; + case ENABLE: + regs.bFeaturesReg = ATA_SMART_ENABLE; + regs.bSectorNumberReg = 1; + break; + case DISABLE: + regs.bFeaturesReg = ATA_SMART_DISABLE; + regs.bSectorNumberReg = 1; + break; + case STATUS: + case STATUS_CHECK: + regs.bFeaturesReg = ATA_SMART_STATUS; + break; + case AUTO_OFFLINE: + regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE; + regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + regs.bFeaturesReg = ATA_SMART_AUTOSAVE; + regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE; + regs.bSectorNumberReg = select; + if (select == ABORT_SELF_TEST) // Abort only supported on Win9x, try + try_ioctl = 0x03; // SMART_SEND_DRIVE_COMMAND, then IOCTL_IDE_PASS_THROUGH + break; + default: + pout("Unrecognized command %d in win32_ata_command_interface()\n" + "Please contact " PACKAGE_BUGREPORT "\n", command); + errno = ENOSYS; + return -1; + } + + if (try_ioctl & 0x01) { + if (smart_ioctl(h_ata_ioctl, fd, ®s, data, (copydata?512:0))) { + if (!(try_ioctl & 0x02) || errno != ENOSYS) + return -1; + // CAUTION: smart_ioctl() MUST NOT change "regs" Parameter in this case + } + else + try_ioctl = 0; + } + + if (try_ioctl & 0x02) { + errno = 0; + if ((GetVersion() & 0x8000ffff) == 0x00000004) { + // Special case WinNT4 + if (command == CHECK_POWER_MODE) { // SCSI_PASS_THROUGH does not return regs! + errno = ENOSYS; + return -1; + } + if (ata_via_scsi_pass_through_ioctl(h_ata_ioctl, ®s, data, (copydata?512:0))) + return -1; + } + else { + if (ide_pass_through_ioctl(h_ata_ioctl, ®s, data, (copydata?512:0))) + return -1; + } + try_ioctl = 0; + } + assert(!try_ioctl); + + switch (command) { + case CHECK_POWER_MODE: + // Return power mode from SectorCountReg in data[0] + data[0] = regs.bSectorCountReg; + return 0; + + case STATUS_CHECK: + // Cyl low and Cyl high unchanged means "Good SMART status" + if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW) + return 0; + + // These values mean "Bad SMART status" + if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4) + return 1; + + // We haven't gotten output that makes sense; print out some debugging info + syserror("Error SMART Status command failed"); + pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE); + print_ide_regs(®s, 1); + errno = EIO; + return -1; + + default: + return 0; + } + /*NOTREACHED*/ +} + + +#ifndef HAVE_ATA_IDENTIFY_IS_CACHED +#error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h +#endif + +// Return true if OS caches the ATA identify sector +int ata_identify_is_cached(int fd) +{ + ARGUSED(fd); + // WinNT4/2000/XP => true, Win9x/ME => false + return ((GetVersion() & 0x80000000) == 0); +} + + +// Print not implemeted warning once +static void pr_not_impl(const char * what, int * warned) +{ + if (*warned) + return; + pout( + "#######################################################################\n" + "%s\n" + "NOT IMPLEMENTED under Win32.\n" + "Please contact " PACKAGE_BUGREPORT " if\n" + "you want to help in porting smartmontools to Win32.\n" + "#######################################################################\n" + "\n", what + ); + *warned = 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) +{ + static int warned = 0; + ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data); + pr_not_impl("3ware Escalade Controller command routine escalade_command_interface()", &warned); + errno = ENOSYS; + return -1; +} + +// Interface to ATA devices behind Marvell chip-set based controllers. See os_linux.c +int marvell_command_interface(int fd, smart_command_set command, int select, char * data) +{ + static int warned = 0; + ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data); + pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned); + errno = ENOSYS; + return -1; +} + + +///////////////////////////////////////////////////////////////////////////// +// ASPI Interface +///////////////////////////////////////////////////////////////////////////// + +#pragma pack(1) + +#define ASPI_SENSE_SIZE 18 + +// ASPI SCSI Request block header + +typedef struct { + unsigned char cmd; // 00: Command code + unsigned char status; // 01: ASPI status + unsigned char adapter; // 02: Host adapter number + unsigned char flags; // 03: Request flags + unsigned char reserved[4]; // 04: 0 +} ASPI_SRB_HEAD; + +// SRB for host adapter inquiry + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char adapters; // 08: Number of adapters + unsigned char target_id; // 09: Target ID ? + char manager_id[16]; // 10: SCSI manager ID + char adapter_id[16]; // 26: Host adapter ID + unsigned char parameters[16]; // 42: Host adapter unique parmameters +} ASPI_SRB_INQUIRY; + +// SRB for get device type + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char target_id; // 08: Target ID + unsigned char lun; // 09: LUN + unsigned char devtype; // 10: Device type + unsigned char reserved; // 11: Reserved +} ASPI_SRB_DEVTYPE; + +// SRB for SCSI I/O + +typedef struct { + ASPI_SRB_HEAD h; // 00: Header + unsigned char target_id; // 08: Target ID + unsigned char lun; // 09: LUN + unsigned char reserved[2]; // 10: Reserved + unsigned long data_size; // 12: Data alloc. lenght + void * data_addr; // 16: Data buffer pointer + unsigned char sense_size; // 20: Sense alloc. length + unsigned char cdb_size; // 21: CDB length + unsigned char host_status; // 22: Host status + unsigned char target_status; // 23: Target status + void * event_handle; // 24: Event handle + unsigned char workspace[20]; // 28: ASPI workspace + unsigned char cdb[16+ASPI_SENSE_SIZE]; +} ASPI_SRB_IO; + +// Macro to retrieve start of sense information +#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16) + +// SRB union + +typedef union { + ASPI_SRB_HEAD h; // Common header + ASPI_SRB_INQUIRY q; // Inquiry + ASPI_SRB_DEVTYPE t; // Device type + ASPI_SRB_IO i; // I/O +} ASPI_SRB; + +#pragma pack() + +// ASPI commands +#define ASPI_CMD_ADAPTER_INQUIRE 0x00 +#define ASPI_CMD_GET_DEVICE_TYPE 0x01 +#define ASPI_CMD_EXECUTE_IO 0x02 +#define ASPI_CMD_ABORT_IO 0x03 + +// Request flags +#define ASPI_REQFLAG_DIR_TO_HOST 0x08 +#define ASPI_REQFLAG_DIR_TO_TARGET 0x10 +#define ASPI_REQFLAG_DIR_NO_XFER 0x18 +#define ASPI_REQFLAG_EVENT_NOTIFY 0x40 + +// ASPI status +#define ASPI_STATUS_IN_PROGRESS 0x00 +#define ASPI_STATUS_NO_ERROR 0x01 +#define ASPI_STATUS_ABORTED 0x02 +#define ASPI_STATUS_ABORT_ERR 0x03 +#define ASPI_STATUS_ERROR 0x04 +#define ASPI_STATUS_INVALID_COMMAND 0x80 +#define ASPI_STATUS_INVALID_ADAPTER 0x81 +#define ASPI_STATUS_INVALID_TARGET 0x82 +#define ASPI_STATUS_NO_ADAPTERS 0xE8 + +// Adapter (host) status +#define ASPI_HSTATUS_NO_ERROR 0x00 +#define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11 +#define ASPI_HSTATUS_DATA_OVERRUN 0x12 +#define ASPI_HSTATUS_BUS_FREE 0x13 +#define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14 +#define ASPI_HSTATUS_BAD_SGLIST 0x1A + +// Target status +#define ASPI_TSTATUS_NO_ERROR 0x00 +#define ASPI_TSTATUS_CHECK_CONDITION 0x02 +#define ASPI_TSTATUS_BUSY 0x08 +#define ASPI_TSTATUS_RESERV_CONFLICT 0x18 + + +static HINSTANCE h_aspi_dll; // DLL handle +static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint +static unsigned num_aspi_adapters; + +#ifdef __CYGWIN__ +// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork() +static DWORD aspi_dll_pid; // PID of DLL owner to detect fork() +#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId())) +#else +#define aspi_entry_valid() (!!aspi_entry) +#endif + + +static int aspi_call(ASPI_SRB * srb) +{ + int i; + aspi_entry(srb); + i = 0; + while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) { + if (++i > 100/*10sek*/) { + pout("ASPI Adapter %u: Timed out\n", srb->h.adapter); + aspi_entry = 0; + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = EIO; + return -1; + } + if (con->reportscsiioctl > 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 = INVALID_HANDLE_VALUE; + errno = ENOSYS; + return 0; + } + return addr; +} + + +static int aspi_open_dll(int verbose) +{ + UINT (*aspi_info)(void); + UINT info, rc; + + assert(!aspi_entry_valid()); + + // Check structure layout + assert(sizeof(ASPI_SRB_HEAD) == 8); + assert(sizeof(ASPI_SRB_INQUIRY) == 58); + assert(sizeof(ASPI_SRB_DEVTYPE) == 12); + assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE); + assert(offsetof(ASPI_SRB,h.cmd) == 0); + assert(offsetof(ASPI_SRB,h.flags) == 3); + assert(offsetof(ASPI_SRB_IO,lun) == 9); + assert(offsetof(ASPI_SRB_IO,data_addr) == 16); + assert(offsetof(ASPI_SRB_IO,workspace) == 28); + assert(offsetof(ASPI_SRB_IO,cdb) == 48); + + if (h_aspi_dll == INVALID_HANDLE_VALUE) { + // do not retry + errno = ENOENT; + return -1; + } + + // Load ASPI DLL + if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) { + if (verbose) + pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError()); + h_aspi_dll = INVALID_HANDLE_VALUE; + errno = ENOENT; + return -1; + } + if (con->reportscsiioctl > 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 (con->reportscsiioctl > 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 = INVALID_HANDLE_VALUE; + errno = ENOENT; + return -1; + } + + if (con->reportscsiioctl) + 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)event, rc, rc, GetLastError()); + } + // TODO: ASPI_ABORT_IO command + aspi_entry = 0; + h_aspi_dll = INVALID_HANDLE_VALUE; + return -EIO; + } + } + CloseHandle(event); + return 0; +} + + +static int aspi_open(unsigned adapter, unsigned id) +{ + ASPI_SRB srb; + if (!(adapter <= 9 && id < 16)) { + errno = ENOENT; + return -1; + } + + if (!aspi_entry_valid()) { + if (aspi_open_dll(1/*verbose*/)) + return -1; + } + + // Adapter OK? + if (adapter >= num_aspi_adapters) { + pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n", + adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":"")); + if (!is_permissive()) { + errno = ENOENT; + return -1; + } + } + + // Device present ? + 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)) { + errno = EIO; + return -1; + } + 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()) { + errno = (srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO); + return -1; + } + } + else if (con->reportscsiioctl) + pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype); + + return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf)); +} + + +static void aspi_close(int fd) +{ + // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads + ARGUSED(fd); +} + + +// Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present, +// return #drives + +static int aspi_scan(unsigned long * drives) +{ + int cnt = 0; + unsigned ad; + + if (!aspi_entry_valid()) { + if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/)) + return 0; + } + + for (ad = 0; ad < num_aspi_adapters; ad++) { + ASPI_SRB srb; unsigned id; + + if (ad > 9) { + if (con->reportscsiioctl) + 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)) + return 0; + + if (srb.h.status != ASPI_STATUS_NO_ERROR) { + if (con->reportscsiioctl) + pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status); + continue; + } + + if (con->reportscsiioctl) { + int i; + for (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); + } + + for (id = 0; id <= 7; id++) { + // Get device type + memset(&srb, 0, sizeof(srb)); + srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE; + srb.h.adapter = ad; srb.i.target_id = id; + if (aspi_call(&srb)) + return 0; + if (srb.h.status != ASPI_STATUS_NO_ERROR) { + if (con->reportscsiioctl > 1) + pout(" ID %u: No such device (Status=0x%02x)\n", id, srb.h.status); + continue; + } + if (con->reportscsiioctl) + pout(" ID %u: Device Type=0x%02x\n", id, srb.t.devtype); + if (srb.t.devtype == 0x00/*HDD*/) { + drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id)); + cnt++; + } + } + } + return cnt; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Interface to SCSI devices. See os_linux.c +int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) +{ + ASPI_SRB srb; + + if (!aspi_entry_valid()) + return -EBADF; + if (!((fd & ~0xff) == 0x100)) + return -EBADF; + + if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12)) { + pout("do_scsi_cmnd_io: bad CDB length\n"); + return -EINVAL; + } + + if (report > 0) { + // From os_linux.c + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout(buff); + } + + memset(&srb, 0, sizeof(srb)); + srb.h.cmd = ASPI_CMD_EXECUTE_IO; + srb.h.adapter = ((fd >> 4) & 0xf); + srb.i.target_id = (fd & 0xf); + //srb.i.lun = 0; + srb.i.sense_size = ASPI_SENSE_SIZE; + srb.i.cdb_size = iop->cmnd_len; + memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len); + + switch (iop->dxfer_dir) { + case DXFER_NONE: + srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER; + break; + case DXFER_FROM_DEVICE: + srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST; + srb.i.data_size = iop->dxfer_len; + srb.i.data_addr = iop->dxferp; + break; + case DXFER_TO_DEVICE: + srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET; + srb.i.data_size = iop->dxfer_len; + srb.i.data_addr = iop->dxferp; + break; + default: + pout("do_scsi_cmnd_io: bad dxfer_dir\n"); + return -EINVAL; + } + + iop->resp_sense_len = 0; + iop->scsi_status = 0; + iop->resid = 0; + + if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) { + // Timeout + return -EIO; + } + + if (srb.h.status != ASPI_STATUS_NO_ERROR) { + if ( srb.h.status == ASPI_STATUS_ERROR + && srb.i.host_status == ASPI_HSTATUS_NO_ERROR + && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) { + // Sense valid + const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len); + int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len); + iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; + if (len > 0 && iop->sensep) { + memcpy(iop->sensep, sense, len); + iop->resp_sense_len = len; + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", (int)len); + dStrHex(iop->sensep, len , 1); + } + } + if (report) { + pout(" sense_key=%x asc=%x ascq=%x\n", + sense[2] & 0xf, sense[12], sense[13]); + } + return 0; + } + else { + if (report) + pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status); + return -EIO; + } + } + + if (report > 0) + pout(" OK\n"); + + if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + + return 0; +} diff --git a/os_win32/daemon_win32.c b/os_win32/daemon_win32.c new file mode 100644 index 0000000..2cac47f --- /dev/null +++ b/os_win32/daemon_win32.c @@ -0,0 +1,1266 @@ +/* + * os_win32/daemon_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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 +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP) +#define _WIN32_WINNT 0x0400 +#include +#ifdef _DEBUG +#include +#endif + +#include "daemon_win32.h" + +const char *daemon_win32_c_cvsid = "$Id: daemon_win32.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $" +DAEMON_WIN32_H_CVSID; + + +///////////////////////////////////////////////////////////////////////////// + +#define ARGUSED(x) ((void)(x)) + +// Prevent spawning of child process if debugging +#ifdef _DEBUG +#define debugging() IsDebuggerPresent() +#else +#define debugging() FALSE +#endif + + +#define EVT_NAME_LEN 260 + +// Internal events (must be > SIGUSRn) +#define EVT_RUNNING 100 // Exists when running, signaled on creation +#define EVT_DETACHED 101 // Signaled when child detaches from console +#define EVT_RESTART 102 // Signaled when child should restart + +static void make_name(char * name, int sig) +{ + int i; + if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10)) + strcpy(name, "DaemonEvent"); + for (i = 0; name[i]; i++) { + char c = name[i]; + if (!( ('0' <= c && c <= '9') + || ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z'))) + name[i] = '_'; + } + sprintf(name+strlen(name), "-%d", sig); +} + + +static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + 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); +} + + +static int event_exists(int sig) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + make_name(name, sig); + if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) + return 0; + CloseHandle(h); + return 1; +} + + +static int sig_event(int sig) +{ + char name[EVT_NAME_LEN]; + HANDLE h; + make_name(name, sig); + if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) { + make_name(name, EVT_RUNNING); + if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name))) + return -1; + CloseHandle(h); + return 0; + } + SetEvent(h); + CloseHandle(h); + return 1; +} + + +static void daemon_help(FILE * f, const char * ident, const char * message) +{ + fprintf(f, + "%s: %s.\n" + "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", + ident, message, ident); + fflush(f); +} + + +///////////////////////////////////////////////////////////////////////////// +// Parent Process + + +static BOOL WINAPI parent_console_handler(DWORD event) +{ + switch (event) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TRUE; // Ignore + } + return FALSE; // continue with next handler ... +} + + +static int parent_main(HANDLE rev) +{ + HANDLE dev; + HANDLE ht[2]; + char * cmdline; + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD rc, exitcode; + + // Ignore ^C, ^BREAK in parent + SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/); + + // Create event used by child to signal daemon_detach() + if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) { + CloseHandle(rev); + return 101; + } + + // Restart process with same args + cmdline = GetCommandLineA(); + memset(&si, 0, sizeof(si)); si.cb = sizeof(si); + + if (!CreateProcessA( + NULL, cmdline, + NULL, NULL, TRUE/*inherit*/, + 0, NULL, NULL, &si, &pi)) { + fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); + CloseHandle(rev); CloseHandle(dev); + return 101; + } + CloseHandle(pi.hThread); + + // Wait for daemon_detach() or exit() + ht[0] = dev; ht[1] = pi.hProcess; + rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE); + if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) { + fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc); + TerminateProcess(pi.hProcess, 200); + } + CloseHandle(rev); CloseHandle(dev); + + // Get exit code + if (!GetExitCodeProcess(pi.hProcess, &exitcode)) + exitcode = 201; + else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK + exitcode = 0; + + CloseHandle(pi.hProcess); + return exitcode; +} + + +///////////////////////////////////////////////////////////////////////////// +// Child Process + + +static int svc_mode; // Running as service? +static int svc_paused; // Service paused? + +static void service_report_status(int state, int waithint); + + +// Tables of signal handler and corresponding events +typedef void (*sigfunc_t)(int); + +#define MAX_SIG_HANDLERS 8 + +static int num_sig_handlers = 0; +static sigfunc_t sig_handlers[MAX_SIG_HANDLERS]; +static int sig_numbers[MAX_SIG_HANDLERS]; +static HANDLE sig_events[MAX_SIG_HANDLERS]; + +static HANDLE sighup_handle, sigint_handle, sigbreak_handle; +static HANDLE sigterm_handle, sigusr1_handle; + +static HANDLE running_event; + +static int reopen_stdin, reopen_stdout, reopen_stderr; + + +// Handler for windows console events + +static BOOL WINAPI child_console_handler(DWORD event) +{ + // Caution: runs in a new thread + // TODO: Guard with a mutex + HANDLE h = 0; + switch (event) { + case CTRL_C_EVENT: // (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); +} + +static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv) +{ + // Keep EVT_RUNNING open until exit + running_event = hev; + + // Install console handler + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); + + // Install restart handler + atexit(child_exit); + + // Continue in main_func() to do the real work + return main_func(argc, argv); +} + + +// Simulate signal() + +sigfunc_t daemon_signal(int sig, sigfunc_t func) +{ + int i; + HANDLE h; + if (func == SIG_DFL || func == SIG_IGN) + return func; // TODO + for (i = 0; i < num_sig_handlers; i++) { + if (sig_numbers[i] == sig) { + sigfunc_t old = sig_handlers[i]; + sig_handlers[i] = func; + return old; + } + } + if (num_sig_handlers >= MAX_SIG_HANDLERS) + return SIG_ERR; + if (!(h = create_event((!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; +} + + +// strsignal() + +const char * daemon_strsignal(int sig) +{ + switch (sig) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGTERM: return "SIGTERM"; + case SIGBREAK:return "SIGBREAK"; + case SIGUSR1: return "SIGUSR1"; + case SIGUSR2: return "SIGUSR2"; + default: return "*UNKNOWN*"; + } +} + + +// Simulate sleep() + +void daemon_sleep(int seconds) +{ + do { + if (num_sig_handlers <= 0) { + Sleep(seconds*1000L); + } + else { + // Wait for any signal or timeout + DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events, + FALSE/*OR*/, seconds*1000L); + if (rc != WAIT_TIMEOUT) { + if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) { + fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc); + Sleep(seconds*1000L); + return; + } + // Call Handler + sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]); + break; + } + } + } while (svc_paused); +} + + +// Disable/Enable console + +void daemon_disable_console() +{ + SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); + reopen_stdin = reopen_stdout = reopen_stderr = 0; + if (isatty(fileno(stdin))) { + fclose(stdin); reopen_stdin = 1; + } + if (isatty(fileno(stdout))) { + fclose(stdout); reopen_stdout = 1; + } + if (isatty(fileno(stderr))) { + fclose(stderr); reopen_stderr = 1; + } + FreeConsole(); + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); +} + +int daemon_enable_console(const char * title) +{ + BOOL ok; + SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); + ok = AllocConsole(); + SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); + if (!ok) + return -1; + if (title) + SetConsoleTitleA(title); + if (reopen_stdin) + freopen("conin$", "r", stdin); + if (reopen_stdout) + freopen("conout$", "w", stdout); + if (reopen_stderr) + freopen("conout$", "w", stderr); + reopen_stdin = reopen_stdout = reopen_stderr = 0; + return 0; +} + + +// Detach daemon from console & parent + +int daemon_detach(const char * ident) +{ + if (!svc_mode) { + if (ident) { + // Print help + FILE * f = ( isatty(fileno(stdout)) ? stdout + : isatty(fileno(stderr)) ? stderr : NULL); + if (f) + daemon_help(f, ident, "now detaches from console into background mode"); + } + // Signal detach to parent + if (sig_event(EVT_DETACHED) != 1) { + if (!debugging()) + return -1; + } + daemon_disable_console(); + } + else { + // Signal end of initialization to service control manager + service_report_status(SERVICE_RUNNING, 0); + 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; + mb.text = text; + if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid))) + return -1; + + // Wait for args taken + if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0) + TerminateThread(ht, 0); + CloseHandle(mb.taken); + CloseHandle(ht); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +// Spawn a command and redirect outbuf +// return command's exitcode or -1 on error + +int daemon_spawn(const char * cmd, + const char * inpbuf, int inpsize, + char * outbuf, int outsize ) +{ + HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h; + 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; +} + + +///////////////////////////////////////////////////////////////////////////// +// Initd Functions + +static int wait_signaled(HANDLE h, int seconds) +{ + int i; + for (i = 0; ; ) { + if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) + return 0; + if (++i >= seconds) + return -1; + fputchar('.'); fflush(stdout); + } +} + + +static int wait_evt_running(int seconds, int exists) +{ + int i; + if (event_exists(EVT_RUNNING) == exists) + return 0; + for (i = 0; ; ) { + Sleep(1000); + if (event_exists(EVT_RUNNING) == exists) + return 0; + if (++i >= seconds) + return -1; + fputchar('.'); fflush(stdout); + } +} + + +static int is_initd_command(char * s) +{ + if (!strcmp(s, "status")) + return EVT_RUNNING; + if (!strcmp(s, "stop")) + return SIGTERM; + if (!strcmp(s, "reload")) + return SIGHUP; + if (!strcmp(s, "sigusr1")) + return SIGUSR1; + if (!strcmp(s, "sigusr2")) + return SIGUSR2; + if (!strcmp(s, "restart")) + return EVT_RESTART; + return -1; +} + + +static int initd_main(const char * ident, int argc, char **argv) +{ + int rc; + if (argc < 2) + return -1; + if ((rc = is_initd_command(argv[1])) < 0) + return -1; + if (argc != 2) { + printf("%s: no arguments allowed for command %s\n", ident, argv[1]); + return 1; + } + + switch (rc) { + default: + case EVT_RUNNING: + printf("Checking for %s:", ident); fflush(stdout); + rc = event_exists(EVT_RUNNING); + puts(rc ? " running" : " not running"); + return (rc ? 0 : 1); + + case SIGTERM: + printf("Stopping %s:", ident); fflush(stdout); + rc = sig_event(SIGTERM); + if (rc <= 0) { + puts(rc < 0 ? " not running" : " error"); + return (rc < 0 ? 0 : 1); + } + rc = wait_evt_running(10, 0); + puts(!rc ? " done" : " timeout"); + return (!rc ? 0 : 1); + + case SIGHUP: + printf("Reloading %s:", ident); fflush(stdout); + rc = sig_event(SIGHUP); + puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); + return (rc > 0 ? 0 : 1); + + case SIGUSR1: + case SIGUSR2: + printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); + rc = sig_event(rc); + puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); + return (rc > 0 ? 0 : 1); + + case EVT_RESTART: + { + HANDLE rst; + printf("Stopping %s:", ident); fflush(stdout); + if (event_exists(EVT_DETACHED)) { + puts(" not detached, cannot restart"); + return 1; + } + if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { + puts(" error"); + return 1; + } + rc = sig_event(SIGTERM); + if (rc <= 0) { + puts(rc < 0 ? " not running" : " error"); + CloseHandle(rst); + return 1; + } + rc = wait_signaled(rst, 10); + CloseHandle(rst); + if (rc) { + puts(" timeout"); + return 1; + } + puts(" done"); + Sleep(100); + + printf("Starting %s:", ident); fflush(stdout); + rc = wait_evt_running(10, 1); + puts(!rc ? " done" : " error"); + return (!rc ? 0 : 1); + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// Windows Service Functions + +int daemon_winsvc_exitcode; // Set by app to exit(code) + +static SERVICE_STATUS_HANDLE svc_handle; +static SERVICE_STATUS svc_status; + + +// Report status to SCM + +static void service_report_status(int state, int seconds) +{ + // TODO: Avoid race + static DWORD checkpoint = 1; + 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); + } + } +} + + +// Control the service, called by SCM + +static void WINAPI service_control(DWORD ctrlcode) +{ + switch (ctrlcode) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + service_report_status(SERVICE_STOP_PENDING, 30); + svc_paused = 0; + 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; + } +} + + +// Exit handler for service + +static void service_exit(void) +{ + // Close signal events + int i; + for (i = 0; i < num_sig_handlers; i++) + CloseHandle(sig_events[i]); + num_sig_handlers = 0; + + // Set exitcode + if (daemon_winsvc_exitcode) { + svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode; + } + // Report stopped + service_report_status(SERVICE_STOPPED, 0); +} + + +// Variables for passing main(argc, argv) from daemon_main to service_main() +static int (*svc_main_func)(int, char **); +static int svc_main_argc; +static char ** svc_main_argv; + +// Main function for service, called by service dispatcher + +static void WINAPI service_main(DWORD argc, LPSTR * argv) +{ + char path[MAX_PATH], *p; + ARGUSED(argc); + + // Register control handler + svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); + + // Init service status + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS; + service_report_status(SERVICE_START_PENDING, 10); + + // Service started in \windows\system32, change to .exe directory + if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) { + *p = 0; SetCurrentDirectoryA(path); + } + + // Install exit handler + atexit(service_exit); + + // Do the real work, service status later updated by daemon_detach() + daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv); + + exit(daemon_winsvc_exitcode); + // ... continued in service_exit() +} + + +///////////////////////////////////////////////////////////////////////////// +// Windows Service Admin Functions + +// Set Service description (Win2000/XP) + +static int svcadm_setdesc(SC_HANDLE hs, const char * desc) +{ + HANDLE hdll; + BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID); + BOOL ret; + if (!(hdll = LoadLibraryA("ADVAPI32.DLL"))) + return FALSE; + if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A")))) + ret = FALSE; + else { + SERVICE_DESCRIPTIONA sd = { (char *)desc }; + ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd); + } + FreeLibrary(hdll); + return ret; +} + + +// Service install/remove commands + +static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, + int argc, char **argv ) +{ + int remove; 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; + } + // Append options + strcat(path, " "); strcat(path, svc_opts->cmd_opt); + for (i = 2; i < argc; i++) { + const char * s = argv[i]; + if (strlen(path)+strlen(s)+1 >= sizeof(path)) + break; + strcat(path, " "); strcat(path, s); + } + // Create + if (!(hs = CreateService(hm, + svc_opts->svcname, svc_opts->dispname, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, + NULL/*no load ordering*/, NULL/*no tag id*/, + ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) { + 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; +} + + +///////////////////////////////////////////////////////////////////////////// +// Main Function + +// This function must be called from main() +// main_func is the function doing the real work + +int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, + int (*main_func)(int, char **), int argc, char **argv ) +{ + int rc; +#ifdef _DEBUG + // Enable Debug heap checks + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) + |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); +#endif + + // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters + if ((rc = initd_main(ident, argc, argv)) >= 0) + return rc; + // Check for [install|remove] parameters + if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0) + return rc; + + // Run as service if svc_opts.cmd_opt is given as first(!) argument + svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt)); + + if (!svc_mode) { + // Daemon: Try to simulate a Unix-like daemon + HANDLE rev; + BOOL exists; + + // Create main event to detect process type: + // 1. new: parent process => start child and wait for detach() or exit() of child. + // 2. exists && signaled: child process => do the real work, signal detach() to parent + // 3. exists && !signaled: already running => exit() + if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists))) + return 100; + + if (!exists && !debugging()) { + // Event new => parent process + return parent_main(rev); + } + + if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) { + // Event was signaled => In child process + return child_main(rev, main_func, argc, argv); + } + + // Event no longer signaled => Already running! + daemon_help(stdout, ident, "already running"); + CloseHandle(rev); + return 1; + } + else { + // Service: Start service_main() via SCM + SERVICE_TABLE_ENTRY service_table[] = { + { (char*)svc_opts->svcname, service_main }, { NULL, NULL } + }; + + svc_main_func = main_func; + svc_main_argc = argc; + svc_main_argv = argv; + if (!StartServiceCtrlDispatcher(service_table)) { + 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); +#endif + return 100; + } + Sleep(1000); + ExitThread(0); // Do not redo exit() processing + /*NOTREACHED*/ + return 0; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// Test Program + +#ifdef TEST + +static volatile sig_atomic_t caughtsig = 0; + +static void sig_handler(int sig) +{ + caughtsig = sig; +} + +static void test_exit(void) +{ + printf("Main exit\n"); +} + +int test_main(int argc, char **argv) +{ + int i; + int debug = 0; + 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" + }; + + 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 new file mode 100644 index 0000000..60b72a3 --- /dev/null +++ b/os_win32/daemon_win32.h @@ -0,0 +1,71 @@ +/* + * os_win32/daemon_win32.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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 DAEMON_WIN32_H +#define DAEMON_WIN32_H + +#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h,v 1.6 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include + +// Additional non-ANSI signals +#define SIGHUP (NSIG+1) +#define SIGUSR1 (NSIG+2) +#define SIGUSR2 (NSIG+3) + + +// Options for Windows service +typedef struct daemon_winsvc_options_s { + const char * cmd_opt; // argv[1] option for services + // For service "install" command only: + const char * svcname; // Service name + const char * dispname; // Service display name + const char * descript; // Service description +} daemon_winsvc_options; + + +// This function must be called from main() +int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, + int (*main_func)(int, char **), int argc, char **argv ); + +// exit(code) returned by a service +extern int daemon_winsvc_exitcode; + +// Simulate signal() +void (*daemon_signal(int sig, void (*func)(int)))(int); +const char * daemon_strsignal(int sig); + +// Simulate sleep() +void daemon_sleep(int seconds); + +// Disable/Enable console +void daemon_disable_console(void); +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, + char * outbuf, int outsize ); + +#endif // DAEMON_WIN32_H diff --git a/os_win32/hostname_win32.c b/os_win32/hostname_win32.c new file mode 100644 index 0000000..6202eff --- /dev/null +++ b/os_win32/hostname_win32.c @@ -0,0 +1,186 @@ +/* + * os_win32/hostname_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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.c,v 1.4 2006/04/12 14:54:28 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) +{ + HANDLE 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) +{ + HANDLE 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, name, &size) == ERROR_SUCCESS && type == REG_SZ)) + size = 0; + if (size == 0 && domain) { + size = len-1; + if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ)) + size = 0; + } + RegCloseKey(hk); + return size; +} + + +static int gethostdomname(int domain, char * name, int len) +{ + DWORD size; FIXED_INFO info; + + // try KERNEL32.dll::GetComputerNameEx() + size = len - 1; + if (CallGetComputerNameExA((!domain ? 1:2/*ComputerNameDnsHost:Domain*/), name, &size)) + return 0; + + // try IPHlpApi.dll::GetNetworkParams() + size = sizeof(info); + if (CallGetNetworkParams(&info, &size) == ERROR_SUCCESS) { + strncpy(name, (!domain?info.HostName:info.DomainName), len-1); name[len-1] = 0; + return 0; + } + + // try registry + if (GetNamesFromRegistry(domain, name, len)) + return 0; + + if (domain) + return -1; + + // last resort: get NETBIOS name + size = len - 1; + if (GetComputerNameA(name, &size)) + return 0; + + return -1; +} + + +int gethostname(char * name, int len) +{ + return gethostdomname(0, name, len); +} + + +int getdomainname(char * name, int len) +{ + return gethostdomname(1, name, len); +} + + +#ifdef TEST + +#include + +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 new file mode 100644 index 0000000..259735f --- /dev/null +++ b/os_win32/hostname_win32.h @@ -0,0 +1,27 @@ +/* + * os_win32/hostname_win32.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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.3 2006/04/12 14:54:28 ballen4705 Exp $\n" + +int gethostname(char * name, int len); +int getdomainname(char * name, int len); + +#endif // HOSTNAME_WIN32_H diff --git a/os_win32/syslog.h b/os_win32/syslog.h new file mode 100644 index 0000000..7ef23f0 --- /dev/null +++ b/os_win32/syslog.h @@ -0,0 +1,62 @@ +/* + * os_win32/syslog.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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 SYSLOG_H +#define SYSLOG_H + +#define SYSLOG_H_CVSID "$Id: syslog.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include + +/* EVENTLOG_ERROR_TYPE: */ +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +/* EVENTLOG_WARNING_TYPE: */ +#define LOG_WARNING 4 +/* EVENTLOG_INFORMATION_TYPE: */ +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +/* event log: */ +#define LOG_DAEMON ( 3<<3) +/* ident.log: */ +#define LOG_LOCAL0 (16<<3) +/* ident1-7.log: */ +#define LOG_LOCAL1 (17<<3) +#define LOG_LOCAL2 (18<<3) +#define LOG_LOCAL3 (19<<3) +#define LOG_LOCAL4 (20<<3) +#define LOG_LOCAL5 (21<<3) +#define LOG_LOCAL6 (22<<3) +#define LOG_LOCAL7 (23<<3) + +#define LOG_FACMASK 0x03f8 +#define LOG_FAC(f) (((f) & LOG_FACMASK) >> 3) + +#define LOG_PID 0x01 + +void openlog(const char * ident, int option, int facility); + +void closelog(void); + +void vsyslog(int priority, const char * message, va_list args); + +#endif /* SYSLOG_H */ diff --git a/os_win32/syslog_win32.c b/os_win32/syslog_win32.c new file mode 100644 index 0000000..7ed039f --- /dev/null +++ b/os_win32/syslog_win32.c @@ -0,0 +1,435 @@ +/* + * os_win32/syslog_win32.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2004-6 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. + * + */ + +// 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". + + +#include "syslog.h" + +#include +#include +#include +#include +#include // getpid() + +#define WIN32_LEAN_AND_MEAN +#include // RegisterEventSourceA(), ReportEventA(), ... + +const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.6 2006/04/12 14:54:29 ballen4705 Exp $" +SYSLOG_H_CVSID; + +#ifdef _MSC_VER +// MSVC +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +#define ARGUSED(x) ((void)(x)) + + +#ifndef _MT +//MT runtime not necessary, because thread uses no unsafe lib functions +//#error Program must be linked with multithreaded runtime library +#endif + + +#ifdef TESTEVT +// Redirect event log to stdout for testing + +static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid, + WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data) +{ + int i; + printf("%u %lu:%s", type, id, nstrings != 1?"\n":""); + for (i = 0; i < nstrings; i++) + printf(" \"%s\"\n", strings[i]); + fflush(stdout); + return TRUE; +} + +HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source) +{ + return (HANDLE)42; +} + +#define ReportEventA Test_ReportEventA +#define RegisterEventSourceA Test_RegisterEventSourceA +#endif // TESTEVT + + +// Event message ids, +// should be identical to MSG_SYSLOG* in "syslogevt.h" +// (generated by "mc" from "syslogevt.mc") +#define MSG_SYSLOG 0x00000000L +#define MSG_SYSLOG_01 0x00000001L +// ... +#define MSG_SYSLOG_10 0x0000000AL + +static char sl_ident[100]; +static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1]; +static FILE * sl_logfile; +static char sl_pidstr[16]; +static HANDLE sl_hevtsrc; + + +// Ring buffer for event log output via thread +#define MAXLINES 10 +#define LINELEN 200 + +static HANDLE evt_hthread; +static char evt_lines[MAXLINES][LINELEN+1]; +static int evt_priorities[MAXLINES]; +static volatile int evt_timeout; +static int evt_index_in, evt_index_out; +static HANDLE evt_wait_in, evt_wait_out; + + +// Map syslog priority to event type + +static WORD pri2evtype(int priority) +{ + switch (priority) { + default: + case LOG_EMERG: case LOG_ALERT: + case LOG_CRIT: case LOG_ERR: + return EVENTLOG_ERROR_TYPE; + case LOG_WARNING: + return EVENTLOG_WARNING_TYPE; + case LOG_NOTICE: case LOG_INFO: + case LOG_DEBUG: + return EVENTLOG_INFORMATION_TYPE; + } +} + + +// Map syslog priority to string + +static const char * pri2text(int priority) +{ + switch (priority) { + case LOG_EMERG: return "EMERG"; + case LOG_ALERT: return "ALERT"; + case LOG_CRIT: return "CRIT"; + default: + case LOG_ERR: return "ERROR"; + case LOG_WARNING: return "Warn"; + case LOG_NOTICE: return "Note"; + case LOG_INFO: return "Info"; + case LOG_DEBUG: return "Debug"; + } +} + + +// Output cnt events from ring buffer + +static void report_events(int cnt) +{ + const char * msgs[3+MAXLINES]; + + int i, pri; + if (cnt <= 0) + return; + if (cnt > MAXLINES) + cnt = MAXLINES; + + pri = evt_priorities[evt_index_out]; + + msgs[0] = sl_ident; + msgs[1] = sl_pidstr; + msgs[2] = pri2text(pri); + for (i = 0; i < cnt; i++) { + //assert(evt_priorities[evt_index_out] == pri); + msgs[3+i] = evt_lines[evt_index_out]; + if (++evt_index_out >= MAXLINES) + evt_index_out = 0; + } + ReportEventA(sl_hevtsrc, + pri2evtype(pri), // type + 0, MSG_SYSLOG+cnt, // category, message id + NULL, // no security id + (WORD)(3+cnt), 0, // 3+cnt strings, ... + msgs, NULL); // ... , no data +} + + +// Thread to combine several syslog lines into one event log entry + +static ULONG WINAPI event_logger_thread(LPVOID arg) +{ + int cnt; + ARGUSED(arg); + + cnt = 0; + for (;;) { + // Wait for first line ... + int prior, i, rest; + if (cnt == 0) { + if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0) + break; + cnt = 1; + } + + // ... wait some time for more lines with same prior + i = evt_index_out; + prior = evt_priorities[i]; + rest = 0; + while (cnt < MAXLINES) { + long timeout = + evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES); + if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0) + break; + if (++i >= MAXLINES) + i = 0; + if (evt_priorities[i] != prior) { + rest = 1; + break; + } + cnt++; + } + + // Output all in one event log entry + report_events(cnt); + + // Signal space + if (!ReleaseSemaphore(evt_wait_in, cnt, NULL)) + break; + cnt = rest; + } + return 0; +} + + +static void on_exit_event_logger(void) +{ + // Output lines immediate if exiting + evt_timeout = 0; + // Wait for thread to finish + WaitForSingleObject(evt_hthread, 1000L); + CloseHandle(evt_hthread); +#if 0 + if (sl_hevtsrc) { + DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0; + } +#else + // Leave event message source open to prevent losing messages during shutdown +#endif +} + + +static int start_event_logger() +{ + DWORD tid; + evt_timeout = 1; + if ( !(evt_wait_in = CreateSemaphore(NULL, MAXLINES, MAXLINES, NULL)) + || !(evt_wait_out = CreateSemaphore(NULL, 0, MAXLINES, NULL))) { + fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError()); + return -1; + } + if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) { + fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError()); + return -1; + } + atexit(on_exit_event_logger); + return 0; +} + + +// Write lines to event log ring buffer + +static void write_event_log(int priority, const char * lines) +{ + int cnt = 0; + int i; + for (i = 0; lines[i]; i++) { + int len = 0; + while (lines[i+len] && lines[i+len] != '\n') + len++; + ; + if (len > 0) { + // Wait for space + if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0) + return; + // Copy line + evt_priorities[evt_index_in] = priority; + memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN)); + if (len < LINELEN) + evt_lines[evt_index_in][len] = 0; + if (++evt_index_in >= MAXLINES) + evt_index_in = 0; + // Signal avail if ring buffer full + if (++cnt >= MAXLINES) { + ReleaseSemaphore(evt_wait_out, cnt, NULL); + cnt = 0; + } + i += len; + } + if (!lines[i]) + break; + } + + // Signal avail + if (cnt > 0) + ReleaseSemaphore(evt_wait_out, cnt, NULL); + Sleep(1); +} + + +// Write lines to logfile + +static void write_logfile(FILE * f, int priority, const char * lines) +{ + time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13]; + int i; + + now = time((time_t*)0); + if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now))) + strcpy(stamp,"?"); + + for (i = 0; lines[i]; i++) { + int len = 0; + while (lines[i+len] && lines[i+len] != '\n') + len++; + if (len > 0) { + fprintf(f, "%s %s[%s]: %-5s: ", + stamp, sl_ident, sl_pidstr, pri2text(priority)); + fwrite(lines+i, len, 1, f); + fputc('\n', f); + i += len; + } + if (!lines[i]) + break; + } +} + + +void openlog(const char *ident, int logopt, int facility) +{ + int pid; + if (sl_logpath[0] || sl_logfile || sl_hevtsrc) + return; // Already open + + strncpy(sl_ident, ident, sizeof(sl_ident)-1); + // logopt==LOG_PID assumed + ARGUSED(logopt); + pid = getpid(); + if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0) + strcpy(sl_pidstr,"?"); + + if (facility == LOG_LOCAL0) // "ident.log" + strcat(strcpy(sl_logpath, sl_ident), ".log"); + else if (facility == LOG_LOCAL1) // stdout + sl_logfile = stdout; + else if (facility == LOG_LOCAL2) // stderr + sl_logfile = stderr; + else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log" + snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log", + sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2)); + } + else // Assume LOG_DAEMON, use event log if possible, else "ident.log" + if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) { + // Cannot open => Use logfile + long err = GetLastError(); + strcat(strcpy(sl_logpath, sl_ident), ".log"); + if (GetVersion() & 0x80000000) + fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n", + sl_ident, sl_logpath); + else + fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n", + sl_ident, err, sl_logpath); + } + else { + // Start event log thread + start_event_logger(); + } + //assert(sl_logpath[0] || sl_logfile || sl_hevtsrc); + +} + + +void closelog() +{ +} + + +void vsyslog(int priority, const char * message, va_list args) +{ + char buffer[1000]; + + // Translation of %m to error text not supported yet + if (strstr(message, "%m")) + message = "Internal error: \"%%m\" in log message"; + + // Format message + if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0) + strcpy(buffer, "Internal Error: buffer overflow"); + + if (sl_hevtsrc) { + // Write to event log + write_event_log(priority, buffer); + } + else if (sl_logfile) { + // Write to stdout/err + write_logfile(sl_logfile, priority, buffer); + fflush(sl_logfile); + } + else if (sl_logpath[0]) { + // Append to logfile + FILE * f; + if (!(f = fopen(sl_logpath, "a"))) + return; + write_logfile(f, priority, buffer); + fclose(f); + } +} + + +#ifdef TEST +// Test program + +void syslog(int priority, const char *message, ...) +{ + va_list args; + va_start(args, message); + vsyslog(priority, message, args); + va_end(args); +} + +int main(int argc, char* argv[]) +{ + int i; + openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1)); + syslog(LOG_INFO, "Info\n"); + syslog(LOG_WARNING, "Warning %d\n\n", 42); + syslog(LOG_ERR, "Error %s", "Fatal"); + for (i = 0; i < 100; i++) { + char buf[LINELEN]; + if (i % 13 == 0) + Sleep(1000L); + sprintf(buf, "Log Line %d\n", i); + syslog(i % 17 ? LOG_INFO : LOG_ERR, buf); + } + closelog(); + return 0; +} + +#endif diff --git a/posix/regcomp.c b/posix/regcomp.c new file mode 100644 index 0000000..f25ecae --- /dev/null +++ b/posix/regcomp.c @@ -0,0 +1,3544 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + int length, reg_syntax_t syntax); +static void re_compile_fastmap_iter (regex_t *bufp, + const re_dfastate_t *init_state, + char *fastmap); +static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len); +static reg_errcode_t init_word_char (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void free_charset (re_charset_t *cset); +#endif /* RE_ENABLE_I18N */ +static void free_workarea_compile (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +static reg_errcode_t analyze (re_dfa_t *dfa); +static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node); +static void calc_first (re_dfa_t *dfa, bin_tree_t *node); +static void calc_next (re_dfa_t *dfa, bin_tree_t *node); +static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node); +static reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node, + int top_clone_node, int root_node, + unsigned int constraint); +static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx, + unsigned int constraint); +static int search_duplicated_node (re_dfa_t *dfa, int org_node, + unsigned int constraint); +static reg_errcode_t calc_eclosure (re_dfa_t *dfa); +static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, + int node, int root); +static void calc_inveclosure (re_dfa_t *dfa); +static int fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +static re_token_t fetch_token (re_string_t *input, reg_syntax_t syntax); +static int peek_token (re_token_t *token, re_string_t *input, + reg_syntax_t syntax); +static int peek_token_bracket (re_token_t *token, re_string_t *input, + reg_syntax_t syntax); +static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, + re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, + reg_errcode_t *err); +static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token, int token_len, + re_dfa_t *dfa, + reg_syntax_t syntax); +static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token); +#ifndef _LIBC +# ifdef RE_ENABLE_I18N +static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, int *range_alloc, + bracket_elem_t *start_elem, + bracket_elem_t *end_elem); +static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *coll_sym_alloc, + const unsigned char *name); +# else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset, + bracket_elem_t *start_elem, + bracket_elem_t *end_elem); +static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset, + const unsigned char *name); +# endif /* not RE_ENABLE_I18N */ +#endif /* not _LIBC */ +#ifdef RE_ENABLE_I18N +static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *equiv_class_alloc, + const unsigned char *name); +static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *char_class_alloc, + const unsigned char *class_name, + reg_syntax_t syntax); +#else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset, + const unsigned char *name); +static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset, + const unsigned char *class_name, + reg_syntax_t syntax); +#endif /* not RE_ENABLE_I18N */ +static bin_tree_t *build_word_op (re_dfa_t *dfa, int not, reg_errcode_t *err); +static void free_bin_tree (bin_tree_t *tree); +static bin_tree_t *create_tree (bin_tree_t *left, bin_tree_t *right, + re_token_type_t type, int index); +static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +const char __re_error_msgid[] attribute_hidden = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ + }; + +const size_t __re_error_msgid_idx[] attribute_hidden = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length LENGTH) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = re_compile_internal (bufp, pattern, length, re_syntax_options); + + if (!ret) + return NULL; + return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + char *fastmap = bufp->fastmap; + + memset (fastmap, '\0', sizeof (char) * SBC_MAX); + re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); + if (dfa->init_state != dfa->init_state_word) + re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); + if (dfa->init_state != dfa->init_state_nl) + re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); + if (dfa->init_state != dfa->init_state_begbuf) + re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); + bufp->fastmap_accurate = 1; + return 0; +} +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +static inline void +re_set_fastmap (char *fastmap, int icase, int ch) +{ + fastmap[ch] = 1; + if (icase) + fastmap[tolower (ch)] = 1; +} + +/* Helper function for re_compile_fastmap. + Compile fastmap for the initial_state INIT_STATE. */ + +static void +re_compile_fastmap_iter (bufp, init_state, fastmap) + regex_t *bufp; + const re_dfastate_t *init_state; + char *fastmap; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + int node_cnt; + int icase = (MB_CUR_MAX == 1 && (bufp->syntax & RE_ICASE)); + for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) + { + int node = init_state->nodes.elems[node_cnt]; + re_token_type_t type = dfa->nodes[node].type; + + if (type == CHARACTER) + re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); + else if (type == SIMPLE_BRACKET) + { + int i, j, ch; + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (dfa->nodes[node].opr.sbcset[i] & (1 << j)) + re_set_fastmap (fastmap, icase, ch); + } +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET) + { + int i; + re_charset_t *cset = dfa->nodes[node].opr.mbcset; + if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes + || cset->nranges || cset->nchar_classes) + { +# ifdef _LIBC + if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0) + { + /* In this case we want to catch the bytes which are + the first byte of any collation elements. + e.g. In da_DK, we want to catch 'a' since "aa" + is a valid collation element, and don't catch + 'b' since 'b' is the only collation element + which starts from 'b'. */ + int j, ch; + const int32_t *table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (table[ch] < 0) + re_set_fastmap (fastmap, icase, ch); + } +# else + if (MB_CUR_MAX > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + re_set_fastmap (fastmap, icase, i); +# endif /* not _LIBC */ + } + for (i = 0; i < cset->nmbchars; ++i) + { + char buf[256]; + mbstate_t state; + memset (&state, '\0', sizeof (state)); + __wcrtomb (buf, cset->mbchars[i], &state); + re_set_fastmap (fastmap, icase, *(unsigned char *) buf); + } + } +#endif /* RE_ENABLE_I18N */ + else if (type == END_OF_RE || type == OP_PERIOD) + { + memset (fastmap, '\1', sizeof (char) * SBC_MAX); + if (type == END_OF_RE) + bufp->can_be_null = 1; + return; + } + } +} + +/* Entry point for POSIX code. */ +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *__restrict preg; + const char *__restrict pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED + : RE_SYNTAX_POSIX_BASIC); + + preg->buffer = NULL; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = re_malloc (char, SBC_MAX); + if (BE (preg->fastmap == NULL, 0)) + return REG_ESPACE; + + syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + preg->no_sub = !!(cflags & REG_NOSUB); + preg->translate = NULL; + + ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) + ret = REG_EPAREN; + + /* We have already checked preg->fastmap != NULL. */ + if (BE (ret == REG_NOERROR, 1)) + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. This function nevers fails in this implementation. */ + (void) re_compile_fastmap (preg); + else + { + /* Some error occurred while compiling the expression. */ + re_free (preg->fastmap); + preg->fastmap = NULL; + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (BE (errcode < 0 + || errcode >= (int) (sizeof (__re_error_msgid_idx) + / sizeof (__re_error_msgid_idx[0])), 0)) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (BE (errbuf_size != 0, 1)) + { + if (BE (msg_size > errbuf_size, 0)) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +static void +free_dfa_content (re_dfa_t *dfa) +{ + int i, j; + + re_free (dfa->subexps); + + for (i = 0; i < dfa->nodes_len; ++i) + { + re_token_t *node = dfa->nodes + i; +#ifdef RE_ENABLE_I18N + if (node->type == COMPLEX_BRACKET && node->duplicated == 0) + free_charset (node->opr.mbcset); + else +#endif /* RE_ENABLE_I18N */ + if (node->type == SIMPLE_BRACKET && node->duplicated == 0) + re_free (node->opr.sbcset); + } + re_free (dfa->nexts); + for (i = 0; i < dfa->nodes_len; ++i) + { + if (dfa->eclosures != NULL) + re_node_set_free (dfa->eclosures + i); + if (dfa->inveclosures != NULL) + re_node_set_free (dfa->inveclosures + i); + if (dfa->edests != NULL) + re_node_set_free (dfa->edests + i); + } + re_free (dfa->edests); + re_free (dfa->eclosures); + re_free (dfa->inveclosures); + re_free (dfa->nodes); + + for (i = 0; i <= dfa->state_hash_mask; ++i) + { + struct re_state_table_entry *entry = dfa->state_table + i; + for (j = 0; j < entry->num; ++j) + { + re_dfastate_t *state = entry->array[j]; + free_state (state); + } + re_free (entry->array); + } + re_free (dfa->state_table); + + if (dfa->word_char != NULL) + re_free (dfa->word_char); +#ifdef DEBUG + re_free (dfa->re_str); +#endif + + re_free (dfa); +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + if (BE (dfa != NULL, 1)) + free_dfa_content (dfa); + + re_free (preg->fastmap); +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +# ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec above without link errors. */ +weak_function +# endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + char *fastmap; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (re_comp_buf.buffer) + { + fastmap = re_comp_buf.fastmap; + re_comp_buf.fastmap = NULL; + __regfree (&re_comp_buf); + memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); + re_comp_buf.fastmap = fastmap; + } + + if (re_comp_buf.fastmap == NULL) + { + re_comp_buf.fastmap = (char *) malloc (SBC_MAX); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (__re_error_msgid + + __re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + __regfree (&re_comp_buf); +} +#endif + +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. + Compile the regular expression PATTERN, whose length is LENGTH. + SYNTAX indicate regular expression's syntax. */ + +static reg_errcode_t +re_compile_internal (preg, pattern, length, syntax) + regex_t *preg; + const char * pattern; + int length; + reg_syntax_t syntax; +{ + reg_errcode_t err = REG_NOERROR; + re_dfa_t *dfa; + re_string_t regexp; + + /* Initialize the pattern buffer. */ + preg->fastmap_accurate = 0; + preg->syntax = syntax; + preg->not_bol = preg->not_eol = 0; + preg->used = 0; + preg->re_nsub = 0; + preg->can_be_null = 0; + preg->regs_allocated = REGS_UNALLOCATED; + + /* Initialize the dfa. */ + dfa = (re_dfa_t *) preg->buffer; + if (preg->allocated < sizeof (re_dfa_t)) + { + /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. If ->buffer is NULL this + is a simple allocation. */ + dfa = re_realloc (preg->buffer, re_dfa_t, 1); + if (dfa == NULL) + return REG_ESPACE; + preg->allocated = sizeof (re_dfa_t); + } + preg->buffer = (unsigned char *) dfa; + preg->used = sizeof (re_dfa_t); + + err = init_dfa (dfa, length); + if (BE (err != REG_NOERROR, 0)) + { + re_free (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } +#ifdef DEBUG + dfa->re_str = re_malloc (char, length + 1); + strncpy (dfa->re_str, pattern, length + 1); +#endif + + err = re_string_construct (®exp, pattern, length, preg->translate, + syntax & RE_ICASE); + if (BE (err != REG_NOERROR, 0)) + { + re_free (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } + + /* Parse the regular expression, and build a structure tree. */ + preg->re_nsub = 0; + dfa->str_tree = parse (®exp, preg, syntax, &err); + if (BE (dfa->str_tree == NULL, 0)) + goto re_compile_internal_free_return; + + /* Analyze the tree and collect information which is necessary to + create the dfa. */ + err = analyze (dfa); + if (BE (err != REG_NOERROR, 0)) + goto re_compile_internal_free_return; + + /* Then create the initial state of the dfa. */ + err = create_initial_state (dfa); + + /* Release work areas. */ + free_workarea_compile (preg); + re_string_destruct (®exp); + + if (BE (err != REG_NOERROR, 0)) + { + re_compile_internal_free_return: + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + } + + return err; +} + +/* Initialize DFA. We use the length of the regular expression PAT_LEN + as the initial length of some arrays. */ + +static reg_errcode_t +init_dfa (dfa, pat_len) + re_dfa_t *dfa; + int pat_len; +{ + int table_size; + + memset (dfa, '\0', sizeof (re_dfa_t)); + + dfa->nodes_alloc = pat_len + 1; + dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); + + dfa->states_alloc = pat_len + 1; + + /* table_size = 2 ^ ceil(log pat_len) */ + for (table_size = 1; table_size > 0; table_size <<= 1) + if (table_size > pat_len) + break; + + dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); + dfa->state_hash_mask = table_size - 1; + + dfa->subexps_alloc = 1; + dfa->subexps = re_malloc (re_subexp_t, dfa->subexps_alloc); + dfa->word_char = NULL; + + if (BE (dfa->nodes == NULL || dfa->state_table == NULL + || dfa->subexps == NULL, 0)) + { + /* We don't bother to free anything which was allocated. Very + soon the process will go down anyway. */ + dfa->subexps = NULL; + dfa->state_table = NULL; + dfa->nodes = NULL; + return REG_ESPACE; + } + return REG_NOERROR; +} + +/* Initialize WORD_CHAR table, which indicate which character is + "word". In this case "word" means that it is the word construction + character used by some operators like "\<", "\>", etc. */ + +static reg_errcode_t +init_word_char (dfa) + re_dfa_t *dfa; +{ + int i, j, ch; + dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1); + if (BE (dfa->word_char == NULL, 0)) + return REG_ESPACE; + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (isalnum (ch) || ch == '_') + dfa->word_char[i] |= 1 << j; + return REG_NOERROR; +} + +/* Free the work area which are only used while compiling. */ + +static void +free_workarea_compile (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + free_bin_tree (dfa->str_tree); + dfa->str_tree = NULL; + re_free (dfa->org_indices); + dfa->org_indices = NULL; +} + +/* Create initial states for all contexts. */ + +static reg_errcode_t +create_initial_state (dfa) + re_dfa_t *dfa; +{ + int first, i; + reg_errcode_t err; + re_node_set init_nodes; + + /* Initial states have the epsilon closure of the node which is + the first node of the regular expression. */ + first = dfa->str_tree->first; + dfa->init_node = first; + err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* The back-references which are in initial states can epsilon transit, + since in this case all of the subexpressions can be null. + Then we add epsilon closures of the nodes which are the next nodes of + the back-references. */ + if (dfa->nbackref > 0) + for (i = 0; i < init_nodes.nelem; ++i) + { + int node_idx = init_nodes.elems[i]; + re_token_type_t type = dfa->nodes[node_idx].type; + + int clexp_idx; + if (type != OP_BACK_REF) + continue; + for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) + { + re_token_t *clexp_node; + clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; + if (clexp_node->type == OP_CLOSE_SUBEXP + && clexp_node->opr.idx + 1 == dfa->nodes[node_idx].opr.idx) + break; + } + if (clexp_idx == init_nodes.nelem) + continue; + + if (type == OP_BACK_REF) + { + int dest_idx = dfa->edests[node_idx].elems[0]; + if (!re_node_set_contains (&init_nodes, dest_idx)) + { + re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); + i = 0; + } + } + } + + /* It must be the first time to invoke acquire_state. */ + dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); + /* We don't check ERR here, since the initial state must not be NULL. */ + if (BE (dfa->init_state == NULL, 0)) + return err; + if (dfa->init_state->has_constraint) + { + dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_WORD); + dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_NEWLINE); + dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, + &init_nodes, + CONTEXT_NEWLINE + | CONTEXT_BEGBUF); + if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return err; + } + else + dfa->init_state_word = dfa->init_state_nl + = dfa->init_state_begbuf = dfa->init_state; + + re_node_set_free (&init_nodes); + return REG_NOERROR; +} + +/* Analyze the structure tree, and calculate "first", "next", "edest", + "eclosure", and "inveclosure". */ + +static reg_errcode_t +analyze (dfa) + re_dfa_t *dfa; +{ + int i; + reg_errcode_t ret; + + /* Allocate arrays. */ + dfa->nexts = re_malloc (int, dfa->nodes_alloc); + dfa->org_indices = re_malloc (int, dfa->nodes_alloc); + dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc); + if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL + || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0)) + return REG_ESPACE; + /* Initialize them. */ + for (i = 0; i < dfa->nodes_len; ++i) + { + dfa->nexts[i] = -1; + re_node_set_init_empty (dfa->edests + i); + re_node_set_init_empty (dfa->eclosures + i); + re_node_set_init_empty (dfa->inveclosures + i); + } + + ret = analyze_tree (dfa, dfa->str_tree); + if (BE (ret == REG_NOERROR, 1)) + { + ret = calc_eclosure (dfa); + if (ret == REG_NOERROR) + calc_inveclosure (dfa); + } + return ret; +} + +/* Helper functions for analyze. + This function calculate "first", "next", and "edest" for the subtree + whose root is NODE. */ + +static reg_errcode_t +analyze_tree (dfa, node) + re_dfa_t *dfa; + bin_tree_t *node; +{ + reg_errcode_t ret; + if (node->first == -1) + calc_first (dfa, node); + if (node->next == -1) + calc_next (dfa, node); + if (node->eclosure.nelem == 0) + calc_epsdest (dfa, node); + /* Calculate "first" etc. for the left child. */ + if (node->left != NULL) + { + ret = analyze_tree (dfa, node->left); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + /* Calculate "first" etc. for the right child. */ + if (node->right != NULL) + { + ret = analyze_tree (dfa, node->right); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + return REG_NOERROR; +} + +/* Calculate "first" for the node NODE. */ +static void +calc_first (dfa, node) + re_dfa_t *dfa; + bin_tree_t *node; +{ + int idx, type; + idx = node->node_idx; + type = (node->type == 0) ? dfa->nodes[idx].type : node->type; + + switch (type) + { +#ifdef DEBUG + case OP_OPEN_BRACKET: + case OP_CLOSE_BRACKET: + case OP_OPEN_DUP_NUM: + case OP_CLOSE_DUP_NUM: + case OP_NON_MATCH_LIST: + case OP_OPEN_COLL_ELEM: + case OP_CLOSE_COLL_ELEM: + case OP_OPEN_EQUIV_CLASS: + case OP_CLOSE_EQUIV_CLASS: + case OP_OPEN_CHAR_CLASS: + case OP_CLOSE_CHAR_CLASS: + /* These must not be appeared here. */ + assert (0); +#endif + case END_OF_RE: + case CHARACTER: + case OP_PERIOD: + case OP_DUP_ASTERISK: + case OP_DUP_QUESTION: +#ifdef RE_ENABLE_I18N + case COMPLEX_BRACKET: +#endif /* RE_ENABLE_I18N */ + case SIMPLE_BRACKET: + case OP_BACK_REF: + case ANCHOR: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + node->first = idx; + break; + case OP_DUP_PLUS: +#ifdef DEBUG + assert (node->left != NULL); +#endif + if (node->left->first == -1) + calc_first (dfa, node->left); + node->first = node->left->first; + break; + case OP_ALT: + node->first = idx; + break; + /* else fall through */ + default: +#ifdef DEBUG + assert (node->left != NULL); +#endif + if (node->left->first == -1) + calc_first (dfa, node->left); + node->first = node->left->first; + break; + } +} + +/* Calculate "next" for the node NODE. */ + +static void +calc_next (dfa, node) + re_dfa_t *dfa; + bin_tree_t *node; +{ + int idx, type; + bin_tree_t *parent = node->parent; + if (parent == NULL) + { + node->next = -1; + idx = node->node_idx; + if (node->type == 0) + dfa->nexts[idx] = node->next; + return; + } + + idx = parent->node_idx; + type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type; + + switch (type) + { + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + node->next = idx; + break; + case CONCAT: + if (parent->left == node) + { + if (parent->right->first == -1) + calc_first (dfa, parent->right); + node->next = parent->right->first; + break; + } + /* else fall through */ + default: + if (parent->next == -1) + calc_next (dfa, parent); + node->next = parent->next; + break; + } + idx = node->node_idx; + if (node->type == 0) + dfa->nexts[idx] = node->next; +} + +/* Calculate "edest" for the node NODE. */ + +static void +calc_epsdest (dfa, node) + re_dfa_t *dfa; + bin_tree_t *node; +{ + int idx; + idx = node->node_idx; + if (node->type == 0) + { + if (dfa->nodes[idx].type == OP_DUP_ASTERISK + || dfa->nodes[idx].type == OP_DUP_PLUS + || dfa->nodes[idx].type == OP_DUP_QUESTION) + { + if (node->left->first == -1) + calc_first (dfa, node->left); + if (node->next == -1) + calc_next (dfa, node); + re_node_set_init_2 (dfa->edests + idx, node->left->first, + node->next); + } + else if (dfa->nodes[idx].type == OP_ALT) + { + int left, right; + if (node->left != NULL) + { + if (node->left->first == -1) + calc_first (dfa, node->left); + left = node->left->first; + } + else + { + if (node->next == -1) + calc_next (dfa, node); + left = node->next; + } + if (node->right != NULL) + { + if (node->right->first == -1) + calc_first (dfa, node->right); + right = node->right->first; + } + else + { + if (node->next == -1) + calc_next (dfa, node); + right = node->next; + } + re_node_set_init_2 (dfa->edests + idx, left, right); + } + else if (dfa->nodes[idx].type == ANCHOR + || dfa->nodes[idx].type == OP_OPEN_SUBEXP + || dfa->nodes[idx].type == OP_CLOSE_SUBEXP + || dfa->nodes[idx].type == OP_BACK_REF) + re_node_set_init_1 (dfa->edests + idx, node->next); + } +} + +/* Duplicate the epsilon closure of the node ROOT_NODE. + Note that duplicated nodes have constraint INIT_CONSTRAINT in addition + to their own constraint. */ + +static reg_errcode_t +duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node, + init_constraint) + re_dfa_t *dfa; + int top_org_node, top_clone_node, root_node; + unsigned int init_constraint; +{ + reg_errcode_t err; + int org_node, clone_node, ret; + unsigned int constraint = init_constraint; + for (org_node = top_org_node, clone_node = top_clone_node;;) + { + int org_dest, clone_dest; + if (dfa->nodes[org_node].type == OP_BACK_REF) + { + /* If the back reference epsilon-transit, its destination must + also have the constraint. Then duplicate the epsilon closure + of the destination of the back reference, and store it in + edests of the back reference. */ + org_dest = dfa->nexts[org_node]; + re_node_set_empty (dfa->edests + clone_node); + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + dfa->nexts[clone_node] = dfa->nexts[org_node]; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else if (dfa->edests[org_node].nelem == 0) + { + /* In case of the node can't epsilon-transit, don't duplicate the + destination and store the original destination as the + destination of the node. */ + dfa->nexts[clone_node] = dfa->nexts[org_node]; + break; + } + else if (dfa->edests[org_node].nelem == 1) + { + /* In case of the node can epsilon-transit, and it has only one + destination. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + if (dfa->nodes[org_node].type == ANCHOR) + { + /* In case of the node has another constraint, append it. */ + if (org_node == root_node && clone_node != org_node) + { + /* ...but if the node is root_node itself, it means the + epsilon closure have a loop, then tie it to the + destination of the root_node. */ + ret = re_node_set_insert (dfa->edests + clone_node, + org_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + break; + } + constraint |= dfa->nodes[org_node].opr.ctx_type; + } + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else /* dfa->edests[org_node].nelem == 2 */ + { + /* In case of the node can epsilon-transit, and it has two + destinations. E.g. '|', '*', '+', '?'. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* Search for a duplicated node which satisfies the constraint. */ + clone_dest = search_duplicated_node (dfa, org_dest, constraint); + if (clone_dest == -1) + { + /* There are no such a duplicated node, create a new one. */ + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + err = duplicate_node_closure (dfa, org_dest, clone_dest, + root_node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + { + /* There are a duplicated node which satisfy the constraint, + use it to avoid infinite loop. */ + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + + org_dest = dfa->edests[org_node].elems[1]; + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + org_node = org_dest; + clone_node = clone_dest; + } + return REG_NOERROR; +} + +/* Search for a node which is duplicated from the node ORG_NODE, and + satisfies the constraint CONSTRAINT. */ + +static int +search_duplicated_node (dfa, org_node, constraint) + re_dfa_t *dfa; + int org_node; + unsigned int constraint; +{ + int idx; + for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) + { + if (org_node == dfa->org_indices[idx] + && constraint == dfa->nodes[idx].constraint) + return idx; /* Found. */ + } + return -1; /* Not found. */ +} + +/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. + The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded, + otherwise return the error code. */ + +static reg_errcode_t +duplicate_node (new_idx, dfa, org_idx, constraint) + re_dfa_t *dfa; + int *new_idx, org_idx; + unsigned int constraint; +{ + re_token_t dup; + int dup_idx; + + dup = dfa->nodes[org_idx]; + dup_idx = re_dfa_add_node (dfa, dup, 1); + if (BE (dup_idx == -1, 0)) + return REG_ESPACE; + dfa->nodes[dup_idx].constraint = constraint; + if (dfa->nodes[org_idx].type == ANCHOR) + dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type; + dfa->nodes[dup_idx].duplicated = 1; + re_node_set_init_empty (dfa->edests + dup_idx); + re_node_set_init_empty (dfa->eclosures + dup_idx); + re_node_set_init_empty (dfa->inveclosures + dup_idx); + + /* Store the index of the original node. */ + dfa->org_indices[dup_idx] = org_idx; + *new_idx = dup_idx; + return REG_NOERROR; +} + +static void +calc_inveclosure (dfa) + re_dfa_t *dfa; +{ + int src, idx, dest; + for (src = 0; src < dfa->nodes_len; ++src) + { + for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) + { + dest = dfa->eclosures[src].elems[idx]; + re_node_set_insert (dfa->inveclosures + dest, src); + } + } +} + +/* Calculate "eclosure" for all the node in DFA. */ + +static reg_errcode_t +calc_eclosure (dfa) + re_dfa_t *dfa; +{ + int node_idx, incomplete; +#ifdef DEBUG + assert (dfa->nodes_len > 0); +#endif + incomplete = 0; + /* For each nodes, calculate epsilon closure. */ + for (node_idx = 0; ; ++node_idx) + { + reg_errcode_t err; + re_node_set eclosure_elem; + if (node_idx == dfa->nodes_len) + { + if (!incomplete) + break; + incomplete = 0; + node_idx = 0; + } + +#ifdef DEBUG + assert (dfa->eclosures[node_idx].nelem != -1); +#endif + /* If we have already calculated, skip it. */ + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of `node_idx'. */ + err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (dfa->eclosures[node_idx].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + return REG_NOERROR; +} + +/* Calculate epsilon closure of NODE. */ + +static reg_errcode_t +calc_eclosure_iter (new_set, dfa, node, root) + re_node_set *new_set; + re_dfa_t *dfa; + int node, root; +{ + reg_errcode_t err; + unsigned int constraint; + int i, incomplete; + re_node_set eclosure; + incomplete = 0; + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* This indicates that we are calculating this node now. + We reference this value to avoid infinite loop. */ + dfa->eclosures[node].nelem = -1; + + constraint = ((dfa->nodes[node].type == ANCHOR) + ? dfa->nodes[node].opr.ctx_type : 0); + /* If the current node has constraints, duplicate all nodes. + Since they must inherit the constraints. */ + if (constraint && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) + { + int org_node, cur_node; + org_node = cur_node = node; + err = duplicate_node_closure (dfa, node, node, node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Expand each epsilon destination nodes. */ + if (IS_EPSILON_NODE(dfa->nodes[node].type)) + for (i = 0; i < dfa->edests[node].nelem; ++i) + { + re_node_set eclosure_elem; + int edest = dfa->edests[node].elems[i]; + /* If calculating the epsilon closure of `edest' is in progress, + return intermediate result. */ + if (dfa->eclosures[edest].nelem == -1) + { + incomplete = 1; + continue; + } + /* If we haven't calculated the epsilon closure of `edest' yet, + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { + err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + eclosure_elem = dfa->eclosures[edest]; + /* Merge the epsilon closure of `edest'. */ + re_node_set_merge (&eclosure, &eclosure_elem); + /* If the epsilon closure of `edest' is incomplete, + the epsilon closure of this node is also incomplete. */ + if (dfa->eclosures[edest].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + + /* Epsilon closures include itself. */ + re_node_set_insert (&eclosure, node); + if (incomplete && !root) + dfa->eclosures[node].nelem = 0; + else + dfa->eclosures[node] = eclosure; + *new_set = eclosure; + return REG_NOERROR; +} + +/* Functions for token which are used in the parser. */ + +/* Fetch a token from INPUT. + We must not use this function inside bracket expressions. */ + +static re_token_t +fetch_token (input, syntax) + re_string_t *input; + reg_syntax_t syntax; +{ + re_token_t token; + int consumed_byte; + consumed_byte = peek_token (&token, input, syntax); + re_string_skip_bytes (input, consumed_byte); + return token; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function inside bracket expressions. */ + +static int +peek_token (token, input, syntax) + re_token_t *token; + re_string_t *input; + reg_syntax_t syntax; +{ + unsigned char c; + + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + token->mb_partial = 0; + if (MB_CUR_MAX > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + token->mb_partial = 1; + return 1; + } +#endif + if (c == '\\') + { + unsigned char c2; + if (re_string_cur_idx (input) + 1 >= re_string_length (input)) + { + token->type = BACK_SLASH; + return 1; + } + + c2 = re_string_peek_byte_case (input, 1); + token->opr.c = c2; + token->type = CHARACTER; + switch (c2) + { + case '|': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (!(syntax & RE_NO_BK_REFS)) + { + token->type = OP_BACK_REF; + token->opr.idx = c2 - '0'; + } + break; + case '<': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = WORD_FIRST; + } + break; + case '>': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = WORD_LAST; + } + break; + case 'b': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = WORD_DELIM; + } + break; + case 'B': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = INSIDE_WORD; + } + break; + case 'w': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_WORD; + break; + case 'W': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTWORD; + break; + case '`': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = BUF_FIRST; + } + break; + case '\'': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.idx = BUF_LAST; + } + break; + case '(': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_CLOSE_SUBEXP; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_CLOSE_DUP_NUM; + break; + default: + break; + } + return 2; + } + + token->type = CHARACTER; + switch (c) + { + case '\n': + if (syntax & RE_NEWLINE_ALT) + token->type = OP_ALT; + break; + case '|': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '*': + token->type = OP_DUP_ASTERISK; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_CLOSE_DUP_NUM; + break; + case '(': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_CLOSE_SUBEXP; + break; + case '[': + token->type = OP_OPEN_BRACKET; + break; + case '.': + token->type = OP_PERIOD; + break; + case '^': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) != 0) + { + char prev = re_string_peek_byte (input, -1); + if (prev != '|' && prev != '(' && + (!(syntax & RE_NEWLINE_ALT) || prev != '\n')) + break; + } + token->type = ANCHOR; + token->opr.idx = LINE_FIRST; + break; + case '$': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) + 1 != re_string_length (input)) + { + re_token_t next; + re_string_skip_bytes (input, 1); + peek_token (&next, input, syntax); + re_string_skip_bytes (input, -1); + if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) + break; + } + token->type = ANCHOR; + token->opr.idx = LINE_LAST; + break; + default: + break; + } + return 1; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function out of bracket expressions. */ + +static int +peek_token_bracket (token, input, syntax) + re_token_t *token; + re_string_t *input; + reg_syntax_t syntax; +{ + unsigned char c; + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + return 1; + } +#endif /* RE_ENABLE_I18N */ + + if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)) + { + /* In this case, '\' escape a character. */ + unsigned char c2; + re_string_skip_bytes (input, 1); + c2 = re_string_peek_byte (input, 0); + token->opr.c = c2; + token->type = CHARACTER; + return 1; + } + if (c == '[') /* '[' is a special char in a bracket exps. */ + { + unsigned char c2; + int token_len; + c2 = re_string_peek_byte (input, 1); + token->opr.c = c2; + token_len = 2; + switch (c2) + { + case '.': + token->type = OP_OPEN_COLL_ELEM; + break; + case '=': + token->type = OP_OPEN_EQUIV_CLASS; + break; + case ':': + if (syntax & RE_CHAR_CLASSES) + { + token->type = OP_OPEN_CHAR_CLASS; + break; + } + /* else fall through. */ + default: + token->type = CHARACTER; + token->opr.c = c; + token_len = 1; + break; + } + return token_len; + } + switch (c) + { + case '-': + token->type = OP_CHARSET_RANGE; + break; + case ']': + token->type = OP_CLOSE_BRACKET; + break; + case '^': + token->type = OP_NON_MATCH_LIST; + break; + default: + token->type = CHARACTER; + } + return 1; +} + +/* Functions for parser. */ + +/* Entry point of the parser. + Parse the regular expression REGEXP and return the structure tree. + If an error is occured, ERR is set by error code, and return NULL. + This function build the following tree, from regular expression : + CAT + / \ + / \ + EOR + + CAT means concatenation. + EOR means end of regular expression. */ + +static bin_tree_t * +parse (regexp, preg, syntax, err) + re_string_t *regexp; + regex_t *preg; + reg_syntax_t syntax; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *eor, *root; + re_token_t current_token; + int new_idx; + current_token = fetch_token (regexp, syntax); + tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + new_idx = re_dfa_add_node (dfa, current_token, 0); + eor = create_tree (NULL, NULL, 0, new_idx); + if (tree != NULL) + root = create_tree (tree, eor, CONCAT, 0); + else + root = eor; + if (BE (new_idx == -1 || eor == NULL || root == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + return root; +} + +/* This function build the following tree, from regular expression + |: + ALT + / \ + / \ + + + ALT means alternative, which represents the operator `|'. */ + +static bin_tree_t * +parse_reg_exp (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *branch = NULL; + int new_idx; + tree = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type == OP_ALT) + { + re_token_t alt_token = *token; + new_idx = re_dfa_add_node (dfa, alt_token, 0); + *token = fetch_token (regexp, syntax); + if (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + branch = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && branch == NULL, 0)) + { + free_bin_tree (tree); + return NULL; + } + } + else + branch = NULL; + tree = create_tree (tree, branch, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + dfa->has_plural_match = 1; + } + return tree; +} + +/* This function build the following tree, from regular expression + : + CAT + / \ + / \ + + + CAT means concatenation. */ + +static bin_tree_t * +parse_branch (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + bin_tree_t *tree, *exp; + tree = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + exp = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && exp == NULL, 0)) + { + free_bin_tree (tree); + return NULL; + } + if (tree != NULL && exp != NULL) + { + tree = create_tree (tree, exp, CONCAT, 0); + if (tree == NULL) + { + *err = REG_ESPACE; + return NULL; + } + } + else if (tree == NULL) + tree = exp; + /* Otherwise exp == NULL, we don't need to create new tree. */ + } + return tree; +} + +/* This function build the following tree, from regular expression a*: + * + | + a +*/ + +static bin_tree_t * +parse_expression (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + int new_idx; + switch (token->type) + { + case CHARACTER: + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + while (!re_string_eoi (regexp) + && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) + { + bin_tree_t *mbc_remain; + *token = fetch_token (regexp, syntax); + new_idx = re_dfa_add_node (dfa, *token, 0); + mbc_remain = create_tree (NULL, NULL, 0, new_idx); + tree = create_tree (tree, mbc_remain, CONCAT, 0); + if (BE (new_idx == -1 || mbc_remain == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } +#endif + break; + case OP_OPEN_SUBEXP: + tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_OPEN_BRACKET: + tree = parse_bracket_exp (regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_BACK_REF: + if (BE (preg->re_nsub < token->opr.idx + || dfa->subexps[token->opr.idx - 1].end == -1, 0)) + { + *err = REG_ESUBREG; + return NULL; + } + dfa->used_bkref_map |= 1 << (token->opr.idx - 1); + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + ++dfa->nbackref; + dfa->has_mb_node = 1; + break; + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + case OP_DUP_QUESTION: + case OP_OPEN_DUP_NUM: + if (syntax & RE_CONTEXT_INVALID_OPS) + { + *err = REG_BADRPT; + return NULL; + } + else if (syntax & RE_CONTEXT_INDEP_OPS) + { + *token = fetch_token (regexp, syntax); + return parse_expression (regexp, preg, token, syntax, nest, err); + } + /* else fall through */ + case OP_CLOSE_SUBEXP: + if ((token->type == OP_CLOSE_SUBEXP) && + !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) + { + *err = REG_ERPAREN; + return NULL; + } + /* else fall through */ + case OP_CLOSE_DUP_NUM: + /* We treat it as a normal character. */ + + /* Then we can these characters as normal characters. */ + token->type = CHARACTER; + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + break; + case ANCHOR: + if (dfa->word_char == NULL) + { + *err = init_word_char (dfa); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + if (token->opr.ctx_type == WORD_DELIM) + { + bin_tree_t *tree_first, *tree_last; + int idx_first, idx_last; + token->opr.ctx_type = WORD_FIRST; + idx_first = re_dfa_add_node (dfa, *token, 0); + tree_first = create_tree (NULL, NULL, 0, idx_first); + token->opr.ctx_type = WORD_LAST; + idx_last = re_dfa_add_node (dfa, *token, 0); + tree_last = create_tree (NULL, NULL, 0, idx_last); + token->type = OP_ALT; + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (tree_first, tree_last, 0, new_idx); + if (BE (idx_first == -1 || idx_last == -1 || new_idx == -1 + || tree_first == NULL || tree_last == NULL + || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + else + { + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + /* We must return here, since ANCHORs can't be followed + by repetition operators. + eg. RE"^*" is invalid or "", + it must not be "". */ + *token = fetch_token (regexp, syntax); + return tree; + case OP_PERIOD: + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + if (MB_CUR_MAX > 1) + dfa->has_mb_node = 1; + break; + case OP_WORD: + tree = build_word_op (dfa, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_NOTWORD: + tree = build_word_op (dfa, 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_ALT: + case END_OF_RE: + return NULL; + case BACK_SLASH: + *err = REG_EESCAPE; + return NULL; + default: + /* Must not happen? */ +#ifdef DEBUG + assert (0); +#endif + return NULL; + } + *token = fetch_token (regexp, syntax); + + while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS + || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) + { + tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + dfa->has_plural_match = 1; + } + + return tree; +} + +/* This function build the following tree, from regular expression + (): + SUBEXP + | + +*/ + +static bin_tree_t * +parse_sub_exp (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *left_par, *right_par; + size_t cur_nsub; + int new_idx; + cur_nsub = preg->re_nsub++; + if (dfa->subexps_alloc < preg->re_nsub) + { + re_subexp_t *new_array; + dfa->subexps_alloc *= 2; + new_array = re_realloc (dfa->subexps, re_subexp_t, dfa->subexps_alloc); + if (BE (new_array == NULL, 0)) + { + dfa->subexps_alloc /= 2; + *err = REG_ESPACE; + return NULL; + } + dfa->subexps = new_array; + } + dfa->subexps[cur_nsub].start = dfa->nodes_len; + dfa->subexps[cur_nsub].end = -1; + + new_idx = re_dfa_add_node (dfa, *token, 0); + left_par = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || left_par == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + dfa->nodes[new_idx].opr.idx = cur_nsub; + *token = fetch_token (regexp, syntax); + + /* The subexpression may be a null string. */ + if (token->type == OP_CLOSE_SUBEXP) + tree = NULL; + else + { + tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + } + if (BE (token->type != OP_CLOSE_SUBEXP, 0)) + { + free_bin_tree (tree); + *err = REG_BADPAT; + return NULL; + } + new_idx = re_dfa_add_node (dfa, *token, 0); + dfa->subexps[cur_nsub].end = dfa->nodes_len; + right_par = create_tree (NULL, NULL, 0, new_idx); + tree = ((tree == NULL) ? right_par + : create_tree (tree, right_par, CONCAT, 0)); + tree = create_tree (left_par, tree, CONCAT, 0); + if (BE (new_idx == -1 || right_par == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + dfa->nodes[new_idx].opr.idx = cur_nsub; + + return tree; +} + +/* This function parse repetition operators like "*", "+", "{1,3}" etc. */ + +static bin_tree_t * +parse_dup_op (dup_elem, regexp, dfa, token, syntax, err) + bin_tree_t *dup_elem; + re_string_t *regexp; + re_dfa_t *dfa; + re_token_t *token; + reg_syntax_t syntax; + reg_errcode_t *err; +{ + re_token_t dup_token; + bin_tree_t *tree = dup_elem, *work_tree; + int new_idx, start_idx = re_string_cur_idx (regexp); + re_token_t start_token = *token; + if (token->type == OP_OPEN_DUP_NUM) + { + int i; + int end = 0; + int start = fetch_number (regexp, token, syntax); + bin_tree_t *elem; + if (start == -1) + { + if (token->type == CHARACTER && token->opr.c == ',') + start = 0; /* We treat "{,m}" as "{0,m}". */ + else + { + *err = REG_BADBR; /* {} is invalid. */ + return NULL; + } + } + if (BE (start != -2, 1)) + { + /* We treat "{n}" as "{n,n}". */ + end = ((token->type == OP_CLOSE_DUP_NUM) ? start + : ((token->type == CHARACTER && token->opr.c == ',') + ? fetch_number (regexp, token, syntax) : -2)); + } + if (BE (start == -2 || end == -2, 0)) + { + /* Invalid sequence. */ + if (token->type == OP_CLOSE_DUP_NUM) + goto parse_dup_op_invalid_interval; + else + goto parse_dup_op_ebrace; + } + if (BE (start == 0 && end == 0, 0)) + { + /* We treat "{0}" and "{0,0}" as null string. */ + *token = fetch_token (regexp, syntax); + free_bin_tree (dup_elem); + return NULL; + } + + /* Extract "{n,m}" to "...{0,}". */ + elem = tree; + for (i = 0; i < start; ++i) + if (i != 0) + { + work_tree = duplicate_tree (elem, dfa); + tree = create_tree (tree, work_tree, CONCAT, 0); + if (BE (work_tree == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (end == -1) + { + /* We treat "{0,}" as "*". */ + dup_token.type = OP_DUP_ASTERISK; + if (start > 0) + { + elem = duplicate_tree (elem, dfa); + new_idx = re_dfa_add_node (dfa, dup_token, 0); + work_tree = create_tree (elem, NULL, 0, new_idx); + tree = create_tree (tree, work_tree, CONCAT, 0); + if (BE (elem == NULL || new_idx == -1 || work_tree == NULL + || tree == NULL, 0)) + goto parse_dup_op_espace; + } + else + { + new_idx = re_dfa_add_node (dfa, dup_token, 0); + tree = create_tree (elem, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + goto parse_dup_op_espace; + } + } + else if (end - start > 0) + { + /* Then extract "{0,m}" to "??...?". */ + dup_token.type = OP_DUP_QUESTION; + if (start > 0) + { + elem = duplicate_tree (elem, dfa); + new_idx = re_dfa_add_node (dfa, dup_token, 0); + elem = create_tree (elem, NULL, 0, new_idx); + tree = create_tree (tree, elem, CONCAT, 0); + if (BE (elem == NULL || new_idx == -1 || tree == NULL, 0)) + goto parse_dup_op_espace; + } + else + { + new_idx = re_dfa_add_node (dfa, dup_token, 0); + tree = elem = create_tree (elem, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + goto parse_dup_op_espace; + } + for (i = 1; i < end - start; ++i) + { + work_tree = duplicate_tree (elem, dfa); + tree = create_tree (tree, work_tree, CONCAT, 0); + if (BE (work_tree == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } + } + else + { + new_idx = re_dfa_add_node (dfa, *token, 0); + tree = create_tree (tree, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + *token = fetch_token (regexp, syntax); + return tree; + + parse_dup_op_espace: + free_bin_tree (tree); + *err = REG_ESPACE; + return NULL; + + parse_dup_op_ebrace: + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + *err = REG_EBRACE; + return NULL; + } + goto parse_dup_op_rollback; + parse_dup_op_invalid_interval: + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + *err = REG_BADBR; + return NULL; + } + parse_dup_op_rollback: + re_string_set_index (regexp, start_idx); + *token = start_token; + token->type = CHARACTER; + return dup_elem; +} + +/* Size of the names for collating symbol/equivalence_class/character_class. + I'm not sure, but maybe enough. */ +#define BRACKET_NAME_BUF_SIZE 32 + +#ifndef _LIBC + /* Local function for parse_bracket_exp only used in case of NOT _LIBC. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + +static reg_errcode_t +# ifdef RE_ENABLE_I18N +build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + int *range_alloc; +# else /* not RE_ENABLE_I18N */ +build_range_exp (sbcset, start_elem, end_elem) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + bracket_elem_t *start_elem, *end_elem; +{ + unsigned int start_ch, end_ch; + /* Equivalence Classes and Character Classes can't be a range start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + /* We can handle no multi character collating elements without libc + support. */ + if (BE ((start_elem->type == COLL_SYM + && strlen ((char *) start_elem->opr.name) > 1) + || (end_elem->type == COLL_SYM + && strlen ((char *) end_elem->opr.name) > 1), 0)) + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + { + wchar_t wc, start_wc, end_wc; + wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + + start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) + ? __btowc (start_ch) : start_elem->opr.wch); + end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) + ? __btowc (end_ch) : end_elem->opr.wch); + cmp_buf[0] = start_wc; + cmp_buf[4] = end_wc; + if (wcscoll (cmp_buf, cmp_buf + 4) > 0) + return REG_ERANGE; + + /* Check the space of the arrays. */ + if (*range_alloc == mbcset->nranges) + { + /* There are not enough space, need realloc. */ + wchar_t *new_array_start, *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, wchar_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, wchar_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_wc; + mbcset->range_ends[mbcset->nranges++] = end_wc; + + /* Build the table for single byte characters. */ + for (wc = 0; wc <= SBC_MAX; ++wc) + { + cmp_buf[2] = wc; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + bitset_set (sbcset, wc); + } + } +# else /* not RE_ENABLE_I18N */ + { + unsigned int ch; + start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + if (start_ch > end_ch) + return REG_ERANGE; + /* Build the table for single byte characters. */ + for (ch = 0; ch <= SBC_MAX; ++ch) + if (start_ch <= ch && ch <= end_ch) + bitset_set (sbcset, ch); + } +# endif /* not RE_ENABLE_I18N */ + return REG_NOERROR; +} +#endif /* not _LIBC */ + +#ifndef _LIBC +/* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument since we may update it. */ + +static reg_errcode_t +# ifdef RE_ENABLE_I18N +build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + int *coll_sym_alloc; +# else /* not RE_ENABLE_I18N */ +build_collating_symbol (sbcset, name) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *name; +{ + size_t name_len = strlen ((const char *) name); + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } +} +#endif /* not _LIBC */ + +/* This function parse bracket expression like "[abc]", "[a-c]", + "[[.a-a.]]" etc. */ + +static bin_tree_t * +parse_bracket_exp (regexp, dfa, token, syntax, err) + re_string_t *regexp; + re_dfa_t *dfa; + re_token_t *token; + reg_syntax_t syntax; + reg_errcode_t *err; +{ +#ifdef _LIBC + const unsigned char *collseqmb; + const char *collseqwc; + uint32_t nrules; + int32_t table_size; + const int32_t *symb_table; + const unsigned char *extra; + + /* Local function for parse_bracket_exp used in _LIBC environement. + Seek the collating symbol entry correspondings to NAME. + Return the index of the symbol in the SYMB_TABLE. */ + + static inline int32_t + seek_collating_symbol_entry (name, name_len) + const unsigned char *name; + size_t name_len; + { + int32_t hash = elem_hash ((const char *) name, name_len); + int32_t elem = hash % table_size; + int32_t second = hash % (table_size - 2); + while (symb_table[2 * elem] != 0) + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + /* Compare the length of the name. */ + && name_len == extra[symb_table[2 * elem + 1]] + /* Compare the name. */ + && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], + name_len) == 0) + { + /* Yep, this is the entry. */ + break; + } + + /* Next entry. */ + elem += second; + } + return elem; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Look up the collation sequence value of BR_ELEM. + Return the value if succeeded, UINT_MAX otherwise. */ + + static inline unsigned int + lookup_collation_sequence_value (br_elem) + bracket_elem_t *br_elem; + { + if (br_elem->type == SB_CHAR) + { + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + return collseqmb[br_elem->opr.ch]; + else + { + wint_t wc = __btowc (br_elem->opr.ch); + return collseq_table_lookup (collseqwc, wc); + } + } + else if (br_elem->type == MB_CHAR) + { + return collseq_table_lookup (collseqwc, br_elem->opr.wch); + } + else if (br_elem->type == COLL_SYM) + { + size_t sym_name_len = strlen ((char *) br_elem->opr.name); + if (nrules != 0) + { + int32_t elem, idx; + elem = seek_collating_symbol_entry (br_elem->opr.name, + sym_name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + /* Skip the byte sequence of the collating element. */ + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the multibyte collation sequence value. */ + idx += sizeof (unsigned int); + /* Skip the wide char sequence of the collating element. */ + idx += sizeof (unsigned int) * + (1 + *(unsigned int *) (extra + idx)); + /* Return the collation sequence value. */ + return *(unsigned int *) (extra + idx); + } + else if (symb_table[2 * elem] == 0 && sym_name_len == 1) + { + /* No valid character. Match it as a single byte + character. */ + return collseqmb[br_elem->opr.name[0]]; + } + } + else if (sym_name_len == 1) + return collseqmb[br_elem->opr.name[0]]; + } + return UINT_MAX; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + + static inline reg_errcode_t +# ifdef RE_ENABLE_I18N + build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + int *range_alloc; +# else /* not RE_ENABLE_I18N */ + build_range_exp (sbcset, start_elem, end_elem) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + bracket_elem_t *start_elem, *end_elem; + { + unsigned int ch; + uint32_t start_collseq; + uint32_t end_collseq; + +# ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (*range_alloc == mbcset->nranges) + { + /* There are not enough space, need realloc. */ + uint32_t *new_array_start; + uint32_t *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, uint32_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, uint32_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } +# endif /* RE_ENABLE_I18N */ + + /* Equivalence Classes and Character Classes can't be a range + start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + start_collseq = lookup_collation_sequence_value (start_elem); + end_collseq = lookup_collation_sequence_value (end_elem); + /* Check start/end collation sequence values. */ + if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) + return REG_ECOLLATE; + if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) + return REG_ERANGE; + +# ifdef RE_ENABLE_I18N + /* Got valid collation sequence values, add them as a new entry. */ + mbcset->range_starts[mbcset->nranges] = start_collseq; + mbcset->range_ends[mbcset->nranges++] = end_collseq; +# endif /* RE_ENABLE_I18N */ + + /* Build the table for single byte characters. */ + for (ch = 0; ch <= SBC_MAX; ch++) + { + uint32_t ch_collseq; + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + ch_collseq = collseqmb[ch]; + else + ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch)); + if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) + bitset_set (sbcset, ch); + } + return REG_NOERROR; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument sinse we may update it. */ + + static inline reg_errcode_t +# ifdef RE_ENABLE_I18N + build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + int *coll_sym_alloc; +# else /* not RE_ENABLE_I18N */ + build_collating_symbol (sbcset, name) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *name; + { + int32_t elem, idx; + size_t name_len = strlen ((const char *) name); + if (nrules != 0) + { + elem = seek_collating_symbol_entry (name, name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + } + else if (symb_table[2 * elem] == 0 && name_len == 1) + { + /* No valid character, treat it as a normal + character. */ + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + else + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + /* Got valid collation sequence, add it as a new entry. */ + /* Check the space of the arrays. */ + if (*coll_sym_alloc == mbcset->ncoll_syms) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->ncoll_syms is 0. */ + *coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; + /* Use realloc since mbcset->coll_syms is NULL + if *alloc == 0. */ + mbcset->coll_syms = re_realloc (mbcset->coll_syms, int32_t, + *coll_sym_alloc); + if (BE (mbcset->coll_syms == NULL, 0)) + return REG_ESPACE; + } + mbcset->coll_syms[mbcset->ncoll_syms++] = idx; +# endif /* RE_ENABLE_I18N */ + return REG_NOERROR; + } + else + { + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + } + } +#endif + + re_token_t br_token; + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; + int equiv_class_alloc = 0, char_class_alloc = 0; +#else /* not RE_ENABLE_I18N */ + int non_match = 0; +#endif /* not RE_ENABLE_I18N */ + bin_tree_t *work_tree; + int token_len, new_idx; +#ifdef _LIBC + collseqmb = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules) + { + /* + if (MB_CUR_MAX > 1) + */ + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); + } +#endif + sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else + if (BE (sbcset == NULL, 0)) +#endif /* RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_NON_MATCH_LIST) + { +#ifdef RE_ENABLE_I18N + int i; + mbcset->non_match = 1; +#else /* not RE_ENABLE_I18N */ + non_match = 1; +#endif /* not RE_ENABLE_I18N */ + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set (sbcset, '\0'); + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + bitset_set (sbcset, i); +#endif /* RE_ENABLE_I18N */ + } + + /* We treat the first ']' as a normal character. */ + if (token->type == OP_CLOSE_BRACKET) + token->type = CHARACTER; + + while (1) + { + bracket_elem_t start_elem, end_elem; + unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; + unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; + reg_errcode_t ret; + int token_len2 = 0, is_range_exp = 0; + re_token_t token2; + + start_elem.opr.name = start_name_buf; + ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, + syntax); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CHARSET_RANGE) + { + re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ + token_len2 = peek_token_bracket (&token2, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token2.type == OP_CLOSE_BRACKET) + { + /* We treat the last '-' as a normal character. */ + re_string_skip_bytes (regexp, -token_len); + token->type = CHARACTER; + } + else + is_range_exp = 1; + } + + if (is_range_exp == 1) + { + end_elem.opr.name = end_name_buf; + ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, + dfa, syntax); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + *err = build_range_exp (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &range_alloc, +#endif /* RE_ENABLE_I18N */ + &start_elem, &end_elem); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + } + else + { + switch (start_elem.type) + { + case SB_CHAR: + bitset_set (sbcset, start_elem.opr.ch); + break; +#ifdef RE_ENABLE_I18N + case MB_CHAR: + /* Check whether the array has enough space. */ + if (mbchar_alloc == mbcset->nmbchars) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nmbchars is 0. */ + mbchar_alloc = 2 * mbcset->nmbchars + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + mbcset->mbchars = re_realloc (mbcset->mbchars, wchar_t, + mbchar_alloc); + if (BE (mbcset->mbchars == NULL, 0)) + goto parse_bracket_exp_espace; + } + mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; + break; +#endif /* RE_ENABLE_I18N */ + case EQUIV_CLASS: + *err = build_equiv_class (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &equiv_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case COLL_SYM: + *err = build_collating_symbol (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &coll_sym_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case CHAR_CLASS: + *err = build_charclass (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &char_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name, syntax); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + default: + assert (0); + break; + } + } + if (token->type == OP_CLOSE_BRACKET) + break; + } + + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + + /* If it is non-matching list. */ +#ifdef RE_ENABLE_I18N + if (mbcset->non_match) +#else /* not RE_ENABLE_I18N */ + if (non_match) +#endif /* not RE_ENABLE_I18N */ + bitset_not (sbcset); + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + new_idx = re_dfa_add_node (dfa, br_token, 0); + work_tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + +#ifdef RE_ENABLE_I18N + if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes + || mbcset->nranges || (MB_CUR_MAX > 1 && (mbcset->nchar_classes + || mbcset->non_match))) + { + re_token_t alt_token; + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + new_idx = re_dfa_add_node (dfa, br_token, 0); + mbc_tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || mbc_tree == NULL, 0)) + goto parse_bracket_exp_espace; + /* Then join them by ALT node. */ + dfa->has_plural_match = 1; + alt_token.type = OP_ALT; + new_idx = re_dfa_add_node (dfa, alt_token, 0); + work_tree = create_tree (work_tree, mbc_tree, 0, new_idx); + if (BE (new_idx != -1 && mbc_tree != NULL, 1)) + return work_tree; + } + else + { + free_charset (mbcset); + return work_tree; + } +#else /* not RE_ENABLE_I18N */ + return work_tree; +#endif /* not RE_ENABLE_I18N */ + + parse_bracket_exp_espace: + *err = REG_ESPACE; + parse_bracket_exp_free_return: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + return NULL; +} + +/* Parse an element in the bracket expression. */ + +static reg_errcode_t +parse_bracket_element (elem, regexp, token, token_len, dfa, syntax) + bracket_elem_t *elem; + re_string_t *regexp; + re_token_t *token; + int token_len; + re_dfa_t *dfa; + reg_syntax_t syntax; +{ +#ifdef RE_ENABLE_I18N + int cur_char_size; + cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); + if (cur_char_size > 1) + { + elem->type = MB_CHAR; + elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); + re_string_skip_bytes (regexp, cur_char_size); + return REG_NOERROR; + } +#endif /* RE_ENABLE_I18N */ + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS + || token->type == OP_OPEN_EQUIV_CLASS) + return parse_bracket_symbol (elem, regexp, token); + elem->type = SB_CHAR; + elem->opr.ch = token->opr.c; + return REG_NOERROR; +} + +/* Parse a bracket symbol in the bracket expression. Bracket symbols are + such as [::], [..], and + [==]. */ + +static reg_errcode_t +parse_bracket_symbol (elem, regexp, token) + bracket_elem_t *elem; + re_string_t *regexp; + re_token_t *token; +{ + unsigned char ch, delim = token->opr.c; + int i = 0; + for (;; ++i) + { + if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE) + return REG_EBRACK; + if (token->type == OP_OPEN_CHAR_CLASS) + ch = re_string_fetch_byte_case (regexp); + else + ch = re_string_fetch_byte (regexp); + if (ch == delim && re_string_peek_byte (regexp, 0) == ']') + break; + elem->opr.name[i] = ch; + } + re_string_skip_bytes (regexp, 1); + elem->opr.name[i] = '\0'; + switch (token->type) + { + case OP_OPEN_COLL_ELEM: + elem->type = COLL_SYM; + break; + case OP_OPEN_EQUIV_CLASS: + elem->type = EQUIV_CLASS; + break; + case OP_OPEN_CHAR_CLASS: + elem->type = CHAR_CLASS; + break; + default: + break; + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the equivalence class which is represented by NAME. + The result are written to MBCSET and SBCSET. + EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_equiv_class (sbcset, mbcset, equiv_class_alloc, name) + re_charset_t *mbcset; + int *equiv_class_alloc; +#else /* not RE_ENABLE_I18N */ +build_equiv_class (sbcset, name) +#endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *name; +{ +#if defined _LIBC && defined RE_ENABLE_I18N + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + const int32_t *table, *indirect; + const unsigned char *weights, *extra, *cp; + unsigned char char_buf[2]; + int32_t idx1, idx2; + unsigned int ch; + size_t len; + /* This #include defines a local function! */ +# include + /* Calculate the index for equivalence class. */ + cp = name; + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + idx1 = findidx (&cp); + if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) + /* This isn't a valid character. */ + return REG_ECOLLATE; + + /* Build single byte matcing table for this equivalence class. */ + char_buf[1] = (unsigned char) '\0'; + len = weights[idx1]; + for (ch = 0; ch < SBC_MAX; ++ch) + { + char_buf[0] = ch; + cp = char_buf; + idx2 = findidx (&cp); +/* + idx2 = table[ch]; +*/ + if (idx2 == 0) + /* This isn't a valid character. */ + continue; + if (len == weights[idx2]) + { + int cnt = 0; + while (cnt <= len && + weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt]) + ++cnt; + + if (cnt > len) + bitset_set (sbcset, ch); + } + } + /* Check whether the array has enough space. */ + if (*equiv_class_alloc == mbcset->nequiv_classes) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nequiv_classes is 0. */ + *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; + /* Use realloc since the array is NULL if *alloc == 0. */ + mbcset->equiv_classes = re_realloc (mbcset->equiv_classes, int32_t, + *equiv_class_alloc); + if (BE (mbcset->equiv_classes == NULL, 0)) + return REG_ESPACE; + } + mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; + } + else +#endif /* _LIBC && RE_ENABLE_I18N */ + { + if (BE (strlen ((const char *) name) != 1, 0)) + return REG_ECOLLATE; + bitset_set (sbcset, *name); + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the character class which is represented by NAME. + The result are written to MBCSET and SBCSET. + CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_charclass (sbcset, mbcset, char_class_alloc, class_name, syntax) + re_charset_t *mbcset; + int *char_class_alloc; +#else /* not RE_ENABLE_I18N */ +build_charclass (sbcset, class_name, syntax) +#endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *class_name; + reg_syntax_t syntax; +{ + int i; + const char *name = (const char *) class_name; + + /* In case of REG_ICASE "upper" and "lower" match the both of + upper and lower cases. */ + if ((syntax & RE_ICASE) + && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0)) + name = "alpha"; + +#ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (*char_class_alloc == mbcset->nchar_classes) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nchar_classes is 0. */ + *char_class_alloc = 2 * mbcset->nchar_classes + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + mbcset->char_classes = re_realloc (mbcset->char_classes, wctype_t, + *char_class_alloc); + if (BE (mbcset->char_classes == NULL, 0)) + return REG_ESPACE; + } + mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); +#endif /* RE_ENABLE_I18N */ + +#define BUILD_CHARCLASS_LOOP(ctype_func)\ + for (i = 0; i < SBC_MAX; ++i) \ + { \ + if (ctype_func (i)) \ + bitset_set (sbcset, i); \ + } + + if (strcmp (name, "alnum") == 0) + BUILD_CHARCLASS_LOOP (isalnum) + else if (strcmp (name, "cntrl") == 0) + BUILD_CHARCLASS_LOOP (iscntrl) + else if (strcmp (name, "lower") == 0) + BUILD_CHARCLASS_LOOP (islower) + else if (strcmp (name, "space") == 0) + BUILD_CHARCLASS_LOOP (isspace) + else if (strcmp (name, "alpha") == 0) + BUILD_CHARCLASS_LOOP (isalpha) + else if (strcmp (name, "digit") == 0) + BUILD_CHARCLASS_LOOP (isdigit) + else if (strcmp (name, "print") == 0) + BUILD_CHARCLASS_LOOP (isprint) + else if (strcmp (name, "upper") == 0) + BUILD_CHARCLASS_LOOP (isupper) + else if (strcmp (name, "blank") == 0) + BUILD_CHARCLASS_LOOP (isblank) + else if (strcmp (name, "graph") == 0) + BUILD_CHARCLASS_LOOP (isgraph) + else if (strcmp (name, "punct") == 0) + BUILD_CHARCLASS_LOOP (ispunct) + else if (strcmp (name, "xdigit") == 0) + BUILD_CHARCLASS_LOOP (isxdigit) + else + return REG_ECTYPE; + + return REG_NOERROR; +} + +static bin_tree_t * +build_word_op (dfa, not, err) + re_dfa_t *dfa; + int not; + reg_errcode_t *err; +{ + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int alloc = 0; +#else /* not RE_ENABLE_I18N */ + int non_match = 0; +#endif /* not RE_ENABLE_I18N */ + reg_errcode_t ret; + re_token_t br_token; + bin_tree_t *tree; + int new_idx; + + sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ + +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else /* not RE_ENABLE_I18N */ + if (BE (sbcset == NULL, 0)) +#endif /* not RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + if (not) + { +#ifdef RE_ENABLE_I18N + int i; + /* + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set(cset->sbcset, '\0'); + */ + mbcset->non_match = 1; + if (MB_CUR_MAX > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + bitset_set (sbcset, i); +#else /* not RE_ENABLE_I18N */ + non_match = 1; +#endif /* not RE_ENABLE_I18N */ + } + + /* We don't care the syntax in this case. */ + ret = build_charclass (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &alloc, +#endif /* RE_ENABLE_I18N */ + (const unsigned char *) "alpha", 0); + + if (BE (ret != REG_NOERROR, 0)) + { + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = ret; + return NULL; + } + /* \w match '_' also. */ + bitset_set (sbcset, '_'); + + /* If it is non-matching list. */ +#ifdef RE_ENABLE_I18N + if (mbcset->non_match) +#else /* not RE_ENABLE_I18N */ + if (non_match) +#endif /* not RE_ENABLE_I18N */ + bitset_not (sbcset); + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + new_idx = re_dfa_add_node (dfa, br_token, 0); + tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || tree == NULL, 0)) + goto build_word_op_espace; + +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + re_token_t alt_token; + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + new_idx = re_dfa_add_node (dfa, br_token, 0); + mbc_tree = create_tree (NULL, NULL, 0, new_idx); + if (BE (new_idx == -1 || mbc_tree == NULL, 0)) + goto build_word_op_espace; + /* Then join them by ALT node. */ + alt_token.type = OP_ALT; + new_idx = re_dfa_add_node (dfa, alt_token, 0); + tree = create_tree (tree, mbc_tree, 0, new_idx); + if (BE (new_idx != -1 && mbc_tree != NULL, 1)) + return tree; + } + else + { + free_charset (mbcset); + return tree; + } +#else /* not RE_ENABLE_I18N */ + return tree; +#endif /* not RE_ENABLE_I18N */ + + build_word_op_espace: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = REG_ESPACE; + return NULL; +} + +/* This is intended for the expressions like "a{1,3}". + Fetch a number from `input', and return the number. + Return -1, if the number field is empty like "{,1}". + Return -2, If an error is occured. */ + +static int +fetch_number (input, token, syntax) + re_string_t *input; + re_token_t *token; + reg_syntax_t syntax; +{ + int num = -1; + unsigned char c; + while (1) + { + *token = fetch_token (input, syntax); + c = token->opr.c; + if (BE (token->type == END_OF_RE, 0)) + return -2; + if (token->type == OP_CLOSE_DUP_NUM || c == ',') + break; + num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) + ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); + num = (num > RE_DUP_MAX) ? -2 : num; + } + return num; +} + +#ifdef RE_ENABLE_I18N +static void +free_charset (re_charset_t *cset) +{ + re_free (cset->mbchars); +# ifdef _LIBC + re_free (cset->coll_syms); + re_free (cset->equiv_classes); + re_free (cset->range_starts); + re_free (cset->range_ends); +# endif + re_free (cset->char_classes); + re_free (cset); +} +#endif /* RE_ENABLE_I18N */ + +/* Functions for binary tree operation. */ + +/* Create a node of tree. + Note: This function automatically free left and right if malloc fails. */ + +static bin_tree_t * +create_tree (left, right, type, index) + bin_tree_t *left; + bin_tree_t *right; + re_token_type_t type; + int index; +{ + bin_tree_t *tree; + tree = re_malloc (bin_tree_t, 1); + if (BE (tree == NULL, 0)) + { + free_bin_tree (left); + free_bin_tree (right); + return NULL; + } + tree->parent = NULL; + tree->left = left; + tree->right = right; + tree->type = type; + tree->node_idx = index; + tree->first = -1; + tree->next = -1; + re_node_set_init_empty (&tree->eclosure); + + if (left != NULL) + left->parent = tree; + if (right != NULL) + right->parent = tree; + return tree; +} + +/* Free the sub tree pointed by TREE. */ + +static void +free_bin_tree (tree) + bin_tree_t *tree; +{ + if (tree == NULL) + return; + /*re_node_set_free (&tree->eclosure);*/ + free_bin_tree (tree->left); + free_bin_tree (tree->right); + re_free (tree); +} + +/* Duplicate the node SRC, and return new node. */ + +static bin_tree_t * +duplicate_tree (src, dfa) + const bin_tree_t *src; + re_dfa_t *dfa; +{ + bin_tree_t *left = NULL, *right = NULL, *new_tree; + int new_node_idx; + /* Since node indies must be according to Post-order of the tree, + we must duplicate the left at first. */ + if (src->left != NULL) + { + left = duplicate_tree (src->left, dfa); + if (left == NULL) + return NULL; + } + + /* Secondaly, duplicate the right. */ + if (src->right != NULL) + { + right = duplicate_tree (src->right, dfa); + if (right == NULL) + { + free_bin_tree (left); + return NULL; + } + } + + /* At last, duplicate itself. */ + if (src->type == NON_TYPE) + { + new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0); + dfa->nodes[new_node_idx].duplicated = 1; + if (BE (new_node_idx == -1, 0)) + { + free_bin_tree (left); + free_bin_tree (right); + return NULL; + } + } + else + new_node_idx = src->type; + + new_tree = create_tree (left, right, src->type, new_node_idx); + if (BE (new_tree == NULL, 0)) + { + free_bin_tree (left); + free_bin_tree (right); + } + return new_tree; +} diff --git a/posix/regex.c b/posix/regex.c new file mode 100644 index 0000000..98d86e1 --- /dev/null +++ b/posix/regex.c @@ -0,0 +1,61 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) +#endif + +/* POSIX says that must be included (by the caller) before + . */ +#include +#include +#include "regex_internal.h" + +#include "regex_internal.c" +#include "regcomp.c" +#include "regexec.c" + +/* Binary backward compatibility. */ +#if _LIBC +# include +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) +link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") +int re_max_failures = 2000; +# endif +#endif diff --git a/posix/regex.h b/posix/regex.h new file mode 100644 index 0000000..314292b --- /dev/null +++ b/posix/regex.h @@ -0,0 +1,574 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985,1989-93,1995-98,2000,2001,2002 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX says that must be included (by the caller) before + . */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && (defined VMS || defined _MSC_VER) +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. Same for Microsoft Visual C++ 6.0 */ +# include +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE char * +#endif + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +# define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +# define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, size_t length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if defined restrict || 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. */ +#ifndef __restrict_arr +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define __restrict_arr __restrict +# else +# define __restrict_arr +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *__restrict __preg, + const char *__restrict __pattern, + int __cflags)); + +extern int regexec _RE_ARGS ((const regex_t *__restrict __preg, + const char *__restrict __string, size_t __nmatch, + regmatch_t __pmatch[__restrict_arr], + int __eflags)); + +extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size)); + +extern void regfree _RE_ARGS ((regex_t *__preg)); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/posix/regex_internal.c b/posix/regex_internal.c new file mode 100644 index 0000000..f969c7c --- /dev/null +++ b/posix/regex_internal.c @@ -0,0 +1,1263 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +static void re_string_construct_common (const char *str, int len, + re_string_t *pstr, + RE_TRANSLATE_TYPE trans, int icase); +#ifdef RE_ENABLE_I18N +static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx, + wint_t *last_wc); +#endif /* RE_ENABLE_I18N */ +static re_dfastate_t *create_newstate_common (re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int hash); +static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate, + unsigned int hash); +static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int hash); +static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context, + unsigned int hash); +static unsigned int inline calc_state_hash (const re_node_set *nodes, + unsigned int context); + +/* Functions for string operation. */ + +/* This function allocate the buffers. It is necessary to call + re_string_reconstruct before using the object. */ + +static reg_errcode_t +re_string_allocate (pstr, str, len, init_len, trans, icase) + re_string_t *pstr; + const char *str; + int len, init_len, icase; + RE_TRANSLATE_TYPE trans; +{ + reg_errcode_t ret; + int init_buf_len = (len + 1 < init_len) ? len + 1: init_len; + re_string_construct_common (str, len, pstr, trans, icase); + pstr->stop = pstr->len; + + ret = re_string_realloc_buffers (pstr, init_buf_len); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case + : (unsigned char *) str); + pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case; + pstr->valid_len = (MBS_CASE_ALLOCATED (pstr) || MBS_ALLOCATED (pstr) + || MB_CUR_MAX > 1) ? pstr->valid_len : len; + return REG_NOERROR; +} + +/* This function allocate the buffers, and initialize them. */ + +static reg_errcode_t +re_string_construct (pstr, str, len, trans, icase) + re_string_t *pstr; + const char *str; + int len, icase; + RE_TRANSLATE_TYPE trans; +{ + reg_errcode_t ret; + re_string_construct_common (str, len, pstr, trans, icase); + pstr->stop = pstr->len; + /* Set 0 so that this function can initialize whole buffers. */ + pstr->valid_len = 0; + + if (len > 0) + { + ret = re_string_realloc_buffers (pstr, len + 1); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case + : (unsigned char *) str); + pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case; + + if (icase) + { +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + build_wcs_upper_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (trans != NULL) + re_string_translate_buffer (pstr); + else + pstr->valid_len = len; + } + } + + /* Initialized whole buffers, then valid_len == bufs_len. */ + pstr->valid_len = pstr->bufs_len; + return REG_NOERROR; +} + +/* Helper functions for re_string_allocate, and re_string_construct. */ + +static reg_errcode_t +re_string_realloc_buffers (pstr, new_buf_len) + re_string_t *pstr; + int new_buf_len; +{ +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->wcs = new_array; + } +#endif /* RE_ENABLE_I18N */ + if (MBS_ALLOCATED (pstr)) + { + unsigned char *new_array = re_realloc (pstr->mbs, unsigned char, + new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->mbs = new_array; + } + if (MBS_CASE_ALLOCATED (pstr)) + { + unsigned char *new_array = re_realloc (pstr->mbs_case, unsigned char, + new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->mbs_case = new_array; + if (!MBS_ALLOCATED (pstr)) + pstr->mbs = pstr->mbs_case; + } + pstr->bufs_len = new_buf_len; + return REG_NOERROR; +} + + +static void +re_string_construct_common (str, len, pstr, trans, icase) + const char *str; + int len; + re_string_t *pstr; + RE_TRANSLATE_TYPE trans; + int icase; +{ + memset (pstr, '\0', sizeof (re_string_t)); + pstr->raw_mbs = (const unsigned char *) str; + pstr->len = len; + pstr->trans = trans; + pstr->icase = icase ? 1 : 0; +} + +#ifdef RE_ENABLE_I18N + +/* Build wide character buffer PSTR->WCS. + If the byte sequence of the string are: + (0), (1), (0), (1), + Then wide character buffer will be: + , WEOF , , WEOF , + We use WEOF for padding, they indicate that the position isn't + a first byte of a multibyte character. + + Note that this function assumes PSTR->VALID_LEN elements are already + built and starts from PSTR->VALID_LEN. */ + +static void +build_wcs_buffer (pstr) + re_string_t *pstr; +{ + mbstate_t prev_st; + int byte_idx, end_idx, mbclen, remain_len; + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + pstr->cur_state = prev_st; + } + + /* Apply the translateion if we need. */ + if (pstr->trans != NULL && mbclen == 1) + { + int ch = pstr->trans[pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]]; + pstr->mbs_case[byte_idx] = ch; + } + /* Write wide character and padding. */ + pstr->wcs[byte_idx++] = wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + pstr->valid_len = byte_idx; +} + +/* Build wide character buffer PSTR->WCS like build_wcs_buffer, + but for REG_ICASE. */ + +static void +build_wcs_upper_buffer (pstr) + re_string_t *pstr; +{ + mbstate_t prev_st; + int byte_idx, end_idx, mbclen, remain_len; + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (mbclen == 1 || mbclen == (size_t) -1 || mbclen == 0) + { + /* In case of a singlebyte character. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + /* Apply the translateion if we need. */ + if (pstr->trans != NULL && mbclen == 1) + { + ch = pstr->trans[ch]; + pstr->mbs_case[byte_idx] = ch; + } + pstr->wcs[byte_idx] = iswlower (wc) ? toupper (wc) : wc; + pstr->mbs[byte_idx++] = islower (ch) ? toupper (ch) : ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else /* mbclen > 1 */ + { + if (iswlower (wc)) + wcrtomb ((char *) pstr->mbs + byte_idx, towupper (wc), &prev_st); + else + memcpy (pstr->mbs + byte_idx, + pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); + pstr->wcs[byte_idx++] = iswlower (wc) ? toupper (wc) : wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + } + pstr->valid_len = byte_idx; +} + +/* Skip characters until the index becomes greater than NEW_RAW_IDX. + Return the index. */ + +static int +re_string_skip_chars (pstr, new_raw_idx, last_wc) + re_string_t *pstr; + int new_raw_idx; + wint_t *last_wc; +{ + mbstate_t prev_st; + int rawbuf_idx, mbclen; + wchar_t wc = 0; + + /* Skip the characters which are not necessary to check. */ + for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_len; + rawbuf_idx < new_raw_idx;) + { + int remain_len; + remain_len = pstr->len - rawbuf_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx, + remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + pstr->cur_state = prev_st; + } + /* Then proceed the next character. */ + rawbuf_idx += mbclen; + } + *last_wc = (wint_t) wc; + return rawbuf_idx; +} +#endif /* RE_ENABLE_I18N */ + +/* Build the buffer PSTR->MBS, and apply the translation if we need. + This function is used in case of REG_ICASE. */ + +static void +build_upper_buffer (pstr) + re_string_t *pstr; +{ + int char_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; + if (pstr->trans != NULL) + { + ch = pstr->trans[ch]; + pstr->mbs_case[char_idx] = ch; + } + if (islower (ch)) + pstr->mbs[char_idx] = toupper (ch); + else + pstr->mbs[char_idx] = ch; + } + pstr->valid_len = char_idx; +} + +/* Apply TRANS to the buffer in PSTR. */ + +static void +re_string_translate_buffer (pstr) + re_string_t *pstr; +{ + int buf_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; + pstr->mbs_case[buf_idx] = pstr->trans[ch]; + } + + pstr->valid_len = buf_idx; +} + +/* This function re-construct the buffers. + Concretely, convert to wide character in case of MB_CUR_MAX > 1, + convert to upper case in case of REG_ICASE, apply translation. */ + +static reg_errcode_t +re_string_reconstruct (pstr, idx, eflags, newline) + re_string_t *pstr; + int idx, eflags, newline; +{ + int offset = idx - pstr->raw_mbs_idx; + if (offset < 0) + { + /* Reset buffer. */ +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); +#endif /* RE_ENABLE_I18N */ + pstr->len += pstr->raw_mbs_idx; + pstr->stop += pstr->raw_mbs_idx; + pstr->valid_len = pstr->raw_mbs_idx = 0; + pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + if (!MBS_CASE_ALLOCATED (pstr)) + pstr->mbs_case = (unsigned char *) pstr->raw_mbs; + if (!MBS_ALLOCATED (pstr) && !MBS_CASE_ALLOCATED (pstr)) + pstr->mbs = (unsigned char *) pstr->raw_mbs; + offset = idx; + } + + if (offset != 0) + { + /* Are the characters which are already checked remain? */ + if (offset < pstr->valid_len) + { + /* Yes, move them to the front of the buffer. */ + pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags, + newline); +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); +#endif /* RE_ENABLE_I18N */ + if (MBS_ALLOCATED (pstr)) + memmove (pstr->mbs, pstr->mbs + offset, + pstr->valid_len - offset); + if (MBS_CASE_ALLOCATED (pstr)) + memmove (pstr->mbs_case, pstr->mbs_case + offset, + pstr->valid_len - offset); + pstr->valid_len -= offset; +#if DEBUG + assert (pstr->valid_len > 0); +#endif + } + else + { + /* No, skip all characters until IDX. */ + pstr->valid_len = 0; +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + int wcs_idx; + wint_t wc; + pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; + for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) + pstr->wcs[wcs_idx] = WEOF; + if (pstr->trans && wc <= 0xff) + wc = pstr->trans[wc]; + pstr->tip_context = (IS_WIDE_WORD_CHAR (wc) ? CONTEXT_WORD + : ((newline && IS_WIDE_NEWLINE (wc)) + ? CONTEXT_NEWLINE : 0)); + } + else +#endif /* RE_ENABLE_I18N */ + { + int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; + if (pstr->trans) + c = pstr->trans[c]; + pstr->tip_context = (IS_WORD_CHAR (c) ? CONTEXT_WORD + : ((newline && IS_NEWLINE (c)) + ? CONTEXT_NEWLINE : 0)); + } + } + if (!MBS_CASE_ALLOCATED (pstr)) + { + pstr->mbs_case += offset; + /* In case of !MBS_ALLOCATED && !MBS_CASE_ALLOCATED. */ + if (!MBS_ALLOCATED (pstr)) + pstr->mbs += offset; + } + } + pstr->raw_mbs_idx = idx; + pstr->len -= offset; + pstr->stop -= offset; + + /* Then build the buffers. */ +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + if (pstr->icase) + build_wcs_upper_buffer (pstr); + else + build_wcs_buffer (pstr); + } + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->icase) + build_upper_buffer (pstr); + else if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + pstr->cur_idx = 0; + + return REG_NOERROR; +} + +static void +re_string_destruct (pstr) + re_string_t *pstr; +{ +#ifdef RE_ENABLE_I18N + re_free (pstr->wcs); +#endif /* RE_ENABLE_I18N */ + if (MBS_ALLOCATED (pstr)) + re_free (pstr->mbs); + if (MBS_CASE_ALLOCATED (pstr)) + re_free (pstr->mbs_case); +} + +/* Return the context at IDX in INPUT. */ + +static unsigned int +re_string_context_at (input, idx, eflags, newline_anchor) + const re_string_t *input; + int idx, eflags, newline_anchor; +{ + int c; + if (idx < 0 || idx == input->len) + { + if (idx < 0) + /* In this case, we use the value stored in input->tip_context, + since we can't know the character in input->mbs[-1] here. */ + return input->tip_context; + else /* (idx == input->len) */ + return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF + : CONTEXT_NEWLINE | CONTEXT_ENDBUF); + } +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + { + wint_t wc; + int wc_idx = idx; + while(input->wcs[wc_idx] == WEOF) + { +#ifdef DEBUG + /* It must not happen. */ + assert (wc_idx >= 0); +#endif + --wc_idx; + if (wc_idx < 0) + return input->tip_context; + } + wc = input->wcs[wc_idx]; + if (IS_WIDE_WORD_CHAR (wc)) + return CONTEXT_WORD; + return (newline_anchor && IS_WIDE_NEWLINE (wc)) ? CONTEXT_NEWLINE : 0; + } + else +#endif + { + c = re_string_byte_at (input, idx); + if (IS_WORD_CHAR (c)) + return CONTEXT_WORD; + return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0; + } +} + +/* Functions for set operation. */ + +static reg_errcode_t +re_node_set_alloc (set, size) + re_node_set *set; + int size; +{ + set->alloc = size; + set->nelem = 0; + set->elems = re_malloc (int, size); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_1 (set, elem) + re_node_set *set; + int elem; +{ + set->alloc = 1; + set->nelem = 1; + set->elems = re_malloc (int, 1); + if (BE (set->elems == NULL, 0)) + { + set->alloc = set->nelem = 0; + return REG_ESPACE; + } + set->elems[0] = elem; + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_2 (set, elem1, elem2) + re_node_set *set; + int elem1, elem2; +{ + set->alloc = 2; + set->elems = re_malloc (int, 2); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + if (elem1 == elem2) + { + set->nelem = 1; + set->elems[0] = elem1; + } + else + { + set->nelem = 2; + if (elem1 < elem2) + { + set->elems[0] = elem1; + set->elems[1] = elem2; + } + else + { + set->elems[0] = elem2; + set->elems[1] = elem1; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_copy (dest, src) + re_node_set *dest; + const re_node_set *src; +{ + dest->nelem = src->nelem; + if (src->nelem > 0) + { + dest->alloc = dest->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + { + dest->alloc = dest->nelem = 0; + return REG_ESPACE; + } + memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); + } + else + re_node_set_init_empty (dest); + return REG_NOERROR; +} + +/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. + Note: We assume dest->elems is NULL, when dest->alloc is 0. */ + +static reg_errcode_t +re_node_set_add_intersect (dest, src1, src2) + re_node_set *dest; + const re_node_set *src1, *src2; +{ + int i1, i2, id; + if (src1->nelem > 0 && src2->nelem > 0) + { + if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) + { + dest->alloc = src1->nelem + src2->nelem + dest->nelem; + dest->elems = re_realloc (dest->elems, int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + } + else + return REG_NOERROR; + + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + ++i2; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + { + while (id < dest->nelem && dest->elems[id] < src2->elems[i2]) + ++id; + if (id < dest->nelem && dest->elems[id] == src2->elems[i2]) + ++id; + else + { + memmove (dest->elems + id + 1, dest->elems + id, + sizeof (int) * (dest->nelem - id)); + dest->elems[id++] = src2->elems[i2++]; + ++dest->nelem; + } + } + ++i1; + } + return REG_NOERROR; +} + +/* Calculate the union set of the sets SRC1 and SRC2. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +re_node_set_init_union (dest, src1, src2) + re_node_set *dest; + const re_node_set *src1, *src2; +{ + int i1, i2, id; + if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) + { + dest->alloc = src1->nelem + src2->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + else + { + if (src1 != NULL && src1->nelem > 0) + return re_node_set_init_copy (dest, src1); + else if (src2 != NULL && src2->nelem > 0) + return re_node_set_init_copy (dest, src2); + else + re_node_set_init_empty (dest); + return REG_NOERROR; + } + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + dest->elems[id++] = src2->elems[i2++]; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + ++i2; + dest->elems[id++] = src1->elems[i1++]; + } + if (i1 < src1->nelem) + { + memcpy (dest->elems + id, src1->elems + i1, + (src1->nelem - i1) * sizeof (int)); + id += src1->nelem - i1; + } + else if (i2 < src2->nelem) + { + memcpy (dest->elems + id, src2->elems + i2, + (src2->nelem - i2) * sizeof (int)); + id += src2->nelem - i2; + } + dest->nelem = id; + return REG_NOERROR; +} + +/* Calculate the union set of the sets DEST and SRC. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +re_node_set_merge (dest, src) + re_node_set *dest; + const re_node_set *src; +{ + int si, di; + if (src == NULL || src->nelem == 0) + return REG_NOERROR; + if (dest->alloc < src->nelem + dest->nelem) + { + int *new_buffer; + dest->alloc = 2 * (src->nelem + dest->alloc); + new_buffer = re_realloc (dest->elems, int, dest->alloc); + if (BE (new_buffer == NULL, 0)) + return REG_ESPACE; + dest->elems = new_buffer; + } + + for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;) + { + int cp_from, ncp, mid, right, src_elem = src->elems[si]; + /* Binary search the spot we will add the new element. */ + right = dest->nelem; + while (di < right) + { + mid = (di + right) / 2; + if (dest->elems[mid] < src_elem) + di = mid + 1; + else + right = mid; + } + if (di >= dest->nelem) + break; + + if (dest->elems[di] == src_elem) + { + /* Skip since, DEST already has the element. */ + ++di; + ++si; + continue; + } + + /* Skip the src elements which are less than dest->elems[di]. */ + cp_from = si; + while (si < src->nelem && src->elems[si] < dest->elems[di]) + ++si; + /* Copy these src elements. */ + ncp = si - cp_from; + memmove (dest->elems + di + ncp, dest->elems + di, + sizeof (int) * (dest->nelem - di)); + memcpy (dest->elems + di, src->elems + cp_from, + sizeof (int) * ncp); + /* Update counters. */ + di += ncp; + dest->nelem += ncp; + } + + /* Copy remaining src elements. */ + if (si < src->nelem) + { + memcpy (dest->elems + di, src->elems + si, + sizeof (int) * (src->nelem - si)); + dest->nelem += src->nelem - si; + } + return REG_NOERROR; +} + +/* Insert the new element ELEM to the re_node_set* SET. + return 0 if SET already has ELEM, + return -1 if an error is occured, return 1 otherwise. */ + +static int +re_node_set_insert (set, elem) + re_node_set *set; + int elem; +{ + int idx, right, mid; + /* In case of the set is empty. */ + if (set->elems == NULL || set->alloc == 0) + { + if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1)) + return 1; + else + return -1; + } + + /* Binary search the spot we will add the new element. */ + idx = 0; + right = set->nelem; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + + /* Realloc if we need. */ + if (set->alloc < set->nelem + 1) + { + int *new_array; + set->alloc = set->alloc * 2; + new_array = re_malloc (int, set->alloc); + if (BE (new_array == NULL, 0)) + return -1; + /* Copy the elements they are followed by the new element. */ + if (idx > 0) + memcpy (new_array, set->elems, sizeof (int) * (idx)); + /* Copy the elements which follows the new element. */ + if (set->nelem - idx > 0) + memcpy (new_array + idx + 1, set->elems + idx, + sizeof (int) * (set->nelem - idx)); + re_free (set->elems); + set->elems = new_array; + } + else + { + /* Move the elements which follows the new element. */ + if (set->nelem - idx > 0) + memmove (set->elems + idx + 1, set->elems + idx, + sizeof (int) * (set->nelem - idx)); + } + /* Insert the new element. */ + set->elems[idx] = elem; + ++set->nelem; + return 1; +} + +/* Compare two node sets SET1 and SET2. + return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise. */ + +static int +re_node_set_compare (set1, set2) + const re_node_set *set1, *set2; +{ + int i; + if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) + return 0; + for (i = 0 ; i < set1->nelem ; i++) + if (set1->elems[i] != set2->elems[i]) + return 0; + return 1; +} + +/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ + +static int +re_node_set_contains (set, elem) + const re_node_set *set; + int elem; +{ + int idx, right, mid; + if (set->nelem <= 0) + return 0; + + /* Binary search the element. */ + idx = 0; + right = set->nelem - 1; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + return set->elems[idx] == elem ? idx + 1 : 0; +} + +static void +re_node_set_remove_at (set, idx) + re_node_set *set; + int idx; +{ + if (idx < 0 || idx >= set->nelem) + return; + if (idx < set->nelem - 1) + memmove (set->elems + idx, set->elems + idx + 1, + sizeof (int) * (set->nelem - idx - 1)); + --set->nelem; +} + + +/* Add the token TOKEN to dfa->nodes, and return the index of the token. + Or return -1, if an error will be occured. */ + +static int +re_dfa_add_node (dfa, token, mode) + re_dfa_t *dfa; + re_token_t token; + int mode; +{ + if (dfa->nodes_len >= dfa->nodes_alloc) + { + re_token_t *new_array; + dfa->nodes_alloc *= 2; + new_array = re_realloc (dfa->nodes, re_token_t, dfa->nodes_alloc); + if (BE (new_array == NULL, 0)) + return -1; + else + dfa->nodes = new_array; + if (mode) + { + int *new_nexts, *new_indices; + re_node_set *new_edests, *new_eclosures, *new_inveclosures; + + new_nexts = re_realloc (dfa->nexts, int, dfa->nodes_alloc); + new_indices = re_realloc (dfa->org_indices, int, dfa->nodes_alloc); + new_edests = re_realloc (dfa->edests, re_node_set, dfa->nodes_alloc); + new_eclosures = re_realloc (dfa->eclosures, re_node_set, + dfa->nodes_alloc); + new_inveclosures = re_realloc (dfa->inveclosures, re_node_set, + dfa->nodes_alloc); + if (BE (new_nexts == NULL || new_indices == NULL + || new_edests == NULL || new_eclosures == NULL + || new_inveclosures == NULL, 0)) + return -1; + dfa->nexts = new_nexts; + dfa->org_indices = new_indices; + dfa->edests = new_edests; + dfa->eclosures = new_eclosures; + dfa->inveclosures = new_inveclosures; + } + } + dfa->nodes[dfa->nodes_len] = token; + dfa->nodes[dfa->nodes_len].duplicated = 0; + dfa->nodes[dfa->nodes_len].constraint = 0; + return dfa->nodes_len++; +} + +static unsigned int inline +calc_state_hash (nodes, context) + const re_node_set *nodes; + unsigned int context; +{ + unsigned int hash = nodes->nelem + context; + int i; + for (i = 0 ; i < nodes->nelem ; i++) + hash += nodes->elems[i]; + return hash; +} + +/* Search for the state whose node_set is equivalent to NODES. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t* +re_acquire_state (err, dfa, nodes) + reg_errcode_t *err; + re_dfa_t *dfa; + const re_node_set *nodes; +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (BE (nodes->nelem == 0, 0)) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, 0); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (&state->nodes, nodes)) + return state; + } + + /* There are no appropriate state in the dfa, create the new one. */ + new_state = create_ci_newstate (dfa, nodes, hash); + if (BE (new_state != NULL, 1)) + return new_state; + else + { + *err = REG_ESPACE; + return NULL; + } +} + +/* Search for the state whose node_set is equivalent to NODES and + whose context is equivalent to CONTEXT. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t* +re_acquire_state_context (err, dfa, nodes, context) + reg_errcode_t *err; + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int context; +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (nodes->nelem == 0) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, context); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (state->entrance_nodes, nodes) + && state->context == context) + return state; + } + /* There are no appropriate state in `dfa', create the new one. */ + new_state = create_cd_newstate (dfa, nodes, context, hash); + if (BE (new_state != NULL, 1)) + return new_state; + else + { + *err = REG_ESPACE; + return NULL; + } +} + +/* Allocate memory for DFA state and initialize common properties. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +create_newstate_common (dfa, nodes, hash) + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int hash; +{ + re_dfastate_t *newstate; + reg_errcode_t err; + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + newstate->trtable = NULL; + newstate->trtable_search = NULL; + newstate->hash = hash; + return newstate; +} + +/* Store the new state NEWSTATE whose hash value is HASH in appropriate + position. Return value indicate the error code if failed. */ + +static reg_errcode_t +register_state (dfa, newstate, hash) + re_dfa_t *dfa; + re_dfastate_t *newstate; + unsigned int hash; +{ + struct re_state_table_entry *spot; + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + if (spot->alloc <= spot->num) + { + re_dfastate_t **new_array; + spot->alloc = 2 * spot->num + 2; + new_array = re_realloc (spot->array, re_dfastate_t *, spot->alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + spot->array = new_array; + } + spot->array[spot->num++] = newstate; + return REG_NOERROR; +} + +/* Create the new state which is independ of contexts. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +create_ci_newstate (dfa, nodes, hash) + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int hash; +{ + int i; + reg_errcode_t err; + re_dfastate_t *newstate; + newstate = create_newstate_common (dfa, nodes, hash); + if (BE (newstate == NULL, 0)) + return NULL; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (type == CHARACTER && !node->constraint) + continue; + + /* If the state has the halt node, the state is a halt state. */ + else if (type == END_OF_RE) + newstate->halt = 1; +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET + || (type == OP_PERIOD && MB_CUR_MAX > 1)) + newstate->accept_mb = 1; +#endif /* RE_ENABLE_I18N */ + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR || node->constraint) + newstate->has_constraint = 1; + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/* Create the new state which is depend on the context CONTEXT. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +create_cd_newstate (dfa, nodes, context, hash) + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int context, hash; +{ + int i, nctx_nodes = 0; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = create_newstate_common (dfa, nodes, hash); + if (BE (newstate == NULL, 0)) + return NULL; + newstate->context = context; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + unsigned int constraint = 0; + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (node->constraint) + constraint = node->constraint; + + if (type == CHARACTER && !constraint) + continue; + /* If the state has the halt node, the state is a halt state. */ + else if (type == END_OF_RE) + newstate->halt = 1; +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET + || (type == OP_PERIOD && MB_CUR_MAX > 1)) + newstate->accept_mb = 1; +#endif /* RE_ENABLE_I18N */ + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR) + constraint = node->opr.ctx_type; + + if (constraint) + { + if (newstate->entrance_nodes == &newstate->nodes) + { + newstate->entrance_nodes = re_malloc (re_node_set, 1); + if (BE (newstate->entrance_nodes == NULL, 0)) + { + free_state (newstate); + return NULL; + } + re_node_set_init_copy (newstate->entrance_nodes, nodes); + nctx_nodes = 0; + newstate->has_constraint = 1; + } + + if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) + { + re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); + ++nctx_nodes; + } + } + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +static void +free_state (state) + re_dfastate_t *state; +{ + if (state->entrance_nodes != &state->nodes) + { + re_node_set_free (state->entrance_nodes); + re_free (state->entrance_nodes); + } + re_node_set_free (&state->nodes); + re_free (state->trtable); + re_free (state->trtable_search); + re_free (state); +} diff --git a/posix/regex_internal.h b/posix/regex_internal.h new file mode 100644 index 0000000..bf84ad6 --- /dev/null +++ b/posix/regex_internal.h @@ -0,0 +1,742 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +#ifndef _REGEX_INTERNAL_H +#define _REGEX_INTERNAL_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif +#if defined HAVE_WCHAR_H || defined _LIBC +# include +#endif /* HAVE_WCHAR_H || _LIBC */ +#if defined HAVE_WCTYPE_H || defined _LIBC +# include +#endif /* HAVE_WCTYPE_H || _LIBC */ + +/* In case that the system doesn't have isblank(). */ +#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank +# define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +#ifdef _LIBC +# ifndef _RE_DEFINE_LOCALE_FUNCTIONS +# define _RE_DEFINE_LOCALE_FUNCTIONS 1 +# include +# include +# include +# endif +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if HAVE_LIBINTL_H || defined _LIBC +# include +# ifdef _LIBC +# undef gettext +# define gettext(msgid) \ + INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES) +# endif +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC +# define RE_ENABLE_I18N +#endif + +#if __GNUC__ >= 3 +# define BE(expr, val) __builtin_expect (expr, val) +#else +# define BE(expr, val) (expr) +# define inline +#endif + +/* Number of bits in a byte. */ +#define BYTE_BITS 8 +/* Number of single byte character. */ +#define SBC_MAX 256 + +#define COLL_ELEM_LEN_MAX 8 + +/* The character which represents newline. */ +#define NEWLINE_CHAR '\n' +#define WIDE_NEWLINE_CHAR L'\n' + +/* Rename to standard API for using out of glibc. */ +#ifndef _LIBC +# define __wctype wctype +# define __iswctype iswctype +# define __btowc btowc +# define __mempcpy mempcpy +# define __wcrtomb wcrtomb +# define attribute_hidden +#endif /* not _LIBC */ + +extern const char __re_error_msgid[] attribute_hidden; +extern const size_t __re_error_msgid_idx[] attribute_hidden; + +/* Number of bits in an unsinged int. */ +#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS) +/* Number of unsigned int in an bit_set. */ +#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS) +typedef unsigned int bitset[BITSET_UINTS]; +typedef unsigned int *re_bitset_ptr_t; + +#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS) +#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS)) +#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS)) +#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS) +#define bitset_set_all(set) \ + memset (set, 255, sizeof (unsigned int) * BITSET_UINTS) +#define bitset_copy(dest,src) \ + memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS) +static inline void bitset_not (bitset set); +static inline void bitset_merge (bitset dest, const bitset src); +static inline void bitset_not_merge (bitset dest, const bitset src); + +#define PREV_WORD_CONSTRAINT 0x0001 +#define PREV_NOTWORD_CONSTRAINT 0x0002 +#define NEXT_WORD_CONSTRAINT 0x0004 +#define NEXT_NOTWORD_CONSTRAINT 0x0008 +#define PREV_NEWLINE_CONSTRAINT 0x0010 +#define NEXT_NEWLINE_CONSTRAINT 0x0020 +#define PREV_BEGBUF_CONSTRAINT 0x0040 +#define NEXT_ENDBUF_CONSTRAINT 0x0080 +#define DUMMY_CONSTRAINT 0x0100 + +typedef enum +{ + INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + LINE_FIRST = PREV_NEWLINE_CONSTRAINT, + LINE_LAST = NEXT_NEWLINE_CONSTRAINT, + BUF_FIRST = PREV_BEGBUF_CONSTRAINT, + BUF_LAST = NEXT_ENDBUF_CONSTRAINT, + WORD_DELIM = DUMMY_CONSTRAINT +} re_context_type; + +typedef struct +{ + int alloc; + int nelem; + int *elems; +} re_node_set; + +typedef enum +{ + NON_TYPE = 0, + + /* Token type, these are used only by token. */ + OP_OPEN_BRACKET, + OP_CLOSE_BRACKET, + OP_CHARSET_RANGE, + OP_OPEN_DUP_NUM, + OP_CLOSE_DUP_NUM, + OP_NON_MATCH_LIST, + OP_OPEN_COLL_ELEM, + OP_CLOSE_COLL_ELEM, + OP_OPEN_EQUIV_CLASS, + OP_CLOSE_EQUIV_CLASS, + OP_OPEN_CHAR_CLASS, + OP_CLOSE_CHAR_CLASS, + OP_WORD, + OP_NOTWORD, + BACK_SLASH, + + /* Tree type, these are used only by tree. */ + CONCAT, + ALT, + SUBEXP, + SIMPLE_BRACKET, +#ifdef RE_ENABLE_I18N + COMPLEX_BRACKET, +#endif /* RE_ENABLE_I18N */ + + /* Node type, These are used by token, node, tree. */ + OP_OPEN_SUBEXP, + OP_CLOSE_SUBEXP, + OP_PERIOD, + CHARACTER, + END_OF_RE, + OP_ALT, + OP_DUP_ASTERISK, + OP_DUP_PLUS, + OP_DUP_QUESTION, + OP_BACK_REF, + ANCHOR, + + /* Dummy marker. */ + END_OF_RE_TOKEN_T +} re_token_type_t; + +#ifdef RE_ENABLE_I18N +typedef struct +{ + /* Multibyte characters. */ + wchar_t *mbchars; + + /* Collating symbols. */ +# ifdef _LIBC + int32_t *coll_syms; +# endif + + /* Equivalence classes. */ +# ifdef _LIBC + int32_t *equiv_classes; +# endif + + /* Range expressions. */ +# ifdef _LIBC + uint32_t *range_starts; + uint32_t *range_ends; +# else /* not _LIBC */ + wchar_t *range_starts; + wchar_t *range_ends; +# endif /* not _LIBC */ + + /* Character classes. */ + wctype_t *char_classes; + + /* If this character set is the non-matching list. */ + unsigned int non_match : 1; + + /* # of multibyte characters. */ + int nmbchars; + + /* # of collating symbols. */ + int ncoll_syms; + + /* # of equivalence classes. */ + int nequiv_classes; + + /* # of range expressions. */ + int nranges; + + /* # of character classes. */ + int nchar_classes; +} re_charset_t; +#endif /* RE_ENABLE_I18N */ + +typedef struct +{ + union + { + unsigned char c; /* for CHARACTER */ + re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; /* for COMPLEX_BRACKET */ +#endif /* RE_ENABLE_I18N */ + int idx; /* for BACK_REF */ + re_context_type ctx_type; /* for ANCHOR */ + } opr; +#if __GNUC__ >= 2 + re_token_type_t type : 8; +#else + re_token_type_t type; +#endif + unsigned int constraint : 10; /* context constraint */ + unsigned int duplicated : 1; +#ifdef RE_ENABLE_I18N + unsigned int mb_partial : 1; +#endif +} re_token_t; + +#define IS_EPSILON_NODE(type) \ + ((type) == OP_ALT || (type) == OP_DUP_ASTERISK || (type) == OP_DUP_PLUS \ + || (type) == OP_DUP_QUESTION || (type) == ANCHOR \ + || (type) == OP_OPEN_SUBEXP || (type) == OP_CLOSE_SUBEXP) + +#define ACCEPT_MB_NODE(type) \ + ((type) == COMPLEX_BRACKET || (type) == OP_PERIOD) + +struct re_string_t +{ + /* Indicate the raw buffer which is the original string passed as an + argument of regexec(), re_search(), etc.. */ + const unsigned char *raw_mbs; + /* Store the multibyte string. In case of "case insensitive mode" like + REG_ICASE, upper cases of the string are stored, otherwise MBS points + the same address that RAW_MBS points. */ + unsigned char *mbs; + /* Store the case sensitive multibyte string. In case of + "case insensitive mode", the original string are stored, + otherwise MBS_CASE points the same address that MBS points. */ + unsigned char *mbs_case; +#ifdef RE_ENABLE_I18N + /* Store the wide character string which is corresponding to MBS. */ + wint_t *wcs; + mbstate_t cur_state; +#endif + /* Index in RAW_MBS. Each character mbs[i] corresponds to + raw_mbs[raw_mbs_idx + i]. */ + int raw_mbs_idx; + /* The length of the valid characters in the buffers. */ + int valid_len; + /* The length of the buffers MBS, MBS_CASE, and WCS. */ + int bufs_len; + /* The index in MBS, which is updated by re_string_fetch_byte. */ + int cur_idx; + /* This is length_of_RAW_MBS - RAW_MBS_IDX. */ + int len; + /* End of the buffer may be shorter than its length in the cases such + as re_match_2, re_search_2. Then, we use STOP for end of the buffer + instead of LEN. */ + int stop; + + /* The context of mbs[0]. We store the context independently, since + the context of mbs[0] may be different from raw_mbs[0], which is + the beginning of the input string. */ + unsigned int tip_context; + /* The translation passed as a part of an argument of re_compile_pattern. */ + RE_TRANSLATE_TYPE trans; + /* 1 if REG_ICASE. */ + unsigned int icase : 1; +}; +typedef struct re_string_t re_string_t; +/* In case of REG_ICASE, we allocate the buffer dynamically for mbs. */ +#define MBS_ALLOCATED(pstr) (pstr->icase) +/* In case that we need translation, we allocate the buffer dynamically + for mbs_case. Note that mbs == mbs_case if not REG_ICASE. */ +#define MBS_CASE_ALLOCATED(pstr) (pstr->trans != NULL) + + +static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str, + int len, int init_len, + RE_TRANSLATE_TYPE trans, int icase); +static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str, + int len, RE_TRANSLATE_TYPE trans, + int icase); +static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx, + int eflags, int newline); +static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, + int new_buf_len); +#ifdef RE_ENABLE_I18N +static void build_wcs_buffer (re_string_t *pstr); +static void build_wcs_upper_buffer (re_string_t *pstr); +#endif /* RE_ENABLE_I18N */ +static void build_upper_buffer (re_string_t *pstr); +static void re_string_translate_buffer (re_string_t *pstr); +static void re_string_destruct (re_string_t *pstr); +#ifdef RE_ENABLE_I18N +static int re_string_elem_size_at (const re_string_t *pstr, int idx); +static inline int re_string_char_size_at (const re_string_t *pstr, int idx); +static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx); +#endif /* RE_ENABLE_I18N */ +static unsigned int re_string_context_at (const re_string_t *input, int idx, + int eflags, int newline_anchor); +#define re_string_peek_byte(pstr, offset) \ + ((pstr)->mbs[(pstr)->cur_idx + offset]) +#define re_string_peek_byte_case(pstr, offset) \ + ((pstr)->mbs_case[(pstr)->cur_idx + offset]) +#define re_string_fetch_byte(pstr) \ + ((pstr)->mbs[(pstr)->cur_idx++]) +#define re_string_fetch_byte_case(pstr) \ + ((pstr)->mbs_case[(pstr)->cur_idx++]) +#define re_string_first_byte(pstr, idx) \ + ((idx) == (pstr)->len || (pstr)->wcs[idx] != WEOF) +#define re_string_is_single_byte_char(pstr, idx) \ + ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \ + || (pstr)->wcs[(idx) + 1] != WEOF)) +#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) +#define re_string_cur_idx(pstr) ((pstr)->cur_idx) +#define re_string_get_buffer(pstr) ((pstr)->mbs) +#define re_string_length(pstr) ((pstr)->len) +#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) +#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) +#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) + +#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) +#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t))) +#define re_free(p) free (p) + +struct bin_tree_t +{ + struct bin_tree_t *parent; + struct bin_tree_t *left; + struct bin_tree_t *right; + + /* `node_idx' is the index in dfa->nodes, if `type' == 0. + Otherwise `type' indicate the type of this node. */ + re_token_type_t type; + int node_idx; + + int first; + int next; + re_node_set eclosure; +}; +typedef struct bin_tree_t bin_tree_t; + + +#define CONTEXT_WORD 1 +#define CONTEXT_NEWLINE (CONTEXT_WORD << 1) +#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) +#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) + +#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) +#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) +#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) +#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) +#define IS_ORDINARY_CONTEXT(c) ((c) == 0) + +#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') +#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) +#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') +#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) + +#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ + ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ + || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) + +#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ + ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ + || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) + +struct re_dfastate_t +{ + unsigned int hash; + re_node_set nodes; + re_node_set *entrance_nodes; + struct re_dfastate_t **trtable; + struct re_dfastate_t **trtable_search; + /* If this state is a special state. + A state is a special state if the state is the halt state, or + a anchor. */ + unsigned int context : 2; + unsigned int halt : 1; + /* If this state can accept `multi byte'. + Note that we refer to multibyte characters, and multi character + collating elements as `multi byte'. */ + unsigned int accept_mb : 1; + /* If this state has backreference node(s). */ + unsigned int has_backref : 1; + unsigned int has_constraint : 1; +}; +typedef struct re_dfastate_t re_dfastate_t; + +typedef struct +{ + /* start <= node < end */ + int start; + int end; +} re_subexp_t; + +struct re_state_table_entry +{ + int num; + int alloc; + re_dfastate_t **array; +}; + +/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ + +typedef struct +{ + int next_idx; + int alloc; + re_dfastate_t **array; +} state_array_t; + +/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ + +typedef struct +{ + int node; + int str_idx; /* The position NODE match at. */ + state_array_t path; +} re_sub_match_last_t; + +/* Store information about the node NODE whose type is OP_OPEN_SUBEXP. + And information about the node, whose type is OP_CLOSE_SUBEXP, + corresponding to NODE is stored in LASTS. */ + +typedef struct +{ + int str_idx; + int node; + int next_last_offset; + state_array_t *path; + int alasts; /* Allocation size of LASTS. */ + int nlasts; /* The number of LASTS. */ + re_sub_match_last_t **lasts; +} re_sub_match_top_t; + +struct re_backref_cache_entry +{ + int node; + int str_idx; + int subexp_from; + int subexp_to; + int flag; +}; + +typedef struct +{ + /* EFLAGS of the argument of regexec. */ + int eflags; + /* Where the matching ends. */ + int match_last; + int last_node; + /* The string object corresponding to the input string. */ + re_string_t *input; + /* The state log used by the matcher. */ + re_dfastate_t **state_log; + int state_log_top; + /* Back reference cache. */ + int nbkref_ents; + int abkref_ents; + struct re_backref_cache_entry *bkref_ents; + int max_mb_elem_len; + int nsub_tops; + int asub_tops; + re_sub_match_top_t **sub_tops; +} re_match_context_t; + +typedef struct +{ + int cur_bkref; + int cls_subexp_idx; + + re_dfastate_t **sifted_states; + re_dfastate_t **limited_states; + + re_node_set limits; + + int last_node; + int last_str_idx; + int check_subexp; +} re_sift_context_t; + +struct re_fail_stack_ent_t +{ + int idx; + int node; + regmatch_t *regs; + re_node_set eps_via_nodes; +}; + +struct re_fail_stack_t +{ + int num; + int alloc; + struct re_fail_stack_ent_t *stack; +}; + +struct re_dfa_t +{ + re_bitset_ptr_t word_char; + + /* number of subexpressions `re_nsub' is in regex_t. */ + int subexps_alloc; + re_subexp_t *subexps; + + re_token_t *nodes; + int nodes_alloc; + int nodes_len; + bin_tree_t *str_tree; + int *nexts; + int *org_indices; + re_node_set *edests; + re_node_set *eclosures; + re_node_set *inveclosures; + struct re_state_table_entry *state_table; + unsigned int state_hash_mask; + re_dfastate_t *init_state; + re_dfastate_t *init_state_word; + re_dfastate_t *init_state_nl; + re_dfastate_t *init_state_begbuf; + int states_alloc; + int init_node; + int nbackref; /* The number of backreference in this dfa. */ + /* Bitmap expressing which backreference is used. */ + unsigned int used_bkref_map; +#ifdef DEBUG + char* re_str; +#endif + unsigned int has_plural_match : 1; + /* If this dfa has "multibyte node", which is a backreference or + a node which can accept multibyte character or multi character + collating element. */ + unsigned int has_mb_node : 1; +}; +typedef struct re_dfa_t re_dfa_t; + +static reg_errcode_t re_node_set_alloc (re_node_set *set, int size); +static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem); +static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1, + int elem2); +#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) +static reg_errcode_t re_node_set_init_copy (re_node_set *dest, + const re_node_set *src); +static reg_errcode_t re_node_set_add_intersect (re_node_set *dest, + const re_node_set *src1, + const re_node_set *src2); +static reg_errcode_t re_node_set_init_union (re_node_set *dest, + const re_node_set *src1, + const re_node_set *src2); +static reg_errcode_t re_node_set_merge (re_node_set *dest, + const re_node_set *src); +static int re_node_set_insert (re_node_set *set, int elem); +static int re_node_set_compare (const re_node_set *set1, + const re_node_set *set2); +static int re_node_set_contains (const re_node_set *set, int elem); +static void re_node_set_remove_at (re_node_set *set, int idx); +#define re_node_set_remove(set,id) \ + (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) +#define re_node_set_empty(p) ((p)->nelem = 0) +#define re_node_set_free(set) re_free ((set)->elems) +static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode); +static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa, + const re_node_set *nodes); +static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err, + re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context); +static void free_state (re_dfastate_t *state); + + +typedef enum +{ + SB_CHAR, + MB_CHAR, + EQUIV_CLASS, + COLL_SYM, + CHAR_CLASS +} bracket_elem_type; + +typedef struct +{ + bracket_elem_type type; + union + { + unsigned char ch; + unsigned char *name; + wchar_t wch; + } opr; +} bracket_elem_t; + + +/* Inline functions for bitset operation. */ +static inline void +bitset_not (set) + bitset set; +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i) + set[bitset_i] = ~set[bitset_i]; +} + +static inline void +bitset_merge (dest, src) + bitset dest; + const bitset src; +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i) + dest[bitset_i] |= src[bitset_i]; +} + +static inline void +bitset_not_merge (dest, src) + bitset dest; + const bitset src; +{ + int i; + for (i = 0; i < BITSET_UINTS; ++i) + dest[i] |= ~src[i]; +} + +#ifdef RE_ENABLE_I18N +/* Inline functions for re_string. */ +static inline int +re_string_char_size_at (pstr, idx) + const re_string_t *pstr; + int idx; +{ + int byte_idx; + if (MB_CUR_MAX == 1) + return 1; + for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx) + if (pstr->wcs[idx + byte_idx] != WEOF) + break; + return byte_idx; +} + +static inline wint_t +re_string_wchar_at (pstr, idx) + const re_string_t *pstr; + int idx; +{ + if (MB_CUR_MAX == 1) + return (wint_t) pstr->mbs[idx]; + return (wint_t) pstr->wcs[idx]; +} + +static int +re_string_elem_size_at (pstr, idx) + const re_string_t *pstr; + int idx; +{ +#ifdef _LIBC + const unsigned char *p, *extra; + const int32_t *table, *indirect; + int32_t tmp; +# include + uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + + if (nrules != 0) + { + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + p = pstr->mbs + idx; + tmp = findidx (&p); + return p - pstr->mbs - idx; + } + else +#endif /* _LIBC */ + return 1; +} +#endif /* RE_ENABLE_I18N */ + +#endif /* _REGEX_INTERNAL_H */ diff --git a/posix/regexec.c b/posix/regexec.c new file mode 100644 index 0000000..6ea14a6 --- /dev/null +++ b/posix/regexec.c @@ -0,0 +1,3977 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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. */ + +static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + re_string_t *input, int n); +static void match_ctx_clean (re_match_context_t *mctx); +static void match_ctx_free (re_match_context_t *cache); +static void match_ctx_free_subtops (re_match_context_t *mctx); +static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node, + int str_idx, int from, int to); +static int search_cur_bkref_entry (re_match_context_t *mctx, int str_idx); +static void match_ctx_clear_flag (re_match_context_t *mctx); +static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node, + int str_idx); +static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, + int node, int str_idx); +static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, int last_node, + int last_str_idx, int check_subexp); +static reg_errcode_t re_search_internal (const regex_t *preg, + const char *string, int length, + int start, int range, int stop, + size_t nmatch, regmatch_t pmatch[], + int eflags); +static int re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, int length1, + const char *string2, int length2, + int start, int range, struct re_registers *regs, + int stop, int ret_len); +static int re_search_stub (struct re_pattern_buffer *bufp, + const char *string, int length, int start, + int range, int stop, struct re_registers *regs, + int ret_len); +static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, + int nregs, int regs_allocated); +static inline re_dfastate_t *acquire_init_state_context (reg_errcode_t *err, + const regex_t *preg, + const re_match_context_t *mctx, + int idx); +static reg_errcode_t prune_impossible_nodes (const regex_t *preg, + re_match_context_t *mctx); +static int check_matching (const regex_t *preg, re_match_context_t *mctx, + int fl_search, int fl_longest_match); +static int check_halt_node_context (const re_dfa_t *dfa, int node, + unsigned int context); +static int check_halt_state_context (const regex_t *preg, + const re_dfastate_t *state, + const re_match_context_t *mctx, int idx); +static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, int cur_node, + int cur_idx, int nmatch); +static int proceed_next_node (const regex_t *preg, int nregs, regmatch_t *regs, + const re_match_context_t *mctx, + int *pidx, int node, re_node_set *eps_via_nodes, + struct re_fail_stack_t *fs); +static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, + int str_idx, int *dests, int nregs, + regmatch_t *regs, + re_node_set *eps_via_nodes); +static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs, + regmatch_t *regs, re_node_set *eps_via_nodes); +static reg_errcode_t set_regs (const regex_t *preg, + const re_match_context_t *mctx, + size_t nmatch, regmatch_t *pmatch, + int fl_backtrack); +static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs); + +#ifdef RE_ENABLE_I18N +static int sift_states_iter_mb (const regex_t *preg, + const re_match_context_t *mctx, + re_sift_context_t *sctx, + int node_idx, int str_idx, int max_str_idx); +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t sift_states_backward (const regex_t *preg, + re_match_context_t *mctx, + re_sift_context_t *sctx); +static reg_errcode_t update_cur_sifted_state (const regex_t *preg, + re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, + re_node_set *dest_nodes); +static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates); +static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node, + re_node_set *dest_nodes, + const re_node_set *and_nodes); +static int check_dst_limits (re_dfa_t *dfa, re_node_set *limits, + re_match_context_t *mctx, int dst_node, + int dst_idx, int src_node, int src_idx); +static int check_dst_limits_calc_pos (re_dfa_t *dfa, re_match_context_t *mctx, + int limit, re_node_set *eclosures, + int subexp_idx, int node, int str_idx); +static reg_errcode_t check_subexp_limits (re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates, + re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, + int str_idx); +static reg_errcode_t sift_states_bkref (const regex_t *preg, + re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, re_node_set *dest_nodes); +static reg_errcode_t clean_state_log_if_need (re_match_context_t *mctx, + int next_state_log_idx); +static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst, + re_dfastate_t **src, int num); +static re_dfastate_t *transit_state (reg_errcode_t *err, const regex_t *preg, + re_match_context_t *mctx, + re_dfastate_t *state, int fl_search); +static reg_errcode_t check_subexp_matching_top (re_dfa_t *dfa, + re_match_context_t *mctx, + re_node_set *cur_nodes, + int str_idx); +static re_dfastate_t *transit_state_sb (reg_errcode_t *err, const regex_t *preg, + re_dfastate_t *pstate, + int fl_search, + re_match_context_t *mctx); +#ifdef RE_ENABLE_I18N +static reg_errcode_t transit_state_mb (const regex_t *preg, + re_dfastate_t *pstate, + re_match_context_t *mctx); +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t transit_state_bkref (const regex_t *preg, + re_node_set *nodes, + re_match_context_t *mctx); +static reg_errcode_t get_subexp (const regex_t *preg, re_match_context_t *mctx, + int bkref_node, int bkref_str_idx); +static reg_errcode_t get_subexp_sub (const regex_t *preg, + re_match_context_t *mctx, + re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, + int bkref_node, int bkref_str); +static int find_subexp_node (re_dfa_t *dfa, re_node_set *nodes, + int subexp_idx, int fl_open); +static reg_errcode_t check_arrival (const regex_t *preg, + re_match_context_t *mctx, + state_array_t *path, int top_node, + int top_str, int last_node, int last_str, + int fl_open); +static reg_errcode_t check_arrival_add_next_nodes (const regex_t *preg, + re_dfa_t *dfa, + re_match_context_t *mctx, + int str_idx, + re_node_set *cur_nodes, + re_node_set *next_nodes); +static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa, + re_node_set *cur_nodes, + int ex_subexp, int fl_open); +static reg_errcode_t check_arrival_expand_ecl_sub (re_dfa_t *dfa, + re_node_set *dst_nodes, + int target, int ex_subexp, + int fl_open); +static reg_errcode_t expand_bkref_cache (const regex_t *preg, + re_match_context_t *mctx, + re_node_set *cur_nodes, int cur_str, + int last_str, int subexp_num, + int fl_open); +static re_dfastate_t **build_trtable (const regex_t *dfa, + const re_dfastate_t *state, + int fl_search); +#ifdef RE_ENABLE_I18N +static int check_node_accept_bytes (const regex_t *preg, int node_idx, + const re_string_t *input, int idx); +# ifdef _LIBC +static unsigned int find_collation_sequence_value (const unsigned char *mbs, + size_t name_len); +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ +static int group_nodes_into_DFAstates (const regex_t *dfa, + const re_dfastate_t *state, + re_node_set *states_node, + bitset *states_ch); +static int check_node_accept (const regex_t *preg, const re_token_t *node, + const re_match_context_t *mctx, int idx); +static reg_errcode_t extend_buffers (re_match_context_t *mctx); + +/* Entry point for POSIX code. */ + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *__restrict preg; + const char *__restrict string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + reg_errcode_t err; + int length = strlen (string); + if (preg->no_sub) + err = re_search_internal (preg, string, length, 0, length, length, 0, + NULL, eflags); + else + err = re_search_internal (preg, string, length, 0, length, length, nmatch, + pmatch, eflags); + return err != REG_NOERROR; +} +#ifdef _LIBC +weak_alias (__regexec, regexec) +#endif + +/* Entry points for GNU code. */ + +/* re_match, re_search, re_match_2, re_search_2 + + The former two functions operate on STRING with length LENGTH, + while the later two operate on concatenation of STRING1 and STRING2 + with lengths LENGTH1 and LENGTH2, respectively. + + re_match() matches the compiled pattern in BUFP against the string, + starting at index START. + + re_search() first tries matching at index START, then it tries to match + starting from index START + 1, and so on. The last start position tried + is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same + way as re_match().) + + The parameter STOP of re_{match,search}_2 specifies that no match exceeding + the first STOP characters of the concatenation of the strings should be + concerned. + + If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match + and all groups is stroed in REGS. (For the "_2" variants, the offsets are + computed relative to the concatenation, not relative to the individual + strings.) + + On success, re_match* functions return the length of the match, re_search* + return the position of the start of the match. Return value -1 means no + match was found and -2 indicates an internal error. */ + +int +re_match (bufp, string, length, start, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, 0, length, regs, 1); +} +#ifdef _LIBC +weak_alias (__re_match, re_match) +#endif + +int +re_search (bufp, string, length, start, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, range, length, regs, 0); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + +int +re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, 0, regs, stop, 1); +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +int +re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, range, regs, stop, 0); +} +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +static int +re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs, + stop, ret_len) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop, ret_len; + struct re_registers *regs; +{ + const char *str; + int rval; + int len = length1 + length2; + int free_str = 0; + + if (BE (length1 < 0 || length2 < 0 || stop < 0, 0)) + return -2; + + /* Concatenate the strings. */ + if (length2 > 0) + if (length1 > 0) + { + char *s = re_malloc (char, len); + + if (BE (s == NULL, 0)) + return -2; + memcpy (s, string1, length1); + memcpy (s + length1, string2, length2); + str = s; + free_str = 1; + } + else + str = string2; + else + str = string1; + + rval = re_search_stub (bufp, str, len, start, range, stop, regs, + ret_len); + if (free_str) + re_free ((char *) str); + return rval; +} + +/* The parameters have the same meaning as those of re_search. + Additional parameters: + If RET_LEN is nonzero the length of the match is returned (re_match style); + otherwise the position of the match is returned. */ + +static int +re_search_stub (bufp, string, length, start, range, stop, regs, ret_len) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range, stop, ret_len; + struct re_registers *regs; +{ + reg_errcode_t result; + regmatch_t *pmatch; + int nregs, rval; + int eflags = 0; + + /* Check for out-of-range. */ + if (BE (start < 0 || start > length, 0)) + return -1; + if (BE (start + range > length, 0)) + range = length - start; + else if (BE (start + range < 0, 0)) + range = -start; + + eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; + eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; + + /* Compile fastmap if we haven't yet. */ + if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate) + re_compile_fastmap (bufp); + + if (BE (bufp->no_sub, 0)) + regs = NULL; + + /* We need at least 1 register. */ + if (regs == NULL) + nregs = 1; + else if (BE (bufp->regs_allocated == REGS_FIXED && + regs->num_regs < bufp->re_nsub + 1, 0)) + { + nregs = regs->num_regs; + if (BE (nregs < 1, 0)) + { + /* Nothing can be copied to regs. */ + regs = NULL; + nregs = 1; + } + } + else + nregs = bufp->re_nsub + 1; + pmatch = re_malloc (regmatch_t, nregs); + if (BE (pmatch == NULL, 0)) + return -2; + + result = re_search_internal (bufp, string, length, start, range, stop, + nregs, pmatch, eflags); + + rval = 0; + + /* I hope we needn't fill ther regs with -1's when no match was found. */ + if (result != REG_NOERROR) + rval = -1; + else if (regs != NULL) + { + /* If caller wants register contents data back, copy them. */ + bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated); + if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) + rval = -2; + } + + if (BE (rval == 0, 1)) + { + if (ret_len) + { + assert (pmatch[0].rm_so == start); + rval = pmatch[0].rm_eo - start; + } + else + rval = pmatch[0].rm_so; + } + re_free (pmatch); + return rval; +} + +static unsigned +re_copy_regs (regs, pmatch, nregs, regs_allocated) + struct re_registers *regs; + regmatch_t *pmatch; + int nregs, regs_allocated; +{ + int rval = REGS_REALLOCATE; + int i; + int need_regs = nregs + 1; + /* We need one extra element beyond `num_regs' for the `-1' marker GNU code + uses. */ + + /* Have the register data arrays been allocated? */ + if (regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. */ + regs->start = re_malloc (regoff_t, need_regs); + if (BE (regs->start == NULL, 0)) + return REGS_UNALLOCATED; + regs->end = re_malloc (regoff_t, need_regs); + if (BE (regs->end == NULL, 0)) + { + re_free (regs->start); + return REGS_UNALLOCATED; + } + regs->num_regs = need_regs; + } + else if (regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (need_regs > regs->num_regs) + { + regs->start = re_realloc (regs->start, regoff_t, need_regs); + if (BE (regs->start == NULL, 0)) + { + if (regs->end != NULL) + re_free (regs->end); + return REGS_UNALLOCATED; + } + regs->end = re_realloc (regs->end, regoff_t, need_regs); + if (BE (regs->end == NULL, 0)) + { + re_free (regs->start); + return REGS_UNALLOCATED; + } + regs->num_regs = need_regs; + } + } + else + { + assert (regs_allocated == REGS_FIXED); + /* This function may not be called with REGS_FIXED and nregs too big. */ + assert (regs->num_regs >= nregs); + rval = REGS_FIXED; + } + + /* Copy the regs. */ + for (i = 0; i < nregs; ++i) + { + regs->start[i] = pmatch[i].rm_so; + regs->end[i] = pmatch[i].rm_eo; + } + for ( ; i < regs->num_regs; ++i) + regs->start[i] = regs->end[i] = -1; + + return rval; +} + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC +int +# ifdef _LIBC +weak_function +# endif +re_exec (s) + const char *s; +{ + return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); +} +#endif /* _REGEX_RE_COMP */ + +static re_node_set empty_set; + +/* Internal entry point. */ + +/* Searches for a compiled pattern PREG in the string STRING, whose + length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same + mingings with regexec. START, and RANGE have the same meanings + with re_search. + Return REG_NOERROR if we find a match, and REG_NOMATCH if not, + otherwise return the error code. + Note: We assume front end functions already check ranges. + (START + RANGE >= 0 && START + RANGE <= LENGTH) */ + +static reg_errcode_t +re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch, + eflags) + const regex_t *preg; + const char *string; + int length, start, range, stop, eflags; + size_t nmatch; + regmatch_t pmatch[]; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + re_string_t input; + int left_lim, right_lim, incr; + int fl_longest_match, match_first, match_last = -1; + int fast_translate, sb; + re_match_context_t mctx; + char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate + && range && !preg->can_be_null) ? preg->fastmap : NULL); + + /* Check if the DFA haven't been compiled. */ + if (BE (preg->used == 0 || dfa->init_state == NULL + || dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return REG_NOMATCH; + + re_node_set_init_empty (&empty_set); + memset (&mctx, '\0', sizeof (re_match_context_t)); + + /* We must check the longest matching, if nmatch > 0. */ + fl_longest_match = (nmatch != 0 || dfa->nbackref); + + err = re_string_allocate (&input, string, length, dfa->nodes_len + 1, + preg->translate, preg->syntax & RE_ICASE); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + input.stop = stop; + + err = match_ctx_init (&mctx, eflags, &input, dfa->nbackref * 2); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* We will log all the DFA states through which the dfa pass, + if nmatch > 1, or this dfa has "multibyte node", which is a + back-reference or a node which can accept multibyte character or + multi character collating element. */ + if (nmatch > 1 || dfa->has_mb_node) + { + mctx.state_log = re_malloc (re_dfastate_t *, dfa->nodes_len + 1); + if (BE (mctx.state_log == NULL, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + else + mctx.state_log = NULL; + +#ifdef DEBUG + /* We assume front-end functions already check them. */ + assert (start + range >= 0 && start + range <= length); +#endif + + match_first = start; + input.tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + + /* Check incrementally whether of not the input string match. */ + incr = (range < 0) ? -1 : 1; + left_lim = (range < 0) ? start + range : start; + right_lim = (range < 0) ? start : start + range; + sb = MB_CUR_MAX == 1; + fast_translate = sb || !(preg->syntax & RE_ICASE || preg->translate); + + for (;;) + { + /* At first get the current byte from input string. */ + if (fastmap) + { + if (BE (fast_translate, 1)) + { + unsigned RE_TRANSLATE_TYPE t + = (unsigned RE_TRANSLATE_TYPE) preg->translate; + if (BE (range >= 0, 1)) + { + if (BE (t != NULL, 0)) + { + while (BE (match_first < right_lim, 1) + && !fastmap[t[(unsigned char) string[match_first]]]) + ++match_first; + } + else + { + while (BE (match_first < right_lim, 1) + && !fastmap[(unsigned char) string[match_first]]) + ++match_first; + } + if (BE (match_first == right_lim, 0)) + { + int ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (!fastmap[t ? t[ch] : ch]) + break; + } + } + else + { + while (match_first >= left_lim) + { + int ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (fastmap[t ? t[ch] : ch]) + break; + --match_first; + } + if (match_first < left_lim) + break; + } + } + else + { + int ch; + + do + { + /* In this case, we can't determine easily the current byte, + since it might be a component byte of a multibyte + character. Then we use the constructed buffer + instead. */ + /* If MATCH_FIRST is out of the valid range, reconstruct the + buffers. */ + if (input.raw_mbs_idx + input.valid_len <= match_first + || match_first < input.raw_mbs_idx) + { + err = re_string_reconstruct (&input, match_first, eflags, + preg->newline_anchor); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + /* If MATCH_FIRST is out of the buffer, leave it as '\0'. + Note that MATCH_FIRST must not be smaller than 0. */ + ch = ((match_first >= length) ? 0 + : re_string_byte_at (&input, + match_first - input.raw_mbs_idx)); + if (fastmap[ch]) + break; + match_first += incr; + } + while (match_first >= left_lim && match_first <= right_lim); + if (! fastmap[ch]) + break; + } + } + + /* Reconstruct the buffers so that the matcher can assume that + the matching starts from the begining of the buffer. */ + err = re_string_reconstruct (&input, match_first, eflags, + preg->newline_anchor); + if (BE (err != REG_NOERROR, 0)) + goto free_return; +#ifdef RE_ENABLE_I18N + /* Eliminate it when it is a component of a multibyte character + and isn't the head of a multibyte character. */ + if (sb || re_string_first_byte (&input, 0)) +#endif + { + /* It seems to be appropriate one, then use the matcher. */ + /* We assume that the matching starts from 0. */ + mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; + match_last = check_matching (preg, &mctx, 0, fl_longest_match); + if (match_last != -1) + { + if (BE (match_last == -2, 0)) + { + err = REG_ESPACE; + goto free_return; + } + else + { + mctx.match_last = match_last; + if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) + { + re_dfastate_t *pstate = mctx.state_log[match_last]; + mctx.last_node = check_halt_state_context (preg, pstate, + &mctx, match_last); + } + if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) + || dfa->nbackref) + { + err = prune_impossible_nodes (preg, &mctx); + if (err == REG_NOERROR) + break; + if (BE (err != REG_NOMATCH, 0)) + goto free_return; + } + else + break; /* We found a matching. */ + } + } + match_ctx_clean (&mctx); + } + /* Update counter. */ + match_first += incr; + if (match_first < left_lim || right_lim < match_first) + break; + } + + /* Set pmatch[] if we need. */ + if (match_last != -1 && nmatch > 0) + { + int reg_idx; + + /* Initialize registers. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; + + /* Set the points where matching start/end. */ + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = mctx.match_last; + + if (!preg->no_sub && nmatch > 1) + { + err = set_regs (preg, &mctx, nmatch, pmatch, + dfa->has_plural_match && dfa->nbackref > 0); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* At last, add the offset to the each registers, since we slided + the buffers so that We can assume that the matching starts from 0. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so != -1) + { + pmatch[reg_idx].rm_so += match_first; + pmatch[reg_idx].rm_eo += match_first; + } + } + err = (match_last == -1) ? REG_NOMATCH : REG_NOERROR; + free_return: + re_free (mctx.state_log); + if (dfa->nbackref) + match_ctx_free (&mctx); + re_string_destruct (&input); + return err; +} + +static reg_errcode_t +prune_impossible_nodes (preg, mctx) + const regex_t *preg; + re_match_context_t *mctx; +{ + int halt_node, match_last; + reg_errcode_t ret; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +#ifdef DEBUG + assert (mctx->state_log != NULL); +#endif + match_last = mctx->match_last; + halt_node = mctx->last_node; + sifted_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (sifted_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (lim_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + while (1) + { + memset (lim_states, '\0', + sizeof (re_dfastate_t *) * (match_last + 1)); + match_ctx_clear_flag (mctx); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last, 0); + ret = sift_states_backward (preg, mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] != NULL || lim_states[0] != NULL) + break; + do + { + --match_last; + if (match_last < 0) + { + ret = REG_NOMATCH; + goto free_return; + } + } while (!mctx->state_log[match_last]->halt); + halt_node = check_halt_state_context (preg, + mctx->state_log[match_last], + mctx, match_last); + } + ret = merge_state_array (dfa, sifted_states, lim_states, + match_last + 1); + re_free (lim_states); + lim_states = NULL; + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last, 0); + ret = sift_states_backward (preg, mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + re_free (mctx->state_log); + mctx->state_log = sifted_states; + sifted_states = NULL; + mctx->last_node = halt_node; + mctx->match_last = match_last; + ret = REG_NOERROR; + free_return: + re_free (sifted_states); + re_free (lim_states); + return ret; +} + +/* Acquire an initial state and return it. + We must select appropriate initial state depending on the context, + since initial states may have constraints like "\<", "^", etc.. */ + +static inline re_dfastate_t * +acquire_init_state_context (err, preg, mctx, idx) + reg_errcode_t *err; + const regex_t *preg; + const re_match_context_t *mctx; + int idx; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + + *err = REG_NOERROR; + if (dfa->init_state->has_constraint) + { + unsigned int context; + context = re_string_context_at (mctx->input, idx - 1, mctx->eflags, + preg->newline_anchor); + if (IS_WORD_CONTEXT (context)) + return dfa->init_state_word; + else if (IS_ORDINARY_CONTEXT (context)) + return dfa->init_state; + else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_begbuf; + else if (IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_nl; + else if (IS_BEGBUF_CONTEXT (context)) + { + /* It is relatively rare case, then calculate on demand. */ + return re_acquire_state_context (err, dfa, + dfa->init_state->entrance_nodes, + context); + } + else + /* Must not happen? */ + return dfa->init_state; + } + else + return dfa->init_state; +} + +/* Check whether the regular expression match input string INPUT or not, + and return the index where the matching end, return -1 if not match, + or return -2 in case of an error. + FL_SEARCH means we must search where the matching starts, + FL_LONGEST_MATCH means we want the POSIX longest matching. + Note that the matcher assume that the maching starts from the current + index of the buffer. */ + +static int +check_matching (preg, mctx, fl_search, fl_longest_match) + const regex_t *preg; + re_match_context_t *mctx; + int fl_search, fl_longest_match; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t err; + int match = 0; + int match_last = -1; + int cur_str_idx = re_string_cur_idx (mctx->input); + re_dfastate_t *cur_state; + + cur_state = acquire_init_state_context (&err, preg, mctx, cur_str_idx); + /* An initial state must not be NULL(invalid state). */ + if (BE (cur_state == NULL, 0)) + return -2; + if (mctx->state_log != NULL) + mctx->state_log[cur_str_idx] = cur_state; + + /* Check OP_OPEN_SUBEXP in the initial state in case that we use them + later. E.g. Processing back references. */ + if (dfa->nbackref) + { + err = check_subexp_matching_top (dfa, mctx, &cur_state->nodes, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (cur_state->has_backref) + { + err = transit_state_bkref (preg, &cur_state->nodes, mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* If the RE accepts NULL string. */ + if (cur_state->halt) + { + if (!cur_state->has_constraint + || check_halt_state_context (preg, cur_state, mctx, cur_str_idx)) + { + if (!fl_longest_match) + return cur_str_idx; + else + { + match_last = cur_str_idx; + match = 1; + } + } + } + + while (!re_string_eoi (mctx->input)) + { + cur_state = transit_state (&err, preg, mctx, cur_state, + fl_search && !match); + if (cur_state == NULL) /* Reached at the invalid state or an error. */ + { + cur_str_idx = re_string_cur_idx (mctx->input); + if (BE (err != REG_NOERROR, 0)) + return -2; + if (fl_search && !match) + { + /* Restart from initial state, since we are searching + the point from where matching start. */ +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX == 1 + || re_string_first_byte (mctx->input, cur_str_idx)) +#endif /* RE_ENABLE_I18N */ + cur_state = acquire_init_state_context (&err, preg, mctx, + cur_str_idx); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + return -2; + if (mctx->state_log != NULL) + mctx->state_log[cur_str_idx] = cur_state; + } + else if (!fl_longest_match && match) + break; + else /* (fl_longest_match && match) || (!fl_search && !match) */ + { + if (mctx->state_log == NULL) + break; + else + { + int max = mctx->state_log_top; + for (; cur_str_idx <= max; ++cur_str_idx) + if (mctx->state_log[cur_str_idx] != NULL) + break; + if (cur_str_idx > max) + break; + } + } + } + + if (cur_state != NULL && cur_state->halt) + { + /* Reached at a halt state. + Check the halt state can satisfy the current context. */ + if (!cur_state->has_constraint + || check_halt_state_context (preg, cur_state, mctx, + re_string_cur_idx (mctx->input))) + { + /* We found an appropriate halt state. */ + match_last = re_string_cur_idx (mctx->input); + match = 1; + if (!fl_longest_match) + break; + } + } + } + return match_last; +} + +/* Check NODE match the current context. */ + +static int check_halt_node_context (dfa, node, context) + const re_dfa_t *dfa; + int node; + unsigned int context; +{ + re_token_type_t type = dfa->nodes[node].type; + unsigned int constraint = dfa->nodes[node].constraint; + if (type != END_OF_RE) + return 0; + if (!constraint) + return 1; + if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) + return 0; + return 1; +} + +/* Check the halt state STATE match the current context. + Return 0 if not match, if the node, STATE has, is a halt node and + match the context, return the node. */ + +static int +check_halt_state_context (preg, state, mctx, idx) + const regex_t *preg; + const re_dfastate_t *state; + const re_match_context_t *mctx; + int idx; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int i; + unsigned int context; +#ifdef DEBUG + assert (state->halt); +#endif + context = re_string_context_at (mctx->input, idx, mctx->eflags, + preg->newline_anchor); + for (i = 0; i < state->nodes.nelem; ++i) + if (check_halt_node_context (dfa, state->nodes.elems[i], context)) + return state->nodes.elems[i]; + return 0; +} + +/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA + corresponding to the DFA). + Return the destination node, and update EPS_VIA_NODES, return -1 in case + of errors. */ + +static int +proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs) + const regex_t *preg; + regmatch_t *regs; + const re_match_context_t *mctx; + int nregs, *pidx, node; + re_node_set *eps_via_nodes; + struct re_fail_stack_t *fs; +{ + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + int i, err, dest_node; + dest_node = -1; + if (IS_EPSILON_NODE (dfa->nodes[node].type)) + { + re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; + int ndest, dest_nodes[2]; + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -1; + /* Pick up valid destinations. */ + for (ndest = 0, i = 0; i < dfa->edests[node].nelem; ++i) + { + int candidate = dfa->edests[node].elems[i]; + if (!re_node_set_contains (cur_nodes, candidate)) + continue; + dest_nodes[0] = (ndest == 0) ? candidate : dest_nodes[0]; + dest_nodes[1] = (ndest == 1) ? candidate : dest_nodes[1]; + ++ndest; + } + if (ndest <= 1) + return ndest == 0 ? -1 : (ndest == 1 ? dest_nodes[0] : 0); + /* In order to avoid infinite loop like "(a*)*". */ + if (re_node_set_contains (eps_via_nodes, dest_nodes[0])) + return dest_nodes[1]; + if (fs != NULL) + push_fail_stack (fs, *pidx, dest_nodes, nregs, regs, eps_via_nodes); + return dest_nodes[0]; + } + else + { + int naccepted = 0; + re_token_type_t type = dfa->nodes[node].type; + +#ifdef RE_ENABLE_I18N + if (ACCEPT_MB_NODE (type)) + naccepted = check_node_accept_bytes (preg, node, mctx->input, *pidx); + else +#endif /* RE_ENABLE_I18N */ + if (type == OP_BACK_REF) + { + int subexp_idx = dfa->nodes[node].opr.idx; + naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; + if (fs != NULL) + { + if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) + return -1; + else if (naccepted) + { + char *buf = (char *) re_string_get_buffer (mctx->input); + if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) != 0) + return -1; + } + } + + if (naccepted == 0) + { + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -2; + dest_node = dfa->edests[node].elems[0]; + if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node)) + return dest_node; + } + } + + if (naccepted != 0 + || check_node_accept (preg, dfa->nodes + node, mctx, *pidx)) + { + dest_node = dfa->nexts[node]; + *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; + if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL + || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node))) + return -1; + re_node_set_empty (eps_via_nodes); + return dest_node; + } + } + return -1; +} + +static reg_errcode_t +push_fail_stack (fs, str_idx, dests, nregs, regs, eps_via_nodes) + struct re_fail_stack_t *fs; + int str_idx, *dests, nregs; + regmatch_t *regs; + re_node_set *eps_via_nodes; +{ + reg_errcode_t err; + int num = fs->num++; + if (fs->num == fs->alloc) + { + struct re_fail_stack_ent_t *new_array; + fs->alloc *= 2; + new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) + * fs->alloc)); + if (new_array == NULL) + return REG_ESPACE; + fs->stack = new_array; + } + fs->stack[num].idx = str_idx; + fs->stack[num].node = dests[1]; + fs->stack[num].regs = re_malloc (regmatch_t, nregs); + memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); + err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); + return err; +} + +static int +pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes) + struct re_fail_stack_t *fs; + int *pidx, nregs; + regmatch_t *regs; + re_node_set *eps_via_nodes; +{ + int num = --fs->num; + assert (num >= 0); + *pidx = fs->stack[num].idx; + memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); + re_node_set_free (eps_via_nodes); + re_free (fs->stack[num].regs); + *eps_via_nodes = fs->stack[num].eps_via_nodes; + return fs->stack[num].node; +} + +/* Set the positions where the subexpressions are starts/ends to registers + PMATCH. + Note: We assume that pmatch[0] is already set, and + pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1). */ + +static reg_errcode_t +set_regs (preg, mctx, nmatch, pmatch, fl_backtrack) + const regex_t *preg; + const re_match_context_t *mctx; + size_t nmatch; + regmatch_t *pmatch; + int fl_backtrack; +{ + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + int idx, cur_node, real_nmatch; + re_node_set eps_via_nodes; + struct re_fail_stack_t *fs; + struct re_fail_stack_t fs_body = {0, 2, NULL}; +#ifdef DEBUG + assert (nmatch > 1); + assert (mctx->state_log != NULL); +#endif + if (fl_backtrack) + { + fs = &fs_body; + fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); + } + else + fs = NULL; + cur_node = dfa->init_node; + real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1; + re_node_set_init_empty (&eps_via_nodes); + for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) + { + update_regs (dfa, pmatch, cur_node, idx, real_nmatch); + if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) + { + int reg_idx; + if (fs) + { + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) + break; + if (reg_idx == nmatch) + { + re_node_set_free (&eps_via_nodes); + return free_fail_stack_return (fs); + } + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + } + else + { + re_node_set_free (&eps_via_nodes); + return REG_NOERROR; + } + } + + /* Proceed to next node. */ + cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node, + &eps_via_nodes, fs); + + if (BE (cur_node < 0, 0)) + { + if (cur_node == -2) + return REG_ESPACE; + if (fs) + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + else + { + re_node_set_free (&eps_via_nodes); + return REG_NOMATCH; + } + } + } + re_node_set_free (&eps_via_nodes); + return free_fail_stack_return (fs); +} + +static reg_errcode_t +free_fail_stack_return (fs) + struct re_fail_stack_t *fs; +{ + if (fs) + { + int fs_idx; + for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) + { + re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); + re_free (fs->stack[fs_idx].regs); + } + re_free (fs->stack); + } + return REG_NOERROR; +} + +static void +update_regs (dfa, pmatch, cur_node, cur_idx, nmatch) + re_dfa_t *dfa; + regmatch_t *pmatch; + int cur_node, cur_idx, nmatch; +{ + int type = dfa->nodes[cur_node].type; + int reg_num; + if (type != OP_OPEN_SUBEXP && type != OP_CLOSE_SUBEXP) + return; + reg_num = dfa->nodes[cur_node].opr.idx + 1; + if (reg_num >= nmatch) + return; + if (type == OP_OPEN_SUBEXP) + { + /* We are at the first node of this sub expression. */ + pmatch[reg_num].rm_so = cur_idx; + pmatch[reg_num].rm_eo = -1; + } + else if (type == OP_CLOSE_SUBEXP) + /* We are at the first node of this sub expression. */ + pmatch[reg_num].rm_eo = cur_idx; +} + +#define NUMBER_OF_STATE 1 + +/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 + and sift the nodes in each states according to the following rules. + Updated state_log will be wrote to STATE_LOG. + + Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... + 1. When STR_IDX == MATCH_LAST(the last index in the state_log): + If `a' isn't the LAST_NODE and `a' can't epsilon transit to + the LAST_NODE, we throw away the node `a'. + 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts + string `s' and transit to `b': + i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw + away the node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is + throwed away, we throw away the node `a'. + 3. When 0 <= STR_IDX < n and 'a' epsilon transit to 'b': + i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the + node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is throwed away, + we throw away the node `a'. */ + +#define STATE_NODE_CONTAINS(state,node) \ + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + +static reg_errcode_t +sift_states_backward (preg, mctx, sctx) + const regex_t *preg; + re_match_context_t *mctx; + re_sift_context_t *sctx; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + int null_cnt = 0; + int str_idx = sctx->last_str_idx; + re_node_set cur_dest; + re_node_set *cur_src; /* Points the state_log[str_idx]->nodes */ + +#ifdef DEBUG + assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); +#endif + cur_src = &mctx->state_log[str_idx]->nodes; + + /* Build sifted state_log[str_idx]. It has the nodes which can epsilon + transit to the last_node and the last_node itself. */ + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* Then check each states in the state_log. */ + while (str_idx > 0) + { + int i, ret; + /* Update counters. */ + null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; + if (null_cnt > mctx->max_mb_elem_len) + { + memset (sctx->sifted_states, '\0', + sizeof (re_dfastate_t *) * str_idx); + re_node_set_free (&cur_dest); + return REG_NOERROR; + } + re_node_set_empty (&cur_dest); + --str_idx; + cur_src = ((mctx->state_log[str_idx] == NULL) ? &empty_set + : &mctx->state_log[str_idx]->nodes); + + /* Then build the next sifted state. + We build the next sifted state on `cur_dest', and update + `sifted_states[str_idx]' with `cur_dest'. + Note: + `cur_dest' is the sifted state from `state_log[str_idx + 1]'. + `cur_src' points the node_set of the old `state_log[str_idx]'. */ + for (i = 0; i < cur_src->nelem; i++) + { + int prev_node = cur_src->elems[i]; + int naccepted = 0; + re_token_type_t type = dfa->nodes[prev_node].type; + + if (IS_EPSILON_NODE(type)) + continue; +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (ACCEPT_MB_NODE (type)) + naccepted = sift_states_iter_mb (preg, mctx, sctx, prev_node, + str_idx, sctx->last_str_idx); + +#endif /* RE_ENABLE_I18N */ + /* We don't check backreferences here. + See update_cur_sifted_state(). */ + + if (!naccepted + && check_node_accept (preg, dfa->nodes + prev_node, mctx, + str_idx) + && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], + dfa->nexts[prev_node])) + naccepted = 1; + + if (naccepted == 0) + continue; + + if (sctx->limits.nelem) + { + int to_idx = str_idx + naccepted; + if (check_dst_limits (dfa, &sctx->limits, mctx, + dfa->nexts[prev_node], to_idx, + prev_node, str_idx)) + continue; + } + ret = re_node_set_insert (&cur_dest, prev_node); + if (BE (ret == -1, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + + /* Add all the nodes which satisfy the following conditions: + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ + err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + err = REG_NOERROR; + free_return: + re_node_set_free (&cur_dest); + return err; +} + +/* Helper functions. */ + +static inline reg_errcode_t +clean_state_log_if_need (mctx, next_state_log_idx) + re_match_context_t *mctx; + int next_state_log_idx; +{ + int top = mctx->state_log_top; + + if (next_state_log_idx >= mctx->input->bufs_len + || (next_state_log_idx >= mctx->input->valid_len + && mctx->input->valid_len < mctx->input->len)) + { + reg_errcode_t err; + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (top < next_state_log_idx) + { + memset (mctx->state_log + top + 1, '\0', + sizeof (re_dfastate_t *) * (next_state_log_idx - top)); + mctx->state_log_top = next_state_log_idx; + } + return REG_NOERROR; +} + +static reg_errcode_t +merge_state_array (dfa, dst, src, num) + re_dfa_t *dfa; + re_dfastate_t **dst; + re_dfastate_t **src; + int num; +{ + int st_idx; + reg_errcode_t err; + for (st_idx = 0; st_idx < num; ++st_idx) + { + if (dst[st_idx] == NULL) + dst[st_idx] = src[st_idx]; + else if (src[st_idx] != NULL) + { + re_node_set merged_set; + err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, + &src[st_idx]->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); + re_node_set_free (&merged_set); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +update_cur_sifted_state (preg, mctx, sctx, str_idx, dest_nodes) + const regex_t *preg; + re_match_context_t *mctx; + re_sift_context_t *sctx; + int str_idx; + re_node_set *dest_nodes; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set + : &mctx->state_log[str_idx]->nodes); + + /* At first, add the nodes which can epsilon transit to a node in + DEST_NODE. */ + if (dest_nodes->nelem) + { + err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Then, check the limitations in the current sift_context. */ + if (dest_nodes->nelem && sctx->limits.nelem) + { + err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, + mctx->bkref_ents, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Update state_log. */ + sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); + if (BE (sctx->sifted_states[str_idx] == NULL && err != REG_NOERROR, 0)) + return err; + + if ((mctx->state_log[str_idx] != NULL + && mctx->state_log[str_idx]->has_backref)) + { + err = sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} + +static reg_errcode_t +add_epsilon_src_nodes (dfa, dest_nodes, candidates) + re_dfa_t *dfa; + re_node_set *dest_nodes; + const re_node_set *candidates; +{ + reg_errcode_t err; + int src_idx; + re_node_set src_copy; + + err = re_node_set_init_copy (&src_copy, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + for (src_idx = 0; src_idx < src_copy.nelem; ++src_idx) + { + err = re_node_set_add_intersect (dest_nodes, candidates, + dfa->inveclosures + + src_copy.elems[src_idx]); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&src_copy); + return err; + } + } + re_node_set_free (&src_copy); + return REG_NOERROR; +} + +static reg_errcode_t +sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates) + re_dfa_t *dfa; + int node; + re_node_set *dest_nodes; + const re_node_set *candidates; +{ + int ecl_idx; + reg_errcode_t err; + re_node_set *inv_eclosure = dfa->inveclosures + node; + re_node_set except_nodes; + re_node_set_init_empty (&except_nodes); + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (cur_node == node) + continue; + if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) + { + int edst1 = dfa->edests[cur_node].elems[0]; + int edst2 = ((dfa->edests[cur_node].nelem > 1) + ? dfa->edests[cur_node].elems[1] : -1); + if ((!re_node_set_contains (inv_eclosure, edst1) + && re_node_set_contains (dest_nodes, edst1)) + || (edst2 > 0 + && !re_node_set_contains (inv_eclosure, edst2) + && re_node_set_contains (dest_nodes, edst2))) + { + err = re_node_set_add_intersect (&except_nodes, candidates, + dfa->inveclosures + cur_node); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&except_nodes); + return err; + } + } + } + } + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (!re_node_set_contains (&except_nodes, cur_node)) + { + int idx = re_node_set_contains (dest_nodes, cur_node) - 1; + re_node_set_remove_at (dest_nodes, idx); + } + } + re_node_set_free (&except_nodes); + return REG_NOERROR; +} + +static int +check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx) + re_dfa_t *dfa; + re_node_set *limits; + re_match_context_t *mctx; + int dst_node, dst_idx, src_node, src_idx; +{ + int lim_idx, src_pos, dst_pos; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = mctx->bkref_ents + limits->elems[lim_idx]; + subexp_idx = dfa->nodes[ent->node].opr.idx - 1; + + dst_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx], + dfa->eclosures + dst_node, + subexp_idx, dst_node, dst_idx); + src_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx], + dfa->eclosures + src_node, + subexp_idx, src_node, src_idx); + + /* In case of: + ( ) + ( ) + ( ) */ + if (src_pos == dst_pos) + continue; /* This is unrelated limitation. */ + else + return 1; + } + return 0; +} + +static int +check_dst_limits_calc_pos (dfa, mctx, limit, eclosures, subexp_idx, node, + str_idx) + re_dfa_t *dfa; + re_match_context_t *mctx; + re_node_set *eclosures; + int limit, subexp_idx, node, str_idx; +{ + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int pos = (str_idx < lim->subexp_from ? -1 + : (lim->subexp_to < str_idx ? 1 : 0)); + if (pos == 0 + && (str_idx == lim->subexp_from || str_idx == lim->subexp_to)) + { + int node_idx; + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) + { + int node = eclosures->elems[node_idx]; + re_token_type_t type= dfa->nodes[node].type; + if (type == OP_BACK_REF) + { + int bi = search_cur_bkref_entry (mctx, str_idx); + for (; bi < mctx->nbkref_ents; ++bi) + { + struct re_backref_cache_entry *ent = mctx->bkref_ents + bi; + if (ent->str_idx > str_idx) + break; + if (ent->node == node && ent->subexp_from == ent->subexp_to) + { + int cpos, dst; + dst = dfa->edests[node].elems[0]; + cpos = check_dst_limits_calc_pos (dfa, mctx, limit, + dfa->eclosures + dst, + subexp_idx, dst, + str_idx); + if ((str_idx == lim->subexp_from && cpos == -1) + || (str_idx == lim->subexp_to && cpos == 0)) + return cpos; + } + } + } + if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx + && str_idx == lim->subexp_from) + { + pos = -1; + break; + } + if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx + && str_idx == lim->subexp_to) + break; + } + if (node_idx == eclosures->nelem && str_idx == lim->subexp_to) + pos = 1; + } + return pos; +} + +/* Check the limitations of sub expressions LIMITS, and remove the nodes + which are against limitations from DEST_NODES. */ + +static reg_errcode_t +check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx) + re_dfa_t *dfa; + re_node_set *dest_nodes; + const re_node_set *candidates; + re_node_set *limits; + struct re_backref_cache_entry *bkref_ents; + int str_idx; +{ + reg_errcode_t err; + int node_idx, lim_idx; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = bkref_ents + limits->elems[lim_idx]; + + if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) + continue; /* This is unrelated limitation. */ + + subexp_idx = dfa->nodes[ent->node].opr.idx - 1; + if (ent->subexp_to == str_idx) + { + int ops_node = -1; + int cls_node = -1; + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type= dfa->nodes[node].type; + if (type == OP_OPEN_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + ops_node = node; + else if (type == OP_CLOSE_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + cls_node = node; + } + + /* Check the limitation of the open subexpression. */ + /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ + if (ops_node >= 0) + { + err = sub_epsilon_src_nodes(dfa, ops_node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + /* Check the limitation of the close subexpression. */ + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + if (!re_node_set_contains (dfa->inveclosures + node, cls_node) + && !re_node_set_contains (dfa->eclosures + node, cls_node)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes(dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + --node_idx; + } + } + } + else /* (ent->subexp_to != str_idx) */ + { + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type= dfa->nodes[node].type; + if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) + { + if (subexp_idx != dfa->nodes[node].opr.idx) + continue; + if ((type == OP_CLOSE_SUBEXP && ent->subexp_to != str_idx) + || (type == OP_OPEN_SUBEXP)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes(dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + } + } + return REG_NOERROR; +} + +static reg_errcode_t +sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes) + const regex_t *preg; + re_match_context_t *mctx; + re_sift_context_t *sctx; + int str_idx; + re_node_set *dest_nodes; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + int node_idx, node; + re_sift_context_t local_sctx; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set + : &mctx->state_log[str_idx]->nodes); + local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ + + for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) + { + int cur_bkref_idx = re_string_cur_idx (mctx->input); + re_token_type_t type; + node = candidates->elems[node_idx]; + type = dfa->nodes[node].type; + if (node == sctx->cur_bkref && str_idx == cur_bkref_idx) + continue; + /* Avoid infinite loop for the REs like "()\1+". */ + if (node == sctx->last_node && str_idx == sctx->last_str_idx) + continue; + if (type == OP_BACK_REF) + { + int enabled_idx = search_cur_bkref_entry (mctx, str_idx); + for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx) + { + int disabled_idx, subexp_len, to_idx, dst_node; + struct re_backref_cache_entry *entry; + entry = mctx->bkref_ents + enabled_idx; + if (entry->str_idx > str_idx) + break; + if (entry->node != node) + continue; + subexp_len = entry->subexp_to - entry->subexp_from; + to_idx = str_idx + subexp_len; + dst_node = (subexp_len ? dfa->nexts[node] + : dfa->edests[node].elems[0]); + + if (to_idx > sctx->last_str_idx + || sctx->sifted_states[to_idx] == NULL + || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], + dst_node) + || check_dst_limits (dfa, &sctx->limits, mctx, node, + str_idx, dst_node, to_idx)) + continue; + { + re_dfastate_t *cur_state; + entry->flag = 0; + for (disabled_idx = enabled_idx + 1; + disabled_idx < mctx->nbkref_ents; ++disabled_idx) + { + struct re_backref_cache_entry *entry2; + entry2 = mctx->bkref_ents + disabled_idx; + if (entry2->str_idx > str_idx) + break; + entry2->flag = (entry2->node == node) ? 1 : entry2->flag; + } + + if (local_sctx.sifted_states == NULL) + { + local_sctx = *sctx; + err = re_node_set_init_copy (&local_sctx.limits, + &sctx->limits); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.last_node = node; + local_sctx.last_str_idx = str_idx; + err = re_node_set_insert (&local_sctx.limits, enabled_idx); + if (BE (err < 0, 0)) + { + err = REG_ESPACE; + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; + err = sift_states_backward (preg, mctx, &local_sctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + if (sctx->limited_states != NULL) + { + err = merge_state_array (dfa, sctx->limited_states, + local_sctx.sifted_states, + str_idx + 1); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.sifted_states[str_idx] = cur_state; + re_node_set_remove (&local_sctx.limits, enabled_idx); + /* We must not use the variable entry here, since + mctx->bkref_ents might be realloced. */ + mctx->bkref_ents[enabled_idx].flag = 1; + } + } + enabled_idx = search_cur_bkref_entry (mctx, str_idx); + for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx) + { + struct re_backref_cache_entry *entry; + entry = mctx->bkref_ents + enabled_idx; + if (entry->str_idx > str_idx) + break; + if (entry->node == node) + entry->flag = 0; + } + } + } + err = REG_NOERROR; + free_return: + if (local_sctx.sifted_states != NULL) + { + re_node_set_free (&local_sctx.limits); + } + + return err; +} + + +#ifdef RE_ENABLE_I18N +static int +sift_states_iter_mb (preg, mctx, sctx, node_idx, str_idx, max_str_idx) + const regex_t *preg; + const re_match_context_t *mctx; + re_sift_context_t *sctx; + int node_idx, str_idx, max_str_idx; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int naccepted; + /* Check the node can accept `multi byte'. */ + naccepted = check_node_accept_bytes (preg, node_idx, mctx->input, str_idx); + if (naccepted > 0 && str_idx + naccepted <= max_str_idx && + !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], + dfa->nexts[node_idx])) + /* The node can't accept the `multi byte', or the + destination was already throwed away, then the node + could't accept the current input `multi byte'. */ + naccepted = 0; + /* Otherwise, it is sure that the node could accept + `naccepted' bytes input. */ + return naccepted; +} +#endif /* RE_ENABLE_I18N */ + + +/* Functions for state transition. */ + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte, and update STATE_LOG if necessary. + If STATE can accept a multibyte char/collating element/back reference + update the destination of STATE_LOG. */ + +static re_dfastate_t * +transit_state (err, preg, mctx, state, fl_search) + reg_errcode_t *err; + const regex_t *preg; + re_match_context_t *mctx; + re_dfastate_t *state; + int fl_search; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + re_dfastate_t **trtable, *next_state; + unsigned char ch; + int cur_idx; + + if (re_string_cur_idx (mctx->input) + 1 >= mctx->input->bufs_len + || (re_string_cur_idx (mctx->input) + 1 >= mctx->input->valid_len + && mctx->input->valid_len < mctx->input->len)) + { + *err = extend_buffers (mctx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + + *err = REG_NOERROR; + if (state == NULL) + { + next_state = state; + re_string_skip_bytes (mctx->input, 1); + } + else + { +#ifdef RE_ENABLE_I18N + /* If the current state can accept multibyte. */ + if (state->accept_mb) + { + *err = transit_state_mb (preg, state, mctx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } +#endif /* RE_ENABLE_I18N */ + + /* Then decide the next state with the single byte. */ + if (1) + { + /* Use transition table */ + ch = re_string_fetch_byte (mctx->input); + trtable = fl_search ? state->trtable_search : state->trtable; + if (trtable == NULL) + { + trtable = build_trtable (preg, state, fl_search); + if (fl_search) + state->trtable_search = trtable; + else + state->trtable = trtable; + } + next_state = trtable[ch]; + } + else + { + /* don't use transition table */ + next_state = transit_state_sb (err, preg, state, fl_search, mctx); + if (BE (next_state == NULL && err != REG_NOERROR, 0)) + return NULL; + } + } + + cur_idx = re_string_cur_idx (mctx->input); + /* Update the state_log if we need. */ + if (mctx->state_log != NULL) + { + if (cur_idx > mctx->state_log_top) + { + mctx->state_log[cur_idx] = next_state; + mctx->state_log_top = cur_idx; + } + else if (mctx->state_log[cur_idx] == 0) + { + mctx->state_log[cur_idx] = next_state; + } + else + { + re_dfastate_t *pstate; + unsigned int context; + re_node_set next_nodes, *log_nodes, *table_nodes = NULL; + /* If (state_log[cur_idx] != 0), it implies that cur_idx is + the destination of a multibyte char/collating element/ + back reference. Then the next state is the union set of + these destinations and the results of the transition table. */ + pstate = mctx->state_log[cur_idx]; + log_nodes = pstate->entrance_nodes; + if (next_state != NULL) + { + table_nodes = next_state->entrance_nodes; + *err = re_node_set_init_union (&next_nodes, table_nodes, + log_nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + else + next_nodes = *log_nodes; + /* Note: We already add the nodes of the initial state, + then we don't need to add them here. */ + + context = re_string_context_at (mctx->input, + re_string_cur_idx (mctx->input) - 1, + mctx->eflags, preg->newline_anchor); + next_state = mctx->state_log[cur_idx] + = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + if (table_nodes != NULL) + re_node_set_free (&next_nodes); + } + } + + /* Check OP_OPEN_SUBEXP in the current state in case that we use them + later. We must check them here, since the back references in the + next state might use them. */ + if (dfa->nbackref && next_state/* && fl_process_bkref */) + { + *err = check_subexp_matching_top (dfa, mctx, &next_state->nodes, + cur_idx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + + /* If the next state has back references. */ + if (next_state != NULL && next_state->has_backref) + { + *err = transit_state_bkref (preg, &next_state->nodes, mctx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + next_state = mctx->state_log[cur_idx]; + } + return next_state; +} + +/* Helper functions for transit_state. */ + +/* From the node set CUR_NODES, pick up the nodes whose types are + OP_OPEN_SUBEXP and which have corresponding back references in the regular + expression. And register them to use them later for evaluating the + correspoding back references. */ + +static reg_errcode_t +check_subexp_matching_top (dfa, mctx, cur_nodes, str_idx) + re_dfa_t *dfa; + re_match_context_t *mctx; + re_node_set *cur_nodes; + int str_idx; +{ + int node_idx; + reg_errcode_t err; + + /* TODO: This isn't efficient. + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) + { + int node = cur_nodes->elems[node_idx]; + if (dfa->nodes[node].type == OP_OPEN_SUBEXP + && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx)) + { + err = match_ctx_add_subtop (mctx, node, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte. */ + +static re_dfastate_t * +transit_state_sb (err, preg, state, fl_search, mctx) + reg_errcode_t *err; + const regex_t *preg; + re_dfastate_t *state; + int fl_search; + re_match_context_t *mctx; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + re_node_set next_nodes; + re_dfastate_t *next_state; + int node_cnt, cur_str_idx = re_string_cur_idx (mctx->input); + unsigned int context; + + *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) + { + int cur_node = state->nodes.elems[node_cnt]; + if (check_node_accept (preg, dfa->nodes + cur_node, mctx, cur_str_idx)) + { + *err = re_node_set_merge (&next_nodes, + dfa->eclosures + dfa->nexts[cur_node]); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + if (fl_search) + { +#ifdef RE_ENABLE_I18N + int not_initial = 0; + if (MB_CUR_MAX > 1) + for (node_cnt = 0; node_cnt < next_nodes.nelem; ++node_cnt) + if (dfa->nodes[next_nodes.elems[node_cnt]].type == CHARACTER) + { + not_initial = dfa->nodes[next_nodes.elems[node_cnt]].mb_partial; + break; + } + if (!not_initial) +#endif + { + *err = re_node_set_merge (&next_nodes, + dfa->init_state->entrance_nodes); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + context = re_string_context_at (mctx->input, cur_str_idx, mctx->eflags, + preg->newline_anchor); + next_state = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + re_node_set_free (&next_nodes); + re_string_skip_bytes (mctx->input, 1); + return next_state; +} + +#ifdef RE_ENABLE_I18N +static reg_errcode_t +transit_state_mb (preg, pstate, mctx) + const regex_t *preg; + re_dfastate_t *pstate; + re_match_context_t *mctx; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int i; + + for (i = 0; i < pstate->nodes.nelem; ++i) + { + re_node_set dest_nodes, *new_nodes; + int cur_node_idx = pstate->nodes.elems[i]; + int naccepted = 0, dest_idx; + unsigned int context; + re_dfastate_t *dest_state; + + if (dfa->nodes[cur_node_idx].constraint) + { + context = re_string_context_at (mctx->input, + re_string_cur_idx (mctx->input), + mctx->eflags, preg->newline_anchor); + if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, + context)) + continue; + } + + /* How many bytes the node can accepts? */ + if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type)) + naccepted = check_node_accept_bytes (preg, cur_node_idx, mctx->input, + re_string_cur_idx (mctx->input)); + if (naccepted == 0) + continue; + + /* The node can accepts `naccepted' bytes. */ + dest_idx = re_string_cur_idx (mctx->input) + naccepted; + mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted + : mctx->max_mb_elem_len); + err = clean_state_log_if_need (mctx, dest_idx); + if (BE (err != REG_NOERROR, 0)) + return err; +#ifdef DEBUG + assert (dfa->nexts[cur_node_idx] != -1); +#endif + /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE, + then we use pstate->nodes.elems[i] instead. */ + new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]]; + + dest_state = mctx->state_log[dest_idx]; + if (dest_state == NULL) + dest_nodes = *new_nodes; + else + { + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, new_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + context = re_string_context_at (mctx->input, dest_idx - 1, mctx->eflags, + preg->newline_anchor); + mctx->state_log[dest_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + if (dest_state != NULL) + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} +#endif /* RE_ENABLE_I18N */ + +static reg_errcode_t +transit_state_bkref (preg, nodes, mctx) + const regex_t *preg; + re_node_set *nodes; + re_match_context_t *mctx; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int i; + int cur_str_idx = re_string_cur_idx (mctx->input); + + for (i = 0; i < nodes->nelem; ++i) + { + int dest_str_idx, prev_nelem, bkc_idx; + int node_idx = nodes->elems[i]; + unsigned int context; + re_token_t *node = dfa->nodes + node_idx; + re_node_set *new_dest_nodes; + + /* Check whether `node' is a backreference or not. */ + if (node->type != OP_BACK_REF) + continue; + + if (node->constraint) + { + context = re_string_context_at (mctx->input, cur_str_idx, + mctx->eflags, preg->newline_anchor); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + continue; + } + + /* `node' is a backreference. + Check the substring which the substring matched. */ + bkc_idx = mctx->nbkref_ents; + err = get_subexp (preg, mctx, node_idx, cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* And add the epsilon closures (which is `new_dest_nodes') of + the backreference to appropriate state_log. */ +#ifdef DEBUG + assert (dfa->nexts[node_idx] != -1); +#endif + for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) + { + int subexp_len; + re_dfastate_t *dest_state; + struct re_backref_cache_entry *bkref_ent; + bkref_ent = mctx->bkref_ents + bkc_idx; + if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) + continue; + subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; + new_dest_nodes = (subexp_len == 0 + ? dfa->eclosures + dfa->edests[node_idx].elems[0] + : dfa->eclosures + dfa->nexts[node_idx]); + dest_str_idx = (cur_str_idx + bkref_ent->subexp_to + - bkref_ent->subexp_from); + context = re_string_context_at (mctx->input, dest_str_idx - 1, + mctx->eflags, preg->newline_anchor); + dest_state = mctx->state_log[dest_str_idx]; + prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 + : mctx->state_log[cur_str_idx]->nodes.nelem); + /* Add `new_dest_node' to state_log. */ + if (dest_state == NULL) + { + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, new_dest_nodes, + context); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + else + { + re_node_set dest_nodes; + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, + new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&dest_nodes); + goto free_return; + } + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + /* We need to check recursively if the backreference can epsilon + transit. */ + if (subexp_len == 0 + && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) + { + err = check_subexp_matching_top (dfa, mctx, new_dest_nodes, + cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + err = transit_state_bkref (preg, new_dest_nodes, mctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + } + } + err = REG_NOERROR; + free_return: + return err; +} + +/* Enumerate all the candidates which the backreference BKREF_NODE can match + at BKREF_STR_IDX, and register them by match_ctx_add_entry(). + Note that we might collect inappropriate candidates here. + However, the cost of checking them strictly here is too high, then we + delay these checking for prune_impossible_nodes(). */ + +static reg_errcode_t +get_subexp (preg, mctx, bkref_node, bkref_str_idx) + const regex_t *preg; + re_match_context_t *mctx; + int bkref_node, bkref_str_idx; +{ + int subexp_num, sub_top_idx; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + char *buf = (char *) re_string_get_buffer (mctx->input); + /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ + int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); + for (; cache_idx < mctx->nbkref_ents; ++cache_idx) + { + struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx; + if (entry->str_idx > bkref_str_idx) + break; + if (entry->node == bkref_node) + return REG_NOERROR; /* We already checked it. */ + } + subexp_num = dfa->nodes[bkref_node].opr.idx - 1; + + /* For each sub expression */ + for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) + { + reg_errcode_t err; + re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; + re_sub_match_last_t *sub_last; + int sub_last_idx, sl_str; + char *bkref_str; + + if (dfa->nodes[sub_top->node].opr.idx != subexp_num) + continue; /* It isn't related. */ + + sl_str = sub_top->str_idx; + bkref_str = buf + bkref_str_idx; + /* At first, check the last node of sub expressions we already + evaluated. */ + for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) + { + int sl_str_diff; + sub_last = sub_top->lasts[sub_last_idx]; + sl_str_diff = sub_last->str_idx - sl_str; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_diff > 0 + && memcmp (bkref_str, buf + sl_str, sl_str_diff) != 0) + break; /* We don't need to search this sub expression any more. */ + bkref_str += sl_str_diff; + sl_str += sl_str_diff; + err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + } + if (sub_last_idx < sub_top->nlasts) + continue; + if (sub_last_idx > 0) + ++sl_str; + /* Then, search for the other last nodes of the sub expression. */ + for (; sl_str <= bkref_str_idx; ++sl_str) + { + int cls_node, sl_str_off; + re_node_set *nodes; + sl_str_off = sl_str - sub_top->str_idx; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_off > 0 + && memcmp (bkref_str++, buf + sl_str - 1, 1) != 0) + break; /* We don't need to search this sub expression any more. */ + if (mctx->state_log[sl_str] == NULL) + continue; + /* Does this state have a ')' of the sub expression? */ + nodes = &mctx->state_log[sl_str]->nodes; + cls_node = find_subexp_node (dfa, nodes, subexp_num, 0); + if (cls_node == -1) + continue; /* No. */ + if (sub_top->path == NULL) + { + sub_top->path = calloc (sizeof (state_array_t), + sl_str - sub_top->str_idx + 1); + if (sub_top->path == NULL) + return REG_ESPACE; + } + /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node + in the current context? */ + err = check_arrival (preg, mctx, sub_top->path, sub_top->node, + sub_top->str_idx, cls_node, sl_str, 0); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); + if (BE (sub_last == NULL, 0)) + return REG_ESPACE; + err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + } + } + return REG_NOERROR; +} + +/* Helper functions for get_subexp(). */ + +/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. + If it can arrive, register the sub expression expressed with SUB_TOP + and SUB_LAST. */ + +static reg_errcode_t +get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, bkref_str) + const regex_t *preg; + re_match_context_t *mctx; + re_sub_match_top_t *sub_top; + re_sub_match_last_t *sub_last; + int bkref_node, bkref_str; +{ + reg_errcode_t err; + int to_idx; + /* Can the subexpression arrive the back reference? */ + err = check_arrival (preg, mctx, &sub_last->path, sub_last->node, + sub_last->str_idx, bkref_node, bkref_str, 1); + if (err != REG_NOERROR) + return err; + err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, + sub_last->str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; + clean_state_log_if_need (mctx, to_idx); + return REG_NOERROR; +} + +/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. + Search '(' if FL_OPEN, or search ')' otherwise. + TODO: This function isn't efficient... + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + +static int +find_subexp_node (dfa, nodes, subexp_idx, fl_open) + re_dfa_t *dfa; + re_node_set *nodes; + int subexp_idx, fl_open; +{ + int cls_idx; + for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) + { + int cls_node = nodes->elems[cls_idx]; + re_token_t *node = dfa->nodes + cls_node; + if (((fl_open && node->type == OP_OPEN_SUBEXP) + || (!fl_open && node->type == OP_CLOSE_SUBEXP)) + && node->opr.idx == subexp_idx) + return cls_node; + } + return -1; +} + +/* Check whether the node TOP_NODE at TOP_STR can arrive to the node + LAST_NODE at LAST_STR. We record the path onto PATH since it will be + heavily reused. + Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ + +static reg_errcode_t +check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str, + fl_open) + const regex_t *preg; + re_match_context_t *mctx; + state_array_t *path; + int top_node, top_str, last_node, last_str, fl_open; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t err; + int subexp_num, backup_cur_idx, str_idx, null_cnt; + re_dfastate_t *cur_state = NULL; + re_node_set *cur_nodes, next_nodes; + re_dfastate_t **backup_state_log; + unsigned int context; + + subexp_num = dfa->nodes[top_node].opr.idx; + /* Extend the buffer if we need. */ + if (path->alloc < last_str + mctx->max_mb_elem_len + 1) + { + re_dfastate_t **new_array; + int old_alloc = path->alloc; + path->alloc += last_str + mctx->max_mb_elem_len + 1; + new_array = re_realloc (path->array, re_dfastate_t *, path->alloc); + if (new_array == NULL) + return REG_ESPACE; + path->array = new_array; + memset (new_array + old_alloc, '\0', + sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); + } + + str_idx = path->next_idx == 0 ? top_str : path->next_idx; + + /* Temporary modify MCTX. */ + backup_state_log = mctx->state_log; + backup_cur_idx = mctx->input->cur_idx; + mctx->state_log = path->array; + mctx->input->cur_idx = str_idx; + + /* Setup initial node set. */ + context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags, + preg->newline_anchor); + if (str_idx == top_str) + { + err = re_node_set_init_1 (&next_nodes, top_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, fl_open); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + else + { + cur_state = mctx->state_log[str_idx]; + if (cur_state && cur_state->has_backref) + { + err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); + if (BE ( err != REG_NOERROR, 0)) + return err; + } + else + re_node_set_init_empty (&next_nodes); + } + if (str_idx == top_str || (cur_state && cur_state->has_backref)) + { + if (next_nodes.nelem) + { + err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str, + subexp_num, fl_open); + if (BE ( err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + } + + for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) + { + re_node_set_empty (&next_nodes); + if (mctx->state_log[str_idx + 1]) + { + err = re_node_set_merge (&next_nodes, + &mctx->state_log[str_idx + 1]->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + if (cur_state) + { + err = check_arrival_add_next_nodes(preg, dfa, mctx, str_idx, + &cur_state->nodes, &next_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + ++str_idx; + if (next_nodes.nelem) + { + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, + fl_open); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str, + subexp_num, fl_open); + if (BE ( err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags, + preg->newline_anchor); + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + null_cnt = cur_state == NULL ? null_cnt + 1 : 0; + } + re_node_set_free (&next_nodes); + cur_nodes = (mctx->state_log[last_str] == NULL ? NULL + : &mctx->state_log[last_str]->nodes); + path->next_idx = str_idx; + + /* Fix MCTX. */ + mctx->state_log = backup_state_log; + mctx->input->cur_idx = backup_cur_idx; + + if (cur_nodes == NULL) + return REG_NOMATCH; + /* Then check the current node set has the node LAST_NODE. */ + return (re_node_set_contains (cur_nodes, last_node) + || re_node_set_contains (cur_nodes, last_node) ? REG_NOERROR + : REG_NOMATCH); +} + +/* Helper functions for check_arrival. */ + +/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them + to NEXT_NODES. + TODO: This function is similar to the functions transit_state*(), + however this function has many additional works. + Can't we unify them? */ + +static reg_errcode_t +check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes) + const regex_t *preg; + re_dfa_t *dfa; + re_match_context_t *mctx; + int str_idx; + re_node_set *cur_nodes, *next_nodes; +{ + int cur_idx; + reg_errcode_t err; + re_node_set union_set; + re_node_set_init_empty (&union_set); + for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) + { + int naccepted = 0; + int cur_node = cur_nodes->elems[cur_idx]; + re_token_type_t type = dfa->nodes[cur_node].type; + if (IS_EPSILON_NODE(type)) + continue; +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (ACCEPT_MB_NODE (type)) + { + naccepted = check_node_accept_bytes (preg, cur_node, mctx->input, + str_idx); + if (naccepted > 1) + { + re_dfastate_t *dest_state; + int next_node = dfa->nexts[cur_node]; + int next_idx = str_idx + naccepted; + dest_state = mctx->state_log[next_idx]; + re_node_set_empty (&union_set); + if (dest_state) + { + err = re_node_set_merge (&union_set, &dest_state->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + err = re_node_set_insert (&union_set, next_node); + if (BE (err < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + else + { + err = re_node_set_insert (&union_set, next_node); + if (BE (err < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + mctx->state_log[next_idx] = re_acquire_state (&err, dfa, + &union_set); + if (BE (mctx->state_log[next_idx] == NULL + && err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + } +#endif /* RE_ENABLE_I18N */ + if (naccepted + || check_node_accept (preg, dfa->nodes + cur_node, mctx, + str_idx)) + { + err = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); + if (BE (err < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + } + re_node_set_free (&union_set); + return REG_NOERROR; +} + +/* For all the nodes in CUR_NODES, add the epsilon closures of them to + CUR_NODES, however exclude the nodes which are: + - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. + - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. +*/ + +static reg_errcode_t +check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open) + re_dfa_t *dfa; + re_node_set *cur_nodes; + int ex_subexp, fl_open; +{ + reg_errcode_t err; + int idx, outside_node; + re_node_set new_nodes; +#ifdef DEBUG + assert (cur_nodes->nelem); +#endif + err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* Create a new node set NEW_NODES with the nodes which are epsilon + closures of the node in CUR_NODES. */ + + for (idx = 0; idx < cur_nodes->nelem; ++idx) + { + int cur_node = cur_nodes->elems[idx]; + re_node_set *eclosure = dfa->eclosures + cur_node; + outside_node = find_subexp_node (dfa, eclosure, ex_subexp, fl_open); + if (outside_node == -1) + { + /* There are no problematic nodes, just merge them. */ + err = re_node_set_merge (&new_nodes, eclosure); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + else + { + /* There are problematic nodes, re-calculate incrementally. */ + err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, + ex_subexp, fl_open); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + } + re_node_set_free (cur_nodes); + *cur_nodes = new_nodes; + return REG_NOERROR; +} + +/* Helper function for check_arrival_expand_ecl. + Check incrementally the epsilon closure of TARGET, and if it isn't + problematic append it to DST_NODES. */ + +static reg_errcode_t +check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, fl_open) + re_dfa_t *dfa; + int target, ex_subexp, fl_open; + re_node_set *dst_nodes; +{ + int cur_node, type; + for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) + { + int err; + type = dfa->nodes[cur_node].type; + + if (((type == OP_OPEN_SUBEXP && fl_open) + || (type == OP_CLOSE_SUBEXP && !fl_open)) + && dfa->nodes[cur_node].opr.idx == ex_subexp) + { + if (!fl_open) + { + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + } + break; + } + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + if (dfa->edests[cur_node].nelem == 0) + break; + if (dfa->edests[cur_node].nelem == 2) + { + err = check_arrival_expand_ecl_sub (dfa, dst_nodes, + dfa->edests[cur_node].elems[1], + ex_subexp, fl_open); + if (BE (err != REG_NOERROR, 0)) + return err; + } + cur_node = dfa->edests[cur_node].elems[0]; + } + return REG_NOERROR; +} + + +/* For all the back references in the current state, calculate the + destination of the back references by the appropriate entry + in MCTX->BKREF_ENTS. */ + +static reg_errcode_t +expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num, + fl_open) + const regex_t *preg; + re_match_context_t *mctx; + int cur_str, last_str, subexp_num, fl_open; + re_node_set *cur_nodes; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int cache_idx, cache_idx_start; + /* The current state. */ + + cache_idx_start = search_cur_bkref_entry (mctx, cur_str); + for (cache_idx = cache_idx_start; cache_idx < mctx->nbkref_ents; ++cache_idx) + { + int to_idx, next_node; + struct re_backref_cache_entry *ent = mctx->bkref_ents + cache_idx; + if (ent->str_idx > cur_str) + break; + /* Is this entry ENT is appropriate? */ + if (!re_node_set_contains (cur_nodes, ent->node)) + continue; /* No. */ + + to_idx = cur_str + ent->subexp_to - ent->subexp_from; + /* Calculate the destination of the back reference, and append it + to MCTX->STATE_LOG. */ + if (to_idx == cur_str) + { + /* The backreference did epsilon transit, we must re-check all the + node in the current state. */ + re_node_set new_dests; + reg_errcode_t err2, err3; + next_node = dfa->edests[ent->node].elems[0]; + if (re_node_set_contains (cur_nodes, next_node)) + continue; + err = re_node_set_init_1 (&new_dests, next_node); + err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, + fl_open); + err3 = re_node_set_merge (cur_nodes, &new_dests); + re_node_set_free (&new_dests); + if (BE (err != REG_NOERROR || err2 != REG_NOERROR + || err3 != REG_NOERROR, 0)) + { + err = (err != REG_NOERROR ? err + : (err2 != REG_NOERROR ? err2 : err3)); + return err; + } + /* TODO: It is still inefficient... */ + cache_idx = cache_idx_start - 1; + continue; + } + else + { + re_node_set union_set; + next_node = dfa->nexts[ent->node]; + if (mctx->state_log[to_idx]) + { + int ret; + if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, + next_node)) + continue; + err = re_node_set_init_copy (&union_set, + &mctx->state_log[to_idx]->nodes); + ret = re_node_set_insert (&union_set, next_node); + if (BE (err != REG_NOERROR || ret < 0, 0)) + { + re_node_set_free (&union_set); + err = err != REG_NOERROR ? err : REG_ESPACE; + return err; + } + } + else + { + err = re_node_set_init_1 (&union_set, next_node); + if (BE (err != REG_NOERROR, 0)) + return err; + } + mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); + re_node_set_free (&union_set); + if (BE (mctx->state_log[to_idx] == NULL + && err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +/* Build transition table for the state. + Return the new table if succeeded, otherwise return NULL. */ + +static re_dfastate_t ** +build_trtable (preg, state, fl_search) + const regex_t *preg; + const re_dfastate_t *state; + int fl_search; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int i, j, k, ch; + int dests_node_malloced = 0, dest_states_malloced = 0; + int ndests; /* Number of the destination states from `state'. */ + re_dfastate_t **trtable; + re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; + re_node_set follows, *dests_node; + bitset *dests_ch; + bitset acceptable; + + /* We build DFA states which corresponds to the destination nodes + from `state'. `dests_node[i]' represents the nodes which i-th + destination state contains, and `dests_ch[i]' represents the + characters which i-th destination state accepts. */ +#ifdef _LIBC + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX)) + dests_node = (re_node_set *) + alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX); + else +#endif + { + dests_node = (re_node_set *) + malloc ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX); + if (BE (dests_node == NULL, 0)) + return NULL; + dests_node_malloced = 1; + } + dests_ch = (bitset *) (dests_node + SBC_MAX); + + /* Initialize transiton table. */ + trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); + if (BE (trtable == NULL, 0)) + { + if (dests_node_malloced) + free (dests_node); + return NULL; + } + + /* At first, group all nodes belonging to `state' into several + destinations. */ + ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch); + if (BE (ndests <= 0, 0)) + { + if (dests_node_malloced) + free (dests_node); + /* Return NULL in case of an error, trtable otherwise. */ + if (ndests == 0) + return trtable; + free (trtable); + return NULL; + } + + err = re_node_set_alloc (&follows, ndests + 1); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + +#ifdef _LIBC + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX + + ndests * 3 * sizeof (re_dfastate_t *))) + dest_states = (re_dfastate_t **) + alloca (ndests * 3 * sizeof (re_dfastate_t *)); + else +#endif + { + dest_states = (re_dfastate_t **) + malloc (ndests * 3 * sizeof (re_dfastate_t *)); + if (BE (dest_states == NULL, 0)) + { +out_free: + if (dest_states_malloced) + free (dest_states); + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + free (trtable); + if (dests_node_malloced) + free (dests_node); + return NULL; + } + dest_states_malloced = 1; + } + dest_states_word = dest_states + ndests; + dest_states_nl = dest_states_word + ndests; + bitset_empty (acceptable); + + /* Then build the states for all destinations. */ + for (i = 0; i < ndests; ++i) + { + int next_node; + re_node_set_empty (&follows); + /* Merge the follows of this destination states. */ + for (j = 0; j < dests_node[i].nelem; ++j) + { + next_node = dfa->nexts[dests_node[i].elems[j]]; + if (next_node != -1) + { + err = re_node_set_merge (&follows, dfa->eclosures + next_node); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + /* If search flag is set, merge the initial state. */ + if (fl_search) + { +#ifdef RE_ENABLE_I18N + int not_initial = 0; + for (j = 0; j < follows.nelem; ++j) + if (dfa->nodes[follows.elems[j]].type == CHARACTER) + { + not_initial = dfa->nodes[follows.elems[j]].mb_partial; + break; + } + if (!not_initial) +#endif + { + err = re_node_set_merge (&follows, + dfa->init_state->entrance_nodes); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); + if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + /* If the new state has context constraint, + build appropriate states for these contexts. */ + if (dest_states[i]->has_constraint) + { + dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_WORD); + if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_NEWLINE); + if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + } + else + { + dest_states_word[i] = dest_states[i]; + dest_states_nl[i] = dest_states[i]; + } + bitset_merge (acceptable, dests_ch[i]); + } + + /* Update the transition table. */ + /* For all characters ch...: */ + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if ((acceptable[i] >> j) & 1) + { + /* The current state accepts the character ch. */ + if (IS_WORD_CHAR (ch)) + { + for (k = 0; k < ndests; ++k) + if ((dests_ch[k][i] >> j) & 1) + { + /* k-th destination accepts the word character ch. */ + trtable[ch] = dest_states_word[k]; + /* There must be only one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + break; + } + } + else /* not WORD_CHAR */ + { + for (k = 0; k < ndests; ++k) + if ((dests_ch[k][i] >> j) & 1) + { + /* k-th destination accepts the non-word character ch. */ + trtable[ch] = dest_states[k]; + /* There must be only one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + break; + } + } + } + /* new line */ + if (bitset_contain (acceptable, NEWLINE_CHAR)) + { + /* The current state accepts newline character. */ + for (k = 0; k < ndests; ++k) + if (bitset_contain (dests_ch[k], NEWLINE_CHAR)) + { + /* k-th destination accepts newline character. */ + trtable[NEWLINE_CHAR] = dest_states_nl[k]; + /* There must be only one destination which accepts + newline. See group_nodes_into_DFAstates. */ + break; + } + } + + if (dest_states_malloced) + free (dest_states); + + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + + if (dests_node_malloced) + free (dests_node); + + return trtable; +} + +/* Group all nodes belonging to STATE into several destinations. + Then for all destinations, set the nodes belonging to the destination + to DESTS_NODE[i] and set the characters accepted by the destination + to DEST_CH[i]. This function return the number of destinations. */ + +static int +group_nodes_into_DFAstates (preg, state, dests_node, dests_ch) + const regex_t *preg; + const re_dfastate_t *state; + re_node_set *dests_node; + bitset *dests_ch; +{ + reg_errcode_t err; + const re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int i, j, k; + int ndests; /* Number of the destinations from `state'. */ + bitset accepts; /* Characters a node can accept. */ + const re_node_set *cur_nodes = &state->nodes; + bitset_empty (accepts); + ndests = 0; + + /* For all the nodes belonging to `state', */ + for (i = 0; i < cur_nodes->nelem; ++i) + { + re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + /* Enumerate all single byte character this node can accept. */ + if (type == CHARACTER) + bitset_set (accepts, node->opr.c); + else if (type == SIMPLE_BRACKET) + { + bitset_merge (accepts, node->opr.sbcset); + } + else if (type == OP_PERIOD) + { + bitset_set_all (accepts); + if (!(preg->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (preg->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } + else + continue; + + /* Check the `accepts' and sift the characters which are not + match it the context. */ + if (constraint) + { + if (constraint & NEXT_WORD_CONSTRAINT) + for (j = 0; j < BITSET_UINTS; ++j) + accepts[j] &= dfa->word_char[j]; + if (constraint & NEXT_NOTWORD_CONSTRAINT) + for (j = 0; j < BITSET_UINTS; ++j) + accepts[j] &= ~dfa->word_char[j]; + if (constraint & NEXT_NEWLINE_CONSTRAINT) + { + int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); + bitset_empty (accepts); + if (accepts_newline) + bitset_set (accepts, NEWLINE_CHAR); + else + continue; + } + } + + /* Then divide `accepts' into DFA states, or create a new + state. */ + for (j = 0; j < ndests; ++j) + { + bitset intersec; /* Intersection sets, see below. */ + bitset remains; + /* Flags, see below. */ + int has_intersec, not_subset, not_consumed; + + /* Optimization, skip if this state doesn't accept the character. */ + if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) + continue; + + /* Enumerate the intersection set of this state and `accepts'. */ + has_intersec = 0; + for (k = 0; k < BITSET_UINTS; ++k) + has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; + /* And skip if the intersection set is empty. */ + if (!has_intersec) + continue; + + /* Then check if this state is a subset of `accepts'. */ + not_subset = not_consumed = 0; + for (k = 0; k < BITSET_UINTS; ++k) + { + not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; + not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; + } + + /* If this state isn't a subset of `accepts', create a + new group state, which has the `remains'. */ + if (not_subset) + { + bitset_copy (dests_ch[ndests], remains); + bitset_copy (dests_ch[j], intersec); + err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + } + + /* Put the position in the current group. */ + err = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); + if (BE (err < 0, 0)) + goto error_return; + + /* If all characters are consumed, go to next node. */ + if (!not_consumed) + break; + } + /* Some characters remain, create a new group. */ + if (j == ndests) + { + bitset_copy (dests_ch[ndests], accepts); + err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + bitset_empty (accepts); + } + } + return ndests; + error_return: + for (j = 0; j < ndests; ++j) + re_node_set_free (dests_node + j); + return -1; +} + +#ifdef RE_ENABLE_I18N +/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. + Return the number of the bytes the node accepts. + STR_IDX is the current index of the input string. + + This function handles the nodes which can accept one character, or + one collating element like '.', '[a-z]', opposite to the other nodes + can only accept one byte. */ + +static int +check_node_accept_bytes (preg, node_idx, input, str_idx) + const regex_t *preg; + int node_idx, str_idx; + const re_string_t *input; +{ + const re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + const re_token_t *node = dfa->nodes + node_idx; + int elem_len = re_string_elem_size_at (input, str_idx); + int char_len = re_string_char_size_at (input, str_idx); + int i; +# ifdef _LIBC + int j; + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); +# endif /* _LIBC */ + if (elem_len <= 1 && char_len <= 1) + return 0; + if (node->type == OP_PERIOD) + { + /* '.' accepts any one character except the following two cases. */ + if ((!(preg->syntax & RE_DOT_NEWLINE) && + re_string_byte_at (input, str_idx) == '\n') || + ((preg->syntax & RE_DOT_NOT_NULL) && + re_string_byte_at (input, str_idx) == '\0')) + return 0; + return char_len; + } + else if (node->type == COMPLEX_BRACKET) + { + const re_charset_t *cset = node->opr.mbcset; +# ifdef _LIBC + const unsigned char *pin = ((char *) re_string_get_buffer (input) + + str_idx); +# endif /* _LIBC */ + int match_len = 0; + wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) + ? re_string_wchar_at (input, str_idx) : 0); + + /* match with multibyte character? */ + for (i = 0; i < cset->nmbchars; ++i) + if (wc == cset->mbchars[i]) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + /* match with character_class? */ + for (i = 0; i < cset->nchar_classes; ++i) + { + wctype_t wt = cset->char_classes[i]; + if (__iswctype (wc, wt)) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + +# ifdef _LIBC + if (nrules != 0) + { + unsigned int in_collseq = 0; + const int32_t *table, *indirect; + const unsigned char *weights, *extra; + const char *collseqwc; + int32_t idx; + /* This #include defines a local function! */ +# include + + /* match with collating_symbol? */ + if (cset->ncoll_syms) + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + for (i = 0; i < cset->ncoll_syms; ++i) + { + const unsigned char *coll_sym = extra + cset->coll_syms[i]; + /* Compare the length of input collating element and + the length of current collating element. */ + if (*coll_sym != elem_len) + continue; + /* Compare each bytes. */ + for (j = 0; j < *coll_sym; j++) + if (pin[j] != coll_sym[1 + j]) + break; + if (j == *coll_sym) + { + /* Match if every bytes is equal. */ + match_len = j; + goto check_node_accept_bytes_match; + } + } + + if (cset->nranges) + { + if (elem_len <= char_len) + { + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + in_collseq = collseq_table_lookup (collseqwc, wc); + } + else + in_collseq = find_collation_sequence_value (pin, elem_len); + } + /* match with range expression? */ + for (i = 0; i < cset->nranges; ++i) + if (cset->range_starts[i] <= in_collseq + && in_collseq <= cset->range_ends[i]) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + + /* match with equivalence_class? */ + if (cset->nequiv_classes) + { + const unsigned char *cp = pin; + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + idx = findidx (&cp); + if (idx > 0) + for (i = 0; i < cset->nequiv_classes; ++i) + { + int32_t equiv_class_idx = cset->equiv_classes[i]; + size_t weight_len = weights[idx]; + if (weight_len == weights[equiv_class_idx]) + { + int cnt = 0; + while (cnt <= weight_len + && (weights[equiv_class_idx + 1 + cnt] + == weights[idx + 1 + cnt])) + ++cnt; + if (cnt > weight_len) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + } + } + } + } + else +# endif /* _LIBC */ + { + /* match with range expression? */ +#if __GNUC__ >= 2 + wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; +#else + wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + cmp_buf[2] = wc; +#endif + for (i = 0; i < cset->nranges; ++i) + { + cmp_buf[0] = cset->range_starts[i]; + cmp_buf[4] = cset->range_ends[i]; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + } + check_node_accept_bytes_match: + if (!cset->non_match) + return match_len; + else + { + if (match_len > 0) + return 0; + else + return (elem_len > char_len) ? elem_len : char_len; + } + } + return 0; +} + +# ifdef _LIBC +static unsigned int +find_collation_sequence_value (mbs, mbs_len) + const unsigned char *mbs; + size_t mbs_len; +{ + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules == 0) + { + if (mbs_len == 1) + { + /* No valid character. Match it as a single byte character. */ + const unsigned char *collseq = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + return collseq[mbs[0]]; + } + return UINT_MAX; + } + else + { + int32_t idx; + const unsigned char *extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + + for (idx = 0; ;) + { + int mbs_cnt, found = 0; + int32_t elem_mbs_len; + /* Skip the name of collating element name. */ + idx = idx + extra[idx] + 1; + elem_mbs_len = extra[idx++]; + if (mbs_len == elem_mbs_len) + { + for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) + if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) + break; + if (mbs_cnt == elem_mbs_len) + /* Found the entry. */ + found = 1; + } + /* Skip the byte sequence of the collating element. */ + idx += elem_mbs_len; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + /* Skip the wide char sequence of the collating element. */ + idx = idx + sizeof (uint32_t) * (extra[idx] + 1); + /* If we found the entry, return the sequence value. */ + if (found) + return *(uint32_t *) (extra + idx); + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + } + } +} +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ + +/* Check whether the node accepts the byte which is IDX-th + byte of the INPUT. */ + +static int +check_node_accept (preg, node, mctx, idx) + const regex_t *preg; + const re_token_t *node; + const re_match_context_t *mctx; + int idx; +{ + unsigned char ch; + if (node->constraint) + { + /* The node has constraints. Check whether the current context + satisfies the constraints. */ + unsigned int context = re_string_context_at (mctx->input, idx, + mctx->eflags, + preg->newline_anchor); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + return 0; + } + ch = re_string_byte_at (mctx->input, idx); + if (node->type == CHARACTER) + return node->opr.c == ch; + else if (node->type == SIMPLE_BRACKET) + return bitset_contain (node->opr.sbcset, ch); + else if (node->type == OP_PERIOD) + return !((ch == '\n' && !(preg->syntax & RE_DOT_NEWLINE)) + || (ch == '\0' && (preg->syntax & RE_DOT_NOT_NULL))); + else + return 0; +} + +/* Extend the buffers, if the buffers have run out. */ + +static reg_errcode_t +extend_buffers (mctx) + re_match_context_t *mctx; +{ + reg_errcode_t ret; + re_string_t *pstr = mctx->input; + + /* Double the lengthes of the buffers. */ + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + if (mctx->state_log != NULL) + { + /* And double the length of state_log. */ + re_dfastate_t **new_array; + new_array = re_realloc (mctx->state_log, re_dfastate_t *, + pstr->bufs_len * 2); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->state_log = new_array; + } + + /* Then reconstruct the buffers. */ + if (pstr->icase) + { +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + build_wcs_upper_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (MB_CUR_MAX > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + else + pstr->valid_len = pstr->bufs_len; + } + } + return REG_NOERROR; +} + + +/* Functions for matching context. */ + +/* Initialize MCTX. */ + +static reg_errcode_t +match_ctx_init (mctx, eflags, input, n) + re_match_context_t *mctx; + int eflags, n; + re_string_t *input; +{ + mctx->eflags = eflags; + mctx->input = input; + mctx->match_last = -1; + if (n > 0) + { + mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); + mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); + if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) + return REG_ESPACE; + } + else + mctx->bkref_ents = NULL; + mctx->nbkref_ents = 0; + mctx->abkref_ents = n; + mctx->max_mb_elem_len = 1; + mctx->nsub_tops = 0; + mctx->asub_tops = n; + return REG_NOERROR; +} + +/* Clean the entries which depend on the current input in MCTX. + This function must be invoked when the matcher changes the start index + of the input, or changes the input string. */ + +static void +match_ctx_clean (mctx) + re_match_context_t *mctx; +{ + match_ctx_free_subtops (mctx); + mctx->nsub_tops = 0; + mctx->nbkref_ents = 0; +} + +/* Free all the memory associated with MCTX. */ + +static void +match_ctx_free (mctx) + re_match_context_t *mctx; +{ + match_ctx_free_subtops (mctx); + re_free (mctx->sub_tops); + re_free (mctx->bkref_ents); +} + +/* Free all the memory associated with MCTX->SUB_TOPS. */ + +static void +match_ctx_free_subtops (mctx) + re_match_context_t *mctx; +{ + int st_idx; + for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) + { + int sl_idx; + re_sub_match_top_t *top = mctx->sub_tops[st_idx]; + for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) + { + re_sub_match_last_t *last = top->lasts[sl_idx]; + re_free (last->path.array); + re_free (last); + } + re_free (top->lasts); + if (top->path) + { + re_free (top->path->array); + re_free (top->path); + } + free (top); + } +} + +/* Add a new backreference entry to MCTX. + Note that we assume that caller never call this function with duplicate + entry, and call with STR_IDX which isn't smaller than any existing entry. +*/ + +static reg_errcode_t +match_ctx_add_entry (mctx, node, str_idx, from, to) + re_match_context_t *mctx; + int node, str_idx, from, to; +{ + if (mctx->nbkref_ents >= mctx->abkref_ents) + { + struct re_backref_cache_entry* new_entry; + new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, + mctx->abkref_ents * 2); + if (BE (new_entry == NULL, 0)) + { + re_free (mctx->bkref_ents); + return REG_ESPACE; + } + mctx->bkref_ents = new_entry; + memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', + sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); + mctx->abkref_ents *= 2; + } + mctx->bkref_ents[mctx->nbkref_ents].node = node; + mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; + mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; + mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; + mctx->bkref_ents[mctx->nbkref_ents++].flag = 0; + if (mctx->max_mb_elem_len < to - from) + mctx->max_mb_elem_len = to - from; + return REG_NOERROR; +} + +/* Search for the first entry which has the same str_idx. + Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ + +static int +search_cur_bkref_entry (mctx, str_idx) + re_match_context_t *mctx; + int str_idx; +{ + int left, right, mid; + right = mctx->nbkref_ents; + for (left = 0; left < right;) + { + mid = (left + right) / 2; + if (mctx->bkref_ents[mid].str_idx < str_idx) + left = mid + 1; + else + right = mid; + } + return left; +} + +static void +match_ctx_clear_flag (mctx) + re_match_context_t *mctx; +{ + int i; + for (i = 0; i < mctx->nbkref_ents; ++i) + { + mctx->bkref_ents[i].flag = 0; + } +} + +/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches + at STR_IDX. */ + +static reg_errcode_t +match_ctx_add_subtop (mctx, node, str_idx) + re_match_context_t *mctx; + int node, str_idx; +{ +#ifdef DEBUG + assert (mctx->sub_tops != NULL); + assert (mctx->asub_tops > 0); +#endif + if (mctx->nsub_tops == mctx->asub_tops) + { + re_sub_match_top_t **new_array; + mctx->asub_tops *= 2; + new_array = re_realloc (mctx->sub_tops, re_sub_match_top_t *, + mctx->asub_tops); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops = new_array; + } + mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); + if (mctx->sub_tops[mctx->nsub_tops] == NULL) + return REG_ESPACE; + mctx->sub_tops[mctx->nsub_tops]->node = node; + mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; + return REG_NOERROR; +} + +/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches + at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ + +static re_sub_match_last_t * +match_ctx_add_sublast (subtop, node, str_idx) + re_sub_match_top_t *subtop; + int node, str_idx; +{ + re_sub_match_last_t *new_entry; + if (subtop->nlasts == subtop->alasts) + { + re_sub_match_last_t **new_array; + subtop->alasts = 2 * subtop->alasts + 1; + new_array = re_realloc (subtop->lasts, re_sub_match_last_t *, + subtop->alasts); + if (BE (new_array == NULL, 0)) + return NULL; + subtop->lasts = new_array; + } + new_entry = calloc (1, sizeof (re_sub_match_last_t)); + if (BE (new_entry == NULL, 0)) + return NULL; + subtop->lasts[subtop->nlasts] = new_entry; + new_entry->node = node; + new_entry->str_idx = str_idx; + ++subtop->nlasts; + return new_entry; +} + +static void +sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx, + check_subexp) + re_sift_context_t *sctx; + re_dfastate_t **sifted_sts, **limited_sts; + int last_node, last_str_idx, check_subexp; +{ + sctx->sifted_states = sifted_sts; + sctx->limited_states = limited_sts; + sctx->last_node = last_node; + sctx->last_str_idx = last_str_idx; + sctx->check_subexp = check_subexp; + sctx->cur_bkref = -1; + sctx->cls_subexp_idx = -1; + re_node_set_init_empty (&sctx->limits); +} diff --git a/scsicmds.c b/scsicmds.c new file mode 100644 index 0000000..36ae6dc --- /dev/null +++ b/scsicmds.c @@ -0,0 +1,2097 @@ +/* + * scsicmds.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 1999-2000 Michael Cornwell + * + * Additional SCSI work: + * Copyright (C) 2003-6 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + * + * In the SCSI world "SMART" is a dead or withdrawn standard. In recent + * SCSI standards (since SCSI-3) it goes under the awkward name of + * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")]. + * The relevant information is spread around several SCSI draft + * standards available at http://www.t10.org . Reference is made in the + * code to the following acronyms: + * - SAM [SCSI Architectural model, versions 2 or 3] + * - SPC [SCSI Primary commands, versions 2 or 3] + * - SBC [SCSI Block commands, versions 2] + * + * Some SCSI disk vendors have snippets of "SMART" information in their + * product manuals. + */ + +#include +#include + +#include "config.h" +#include "int64.h" +#include "extern.h" +#include "scsicmds.h" +#include "utility.h" + +const char *scsicmds_c_cvsid="$Id: scsicmds.c,v 1.85 2006/04/12 14:54:28 ballen4705 Exp $" +CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +/* for passing global control variables */ +extern smartmonctrl *con; + +/* output binary in hex and optionally ascii */ +void dStrHex(const char* str, int len, int no_ascii) +{ + const char* p = str; + unsigned char c; + char buff[82]; + int a = 0; + const int bpstart = 5; + const int cpstart = 60; + int cpos = cpstart; + int bpos = bpstart; + int i, k; + + if (len <= 0) return; + memset(buff,' ',80); + buff[80]='\0'; + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + if (bpos >= ((bpstart + (9 * 3)))) + bpos++; + + for(i = 0; i < len; i++) + { + c = *p++; + bpos += 3; + if (bpos == (bpstart + (9 * 3))) + bpos++; + sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); + buff[bpos + 2] = ' '; + if (no_ascii) + buff[cpos++] = ' '; + else { + if ((c < ' ') || (c >= 0x7f)) + c='.'; + buff[cpos++] = c; + } + if (cpos > (cpstart+15)) + { + pout("%s\n", buff); + bpos = bpstart; + cpos = cpstart; + a += 16; + memset(buff,' ',80); + k = sprintf(buff + 1, "%.2x", a); + buff[k + 1] = ' '; + } + } + if (cpos > cpstart) + { + pout("%s\n", buff); + } +} + +struct scsi_opcode_name { + UINT8 opcode; + const char * name; +}; + +static struct scsi_opcode_name opcode_name_arr[] = { + /* in ascending opcode order */ + {TEST_UNIT_READY, "test unit ready"}, /* 0x00 */ + {REQUEST_SENSE, "request sense"}, /* 0x03 */ + {INQUIRY, "inquiry"}, /* 0x12 */ + {MODE_SELECT, "mode select"}, /* 0x15 */ + {MODE_SENSE, "mode sense"}, /* 0x1a */ + {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */ + {SEND_DIAGNOSTIC, "send diagnostic"}, /* 0x1d */ + {READ_DEFECT_10, "read defect list(10)"}, /* 0x37 */ + {LOG_SENSE, "log sense"}, /* 0x4d */ + {MODE_SELECT_10, "mode select(10)"}, /* 0x55 */ + {MODE_SENSE_10, "mode sense(10)"}, /* 0x5a */ +}; + +const char * scsi_get_opcode_name(UINT8 opcode) +{ + int k; + int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]); + struct scsi_opcode_name * onp; + + for (k = 0; k < len; ++k) { + onp = &opcode_name_arr[k]; + if (opcode == onp->opcode) + return onp->name; + else if (opcode < onp->opcode) + return NULL; + } + return NULL; +} + + +void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf, + struct scsi_sense_disect * out) +{ + memset(out, 0, sizeof(struct scsi_sense_disect)); + if ((SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) && + (io_buf->resp_sense_len > 7)) { + out->error_code = (io_buf->sensep[0] & 0x7f); + out->sense_key = (io_buf->sensep[2] & 0xf); + if (io_buf->resp_sense_len > 13) { + out->asc = io_buf->sensep[12]; + out->ascq = io_buf->sensep[13]; + } + } +} + +static int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) +{ + switch (sinfo->sense_key) { + case SCSI_SK_NOT_READY: + if (SCSI_ASC_NO_MEDIUM == sinfo->asc) + return SIMPLE_ERR_NO_MEDIUM; + else if (SCSI_ASC_NOT_READY == sinfo->asc) { + if (0x1 == sinfo->ascq) + return SIMPLE_ERR_BECOMING_READY; + else + return SIMPLE_ERR_NOT_READY; + } else + return SIMPLE_ERR_NOT_READY; + case SCSI_SK_MEDIUM_ERROR: + case SCSI_SK_HARDWARE_ERROR: + return SIMPLE_ERR_MEDIUM_HARDWARE; + 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) + return SIMPLE_ERR_BAD_FIELD; + else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc) + return SIMPLE_ERR_BAD_PARAM; + else + return SIMPLE_ERR_BAD_PARAM; /* all other illegal request */ + case SCSI_SK_UNIT_ATTENTION: + return SIMPLE_ERR_TRY_AGAIN; + default: + return SIMPLE_NO_ERROR; + } +} + +const char * scsiErrString(int scsiErr) +{ + if (scsiErr < 0) + return strerror(-scsiErr); + switch (scsiErr) { + case SIMPLE_NO_ERROR: + return "no error"; + case SIMPLE_ERR_NOT_READY: + return "device not ready"; + case SIMPLE_ERR_BAD_OPCODE: + return "unsupported scsi opcode"; + case SIMPLE_ERR_BAD_FIELD: + return "unsupported field in scsi command"; + case SIMPLE_ERR_BAD_PARAM: + return "badly formed scsi parameters"; + case SIMPLE_ERR_BAD_RESP: + return "scsi response fails sanity test"; + case SIMPLE_ERR_NO_MEDIUM: + return "no medium present"; + case SIMPLE_ERR_BECOMING_READY: + return "device will be ready soon"; + case SIMPLE_ERR_TRY_AGAIN: + return "unit attention reported, try again"; + case SIMPLE_ERR_MEDIUM_HARDWARE: + return "medium or hardware error (serious)"; + default: + return "unknown error"; + } +} + +/* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if + command not supported, 3 if field (within command) not supported or + returns negated errno. SPC-3 sections 6.6 and 7.2 (rec 22a). + N.B. Sets PC==1 to fetch "current cumulative" log pages. + If known_resp_len > 0 then a single fetch is done for this response + length. If known_resp_len == 0 then twin fetches are performed, the + first to deduce the response length, then send the same command again + requesting the deduced response length. This protects certain fragile + HBAs. The twin fetch technique should not be used with the TapeAlert + log page since it clears its state flags after each fetch. */ +int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen, + int known_resp_len) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int pageLen; + int status, res; + + if (known_resp_len > bufLen) + return -EIO; + if (known_resp_len > 0) + pageLen = known_resp_len; + else { + /* Starting twin fetch strategy: first fetch to find respone length */ + pageLen = 4; + if (pageLen > bufLen) + return -EIO; + else + memset(pBuf, 0, pageLen); + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = pageLen; + io_hdr.dxferp = pBuf; + cdb[0] = LOG_SENSE; + cdb[2] = 0x40 | (pagenum & 0x3f); /* Page control (PC)==1 */ + cdb[7] = (pageLen >> 8) & 0xff; + cdb[8] = pageLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + if ((res = scsiSimpleSenseFilter(&sinfo))) + return res; + /* sanity check on response */ + if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum)) + return SIMPLE_ERR_BAD_RESP; + if (0 == ((pBuf[2] << 8) + pBuf[3])) + return SIMPLE_ERR_BAD_RESP; + pageLen = (pBuf[2] << 8) + pBuf[3] + 4; + /* some SCSI HBA don't like "odd" length transfers */ + if (pageLen % 2) + pageLen += 1; + if (pageLen > bufLen) + pageLen = bufLen; + } + memset(pBuf, 0, 4); + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = pageLen; + io_hdr.dxferp = pBuf; + cdb[0] = LOG_SENSE; + cdb[2] = 0x40 | (pagenum & 0x3f); /* Page control (PC)==1 */ + cdb[7] = (pageLen >> 8) & 0xff; + cdb[8] = pageLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (0 != status) + return status; + /* sanity check on response */ + if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum)) + return SIMPLE_ERR_BAD_RESP; + if (0 == ((pBuf[2] << 8) + pBuf[3])) + return SIMPLE_ERR_BAD_RESP; + return 0; +} + +/* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY, + * 2 if command not supported (then MODE SENSE(10) should be supported), + * 3 if field in command not supported or returns negated errno. + * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */ +int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SENSE; + cdb[2] = (pc << 6) | (pagenum & 0x3f); + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + } + if ((0 == status) && (ALL_MODE_PAGES != pagenum)) { + int offset; + + offset = scsiModePageOffset(pBuf, bufLen, 0); + if (offset < 0) + return SIMPLE_ERR_BAD_RESP; + else if (pagenum != (pBuf[offset] & 0x3f)) + return SIMPLE_ERR_BAD_RESP; + } + return status; +} + +/* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response + * from a corresponding 6 byte MODE SENSE command. Such a response should + * have a 4 byte header followed by 0 or more 8 byte block descriptors + * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY, + * 2 if command not supported (then MODE SELECT(10) may be supported), + * 3 if field in command not supported, 4 if bad parameter to command + * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */ +int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status, pg_offset, pg_len, hdr_plus_1_pg; + + pg_offset = 4 + pBuf[3]; + if (pg_offset + 2 >= bufLen) + return -EINVAL; + pg_len = pBuf[pg_offset + 1] + 2; + hdr_plus_1_pg = pg_offset + pg_len; + if (hdr_plus_1_pg > bufLen) + return -EINVAL; + pBuf[0] = 0; /* Length of returned mode sense data reserved for SELECT */ + pBuf[pg_offset] &= 0x7f; /* Mask out PS bit from byte 0 of page data */ + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_TO_DEVICE; + io_hdr.dxfer_len = hdr_plus_1_pg; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SELECT; + cdb[1] = 0x10 | (sp & 1); /* set PF (page format) bit always */ + cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */ + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command + * not supported (then MODE SENSE(6) might be supported), 3 if field in + * command not supported or returns negated errno. + * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */ +int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SENSE_10; + cdb[2] = (pc << 6) | (pagenum & 0x3f); + cdb[7] = (bufLen >> 8) & 0xff; + cdb[8] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + status = scsiSimpleSenseFilter(&sinfo); + } + if ((0 == status) && (ALL_MODE_PAGES != pagenum)) { + int offset; + + offset = scsiModePageOffset(pBuf, bufLen, 1); + if (offset < 0) + return SIMPLE_ERR_BAD_RESP; + else if (pagenum != (pBuf[offset] & 0x3f)) + return SIMPLE_ERR_BAD_RESP; + } + return status; +} + +/* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response + * from a corresponding 10 byte MODE SENSE command. Such a response should + * have a 8 byte header followed by 0 or more 8 byte block descriptors + * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if + * command not supported (then MODE SELECT(6) may be supported), 3 if field + * in command not supported, 4 if bad parameter to command or returns + * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */ +int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[10]; + UINT8 sense[32]; + int status, pg_offset, pg_len, hdr_plus_1_pg; + + pg_offset = 8 + (pBuf[6] << 8) + pBuf[7]; + if (pg_offset + 2 >= bufLen) + return -EINVAL; + pg_len = pBuf[pg_offset + 1] + 2; + hdr_plus_1_pg = pg_offset + pg_len; + if (hdr_plus_1_pg > bufLen) + return -EINVAL; + pBuf[0] = 0; + pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */ + pBuf[pg_offset] &= 0x7f; /* Mask out PS bit from byte 0 of page data */ + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_TO_DEVICE; + io_hdr.dxfer_len = hdr_plus_1_pg; + io_hdr.dxferp = pBuf; + cdb[0] = MODE_SELECT_10; + cdb[1] = 0x10 | (sp & 1); /* set PF (page format) bit always */ + cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */ + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* Standard INQUIRY returns 0 for ok, anything else is a major problem. + * bufLen should be 36 for unsafe devices (like USB mass storage stuff) + * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */ +int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen) +{ + struct scsi_sense_disect sinfo; + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = INQUIRY; + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* INQUIRY to fetch Vital Page Data. Returns 0 if ok, 1 if NOT READY + * (unlikely), 2 if command not supported, 3 if field in command not + * supported, 5 if response indicates that EVPD bit ignored or returns + * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */ +int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status, res; + + if ((bufLen < 0) || (bufLen > 255)) + return -EINVAL; + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + if (bufLen > 1) + pBuf[1] = 0x0; + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = INQUIRY; + cdb[1] = 0x1; /* set EVPD bit (enable Vital Product Data) */ + cdb[2] = vpd_page; + cdb[4] = bufLen; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + if ((res = scsiSimpleSenseFilter(&sinfo))) + return res; + /* Guard against devices that ignore EVPD bit and do standard INQUIRY */ + if (bufLen > 1) { + if (vpd_page == pBuf[1]) { + if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2])) + return SIMPLE_ERR_BAD_RESP; + } else + return SIMPLE_ERR_BAD_RESP; + } + return 0; +} + +/* REQUEST SENSE command. Returns 0 if ok, anything else major problem. + * SPC-3 section 6.27 (rev 22a) */ +int scsiRequestSense(int device, struct scsi_sense_disect * sense_info) +{ + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + UINT8 sense[32]; + UINT8 buff[18]; + int status, len; + UINT8 ecode; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = sizeof(buff); + io_hdr.dxferp = buff; + cdb[0] = REQUEST_SENSE; + cdb[4] = sizeof(buff); + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if ((0 == status) && (sense_info)) { + ecode = buff[0] & 0x7f; + sense_info->error_code = ecode; + sense_info->sense_key = buff[2] & 0xf; + sense_info->asc = 0; + sense_info->ascq = 0; + if ((0x70 == ecode) || (0x71 == ecode)) { + len = buff[7] + 8; + if (len > 13) { + sense_info->asc = buff[12]; + sense_info->ascq = buff[13]; + } + } + } + return status; +} + +/* SEND DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if command + * not supported, 3 if field in command not supported or returns negated + * errno. SPC-3 section 6.28 (rev 22a) */ +int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = SEND_DIAGNOSTIC; + if (SCSI_DIAG_DEF_SELF_TEST == functioncode) + cdb[1] = 0x4; /* SelfTest bit */ + else if (SCSI_DIAG_NO_SELF_TEST != functioncode) + cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */ + else /* SCSI_DIAG_NO_SELF_TEST == functioncode */ + cdb[1] = 0x10; /* PF bit */ + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + /* worst case is an extended foreground self test on a big disk */ + io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if + * command not supported, 3 if field in command not supported or returns + * negated errno. SPC-3 section 6.18 (rev 22a) */ +int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, + int bufLen) +{ + struct scsi_cmnd_io io_hdr; + struct scsi_sense_disect sinfo; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = RECEIVE_DIAGNOSTIC; + cdb[1] = pcv; + cdb[2] = pagenum; + cdb[3] = (bufLen >> 8) & 0xff; + cdb[4] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */ +static int _testunitready(int device, struct scsi_sense_disect * sinfo) +{ + struct scsi_cmnd_io io_hdr; + UINT8 cdb[6]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_NONE; + io_hdr.dxfer_len = 0; + io_hdr.dxferp = NULL; + cdb[0] = TEST_UNIT_READY; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, sinfo); + return 0; +} + +/* Returns 0 for device responds and media ready, 1 for device responds and + media not ready, or returns a negated errno value */ +int scsiTestUnitReady(int device) +{ + struct scsi_sense_disect sinfo; + int status; + + status = _testunitready(device, &sinfo); + if (0 != status) + return status; + status = scsiSimpleSenseFilter(&sinfo); + if (SIMPLE_ERR_TRY_AGAIN == status) { + /* power on reset, media changed, ok ... try again */ + status = _testunitready(device, &sinfo); + if (0 != status) + return status; + status = scsiSimpleSenseFilter(&sinfo); + } + return status; +} + +/* 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(int 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; + UINT8 cdb[10]; + UINT8 sense[32]; + int status; + + memset(&io_hdr, 0, sizeof(io_hdr)); + memset(cdb, 0, sizeof(cdb)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = bufLen; + io_hdr.dxferp = pBuf; + cdb[0] = READ_DEFECT_10; + cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) | + ((req_glist << 3) & 0x8) | (dl_format & 0x7)); + cdb[7] = (bufLen >> 8) & 0xff; + cdb[8] = bufLen & 0xff; + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + io_hdr.sensep = sense; + io_hdr.max_sense_len = sizeof(sense); + io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; + + status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); + if (0 != status) + return status; + scsi_do_sense_disect(&io_hdr, &sinfo); + return scsiSimpleSenseFilter(&sinfo); +} + +/* Offset into mode sense (6 or 10 byte) response that actual mode page + * starts at (relative to resp[0]). Returns -1 if problem */ +int scsiModePageOffset(const UINT8 * resp, int len, int modese_len) +{ + int resp_len, bd_len; + int offset = -1; + + if (resp) { + if (10 == modese_len) { + resp_len = (resp[0] << 8) + resp[1] + 2; + bd_len = (resp[6] << 8) + resp[7]; + offset = bd_len + 8; + } else { + resp_len = resp[0] + 1; + bd_len = resp[3]; + offset = bd_len + 4; + } + if ((offset + 2) > len) { + pout("scsiModePageOffset: raw_curr too small, offset=%d " + "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len); + offset = -1; + } else if ((offset + 2) > resp_len) { + if ((resp_len > 2) || con->reportscsiioctl) + pout("scsiModePageOffset: response length too short, " + "resp_len=%d offset=%d bd_len=%d\n", resp_len, + offset, bd_len); + offset = -1; + } + } + return offset; +} + +/* IEC mode page byte 2 bit masks */ +#define DEXCPT_ENABLE 0x08 +#define EWASC_ENABLE 0x10 +#define DEXCPT_DISABLE 0xf7 +#define EWASC_DISABLE 0xef +#define TEST_DISABLE 0xfb + +/* Fetches the Informational Exceptions Control mode page. First tries + * the 6 byte MODE SENSE command and if that fails with an illegal opcode + * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive + * number if a known error (see SIMPLE_ERR_ ...) or a negative errno + * value. */ +int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len) +{ + int err = 0; + + memset(iecp, 0, sizeof(*iecp)); + iecp->modese_len = modese_len; + iecp->requestedCurrent = 1; + if (iecp->modese_len <= 6) { + if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CURRENT, + iecp->raw_curr, sizeof(iecp->raw_curr)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + iecp->modese_len = 10; + else { + iecp->modese_len = 0; + return err; + } + } else if (0 == iecp->modese_len) + iecp->modese_len = 6; + } + if (10 == iecp->modese_len) { + err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CURRENT, + iecp->raw_curr, sizeof(iecp->raw_curr)); + if (err) { + iecp->modese_len = 0; + return err; + } + } + iecp->gotCurrent = 1; + iecp->requestedChangeable = 1; + if (10 == iecp->modese_len) + err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CHANGEABLE, + iecp->raw_chg, sizeof(iecp->raw_chg)); + else if (6 == iecp->modese_len) + err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, + MPAGE_CONTROL_CHANGEABLE, + iecp->raw_chg, sizeof(iecp->raw_chg)); + if (err) + return err; + iecp->gotChangeable = 1; + return 0; +} + +int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp) +{ + int offset; + + if (iecp && iecp->gotCurrent) { + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset >= 0) + return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1; + else + return 0; + } else + return 0; +} + +int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp) +{ + int offset; + + if (iecp && iecp->gotCurrent) { + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset >= 0) + return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0; + else + return 0; + } else + return 0; +} + +/* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */ +#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 +#define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4 +/* exception/warning via an unrequested REQUEST SENSE command */ +#define SCSI_IEC_MP_MRIE 6 +#define SCSI_IEC_MP_INTERVAL_T 0 +#define SCSI_IEC_MP_REPORT_COUNT 1 + +/* Try to set (or clear) both Exception Control and Warning in the IE + * mode page subject to the "changeable" mask. The object pointed to + * by iecp is (possibly) inaccurate after this call, therefore + * scsiFetchIECmpage() should be called again if the IEC mode page + * is to be re-examined. + * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...' + * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */ +int scsiSetExceptionControlAndWarning(int device, int enabled, + const struct scsi_iec_mode_page *iecp) +{ + int k, offset, resp_len; + int err = 0; + UINT8 rout[SCSI_IECMP_RAW_LEN]; + int sp, eCEnabled, wEnabled; + + if ((! iecp) || (! iecp->gotCurrent)) + return -EINVAL; + offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr), + iecp->modese_len); + if (offset < 0) + return -EINVAL; + memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN); + if (10 == iecp->modese_len) { + resp_len = (rout[0] << 8) + rout[1] + 2; + rout[3] &= 0xef; /* for disks mask out DPOFUA bit */ + } else { + resp_len = rout[0] + 1; + rout[2] &= 0xef; /* for disks mask out DPOFUA bit */ + } + sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */ + if (enabled) { + rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED; + if (con->reportscsiioctl > 2) + rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK; + rout[offset + 3] = SCSI_IEC_MP_MRIE; + rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff; + rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff; + rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff; + rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff; + rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff; + rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff; + rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff; + rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff; + if (iecp->gotChangeable) { + UINT8 chg2 = iecp->raw_chg[offset + 2]; + + rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) : + iecp->raw_curr[offset + 2]; + for (k = 3; k < 12; ++k) { + if (0 == iecp->raw_chg[offset + k]) + rout[offset + k] = iecp->raw_curr[offset + k]; + } + } + if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) { + if (con->reportscsiioctl > 0) + pout("scsiSetExceptionControlAndWarning: already enabled\n"); + return 0; + } + } else { /* disabling Exception Control and (temperature) Warnings */ + eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1; + wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0; + if ((! eCEnabled) && (! wEnabled)) { + if (con->reportscsiioctl > 0) + pout("scsiSetExceptionControlAndWarning: already disabled\n"); + return 0; /* nothing to do, leave other setting alone */ + } + if (wEnabled) + rout[offset + 2] &= EWASC_DISABLE; + if (eCEnabled) { + if (iecp->gotChangeable && + (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE)) + rout[offset + 2] |= DEXCPT_ENABLE; + rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */ + } + } + if (10 == iecp->modese_len) + err = scsiModeSelect10(device, sp, rout, resp_len); + else if (6 == iecp->modese_len) + err = scsiModeSelect(device, sp, rout, resp_len); + return err; +} + +int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp) +{ + UINT8 tBuf[252]; + int err; + + memset(tBuf, 0, sizeof(tBuf)); + if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, tBuf, + sizeof(tBuf), 0))) { + *currenttemp = 0; + *triptemp = 0; + pout("Log Sense for temperature failed [%s]\n", scsiErrString(err)); + return err; + } + *currenttemp = tBuf[9]; + *triptemp = tBuf[15]; + return 0; +} + +/* Read informational exception log page or Request Sense response. + * Fetching asc/ascq code potentially flagging an exception or warning. + * Returns 0 if ok, else error number. A current temperature of 255 + * (Celsius) implies that the temperature not available. */ +int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage, + UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, + UINT8 *triptemp) +{ + UINT8 tBuf[252]; + struct scsi_sense_disect sense_info; + int err; + int temperatureSet = 0; + unsigned short pagesize; + UINT8 currTemp, trTemp; + + *asc = 0; + *ascq = 0; + *currenttemp = 0; + *triptemp = 0; + memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk + memset(&sense_info, 0, sizeof(sense_info)); + if (hasIELogPage) { + if ((err = scsiLogSense(device, IE_LPAGE, tBuf, + sizeof(tBuf), 0))) { + pout("Log Sense failed, IE page [%s]\n", scsiErrString(err)); + return err; + } + // pull out page size from response, don't forget to add 4 + pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4; + if ((pagesize < 4) || tBuf[4] || tBuf[5]) { + pout("Log Sense failed, IE page, bad parameter code or length\n"); + return SIMPLE_ERR_BAD_PARAM; + } + if (tBuf[7] > 1) { + sense_info.asc = tBuf[8]; + sense_info.ascq = tBuf[9]; + if (! hasTempLogPage) { + if (tBuf[7] > 2) + *currenttemp = tBuf[10]; + if (tBuf[7] > 3) /* IBM extension in SMART (IE) lpage */ + *triptemp = tBuf[11]; + } + } + } + if (0 == sense_info.asc) { + /* ties in with MRIE field of 6 in IEC mode page (0x1c) */ + if ((err = scsiRequestSense(device, &sense_info))) { + pout("Request Sense failed, [%s]\n", scsiErrString(err)); + return err; + } + } + *asc = sense_info.asc; + *ascq = sense_info.ascq; + if ((! temperatureSet) && hasTempLogPage) { + if (0 == scsiGetTemp(device, &currTemp, &trTemp)) { + *currenttemp = currTemp; + *triptemp = trTemp; + } + } + return 0; +} + +// The first character (W, C, I) tells the severity +static const char * TapeAlertsMessageTable[]= { + " ", + /* 0x01 */ + "W: The tape drive is having problems reading data. No data has been lost,\n" + " but there has been a reduction in the performance of the tape.", + /* 0x02 */ + "W: The tape drive is having problems writing data. No data has been lost,\n" + " but there has been a reduction in the capacity of the tape.", + /* 0x03 */ + "W: The operation has stopped because an error has occurred while reading\n" + " or writing data that the drive cannot correct.", + /* 0x04 */ + "C: Your data is at risk:\n" + " 1. Copy any data you require from this tape. \n" + " 2. Do not use this tape again.\n" + " 3. Restart the operation with a different tape.", + /* 0x05 */ + "C: The tape is damaged or the drive is faulty. Call the tape drive\n" + " supplier helpline.", + /* 0x06 */ + "C: The tape is from a faulty batch or the tape drive is faulty:\n" + " 1. Use a good tape to test the drive.\n" + " 2. If problem persists, call the tape drive supplier helpline.", + /* 0x07 */ + "W: The tape cartridge has reached the end of its calculated useful life:\n" + " 1. Copy data you need to another tape.\n" + " 2. Discard the old tape.", + /* 0x08 */ + "W: The tape cartridge is not data-grade. Any data you back up to the tape\n" + " is at risk. Replace the cartridge with a data-grade tape.", + /* 0x09 */ + "C: You are trying to write to a write-protected cartridge. Remove the\n" + " write-protection or use another tape.", + /* 0x0a */ + "I: You cannot eject the cartridge because the tape drive is in use. Wait\n" + " until the operation is complete before ejecting the cartridge.", + /* 0x0b */ + "I: The tape in the drive is a cleaning cartridge.", + /* 0x0c */ + "I: You have tried to load a cartridge of a type which is not supported\n" + " by this drive.", + /* 0x0d */ + "C: The operation has failed because the tape in the drive has experienced\n" + " a mechanical failure:\n" + " 1. Discard the old tape.\n" + " 2. Restart the operation with a different tape.", + /* 0x0e */ + "C: The operation has failed because the tape in the drive has experienced\n" + " a mechanical failure:\n" + " 1. Do not attempt to extract the tape cartridge\n" + " 2. Call the tape drive supplier helpline.", + /* 0x0f */ + "W: The memory in the tape cartridge has failed, which reduces\n" + " performance. Do not use the cartridge for further write operations.", + /* 0x10 */ + "C: The operation has failed because the tape cartridge was manually\n" + " de-mounted while the tape drive was actively writing or reading.", + /* 0x11 */ + "W: You have loaded a cartridge of a type that is read-only in this drive.\n" + " The cartridge will appear as write-protected.", + /* 0x12 */ + "W: The tape directory on the tape cartridge has been corrupted. File\n" + " search performance will be degraded. The tape directory can be rebuilt\n" + " by reading all the data on the cartridge.", + /* 0x13 */ + "I: The tape cartridge is nearing the end of its calculated life. It is\n" + " recommended that you:\n" + " 1. Use another tape cartridge for your next backup.\n" + " 2. Store this tape in a safe place in case you need to restore " + " data from it.", + /* 0x14 */ + "C: The tape drive needs cleaning:\n" + " 1. If the operation has stopped, eject the tape and clean the drive.\n" + " 2. If the operation has not stopped, wait for it to finish and then\n" + " clean the drive.\n" + " Check the tape drive users manual for device specific cleaning instructions.", + /* 0x15 */ + "W: The tape drive is due for routine cleaning:\n" + " 1. Wait for the current operation to finish.\n" + " 2. The use a cleaning cartridge.\n" + " Check the tape drive users manual for device specific cleaning instructions.", + /* 0x16 */ + "C: The last cleaning cartridge used in the tape drive has worn out:\n" + " 1. Discard the worn out cleaning cartridge.\n" + " 2. Wait for the current operation to finish.\n" + " 3. Then use a new cleaning cartridge.", + /* 0x17 */ + "C: The last cleaning cartridge used in the tape drive was an invalid\n" + " type:\n" + " 1. Do not use this cleaning cartridge in this drive.\n" + " 2. Wait for the current operation to finish.\n" + " 3. Then use a new cleaning cartridge.", + /* 0x18 */ + "W: The tape drive has requested a retention operation", + /* 0x19 */ + "W: A redundant interface port on the tape drive has failed", + /* 0x1a */ + "W: A tape drive cooling fan has failed", + /* 0x1b */ + "W: A redundant power supply has failed inside the tape drive enclosure.\n" + " Check the enclosure users manual for instructions on replacing the\n" + " failed power supply.", + /* 0x1c */ + "W: The tape drive power consumption is outside the specified range.", + /* 0x1d */ + "W: Preventive maintenance of the tape drive is required. Check the tape\n" + " drive users manual for device specific preventive maintenance\n" + " tasks or call the tape drive supplier helpline.", + /* 0x1e */ + "C: The tape drive has a hardware fault:\n" + " 1. Eject the tape or magazine.\n" + " 2. Reset the drive.\n" + " 3. Restart the operation.", + /* 0x1f */ + "C: The tape drive has a hardware fault:\n" + " 1. Turn the tape drive off and then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the tape drive supplier helpline.", + /* 0x20 */ + "W: The tape drive has a problem with the application client interface:\n" + " 1. Check the cables and cable connections.\n" + " 2. Restart the operation.", + /* 0x21 */ + "C: The operation has failed:\n" + " 1. Eject the tape or magazine.\n" + " 2. Insert the tape or magazine again.\n" + " 3. Restart the operation.", + /* 0x22 */ + "W: The firmware download has failed because you have tried to use the\n" + " incorrect firmware for this tape drive. Obtain the correct\n" + " firmware and try again.", + /* 0x23 */ + "W: Environmental conditions inside the tape drive are outside the\n" + " specified humidity range.", + /* 0x24 */ + "W: Environmental conditions inside the tape drive are outside the\n" + " specified temperature range.", + /* 0x25 */ + "W: The voltage supply to the tape drive is outside the specified range.", + /* 0x26 */ + "C: A hardware failure of the tape drive is predicted. Call the tape\n" + " drive supplier helpline.", + /* 0x27 */ + "W: The tape drive may have a hardware fault. Run extended diagnostics to\n" + " verify and diagnose the problem. Check the tape drive users manual for\n" + " device specific instructions on running extended diagnostic tests.", + /* 0x28 */ + "C: The changer mechanism is having difficulty communicating with the tape\n" + " drive:\n" + " 1. Turn the autoloader off then on.\n" + " 2. Restart the operation.\n" + " 3. If problem persists, call the tape drive supplier helpline.", + /* 0x29 */ + "C: A tape has been left in the autoloader by a previous hardware fault:\n" + " 1. Insert an empty magazine to clear the fault.\n" + " 2. If the fault does not clear, turn the autoloader off and then\n" + " on again.\n" + " 3. If the problem persists, call the tape drive supplier helpline.", + /* 0x2a */ + "W: There is a problem with the autoloader mechanism.", + /* 0x2b */ + "C: The operation has failed because the autoloader door is open:\n" + " 1. Clear any obstructions from the autoloader door.\n" + " 2. Eject the magazine and then insert it again.\n" + " 3. If the fault does not clear, turn the autoloader off and then\n" + " on again.\n" + " 4. If the problem persists, call the tape drive supplier helpline.", + /* 0x2c */ + "C: The autoloader has a hardware fault:\n" + " 1. Turn the autoloader off and then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the tape drive supplier helpline.\n" + " Check the autoloader users manual for device specific instructions\n" + " on turning the device power on and off.", + /* 0x2d */ + "C: The autoloader cannot operate without the magazine,\n" + " 1. Insert the magazine into the autoloader.\n" + " 2. Restart the operation.", + /* 0x2e */ + "W: A hardware failure of the changer mechanism is predicted. Call the\n" + " tape drive supplier helpline.", + /* 0x2f */ + "I: Reserved.", + /* 0x30 */ + "I: Reserved.", + /* 0x31 */ + "I: Reserved.", + /* 0x32 */ + "W: Media statistics have been lost at some time in the past", + /* 0x33 */ + "W: The tape directory on the tape cartridge just unloaded has been\n" + " corrupted. File search performance will be degraded. The tape\n" + " directory can be rebuilt by reading all the data.", + /* 0x34 */ + "C: The tape just unloaded could not write its system area successfully:\n" + " 1. Copy data to another tape cartridge.\n" + " 2. Discard the old cartridge.", + /* 0x35 */ + "C: The tape system are could not be read successfully at load time:\n" + " 1. Copy data to another tape cartridge.\n", + /* 0x36 */ + "C: The start or data could not be found on the tape:\n" + " 1. Check you are using the correct format tape.\n" + " 2. Discard the tape or return the tape to your supplier", + /* 0x37 */ + "C: The operation has failed because the media cannot be loaded\n" + " and threaded.\n" + " 1. Remove the cartridge, inspect it as specified in the product\n" + " manual, and retry the operation.\n" + " 2. If the problem persists, call the tape drive supplier help line.", + /* 0x38 */ + "C: The operation has failed because the medium cannot be unloaded:\n" + " 1. Do not attempt to extract the tape cartridge.\n" + " 2. Call the tape driver supplier help line.", + /* 0x39 */ + "C: The tape drive has a problem with the automation interface:\n" + " 1. Check the power to the automation system.\n" + " 2. Check the cables and cable connections.\n" + " 3. Call the supplier help line if problem persists.", + /* 0x3a */ + "W: The tape drive has reset itself due to a detected firmware\n" + " fault. If problem persists, call the supplier help line.", + }; + +const char * scsiTapeAlertsTapeDevice(unsigned short code) +{ + const int num = sizeof(TapeAlertsMessageTable) / + sizeof(TapeAlertsMessageTable[0]); + + return (code < num) ? TapeAlertsMessageTable[code] : "Unknown Alert"; +} + +// The first character (W, C, I) tells the severity +static const char * ChangerTapeAlertsMessageTable[]= { + " ", + /* 0x01 */ + "C: The library mechanism is having difficulty communicating with the\n" + " drive:\n" + " 1. Turn the library off then on.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x02 */ + "W: There is a problem with the library mechanism. If problem persists,\n" + " call the library supplier help line.", + /* 0x03 */ + "C: The library has a hardware fault:\n" + " 1. Reset the library.\n" + " 2. Restart the operation.\n" + " Check the library users manual for device specific instructions on resetting\n" + " the device.", + /* 0x04 */ + "C: The library has a hardware fault:\n" + " 1. Turn the library off then on again.\n" + " 2. Restart the operation.\n" + " 3. If the problem persists, call the library supplier help line.\n" + " Check the library users manual for device specific instructions on turning the\n" + " device power on and off.", + /* 0x05 */ + "W: The library mechanism may have a hardware fault.\n" + " Run extended diagnostics to verify and diagnose the problem. Check the library\n" + " users manual for device specific instructions on running extended diagnostic\n" + " tests.", + /* 0x06 */ + "C: The library has a problem with the host interface:\n" + " 1. Check the cables and connections.\n" + " 2. Restart the operation.", + /* 0x07 */ + "W: A hardware failure of the library is predicted. Call the library\n" + " supplier help line.", + /* 0x08 */ + "W: Preventive maintenance of the library is required.\n" + " Check the library users manual for device specific preventative maintenance\n" + " tasks, or call your library supplier help line.", + /* 0x09 */ + "C: General environmental conditions inside the library are outside the\n" + " specified humidity range.", + /* 0x0a */ + "C: General environmental conditions inside the library are outside the\n" + " specified temperature range.", + /* 0x0b */ + "C: The voltage supply to the library is outside the specified range.\n" + " There is a potential problem with the power supply or failure of\n" + " a redundant power supply.", + /* 0x0c */ + "C: A cartridge has been left inside the library by a previous hardware\n" + " fault:\n" + " 1. Insert an empty magazine to clear the fault.\n" + " 2. If the fault does not clear, turn the library off and then on again.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x0d */ + "W: There is a potential problem with the drive ejecting cartridges or\n" + " with the library mechanism picking a cartridge from a slot.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x0e */ + "W: There is a potential problem with the library mechanism placing a\n" + " cartridge into a slot.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x0f */ + "W: There is a potential problem with the drive or the library mechanism\n" + " loading cartridges, or an incompatible cartridge.", + /* 0x10 */ + "C: The library has failed because the door is open:\n" + " 1. Clear any obstructions from the library door.\n" + " 2. Close the library door.\n" + " 3. If the problem persists, call the library supplier help line.", + /* 0x11 */ + "C: There is a mechanical problem with the library media import/export\n" + " mailslot.", + /* 0x12 */ + "C: The library cannot operate without the magazine.\n" + " 1. Insert the magazine into the library.\n" + " 2. Restart the operation.", + /* 0x13 */ + "W: Library security has been compromised.", + /* 0x14 */ + "I: The library security mode has been changed.\n" + " The library has either been put into secure mode, or the library has exited\n" + " the secure mode.\n" + " This is for information purposes only. No action is required.", + /* 0x15 */ + "I: The library has been manually turned offline and is unavailable for use.", + /* 0x16 */ + "I: A drive inside the library has been taken offline.\n" + " This is for information purposes only. No action is required.", + /* 0x17 */ + "W: There is a potential problem with the bar code label or the scanner\n" + " hardware in the library mechanism.\n" + " 1. No action needs to be taken at this time.\n" + " 2. If the problem persists, call the library supplier help line.", + /* 0x18 */ + "C: The library has detected an inconsistency in its inventory.\n" + " 1. Redo the library inventory to correct inconsistency.\n" + " 2. Restart the operation.\n" + " Check the applications users manual or the hardware users manual for\n" + " specific instructions on redoing the library inventory.", + /* 0x19 */ + "W: A library operation has been attempted that is invalid at this time.", + /* 0x1a */ + "W: A redundant interface port on the library has failed.", + /* 0x1b */ + "W: A library cooling fan has failed.", + /* 0x1c */ + "W: A redundant power supply has failed inside the library. Check the\n" + " library users manual for instructions on replacing the failed power supply.", + /* 0x1d */ + "W: The library power consumption is outside the specified range.", + /* 0x1e */ + "C: A failure has occurred in the cartridge pass-through mechanism between\n" + " two library modules.", + /* 0x1f */ + "C: A cartridge has been left in the pass-through mechanism from a\n" + " previous hardware fault. Check the library users guide for instructions on\n" + " clearing this fault.", + /* 0x20 */ + "I: The library was unable to read the bar code on a cartridge.", +}; + +const char * scsiTapeAlertsChangerDevice(unsigned short code) +{ + const int num = sizeof(ChangerTapeAlertsMessageTable) / + sizeof(ChangerTapeAlertsMessageTable[0]); + + return (code < num) ? ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; +} + + +/* this is a subset of the SCSI additional sense code strings indexed + * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d) + */ +static const char * strs_for_asc_5d[] = { + /* 0x00 */ "FAILURE PREDICTION THRESHOLD EXCEEDED", + "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED", + "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED", + "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 0x10 */ "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS", + "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED", + "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT", + "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x20 */ "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS", + "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED", + "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE", + "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT", + "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x30 */ "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS", + "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED", + "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE", + "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT", + "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x40 */ "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS", + "SERVO IMPENDING FAILURE CONTROLLER DETECTED", + "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE", + "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT", + "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x50 */ "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS", + "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED", + "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT", + "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT", + "", + "", + "", + /* 0x60 */ "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE", + "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH", + "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS", + "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH", + "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH", + "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS", + "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED", + "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE", + "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE", + "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT", + /* 0x6c */ "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"}; + + +/* this is a subset of the SCSI additional sense code strings indexed + * * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb) + * */ +static const char * strs_for_asc_b[] = { + /* 0x00 */ "WARNING", + "WARNING - SPECIFIED TEMPERATURE EXCEEDED", + "WARNING - ENCLOSURE DEGRADED"}; + +static char spare_buff[128]; + +const char * scsiGetIEString(UINT8 asc, UINT8 ascq) +{ + const char * rp; + + if (SCSI_ASC_IMPENDING_FAILURE == asc) { + if (ascq == 0xff) + return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)"; + else if (ascq < + (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) { + rp = strs_for_asc_5d[ascq]; + if (strlen(rp) > 0) + return rp; + } + snprintf(spare_buff, sizeof(spare_buff), + "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq); + return spare_buff; + } else if (SCSI_ASC_WARNING == asc) { + if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) { + rp = strs_for_asc_b[ascq]; + if (strlen(rp) > 0) + return rp; + } + snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq); + return spare_buff; + } + return NULL; /* not a IE additional sense code */ +} + + +/* This is not documented in t10.org, page 0x80 is vendor specific */ +/* Some IBM disks do an offline read-scan when they get this command. */ +int scsiSmartIBMOfflineTest(int device) +{ + UINT8 tBuf[256]; + int res; + + memset(tBuf, 0, sizeof(tBuf)); + /* Build SMART Off-line Immediate Diag Header */ + tBuf[0] = 0x80; /* Page Code */ + tBuf[1] = 0x00; /* Reserved */ + tBuf[2] = 0x00; /* Page Length MSB */ + tBuf[3] = 0x04; /* Page Length LSB */ + tBuf[4] = 0x03; /* SMART Revision */ + tBuf[5] = 0x00; /* Reserved */ + tBuf[6] = 0x00; /* Off-line Immediate Time MSB */ + tBuf[7] = 0x00; /* Off-line Immediate Time LSB */ + res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8); + if (res) + pout("IBM offline test failed [%s]\n", scsiErrString(res)); + return res; +} + +int scsiSmartDefaultSelfTest(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0); + if (res) + pout("Default self test failed [%s]\n", scsiErrString(res)); + return res; +} + +int scsiSmartShortSelfTest(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0); + if (res) + pout("Short offline self test failed [%s]\n", scsiErrString(res)); + return res; +} + +int scsiSmartExtendSelfTest(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0); + if (res) + pout("Long (extended) offline self test failed [%s]\n", + scsiErrString(res)); + return res; +} + +int scsiSmartShortCapSelfTest(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0); + if (res) + pout("Short foreground self test failed [%s]\n", scsiErrString(res)); + return res; +} + +int scsiSmartExtendCapSelfTest(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0); + if (res) + pout("Long (extended) foreground self test failed [%s]\n", + scsiErrString(res)); + return res; +} + +int scsiSmartSelfTestAbort(int device) +{ + int res; + + res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0); + if (res) + pout("Abort self test failed [%s]\n", scsiErrString(res)); + return res; +} + +/* Returns 0 and the expected duration of an extended self test (in seconds) + if successful; any other return value indicates a failure. */ +int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len) +{ + int err, offset, res; + UINT8 buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return err; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return err; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if (offset < 0) + return -EINVAL; + if (buff[offset + 1] >= 0xa) { + res = (buff[offset + 10] << 8) | buff[offset + 11]; + *durationSec = res; + return 0; + } + else + return -EINVAL; +} + +void scsiDecodeErrCounterPage(unsigned char * resp, + struct scsiErrorCounter *ecp) +{ + int k, j, num, pl, pc; + unsigned char * ucp; + unsigned char * xp; + uint64_t * ullp; + + memset(ecp, 0, sizeof(*ecp)); + num = (resp[2] << 8) | resp[3]; + ucp = &resp[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + ecp->gotPC[pc] = 1; + ullp = &ecp->counter[pc]; + break; + default: + ecp->gotExtraPC = 1; + ullp = &ecp->counter[7]; + break; + } + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(*ullp)) { + xp += (k - sizeof(*ullp)); + k = sizeof(*ullp); + } + *ullp = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + *ullp <<= 8; + *ullp |= xp[j]; + } + num -= pl; + ucp += pl; + } +} + +void scsiDecodeNonMediumErrPage(unsigned char *resp, + struct scsiNonMediumError *nmep) +{ + int k, j, num, pl, pc, szof; + unsigned char * ucp; + unsigned char * xp; + + memset(nmep, 0, sizeof(*nmep)); + num = (resp[2] << 8) | resp[3]; + ucp = &resp[0] + 4; + szof = sizeof(nmep->counterPC0); + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: + nmep->gotPC0 = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterPC0 = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterPC0 <<= 8; + nmep->counterPC0 |= xp[j]; + } + break; + case 0x8009: + nmep->gotTFE_H = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterTFE_H = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterTFE_H <<= 8; + nmep->counterTFE_H |= xp[j]; + } + break; + case 0x8015: + nmep->gotPE_H = 1; + k = pl - 4; + xp = ucp + 4; + if (k > szof) { + xp += (k - szof); + k = szof; + } + nmep->counterPE_H = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + nmep->counterPE_H <<= 8; + nmep->counterPE_H |= xp[j]; + } + break; + default: + nmep->gotExtraPC = 1; + break; + } + num -= pl; + ucp += pl; + } +} + +/* Counts number of failed self-tests. Also encodes the poweron_hour + of the most recent failed self-test. Return value is negative if + this function has a problem (typically -1), otherwise the bottom 8 + bits are the number of failed self tests and the 16 bits above that + are the poweron hour of the most recent failure. Note: aborted self + tests (typically by the user) and self tests in progress are not + considered failures. See Working Draft SCSI Primary Commands - 3 + (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */ +int scsiCountFailedSelfTests(int fd, int noisy) +{ + int num, k, n, err, res, fails, fail_hour; + UINT8 * ucp; + unsigned char resp[LOG_RESP_SELF_TEST_LEN]; + + if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, + LOG_RESP_SELF_TEST_LEN, 0))) { + if (noisy) + pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err)); + return -1; + } + if (resp[0] != SELFTEST_RESULTS_LPAGE) { + if (noisy) + pout("Self-test Log Sense Failed, page mismatch\n"); + return -1; + } + // compute page length + num = (resp[2] << 8) + resp[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + if (noisy) + pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num); + return -1; + } + fails = 0; + fail_hour = 0; + // loop through the twenty possible entries + for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) { + + // timestamp in power-on hours (or zero if test in progress) + n = (ucp[6] << 8) | ucp[7]; + + // The spec says "all 20 bytes will be zero if no test" but + // DG has found otherwise. So this is a heuristic. + if ((0 == n) && (0 == ucp[4])) + break; + res = ucp[4] & 0xf; + if ((res > 2) && (res < 8)) { + fails++; + if (1 == fails) + fail_hour = (ucp[6] << 8) + ucp[7]; + } + } + return (fail_hour << 8) + fails; +} + +/* Returns 0 if able to read self test log page; then outputs 1 into + *inProgress if self test still in progress, else outputs 0. */ +int scsiSelfTestInProgress(int fd, int * inProgress) +{ + int num; + UINT8 * ucp; + unsigned char resp[LOG_RESP_SELF_TEST_LEN]; + + if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, + LOG_RESP_SELF_TEST_LEN, 0)) + return -1; + if (resp[0] != SELFTEST_RESULTS_LPAGE) + return -1; + // compute page length + num = (resp[2] << 8) + resp[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + return -1; + } + ucp = resp + 4; + if (inProgress) + *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0; + return 0; +} + +/* Returns a negative value if failed to fetch Contol mode page or it was + malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD + bit is set. Examines default mode page when current==0 else examines + current mode page. */ +int scsiFetchControlGLTSD(int device, int modese_len, int current) +{ + int err, offset; + UINT8 buff[64]; + int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, pc, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return -EINVAL; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, pc, + buff, sizeof(buff)); + if (err) + return -EINVAL; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset >= 0) && (buff[offset + 1] >= 0xa)) + return (buff[offset + 2] & 2) ? 1 : 0; + return -EINVAL; +} + +/* Attempts to set or clear GLTSD bit in Control mode page. If enabled is + 0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if + successful, negative if low level error, > 0 if higher level error (e.g. + SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */ +int scsiSetControlGLTSD(int device, int enabled, int modese_len) +{ + int err, offset, resp_len, sp; + UINT8 buff[64]; + UINT8 ch_buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return err; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return err; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset < 0) || (buff[offset + 1] < 0xa)) + return SIMPLE_ERR_BAD_RESP; + + if (enabled) + enabled = 2; + if (enabled == (buff[offset + 2] & 2)) + return 0; /* GLTSD already in wanted state so nothing to do */ + + if (modese_len == 6) + err = scsiModeSense(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + else + err = scsiModeSense10(device, CONTROL_MODE_PAGE, + MPAGE_CONTROL_CHANGEABLE, + ch_buff, sizeof(ch_buff)); + if (err) + return err; + if (0 == (ch_buff[offset + 2] & 2)) + return SIMPLE_ERR_BAD_PARAM; /* GLTSD bit not chageable */ + + if (10 == modese_len) { + resp_len = (buff[0] << 8) + buff[1] + 2; + 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 = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */ + if (enabled) + buff[offset + 2] |= 0x2; /* set GLTSD bit */ + else + buff[offset + 2] &= 0xfd; /* clear GLTSD bit */ + if (10 == modese_len) + err = scsiModeSelect10(device, sp, buff, resp_len); + else if (6 == modese_len) + err = scsiModeSelect(device, sp, buff, resp_len); + return err; +} + +/* Returns a negative value if failed to fetch Protocol specific port mode + page or it was malformed. Returns transport protocol identifier when + value >= 0 . */ +int scsiFetchTransportProtocol(int device, int modese_len) +{ + int err, offset; + UINT8 buff[64]; + + memset(buff, 0, sizeof(buff)); + if (modese_len <= 6) { + if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)))) { + if (SIMPLE_ERR_BAD_OPCODE == err) + modese_len = 10; + else + return -EINVAL; + } else if (0 == modese_len) + modese_len = 6; + } + if (10 == modese_len) { + err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, + MPAGE_CONTROL_CURRENT, + buff, sizeof(buff)); + if (err) + return -EINVAL; + } + offset = scsiModePageOffset(buff, sizeof(buff), modese_len); + if ((offset >= 0) && (buff[offset + 1] > 1)) { + if ((0 == (buff[offset] & 0x40)) && /* SPF==0 */ + (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) + return (buff[offset + 2] & 0xf); + } + return -EINVAL; +} + + +/* This is Linux specific code to look for the libata ATA-SCSI + simulator in the vendor device identification page. + Returns 1 if found else 0. */ +int isLinuxLibAta(unsigned char * vpd_di_buff, int len) +{ + int k, id_len, c_set, assoc, id_type, i_len; + unsigned char * ucp; + unsigned char * ip; + + if (len < 4) { + /* Device identification VPD page length too short */ + return 0; + } + len -= 4; + ucp = vpd_di_buff + 4; + for (k = 0; k < len; k += id_len, ucp += id_len) { + i_len = ucp[3]; + id_len = i_len + 4; + if ((k + id_len) > len) { + /* short descriptor, badly formed */ + return 0; + } + ip = ucp + 4; + c_set = (ucp[0] & 0xf); + assoc = ((ucp[1] >> 4) & 0x3); + id_type = (ucp[1] & 0xf); + if ((0 == id_type) && (2 == c_set) && (0 == assoc) && + (0 == strncmp((const char *)ip, + "Linux ATA-SCSI simulator", i_len))) { + return 1; + } + } + return 0; +} diff --git a/scsicmds.h b/scsicmds.h new file mode 100644 index 0000000..11b5087 --- /dev/null +++ b/scsicmds.h @@ -0,0 +1,355 @@ +/* + * scsicmds.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * Additional SCSI work: + * Copyright (C) 2003-6 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 + * 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/ + * + * N.B. What was formerly known as "SMART" are now called "informational + * exceptions" in recent t10.org drafts (i.e. recent SCSI). + * + */ + + +#ifndef SCSICMDS_H_ +#define SCSICMDS_H_ + +#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.57 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include +#include +#include +#include + +/* #define SCSI_DEBUG 1 */ /* Comment out to disable command debugging */ + +/* Following conditional defines bypass inclusion of scsi/scsi.h and + * scsi/scsi_ioctl.h . Issue will be resolved later ... */ +#ifndef TEST_UNIT_READY +#define TEST_UNIT_READY 0x0 +#endif +#ifndef LOG_SENSE +#define LOG_SENSE 0x4d +#endif +#ifndef MODE_SENSE +#define MODE_SENSE 0x1a +#endif +#ifndef MODE_SENSE_10 +#define MODE_SENSE_10 0x5a +#endif +#ifndef MODE_SELECT +#define MODE_SELECT 0x15 +#endif +#ifndef MODE_SELECT_10 +#define MODE_SELECT_10 0x55 +#endif +#ifndef INQUIRY +#define INQUIRY 0x12 +#endif +#ifndef REQUEST_SENSE +#define REQUEST_SENSE 0x03 +#endif +#ifndef RECEIVE_DIAGNOSTIC +#define RECEIVE_DIAGNOSTIC 0x1c +#endif +#ifndef SEND_DIAGNOSTIC +#define SEND_DIAGNOSTIC 0x1d +#endif +#ifndef READ_DEFECT_10 +#define READ_DEFECT_10 0x37 +#endif + +typedef unsigned char UINT8; +typedef char INT8; +typedef unsigned int UINT32; +typedef int INT32; + +#define DXFER_NONE 0 +#define DXFER_FROM_DEVICE 1 +#define DXFER_TO_DEVICE 2 + +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 + 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 + 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?) */ + size_t resp_sense_len; /* [out]: sense buffer length written */ + UINT8 scsi_status; /* [out]: 0->ok, 2->CHECK CONDITION, etc ... */ + int resid; /* [out]: Number of bytes requested to be transferred + less actual number transferred (0 if not + supported) */ +}; + +struct scsi_sense_disect { + UINT8 error_code; + UINT8 sense_key; + UINT8 asc; + UINT8 ascq; +}; + +/* Useful data from Informational Exception Control mode page (0x1c) */ +#define SCSI_IECMP_RAW_LEN 64 +struct scsi_iec_mode_page { + UINT8 requestedCurrent; + UINT8 gotCurrent; + UINT8 requestedChangeable; + UINT8 gotChangeable; + UINT8 modese_len; /* 0 (don't know), 6 or 10 */ + UINT8 raw_curr[SCSI_IECMP_RAW_LEN]; + UINT8 raw_chg[SCSI_IECMP_RAW_LEN]; +}; + +/* Carrier for Error counter log pages (e.g. read, write, verify ...) */ +struct scsiErrorCounter { + UINT8 gotPC[7]; + UINT8 gotExtraPC; + uint64_t counter[8]; +}; + +/* Carrier for Non-medium error log page */ +struct scsiNonMediumError { + UINT8 gotPC0; + UINT8 gotExtraPC; + uint64_t counterPC0; + UINT8 gotTFE_H; + uint64_t counterTFE_H; /* Track following errors [Hitachi] */ + UINT8 gotPE_H; + uint64_t counterPE_H; /* Positioning errors [Hitachi] */ +}; + +/* SCSI Peripheral types (of interest) */ +#define SCSI_PT_DIRECT_ACCESS 0x0 +#define SCSI_PT_SEQUENTIAL_ACCESS 0x1 +#define SCSI_PT_CDROM 0x5 +#define SCSI_PT_MEDIUM_CHANGER 0x8 +#define SCSI_PT_ENCLOSURE 0xd + +/* ANSI SCSI-3 Log Pages retrieved by LOG SENSE. */ +#define SUPPORTED_LPAGES 0x00 +#define BUFFER_OVERRUN_LPAGE 0x01 +#define WRITE_ERROR_COUNTER_LPAGE 0x02 +#define READ_ERROR_COUNTER_LPAGE 0x03 +#define READ_REVERSE_ERROR_COUNTER_LPAGE 0x04 +#define VERIFY_ERROR_COUNTER_LPAGE 0x05 +#define NON_MEDIUM_ERROR_LPAGE 0x06 +#define LAST_N_ERROR_LPAGE 0x07 +#define FORMAT_STATUS_LPAGE 0x08 +#define TEMPERATURE_LPAGE 0x0d +#define STARTSTOP_CYCLE_COUNTER_LPAGE 0x0e +#define APPLICATION_CLIENT_LPAGE 0x0f +#define SELFTEST_RESULTS_LPAGE 0x10 +#define IE_LPAGE 0x2f + +/* Seagate vendor specific log pages. */ +#define SEAGATE_CACHE_LPAGE 0x37 +#define SEAGATE_FACTORY_LPAGE 0x3e + +/* Log page response lengths */ +#define LOG_RESP_SELF_TEST_LEN 0x194 + +/* 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 + +/* ANSI SCSI-3 Mode Pages */ +#define VENDOR_UNIQUE_PAGE 0x00 +#define READ_WRITE_ERROR_RECOVERY_PAGE 0x01 +#define DISCONNECT_RECONNECT_PAGE 0x02 +#define FORMAT_DEVICE_PAGE 0x03 +#define RIGID_DISK_DRIVE_GEOMETRY_PAGE 0x04 +#define FLEXIBLE_DISK_PAGE 0x05 +#define VERIFY_ERROR_RECOVERY_PAGE 0x07 +#define CACHING_PAGE 0x08 +#define PERIPHERAL_DEVICE_PAGE 0x09 +#define XOR_CONTROL_MODE_PAGE 0x10 +#define CONTROL_MODE_PAGE 0x0a +#define MEDIUM_TYPES_SUPPORTED_PAGE 0x0b +#define NOTCH_PAGE 0x0c +#define CD_DEVICE_PAGE 0x0d +#define CD_AUDIO_CONTROL_PAGE 0x0e +#define DATA_COMPRESSION_PAGE 0x0f +#define ENCLOSURE_SERVICES_MANAGEMENT_PAGE 0x14 +#define PROTOCOL_SPECIFIC_LUN_PAGE 0x18 +#define PROTOCOL_SPECIFIC_PORT_PAGE 0x19 +#define POWER_CONDITION_PAGE 0x1a +#define INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE 0x1c +#define FAULT_FAILURE_REPORTING_PAGE 0x1c + +#define ALL_MODE_PAGES 0x3f + +/* Mode page control field */ +#define MPAGE_CONTROL_CURRENT 0 +#define MPAGE_CONTROL_CHANGEABLE 1 +#define MPAGE_CONTROL_DEFAULT 2 +#define MPAGE_CONTROL_SAVED 3 + +/* defines for useful SCSI Status codes */ +#define SCSI_STATUS_CHECK_CONDITION 0x2 + +/* defines for useful Sense Key codes */ +#define SCSI_SK_NOT_READY 0x2 +#define SCSI_SK_MEDIUM_ERROR 0x3 +#define SCSI_SK_HARDWARE_ERROR 0x4 +#define SCSI_SK_ILLEGAL_REQUEST 0x5 +#define SCSI_SK_UNIT_ATTENTION 0x6 + +/* defines for useful Additional Sense Codes (ASCs) */ +#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_UNKNOWN_PARAM 0x26 +#define SCSI_ASC_WARNING 0xb +#define SCSI_ASC_IMPENDING_FAILURE 0x5d + +/* Simplified error code (negative values as per errno) */ +#define SIMPLE_NO_ERROR 0 +#define SIMPLE_ERR_NOT_READY 1 +#define SIMPLE_ERR_BAD_OPCODE 2 +#define SIMPLE_ERR_BAD_FIELD 3 /* in cbd */ +#define SIMPLE_ERR_BAD_PARAM 4 /* in data */ +#define SIMPLE_ERR_BAD_RESP 5 /* response fails sanity */ +#define SIMPLE_ERR_NO_MEDIUM 6 /* no medium present */ +#define SIMPLE_ERR_BECOMING_READY 7 /* device will be ready soon */ +#define SIMPLE_ERR_TRY_AGAIN 8 /* some warning, try again */ +#define SIMPLE_ERR_MEDIUM_HARDWARE 9 /* medium or hardware error */ + + +/* defines for functioncode parameter in SENDDIAGNOSTIC function */ +#define SCSI_DIAG_NO_SELF_TEST 0x00 +#define SCSI_DIAG_DEF_SELF_TEST 0xff +#define SCSI_DIAG_BG_SHORT_SELF_TEST 0x01 +#define SCSI_DIAG_BG_EXTENDED_SELF_TEST 0x02 +#define SCSI_DIAG_FG_SHORT_SELF_TEST 0x05 +#define SCSI_DIAG_FG_EXTENDED_SELF_TEST 0x06 +#define SCSI_DIAG_ABORT_SELF_TEST 0x04 + + +/* SCSI command timeout values (units are seconds) */ +#define SCSI_TIMEOUT_DEFAULT 6 /* 6 seconds should be ample */ +#define SCSI_TIMEOUT_SELF_TEST (5 * 60 * 60) /* allow max 5 hours for */ + /* extended foreground self test */ + + + +#define LOGPAGEHDRSIZE 4 + +void scsi_do_sense_disect(const struct scsi_cmnd_io * in, + struct scsi_sense_disect * out); + +const char * scsiErrString(int scsiErr); + +/* STANDARD SCSI Commands */ +int scsiTestUnitReady(int device); + +int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen); + +int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen); + +int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen, + int known_resp_len); + +int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen); + +int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen); + +int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen); + +int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen); + +int scsiModePageOffset(const UINT8 * resp, int len, int modese_len); + +int scsiRequestSense(int device, struct scsi_sense_disect * sense_info); + +int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen); + +int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, + int bufLen); + +int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format, + UINT8 *pBuf, int bufLen); + +/* SMART specific commands */ +int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage, UINT8 *asc, + UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp); + +int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, + int modese_len); +int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp); +int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp); +int scsiSetExceptionControlAndWarning(int device, int enabled, + const struct scsi_iec_mode_page *iecp); +void scsiDecodeErrCounterPage(unsigned char * resp, + struct scsiErrorCounter *ecp); +void scsiDecodeNonMediumErrPage(unsigned char * resp, + struct scsiNonMediumError *nmep); +int scsiFetchExtendedSelfTestTime(int device, int * durationSec, + int modese_len); +int scsiCountFailedSelfTests(int fd, int noisy); +int scsiSelfTestInProgress(int fd, int * inProgress); +int scsiFetchControlGLTSD(int device, int modese_len, int current); +int scsiSetControlGLTSD(int device, int enabled, int modese_len); +int scsiFetchTransportProtocol(int device, int modese_len); + +/* T10 Standard IE Additional Sense Code strings taken from t10.org */ + +const char* scsiGetIEString(UINT8 asc, UINT8 ascq); +int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp); + + +int scsiSmartIBMOfflineTest(int device); + +int scsiSmartDefaultSelfTest(int device); +int scsiSmartShortSelfTest(int device); +int scsiSmartExtendSelfTest(int device); +int scsiSmartShortCapSelfTest(int device); +int scsiSmartExtendCapSelfTest(int device); +int scsiSmartSelfTestAbort(int device); + +const char * scsiTapeAlertsTapeDevice(unsigned short code); +const char * scsiTapeAlertsChangerDevice(unsigned short code); + +const char * scsi_get_opcode_name(UINT8 opcode); +void dStrHex(const char* str, int len, int no_ascii); + +/* SCSI command transmission interface function declaration. Its + * definition is target OS specific (see os_.c file). + * Returns 0 if SCSI command successfully launched and response + * received. Even when 0 is returned the caller should check + * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings + * (e.g. CHECK CONDITION). If the SCSI command could not be issued + * (e.g. device not present or not a SCSI device) or some other problem + * arises (e.g. timeout) then returns a negative errno value. */ +int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); + + +/* This is Linux specific and will be generalized later. */ +int isLinuxLibAta(unsigned char * vpd_di_buff, int len); + +#endif + diff --git a/scsiprint.c b/scsiprint.c new file mode 100644 index 0000000..0b7c70b --- /dev/null +++ b/scsiprint.c @@ -0,0 +1,1233 @@ +/* + * scsiprint.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * Additional SCSI work: + * Copyright (C) 2003-6 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + + +#include +#include +#include +#include + +#include "config.h" +#include "int64.h" +#include "extern.h" +#include "scsicmds.h" +#include "scsiprint.h" +#include "smartctl.h" +#include "utility.h" + +#define GBUF_SIZE 65535 + +const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.107 2006/04/12 16:18:57 ballen4705 Exp $" +CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; + +// control block which points to external global control variables +extern smartmonctrl *con; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +UINT8 gBuf[GBUF_SIZE]; +#define LOG_RESP_LEN 252 +#define LOG_RESP_LONG_LEN 8192 +#define LOG_RESP_TAPE_ALERT_LEN 0x144 + +/* Log pages supported */ +static int gSmartLPage = 0; /* Informational Exceptions log page */ +static int gTempLPage = 0; +static int gSelfTestLPage = 0; +static int gStartStopLPage = 0; +static int gReadECounterLPage = 0; +static int gWriteECounterLPage = 0; +static int gVerifyECounterLPage = 0; +static int gNonMediumELPage = 0; +static int gLastNErrorLPage = 0; +static int gTapeAlertsLPage = 0; +static int gSeagateCacheLPage = 0; +static int gSeagateFactoryLPage = 0; + +/* Mode pages supported */ +static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ + +/* Remember last successful mode sense/select command */ +static int modese_len = 0; + +// Compares failure type to policy in effect, and either exits or +// simply returns to the calling routine. +extern void failuretest(int type, int returnvalue); + +static void scsiGetSupportedLogPages(int device) +{ + int i, err; + + if ((err = scsiLogSense(device, SUPPORTED_LPAGES, gBuf, + LOG_RESP_LEN, 0))) { + if (con->reportscsiioctl > 0) + pout("Log Sense for supported pages failed [%s]\n", + scsiErrString(err)); + return; + } + + for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) { + switch (gBuf[i]) + { + case READ_ERROR_COUNTER_LPAGE: + gReadECounterLPage = 1; + break; + case WRITE_ERROR_COUNTER_LPAGE: + gWriteECounterLPage = 1; + break; + case VERIFY_ERROR_COUNTER_LPAGE: + gVerifyECounterLPage = 1; + break; + case LAST_N_ERROR_LPAGE: + gLastNErrorLPage = 1; + break; + case NON_MEDIUM_ERROR_LPAGE: + gNonMediumELPage = 1; + break; + case TEMPERATURE_LPAGE: + gTempLPage = 1; + break; + case STARTSTOP_CYCLE_COUNTER_LPAGE: + gStartStopLPage = 1; + break; + case SELFTEST_RESULTS_LPAGE: + gSelfTestLPage = 1; + break; + case IE_LPAGE: + gSmartLPage = 1; + break; + case TAPE_ALERTS_LPAGE: + gTapeAlertsLPage = 1; + break; + case SEAGATE_CACHE_LPAGE: + gSeagateCacheLPage = 1; + break; + case SEAGATE_FACTORY_LPAGE: + gSeagateFactoryLPage = 1; + break; + default: + break; + } + } +} + +/* 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(int device, int attribs) +{ + UINT8 asc; + UINT8 ascq; + UINT8 currenttemp = 0; + UINT8 triptemp = 0; + const char * cp; + int err = 0; + + PRINT_ON(con); + if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, + ¤ttemp, &triptemp)) { + /* error message already announced */ + PRINT_OFF(con); + return -1; + } + PRINT_OFF(con); + cp = scsiGetIEString(asc, ascq); + if (cp) { + err = -2; + PRINT_ON(con); + pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); + PRINT_OFF(con); + } else if (gIecMPage) + pout("SMART Health Status: OK\n"); + + if (attribs && !gTempLPage) { + if (currenttemp || triptemp) + pout("\n"); + if (currenttemp) { + if (255 != currenttemp) + pout("Current Drive Temperature: %d C\n", currenttemp); + else + pout("Current Drive Temperature: \n"); + } + if (triptemp) + pout("Drive Trip Temperature: %d C\n", triptemp); + } + return err; +} + + +// Returns number of logged errors or zero if none or -1 if fetching +// TapeAlerts fails +static char *severities = "CWI"; + +static int scsiGetTapeAlertsData(int device, int peripheral_type) +{ + unsigned short pagelength; + unsigned short parametercode; + int i, err; + char *s; + const char *ts; + int failures = 0; + + PRINT_ON(con); + if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, gBuf, + LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { + pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return -1; + } + if (gBuf[0] != 0x2e) { + pout("TapeAlerts Log Sense Failed\n"); + PRINT_OFF(con); + return -1; + } + pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3]; + + for (s=severities; *s; s++) { + for (i = 4; i < pagelength; i += 5) { + parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1]; + + if (gBuf[i + 4]) { + ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ? + scsiTapeAlertsChangerDevice(parametercode) : + scsiTapeAlertsTapeDevice(parametercode); + if (*ts == *s) { + if (!failures) + pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n"); + pout("[0x%02x] %s\n", parametercode, ts); + failures += 1; + } + } + } + } + PRINT_OFF(con); + + if (! failures) + pout("TapeAlert: OK\n"); + + return failures; +} + +static void scsiGetStartStopData(int device) +{ + UINT32 currentStartStop; + UINT32 recommendedStartStop; + int err, len, k; + char str[6]; + + if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_LPAGE) { + PRINT_ON(con); + pout("StartStop Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return; + } + len = ((gBuf[2] << 8) | gBuf[3]) + 4; + if (len > 13) { + for (k = 0; k < 2; ++k) + str[k] = gBuf[12 + k]; + str[k] = '\0'; + pout("Manufactured in week %s of year ", str); + for (k = 0; k < 4; ++k) + str[k] = gBuf[8 + k]; + str[k] = '\0'; + pout("%s\n", str); + } + if (len > 39) { + recommendedStartStop = (gBuf[28] << 24) | (gBuf[29] << 16) | + (gBuf[30] << 8) | gBuf[31]; + currentStartStop = (gBuf[36] << 24) | (gBuf[37] << 16) | + (gBuf[38] << 8) | gBuf[39]; + pout("Current start stop count: %u times\n", currentStartStop); + if (0xffffffff != recommendedStartStop) + pout("Recommended maximum start stop count: %u times\n", + recommendedStartStop); + } +} + +static void scsiPrintGrownDefectListLen(int device) +{ + int err, dl_format, dl_len, div; + + memset(gBuf, 0, 4); + if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, + 4 /* bytes from index */, gBuf, 4))) { + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("Read defect list (10) Failed: %s\n", scsiErrString(err)); + PRINT_OFF(con); + } + return; + } + if (0x8 != (gBuf[1] & 0x18)) { + PRINT_ON(con); + pout("Read defect list: asked for grown list but didn't get it\n"); + PRINT_OFF(con); + return; + } + div = 0; + dl_format = (gBuf[1] & 0x7); + switch (dl_format) { + case 0: /* short block */ + div = 4; + break; + case 3: /* long block */ + case 4: /* bytes from index */ + case 5: /* physical sector */ + div = 8; + break; + default: + PRINT_ON(con); + pout("defect list format %d unknown\n", dl_format); + PRINT_OFF(con); + break; + } + dl_len = (gBuf[2] << 8) + gBuf[3]; + if (0 == dl_len) + pout("Elements in grown defect list: 0\n"); + else { + if (0 == div) + pout("Grown defect list length=%d bytes [unknown " + "number of elements]\n", dl_len); + else + pout("Elements in grown defect list: %d\n", dl_len / div); + } +} + +static void scsiPrintSeagateCacheLPage(int device) +{ + int k, j, num, pl, pc, err, len; + unsigned char * ucp; + unsigned char * xp; + uint64_t ull; + + if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != SEAGATE_CACHE_LPAGE) { + PRINT_ON(con); + pout("Seagate Cache Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return; + } + len = ((gBuf[2] << 8) | gBuf[3]) + 4; + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: case 1: case 2: case 3: case 4: + break; + default: + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("Vendor (Seagate) cache lpage has unexpected parameter" + ", skip\n"); + PRINT_OFF(con); + } + return; + } + num -= pl; + ucp += pl; + } + pout("Vendor (Seagate) cache information\n"); + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: pout(" Blocks sent to initiator"); break; + case 1: pout(" Blocks received from initiator"); break; + case 2: pout(" Blocks read from cache and sent to initiator"); break; + case 3: pout(" Number of read and write commands whose size " + "<= segment size"); break; + case 4: pout(" Number of read and write commands whose size " + "> segment size"); break; + default: pout(" Unknown Seagate parameter code [0x%x]", pc); break; + } + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(ull)) { + xp += (k - (int)sizeof(ull)); + k = (int)sizeof(ull); + } + ull = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + ull <<= 8; + ull |= xp[j]; + } + pout(" = %"PRIu64"\n", ull); + num -= pl; + ucp += pl; + } +} + +static void scsiPrintSeagateFactoryLPage(int device) +{ + int k, j, num, pl, pc, len, err, good, bad; + unsigned char * ucp; + unsigned char * xp; + uint64_t ull; + + if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, gBuf, + LOG_RESP_LEN, 0))) { + PRINT_ON(con); + pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return; + } + if (gBuf[0] != SEAGATE_FACTORY_LPAGE) { + PRINT_ON(con); + pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return; + } + len = ((gBuf[2] << 8) | gBuf[3]) + 4; + num = len - 4; + ucp = &gBuf[0] + 4; + good = 0; + bad = 0; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + switch (pc) { + case 0: case 8: + ++good; + break; + default: + ++bad; + break; + } + num -= pl; + ucp += pl; + } + if ((good < 2) || (bad > 4)) { /* heuristic */ + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("\nVendor (Seagate/Hitachi) factory lpage has too many " + "unexpected parameters, skip\n"); + PRINT_OFF(con); + } + return; + } + pout("Vendor (Seagate/Hitachi) factory information\n"); + num = len - 4; + ucp = &gBuf[0] + 4; + while (num > 3) { + pc = (ucp[0] << 8) | ucp[1]; + pl = ucp[3] + 4; + good = 0; + switch (pc) { + case 0: pout(" number of hours powered up"); + good = 1; + break; + case 8: pout(" number of minutes until next internal SMART test"); + good = 1; + break; + default: + if (con->reportscsiioctl > 0) { + PRINT_ON(con); + pout("Vendor (Seagate/Hitachi) factory lpage: " + "unknown parameter code [0x%x]\n", pc); + PRINT_OFF(con); + } + break; + } + if (good) { + k = pl - 4; + xp = ucp + 4; + if (k > (int)sizeof(ull)) { + xp += (k - (int)sizeof(ull)); + k = (int)sizeof(ull); + } + ull = 0; + for (j = 0; j < k; ++j) { + if (j > 0) + ull <<= 8; + ull |= xp[j]; + } + if (0 == pc) + pout(" = %.2f\n", uint64_to_double(ull) / 60.0 ); + else + pout(" = %"PRIu64"\n", ull); + } + num -= pl; + ucp += pl; + } +} + +static void scsiPrintErrorCounterLog(int device) +{ + struct scsiErrorCounter errCounterArr[3]; + struct scsiErrorCounter * ecp; + struct scsiNonMediumError nme; + int found[3] = {0, 0, 0}; + const char * pageNames[3] = {"read: ", "write: ", "verify: "}; + int k; + double processed_gb; + + if (gReadECounterLPage && (0 == scsiLogSense(device, + READ_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) { + scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); + found[0] = 1; + } + if (gWriteECounterLPage && (0 == scsiLogSense(device, + WRITE_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) { + scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); + found[1] = 1; + } + if (gVerifyECounterLPage && (0 == scsiLogSense(device, + VERIFY_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) { + scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]); + ecp = &errCounterArr[2]; + for (k = 0; k < 7; ++k) { + if (ecp->gotPC[k] && ecp->counter[k]) { + found[2] = 1; + break; + } + } + } + if (found[0] || found[1] || found[2]) { + pout("\nError counter log:\n"); + pout(" Errors Corrected by Total " + "Correction Gigabytes Total\n"); + pout(" ECC rereads/ errors " + "algorithm processed uncorrected\n"); + pout(" fast | delayed rewrites corrected " + "invocations [10^9 bytes] errors\n"); + for (k = 0; k < 3; ++k) { + if (! found[k]) + continue; + ecp = &errCounterArr[k]; + pout("%s%8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64" %8"PRIu64, + pageNames[k], ecp->counter[0], ecp->counter[1], + ecp->counter[2], ecp->counter[3], ecp->counter[4]); + processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0; + pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]); + } + } + else + pout("\nError Counter logging not supported\n"); + if (gNonMediumELPage && (0 == scsiLogSense(device, + NON_MEDIUM_ERROR_LPAGE, gBuf, LOG_RESP_LEN, 0))) { + scsiDecodeNonMediumErrPage(gBuf, &nme); + if (nme.gotPC0) + pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0); + if (nme.gotTFE_H) + pout("Track following error count [Hitachi]: %8"PRIu64"\n", + nme.counterTFE_H); + if (nme.gotPE_H) + pout("Positioning error count [Hitachi]: %8"PRIu64"\n", + nme.counterPE_H); + } + if (gLastNErrorLPage && (0 == scsiLogSense(device, + LAST_N_ERROR_LPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) { + unsigned char * ucp; + int num, k, pc, pl; + + num = (gBuf[2] << 8) + gBuf[3] + 4; + num = (num < LOG_RESP_LONG_LEN) ? num : LOG_RESP_LONG_LEN; + ucp = gBuf + 4; + num -= 4; + if (num < 4) + pout("\nNo error events logged\n"); + else { + pout("\nLast n error events log page\n"); + for (k = num; k > 0; k -= pl, ucp += pl) { + if (k < 3) { + pout(" <>\n"); + break; + } + pl = ucp[3] + 4; + pc = (ucp[0] << 8) + ucp[1]; + if (pl > 4) { + if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { + pout(" Error event %d:\n", pc); + pout(" [binary]:\n"); + dStrHex((const char *)ucp + 4, pl - 4, 1); + } else if (ucp[2] & 0x1) { + pout(" Error event %d:\n", pc); + pout(" %.*s\n", pl - 4, (const char *)(ucp + 4)); + } else { + if (con->reportscsiioctl > 0) { + pout(" Error event %d:\n", pc); + pout(" [data counter??]:\n"); + dStrHex((const char *)ucp + 4, pl - 4, 1); + } + } + } + } + } + } +} + +static const char * self_test_code[] = { + "Default ", + "Background short", + "Background long ", + "Reserved(3) ", + "Abort background", + "Foreground short", + "Foreground long ", + "Reserved(7) " +}; + +static const char * self_test_result[] = { + "Completed ", + "Interrupted ('-X' switch)", + "Interrupted (bus reset ?)", + "Unknown error, incomplete", + "Completed, segment failed", + "Failed in first segment ", + "Failed in second segment ", + "Failed in segment --> ", + "Reserved(8) ", + "Reserved(9) ", + "Reserved(10) ", + "Reserved(11) ", + "Reserved(12) ", + "Reserved(13) ", + "Reserved(14) ", + "Self test in progress ..." +}; + +// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 . +// 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(int device) +{ + int num, k, n, res, err, durationSec; + int noheader = 1; + int retval = 0; + UINT8 * ucp; + uint64_t ull=0; + + if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, gBuf, + LOG_RESP_SELF_TEST_LEN, 0))) { + PRINT_ON(con); + pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + return FAILSMART; + } + if (gBuf[0] != SELFTEST_RESULTS_LPAGE) { + PRINT_ON(con); + pout("Self-test Log Sense Failed, page mismatch\n"); + PRINT_OFF(con); + return FAILSMART; + } + // compute page length + num = (gBuf[2] << 8) + gBuf[3]; + // Log sense page length 0x190 bytes + if (num != 0x190) { + PRINT_ON(con); + pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num); + PRINT_OFF(con); + return FAILSMART; + } + // loop through the twenty possible entries + for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) { + int i; + + // timestamp in power-on hours (or zero if test in progress) + n = (ucp[6] << 8) | ucp[7]; + + // The spec says "all 20 bytes will be zero if no test" but + // DG has found otherwise. So this is a heuristic. + if ((0 == n) && (0 == ucp[4])) + break; + + // only print header if needed + if (noheader) { + pout("\nSMART Self-test log\n"); + pout("Num Test Status segment " + "LifeTime LBA_first_err [SK ASC ASQ]\n"); + pout(" Description number " + "(hours)\n"); + noheader=0; + } + + // print parameter code (test number) & self-test code text + pout("#%2d %s", (ucp[0] << 8) | ucp[1], + self_test_code[(ucp[4] >> 5) & 0x7]); + + // check the self-test result nibble, using the self-test results + // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10: + switch ((res = ucp[4] & 0xf)) { + case 0x3: + // an unknown error occurred while the device server + // was processing the self-test and the device server + // was unable to complete the self-test + retval|=FAILSMART; + break; + case 0x4: + // the self-test completed with a failure in a test + // segment, and the test segment that failed is not + // known + retval|=FAILLOG; + break; + case 0x5: + // the first segment of the self-test failed + retval|=FAILLOG; + break; + case 0x6: + // the second segment of the self-test failed + retval|=FAILLOG; + break; + case 0x7: + // another segment of the self-test failed and which + // test is indicated by the contents of the SELF-TEST + // NUMBER field + retval|=FAILLOG; + break; + default: + break; + } + pout(" %s", self_test_result[res]); + + // self-test number identifies test that failed and consists + // of either the number of the segment that failed during + // the test, or the number of the test that failed and the + // number of the segment in which the test was run, using a + // vendor-specific method of putting both numbers into a + // single byte. + if (ucp[5]) + pout(" %3d", (int)ucp[5]); + else + pout(" -"); + + // print time that the self-test was completed + if (n==0 && res==0xf) + // self-test in progress + pout(" NOW"); + else + pout(" %5d", n); + + // construct 8-byte integer address of first failure + for (i = 0; i < 8; i++) { + ull <<= 8; + ull |= ucp[i+8]; + } + // print Address of First Failure, if sensible + if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) { + char buff[32]; + + // was hex but change to decimal to conform with ATA + snprintf(buff, sizeof(buff), "%"PRIu64, ull); + // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull); + pout("%18s", buff); + } else + pout(" -"); + + // if sense key nonzero, then print it, along with + // additional sense code and additional sense code qualifier + if (ucp[16] & 0xf) + pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]); + else + pout(" [- - -]\n"); + } + + // if header never printed, then there was no output + if (noheader) + pout("No self-tests have been logged\n"); + else + pout("\n"); + if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, + modese_len)) && (durationSec > 0)) { + pout("Long (extended) Self Test duration: %d seconds " + "[%.1f minutes]\n", durationSec, durationSec / 60.0); + } + return retval; +} + +static const char * peripheral_dt_arr[] = { + "disk", + "tape", + "printer", + "processor", + "optical disk(4)", + "CD/DVD", + "scanner", + "optical disk(7)", + "medium changer", + "communications", + "graphics(10)", + "graphics(11)", + "storage array", + "enclosure", + "simplified disk", + "optical card reader" +}; + +static const char * transport_proto_arr[] = { + "Fibre channel (FCP-2)", + "Parallel SCSI (SPI-4)", + "SSA", + "IEEE 1394 (SBP-2)", + "RDMA (SRP)", + "iSCSI", + "SAS", + "ADT", + "0x8", + "0x9", + "0xa", + "0xb", + "0xc", + "0xd", + "0xe", + "0xf" +}; + +/* Returns 0 on success, 1 on general error and 2 for early, clean exit */ +static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) +{ + char manufacturer[9]; + char product[17]; + char revision[5]; + char timedatetz[DATEANDEPOCHLEN]; + struct scsi_iec_mode_page iec; + int err, iec_err, len, req_len, avail_len, val; + int is_tape = 0; + int peri_dt = 0; + int returnval=0; + + memset(gBuf, 0, 96); + req_len = 36; + if ((err = scsiStdInquiry(device, gBuf, req_len))) { + PRINT_ON(con); + pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); + pout("Retrying with a 64 byte Standard Inquiry\n"); + PRINT_OFF(con); + /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ + req_len = 64; + if ((err = scsiStdInquiry(device, gBuf, req_len))) { + PRINT_ON(con); + pout("Standard Inquiry (64 bytes) failed [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + } + avail_len = gBuf[4] + 5; + len = (avail_len < req_len) ? avail_len : req_len; + peri_dt = gBuf[0] & 0x1f; + if (peripheral_type) + *peripheral_type = peri_dt; + if (! all) + return 0; + + if (len < 36) { + PRINT_ON(con); + pout("Short INQUIRY response, skip product id\n"); + PRINT_OFF(con); + return 1; + } + memset(manufacturer, 0, sizeof(manufacturer)); + strncpy(manufacturer, (char *)&gBuf[8], 8); + + memset(product, 0, sizeof(product)); + strncpy(product, (char *)&gBuf[16], 16); + + memset(revision, 0, sizeof(revision)); + strncpy(revision, (char *)&gBuf[32], 4); + pout("Device: %s %s Version: %s\n", manufacturer, product, revision); + + if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) { + pout("please try adding '-d 3ware,N'\n"); + pout("you may also need to change device to /dev/twaN or /dev/tweN\n"); + return 2; + } else if ((len >= 42) && + (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) { + pout("please try '-d marvell'\n"); + return 2; + } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) { + /* <<<< This is Linux specific code to detect SATA disks using a + SCSI-ATA command translation layer. This may be generalized + later when the t10.org SAT project matures. >>>> */ + req_len = 96; + memset(gBuf, 0, req_len); + if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) { + PRINT_ON(con); + pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4; + len = (avail_len < req_len) ? avail_len : req_len; + if (isLinuxLibAta(gBuf, len)) { + pout("\nIn Linux, SATA disks accessed via libata are " + "only supported by smartmontools\n" + "for kernel versions 2.6.15 and above. Try " + "an additional '-d ata' argument.\n"); + return 2; + } + } + + /* Do this here to try and detect badly conforming devices (some USB + keys) that will lock up on a InquiryVpd or log sense or ... */ + if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) { + if (SIMPLE_ERR_BAD_RESP == iec_err) { + pout(">> Terminate command early due to bad response to IEC " + "mode page\n"); + PRINT_OFF(con); + gIecMPage = 0; + return 1; + } + } else + modese_len = iec.modese_len; + + if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) { + /* should use VPD page 0x83 and fall back to this page (0x80) + * if 0x83 not supported. NAA requires a lot of decoding code */ + len = gBuf[3]; + gBuf[4 + len] = '\0'; + pout("Serial number: %s\n", &gBuf[4]); + } + else if (con->reportscsiioctl > 0) { + PRINT_ON(con); + if (SIMPLE_ERR_BAD_RESP == err) + pout("Vital Product Data (VPD) bit ignored in INQUIRY\n"); + else + pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); + PRINT_OFF(con); + } + + // print SCSI peripheral device type + if (peri_dt < (int)(sizeof(peripheral_dt_arr) / + sizeof(peripheral_dt_arr[0]))) + pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); + else + pout("Device type: <%d>\n", peri_dt); + + // See if transport protocol is known + val = scsiFetchTransportProtocol(device, modese_len); + if ((val >= 0) && (val <= 0xf)) + pout("Transport protocol: %s\n", transport_proto_arr[val]); + + // print current time and date and timezone + dateandtimezone(timedatetz); + pout("Local Time is: %s\n", timedatetz); + + if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) || + (SCSI_PT_MEDIUM_CHANGER == *peripheral_type)) + is_tape = 1; + // See if unit accepts SCSI commmands from us + if ((err = scsiTestUnitReady(device))) { + if (SIMPLE_ERR_NOT_READY == err) { + PRINT_ON(con); + if (!is_tape) + pout("device is NOT READY (e.g. spun down, busy)\n"); + else + pout("device is NOT READY (e.g. no tape)\n"); + PRINT_OFF(con); + } else if (SIMPLE_ERR_NO_MEDIUM == err) { + PRINT_ON(con); + pout("NO MEDIUM present on device\n"); + PRINT_OFF(con); + } else if (SIMPLE_ERR_BECOMING_READY == err) { + PRINT_ON(con); + pout("device becoming ready (wait)\n"); + PRINT_OFF(con); + } else { + PRINT_ON(con); + pout("device Test Unit Ready [%s]\n", scsiErrString(err)); + PRINT_OFF(con); + } + failuretest(MANDATORY_CMD, returnval|=FAILID); + } + + if (iec_err) { + if (!is_tape) { + PRINT_ON(con); + pout("Device does not support SMART"); + if (con->reportscsiioctl > 0) + pout(" [%s]\n", scsiErrString(iec_err)); + else + pout("\n"); + PRINT_OFF(con); + } + gIecMPage = 0; + return 0; + } + + if (!is_tape) + pout("Device supports SMART and is %s\n", + (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled"); + pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? + "Temperature Warning Enabled" : + "Temperature Warning Disabled or Not Supported"); + return 0; +} + +static int scsiSmartEnable(int device) +{ + struct scsi_iec_mode_page iec; + int err; + + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + PRINT_ON(con); + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } else + modese_len = iec.modese_len; + + if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { + PRINT_ON(con); + pout("unable to enable Exception control and warning [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + /* Need to refetch 'iec' since could be modified by previous call */ + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + return 1; + } else + modese_len = iec.modese_len; + + pout("Informational Exceptions (SMART) %s\n", + scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); + pout("Temperature warning %s\n", + scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); + return 0; +} + +static int scsiSmartDisable(int device) +{ + struct scsi_iec_mode_page iec; + int err; + + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + PRINT_ON(con); + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } else + modese_len = iec.modese_len; + + if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { + PRINT_ON(con); + pout("unable to disable Exception control and warning [%s]\n", + scsiErrString(err)); + PRINT_OFF(con); + return 1; + } + /* Need to refetch 'iec' since could be modified by previous call */ + if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { + pout("unable to fetch IEC (SMART) mode page [%s]\n", + scsiErrString(err)); + return 1; + } else + modese_len = iec.modese_len; + + pout("Informational Exceptions (SMART) %s\n", + scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); + pout("Temperature warning %s\n", + scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); + return 0; +} + +static void scsiPrintTemp(int device) +{ + UINT8 temp = 0; + UINT8 trip = 0; + + if (scsiGetTemp(device, &temp, &trip)) + return; + + if (temp) { + if (255 != temp) + pout("Current Drive Temperature: %d C\n", temp); + else + pout("Current Drive Temperature: \n"); + } + if (trip) + pout("Drive Trip Temperature: %d C\n", trip); +} + +/* Main entry point used by smartctl command. Return 0 for success */ +int scsiPrintMain(int fd) +{ + int checkedSupportedLogPages = 0; + UINT8 peripheral_type = 0; + int returnval = 0; + int res, durationSec; + + res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo); + if (res) { + if (2 == res) + return 0; + else + failuretest(MANDATORY_CMD, returnval |= FAILID); + } + + if (con->smartenable) { + if (scsiSmartEnable(fd)) + failuretest(MANDATORY_CMD, returnval |= FAILSMART); + } + + if (con->smartdisable) { + if (scsiSmartDisable(fd)) + failuretest(MANDATORY_CMD,returnval |= FAILSMART); + } + + if (con->smartautosaveenable) { + if (scsiSetControlGLTSD(fd, 0, modese_len)) { + pout("Enable autosave (clear GLTSD bit) failed\n"); + failuretest(OPTIONAL_CMD,returnval |= FAILSMART); + } + } + + if (con->smartautosavedisable) { + if (scsiSetControlGLTSD(fd, 1, modese_len)) { + pout("Disable autosave (set GLTSD bit) failed\n"); + failuretest(OPTIONAL_CMD,returnval |= FAILSMART); + } + } + + if (con->checksmart) { + scsiGetSupportedLogPages(fd); + checkedSupportedLogPages = 1; + if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || + (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */ + if (gTapeAlertsLPage) { + if (con->driveinfo) + pout("TapeAlert Supported\n"); + if (-1 == scsiGetTapeAlertsData(fd, peripheral_type)) + failuretest(OPTIONAL_CMD, returnval |= FAILSMART); + } + else + pout("TapeAlert Not Supported\n"); + } else { /* disk, cd/dvd, enclosure, etc */ + if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) { + if (-2 == res) + returnval |= FAILSTATUS; + else + returnval |= FAILSMART; + } + } + } + if (con->smartvendorattrib) { + if (! checkedSupportedLogPages) + scsiGetSupportedLogPages(fd); + if (gTempLPage) { + if (con->checksmart) + pout("\n"); + scsiPrintTemp(fd); + } + if (gStartStopLPage) + scsiGetStartStopData(fd); + if (SCSI_PT_DIRECT_ACCESS == peripheral_type) { + scsiPrintGrownDefectListLen(fd); + if (gSeagateCacheLPage) + scsiPrintSeagateCacheLPage(fd); + if (gSeagateFactoryLPage) + scsiPrintSeagateFactoryLPage(fd); + } + } + if (con->smarterrorlog) { + if (! checkedSupportedLogPages) + scsiGetSupportedLogPages(fd); + scsiPrintErrorCounterLog(fd); + if (1 == scsiFetchControlGLTSD(fd, modese_len, 1)) + pout("\n[GLTSD (Global Logging Target Save Disable) set. " + "Enable Save with '-S on']\n"); + } + if (con->smartselftestlog) { + if (! checkedSupportedLogPages) + scsiGetSupportedLogPages(fd); + res = 0; + if (gSelfTestLPage) + res = scsiPrintSelfTest(fd); + else { + pout("Device does not support Self Test logging\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + if (0 != res) + failuretest(OPTIONAL_CMD, returnval|=res); + } + if (con->smartexeoffimmediate) { + if (scsiSmartDefaultSelfTest(fd)) + return returnval | FAILSMART; + pout("Default Self Test Successful\n"); + } + if (con->smartshortcapselftest) { + if (scsiSmartShortCapSelfTest(fd)) + return returnval | FAILSMART; + pout("Short Foreground Self Test Successful\n"); + } + if (con->smartshortselftest ) { + if (scsiSmartShortSelfTest(fd)) + return returnval | FAILSMART; + pout("Short Background Self Test has begun\n"); + pout("Use smartctl -X to abort test\n"); + } + if (con->smartextendselftest) { + if (scsiSmartExtendSelfTest(fd)) + return returnval | FAILSMART; + pout("Extended Background Self Test has begun\n"); + if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, + modese_len)) && (durationSec > 0)) { + time_t t = time(NULL); + + t += durationSec; + pout("Please wait %d minutes for test to complete.\n", + durationSec / 60); + pout("Estimated completion time: %s\n", ctime(&t)); + } + pout("Use smartctl -X to abort test\n"); + } + if (con->smartextendcapselftest) { + if (scsiSmartExtendCapSelfTest(fd)) + return returnval | FAILSMART; + pout("Extended Foreground Self Test Successful\n"); + } + if (con->smartselftestabort) { + if (scsiSmartSelfTestAbort(fd)) + return returnval | FAILSMART; + pout("Self Test returned without error\n"); + } + return returnval; +} diff --git a/scsiprint.h b/scsiprint.h new file mode 100644 index 0000000..429223e --- /dev/null +++ b/scsiprint.h @@ -0,0 +1,37 @@ +/* + * scsiprint.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * Additional SCSI work: + * Copyright (C) 2003-6 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 + * 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/ + * + */ + + +/* scsismart version number */ +#ifndef SCSI_PRINT_H_ +#define SCSI_PRINT_H_ + +#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.20 2006/04/12 14:54:28 ballen4705 Exp $\n" + +int scsiPrintMain(int fd); + +#endif diff --git a/smartctl.8.in b/smartctl.8.in new file mode 100644 index 0000000..d1eabab --- /dev/null +++ b/smartctl.8.in @@ -0,0 +1,1290 @@ +.ig + Copyright (C) 2002-6 Bruce Allen + + $Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $ + + 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/ + +.. +.TH SMARTCTL 8 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE +.SH NAME +\fBsmartctl\fP \- Control and Monitor Utility for SMART Disks + +.SH SYNOPSIS +.B smartctl [options] device + +.SH FULL PATH +.B /usr/local/sbin/smartctl + +.SH PACKAGE VERSION +CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME + +.SH DESCRIPTION +\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 also provides support for polling TapeAlert messages +from SCSI tape drives and changers. + +The user must specify the device to be controlled or interrogated as +the final argument to \fBsmartctl\fP. Device paths are as follows: +.IP \fBLINUX\fP: 9 +Use the forms \fB"/dev/hd[a\-t]"\fP for IDE/ATA +devices, and \fB"/dev/sd[a\-z]"\fP for SCSI devices. For +SCSI Tape Drives and Changers with TapeAlert support use the devices +\fB"/dev/nst*"\fP and \fB"/dev/sg*"\fP. +For SATA disks accessed with libata, use \fB"/dev/sd[a\-z]"\fP +and append \fB"\-d ata"\fP. For disks behind 3ware controllers +you may need \fB"/dev/sd[a\-z]"\fP or \fB"/dev/twe[0\-9]"\fP +or \fB"/dev/twa[0\-9]"\fP: see details below. +More general paths (such as devfs ones) may also be specified. +.IP \fBDARWIN\fP: 9 +Use the forms \fB/dev/disk[0\-9]\fP or equivalently \fBdisk[0\-9]\fP or equivalently +\fB/dev/rdisk[0\-9]\fP. Long forms are also available: please use \'\-h\' to see some +examples. Note that there is currently no Darwin SCSI support. +.IP \fBFREEBSD\fP: 9 +Use the forms \fB"/dev/ad[0\-9]+"\fP for IDE/ATA +devices and \fB"/dev/da[0\-9]+"\fP for SCSI devices. +.IP \fBNETBSD/OPENBSD\fP: 9 +Use the form \fB"/dev/wd[0\-9]+c"\fP for IDE/ATA +devices. For SCSI disk and tape devices, use the device names +\fB"/dev/sd[0\-9]+c"\fP and \fB"/dev/st[0\-9]+c"\fP respectively. +Be sure to specify the correct "whole disk" partition letter for +your architecture. +.IP \fBSOLARIS\fP: 9 +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. +.IP \fBWINDOWS\fP: 9 +Use the forms \fB"/dev/hd[a\-j]"\fP for IDE/ATA devices +"\\\\.\\PhysicalDrive[0\-9]" on WinNT4/2000/XP, +\fB"/dev/hd[a\-d]"\fP for standard IDE/ATA devices on Win95/98/98SE/ME, +and \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices on ASPI adapter 0\-9, ID 0\-15. +The prefix \fB"/dev/"\fP is optional. +.IP \fBCYGWIN\fP: 9 +See "WINDOWS" above. +.IP \fBOS/2,eComStation\fP: 9 +Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices. +.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 +this guess + +Note that the printed output of \fBsmartctl\fP displays most numerical +values in base 10 (decimal), but some values are displayed in base 16 +(hexidecimal). To distinguish them, the base 16 values are always +displayed with a leading \fB"0x"\fP, for example: "0xff". This man +page follows the same convention. + +.PP +.SH OPTIONS +.PP +The options are grouped below into several categories. \fBsmartctl\fP +will execute the corresponding commands in the order: INFORMATION, +ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS. + +SCSI devices only accept the options \fB\-h, \-V, \-i, \-a, \-A, \-d, +\-s, \-S,\-H, \-t, \-C, \-l selftest, \-l error, \-r,\fP and +\fB\-X\fP. TapeAlert devices only accept the options \fB\-h, \-V, +\-i, \-a, \-A, \-d, \-s, \-S, \-t, \-l selftest, \-l error, \-r,\fP +and \fB\-H\fP. + +Long options are not supported on all systems. Use +.B \'smartctl \-h\' +to see the available options. + +.TP +.B SHOW INFORMATION OPTIONS: +.TP +.B \-h, \-\-help, \-\-usage +Prints a usage message to STDOUT and exits. +.TP +.B \-V, \-\-version, \-\-copyright, \-\-license +Prints version, copyright, license, home page and CVS\-id information +for your copy of \fBsmartctl\fP to STDOUT and then exits. Please +include this information if you are reporting bugs or problems. +.TP +.B \-i, \-\-info +Prints the device model number, serial number, firmware version, and +ATA Standard version/revision information. Says if the device +supports SMART, and if so, whether SMART support is currently enabled +or disabled. If the device supports Logical Block Address mode (LBA +mode) print current user drive capacity in bytes. (If drive is has a +user protected area reserved, or is "clipped", this may be smaller +than the potential maximum drive capacity.) Indicates if the drive is +in the smartmontools database (see \'\-v\' options below). If so, the +drive model family may also be printed. +.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 +to +.nf +\'\-H \-i \-c \-A \-l error \-l selftest -l selective\' +.fi +and for SCSI, this is equivalent to +.nf +\'\-H \-i \-A \-l error \-l selftest\'. +.fi +Note that for ATA disks this does \fBnot\fP enable the \'\-l +directory\' option. + +.TP +.B RUN\-TIME BEHAVIOR OPTIONS: +.TP +.B \-q TYPE, \-\-quietmode=TYPE +Specifies that \fBsmartctl\fP should run in one of the two quiet modes +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 +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) +which failed either now or in the past. + +.I silent +\- print no output. The only way to learn about what was found is to +use the exit status of \fBsmartctl\fP (see RETURN VALUES below). +.TP +.B \-d TYPE, \-\-device=TYPE +Specifies the type of the device. The valid arguments to this option +are \fIata\fP, \fIscsi\fP, \fImarvell\fP, and \fI3ware,N\fP. If this option is not +used then \fBsmartctl\fP will attempt to guess the device type from +the device name. + +Under Linux, to look at SATA disks behind Marvell SATA controllers +(using Marvell's \'linuxIAL\' driver rather than libata driver) use \'\-d marvell\'. Such +controllers show up as Marvell Technology Group Ltd. SATA I or II controllers +using lspci, or using lspci -n show a vendor ID 0x11ab and a device ID of +either 0x5040, 0x5041, 0x5080, 0x5081, 0x6041 or 0x6081. The \'linuxIAL\' driver +seems not (yet?) available in the Linux kernel source tree, but should be available +from system vendors (ftp://ftp.aslab.com/ is known to provide a patch with the driver). + +To look at ATA disks behind 3ware SCSI RAID controllers, use syntax +such as: +.nf +\fBsmartctl \-a \-d 3ware,2 /dev/sda\fP +.fi +.nf +\fBsmartctl \-a \-d 3ware,0 /dev/twe0\fP +.fi +.nf +\fBsmartctl \-a \-d 3ware,1 /dev/twa0\fP +.fi +where in the argument \fI3ware,N\fP, the integer N is the disk number +(3ware \'port\') within the 3ware ATA RAID controller. The allowed +values of N are from 0 to 15 inclusive. 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. \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. + +Note that if the special character device nodes /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 +second 9000 series controller, and so on. Likewise /dev/twe0 refers to +the first 6/7/8000-series controller, /dev/twa1 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 +disks can be queried or examined using \fBany\fP of the 3ware's SCSI +logical device /dev/sd? entries. Thus, if logical device /dev/sda is +made up of two physical disks (3ware ports zero and one) and logical +device /dev/sdb is made up of two other physical disks (3ware ports +two and three) then you can examine the SMART data on \fBany\fP of the +four physical disks using \fBeither\fP SCSI device /dev/sda \fBor\fP +/dev/sdb. If you need to know which logical SCSI device a particular +physical disk (3ware port) is associated with, use the dmesg or SYSLOG +output to show which SCSI ID corresponds to a particular 3ware unit, +and then use the 3ware CLI or 3dm tool to determine which ports +(physical disks) correspond to particular 3ware units. + +If the value of N corresponds to a port that does \fBnot\fP exist on +the 3ware controller, or to a port that does not physically have a +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. + +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 +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 +versions. See \fBhttp://smartmontools.sourceforge.net/\fP for +instructions. 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/twa0\-15 and /dev/twe0\-15. +The necessary WRITE LOG commands can not be passed through the SCSI +interface. + +.B 3ware controllers are currently ONLY supported under Linux and FreeBSD. + +.TP +.B \-T TYPE, \-\-tolerance=TYPE +Specifies how tolerant \fBsmartctl\fP should be of ATA and SMART 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 +the SMART command set" and "\fBoptional\fP" means "not required by the +ATA/ATAPI\-5 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. + +The valid arguments to this option are: + +.I normal +\- exit on failure of any \fBmandatory\fP SMART command, and ignore +all failures of \fBoptional\fP SMART commands. This is the default. +Note that on some devices, issuing unimplemented optional SMART +commands doesn\'t cause an error. This can result in misleading +\fBsmartctl\fP messages such as "Feature X not implemented", followed +shortly by "Feature X: enabled". In most such cases, contrary to the +final message, Feature X is \fBnot\fP enabled. + +.I conservative +\- exit on failure of any \fBoptional\fP SMART command. + +.I permissive +\- 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 +such cases, contrary to the final message, Feature X \fBis\fP enabled. + +.I verypermissive +\- equivalent to giving a large number of \'\-T permissive\' options: +ignore failures of \fBany number\fP of \fBmandatory\fP SMART commands. +Please see the note above. + +.TP +.B \-b TYPE, \-\-badsum=TYPE +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 +Attribute Threshold Structure, or (5) ATA Error Log Structure. + +The valid arguments to this option are: + +.I warn +\- report the incorrect checksum but carry on in spite of it. This is the +default. + +.I exit +\- exit \fBsmartctl\fP. + +.I ignore +\- continue silently without issuing a warning. + +.TP +.B \-r TYPE, \-\-report=TYPE +Intended primarily to help \fBsmartmontools\fP developers understand +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 +with the device. When used more than once, the detail of these +ioctl() transactions are reported in greater detail. The valid +arguments to this option are: + +.I ioctl +\- report all ioctl() transactions. + +.I ataioctl +\- report only ioctl() transactions with ATA devices. + +.I scsiioctl +\- report only ioctl() transactions with SCSI devices. Invoking this once +shows the SCSI commands in hex and the corresponding status. Invoking +it a second time adds a hex listing of the first 64 bytes of data send to, +or received from the device. + +Any argument may include a positive integer to specify the level of detail +that should be reported. The argument should be followed by a comma then +the integer with no spaces. For example, +.I ataioctl,2 +The default +level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent. + +.TP +.B SMART FEATURE ENABLE/DISABLE COMMANDS: +.IP +.B Note: +if multiple options are used to both enable and disable a +feature, then +.B both +the enable and disable commands will be issued. The enable command +will always be issued +.B before +the corresponding disable command. +.TP +.B \-s VALUE, \-\-smart=VALUE +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 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 +useful) to enable SMART to see the TapeAlert messages. +.TP +.B \-o VALUE, \-\-offlineauto=VALUE +Enables or disables SMART automatic offline test, which scans the drive +every four hours for disk defects. This command can be given during normal +system operation. The valid arguments to this option are \fIon\fP +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, +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.] +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 +(displayed with \'\-c\'). + +SMART provides \fBthree\fP basic categories of testing. The +\fBfirst\fP category, called "online" testing, has no effect on the +performance of the device. It is turned on by the \'\-s on\' option. + +The \fBsecond\fP category of testing is called "offline" testing. This +type of test can, in principle, degrade the device performance. The +\'\-o on\' option causes this offline testing to be carried out, +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 +also be carried out immediately upon receipt of a user command. See +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 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 +as online and offline \fBdata collection\fP. + +The results of this automatic or immediate offline testing (data +collection) are reflected in the values of the SMART Attributes. +Thus, if problems or errors are detected, the values of these +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 +collection activities; the rest are updated during normal operation of +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". + +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 +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\' +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 +connection with the third category. +.TP +.B \-S VALUE, \-\-saveauto=VALUE +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. + +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 +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 +issue a warning. Use \fIon\fP to clear the GLTSD bit and thus enable +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 SMART READ AND DISPLAY DATA OPTIONS: +.TP +.B \-H, \-\-health +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 +by reading the TapeAlert log page. + +If the device reports failing health status, this means +.B either +that the device has already failed, +.B or +that it is predicting its own failure within the next 24 hours. If +this happens, use the \'\-a\' option to get more information, and +.B get your data off the disk and someplace safe as soon as you can. +.TP +.B \-c, \-\-capabilities +Prints only the generic SMART capabilities. These 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 +option also shows the estimated time required to run those tests. + +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, +then the time may jump to a larger value and then count down as the +Immediate Offline Test is carried out. Please see REFERENCES below +for further information about the the flags and capabilities described +by this option. +.TP +.B \-A, \-\-attributes +Prints only the vendor specific SMART Attributes. The Attributes are +numbered from 1 to 253 and have specific names and ID numbers. For +example Attribute 12 is "power cycle count": how many times has the +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 +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 +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 +in mind that \fBsmartctl\fP only reports the different Attribute +types, values, and thresholds as read from the device. It does +\fBnot\fP carry out the conversion between "Raw" and "Normalized" +values: this is done by the disk\'s firmware. + +The conversion from Raw value to a quantity with physical units is +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, +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, +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.] + +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 +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 +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 +\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. + +If the Attribute\'s current Normalized value is less than or equal to +the threshold value, then the "WHEN_FAILED" column will display +"FAILING_NOW". If not, but the worst recorded value is less than or +equal to the threshold value, then this column will display +"In_the_past". If the "WHEN_FAILED" column has no entry (indicated by +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 +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, +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 +values are stored on the disk, along with a Threshold value that the +manufacturer has determined will indicate that the disk is going to +fail, or that it has exceeded its design age or aging limit. +\fBsmartctl\fP does \fBnot\fP calculate any of the Attribute values, +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 +the option of printing the Attribute values. + +For SCSI devices the "attributes" are obtained from the temperature +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 +.B \-l TYPE, \-\-log=TYPE +Prints either the SMART Error Log, the SMART Self\-Test Log, the SMART +Selective Self\-Test Log [ATA only], or the Log Directory [ATA only]. +The valid arguments to this option are: + +.I error +\- prints only the 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 +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 +are: +.nf + \fBABRT\fP: Command \fBAB\fPo\fBRT\fPed + \fBAMNF\fP: \fBA\fPddress \fBM\fPark \fBN\fPot \fBF\fPound + \fBCCTO\fP: \fBC\fPommand \fBC\fPompletion \fBT\fPimed \fBO\fPut + \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) + \fBMC\fP: \fBM\fPedia \fBC\fPhanged + \fBMCR\fP: \fBM\fPedia \fBC\fPhange \fBR\fPequest + \fBNM\fP: \fBN\fPo \fBM\fPedia + \fBobs\fP: \fBobs\fPolete + \fBTK0NF\fP: \fBT\fPrac\fBK 0 N\fPot \fBF\fPound + \fBUNC\fP: \fBUNC\fPorrectable Error in Data + \fBWP\fP: Media is \fBW\fPrite \fBP\fProtected +.fi +In addition, up to the last five commands that preceded the error are +listed, along with a timestamp measured from the start of the +corresponding power cycle. This is displayed in the form +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 +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 +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 +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. + +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 +attributed to the receipt of faulty commands such as command codes not +implemented by the device or requests with invalid parameters or +invalid addresses."\fP The definitions of these terms are: +.br +\fBUNC\fP (\fBUNC\fPorrectable): data is uncorrectable. This refers +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 +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 +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 +a capacity greater than 128 GiB or 137 GB.) On Linux systems the +smartmontools web page has instructions about how to convert the LBA +address to the name of the disk file containing the erroneous disk +sector. + +Please note that some manufacturers \fBignore\fP the ATA +specifications, and make entries in the error log if the device +receives a command which is not implemented or is not valid. + +.I error [SCSI] +\- prints the error counter log pages for reads, write and verifies. +The verify row is only output if it has an element other than zero. + +.I selftest +\- 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 +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. If any errors +were detected, the Logical Block Address (LBA) of the first error is +printed in decimal notation. On Linux systems the 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 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 +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 +"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 +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 +address to the name of the disk file containing the erroneous block. +If provided, the SCSI Sense Key (SK), Additional Sense Code (ASC) and +Additional Sense Code Qualifier (ASQ) are also printed. The self tests +can be run using the \'\-t\' option described below (using the ATA +test terminology). + +.I selective [ATA] +\- Some ATA\-7 disks (example: Maxtor) also maintain a selective +self\-test log. Please see the \'\-t select\' option below for a +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 +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. + +.I directory +\- if the device supports the General Purpose Logging feature set +(ATA\-6 and ATA\-7 only) 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 +.I error +and +.I selftest +arguments to this option. [Please note: this is a new, experimental +feature. We would like to add support for printing the contents of +extended and comprehensive SMART self\-test and error logs. If your +disk supports these, and you would like to assist, please contact the +\fBsmartmontools\fP developers.] + +.TP +.B \-v N,OPTION, \-\-vendorattribute=N,OPTION +Sets a vendor\-specific display OPTION for Attribute N. This option +may be used multiple times. Valid arguments to this option are: + +.I help +\- Prints (to STDOUT) a list of all valid arguments to this option, +then exits. + +.I 9,minutes +\- Raw Attribute number 9 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 9,seconds +\- Raw Attribute number 9 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 9,halfminutes +\- Raw Attribute number 9 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 9,temp +\- Raw Attribute number 9 is the disk temperature in Celsius. + +.I 192,emergencyretractcyclect +\- Raw Attribute number 192 is the Emergency Retract Cycle Count. + +.I 193,loadunload +\- Raw Attribute number 193 contains two 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 +unload). As a rule of thumb, the mechanical stress created by one +emergency unload is equivalent to that created by one hundred normal +unloads. + +.I 194,10xCelsius +\- Raw Attribute number 194 is ten times the disk temperature in +Celsius. This is used by some Samsung disks (example: model SV1204H +with RK100\-13 firmware). + +.I 194,unknown +\- Raw Attribute number 194 is NOT the disk temperature, and its +interpretation is unknown. This is primarily useful for the \-P +(presets) option. + +.I 198,offlinescanuncsectorct +\- Raw Attribute number 198 is the Offline Scan UNC Sector Count. + +.I 200,writeerrorcount +\- Raw Attribute number 200 is the Write Error Count. + +.I 201,detectedtacount +\- Raw Attribute number 201 is the Detected TA Count. + +.I 220,temp +\- Raw Attribute number 220 is the disk temperature in Celsius. + +Note: a table of hard drive models, listing which Attribute +corresponds to temperature, can be found at: +\fBhttp://www.guzu.net/linux/hddtemp.db\fP + +.I N,raw8 +\- Print the Raw value of Attribute N as six 8\-bit unsigned base\-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw8\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw8\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw16 +\- Print the Raw value of Attribute N as three 16\-bit unsigned base\-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw16\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw16\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw48 +\- Print the Raw value of Attribute N as a 48\-bit unsigned base\-10 +integer. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw48\' prints Raw values for ALL Attributes in +this form. The form (for example) \'123,raw48\' only prints the Raw +value for Attribute 123 in this form. + +.TP +.B \-F TYPE, \-\-firmwarebug=TYPE +Modifies the behavior of \fBsmartctl\fP to compensate for some known +and understood device firmware bug. The arguments to this option are +exclusive, so that only the final option given is used. The valid +values 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). + +.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). +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; +(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 more recent Samsung disks (firmware revisions ending in "\-23") +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 +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). + +Note that an explicit \'\-F\' option on the command line will +over\-ride any preset values for \'\-F\' (see the \'\-P\' option +below). + +.TP +.B \-P TYPE, \-\-presets=TYPE +Specifies whether \fBsmartctl\fP should use any preset options that +are available for this drive. By default, if the drive is recognized +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 +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 +line. + +The argument +.I show +will show any preset options for your drive and the argument +.I showall +will show all known drives in the \fBsmartmontools\fP database, along +with their preset options. If there are no presets for your drive and +you think there should be (for example, a \-v or \-F option is needed +to get \fBsmartctl\fP to display correct values) then please contact +the \fBsmartmontools\fP developers so that this information can be +added to the \fBsmartmontools\fP database. Contact information is at the +end of this man page. + +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.. + +.I ignore +\- do not use presets. + +.I show +\- show if the drive is recognized in the database, and if so, its +presets, then exit. + +.I showall +\- list all recognized drives, and the presets that are set for them, +then exit. + +The \'\-P showall\' option takes up to two optional arguments to +match a specific drive type and firmware version. The command: +.nf + smartctl \-P showall +.fi +lists all entries, the command: +.nf + smartctl \-P showall \'MODEL\' +.fi +lists all entries matching MODEL, and the command: +.nf + smartctl \-P showall \'MODEL\' \'FIRMWARE\' +.fi +lists all entries for this MODEL and a specific FIRMWARE version. + +.TP +.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 +(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. + +The valid arguments to this option are: + +.I offline +\- runs SMART Immediate Offline Test. This immediately +starts the test described above. This command can be given during +normal system operation. The effects of this test are visible only in +that it updates the SMART Attribute values, and if errors are +found they will appear in the SMART error log, visible with the \'\-l error\' +option. [In the case of SCSI devices runs the default self test in +foreground. No entry is placed in the self test log.] + +If the \'\-c\' option to \fBsmartctl\fP shows that the device has the +"Suspend Offline collection upon new command" capability then you can +track the progress of the Immediate Offline test using the \'\-c\' +option to \fBsmartctl\fP. If the \'\-c\' option show that the device +has the "Abort Offline collection upon new command" capability then +most commands will abort the Immediate Offline Test, so you should not +try to track the progress of the test with \'\-c\', as it will abort +the test. + +.I short +\- runs SMART Short Self Test (usually under ten minutes). +[Note: in the case of SCSI devices, +this command option runs the "Background short" self\-test.] +This command can be given during normal system operation (unless run in +captive mode \- see the \'\-C\' option below). This is a +test in a different category than the immediate or automatic offline +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 +use the \'\-c\' option to monitor progress. + +.I long +\- runs SMART Extended Self Test (tens of minutes). +[Note: in the case of SCSI devices, +this command option runs the "Background long" self\-test.] +This is a +longer and more thorough version of the Short Self Test described +above. Note that this command can be given during normal +system operation (unless run in captive mode \- see the \'\-C\' option below). + +.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 +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). + +.I select,N\-M +\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] runs a SMART +Selective Self Test, to test a \fBrange\fP of disk Logical Block +Addresses (LBAs), rather than the entire disk. Each range of LBAs +that is checked is called a "span" and is specified by a starting LBA +(N) and an ending LBA (M) with N less than or equal to M. For example +the command: +.nf + smartctl \-t select,10\-20 /dev/hda +.fi +runs a self test on one span consisting of LBAs ten to twenty +(inclusive). The \'\-t\' option can be given up to five times, to test +up to five spans. For example the command: +.nf + smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/hda +.fi +runs a self test on two spans. The first span consists of 101 LBAs +and the second span consists of 1001 LBAs. Note that the spans can +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 +\'\-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 +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 +done in captive mode \- see the \'\-C\' option below). + +[Note: this new experimental smartmontools feature is currently only +available under Linux. The Linux kernel must be compiled with the +configuration option CONFIG_IDE_TASKFILE_IO enabled. Please report +unusual or incorrect behavior to the smartmontools\-support mailing +list.] + +.I afterselect,on +\- [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 +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 +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. + +.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 +with one or more of the \fIselect,N\-M\fP options above. The value of this +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 +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. + +.TP +.B \-C, \-\-captive +Runs self\-tests in captive mode. This has no effect with \'\-t +offline\' or if the \'\-t\' option is not used. [Note: in the case of +SCSI devices, this command option runs the self\-test in "Foreground" +mode.] + +\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 + +.TP +.B \-X, \-\-abort +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 +.SH EXAMPLES +.nf +.B smartctl \-a /dev/hda +.fi +Print all SMART information for drive /dev/hda (Primary Master). +.PP +.nf +.B smartctl \-s off /dev/hdd +.fi +Disable SMART on drive /dev/hdd (Secondary Slave). +.PP +.nf +.B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/hda +.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 +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 +log visible with the \'\-l selftest\' option after it has completed. +.PP +.nf +.B smartctl \-s on \-t offline /dev/hda +.fi +Enable SMART on the disk, and begin an immediate offline test of +drive /dev/hda. You can issue this command on a running system. The +results are only used to update the SMART Attributes, visible +with the \'\-A\' option. If any device errors occur, they are logged to +the SMART error log, which can be seen with the \'\-l error\' option. +.PP +.nf +.B smartctl \-A \-v 9,minutes /dev/hda +.fi +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. +.PP +.nf +.B smartctl \-q silent \-a /dev/hda +.fi +Examine all SMART data for device /dev/hda, but produce no +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. +.PP +.nf +.B smartctl \-a \-d 3ware,0 /dev/sda +.fi +Examine all SMART data for the first ATA disk connected to a 3ware +RAID controller card. +.PP +.nf +.B smartctl \-a \-d 3ware,0 /dev/twe0 +.fi +Examine all SMART data for the first ATA disk connected to a 3ware +RAID 6000/7000/8000 controller card. +.PP +.nf +.B smartctl \-a \-d 3ware,0 /dev/twa0 +.fi +Examine all SMART data for the first ATA disk connected to a 3ware +RAID 9000 controller card. +.PP +.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 +controller card which is the second SCSI device /dev/sdb. +.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 +device is restored. +.PP +.SH RETURN VALUES +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 +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. +.TP +.B Bit 0: +Command line did not parse. +.TP +.B Bit 1: +Device open failed, or device did not return an IDENTIFY DEVICE structure. +.TP +.B Bit 2: +Some SMART command to the disk failed, or there was a checksum error +in a SMART data structure (see \'\-b\' option above). +.TP +.B Bit 3: +SMART status check returned "DISK FAILING". +.TP +.B Bit 4: +SMART status check returned "DISK OK" but we found prefail Attributes <= threshold. +.TP +.B Bit 5: +SMART status check returned "DISK OK" but we found that some (usage +or prefail) Attributes have been <= threshold at some time in the +past. +.TP +.B Bit 6: +The device error log contains records of errors. +.TP +.B Bit 7: +The device self\-test log contains records of errors. + +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 +is bash syntax): +.nf +.B smartstat=$(($? & 8)) +.fi +This looks at only at bit 3 of the exit status +.B $? +(since 8=2^3). The shell variable +$smartstat will be nonzero if SMART status check returned "disk +failing" and zero otherwise. + +.PP +.SH NOTES +The TapeAlert log page flags are cleared for the initiator when the +page is read. This means that each alert condition is reported only +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 +University of Wisconsin \- Milwaukee Physics Department + +.PP +.SH CONTRIBUTORS +The following have made large contributions to smartmontools: +.nf +\fBCasper Dik\fP (Solaris SCSI interface) +\fBChristian Franke\fP (Windows interface and Cygwin package) +\fBDouglas Gilbert\fP (SCSI subsystem) +\fBGuido Guenther\fP (Autoconf/Automake packaging) +\fBGeoffrey Keating\fP (Darwin ATA interface) +\fBEduard Martinescu\fP (FreeBSD interface) +\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list) +\fBKeiji Sawada\fP (Solaris ATA interface) +\fBSergey Svishchev\fP (NetBSD interface) +\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface) +\fBPhil Williams\fP (User interface and drive database) +\fBYuri Dario\fP (OS/2, eComStation interface) +.fi +Many other individuals have made smaller contributions and corrections. + +.PP +.SH CREDITS +.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 +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. \fBhttp://ssrc.soe.ucsc.edu/\fP . +.SH +HOME PAGE FOR SMARTMONTOOLS: +.fi +Please see the following web site for updates, further documentation, bug +reports and patches: \fBhttp://smartmontools.sourceforge.net/\fP + +.SH +SEE ALSO: +\fBsmartd\fP(8), \fBbadblocks\fP(8), \fBide\-smart\fP(8). +.SH +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.php?sid=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) +specification. This documents the SMART functionality which the +\fBsmartmontools\fP utilities provide access to. You can find +Revision 4b of this document at +\fBhttp://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf\fP . +Earlier and later versions of this Specification are available from +the T13 web site \fBhttp://www.t13.org/\fP . + +.fi +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 documents may be found in the References section of the +\fBsmartmontools\fP home page at +\fBhttp://smartmontools.sourceforge.net/\fP . + +.SH +CVS ID OF THIS PAGE: +$Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $ +.\" Local Variables: +.\" mode: nroff +.\" End: diff --git a/smartctl.c b/smartctl.c new file mode 100644 index 0000000..6c0886f --- /dev/null +++ b/smartctl.c @@ -0,0 +1,896 @@ +/* + * smartctl.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#ifdef HAVE_GETOPT_LONG +#include +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) +#include +#endif + +#include "int64.h" +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "knowndrives.h" +#include "scsicmds.h" +#include "scsiprint.h" +#include "smartctl.h" +#include "utility.h" + +#ifdef NEED_SOLARIS_ATA_CODE +extern const char *os_solaris_ata_s_cvsid; +#endif +extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid; +const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.143 2006/04/12 14:54:28 ballen4705 Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; + +// This is a block containing all the "control variables". We declare +// this globally in this file, and externally in other files. +smartmonctrl *con=NULL; + +// to hold onto exit code for atexit routine +extern int exitstatus; + +// Track memory use +extern int64_t bytes; + +void printslogan(){ +#ifdef HAVE_GET_OS_VERSION_STR + const char * ver = get_os_version_str(); +#else + const char * ver = SMARTMONTOOLS_BUILD_HOST; +#endif + pout("smartctl version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver); + pout("Home page is " PACKAGE_HOMEPAGE "\n\n"); + return; +} + +void PrintOneCVS(const char *a_cvs_id){ + char out[CVSMAXLEN]; + printone(out,a_cvs_id); + pout("%s",out); + return; +} + +void printcopy(){ + char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]"; + + pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n"); + pout("is free software, and you are welcome to redistribute it\n"); + pout("under the terms of the GNU General Public License Version 2.\n"); + pout("See http://www.gnu.org for further details.\n\n"); + pout("CVS version IDs of files used to build this code are:\n"); + PrintOneCVS(atacmdnames_c_cvsid); + PrintOneCVS(atacmds_c_cvsid); + PrintOneCVS(ataprint_c_cvsid); + PrintOneCVS(knowndrives_c_cvsid); + PrintOneCVS(os_XXXX_c_cvsid); +#ifdef NEED_SOLARIS_ATA_CODE + PrintOneCVS(os_solaris_ata_s_cvsid); +#endif + PrintOneCVS(scsicmds_c_cvsid); + PrintOneCVS(scsiprint_c_cvsid); + PrintOneCVS(smartctl_c_cvsid); + PrintOneCVS(utility_c_cvsid); + pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"); + pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"); + pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n"); + pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n"); + pout("smartmontools configure arguments: %s\n", configargs); + return; +} + +void UsageSummary(){ + pout("\nUse smartctl -h to get a usage summary\n\n"); + return; +} + +/* void prints help information for command syntax */ +void Usage (void){ + printf("Usage: smartctl [options] device\n\n"); + printf("============================================ SHOW INFORMATION OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -h, --help, --usage\n" +" Display this help and exit\n\n" +" -V, --version, --copyright, --license\n" +" Print license, copyright, and version information and exit\n\n" +" -i, --info \n" +" Show identity information for device\n\n" +" -a, --all \n" +" Show all SMART information for device\n\n" + ); +#else + printf( +" -h Display this help and exit\n" +" -V Print license, copyright, and version information\n" +" -i Show identity information for device\n" +" -a Show all SMART information for device\n\n" + ); +#endif + printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -q TYPE, --quietmode=TYPE (ATA)\n" +" Set smartctl quiet mode to one of: errorsonly, silent\n\n" +" -d TYPE, --device=TYPE\n" +" Specify device type to one of: ata, scsi, marvell, 3ware,N\n\n" +" -T TYPE, --tolerance=TYPE (ATA)\n" +" Tolerance: normal, conservative, permissive, verypermissive\n\n" +" -b TYPE, --badsum=TYPE (ATA)\n" +" Set action on bad checksum to one of: warn, exit, ignore\n\n" +" -r TYPE, --report=TYPE\n" +" Report transactions (see man page)\n\n" + ); +#else + printf( +" -q TYPE Set smartctl quiet mode to one of: errorsonly, silent (ATA)\n" +" -d TYPE Specify device type to one of: ata, scsi, 3ware,N\n" +" -T TYPE Tolerance: normal, conservative,permissive,verypermissive (ATA\n" +" -b TYPE Set action on bad checksum to one of: warn, exit, ignore (ATA)\n" +" -r TYPE Report transactions (see man page)\n\n" + ); +#endif + printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -s VALUE, --smart=VALUE\n" +" Enable/disable SMART on device (on/off)\n\n" +" -o VALUE, --offlineauto=VALUE (ATA)\n" +" Enable/disable automatic offline testing on device (on/off)\n\n" +" -S VALUE, --saveauto=VALUE (ATA)\n" +" Enable/disable Attribute autosave on device (on/off)\n\n" + ); +#else + printf( +" -s VALUE Enable/disable SMART on device (on/off)\n" +" -o VALUE Enable/disable device automatic offline testing (on/off) (ATA)\n" +" -S VALUE Enable/disable device Attribute autosave (on/off) (ATA)\n\n" + ); +#endif + printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -H, --health\n" +" Show device SMART health status\n\n" +" -c, --capabilities (ATA)\n" +" Show device SMART capabilities\n\n" +" -A, --attributes \n" +" Show device SMART vendor-specific Attributes and values\n\n" +" -l TYPE, --log=TYPE\n" +" Show device log. TYPE: error, selftest, selective, directory\n\n" +" -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" +" Set display OPTION for vendor Attribute N (see man page)\n\n" +" -F TYPE, --firmwarebug=TYPE (ATA)\n" +" Use firmware bug workaround: none, samsung, samsung2\n\n" +" -P TYPE, --presets=TYPE (ATA)\n" +" Drive-specific presets: use, ignore, show, showall\n\n" + ); +#else + printf( +" -H Show device SMART health status\n" +" -c Show device SMART capabilities (ATA)\n" +" -A Show device SMART vendor-specific Attributes and values (ATA)\n" +" -l TYPE Show device log. TYPE: error,selftest,selective,directory\n" +" -v N,OPT Set display OPTion for vendor Attribute N (see man page) (ATA)\n" +" -F TYPE Use firmware bug workaround: none, samsung, samsung2 (ATA)\n" +" -P TYPE Drive-specific presets: use, ignore, show, showall (ATA)\n\n" + ); +#endif + printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n"); +#ifdef HAVE_GETOPT_LONG + printf( +" -t TEST, --test=TEST\n" +" Run test. TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n\n" +" -C, --captive\n" +" Do test in captive mode (along with -t)\n\n" +" -X, --abort\n" +" Abort any non-captive test on device\n\n" +); +#else + printf( +" -t TEST Run test. TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n" +" -C Do test in captive mode (along with -t)\n" +" -X Abort any non-captive test\n\n" + ); +#endif + print_smartctl_examples(); + return; +} + +/* Returns a pointer to a static string containing a formatted list of the valid + arguments to the option opt or NULL on failure. Note 'v' case different */ +const char *getvalidarglist(char opt) { + switch (opt) { + case 'q': + return "errorsonly, silent"; + case 'd': + return "ata, scsi, marvell, 3ware,N"; + case 'T': + return "normal, conservative, permissive, verypermissive"; + case 'b': + return "warn, exit, ignore"; + case 'r': + return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; + case 's': + case 'o': + case 'S': + return "on, off"; + case 'l': + return "error, selftest, selective, directory"; + case 'P': + return "use, ignore, show, showall"; + case 't': + return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,on, afterselect,off"; + case 'F': + return "none, samsung, samsung2"; + case 'v': + default: + return NULL; + } +} + +/* Prints the message "=======> VALID ARGUMENTS ARE: \n", where + is the list of valid arguments for option opt. */ +void printvalidarglistmessage(char opt) { + char *s; + + if (opt=='v') + s=create_vendor_attribute_arg_list(); + else + s=(char *)getvalidarglist(opt); + + if (!s) { + pout("Error whilst constructing argument list for option %c", opt); + return; + } + + if (opt=='v'){ + pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s); + free(s); + } + else { + // getvalidarglist() might produce a multiline or single line string. We + // need to figure out which to get the formatting right. + char separator = strchr(s, '\n') ? '\n' : ' '; + pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator); + } + + return; +} + +/* Takes command options and sets features to be run */ +void ParseOpts (int argc, char** argv){ + int optchar; + int badarg; + int captive; + unsigned char *charp; + extern char *optarg; + extern int optopt, optind, opterr; + char extraerror[256]; + // Please update getvalidarglist() if you edit shortopts + const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:"; +#ifdef HAVE_GETOPT_LONG + char *arg; + // Please update getvalidarglist() if you edit longopts + struct option longopts[] = { + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V' }, + { "copyright", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'V' }, + { "quietmode", required_argument, 0, 'q' }, + { "device", required_argument, 0, 'd' }, + { "tolerance", required_argument, 0, 'T' }, + { "badsum", required_argument, 0, 'b' }, + { "report", required_argument, 0, 'r' }, + { "smart", required_argument, 0, 's' }, + { "offlineauto", required_argument, 0, 'o' }, + { "saveauto", required_argument, 0, 'S' }, + { "health", no_argument, 0, 'H' }, + { "capabilities", no_argument, 0, 'c' }, + { "attributes", no_argument, 0, 'A' }, + { "log", required_argument, 0, 'l' }, + { "info", no_argument, 0, 'i' }, + { "all", no_argument, 0, 'a' }, + { "vendorattribute", required_argument, 0, 'v' }, + { "presets", required_argument, 0, 'P' }, + { "test", required_argument, 0, 't' }, + { "captive", no_argument, 0, 'C' }, + { "abort", no_argument, 0, 'X' }, + { "firmwarebug", required_argument, 0, 'F' }, + { 0, 0, 0, 0 } + }; +#endif + + memset(extraerror, 0, sizeof(extraerror)); + memset(con,0,sizeof(*con)); + con->testcase=-1; + opterr=optopt=0; + badarg = captive = FALSE; + + // This miserable construction is needed to get emacs to do proper indenting. Sorry! + while (-1 != (optchar = +#ifdef HAVE_GETOPT_LONG + getopt_long(argc, argv, shortopts, longopts, NULL) +#else + getopt(argc, argv, shortopts) +#endif + )){ + switch (optchar){ + case 'V': + con->dont_print=FALSE; + printslogan(); + printcopy(); + exit(0); + break; + case 'q': + if (!strcmp(optarg,"errorsonly")) { + con->printing_switchable = TRUE; + con->dont_print = FALSE; + } else if (!strcmp(optarg,"silent")) { + con->printing_switchable = FALSE; + con->dont_print = TRUE; + } else { + badarg = TRUE; + } + break; + case 'd': + if (!strcmp(optarg,"ata")) { + con->controller_type = CONTROLLER_ATA; + con->controller_port = 0; + } else if (!strcmp(optarg,"scsi")) { + con->controller_type = CONTROLLER_SCSI; + con->controller_port = 0; + } else if (!strcmp(optarg,"marvell")) { + con->controller_type = CONTROLLER_MARVELL_SATA; + con->controller_port = 0; + } else { + // look for RAID-type device + int i; + char *s; + + // make a copy of the string to mess with + if (!(s = strdup(optarg))) { + con->dont_print = FALSE; + pout("No memory for argument of -d. Exiting...\n"); + exit(FAILCMD); + } else if (strncmp(s,"3ware,",6)) { + badarg = TRUE; + } else if (split_report_arg2(s, &i)) { + sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n"); + badarg = TRUE; + } else if (i<0 || i>15) { + sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15\n", i); + badarg = TRUE; + } else { + // NOTE: controller_port == disk number + 1 + con->controller_type = CONTROLLER_3WARE; + con->controller_port = i+1; + } + free(s); + } + break; + case 'T': + if (!strcmp(optarg,"normal")) { + con->conservative = FALSE; + con->permissive = 0; + } else if (!strcmp(optarg,"conservative")) { + con->conservative = TRUE; + } else if (!strcmp(optarg,"permissive")) { + if (con->permissive<0xff) + con->permissive++; + } else if (!strcmp(optarg,"verypermissive")) { + con->permissive=0xff; + } else { + badarg = TRUE; + } + break; + case 'b': + if (!strcmp(optarg,"warn")) { + con->checksumfail = FALSE; + con->checksumignore = FALSE; + } else if (!strcmp(optarg,"exit")) { + con->checksumfail = TRUE; + con->checksumignore = FALSE; + } else if (!strcmp(optarg,"ignore")) { + con->checksumignore = TRUE; + con->checksumfail = FALSE; + } else { + badarg = TRUE; + } + break; + case 'r': + { + int i; + char *s; + + // split_report_arg() may modify its first argument string, so use a + // copy of optarg in case we want optarg for an error message. + if (!(s = strdup(optarg))) { + con->dont_print = FALSE; + pout("Can't allocate memory to copy argument to -r option" + " - exiting\n"); + EXIT(FAILCMD); + } + if (split_report_arg(s, &i)) { + badarg = TRUE; + } else if (!strcmp(s,"ioctl")) { + con->reportataioctl = con->reportscsiioctl = i; + } else if (!strcmp(s,"ataioctl")) { + con->reportataioctl = i; + } else if (!strcmp(s,"scsiioctl")) { + con->reportscsiioctl = i; + } else { + badarg = TRUE; + } + free(s); + } + break; + case 's': + if (!strcmp(optarg,"on")) { + con->smartenable = TRUE; + con->smartdisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartdisable = TRUE; + con->smartenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'o': + if (!strcmp(optarg,"on")) { + con->smartautoofflineenable = TRUE; + con->smartautoofflinedisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartautoofflinedisable = TRUE; + con->smartautoofflineenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'S': + if (!strcmp(optarg,"on")) { + con->smartautosaveenable = TRUE; + con->smartautosavedisable = FALSE; + } else if (!strcmp(optarg,"off")) { + con->smartautosavedisable = TRUE; + con->smartautosaveenable = FALSE; + } else { + badarg = TRUE; + } + break; + case 'H': + con->checksmart = TRUE; + break; + case 'F': + if (!strcmp(optarg,"none")) { + con->fixfirmwarebug = FIX_NONE; + } else if (!strcmp(optarg,"samsung")) { + con->fixfirmwarebug = FIX_SAMSUNG; + } else if (!strcmp(optarg,"samsung2")) { + con->fixfirmwarebug = FIX_SAMSUNG2; + } else { + badarg = TRUE; + } + break; + case 'c': + con->generalsmartvalues = TRUE; + break; + case 'A': + con->smartvendorattrib = TRUE; + break; + case 'l': + if (!strcmp(optarg,"error")) { + con->smarterrorlog = TRUE; + } else if (!strcmp(optarg,"selftest")) { + con->smartselftestlog = TRUE; + } else if (!strcmp(optarg, "selective")) { + con->selectivetestlog = TRUE; + } else if (!strcmp(optarg,"directory")) { + con->smartlogdirectory = TRUE; + } else { + badarg = TRUE; + } + break; + case 'i': + con->driveinfo = TRUE; + break; + case 'a': + con->driveinfo = TRUE; + con->checksmart = TRUE; + con->generalsmartvalues = TRUE; + con->smartvendorattrib = TRUE; + con->smarterrorlog = TRUE; + con->smartselftestlog = TRUE; + con->selectivetestlog = TRUE; + break; + case 'v': + // parse vendor-specific definitions of attributes + if (!strcmp(optarg,"help")) { + char *s; + con->dont_print=FALSE; + printslogan(); + if (!(s = create_vendor_attribute_arg_list())) { + pout("Insufficient memory to construct argument list\n"); + EXIT(FAILCMD); + } + pout("The valid arguments to -v are:\n\thelp\n%s\n", s); + free(s); + EXIT(0); + } + charp=con->attributedefs; + if (!charp){ + pout("Fatal internal error in ParseOpts()\n"); + EXIT(FAILCMD); + } + if (parse_attribute_def(optarg, &charp)) + badarg = TRUE; + break; + case 'P': + if (!strcmp(optarg, "use")) { + con->ignorepresets = FALSE; + } else if (!strcmp(optarg, "ignore")) { + con->ignorepresets = TRUE; + } else if (!strcmp(optarg, "show")) { + con->showpresets = TRUE; + } else if (!strcmp(optarg, "showall")) { + if (optind < argc) { // -P showall MODEL [FIRMWARE] + int cnt = showmatchingpresets(argv[optind], (optind+1smartexeoffimmediate = TRUE; + con->testcase = OFFLINE_FULL_SCAN; + } else if (!strcmp(optarg,"short")) { + con->smartshortselftest = TRUE; + con->testcase = SHORT_SELF_TEST; + } else if (!strcmp(optarg,"long")) { + con->smartextendselftest = TRUE; + con->testcase = EXTEND_SELF_TEST; + } else if (!strcmp(optarg,"conveyance")) { + con->smartconveyanceselftest = TRUE; + con->testcase = CONVEYANCE_SELF_TEST; + } else if (!strcmp(optarg,"afterselect,on")) { + // scan remainder of disk after doing selected segments + con->scanafterselect=2; + } else if (!strcmp(optarg,"afterselect,off")) { + // don't scan remainder of disk after doing selected segments + con->scanafterselect=1; + } else if (!strncmp(optarg,"pending,",strlen("pending,"))) { + // parse number of minutes that test should be pending + int i; + char *tailptr=NULL; + errno=0; + i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10); + if (errno || *tailptr != '\0') { + sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n"); + badarg = TRUE; + } else if (i<0 || i>65535) { + sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); + badarg = TRUE; + } else { + con->pendingtime=i+1; + } + } else if (!strncmp(optarg,"select",strlen("select"))) { + // parse range of LBAs to test + uint64_t start, stop; + + if (split_selective_arg(optarg, &start, &stop)) { + sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n"); + badarg = TRUE; + } else { + if (con->smartselectivenumspans >= 5 || start > stop) { + if (start > stop) { + sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n", + start, stop, optarg); + } else { + sprintf(extraerror,"ERROR: No more than five selective self-test spans may be" + " defined\n"); + } + badarg = TRUE; + } + con->smartselectivespan[con->smartselectivenumspans][0] = start; + con->smartselectivespan[con->smartselectivenumspans][1] = stop; + con->smartselectivenumspans++; + con->testcase = SELECTIVE_SELF_TEST; + } + } else { + badarg = TRUE; + } + break; + case 'C': + captive = TRUE; + break; + case 'X': + con->smartselftestabort = TRUE; + con->testcase = ABORT_SELF_TEST; + break; + case 'h': + con->dont_print=FALSE; + printslogan(); + Usage(); + EXIT(0); + break; + case '?': + default: + con->dont_print=FALSE; + printslogan(); +#ifdef HAVE_GETOPT_LONG + // Point arg to the argument in which this option was found. + arg = argv[optind-1]; + // Check whether the option is a long option that doesn't map to -h. + if (arg[1] == '-' && optchar != 'h') { + // Iff optopt holds a valid option then argument must be missing. + if (optopt && (strchr(shortopts, optopt) != NULL)) { + pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); + printvalidarglistmessage(optopt); + } else + pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } +#endif + if (optopt) { + // Iff optopt holds a valid option then argument must be + // missing. Note (BA) this logic seems to fail using Solaris + // getopt! + if (strchr(shortopts, optopt) != NULL) { + pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt); + printvalidarglistmessage(optopt); + } else + pout("=======> UNRECOGNIZED OPTION: %c\n",optopt); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } + Usage(); + EXIT(0); + } // closes switch statement to process command-line options + + // Check to see if option had an unrecognized or incorrect argument. + if (badarg) { + printslogan(); + // It would be nice to print the actual option name given by the user + // here, but we just print the short form. Please fix this if you know + // a clean way to do it. + pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg); + printvalidarglistmessage(optchar); + if (extraerror[0]) + pout("=======> %s", extraerror); + UsageSummary(); + EXIT(FAILCMD); + } + } + // At this point we have processed all command-line options. If the + // print output is switchable, then start with the print output + // turned off + if (con->printing_switchable) + con->dont_print=TRUE; + + // error message if user has asked for more than one test + if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+ + con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort + (con->smartselectivenumspans>0?1:0))){ + con->dont_print=FALSE; + printslogan(); + pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // error message if user has set selective self-test options without + // asking for a selective self-test + if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){ + con->dont_print=FALSE; + printslogan(); + if (con->pendingtime) + pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); + else + pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // If captive option was used, change test type if appropriate. + if (captive && con->smartshortselftest) { + con->smartshortselftest = FALSE; + con->smartshortcapselftest = TRUE; + con->testcase = SHORT_CAPTIVE_SELF_TEST; + } else if (captive && con->smartextendselftest) { + con->smartextendselftest = FALSE; + con->smartextendcapselftest = TRUE; + con->testcase = EXTEND_CAPTIVE_SELF_TEST; + } + else if (captive && con->smartconveyanceselftest) { + con->smartconveyanceselftest = FALSE; + con->smartconveyancecapselftest = TRUE; + con->testcase = CONVEYANCE_CAPTIVE_SELF_TEST; + } + else if (captive && con->smartselectiveselftest) { + con->smartselectiveselftest = FALSE; + con->smartselectivecapselftest = TRUE; + con->testcase = SELECTIVE_CAPTIVE_SELF_TEST; + } + + // From here on, normal operations... + printslogan(); + + // Warn if the user has provided no device name + if (argc-optind<1){ + pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); + UsageSummary(); + EXIT(FAILCMD); + } + + // Warn if the user has provided more than one device name + if (argc-optind>1){ + int i; + pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); + pout("You have provided %d device names:\n",argc-optind); + for (i=0; idont_print) +// [From GLIBC Manual: Since the prototype doesn't specify types for +// optional arguments, in a call to a variadic function the default +// argument promotions are performed on the optional argument +// values. This means the objects of type char or short int (whether +// signed or not) are promoted to either int or unsigned int, as +// appropriate.] +void pout(char *fmt, ...){ + va_list ap; + + // initialize variable argument list + va_start(ap,fmt); + if (con->dont_print){ + va_end(ap); + return; + } + + // print out + vprintf(fmt,ap); + va_end(ap); + fflush(stdout); + return; +} + +// This function is used by utility.c to report LOG_CRIT errors. +// The smartctl version prints to stdout instead of syslog(). +void PrintOut(int priority, char *fmt, ...) { + va_list ap; + + // avoid warning message about unused variable from gcc -W: just + // change value of local copy. + priority=0; + + va_start(ap,fmt); + vprintf(fmt,ap); + va_end(ap); + return; +} + + +/* Main Program */ +int main (int argc, char **argv){ + int fd,retval=0; + char *device; + smartmonctrl control; + char *mode=NULL; + + // define control block for external functions + con=&control; + + // Part input arguments + ParseOpts(argc,argv); + + device = argv[argc-1]; + + // If use has specified 3ware controller, determine which interface + if (con->controller_type == CONTROLLER_3WARE) { + con->controller_type=guess_device_type(device); + if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR) + con->controller_type = CONTROLLER_3WARE_678K; + } + + if (con->controller_type == CONTROLLER_UNKNOWN) + con->controller_type=guess_device_type(device); + + if (con->controller_type == CONTROLLER_UNKNOWN) { + pout("Smartctl: please specify device type with the -d option.\n"); + UsageSummary(); + return FAILCMD; + } + + // set up mode for open() call. SCSI case is: + switch (con->controller_type) { + case CONTROLLER_SCSI: + mode="SCSI"; + break; + case CONTROLLER_3WARE_9000_CHAR: + mode="ATA_3WARE_9000"; + break; + case CONTROLLER_3WARE_678K_CHAR: + mode="ATA_3WARE_678K"; + break; + default: + mode="ATA"; + break; + } + + // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the + // scsi generic device can be used (needs write permission for MODE + // SELECT command) plus O_NONBLOCK to stop open hanging if media not + // present (e.g. with st). Opening is retried O_RDONLY if read-only + // media prevents opening O_RDWR (it cannot happen for scsi generic + // devices, but it can for the others). + fd = deviceopen(device, mode); + if (fd<0) { + char errmsg[256]; + snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]); + errmsg[255]='\0'; + syserror(errmsg); + return FAILDEV; + } + + // now call appropriate ATA or SCSI routine + switch (con->controller_type) { + case CONTROLLER_UNKNOWN: + // we should never fall into this branch! + pout("Smartctl: please specify device type with the -d option.\n"); + UsageSummary(); + retval = FAILCMD; + break; + case CONTROLLER_SCSI: + retval = scsiPrintMain(fd); + break; + default: + retval = ataPrintMain(fd); + break; + } + + return retval; +} diff --git a/smartctl.h b/smartctl.h new file mode 100644 index 0000000..cba0616 --- /dev/null +++ b/smartctl.h @@ -0,0 +1,74 @@ +/* + * smartctl.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef SMARTCTL_H_ +#define SMARTCTL_H_ + +#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.23 2006/04/12 14:54:28 ballen4705 Exp $\n" + +/* Boolean Values */ +#define TRUE 0x01 +#define FALSE 0x00 + +// Return codes (bitmask) + +// command line did not parse, or internal error occured in smartctl +#define FAILCMD (0x01<<0) + +// device open failed +#define FAILDEV (0x01<<1) + +// read device identity (ATA only) failed +#define FAILID (0x01<<1) + +// smart command failed, or ATA identify device structure missing information +#define FAILSMART (0x01<<2) + +// SMART STATUS returned FAILURE +#define FAILSTATUS (0x01<<3) + +// Attributes found <= threshold with prefail=1 +#define FAILATTR (0x01<<4) + +// SMART STATUS returned GOOD but age attributes failed or prefail +// attributes have failed in the past +#define FAILAGE (0x01<<5) + +// Device had Errors in the error log +#define FAILERR (0x01<<6) + +// Device had Errors in the self-test log +#define FAILLOG (0x01<<7) + +// Classes of SMART commands. Here 'mandatory' means "Required by the +// ATA/ATAPI-5 Specification if the device implements the S.M.A.R.T. +// command set." The 'mandatory' S.M.A.R.T. commands are: (1) +// Enable/Disable Attribute Autosave, (2) Enable/Disable S.M.A.R.T., +// and (3) S.M.A.R.T. Return Status. All others are optional. +#define OPTIONAL_CMD 1 +#define MANDATORY_CMD 2 + +void print_smartctl_examples(); + +#endif diff --git a/smartd.8.in b/smartd.8.in new file mode 100644 index 0000000..16b426a --- /dev/null +++ b/smartd.8.in @@ -0,0 +1,1844 @@ +.ig +Copyright (C) 2002-6 Bruce Allen + +$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $ + +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/ +.. +.TH SMARTD 8 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE +.SH NAME +\fBsmartd\fP \- SMART Disk Monitoring Daemon + +.SH SYNOPSIS +.B smartd [options] + +.SH FULL PATH +.B /usr/local/sbin/smartd + +.SH PACKAGE VERSION +CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME + +.SH DESCRIPTION +\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 will attempt to enable SMART monitoring on ATA devices +(equivalent to \fBsmartctl -s on\fP) and polls these and SCSI devices +every 30 minutes (configurable), logging SMART errors and changes of +SMART Attributes via the SYSLOG interface. The default location for +these SYSLOG notifications and warnings is \fB/var/log/messages\fP. +To change this default location, please see the \fB\'-l\'\fP +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 +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 +\fBsmartmontools\fP web page/FAQ for further guidance. + +If you send a \fBUSR1\fP signal to \fBsmartd\fP it will immediately +check the status of the disks, and then return to polling the disks +every 30 minutes. See the \fB\'\-i\'\fP option below for additional +details. + +\fBsmartd\fP can be configured at start-up using the configuration +file \fB/usr/local/etc/smartd.conf\fP (Windows: \fB./smartd.conf\fP). +If the configuration file is subsequently modified, \fBsmartd\fP +can be told to re-read the configuration file by sending it a +\fBHUP\fP signal, for example with the command: +.fi +\fBkillall -HUP smartd\fP. +.fi +(Windows: See NOTES below.) + +On startup, if \fBsmartd\fP finds a syntax error in the configuration +file, it will print an error message and then exit. However if +\fBsmartd\fP is already running, then is told with a \fBHUP\fP signal +to re-read the configuration file, and then find a syntax error in +this file, it will print an error message and then continue, ignoring +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 +same way as a \fBHUP\fP signal: it makes \fBsmartd\fP reload its +configuration file. To exit \fBsmartd\fP use CONTROL-\e +(Cygwin: 2x CONTROL\-C, Windows: CONTROL\-Break). + +On startup, in the absence of the configuration file +\fB/usr/local/etc/smartd.conf\fP, the \fBsmartd\fP daemon first scans for all +devices that support SMART. The scanning is done as follows: +.IP \fBLINUX:\fP 9 +Examine all entries \fB"/dev/hd[a-t]"\fP for IDE/ATA +devices, and \fB"/dev/sd[a-z]"\fP for SCSI devices. +.IP \fBFREEBSD:\fP 9 +Examine all entries \fB"/dev/ad[0-9]+"\fP for IDE/ATA +devices and \fB"/dev/da[0-9]+"\fP for SCSI devices. +.IP \fBNETBSD/OPENBSD:\fP 9 +Authoritative list of disk devices is obtained from sysctl +\'hw.disknames\'. +.IP \fBSOLARIS:\fP 9 +Examine 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. +.IP \fBDARWIN:\fP 9 +The IOService plane is scanned for ATA block storage devices. +.IP \fBWINDOWS:\fP 9 +Examine 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. +.IP \fBCYGWIN\fP: 9 +See "WINDOWS" above. +.IP \fBOS/2,eComStation\fP: 9 +Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices. +.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). + +.SH +OPTIONS +Long options are not supported on all systems. Use \fB\'smartd +\-h\'\fP to see the available options. +.TP +.B \-c FILE, \-\-configfile=FILE + +Read \fBsmartd\fP configuration Directives from FILE, instead of from +the default location \fB/usr/local/etc/smartd.conf\fP (Windows: \fB./smartd.conf\fP). +If FILE does \fBnot\fP exist, then \fBsmartd\fP will print an error +message and exit with nonzero status. Thus, \'\-c /usr/local/etc/smartd.conf\' +can be used to verify the existence of the default configuration file. + +By using \'\-\' for FILE, the configuration is read from standard +input. This is useful for commands like: +.nf +.B echo /dev/hdb \-m user@home \-M test | smartd \-c \- \-q onecheck +.fi +to perform quick and simple checks without a configuration file. + +.TP +.B \-d, \-\-debug +Runs \fBsmartd\fP in "debug" mode. In this mode, it displays status +information to STDOUT rather than logging it to SYSLOG and does not +\fBfork(2)\fP into the background and detach from the controlling +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 \fBQUIT\fP signal (normally generated from a +terminal with CONTROL\-C) makes \fBsmartd\fP reload its configuration +file. Please use CONTROL-\e to exit +(Cygwin: 2x CONTROL\-C, 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 +debug mode is enabled. +.TP +.B \-D, \-\-showdirectives +Prints a list (to STDOUT) of all the possible Directives which may +appear in the configuration file /usr/local/etc/smartd.conf, and then exits. +These Directives are also described later in this man page. They may +appear in the configuration file following the device name. +.TP +.B \-h, \-\-help, \-\-usage +Prints usage message to STDOUT and exits. +.TP +.B \-i N, \-\-interval=N +Sets the interval between disk checks to \fIN\fP seconds, where +\fIN\fP is a decimal integer. The minimum allowed value is ten and +the maximum is the largest positive integer that can be represented on +your system (often 2^31-1). The default is 1800 seconds. + +Note that the superuser can make \fBsmartd\fP check the status of the +disks at any time by sending it the \fBSIGUSR1\fP signal, for example +with the command: +.nf +.B kill -SIGUSR1 +.fi +where \fB\fP is the process id number of \fBsmartd\fP. One may +also use: +.nf +.B killall -USR1 smartd +.fi +for the same purpose. +.fi +(Windows: See NOTES below.) + +.TP +.B \-l FACILITY, \-\-logfacility=FACILITY +Uses syslog facility FACILITY to log the messages from \fBsmartd\fP. +Here FACILITY is one of \fIlocal0\fP, \fIlocal1\fP, ..., \fIlocal7\fP, +or \fIdaemon\fP [default]. If this command-line option is not used, +then by default messages from \fBsmartd\fP are logged to the facility +\fIdaemon\fP. + +If you would like to have \fBsmartd\fP messages logged somewhere other +than the default \fB/var/log/messages\fP location, this can typically +be accomplished with (for example) the following steps: +.RS 7 +.IP \fB[1]\fP 4 +Modify the script that starts \fBsmartd\fP to include the \fBsmartd\fP +command-line argument \'\-l local3\'. This tells \fBsmartd\fP to log its +messages to facility \fBlocal3\fP. +.IP \fB[2]\fP 4 +Modify the \fBsyslogd\fP configuration file (typically +\fB/etc/syslog.conf\fP) by adding a line of the form: +.nf +\fBlocal3.* /var/log/smartd.log\fP +.fi +This tells \fBsyslogd\fP to log all the messages from facility \fBlocal3\fP to +the designated file: /var/log/smartd.log. +.IP \fB[3]\fP 4 +Tell \fBsyslogd\fP to re-read its configuration file, typically by +sending the \fBsyslogd\fP process a \fBSIGHUP\fP hang-up signal. +.IP \fB[4]\fP 4 +Start (or restart) the \fBsmartd\fP daemon. +.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. +.TP +.B \& +For more detailed information, please refer to the man pages for +\fBsyslog.conf\fP, \fBsyslogd\fP, and \fBsyslog\fP. You may also want +to modify the log rotation configuration files; see the man pages for +\fBlogrotate\fP and examine your system\'s /etc/logrotate.conf file. + +Cygwin: Support for \fBsyslogd\fP as described above is available starting with Cygwin 1.5.15. +On older releases or if no local \fBsyslogd\fP is running, the \'\-l\' option has no effect. +In this case, all \fBsyslog\fP messages are written to Windows event log +or to file \fBC:/CYGWIN_SYSLOG.TXT\fP if the event log is not available. + +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, +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. + +.TP +.B \-p NAME, \-\-pidfile=NAME +Writes pidfile \fINAME\fP containing the \fBsmartd\fP Process ID +number (PID). To avoid symlink attacks make sure the directory to +which pidfile is written is only writable for root. Without this +option, or if the \-\-debug option is given, no PID file is written on +startup. If \fBsmartd\fP is killed with a maskable signal then the +pidfile is removed. +.TP +.B \-q WHEN, \-\-quit=WHEN +Specifies when, if ever, \fBsmartd\fP should exit. The valid +arguments are to this option are: + +.I nodev +\- Exit if there are no devices to monitor, or if any errors are found +at startup in the configuration file. This is the default. + +.I errors +\- Exit if there are no devices to monitor, or if any errors are found +in the configuration file /usr/local/etc/smartd.conf at startup or whenever it +is reloaded. + +.I nodevstartup +\- Exit if there are no devices to monitor at startup. But continue +to run if no devices are found whenever the configuration file is +reloaded. + +.I never +\- Only exit if a fatal error occurs (no remaining system memory, +invalid command line arguments). In this mode, even if there are no +devices to monitor, or if the configuration file +\fB/usr/local/etc/smartd.conf\fP has errors, \fBsmartd\fP will continue to run, +waiting to load a configuration file listing valid devices. + +.I onecheck +\- Start \fBsmartd\fP in debug mode, then register devices, then check +device\'s SMART status once, and then exit with zero exit status if all +of these steps worked correctly. + +This last option is intended for \'distribution-writers\' who want to +create automated scripts to determine whether or not to automatically +start up \fBsmartd\fP after installing smartmontools. After starting +\fBsmartd\fP with this command-line option, the distribution\'s install +scripts should wait a reasonable length of time (say ten seconds). If +\fBsmartd\fP has not exited with zero status by that time, the script +should send \fBsmartd\fP a SIGTERM or SIGKILL and assume that +\fBsmartd\fP will not operate correctly on the host. Conversely, if +\fBsmartd\fP exits with zero status, then it is safe to run +\fBsmartd\fP in normal daemon mode. If \fBsmartd\fP is unable to +monitor any devices or encounters other problems then it will return +with non-zero exit status. + +.I showtests +\- Start \fBsmartd\fP in debug mode, then register devices, then write +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 +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. +.TP +.B \-r TYPE, \-\-report=TYPE +Intended primarily to help +.B smartmontools +developers understand the behavior of +.B smartmontools +on non-conforming or poorly-conforming hardware. This option reports +details of +\fBsmartd\fP +transactions with the device. The option can be used multiple times. +When used just once, it shows a record of the ioctl() transactions +with the device. When used more than once, the detail of these ioctl() +transactions are reported in greater detail. The valid arguments to +this option are: + +.I ioctl +\- report all ioctl() transactions. + +.I ataioctl +\- report only ioctl() transactions with ATA devices. + +.I scsiioctl +\- report only ioctl() transactions with SCSI devices. + +Any argument may include a positive integer to specify the level of +detail that should be reported. The argument should be followed by a +comma then the integer with no spaces. For example, \fIataioctl,2\fP +The default level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are +equivalent. + +.TP +.B \-\-service +Cygwin and Windows only: Enables \fBsmartd\fP to run as a Windows service. + +On Cygwin, this option simply prevents fork'ing into background mode to +allow running \fBsmartd\fP as service via cygrunsrv, see NOTES below. + +On Windows, this option enables the buildin service support. +The option must be specified in the service command line as the first +argument. It should not be used from console. +See NOTES below for details. + +.TP +.B \-V, \-\-version, \-\-license, \-\-copyright +Prints license, copyright, and CVS version information onto +STDOUT and then exits. Please include this information if you are +reporting bugs, or have specific questions about the behavior of +\fBsmartd\fP. + +.SH EXAMPLES + +.B +smartd +.fi +Runs the daemon in forked mode. This is the normal way to run +\fBsmartd\fP. +Entries are logged to SYSLOG (by default +.B /var/log/messages.) + +.B +smartd -d -i 30 +.fi +Run in foreground (debug) mode, checking the disk status +every 30 seconds. + +.B +smartd -q onecheck +.fi +Registers devices, and checks the status of the devices exactly +once. The exit status (the bash +.B $? +variable) will be zero if all went well, and nonzero if no devices +were detected or some other problem was encountered. + +.fi +Note that \fBsmartmontools\fP provides a start-up script in +\fB/usr/local/etc/rc.d/init.d/smartd\fP which is responsible for starting and +stopping the daemon via the normal init interface. Using this script, +you can start \fBsmartd\fP by giving the command: +.nf +.B /usr/local/etc/rc.d/init.d/smartd start +.fi +and stop it by using the command: +.nf +.B /usr/local/etc/rc.d/init.d/smartd stop + +.fi +If you want \fBsmartd\fP to start running whenever your machine is +booted, this can be enabled by using the command: +.nf +.B /sbin/chkconfig --add smartd +.fi +and disabled using the command: +.nf +.B /sbin/chkconfig --del smartd +.fi + +.\" DO NOT MODIFY THIS OR THE FOLLOWING TWO LINES. THIS MATERIAL +.\" IS AUTOMATICALLY INCLUDED IN THE FILE smartd.conf.5 +.\" STARTINCLUDE + +.SH CONFIGURATION FILE /usr/local/etc/smartd.conf +In the absence of a configuration file, 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]. +Under FreeBSD, +\fBsmartd\fP +will try to open all existing ATA devices (with entries in /dev) +.B /dev/ad[0-9]+ +and all existing SCSI devices +.B /dev/da[0-9]+. +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. +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. +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. +Under Darwin, \fBsmartd\fP will open any ATA block storage device. + +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. + +One can avoid this problem, and gain more control over the types of +events monitored by +\fBsmartd\fP, +by using the configuration file +.B /usr/local/etc/smartd.conf. +This file contains a list of devices to monitor, with one device per +line. An example file is included with the +.B smartmontools +distribution. You will find this sample configuration file in +\fB/usr/local/share/doc/smartmontools-5.1/\fP. For security, the configuration file +should not be writable by anyone but root. The syntax of the file is as +follows: +.IP \(bu 4 +There should be one device listed per line, although you may have +lines that are entirely comments or white space. +.IP \(bu 4 +Any text following a hash sign \'#\' and up to the end of the line is +taken to be a comment, and ignored. +.IP \(bu 4 +Lines may be continued by using a backslash \'\e\' as the last +non-whitespace or non-comment item on a line. +.IP \(bu 4 +Note: a line whose first character is a hash sign \'#\' is treated as +a white-space blank line, \fBnot\fP as a non-existent line, and will +\fBend\fP a continuation line. +.PP 0 +.fi +Here is an example configuration file. It\'s for illustrative purposes +only; please don\'t copy it onto your system without reading to the end +of the +.B DIRECTIVES +Section below! + +.nf +.B ################################################ +.B # This is an example smartd startup config file +.B # /usr/local/etc/smartd.conf for monitoring three +.B # ATA disks, three SCSI disks, six ATA disks +.B # behind two 3ware controllers and one SATA disk +.B # +.nf +.B # First ATA disk on two different interfaces. On +.B # the second disk, start a long self-test every +.B # Sunday between 3 and 4 am. +.B # +.B \ \ /dev/hda -a -m admin@example.com,root@localhost +.B \ \ /dev/hdc -a -I 194 -I 5 -i 12 -s L/../../7/03 +.B # +.nf +.B # SCSI disks. Send a TEST warning email to admin on +.B # startup. +.B # +.B \ \ /dev/sda +.B \ \ /dev/sdb -m admin@example.com -M test +.B # +.nf +.B # Strange device. It\'s SCSI. Start a scheduled +.B # long self test between 5 and 6 am Monday/Thursday +.B \ \ /dev/weird -d scsi -s L/../../(1|4)/05 +.B # +.nf +.B # Linux-specific: SATA disk using the libata +.B # driver. This requires a 2.6.15 or greater +.B # kernel. The device entry is SCSI but the +.B # underlying disk understands ATA SMART commands +.B \ \ /dev/sda -a -d ata +.B # +.nf +.B # Four ATA disks on a 3ware 6/7/8000 controller. +.B # Start short self-tests daily between midnight and 1am, +.B # 1-2, 2-3, and 3-4 am. Starting with the Linux 2.6 +.B # kernel series, /dev/sdX is deprecated in favor of +.B # /dev/tweN. For example replace /dev/sdc by /dev/twe0 +.B # and /dev/sdd by /dev/twe1. +.B \ \ /dev/sdc -d 3ware,0 -a -s S/../.././00 +.B \ \ /dev/sdc -d 3ware,1 -a -s S/../.././01 +.B \ \ /dev/sdd -d 3ware,2 -a -s S/../.././02 +.B \ \ /dev/sdd -d 3ware,3 -a -s S/../.././03 +.B # +.nf +.B # Two ATA disks on a 3ware 9000 controller. +.B # Start long self-tests Sundays between midnight and +.B # 1am and 2-3 am +.B \ \ /dev/twa0 -d 3ware,0 -a -s L/../../7/00 +.B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02 +.B # +.nf +.B # The following line enables monitoring of the +.B # ATA Error Log and the Self-Test Error Log. +.B # It also tracks changes in both Prefailure +.B # and Usage Attributes, apart from Attributes +.B # 9, 194, and 231, and shows continued lines: +.B # +.B \ \ /dev/hdd\ -l\ error\ \e +.B \ \ \ \ \ \ \ \ \ \ \ -l\ selftest\ \e +.B \ \ \ \ \ \ \ \ \ \ \ -t\ \e\ \ \ \ \ \ # Attributes not tracked: +.B \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \e\ \ # temperature +.B \ \ \ \ \ \ \ \ \ \ \ -I\ 231\ \e\ \ # also temperature +.B \ \ \ \ \ \ \ \ \ \ \ -I 9\ \ \ \ \ \ # power-on hours +.B # +.B ################################################ +.fi + +.PP +.SH CONFIGURATION FILE DIRECTIVES +.PP + +If the first non-comment entry in the configuration file is the text +string +.B DEVICESCAN +in capital letters, then +\fBsmartd\fP +will ignore any remaining lines in the configuration file, and will +scan for devices. +.B DEVICESCAN +may optionally be followed by Directives that will apply to all +devices that are found in the scan. Please see below for additional +details. + +.sp 2 +The following are the Directives that may appear following the device +name or +.B DEVICESCAN +on any line of the +.B /usr/local/etc/smartd.conf +configuration file. Note that +.B these are NOT command-line options for +\fBsmartd\fP. +The Directives below may appear in any order, following the device +name. + +.B For an ATA device, +if no Directives appear, then the device will be monitored +as if the \'\-a\' Directive (monitor all SMART properties) had been given. + +.B If a SCSI disk is listed, +it will be monitored at the maximum implemented level: roughly +equivalent to using the \'\-H \-l selftest\' options for an ATA disk. +So with the exception of \'\-d\', \'\-m\', \'\-l selftest\', \'\-s\', and +\'\-M\', the Directives below are ignored for SCSI disks. For SCSI +disks, the \'\-m\' Directive sends a warning email if the SMART status +indicates a disk failure or problem, if the SCSI inquiry about disk +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? +or /dev/twa?) 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). + +.TP +.B \-d TYPE +Specifies the type of the device. This Directive may be used multiple +times for one device, but the arguments \fIata\fP, \fIscsi\fP, +\fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than +one is given then \fBsmartd\fP will use the last one which appears. + +If none of these three arguments is given, then \fBsmartd\fP will +first attempt to guess the device type by looking at whether the sixth +character in the device name is an \'s\' or an \'h\'. This will work for +device names like /dev/hda or /dev/sdb, and corresponds to choosing +\fIata\fP or \fIscsi\fP respectively. If +\fBsmartd\fP +can\'t guess from this sixth character, then it will simply try to +access the device using first ATA and then SCSI ioctl()s. + +The valid arguments to this Directive are: + +.I ata +\- the device type is ATA. This prevents +\fBsmartd\fP +from issuing SCSI commands to an ATA device. + +.I scsi +\- the device type is SCSI. This prevents +\fBsmartd\fP +from issuing ATA commands to a SCSI device. + +.I marvell +\- Under Linux, interact with SATA disks behind Marvell chip-set +controllers (using the Marvell rather than libata driver). + +.I 3ware,N +\- the device consists of one or more ATA disks connected to a 3ware +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 3ware_disk_XX +with XX in the range from 00 to 15 inclusive. + +This Directive may at first appear confusing, because the 3ware +controller is a SCSI device (such as /dev/sda) and should be listed as +such in the the configuration file. +However when the \'\-d 3ware,N\' +Directive is used, then the corresponding disk is addressed using +native ATA commands which are \'passed through\' the SCSI driver. All +ATA Directives listed in this man page may be used. Note that while +you may use \fBany\fP of the 3ware SCSI logical devices /dev/sd? to +address \fBany\fP of the physical disks (3ware ports), error and log +messages will make the most sense if you always list the 3ware SCSI +logical device corresponding to the particular physical disks. Please +see the \fBsmartctl\fP man page for further details. + +ATA disks behind 3ware controllers may alternatively be accessed via a +character device interface /dev/twe0-15 (3ware 6000/7000/8000 +controllers) and /dev/twa0-15 (3ware 9000 series controllers). Note +that the 9000 series controllers may \fBonly\fP be accessed using the +character device interface /dev/twa0-15 and not the SCSI device +interface /dev/sd?. Please see the \fBsmartctl\fP man page for +further details. + +Note that 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, if the SCSI interface is used, and produce these types of +harmless syslog error messages instead: \fB\'3w-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 versions. See +\fBhttp://smartmontools.sourceforge.net/\fP for instructions. +Alternatively use the character device interfaces /dev/twe0-15 (3ware +6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series +controllers). + + +.B 3ware controllers are currently ONLY supported under Linux. + +.I removable +\- the device or its media is removable. This indicates to +\fBsmartd\fP +that it should continue (instead of exiting, which is the default +behavior) if the device does not appear to be present when +\fBsmartd\fP is started. This Directive may be used in conjunction +with the other \'\-d\' Directives. + +.TP +.B \-n POWERMODE[,q] +This \'nocheck\' Directive is used to prevent a disk from being +spun-up when it is periodically polled by \fBsmartd\fP. + +ATA disks have five different power states. In order of increasing +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 +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. + +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 +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 +\fBsmartd\fP checks it. This is the default behavior if the '\-n' +Directive is not given. + +.I sleep +\- check the device unless it is in SLEEP mode. + +.I standby +\- check the device unless it is in SLEEP or STANDBY mode. In +these modes most disks are not spinning, so if you want to prevent +a laptop disk from spinning up each time that \fBsmartd\fP polls, +this is probably what you want. + +.I idle +\- check the device unless it is in SLEEP, STANDBY or IDLE mode. +In the IDLE state, most disks are still spinning, so this is probably +not what you want. + +When a periodic test is skipped, \fBsmartd\fP normally writes an +informal log message. The message can be suppressed by appending +the option \',q\' to POWERMODE (like \'\-n standby,q\'). +This prevents a laptop disk from spinning up due to this message. + +.TP +.B \-T TYPE +Specifies how tolerant +\fBsmartd\fP +should be of SMART command failures. The valid arguments to this +Directive are: + +.I normal +\- do not try to monitor the disk if a mandatory SMART command fails, but +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. + +[Please see the \fBsmartctl \-T\fP command-line option.] +.TP +.B \-o VALUE +Enables or disables SMART Automatic Offline Testing when +\fBsmartd\fP +starts up and has no further effect. The valid arguments to this +Directive are \fIon\fP and \fIoff\fP. + +The delay between tests is vendor-specific, but is typically four +hours. + +Note that SMART Automatic Offline Testing is \fBnot\fP part of the ATA +Specification. Please see the +.B smartctl \-o +command-line option documentation for further information about this +feature. +.TP +.B \-S VALUE +Enables or disables Attribute Autosave when \fBsmartd\fP +starts up and has no further effect. The valid arguments to this +Directive are \fIon\fP and \fIoff\fP. Also affects SCSI devices. +[Please see the \fBsmartctl \-S\fP command-line option.] +.TP +.B \-H +Check the SMART health status of the disk. If any Prefailure +Attributes are less than or equal to their threshold values, then disk +failure is predicted in less than 24 hours, and a message at loglevel +.B \'LOG_CRITICAL\' +will be logged to syslog. [Please see the +.B smartctl \-H +command-line option.] +.TP +.B \-l TYPE +Reports increases in the number of errors in one of the two SMART logs. The +valid arguments to this Directive are: + +.I error +\- report if the number of ATA errors reported in the ATA Error Log +has increased since the last check. + +.I selftest +\- report if the number of failed tests reported in the SMART +Self-Test Log has increased since the last check, or if the timestamp +associated with the most recent failed test has increased. Note that +such errors will \fBonly\fP be logged if you run self-tests on the +disk (and it fails a test!). Self-Tests can be run automatically by +\fBsmartd\fP: please see the \fB\'\-s\'\fP Directive below. +Self-Tests can also be run manually by using the \fB\'\-t\ short\'\fP +and \fB\'\-t\ long\'\fP options of \fBsmartctl\fP and the results of +the testing can be observed using the \fBsmartctl \'\-l\ selftest\'\fP +command-line option.] + +[Please see the \fBsmartctl \-l\fP and \fB\-t\fP command-line +options.] +.TP +.B \-s REGEXP +Run Self-Tests or Offline Immediate Tests, at scheduled times. A +Self- or Offline Immediate Test will be run at the end of periodic +device polling, if all 12 characters of the string \fBT/MM/DD/d/HH\fP +match the extended regular expression \fBREGEXP\fP. Here: +.RS 7 +.IP \fBT\fP 4 +is the type of the test. The values that \fBsmartd\fP will try to +match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a +\fBS\fPhort Self-Test, \'C\' for a \fBC\fPonveyance Self-Test (ATA +only), and \'O\' for an \fBO\fPffline Immediate Test (ATA only). As +soon as a match is found, the test will be started and no additional +matches will be sought for that device and that polling cycle. +.IP \fBMM\fP 4 +is the month of the year, expressed with two decimal digits. The +range is from 01 (January) to 12 (December) inclusive. Do \fBnot\fP +use a single decimal digit or the match will always fail! +.IP \fBDD\fP 4 +is the day of the month, expressed with two decimal digits. The +range is from 01 to 31 inclusive. Do \fBnot\fP +use a single decimal digit or the match will always fail! +.IP \fBd\fP 4 +is the day of the week, expressed with one decimal digit. The +range is from 1 (Monday) to 7 (Sunday) inclusive. +.IP \fBHH\fP 4 +is the hour of the day, written with two decimal digits, and given in +hours after midnight. The range is 00 (midnight to just before 1am) +to 23 (11pm to just before midnight) inclusive. Do \fBnot\fP use a +single decimal digit or the match will always fail! +.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. +.TP +.B \& +Some examples follow. In reading these, keep in mind that in extended +regular expressions a dot \fB\'.\'\fP matches any single character, and +a parenthetical expression such as \fB\'(A|B|C)\'\fP denotes any one of the three possibilities \fBA\fP, +\fBB\fP, or \fBC\fP. + +To schedule a short Self-Test between 2-3am every morning, use: +.nf +\fB \-s S/../.././02\fP +.fi +To schedule a long Self-Test between 4-5am every Sunday morning, use: +.nf +\fB \-s L/../../7/04\fP +.fi +To schedule a long Self-Test between 10-11pm on the first and +fifteenth day of each month, use: +.nf +\fB \-s L/../(01|15)/./22\fP +.fi +To schedule an Offline Immediate test after every midnight, 6am, +noon,and 6pm, plus a Short Self-Test daily at 1-2am and a Long +Self-Test every Saturday at 3-4am, use: +.nf +\fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP +.fi + +Scheduled tests are run immediately following the regularly-scheduled +device polling, if the current local date, time, and test type, match +\fBREGEXP\fP. By default the regularly-scheduled device polling +occurs every thirty minutes after starting \fBsmartd\fP. Take caution +if you use the \'\-i\' option to make this polling interval more than +sixty minutes: the poll times may fail to coincide with any of the +testing times that you have specified with \fBREGEXP\fP, and so the +self tests may not take place as you wish. + +Before running an offline or self-test, \fBsmartd\fP checks to be sure +that a self-test is not already running. If a self-test \fBis\fP +already running, then this running self test will \fBnot\fP be +interrupted to begin another test. + +\fBsmartd\fP will not attempt to run \fBany\fP type of test if another +test was already started or run in the same hour. + +Each time a test is run, \fBsmartd\fP will log an entry to SYSLOG. +You can use these or the '-q showtests' command-line option to verify +that you constructed \fBREGEXP\fP correctly. The matching order +(\fBL\fP before \fBS\fP before \fBC\fP before \fBO\fP) ensures that +if multiple test types are all scheduled for the same hour, the +longer test type has precedence. This is usually the desired behavior. + +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 +issue harmless informational warning messages if it detects characters +in \fBREGEXP\fP that appear to indicate that you have made this +mistake. + +.TP +.B \-m ADD +Send a warning email to the email address \fBADD\fP if the \'\-H\', +\'\-l\', \'\-f\', \'\-C\', or \'\-O\' Directives detect a failure or a +new error, or if a SMART command to the disk fails. This Directive +only works in conjunction with these other Directives (or with the +equivalent default \'\-a\' Directive). + +To prevent your email in-box from getting filled up with warning +messages, by default only a single warning will be sent for each of +the enabled alert types, \'\-H\', \'\-l\', \'\-f\', \'\-C\', or +\'\-O\' even if more than one failure or error is detected or if the +failure or error persists. [This behavior can be modified; see the +\'\-M\' Directive below.] + +To send email to more than one user, please use the following "comma +separated" form for the address: \fBuser1@add1,user2@add2,...,userN@addN\fP +(with no spaces). + +To test that email is being sent correctly, use the \'\-M test\' +Directive described below to send one test email message on +\fBsmartd\fP +startup. + +By default, email is sent using the system +.B mail +command. In order that +\fBsmartd\fP +find the mail command (normally /bin/mail) an executable named +.B \'mail\' +must be in the path of the shell or environment from which +\fBsmartd\fP +was started. If you wish to specify an explicit path to the mail +executable (for example /usr/local/bin/mail) or a custom script to +run, please use the \'\-M exec\' Directive below. + +Note that by default under Solaris, in the previous paragraph, +\'\fBmailx\fP\' and \'\fB/bin/mailx\fP\' are used, since Solaris +\'/bin/mail\' does not accept a \'\-s\' (Subject) command-line +argument. + +On Windows, the \'\fBBlat\fP\' mailer +(\fBhttp://blat.sourceforge.net/\fP) is used by default. +This mailer uses a different command line syntax, see +\'\-M exec\' below. + +Note also that there is a special argument +.B +which can be given to the \'\-m\' Directive in conjunction with the \'\-M +exec\' Directive. Please see below for an explanation of its effect. + +If the mailer or the shell running it produces any STDERR/STDOUT +output, then a snippet of that output will be copied to SYSLOG. The +remainder of the output is discarded. If problems are encountered in +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. + +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. + +.TP +.B \-M TYPE +These Directives modify the behavior of the +\fBsmartd\fP +email warnings enabled with the \'\-m\' email Directive described above. +These \'\-M\' Directives only work in conjunction with the \'\-m\' +Directive and can not be used without it. + +Multiple \-M Directives may be given. If more than one of the +following three \-M Directives are given (example: \-M once \-M daily) +then the final one (in the example, \-M daily) is used. + +The valid arguments to the \-M Directive are (one of the following +three): + +.I once +\- send only one warning email for each type of disk problem detected. This +is the default. + +.I daily +\- send additional warning reminder emails, once per day, for each type +of disk problem detected. + +.I diminishing +\- send additional warning reminder emails, after a one-day interval, +then a two-day interval, then a four-day interval, and so on for each +type of disk problem detected. Each interval is twice as long as the +previous interval. + +In addition, one may add zero or more of the following Directives: + +.I test +\- send a single test email +immediately upon +\fBsmartd\fP +startup. This allows one to verify that email is delivered correctly. + +.I exec PATH +\- run the executable PATH instead of the default mail command, when +\fBsmartd\fP +needs to send email. PATH must point to an executable binary file or +script. + +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 +/usr/local/share/doc/smartmontools-5.1/examplescripts/. + +The return status of the executable is recorded by \fBsmartd\fP in +SYSLOG. The executable is not expected to write to STDOUT or +STDERR. If it does, then this is interpreted as indicating that +something is going wrong with your executable, and a fragment of this +output is logged to SYSLOG to help you to understand the problem. +Normally, if you wish to leave some record behind, the executable +should send mail or write to a file or device. + +Before running the executable, \fBsmartd\fP sets a number of +environment variables. These environment variables may be used to +control the executable\'s behavior. The environment variables +exported by \fBsmartd\fP are: +.RS 7 +.IP \fBSMARTD_MAILER\fP 4 +is set to the argument of \-M exec, if present or else to \'mail\' +(examples: /bin/mail, mail). +.IP \fBSMARTD_DEVICE\fP 4 +is set to the device path (examples: /dev/hda, /dev/sdb). +.IP \fBSMARTD_DEVICETYPE\fP 4 +is set to the device type (possible values: ata, scsi, 3ware,N). Here +N=0,...,15 denotes the ATA disk behind a 3ware RAID controller. +.IP \fBSMARTD_DEVICESTRING\fP 4 +is set to the device description. For SMARTD_DEVICETYPE of ata or +scsi, this is the same as SMARTD_DEVICE. For 3ware RAID controllers, +the form used is \'/dev/sdc [3ware_disk_01]\'. In this case 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_FAILTYPE\fP 4 +gives the reason for the warning or message email. The possible values that +it takes and their meanings are: +.nf +.fi +\fIEmailTest\fP: this is an email test message. +.nf +.fi +\fIHealth\fP: the SMART health status indicates imminent failure. +.nf +.fi +\fIUsage\fP: a usage Attribute has failed. +.nf +.fi +\fISelfTest\fP: the number of self-test failures has increased. +.nf +.fi +\fIErrorCount\fP: the number of errors in the ATA error log has increased. +.nf +.fi +\fICurrentPendingSector\fP: one of more disk sectors could not be +read and are marked to be reallocated (replaced with spare sectors). +.nf +.fi +\fIOfflineUncorrectableSector\fP: during off\-line testing, or self\-testing, +one or more disk sectors could not be read. +.nf +.fi +\fIFailedHealthCheck\fP: the SMART health status command failed. +.nf +.fi +\fIFailedReadSmartData\fP: the command to read SMART Attribute data failed. +.nf +.fi +\fIFailedReadSmartErrorLog\fP: the command to read the SMART error log failed. +.nf +.fi +\fIFailedReadSmartSelfTestLog\fP: the command to read the SMART self-test log failed. +.nf +.fi +\fIFailedOpenDevice\fP: the open() command to the device failed. +.IP \fBSMARTD_ADDRESS\fP 4 +is determined by the address argument ADD of the \'\-m\' Directive. +If ADD is \fB\fP, then \fBSMARTD_ADDRESS\fP is not set. +Otherwise, it is set to the comma-separated-list of email addresses +given by the argument ADD, with the commas replaced by spaces +(example:admin@example.com root). If more than one email address is +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. +.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. +.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. +.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 +and no newlines, and is NOT quoted. For example: +.nf +.fi +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. +.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. +.TP +.B \& +The shell which is used to run PATH is system-dependent. For vanilla +Linux/glibc it\'s bash. For other systems, the man page for +\fBpopen\fP(3) should say what shell is used. + +If the \'\-m ADD\' Directive is given with a normal address argument, +then the executable pointed to by PATH will be run in a shell with +STDIN receiving the body of the email message, and with the same +command-line arguments: +.nf +-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS +.fi +that would normally be provided to \'mail\'. Examples include: +.nf +.B -m user@home -M exec /bin/mail +.B -m admin@work -M exec /usr/local/bin/mailto +.B -m root -M exec /Example_1/bash/script/below +.fi + +Note that on Windows, the syntax of the \'\fBBlat\fP\' mailer is +used: +.nf +- -q -subject "$SMARTD_SUBJECT" -to "$SMARTD_ADDRESS" +.fi + +If the \'\-m ADD\' Directive is given with the special address argument +.B +then the executable pointed to by PATH is run in a shell with +.B no +STDIN and +.B no +command-line arguments, for example: +.nf +.B -m -M exec /Example_2/bash/script/below +.fi +If the executable produces any STDERR/STDOUT output, then \fBsmartd\fP +assumes that something is going wrong, and a snippet of that output +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 +/usr/local/share/doc/smartmontools-5.1/examplescripts/. + +.TP +.B \-f +Check for \'failure\' of any Usage Attributes. If these Attributes are +less than or equal to the threshold, it does NOT indicate imminent +disk failure. It "indicates an advisory condition where the usage or +age of the device has exceeded its intended design life period." +[Please see the \fBsmartctl \-A\fP command-line option.] +.TP +.B \-p +Report anytime that a Prefail Attribute has changed +its value since the last check, 30 minutes ago. [Please see the +.B smartctl \-A +command-line option.] +.TP +.B \-u +Report anytime that a Usage Attribute has changed its value +since the last check, 30 minutes ago. [Please see the +.B smartctl \-A +command-line option.] +.TP +.B \-t +Equivalent to turning on the two previous flags \'\-p\' and \'\-u\'. +Tracks changes in \fIall\fP device Attributes (both Prefailure and +Usage). [Please see the \fBsmartctl\fP \-A command-line option.] +.TP +.B \-i ID +Ignore device Attribute number \fBID\fP when checking for failure of +Usage Attributes. \fBID\fP must be a decimal integer in the range +from 1 to 255. This Directive modifies the behavior of the \'\-f\' +Directive and has no effect without it. + +This is useful, for example, if you have a very old disk and don\'t +want to keep getting messages about the hours-on-lifetime Attribute +(usually Attribute 9) failing. This Directive may appear multiple +times for a single device, if you want to ignore multiple Attributes. +.TP +.B \-I ID +Ignore device Attribute \fBID\fP when tracking changes in the +Attribute values. \fBID\fP must be a decimal integer in the range +from 1 to 255. This Directive modifies the behavior of the \'\-p\', +\'\-u\', and \'\-t\' tracking Directives and has no effect without one +of them. + +This is useful, for example, if one of the device Attributes is the disk +temperature (usually Attribute 194 or 231). It\'s annoying to get reports +each time the temperature changes. This Directive may appear multiple +times for a single device, if you want to ignore multiple Attributes. +.TP +.B \-r ID +When tracking, report the \fIRaw\fP value of Attribute \fBID\fP along +with its (normally reported) \fINormalized\fP value. \fBID\fP must be +a decimal integer in the range from 1 to 255. This Directive modifies +the behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives +and has no effect without one of them. This Directive may be given +multiple times. + +A common use of this Directive is to track the device Temperature +(often ID=194 or 231). + +.TP +.B \-R ID +When tracking, report whenever the \fIRaw\fP value of Attribute +\fBID\fP changes. (Normally \fBsmartd\fP only tracks/reports changes +of the \fINormalized\fP Attribute values.) \fBID\fP must be a decimal +integer in the range from 1 to 255. This Directive modifies the +behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives and +has no effect without one of them. This Directive may be given +multiple times. + +If this Directive is given, it automatically implies the \'\-r\' +Directive for the same Attribute, so that the Raw value of the +Attribute is reported. + +A common use of this Directive is to track the device Temperature +(often ID=194 or 231). It is also useful for understanding how +different types of system behavior affects the values of certain +Attributes. + +.TP +.B \-C ID +[ATA only] Report if the current number of pending sectors is +non-zero. Here \fBID\fP is the id number of the Attribute whose raw +value is the Current Pending Sector count. The allowed range of +\fBID\fP is 0 to 255 inclusive. To turn off this reporting, use +ID\ =\ 0. If the \fB\-C ID\fP option is not given, then it defaults to +\fB\-C 197\fP (since Attribute 197 is generally used to monitor +pending sectors). + +A pending sector is a disk sector (containing 512 bytes of your data) +which the device would like to mark as ``bad" and reallocate. +Typically this is because your computer tried to read that sector, and +the read failed because the data on it has been corrupted and has +inconsistent Error Checking and Correction (ECC) codes. This is +important to know, because it means that there is some unreadable data +on the disk. The problem of figuring out what file this data belongs +to is operating system and file system specific. You can typically +force the sector to reallocate by writing to it (translation: make the +device substitute a spare good sector for the bad one) but at the +price of losing the 512 bytes of data stored there. + +.TP +.B \-U ID +[ATA only] Report if the number of offline uncorrectable sectors is +non-zero. Here \fBID\fP is the id number of the Attribute whose raw +value is the Offline Uncorrectable Sector count. The allowed range of +\fBID\fP is 0 to 255 inclusive. To turn off this reporting, use +ID\ =\ 0. If the \fB\-U ID\fP option is not given, then it defaults to +\fB\-U 198\fP (since Attribute 198 is generally used to monitor +offline uncorrectable sectors). + + +An offline uncorrectable sector is a disk sector which was not +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. + +.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: + +.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. + +.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). +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; +(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 more recent Samsung disks (firmware revisions ending in "\-23") the +number of ATA errors reported is byte swapped. Enabling this option +tells \fBsmartd\fP to evaluate this quantity in byte-reversed order. + +Note that an explicit \'\-F\' Directive will over-ride any preset +values for \'\-F\' (see the \'\-P\' option below). + + +[Please see the \fBsmartctl \-F\fP command-line option.] + +.TP +.B \-v N,OPTION +Modifies the labeling for Attribute N, for disks which use +non-standard Attribute definitions. This is useful in connection with +the Attribute tracking/reporting Directives. + +This Directive may appear multiple times. Valid arguments to this +Directive are: + +.I 9,minutes +\- Raw Attribute number 9 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 9,seconds +\- Raw Attribute number 9 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 9,halfminutes +\- Raw Attribute number 9 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 9,temp +\- Raw Attribute number 9 is the disk temperature in Celsius. + +.I 192,emergencyretractcyclect +\- Raw Attribute number 192 is the Emergency Retract Cycle Count. + +.I 193,loadunload +\- Raw Attribute number 193 contains two 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 +unload). As a rule of thumb, the mechanical stress created by one +emergency unload is equivalent to that created by one hundred normal +unloads. + +.I 194,10xCelsius +\- Raw Attribute number 194 is ten times the disk temperature in +Celsius. This is used by some Samsung disks (example: model SV1204H +with RK100-13 firmware). + +.I 194,unknown +\- Raw Attribute number 194 is NOT the disk temperature, and its +interpretation is unknown. This is primarily useful for the -P +(presets) Directive. + +.I 198,offlinescanuncsectorct +\- Raw Attribute number 198 is the Offline Scan UNC Sector Count. + +.I 200,writeerrorcount +\- Raw Attribute number 200 is the Write Error Count. + +.I 201,detectedtacount +\- Raw Attribute number 201 is the Detected TA Count. + +.I 220,temp +\- Raw Attribute number 220 is the disk temperature in Celsius. + +Note: a table of hard drive models, listing which Attribute +corresponds to temperature, can be found at: +\fBhttp://www.guzu.net/linux/hddtemp.db\fP + +.I N,raw8 +\- Print the Raw value of Attribute N as six 8-bit unsigned base-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw8\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw8\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw16 +\- Print the Raw value of Attribute N as three 16-bit unsigned base-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw16\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw16\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw48 +\- Print the Raw value of Attribute N as a 48-bit unsigned base-10 +integer. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw48\' prints Raw values for ALL Attributes in +this form. The form (for example) \'123,raw48\' only prints the Raw +value for Attribute 123 in this form. + +.TP +.B \-P TYPE +Specifies whether +\fBsmartd\fP +should use any preset options that are available for this drive. The +valid arguments to this Directive are: + +.I use +\- use any presets that are available for this drive. This is the default. + +.I ignore +\- do not use any presets for this drive. + +.I show +\- show the presets listed for this drive in the database. + +.I showall +\- show the presets that are available for all drives and then exit. + +[Please see the +.B smartctl \-P +command-line option.] + +.TP +.B \-a +Equivalent to turning on all of the following Directives: +.B \'\-H\' +to check the SMART health status, +.B \'\-f\' +to report failures of Usage (rather than Prefail) Attributes, +.B \'\-t\' +to track changes in both Prefailure and Usage Attributes, +.B \'\-l\ selftest\' +to report increases in the number of Self-Test Log errors, +.B \'\-l\ error\' +to report increases in the number of ATA errors, +.B \'\-C 197\' +to report nonzero values of the current pending sector count, and +.B \'\-U 198\' +to report nonzero values of the offline pending sector count. + +Note that \-a is the default for ATA devices. If none of these other +Directives is given, then \-a is assumed. + +.TP +.B # +Comment: ignore the remainder of the line. +.TP +.B \e +Continuation character: if this is the last non-white or non-comment +character on a line, then the following line is a continuation of the current +one. +.PP +If you are not sure which Directives to use, I suggest experimenting +for a few minutes with +.B smartctl +to see what SMART functionality your disk(s) support(s). If you do +not like voluminous syslog messages, a good choice of +\fBsmartd\fP +configuration file Directives might be: +.nf +.B \-H \-l\ selftest \-l\ error \-f. +.fi +If you want more frequent information, use: +.B -a. + +.TP +.B ADDITIONAL DETAILS ABOUT DEVICESCAN +If the first 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. + +If \fBDEVICESCAN\fP is not followed by any Directives, then smartd +will scan for both ATA and SCSI devices, and will monitor all possible +SMART properties of any devices that are found. + +\fBDEVICESCAN\fP may optionally be followed by any valid Directives, +which will be applied to all devices that are found in the scan. For +example +.nf +.B DEVICESCAN -m root@example.com +.fi +will scan for all devices, and then monitor them. It will send one +email warning per device for any problems that are found. +.nf +.B DEVICESCAN -d ata -m root@example.com +.fi +will do the same, but restricts the scan to ATA devices only. +.nf +.B DEVICESCAN -H -d ata -m root@example.com +.fi +will do the same, but only monitors the SMART health status of the +devices, (rather than the default \-a, which monitors all SMART +properties). + +.TP +.B EXAMPLES OF SHELL SCRIPTS FOR \'\-M exec\' +These are two examples of shell scripts that can be used with the \'\-M +exec PATH\' Directive described previously. The paths to these scripts +and similar executables is the PATH argument to the \'\-M exec PATH\' +Directive. + +Example 1: This script is for use with \'\-m ADDRESS -M exec PATH\'. It appends +the output of +.B smartctl -a +to the output of the smartd email warning message and sends it to ADDRESS. + +.nf +\fB +#! /bin/bash + +# Save the email message (STDIN) to a file: +cat > /root/msg + +# Append the output of smartctl -a to the message: +/usr/local/sbin/smartctl -a -d $SMART_DEVICETYPE $SMARTD_DEVICE >> /root/msg + +# Now email the message to the user at address ADD: +/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg +\fP +.fi + +Example 2: This script is for use with \'\-m \-M exec +PATH\'. It warns all users about a disk problem, waits 30 seconds, and +then powers down the machine. + +.nf +\fB +#! /bin/bash + +# Warn all users of a problem +wall \'Problem detected with disk: \' "$SMARTD_DEVICESTRING" +wall \'Warning message from smartd is: \' "$SMARTD_MESSAGE" +wall \'Shutting down machine in 30 seconds... \' + +# Wait half a minute +sleep 30 + +# Power down the machine +/sbin/shutdown -hf now +\fP +.fi + +Some example scripts are distributed with the smartmontools package, +in /usr/local/share/doc/smartmontools-5.1/examplescripts/. + +Please note that these scripts typically run as root, so any files +that they read/write should not be writable by ordinary users or +reside in directories like /tmp that are writable by ordinary users +and may expose your system to symlink attacks. + +As previously described, if the scripts write to STDOUT or STDERR, +this is interpreted as indicating that there was an internal error +within the script, and a snippet of STDOUT/STDERR is logged to SYSLOG. +The remainder is flushed. + +.\" ENDINCLUDE +.\" DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE +.\" END OF THE INCLUDE SECTION FOR smartd.conf.5 + +.SH NOTES +\fBsmartd\fP +will make log entries at loglevel +.B LOG_INFO +if the Normalized SMART Attribute values have changed, as reported using the +.B \'\-t\', \'\-p\', +or +.B \'\-u\' +Directives. For example: +.nf +.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93\' +.fi +Note that in this message, the value given is the \'Normalized\' not the \'Raw\' +Attribute value (the disk temperature in this case is about 22 +Celsius). The +.B \'-R\' +and +.B \'-r\' +Directives modify this behavior, so that the information is printed +with the Raw values as well, for example: +.nf +.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]\' +.fi +Here the Raw values are the actual disk temperatures in Celsius. The +way in which the Raw values are printed, and the names under which the +Attributes are reported, is governed by the various +.B \'-v Num,Description\' +Directives described previously. + +Please see the +.B smartctl +manual page for further explanation of the differences between +Normalized and Raw Attribute values. + +\fBsmartd\fP +will make log entries at loglevel +.B LOG_CRIT +if a SMART Attribute has failed, for example: +.nf +.B \'Device: /dev/hdc, Failed SMART Attribute: 5 Reallocated_Sector_Ct\' +.fi + This loglevel is used for reporting enabled by the +.B \'\-H\', \-f\', \'\-l\ selftest\', +and +.B \'\-l\ error\' +Directives. Entries reporting failure of SMART Prefailure Attributes +should not be ignored: they mean that the disk is failing. Use the +.B smartctl +utility to investigate. + +Under Solaris with the default \fB/etc/syslog.conf\fP configuration, +messages below loglevel \fBLOG_NOTICE\fP will \fBnot\fP be recorded. +Hence all \fBsmartd\fP messages with loglevel \fBLOG_INFO\fP will be +lost. If you want to use the existing daemon facility to log all +messages from \fBsmartd\fP, you should change \fB/etc/syslog.conf\fP +from: +.nf + ...;daemon.notice;... /var/adm/messages +.fi +to read: +.nf + ...;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. + +On Cygwin and Windows, the log messages are written to the event log +or to a file. 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: + +\'\fBsmartd status\fP\' \- check status + +\'\fBsmartd stop\fP\' \- stop smartd + +\'\fBsmartd reload\fP\' \- reread config file + +\'\fBsmartd restart\fP\' \- restart smartd + +\'\fBsmartd sigusr1\fP\' \- check disks now + +\'\fBsmartd sigusr2\fP\' \- toggle debug mode + +On WinNT4/2000/XP, \fBsmartd\fP can also be run as a Windows service: + + +The Cygwin Version of \fBsmartd\fP can be run as a service via the +cygrunsrv tool. The start-up script provides Cygwin-specific commands +to install and remove the service: +.nf +.B /usr/local/etc/rc.d/init.d/smartd install [options] +.B /usr/local/etc/rc.d/init.d/smartd remove +.fi +The service can be started and stopped by the start-up script as usual +(see \fBEXAMPLES\fP above). + + +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]\'. + +\'\fBsmartd remove\fP\' can later be used to remove the service entry +from 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 +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. + +Continuing the paused service (\'\fBnet continue smartd\fP\') resets the +interval and rereads the configuration file immediately (like \fBSIGHUP\fP): + +Continuing a still running service (\'\fBnet continue smartd\fP\' without +preceding \'\fBnet pause smartd\fP\') does not reread configuration but +checks disks immediately (like \fBSIGUSR1\fP). + +.SH LOG TIMESTAMP TIMEZONE + +When \fBsmartd\fP makes log entries, these are time-stamped. The time +stamps are in the computer's local time zone, which is generally set +using either the environment variable \'\fBTZ\fP\' or using a +time-zone file such as \fB/etc/localtime\fP. You may wish to change +the timezone while \fBsmartd\fP is running (for example, if you carry +a laptop to a new time-zone and don't reboot it). Due to a bug in the +\fBtzset(3)\fP function of many unix standard C libraries, the +time-zone stamps of \fBsmartd\fP might not change. For some systems, +\fBsmartd\fP will work around this problem \fIif\fP the time-zone is +set using \fB/etc/localtime\fP. The work-around \fIfails\fP if the +time-zone is set using the \'\fBTZ\fP\' variable (or a file that it +points to). + + +.SH RETURN VALUES +The return value (exit status) of +\fBsmartd\fP +can have the following values: +.TP +.B 0: +Daemon startup successful, or \fBsmartd\fP was killed by a SIGTERM (or in debug mode, a SIGQUIT). +.TP +.B 1: +Commandline did not parse. +.TP +.B 2: +There was a syntax error in the config file. +.TP +.B 3: +Forking the daemon failed. +.TP +.B 4: +Couldn\'t create PID file. +.TP +.B 5: +Config file does not exist (only returned in conjunction with the \'-c\' option). +.TP +.B 6: +Config file exists, but cannot be read. +.TP +.B 8: +\fBsmartd\fP +ran out of memory during startup. +.TP +.B 9: +A compile time constant of\fB smartd\fP was too small. This can be caused by an +excessive number of disks, or by lines in \fB /usr/local/etc/smartd.conf\fP that are too long. +Please report this problem to \fB smartmontools-support@lists.sourceforge.net\fP. +.TP +.B 10 +An inconsistency was found in \fBsmartd\fP\'s internal data +structures. This should never happen. It must be due to either a +coding or compiler bug. \fIPlease\fP report such failures to +smartmontools-support@lists.sourceforge.net. +.TP +.B 16: +A device explicitly listed in +.B /usr/local/etc/smartd.conf +can\'t be monitored. +.TP +.B 17: +\fBsmartd\fP +didn\'t find any devices to monitor. +.TP +.B 254: +When in daemon mode, +\fBsmartd\fP +received a SIGINT or SIGQUIT. (Note that in debug mode, SIGINT has +the same effect as SIGHUP, and makes \fBsmartd\fP reload its +configuration file. SIGQUIT has the same effect as SIGTERM and causes +\fBsmartd\fP to exit with zero exit status. +.TP +.B 132 and above +\fBsmartd\fP +was killed by a signal that is not explicitly listed above. The exit +status is then 128 plus the signal number. For example if +\fBsmartd\fP +is killed by SIGKILL (signal 9) then the exit status is 137. + +.PP +.SH AUTHOR +\fBBruce Allen\fP smartmontools-support@lists.sourceforge.net +.fi +University of Wisconsin \- Milwaukee Physics Department + +.PP +.SH CONTRIBUTORS +The following have made large contributions to smartmontools: +.nf +\fBCasper Dik\fP (Solaris SCSI interface) +\fBChristian Franke\fP (Windows interface and Cygwin package) +\fBDouglas Gilbert\fP (SCSI subsystem) +\fBGuido Guenther\fP (Autoconf/Automake packaging) +\fBGeoffrey Keating\fP (Darwin ATA interface) +\fBEduard Martinescu\fP (FreeBSD interface) +\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list) +\fBKeiji Sawada\fP (Solaris ATA interface) +\fBSergey Svishchev\fP (NetBSD interface) +\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface) +\fBPhil Williams\fP (User interface and drive database) +.fi +Many other individuals have made smaller contributions and corrections. + +.PP +.SH CREDITS +.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 +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. \fBhttp://ssrc.soe.ucsc.edu/\fP . +.SH +HOME PAGE FOR SMARTMONTOOLS: +.fi +Please see the following web site for updates, further documentation, bug +reports and patches: \fBhttp://smartmontools.sourceforge.net/\fP + +.SH SEE ALSO: +\fBsmartd.conf\fP(5), \fBsmartctl\fP(8), \fBsyslogd\fP(8), +\fBsyslog.conf\fP(5), \fBbadblocks\fP(8), \fBide\-smart\fP(8), \fBregex\fP(7). + +.SH +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.php?sid=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) +specification. This documents the SMART functionality which the +\fBsmartmontools\fP utilities provide access to. You can find +Revision 4b of this document at +\fBhttp://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf\fP . +Earlier and later versions of this Specification are available from +the T13 web site \fBhttp://www.t13.org/\fP . + +.fi +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 documents may be found in the References section of the +smartmontools home page at \fBhttp://smartmontools.sourceforge.net/#references\fP . + +.SH +CVS ID OF THIS PAGE: +$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $ diff --git a/smartd.c b/smartd.c new file mode 100644 index 0000000..63242fe --- /dev/null +++ b/smartd.c @@ -0,0 +1,4127 @@ +/* + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +// unconditionally included files +#define _GNU_SOURCE +#include +#include +#include // umask +#ifndef _WIN32 +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if SCSITIMEOUT +#include +#endif + +// see which system files to conditionally include +#include "config.h" + +// conditionally included files +#ifdef HAVE_GETOPT_LONG +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4761) // "conversion supplied" +typedef unsigned short mode_t; +typedef int pid_t; +#endif +#include // umask() +#include // getpid() +#endif // _WIN32 + +#ifdef __CYGWIN__ +// From : +// BOOL WINAPI FreeConsole(void); +int __stdcall FreeConsole(void); +#include // setmode() +#endif // __CYGWIN__ + +// locally included files +#include "int64.h" +#include "atacmds.h" +#include "ataprint.h" +#include "extern.h" +#include "knowndrives.h" +#include "scsicmds.h" +#include "smartd.h" +#include "utility.h" + +#ifdef _WIN32 +#include "hostname_win32.h" // gethost/domainname() +#define HAVE_GETHOSTNAME 1 +#define HAVE_GETDOMAINNAME 1 +// fork()/signal()/initd simulation for native Windows +#include "daemon_win32.h" // daemon_main/detach/signal() +#undef SIGNALFN +#define SIGNALFN daemon_signal +#define strsignal daemon_strsignal +#define sleep daemon_sleep +#undef EXIT // see utility.h +#define EXIT(x) { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); } +// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK. +#define SIGQUIT SIGBREAK +#define SIGQUIT_KEYNAME "CONTROL-Break" +#else // _WIN32 +#ifdef __CYGWIN__ +// 2x CONTROL-C simulates missing SIGQUIT via keyboard +#define SIGQUIT_KEYNAME "2x CONTROL-C" +#else // __CYGWIN__ +#define SIGQUIT_KEYNAME "CONTROL-\\" +#endif // __CYGWIN__ +#endif // _WIN32 + +#if defined (__SVR4) && defined (__sun) +int getdomainname(char *, int); /* no declaration in header files! */ +#endif + +#define ARGUSED(x) ((void)(x)) + +// These are CVS identification information for *.c and *.h files +extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, + *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; + +static const char *filenameandversion="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $"; +#ifdef NEED_SOLARIS_ATA_CODE +extern const char *os_solaris_ata_s_cvsid; +#endif +#ifdef _WIN32 +extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid; +#endif +const char *smartd_c_cvsid="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $" +ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID +#ifdef DAEMON_WIN32_H_CVSID +DAEMON_WIN32_H_CVSID +#endif +EXTERN_H_CVSID INT64_H_CVSID +#ifdef HOSTNAME_WIN32_H_CVSID +HOSTNAME_WIN32_H_CVSID +#endif +KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID +#ifdef SYSLOG_H_CVSID +SYSLOG_H_CVSID +#endif +UTILITY_H_CVSID; + +extern const char *reportbug; + +// GNU copyleft statement. Needed for GPL purposes. +const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n" + "free software, and you are welcome to redistribute it\n" + "under the terms of the GNU General Public License\n" + "Version 2. See http://www.gnu.org for further details.\n\n"; + +extern unsigned char debugmode; + +// command-line: how long to sleep between checks +static int checktime=CHECKTIME; + +// command-line: name of PID file (NULL for no pid file) +static char* pid_file=NULL; + +// configuration file name +#ifndef _WIN32 +static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ; +#else +static char* configfile = "./" CONFIGFILENAME ; +#endif +// configuration file "name" if read from stdin +static /*const*/ char * const configfile_stdin = ""; +// allocated memory for alternate configuration file name +static char* configfile_alt = NULL; + +// command-line: when should we exit? +static int quit=0; + +// command-line; this is the default syslog(3) log facility to use. +static int facility=LOG_DAEMON; + +#ifdef __CYGWIN__ +// command-line: running as service, so don't fork() +static int is_service=0; +#endif + +// used for control of printing, passing arguments to atacmds.c +smartmonctrl *con=NULL; + +// pointers to (real or simulated) entries in configuration file, and +// maximum space currently allocated for these entries. +cfgfile **cfgentries=NULL; +int cfgentries_max=0; + +// pointers to ATA and SCSI devices being monitored, maximum and +// actual numbers +cfgfile **atadevlist=NULL, **scsidevlist=NULL; +int atadevlist_max=0, scsidevlist_max=0; +int numdevata=0, numdevscsi=0; + +// track memory usage +extern int64_t bytes; + +// exit status +extern int exitstatus; + +// set to one if we catch a USR1 (check devices now) +volatile int caughtsigUSR1=0; + +#ifdef _WIN32 +// set to one if we catch a USR2 (toggle debug mode) +volatile int caughtsigUSR2=0; +#endif + +// set to one if we catch a HUP (reload config file). In debug mode, +// set to two, if we catch INT (also reload config file). +volatile int caughtsigHUP=0; + +// set to signal value if we catch INT, QUIT, or TERM +volatile int caughtsigEXIT=0; + +#if SCSITIMEOUT +// stack environment if we time out during SCSI access (USB devices) +jmp_buf registerscsienv; +#endif + +// tranlate cfg->pending into the correct Attribute numbers +void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) { + + unsigned char curr = CURR_PEND(pending); + unsigned char off = OFF_PEND(pending); + + // look for special value of CUR_UNC_DEFAULT that means DONT + // monitor. 0 means DO test. + if (curr==CUR_UNC_DEFAULT) + curr=0; + else if (curr==0) + curr=CUR_UNC_DEFAULT; + + // look for special value of OFF_UNC_DEFAULT that means DONT + // monitor. 0 means DO TEST. + if (off==OFF_UNC_DEFAULT) + off=0; + else if (off==0) + off=OFF_UNC_DEFAULT; + + *current=curr; + *offline=off; + + return; +} + + +// free all memory associated with selftest part of configfile entry. Return NULL +testinfo* FreeTestData(testinfo *data){ + + // make sure we have something to do. + if (!data) + return NULL; + + // free space for text pattern + data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion); + + // free compiled expression + regfree(&(data->cregex)); + + // make sure that no sign of the compiled expression is left behind + // (just in case, to help detect bugs if we ever try and refer to + // that again). + memset(&(data->cregex), '0', sizeof(regex_t)); + + // free remaining memory space + data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion); + + return NULL; +} + +cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){ + // for now keep BLOCKSIZE small to help detect coding problems. + // Perhaps increase in the future. + const int BLOCKSIZE=8; + int i; + int old = *oldsize; + int new = old + BLOCKSIZE; + cfgfile **newptr=realloc(oldarray, new*sizeof(cfgfile *)); + + // did we get more space? + if (newptr) { + + // clear remaining entries ala calloc() + for (i=old; iname = FreeNonZero(cfg->name, -1,__LINE__,filenameandversion); + cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion); + cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion); + cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion); + cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion); + if (cfg->mailwarn){ + cfg->mailwarn->address = FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion); + cfg->mailwarn->emailcmdline = FreeNonZero(cfg->mailwarn->emailcmdline, -1,__LINE__,filenameandversion); + cfg->mailwarn = FreeNonZero(cfg->mailwarn, sizeof(maildata),__LINE__,filenameandversion); + } + cfg->testdata = FreeTestData(cfg->testdata); + *anentry = FreeNonZero(cfg, sizeof(cfgfile),__LINE__,filenameandversion); + + return; +} + +// deallocates all memory associated with cfgentries list +void RmAllConfigEntries(){ + int i; + + for (i=0; ih_name, '.'))) + p++; // skip "." + } +#else + ARGUSED(hostname); +#endif + return p; +} + +#define EBUFLEN 1024 + +// If either address or executable path is non-null then send and log +// a warning email, or execute executable +void MailWarning(cfgfile *cfg, int which, char *fmt, ...){ + char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024]; + char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN]; + char environ_strings[11][ENVLENGTH]; + time_t epoch; + va_list ap; + const int day=24*3600; + int days=0; + char *whichfail[]={ + "EmailTest", // 0 + "Health", // 1 + "Usage", // 2 + "SelfTest", // 3 + "ErrorCount", // 4 + "FailedHealthCheck", // 5 + "FailedReadSmartData", // 6 + "FailedReadSmartErrorLog", // 7 + "FailedReadSmartSelfTestLog", // 8 + "FailedOpenDevice", // 9 + "CurrentPendingSector", // 10 + "OfflineUncorrectableSector" // 11 + }; + + char *address, *executable; + mailinfo *mail; + maildata* data=cfg->mailwarn; +#ifndef _WIN32 + FILE *pfp=NULL; +#else + char stdinbuf[1024]; int boxmsgoffs, boxtype; +#endif + char *newadd=NULL, *newwarn=NULL; + const char *unknown="[Unknown]"; + + // See if user wants us to send mail + if(!data) + return; + + address=data->address; + executable=data->emailcmdline; + + if (!address && !executable) + return; + + // which type of mail are we sending? + mail=(data->maillog)+which; + + // checks for sanity + if (data->emailfreq<1 || data->emailfreq>3) { + PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq); + return; + } + if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) { + PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n", + which, (int)sizeof(whichfail)); + return; + } + + // Return if a single warning mail has been sent. + if ((data->emailfreq==1) && mail->logged) + return; + + // Return if this is an email test and one has already been sent. + if (which == 0 && mail->logged) + return; + + // To decide if to send mail, we need to know what time it is. + epoch=time(NULL); + + // Return if less than one day has gone by + if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) + return; + + // Return if less than 2^(logged-1) days have gone by + if (data->emailfreq==3 && mail->logged){ + days=0x01<<(mail->logged-1); + days*=day; + if (epoch<(mail->lastsent+days)) + return; + } + + // record the time of this mail message, and the first mail message + if (!mail->logged) + mail->firstsent=epoch; + mail->lastsent=epoch; + + // get system host & domain names (not null terminated if length=MAX) +#ifdef HAVE_GETHOSTNAME + if (gethostname(hostname, 256)) + strcpy(hostname, unknown); + else { + char *p=NULL; + hostname[255]='\0'; + p = dnsdomain(hostname); + if (p && *p) { + strncpy(domainname, p, 255); + domainname[255]='\0'; + } else + strcpy(domainname, unknown); + } +#else + strcpy(hostname, unknown); + strcpy(domainname, unknown); +#endif + +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(nisdomain, 256)) + strcpy(nisdomain, unknown); + else + nisdomain[255]='\0'; +#else + strcpy(nisdomain, unknown); +#endif + + // print warning string into message + va_start(ap, fmt); + vsnprintf(message, 256, fmt, ap); + va_end(ap); + + // appropriate message about further information + additional[0]=original[0]=further[0]='\0'; + if (which) { + sprintf(further,"You can also use the smartctl utility for further investigation.\n"); + + switch (data->emailfreq){ + case 1: + sprintf(additional,"No additional email messages about this problem will be sent.\n"); + break; + case 2: + sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n"); + break; + case 3: + sprintf(additional,"Another email message will be sent in %d days if the problem persists\n", + (0x01)<logged); + break; + } + if (data->emailfreq>1 && mail->logged){ + dateandtimezoneepoch(dates, mail->firstsent); + sprintf(original,"The original email about this issue was sent at %s\n", dates); + } + } + + snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname); + + // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx". + if (!executable) +#ifdef DEFAULT_MAILER + executable = DEFAULT_MAILER ; +#else +#ifndef _WIN32 + executable = "mail"; +#else + executable = "blat"; // http://blat.sourceforge.net/ +#endif +#endif + + // make a private copy of address with commas replaced by spaces + // to separate recipients + if (address) { + address=CustomStrDup(data->address, 1, __LINE__, filenameandversion); +#ifndef _WIN32 // blat mailer needs comma + { + char *comma=address; + while ((comma=strchr(comma, ','))) + *comma=' '; + } +#endif + } + + // Export information in environment variables that will be useful + // for user scripts + exportenv(environ_strings[0], "SMARTD_MAILER", executable); + exportenv(environ_strings[1], "SMARTD_MESSAGE", message); + exportenv(environ_strings[2], "SMARTD_SUBJECT", subject); + dateandtimezoneepoch(dates, mail->firstsent); + exportenv(environ_strings[3], "SMARTD_TFIRST", dates); + snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); + exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates); + exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]); + if (address) + exportenv(environ_strings[6], "SMARTD_ADDRESS", address); + exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name); + + switch (cfg->controller_type) { + case CONTROLLER_3WARE_678K: + case CONTROLLER_3WARE_9000_CHAR: + case CONTROLLER_3WARE_678K_CHAR: + { + char *s,devicetype[16]; + sprintf(devicetype, "3ware,%d", cfg->controller_port-1); + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype); + if ((s=strchr(cfg->name, ' '))) + *s='\0'; + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + if (s) + *s=' '; + } + break; + case CONTROLLER_ATA: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + break; + case CONTROLLER_MARVELL_SATA: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + break; + case CONTROLLER_SCSI: + exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi"); + exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name); + } + + snprintf(fullmessage, 1024, + "This email was generated by the smartd daemon running on:\n\n" + " host name: %s\n" + " DNS domain: %s\n" + " NIS domain: %s\n\n" + "The following warning/error was logged by the smartd daemon:\n\n" + "%s\n\n" + "For details see host's SYSLOG (default: /var/log/messages).\n\n" + "%s%s%s", + hostname, domainname, nisdomain, message, further, original, additional); + exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage); + + // now construct a command to send this as EMAIL +#ifndef _WIN32 + if (address) + snprintf(command, 2048, + "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n" + "%sENDMAIL\n", subject, address, fullmessage); + else + snprintf(command, 2048, "%s 2>&1", executable); + + // tell SYSLOG what we are about to do... + newadd=address?address:""; + newwarn=which?"Warning via":"Test of"; + + PrintOut(LOG_INFO,"%s %s to %s ...\n", + which?"Sending warning via":"Executing test of", executable, newadd); + + // issue the command to send mail or to run the user's executable + errno=0; + if (!(pfp=popen(command, "r"))) + // failed to popen() mail process + PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", + newwarn, executable, newadd, errno?strerror(errno):""); + else { + // pipe suceeded! + int len, status; + char buffer[EBUFLEN]; + + // if unexpected output on stdout/stderr, null terminate, print, and flush + if ((len=fread(buffer, 1, EBUFLEN, pfp))) { + int count=0; + int newlen = len128) + PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", + newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128)); + else if (status8) + PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", + newwarn, executable, newadd, status, status8); + else + PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); + } + + if (WIFSIGNALED(status)) + PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", + newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status))); + + // this branch is probably not possible. If subprocess is + // stopped then pclose() should not return. + if (WIFSTOPPED(status)) + PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n", + newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status))); + + } + } + +#else // _WIN32 + + // No "here-documents" on Windows, so must use separate commandline and stdin + command[0] = stdinbuf[0] = 0; + boxtype = -1; boxmsgoffs = 0; + newadd = ""; + if (address) { + // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox + int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0); + if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) { + boxtype = (addroffs > 0 ? 1 : 0); + addroffs += 6; + if (address[addroffs]) + addroffs++; + } + else + addroffs = 0; + + if (address[addroffs]) { + // Use "blat" parameter syntax (TODO: configure via -M for other mailers) + snprintf(command, sizeof(command), + "%s - -q -subject \"%s\" -to \"%s\"", + executable, subject, address+addroffs); + newadd = address+addroffs; + } + // Message for mail [0...] and messagebox [boxmsgoffs...] + snprintf(stdinbuf, sizeof(stdinbuf), + "This email was generated by the smartd daemon running on:\n\n" + " host name: %s\n" + " DNS domain: %s\n" +// " NIS domain: %s\n" + "\n%n" + "The following warning/error was logged by the smartd daemon:\n\n" + "%s\n\n" + "For details see the event log or log file of smartd.\n\n" + "%s%s%s" + "\n", + hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional); + } + else + snprintf(command, sizeof(command), "%s", executable); + + newwarn=which?"Warning via":"Test of"; + if (boxtype >= 0) { + // show message box + daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs); + PrintOut(LOG_INFO,"%s message box\n", newwarn); + } + if (command[0]) { + char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog() + int rc; + // run command + PrintOut(LOG_INFO,"%s %s to %s ...\n", + (which?"Sending warning via":"Executing test of"), executable, newadd); + rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf)); + if (rc >= 0 && stdoutbuf[0]) + PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n", + newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf); + if (rc != 0) + PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n", + newwarn, executable, newadd, rc); + else + PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd); + } + +#endif // _WIN32 + + // increment mail sent counter + mail->logged++; + + // free copy of address (without commas) + address=FreeNonZero(address, -1, __LINE__, filenameandversion); + + return; +} + +// Printing function for watching ataprint commands, or losing them +// [From GLIBC Manual: Since the prototype doesn't specify types for +// optional arguments, in a call to a variadic function the default +// argument promotions are performed on the optional argument +// values. This means the objects of type char or short int (whether +// signed or not) are promoted to either int or unsigned int, as +// appropriate.] +void pout(char *fmt, ...){ + va_list ap; + + // get the correct time in syslog() + FixGlibcTimeZoneBug(); + // initialize variable argument list + va_start(ap,fmt); + // in debug==1 mode we will print the output from the ataprint.o functions! + if (debugmode && debugmode!=2) +#ifdef _WIN32 + if (facility == LOG_LOCAL1) // logging to stdout + vfprintf(stderr,fmt,ap); + else +#endif + vprintf(fmt,ap); + // in debug==2 mode we print output from knowndrives.o functions + else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) { + openlog("smartd", LOG_PID, facility); + vsyslog(LOG_INFO, fmt, ap); + closelog(); + } + va_end(ap); + fflush(NULL); + return; +} + +// This function prints either to stdout or to the syslog as needed. +// This function is also used by utility.c to report LOG_CRIT errors. +void PrintOut(int priority,char *fmt, ...){ + va_list ap; + + // get the correct time in syslog() + FixGlibcTimeZoneBug(); + // initialize variable argument list + va_start(ap,fmt); + if (debugmode) +#ifdef _WIN32 + if (facility == LOG_LOCAL1) // logging to stdout + vfprintf(stderr,fmt,ap); + else +#endif + vprintf(fmt,ap); + else { + openlog("smartd", LOG_PID, facility); + vsyslog(priority,fmt,ap); + closelog(); + } + va_end(ap); + return; +} + +// Forks new process, closes ALL file descriptors, redirects stdin, +// stdout, and stderr. Not quite daemon(). See +// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html +// for a good description of why we do things this way. +void DaemonInit(){ +#ifndef _WIN32 + pid_t pid; + int i; + + // flush all buffered streams. Else we might get two copies of open + // streams since both parent and child get copies of the buffers. + fflush(NULL); + + if ((pid=fork()) < 0) { + // unable to fork! + PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); + EXIT(EXIT_STARTUP); + } + else if (pid) + // we are the parent process -- exit cleanly + EXIT(0); + + // from here on, we are the child process. + setsid(); + + // Fork one more time to avoid any possibility of having terminals + if ((pid=fork()) < 0) { + // unable to fork! + PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); + EXIT(EXIT_STARTUP); + } + else if (pid) + // we are the parent process -- exit cleanly + EXIT(0); + + // Now we are the child's child... + + // close any open file descriptors + for (i=getdtablesize();i>=0;--i) + close(i); + +#ifdef __CYGWIN__ + // Cygwin's setsid() does not detach the process from Windows console + FreeConsole(); +#endif // __CYGWIN__ + + // redirect any IO attempts to /dev/null for stdin + i=open("/dev/null",O_RDWR); + // stdout + dup(i); + // stderr + dup(i); + umask(0); + chdir("/"); + + PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid()); + +#else // _WIN32 + + // No fork() on native Win32 + // Detach this process from console + fflush(NULL); + if (daemon_detach("smartd")) { + PrintOut(LOG_CRIT,"smartd unable to detach from console!\n"); + EXIT(EXIT_STARTUP); + } + // stdin/out/err now closed if not redirected + +#endif // _WIN32 + return; +} + +// create a PID file containing the current process id +void WritePidFile() { + if (pid_file) { + int error = 0; + pid_t pid = getpid(); + mode_t old_umask; + FILE* fp; + +#ifndef __CYGWIN__ + old_umask = umask(0077); // rwx------ +#else + // Cygwin: smartd service runs on system account, ensure PID file can be read by admins + old_umask = umask(0033); // rwxr--r-- +#endif + fp = fopen(pid_file, "w"); + umask(old_umask); + if (fp == NULL) { + error = 1; + } else if (fprintf(fp, "%d\n", (int)pid) <= 0) { + error = 1; + } else if (fclose(fp) != 0) { + error = 1; + } + if (error) { + PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file); + EXIT(EXIT_PID); + } + PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid); + } + return; +} + +// Prints header identifying version of code and home +void PrintHead(){ +#ifdef HAVE_GET_OS_VERSION_STR + const char * ver = get_os_version_str(); +#else + const char * ver = SMARTMONTOOLS_BUILD_HOST; +#endif + PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver); + PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n"); + return; +} + +// prints help info for configuration file Directives +void Directives() { + PrintOut(LOG_INFO, + "Configuration file (%s) Directives (after device name):\n" + " -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N\n" + " -T TYPE Set the tolerance to one of: normal, permissive\n" + " -o VAL Enable/disable automatic offline tests (on/off)\n" + " -S VAL Enable/disable attribute autosave (on/off)\n" + " -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n" + " -H Monitor SMART Health Status, report if failed\n" + " -s REG Do Self-Test at time(s) given by regular expression REG\n" + " -l TYPE Monitor SMART log. Type is one of: error, selftest\n" + " -f Monitor 'Usage' Attributes, report failures\n" + " -m ADD Send email warning to address ADD\n" + " -M TYPE Modify email warning behavior (see man page)\n" + " -p Report changes in 'Prefailure' Attributes\n" + " -u Report changes in 'Usage' Attributes\n" + " -t Equivalent to -p and -u Directives\n" + " -r ID Also report Raw values of Attribute ID with -p, -u or -t\n" + " -R ID Track changes in Attribute ID Raw value with -p, -u or -t\n" + " -i ID Ignore Attribute ID for -f Directive\n" + " -I ID Ignore Attribute ID for -p, -u or -t Directive\n" + " -C ID Monitor Current Pending Sectors in Attribute ID\n" + " -U ID Monitor Offline Uncorrectable Sectors in Attribute ID\n" + " -v N,ST Modifies labeling of Attribute N (see man page) \n" + " -P TYPE Drive-specific presets: use, ignore, show, showall\n" + " -a Default: -H -f -t -l error -l selftest -C 197 -U 198\n" + " -F TYPE Firmware bug workaround: none, samsung, samsung2\n" + " # Comment: text after a hash sign is ignored\n" + " \\ Line continuation character\n" + "Attribute ID is a decimal integer 1 <= ID <= 255\n" + "Use ID = 0 to turn off -C and/or -U Directives\n" + "Example: /dev/hda -a\n", + configfile); + return; +} + +/* Returns a pointer to a static string containing a formatted list of the valid + arguments to the option opt or NULL on failure. */ +const char *GetValidArgList(char opt) { + switch (opt) { + case 'c': + return ", -"; + case 's': + return "valid_regular_expression"; + case 'l': + return "daemon, local0, local1, local2, local3, local4, local5, local6, local7"; + case 'q': + return "nodev, errors, nodevstartup, never, onecheck, showtests"; + case 'r': + return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; + case 'p': + return ""; + case 'i': + return ""; + default: + return NULL; + } +} + +/* prints help information for command syntax */ +void Usage (void){ + PrintOut(LOG_INFO,"Usage: smartd [options]\n\n"); +#ifdef HAVE_GETOPT_LONG + PrintOut(LOG_INFO," -c NAME|-, --configfile=NAME|-\n"); + PrintOut(LOG_INFO," Read configuration file NAME or stdin [default is %s]\n\n", configfile); + PrintOut(LOG_INFO," -d, --debug\n"); + PrintOut(LOG_INFO," Start smartd in debug mode\n\n"); + PrintOut(LOG_INFO," -D, --showdirectives\n"); + PrintOut(LOG_INFO," Print the configuration file Directives and exit\n\n"); + PrintOut(LOG_INFO," -h, --help, --usage\n"); + PrintOut(LOG_INFO," Display this help and exit\n\n"); + PrintOut(LOG_INFO," -i N, --interval=N\n"); + PrintOut(LOG_INFO," Set interval between disk checks to N seconds, where N >= 10\n\n"); + PrintOut(LOG_INFO," -l local[0-7], --logfacility=local[0-7]\n"); +#ifndef _WIN32 + PrintOut(LOG_INFO," Use syslog facility local0 - local7 or daemon [default]\n\n"); +#else + PrintOut(LOG_INFO," Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n"); +#endif + PrintOut(LOG_INFO," -p NAME, --pidfile=NAME\n"); + PrintOut(LOG_INFO," Write PID file NAME\n\n"); + PrintOut(LOG_INFO," -q WHEN, --quit=WHEN\n"); + PrintOut(LOG_INFO," Quit on one of: %s\n\n", GetValidArgList('q')); + PrintOut(LOG_INFO," -r, --report=TYPE\n"); + PrintOut(LOG_INFO," Report transactions for one of: %s\n\n", GetValidArgList('r')); +#if defined(_WIN32) || defined(__CYGWIN__) + PrintOut(LOG_INFO," --service\n"); + PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n"); +#ifdef _WIN32 + PrintOut(LOG_INFO," smartd install [options]\n"); + PrintOut(LOG_INFO," Remove service with:\n"); + PrintOut(LOG_INFO," smartd remove\n\n"); +#else + PrintOut(LOG_INFO," /etc/rc.d/init.d/smartd install [options]\n"); + PrintOut(LOG_INFO," Remove service with:\n"); + PrintOut(LOG_INFO," /etc/rc.d/init.d/smartd remove\n\n"); +#endif +#endif // _WIN32 || __CYGWIN__ + PrintOut(LOG_INFO," -V, --version, --license, --copyright\n"); + PrintOut(LOG_INFO," Print License, Copyright, and version information\n"); +#else + PrintOut(LOG_INFO," -c NAME|- Read configuration file NAME or stdin [default is %s]\n", configfile); + PrintOut(LOG_INFO," -d Start smartd in debug mode\n"); + PrintOut(LOG_INFO," -D Print the configuration file Directives and exit\n"); + PrintOut(LOG_INFO," -h Display this help and exit\n"); + PrintOut(LOG_INFO," -i N Set interval between disk checks to N seconds, where N >= 10\n"); + PrintOut(LOG_INFO," -l local? Use syslog facility local0 - local7, or daemon\n"); + PrintOut(LOG_INFO," -p NAME Write PID file NAME\n"); + PrintOut(LOG_INFO," -q WHEN Quit on one of: %s\n", GetValidArgList('q')); + PrintOut(LOG_INFO," -r TYPE Report transactions for one of: %s\n", GetValidArgList('r')); + PrintOut(LOG_INFO," -V Print License, Copyright, and version information\n"); +#endif +} + +// returns negative if problem, else fd>=0 +static int OpenDevice(char *device, char *mode, int scanning) { + int fd; + char *s=device; + + // If there is an ASCII "space" character in the device name, + // terminate string there. This is for 3ware devices only. + if ((s=strchr(device,' '))) + *s='\0'; + + // open the device + fd = deviceopen(device, mode); + + // if we removed a space, put it back in please + if (s) + *s=' '; + + // if we failed to open the device, complain! + if (fd < 0) { + + // For linux+devfs, a nonexistent device gives a strange error + // message. This makes the error message a bit more sensible. + // If no debug and scanning - don't print errors + if (debugmode || !scanning) { + if (errno==ENOENT || errno==ENOTDIR) + errno=ENODEV; + + PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n", + device, strerror(errno)); + } + return -1; + } + // device opened sucessfully + return fd; +} + +int CloseDevice(int fd, char *name){ + if (deviceclose(fd)){ + PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd); + return 1; + } + // device sucessfully closed + return 0; +} + +// returns <0 on failure +int ATAErrorCount(int fd, char *name){ + struct ata_smart_errorlog log; + + if (-1==ataReadErrorLog(fd,&log)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name); + return -1; + } + + // return current number of ATA errors + return log.error_log_pointer?log.ata_error_count:0; +} + +// returns <0 if problem. Otherwise, bottom 8 bits are the self test +// error count, and top bits are the power-on hours of the last error. +int SelfTestErrorCount(int fd, char *name){ + struct ata_smart_selftestlog log; + + if (-1==ataReadSelfTestLog(fd,&log)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name); + return -1; + } + + // return current number of self-test errors + return ataPrintSmartSelfTestlog(&log,0); +} + +// scan to see what ata devices there are, and if they support SMART +int ATADeviceScan(cfgfile *cfg, int scanning){ + int fd, supported=0; + struct ata_identify_device drive; + char *name=cfg->name; + int retainsmartdata=0; + int retid; + char *mode; + + // should we try to register this as an ATA device? + switch (cfg->controller_type) { + case CONTROLLER_ATA: + case CONTROLLER_3WARE_678K: + case CONTROLLER_MARVELL_SATA: + case CONTROLLER_UNKNOWN: + mode="ATA"; + break; + case CONTROLLER_3WARE_678K_CHAR: + mode="ATA_3WARE_678K"; + break; + case CONTROLLER_3WARE_9000_CHAR: + mode="ATA_3WARE_9000"; + break; + default: + // not a recognized ATA or SATA device. We should never enter + // this branch. + return 1; + } + + // open the device + if ((fd=OpenDevice(name, mode, scanning))<0) + // device open failed + return 1; + PrintOut(LOG_INFO,"Device: %s, opened\n", name); + + // pass user settings on to low-level ATA commands + con->controller_port=cfg->controller_port; + con->controller_type=cfg->controller_type; + con->fixfirmwarebug = cfg->fixfirmwarebug; + + // Get drive identity structure + if ((retid=ataReadHDIdentity (fd,&drive))){ + if (retid<0) + // Unable to read Identity structure + PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name); + else + PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n", + name, packetdevicetype(retid-1)); + CloseDevice(fd, name); + return 2; + } + + // Show if device in database, and use preset vendor attribute + // options unless user has requested otherwise. + if (cfg->ignorepresets) + PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name); + else { + // do whatever applypresets decides to do. Will allocate memory if + // cfg->attributedefs is needed. + if (applypresets(&drive, &cfg->attributedefs, con)<0) + PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); + else + PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name); + + // then save the correct state of the flag (applypresets may have changed it) + cfg->fixfirmwarebug = con->fixfirmwarebug; + } + + // If requested, show which presets would be used for this drive + if (cfg->showpresets) { + int savedebugmode=debugmode; + PrintOut(LOG_INFO, "Device %s: presets are:\n", name); + if (!debugmode) + debugmode=2; + showpresets(&drive); + debugmode=savedebugmode; + } + + // see if drive supports SMART + supported=ataSmartSupport(&drive); + if (supported!=1) { + if (supported==0) + // drive does NOT support SMART + PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name); + else + // can't tell if drive supports SMART + PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name); + + // should we proceed anyway? + if (cfg->permissive){ + PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name); + } + else { + PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name); + CloseDevice(fd, name); + return 2; + } + } + + if (ataEnableSmart(fd)){ + // Enable SMART command has failed + PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name); + CloseDevice(fd, name); + return 2; + } + + // disable device attribute autosave... + if (cfg->autosave==1){ + if (ataDisableAutoSave(fd)) + PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name); + else + PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name); + } + + // or enable device attribute autosave + if (cfg->autosave==2){ + if (ataEnableAutoSave(fd)) + PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name); + else + PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name); + } + + // capability check: SMART status + if (cfg->smartcheck && ataSmartStatus2(fd)==-1){ + PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name); + cfg->smartcheck=0; + } + + // capability check: Read smart values and thresholds. Note that + // smart values are ALSO needed even if we ONLY want to know if the + // device is self-test log or error-log capable! After ATA-5, this + // information was ALSO reproduced in the IDENTIFY DEVICE response, + // but sadly not for ATA-5. Sigh. + + // do we need to retain SMART data after returning from this routine? + retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage; + + // do we need to get SMART data? + if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) { + + unsigned char currentpending, offlinepending; + + cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)); + cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt)); + + if (!cfg->smartval || !cfg->smartthres){ + PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n"); + EXIT(EXIT_NOMEM); + } + + if (ataReadSmartValues(fd,cfg->smartval) || + ataReadSmartThresholds (fd,cfg->smartthres)){ + PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name); + retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0; + cfg->pending=DONT_MONITOR_UNC; + } + + // see if the necessary Attribute is there to monitor offline or + // current pending sectors + TranslatePending(cfg->pending, ¤tpending, &offlinepending); + + if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) { + PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n", + name, (int)currentpending); + cfg->pending &= 0xff00; + cfg->pending |= CUR_UNC_DEFAULT; + } + + if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) { + PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count - no Attribute %d\n", + name, (int)offlinepending); + cfg->pending &= 0x00ff; + cfg->pending |= OFF_UNC_DEFAULT<<8; + } + } + + // enable/disable automatic on-line testing + if (cfg->autoofflinetest){ + // is this an enable or disable request? + char *what=(cfg->autoofflinetest==1)?"disable":"enable"; + if (!cfg->smartval) + PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what); + else { + // if command appears unsupported, issue a warning... + if (!isSupportAutomaticTimer(cfg->smartval)) + PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name); + // ... but then try anyway + if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd)) + PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what); + else + PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what); + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int retval; + + // start with service disabled, and re-enable it if all works OK + cfg->selftest=0; + cfg->selflogcount=0; + cfg->selfloghour=0; + + if (!cfg->smartval) + PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name); + else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive)) + PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name); + else if ((retval=SelfTestErrorCount(fd, name))<0) + PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name); + else { + cfg->selftest=1; + cfg->selflogcount=SELFTEST_ERRORCOUNT(retval); + cfg->selfloghour =SELFTEST_ERRORHOURS(retval); + } + } + + // capability check: ATA error log + if (cfg->errorlog){ + int val; + + // start with service disabled, and re-enable it if all works OK + cfg->errorlog=0; + cfg->ataerrorcount=0; + + if (!cfg->smartval) + PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name); + else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive)) + PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name); + else if ((val=ATAErrorCount(fd, name))<0) + PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name); + else { + cfg->errorlog=1; + cfg->ataerrorcount=val; + } + } + + // If we don't need to save SMART data, get rid of it now + if (!retainsmartdata) { + if (cfg->smartval) { + cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion); + bytes-=sizeof(struct ata_smart_values); + } + if (cfg->smartthres) { + cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion); + bytes-=sizeof(struct ata_smart_thresholds_pvt); + } + } + + // capabilities check -- does it support powermode? + if (cfg->powermode) { + int powermode=ataCheckPowerMode(fd); + + if (-1 == powermode) { + PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name); + cfg->powermode=0; + } + else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) { + PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", + name, powermode); + cfg->powermode=0; + } + } + + // If no tests available or selected, return + if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || + cfg->usagefailed || cfg->prefail || cfg->usage)) { + CloseDevice(fd, name); + return 3; + } + + // Do we still have entries available? + while (numdevata>=atadevlist_max) + atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device"); + + // register device + PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name); + + // record number of device, type of device, increment device count + if (cfg->controller_type == CONTROLLER_UNKNOWN) + cfg->controller_type=CONTROLLER_ATA; + + // close file descriptor + CloseDevice(fd, name); + return 0; +} + +// Returns 1 if device recognised as one we do not want to treat as a general +// SCSI device. Also returns 1 if INQUIRY fails (all "SCSI" devices should +// respond to INQUIRY). Otherwise returns 0 (i.e. normal SCSI device). +static int SCSIFilterKnown(int fd, char * device) +{ + char req_buff[256]; + char di_buff[256]; + int req_len, avail_len, len; + + memset(req_buff, 0, 96); + req_len = 36; + if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) { + /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ + /* watch this spot ... other devices could lock up here */ + req_len = 64; + if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) { + PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device); + // device doesn't like INQUIRY commands + return 1; + } + } + avail_len = req_buff[4] + 5; + len = (avail_len < req_len) ? avail_len : req_len; + if (len >= 36) { + if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) { + PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device); + PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device); + return 1; + } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) { + PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device); + return 1; + } else if ((avail_len >= 96) && (0 == strncmp(req_buff + 8, "ATA", 3))) { + /* <<<< This is Linux specific code to detect SATA disks using a + SCSI-ATA command translation layer. This may be generalized + later when the t10.org SAT project matures. >>>> */ + req_len = 96; + memset(di_buff, 0, req_len); + if (scsiInquiryVpd(fd, 0x83, (unsigned char *)di_buff, req_len)) { + return 0; // guess it is normal device + } + avail_len = ((di_buff[2] << 8) + di_buff[3]) + 4; + len = (avail_len < req_len) ? avail_len : req_len; + if (isLinuxLibAta((unsigned char *)di_buff, len)) { + PrintOut(LOG_INFO, "Device %s: SATA disks accessed via libata are " + "supported by Linux kernel versions 2.6.15-rc1 and above.\n" + "Try adding '-d ata' to the smartd.conf config file line.\n", device); + return 1; + } + } + } + return 0; +} + +// on success, return 0. On failure, return >0. Never return <0, +// please. +static int SCSIDeviceScan(cfgfile *cfg, int scanning) { + int k, fd, err; + char *device = cfg->name; + struct scsi_iec_mode_page iec; + UINT8 tBuf[64]; + + // should we try to register this as a SCSI device? + switch (cfg->controller_type) { + case CONTROLLER_SCSI: + case CONTROLLER_UNKNOWN: + break; + default: + return 1; + } + + // open the device + if ((fd = OpenDevice(device, "SCSI", scanning)) < 0) + return 1; + PrintOut(LOG_INFO,"Device: %s, opened\n", device); + + // early skip if device known and needs to be handled by some other + // device type (e.g. '-d 3ware,') + if (SCSIFilterKnown(fd, device)) { + CloseDevice(fd, device); + return 2; + } + + // check that device is ready for commands. IE stores its stuff on + // the media. + if ((err = scsiTestUnitReady(fd))) { + if (SIMPLE_ERR_NOT_READY == err) + PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device); + else if (SIMPLE_ERR_NO_MEDIUM == err) + PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device); + else if (SIMPLE_ERR_BECOMING_READY == err) + PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device); + else + PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err); + CloseDevice(fd, device); + return 2; + } + + // Badly-conforming USB storage devices may fail this check. + // The response to the following IE mode page fetch (current and + // changeable values) is carefully examined. It has been found + // that various USB devices that malform the response will lock up + // if asked for a log page (e.g. temperature) so it is best to + // bail out now. + if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len))) + cfg->modese_len = iec.modese_len; + else if (SIMPLE_ERR_BAD_FIELD == err) + ; /* continue since it is reasonable not to support IE mpage */ + else { /* any other error (including malformed response) unreasonable */ + PrintOut(LOG_INFO, + "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", + device, err); + CloseDevice(fd, device); + return 3; + } + + // N.B. The following is passive (i.e. it doesn't attempt to turn on + // smart if it is off). This may change to be the same as the ATA side. + if (!scsi_IsExceptionControlEnabled(&iec)) { + PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n" + "Try 'smartctl -s on %s' to turn on SMART features\n", + device, device); + CloseDevice(fd, device); + return 3; + } + + // Device exists, and does SMART. Add to list (allocating more space if needed) + while (numdevscsi >= scsidevlist_max) + scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device"); + + // Flag that certain log pages are supported (information may be + // available from other sources). + if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, tBuf, sizeof(tBuf), 0)) { + for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) { + switch (tBuf[k]) { + case TEMPERATURE_LPAGE: + cfg->TempPageSupported = 1; + break; + case IE_LPAGE: + cfg->SmartPageSupported = 1; + break; + default: + break; + } + } + } + + // record type of device + cfg->controller_type = CONTROLLER_SCSI; + + // get rid of allocated memory only needed for ATA devices. These + // might have been allocated if the user specified Ignore options or + // other ATA-only Attribute-specific options on the DEVICESCAN line. + cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion); + cfg->attributedefs = FreeNonZero(cfg->attributedefs, MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion); + cfg->smartval = FreeNonZero(cfg->smartval, sizeof(struct ata_smart_values),__LINE__,filenameandversion); + cfg->smartthres = FreeNonZero(cfg->smartthres, sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion); + + // Check if scsiCheckIE() is going to work + { + UINT8 asc = 0; + UINT8 ascq = 0; + UINT8 currenttemp = 0; + UINT8 triptemp = 0; + + if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported, + &asc, &ascq, ¤ttemp, &triptemp)) { + PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); + cfg->SuppressReport = 1; + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int retval=scsiCountFailedSelfTests(fd, 0); + if (retval<0) { + // no self-test log, turn off monitoring + PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device); + cfg->selftest=0; + cfg->selflogcount=0; + cfg->selfloghour=0; + } + else { + // register starting values to watch for changes + cfg->selflogcount=SELFTEST_ERRORCOUNT(retval); + cfg->selfloghour =SELFTEST_ERRORHOURS(retval); + } + } + + // disable autosave (set GLTSD bit) + if (cfg->autosave==1){ + if (scsiSetControlGLTSD(fd, 1, cfg->modese_len)) + PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device); + else + PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device); + } + + // or enable autosave (clear GLTSD bit) + if (cfg->autosave==2){ + if (scsiSetControlGLTSD(fd, 0, cfg->modese_len)) + PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device); + else + PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device); + } + + // tell user we are registering device + PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device); + + // close file descriptor + CloseDevice(fd, device); + return 0; +} + +// We compare old and new values of the n'th attribute. Note that n +// is NOT the attribute ID number.. If (Normalized & Raw) equal, +// then return 0, else nonzero. +int ATACompareValues(changedattribute_t *delta, + struct ata_smart_values *new, + struct ata_smart_values *old, + struct ata_smart_thresholds_pvt *thresholds, + int n, char *name){ + struct ata_smart_attribute *now,*was; + struct ata_smart_threshold_entry *thre; + unsigned char oldval,newval; + int sameraw; + + // check that attribute number in range, and no null pointers + if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds) + return 0; + + // pointers to disk's values and vendor's thresholds + now=new->vendor_attributes+n; + was=old->vendor_attributes+n; + thre=thresholds->thres_entries+n; + + // consider only valid attributes + if (!now->id || !was->id || !thre->id) + return 0; + + + // issue warning if they don't have the same ID in all structures: + if ( (now->id != was->id) || (now->id != thre->id) ){ + PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n", + name, (int)now->id, (int)was->id, (int)thre->id); + return 0; + } + + // new and old values of Normalized Attributes + newval=now->current; + oldval=was->current; + + // See if the RAW values are unchanged (ie, the same) + if (memcmp(now->raw, was->raw, 6)) + sameraw=0; + else + sameraw=1; + + // if any values out of the allowed range, or if the values haven't + // changed, return 0 + if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw)) + return 0; + + // values have changed. Construct output and return + delta->newval=newval; + delta->oldval=oldval; + delta->id=now->id; + delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags); + delta->sameraw=sameraw; + + return 1; +} + +// This looks to see if the corresponding bit of the 32 bytes is set. +// This wastes a few bytes of storage but eliminates all searching and +// sorting functions! Entry is ZERO <==> the attribute ON. Calling +// with set=0 tells you if the attribute is being tracked or not. +// Calling with set=1 turns the attribute OFF. +int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){ + unsigned char *data; + int loc=attr>>3; + int bit=attr & 0x07; + unsigned char mask=0x01<=NMONITOR || which < 0){ + PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s", + whatline, filenameandversion, which, reportbug); + EXIT(EXIT_BADCODE); + } + + if (*datap == NULL){ + // NULL data implies Attributes are ON... + if (!set) + return 0; + + // we are writing + if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){ + PrintOut(LOG_CRIT,"No memory to create monattflags\n"); + EXIT(EXIT_NOMEM); + } + } + + // pointer to the 256 bits that we need + data=*datap+which*32; + + // attribute zero is always OFF + if (!attr) + return 1; + + if (!set) + return (data[loc] & mask); + + data[loc]|=mask; + + // return value when setting has no sense + return 0; +} + +// If the self-test log has got more self-test errors (or more recent +// self-test errors) recorded, then notify user. +void CheckSelfTestLogs(cfgfile *cfg, int new){ + char *name=cfg->name; + + if (new<0) + // command failed + MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name); + else { + // old and new error counts + int oldc=cfg->selflogcount; + int newc=SELFTEST_ERRORCOUNT(new); + + // old and new error timestamps in hours + int oldh=cfg->selfloghour; + int newh=SELFTEST_ERRORHOURS(new); + + if (oldcselflogcount= newc; + cfg->selfloghour = newh; + } + return; +} + +// returns 1 if time to do test of type testtype, 0 if not time to do +// test, < 0 if error +int DoTestNow(cfgfile *cfg, char testtype, time_t testtime) { + // start by finding out the time: + struct tm *timenow; + time_t epochnow; + char matchpattern[16]; + regmatch_t substring; + int weekday, length; + unsigned short hours; + testinfo *dat=cfg->testdata; + + // check that self-testing has been requested + if (!dat) + return 0; + + // since we are about to call localtime(), be sure glibc is informed + // of any timezone changes we make. + if (!testtime) + FixGlibcTimeZoneBug(); + + // construct pattern containing the month, day of month, day of + // week, and hour + epochnow = (!testtime ? time(NULL) : testtime); + timenow=localtime(&epochnow); + + // tm_wday is 0 (Sunday) to 6 (Saturday). We use 1 (Monday) to 7 + // (Sunday). + weekday=timenow->tm_wday?timenow->tm_wday:7; + sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1, + timenow->tm_mday, weekday, timenow->tm_hour); + + // if no match, we are done + if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0)) + return 0; + + // must match the ENTIRE type/date/time string + length=strlen(matchpattern); + if (substring.rm_so!=0 || substring.rm_eo!=length) + return 0; + + // never do a second test in the same hour as another test (the % 7 ensures + // that the RHS will never be greater than 65535 and so will always fit into + // an unsigned short) + hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7)); + if (hours==dat->hour) { + if (!testtime && testtype!=dat->testtype) + PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n", + cfg->name, dat->testtype, testtype); + return 0; + } + + // save time and type of the current test; we are ready to do a test + dat->hour=hours; + dat->testtype=testtype; + return 1; +} + +// Print a list of future tests. +void PrintTestSchedule(cfgfile **atadevices, cfgfile **scsidevices){ + int i, t; + cfgfile * cfg; + char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN]; + time_t now; long seconds; + int numdev = numdevata+numdevscsi; + typedef int cnt_t[4]; + cnt_t * testcnts; // testcnts[numdev][4] + if (numdev <= 0) + return; + testcnts = calloc(numdev, sizeof(testcnts[0])); + if (!testcnts) + return; + + PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n"); + + // FixGlibcTimeZoneBug(); // done in PrintOut() + now=time(NULL); + dateandtimezoneepoch(datenow, now); + for (seconds=0; seconds<3600L*24*90; seconds+=checktime) { + // Check for each device whether a test will be run + time_t testtime = now + seconds; + for (i=0; iname, + testcnts[i][t], testtype, date); + } + } + } + } + } + + // Report totals + dateandtimezoneepoch(date, now+seconds); + PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date); + for (i=0; iname, testcnts[i][t], + (testcnts[i][t]==1?"":"s"), "LSCO"[t]); + } + } + + free(testcnts); +} + +// Return zero on success, nonzero on failure. Perform offline (background) +// short or long (extended) self test on given scsi device. +int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) { + int retval = 0; + char *testname = NULL; + char *name = cfg->name; + int inProgress; + + if (scsiSelfTestInProgress(fd, &inProgress)) { + PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name); + cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1; + return 1; + } + + if (1 == inProgress) { + PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in " + "progress.\n", name); + return 1; + } + + switch (testtype) { + case 'S': + testname = "Short Self"; + retval = scsiSmartShortSelfTest(fd); + break; + case 'L': + testname = "Long Self"; + retval = scsiSmartExtendSelfTest(fd); + break; + } + // If we can't do the test, exit + if (NULL == testname) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, + testtype); + return 1; + } + if (retval) { + if ((SIMPLE_ERR_BAD_OPCODE == retval) || + (SIMPLE_ERR_BAD_FIELD == retval)) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, + testname); + if ('L'==testtype) + cfg->testdata->not_cap_long=1; + else + cfg->testdata->not_cap_short=1; + + return 1; + } + PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, + testname, retval); + return 1; + } + + PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname); + + return 0; +} + +// Do an offline immediate or self-test. Return zero on success, +// nonzero on failure. +int DoATASelfTest(int fd, cfgfile *cfg, char testtype) { + + struct ata_smart_values data; + char *testname=NULL; + int retval, dotest=-1; + char *name=cfg->name; + + // Read current smart data and check status/capability + if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) { + PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name); + return 1; + } + + // Check for capability to do the test + switch (testtype) { + case 'O': + testname="Offline Immediate "; + if (isSupportExecuteOfflineImmediate(&data)) + dotest=OFFLINE_FULL_SCAN; + else + cfg->testdata->not_cap_offline=1; + break; + case 'C': + testname="Conveyance Self-"; + if (isSupportConveyanceSelfTest(&data)) + dotest=CONVEYANCE_SELF_TEST; + else + cfg->testdata->not_cap_conveyance=1; + break; + case 'S': + testname="Short Self-"; + if (isSupportSelfTest(&data)) + dotest=SHORT_SELF_TEST; + else + cfg->testdata->not_cap_short=1; + break; + case 'L': + testname="Long Self-"; + if (isSupportSelfTest(&data)) + dotest=EXTEND_SELF_TEST; + else + cfg->testdata->not_cap_long=1; + break; + } + + // If we can't do the test, exit + if (dotest<0) { + PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname); + return 1; + } + + // If currently running a self-test, do not interrupt it to start another. + if (15==(data.self_test_exec_status >> 4)) { + PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n", + name, testname, (int)(data.self_test_exec_status & 0x0f)); + return 1; + } + + // else execute the test, and return status + if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL))) + PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname); + else + PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname); + + return retval; +} + + +int ATACheckDevice(cfgfile *cfg){ + int fd,i; + char *name=cfg->name; + char *mode="ATA"; + + // fix firmware bug if requested + con->fixfirmwarebug=cfg->fixfirmwarebug; + con->controller_port=cfg->controller_port; + con->controller_type=cfg->controller_type; + + // If user has asked, test the email warning system + if (cfg->mailwarn && cfg->mailwarn->emailtest) + MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); + + if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR) + mode="ATA_3WARE_9000"; + + if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR) + mode="ATA_3WARE_678K"; + + // if we can't open device, fail gracefully rather than hard -- + // perhaps the next time around we'll be able to open it. ATAPI + // cd/dvd devices will hang awaiting media if O_NONBLOCK is not + // given (see linux cdrom driver). + if ((fd=OpenDevice(name, mode, 0))<0){ + MailWarning(cfg, 9, "Device: %s, unable to open device", name); + return 1; + } + + // user may have requested (with the -n Directive) to leave the disk + // alone if it is in idle or sleeping mode. In this case check the + // power mode and exit without check if needed + if (cfg->powermode){ + int dontcheck=0, powermode=ataCheckPowerMode(fd); + char *mode=NULL; + if (powermode >= 0) { + int powermode2 = ataCheckPowerMode(fd); + if (powermode2 > powermode) + PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2); + powermode = powermode2; + } + + switch (powermode){ + case -1: + // SLEEP + mode="SLEEP"; + if (cfg->powermode>=1) + dontcheck=1; + break; + case 0: + // STANDBY + mode="STANDBY"; + if (cfg->powermode>=2) + dontcheck=1; + break; + case 0x80: + // IDLE + mode="IDLE"; + if (cfg->powermode>=3) + dontcheck=1; + break; + case 0xff: + // ACTIVE/IDLE + break; + default: + // UNKNOWN + PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", + name, powermode); + cfg->powermode=0; + break; + } + + // if we are going to skip a check, return now + if (dontcheck){ + CloseDevice(fd, name); + if (!cfg->powerquiet) // to avoid waking up system disk + PrintOut(LOG_INFO, "Device: %s, is in %s mode, skipping checks\n", name, mode); + return 0; + } + } + + // check smart status + if (cfg->smartcheck){ + int status=ataSmartStatus2(fd); + if (status==-1){ + PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name); + MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name); + } + else if (status==1){ + PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name); + MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name); + } + } + + // Check everything that depends upon SMART Data (eg, Attribute values) + if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC){ + struct ata_smart_values curval; + struct ata_smart_thresholds_pvt *thresh=cfg->smartthres; + + // Read current attribute values. *drive contains old values and thresholds + if (ataReadSmartValues(fd,&curval)){ + PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name); + MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name); + } + else { + // look for current or offline pending sectors + if (cfg->pending != DONT_MONITOR_UNC) { + int64_t rawval; + unsigned char currentpending, offlinepending; + + TranslatePending(cfg->pending, ¤tpending, &offlinepending); + + if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) { + // Unreadable pending sectors!! + PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval); + MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval); + } + + if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) { + // Unreadable offline sectors!! + PrintOut(LOG_CRIT, "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval); + MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval); + } + } + + if (cfg->usagefailed || cfg->prefail || cfg->usage) { + + // look for failed usage attributes, or track usage or prefail attributes + for (i=0; iusagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){ + + // are we ignoring failures of this attribute? + att *= -1; + if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){ + char attname[64], *loc=attname; + + // get attribute name & skip white space + ataPrintSmartAttribName(loc, att, cfg->attributedefs); + while (*loc && *loc==' ') loc++; + + // warning message + PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc); + MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc); + } + } + + // This block tracks usage or prefailure attributes to see if + // they are changing. It also looks for changes in RAW values + // if this has been requested by user. + if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){ + unsigned char id=delta.id; + + // if the only change is the raw value, and we're not + // tracking raw value, then continue loop over attributes + if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__)) + continue; + + // are we tracking this attribute? + if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){ + char newrawstring[64], oldrawstring[64], attname[64], *loc=attname; + + // get attribute name, skip spaces + ataPrintSmartAttribName(loc, id, cfg->attributedefs); + while (*loc && *loc==' ') loc++; + + // has the user asked for us to print raw values? + if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) { + // get raw values (as a string) and add to printout + char rawstring[64]; + ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs); + sprintf(newrawstring, " [Raw %s]", rawstring); + ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs); + sprintf(oldrawstring, " [Raw %s]", rawstring); + } + else + newrawstring[0]=oldrawstring[0]='\0'; + + // prefailure attribute + if (cfg->prefail && delta.prefail) + PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n", + name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring); + + // usage attribute + if (cfg->usage && !delta.prefail) + PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n", + name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring); + } + } // endof block tracking usage or prefailure + } // end of loop over attributes + + // Save the new values into *drive for the next time around + *(cfg->smartval)=curval; + } + } + } + + // check if number of selftest errors has increased (note: may also DECREASE) + if (cfg->selftest) + CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name)); + + // check if number of ATA errors has increased + if (cfg->errorlog){ + + int new,old=cfg->ataerrorcount; + + // new number of errors + new=ATAErrorCount(fd, name); + + // did command fail? + if (new<0) + // lack of PrintOut here is INTENTIONAL + MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name); + + // has error count increased? + if (new>old){ + PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n", + name, old, new); + MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d", + name, old, new); + } + + // this last line is probably not needed, count always increases + if (new>=0) + cfg->ataerrorcount=new; + } + + // if the user has asked, and device is capable (or we're not yet + // sure) carry out scheduled self-tests. + if (cfg->testdata) { + // long test + if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0) + DoATASelfTest(fd, cfg, 'L'); + // short test + else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0) + DoATASelfTest(fd, cfg, 'S'); + // conveyance test + else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C', 0)>0) + DoATASelfTest(fd, cfg, 'C'); + // offline immediate + else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O', 0)>0) + DoATASelfTest(fd, cfg, 'O'); + } + + // Don't leave device open -- the OS/user may want to access it + // before the next smartd cycle! + CloseDevice(fd, name); + return 0; +} + +#define DEF_SCSI_REPORT_TEMPERATURE_DELTA 2 +static int scsi_report_temperature_delta = DEF_SCSI_REPORT_TEMPERATURE_DELTA; + +int SCSICheckDevice(cfgfile *cfg) +{ + UINT8 asc, ascq; + UINT8 currenttemp; + UINT8 triptemp; + int fd; + char *name=cfg->name; + const char *cp; + + // If the user has asked for it, test the email warning system + if (cfg->mailwarn && cfg->mailwarn->emailtest) + MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name); + + // if we can't open device, fail gracefully rather than hard -- + // perhaps the next time around we'll be able to open it + if ((fd=OpenDevice(name, "SCSI", 0))<0) { + // Lack of PrintOut() here is intentional! + MailWarning(cfg, 9, "Device: %s, unable to open device", name); + return 1; + } + currenttemp = 0; + asc = 0; + ascq = 0; + if (! cfg->SuppressReport) { + if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported, + &asc, &ascq, ¤ttemp, &triptemp)) { + PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n", + name); + MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name); + cfg->SuppressReport = 1; + } + } + if (asc > 0) { + cp = scsiGetIEString(asc, ascq); + if (cp) { + PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); + MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp); + } + } else if (debugmode) + PrintOut(LOG_INFO,"Device: %s, Acceptable asc,ascq: %d,%d\n", + name, (int)asc, (int)ascq); + + if (currenttemp && currenttemp!=255) { + if (cfg->Temperature) { + if (abs(((int)currenttemp - (int)cfg->Temperature)) >= + scsi_report_temperature_delta) { + PrintOut(LOG_INFO, "Device: %s, Temperature changed %d Celsius " + "to %d Celsius since last report\n", name, + (int)(currenttemp - cfg->Temperature), + (int)currenttemp); + cfg->Temperature = currenttemp; + } + } + else { + PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d " + "Celsius\n", name, (int)currenttemp); + if (triptemp) + PrintOut(LOG_INFO, " [trip Temperature is %d Celsius]\n", + (int)triptemp); + cfg->Temperature = currenttemp; + cfg->Temperature = currenttemp; + } + } + + // check if number of selftest errors has increased (note: may also DECREASE) + if (cfg->selftest) + CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0)); + + if (cfg->testdata) { + // long (extended) background test + if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0) + DoSCSISelfTest(fd, cfg, 'L'); + // short background test + else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0) + DoSCSISelfTest(fd, cfg, 'S'); + } + CloseDevice(fd, name); + return 0; +} + +// Checks the SMART status of all ATA and SCSI devices +void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){ + int i; + + for (i=0; i ==> HUP + if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN) + SIGNALFN(SIGINT, SIG_IGN); + + // Catch HUP and USR1 + if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN) + SIGNALFN(SIGHUP, SIG_IGN); + if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN) + SIGNALFN(SIGUSR1, SIG_IGN); +#ifdef _WIN32 + if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN) + SIGNALFN(SIGUSR2, SIG_IGN); +#endif + + // initialize wakeup time to CURRENT time + *wakeuptime=time(NULL); + + return; +} + +#ifdef _WIN32 +// Toggle debug mode implemented for native windows only +// (there is no easy way to reopen tty on *nix) +static void ToggleDebugMode() +{ + if (!debugmode) { + PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n"); + if (!daemon_enable_console("smartd [Debug]")) { + debugmode = 1; + daemon_signal(SIGINT, HUPhandler); + PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid()); + } + else + PrintOut(LOG_INFO,"enable console failed\n"); + } + else if (debugmode == 1) { + daemon_disable_console(); + debugmode = 0; + daemon_signal(SIGINT, sighandler); + PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n"); + } + else + PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode); +} +#endif + +time_t dosleep(time_t wakeuptime){ + time_t timenow=0; + + // If past wake-up-time, compute next wake-up-time + timenow=time(NULL); + while (wakeuptime<=timenow){ + int intervals=1+(timenow-wakeuptime)/checktime; + wakeuptime+=intervals*checktime; + } + + // sleep until we catch SIGUSR1 or have completed sleeping + while (timenowtimenow+checktime){ + PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n"); + wakeuptime=timenow+checktime; + } + + // Exit sleep when time interval has expired or a signal is received + sleep(wakeuptime-timenow); + +#ifdef _WIN32 + // toggle debug mode? + if (caughtsigUSR2) { + ToggleDebugMode(); + caughtsigUSR2 = 0; + } +#endif + + timenow=time(NULL); + } + + // if we caught a SIGUSR1 then print message and clear signal + if (caughtsigUSR1){ + PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n", + wakeuptime-timenow>0?(int)(wakeuptime-timenow):0); + caughtsigUSR1=0; + } + + // return adjusted wakeuptime + return wakeuptime; +} + +// Print out a list of valid arguments for the Directive d +void printoutvaliddirectiveargs(int priority, char d) { + char *s=NULL; + + switch (d) { + case 'n': + PrintOut(priority, "never[,q], sleep[,q], standby[,q], idle[,q]"); + break; + case 's': + PrintOut(priority, "valid_regular_expression"); + break; + case 'd': + PrintOut(priority, "ata, scsi, marvell, removable, 3ware,N"); + break; + case 'T': + PrintOut(priority, "normal, permissive"); + break; + case 'o': + case 'S': + PrintOut(priority, "on, off"); + break; + case 'l': + PrintOut(priority, "error, selftest"); + break; + case 'M': + PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\""); + break; + case 'v': + if (!(s = create_vendor_attribute_arg_list())) { + PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n"); + EXIT(EXIT_NOMEM); + } + PrintOut(priority, "\n%s\n", s); + s=CheckFree(s, __LINE__,filenameandversion); + break; + case 'P': + PrintOut(priority, "use, ignore, show, showall"); + break; + case 'F': + PrintOut(priority, "none, samsung, samsung2"); + break; + } +} + +// exits with an error message, or returns integer value of token +int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){ + char *endptr; + int val; + + // check input range + if (min<0){ + PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min); + return -1; + } + + // make sure argument is there + if (!arg) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n", + configfile, lineno, name, token, min, max); + return -1; + } + + // get argument value (base 10), check that it's integer, and in-range + val=strtol(arg,&endptr,10); + if (*endptr!='\0' || valmax ) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n", + configfile, lineno, name, token, arg, min, max); + return -1; + } + + // all is well; return value + return val; +} + +// This function returns 1 if it has correctly parsed one token (and +// any arguments), else zero if no tokens remain. It returns -1 if an +// error was encountered. +int ParseToken(char *token,cfgfile *cfg){ + char sym; + char *name=cfg->name; + int lineno=cfg->lineno; + char *delim = " \n\t"; + int badarg = 0; + int missingarg = 0; + char *arg = NULL; + int makemail=0; + maildata *mdat=NULL, tempmail; + + // is the rest of the line a comment + if (*token=='#') + return 1; + + // is the token not recognized? + if (*token!='-' || strlen(token)!=2) { + PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", + configfile, lineno, name, token); + PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n"); + return -1; + } + + // token we will be parsing: + sym=token[1]; + + // create temporary maildata structure. This means we can postpone + // allocating space in the data segment until we are sure there are + // no errors. + if ('m'==sym || 'M'==sym){ + if (!cfg->mailwarn){ + memset(&tempmail, 0, sizeof(maildata)); + mdat=&tempmail; + makemail=1; + } + else + mdat=cfg->mailwarn; + } + + // parse the token and swallow its argument + switch (sym) { + int val; + + case 'C': + // monitor current pending sector count (default 197) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0) + return -1; + if (val==CUR_UNC_DEFAULT) + val=0; + else if (val==0) + val=CUR_UNC_DEFAULT; + // set bottom 8 bits to correct value + cfg->pending &= 0xff00; + cfg->pending |= val; + break; + case 'U': + // monitor offline uncorrectable sectors (default 198) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0) + return -1; + if (val==OFF_UNC_DEFAULT) + val=0; + else if (val==0) + val=OFF_UNC_DEFAULT; + // turn off top 8 bits, then set to correct value + cfg->pending &= 0xff; + cfg->pending |= (val<<8); + break; + case 'T': + // Set tolerance level for SMART command failures + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "normal")) { + // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but + // not on failure of an optional S.M.A.R.T. command. + // This is the default so we don't need to actually do anything here. + cfg->permissive=0; + } else if (!strcmp(arg, "permissive")) { + // Permissive mode; ignore errors from Mandatory SMART commands + cfg->permissive=1; + } else { + badarg = 1; + } + break; + case 'd': + // specify the device type + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "ata")) { + cfg->controller_port = 0; + cfg->controller_type = CONTROLLER_ATA; + } else if (!strcmp(arg, "scsi")) { + cfg->controller_port =0; + cfg->controller_type = CONTROLLER_SCSI; + } else if (!strcmp(arg, "marvell")) { + cfg->controller_port =0; + cfg->controller_type = CONTROLLER_MARVELL_SATA; + } else if (!strcmp(arg, "removable")) { + cfg->removable = 1; + } else { + // look 3ware,N RAID device + int i; + char *s; + + // make a copy of the string to mess with + if (!(s = strdup(arg))) { + PrintOut(LOG_CRIT, + "No memory to copy argument to -d option - exiting\n"); + EXIT(EXIT_NOMEM); + } else if (strncmp(s,"3ware,",6)) { + badarg=1; + } else if (split_report_arg2(s, &i)){ + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n", + configfile, lineno, name); + badarg=1; + } else if ( i<0 || i>15) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n", + configfile, lineno, name, i); + badarg=1; + } else { + // determine type of escalade device from name of device + cfg->controller_type = guess_device_type(name); + if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR) + cfg->controller_type=CONTROLLER_3WARE_678K; + + // NOTE: controller_port == disk number + 1 + cfg->controller_port = i+1; + } + s=CheckFree(s, __LINE__,filenameandversion); + } + break; + case 'F': + // fix firmware bug + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "none")) { + cfg->fixfirmwarebug = FIX_NONE; + } else if (!strcmp(arg, "samsung")) { + cfg->fixfirmwarebug = FIX_SAMSUNG; + } else if (!strcmp(arg, "samsung2")) { + cfg->fixfirmwarebug = FIX_SAMSUNG2; + } else { + badarg = 1; + } + break; + case 'H': + // check SMART status + cfg->smartcheck=1; + break; + case 'f': + // check for failure of usage attributes + cfg->usagefailed=1; + break; + case 't': + // track changes in all vendor attributes + cfg->prefail=1; + cfg->usage=1; + break; + case 'p': + // track changes in prefail vendor attributes + cfg->prefail=1; + break; + case 'u': + // track changes in usage vendor attributes + cfg->usage=1; + break; + case 'l': + // track changes in SMART logs + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "selftest")) { + // track changes in self-test log + cfg->selftest=1; + } else if (!strcmp(arg, "error")) { + // track changes in ATA error log + cfg->errorlog=1; + } else { + badarg = 1; + } + break; + case 'a': + // monitor everything + cfg->smartcheck=1; + cfg->prefail=1; + cfg->usagefailed=1; + cfg->usage=1; + cfg->selftest=1; + cfg->errorlog=1; + break; + case 'o': + // automatic offline testing enable/disable + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "on")) { + cfg->autoofflinetest = 2; + } else if (!strcmp(arg, "off")) { + cfg->autoofflinetest = 1; + } else { + badarg = 1; + } + break; + case 'n': + // skip disk check if in idle or standby mode + if (!(arg = strtok(NULL, delim))) + missingarg = 1; + else if (!strcmp(arg, "never") || !strcmp(arg, "never,q")) + cfg->powermode = 0; + else if (!strcmp(arg, "sleep") || !strcmp(arg, "sleep,q")) + cfg->powermode = 1; + else if (!strcmp(arg, "standby") || !strcmp(arg, "standby,q")) + cfg->powermode = 2; + else if (!strcmp(arg, "idle") || !strcmp(arg, "idle,q")) + cfg->powermode = 3; + else + badarg = 1; + cfg->powerquiet = !!strchr(arg, ','); + break; + case 'S': + // automatic attribute autosave enable/disable + if ((arg = strtok(NULL, delim)) == NULL) { + missingarg = 1; + } else if (!strcmp(arg, "on")) { + cfg->autosave = 2; + } else if (!strcmp(arg, "off")) { + cfg->autosave = 1; + } else { + badarg = 1; + } + break; + case 's': + // warn user, and delete any previously given -s REGEXP Directives + if (cfg->testdata){ + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n", + configfile, lineno, name, cfg->testdata->regex); + cfg->testdata=FreeTestData(cfg->testdata); + } + // check for missing argument + if (!(arg = strtok(NULL, delim))) { + missingarg = 1; + } + // allocate space for structure and string + else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n", + configfile, lineno, name, arg); + EXIT(EXIT_NOMEM); + } + else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) { + char errormsg[512]; + // not a valid regular expression! + regerror(val, &(cfg->testdata->cregex), errormsg, 512); + PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n", + configfile, lineno, name, arg, errormsg); + cfg->testdata=FreeTestData(cfg->testdata); + return -1; + } + // Do a bit of sanity checking and warn user if we think that + // their regexp is "strange". User probably confused about shell + // glob(3) syntax versus regular expression syntax regexp(7). + if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO"))) + PrintOut(LOG_INFO, "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n", + configfile, lineno, name, val+1, arg[val], arg); + break; + case 'm': + // send email to address that follows + if (!(arg = strtok(NULL,delim))) + missingarg = 1; + else { + if (mdat->address) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", + configfile, lineno, name, mdat->address); + mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion); + } + mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion); + } + break; + case 'M': + // email warning options + if (!(arg = strtok(NULL, delim))) + missingarg = 1; + else if (!strcmp(arg, "once")) + mdat->emailfreq = 1; + else if (!strcmp(arg, "daily")) + mdat->emailfreq = 2; + else if (!strcmp(arg, "diminishing")) + mdat->emailfreq = 3; + else if (!strcmp(arg, "test")) + mdat->emailtest = 1; + else if (!strcmp(arg, "exec")) { + // Get the next argument (the command line) + if (!(arg = strtok(NULL, delim))) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", + configfile, lineno, name, token); + return -1; + } + // Free the last cmd line given if any, and copy new one + if (mdat->emailcmdline) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n", + configfile, lineno, name, mdat->emailcmdline); + mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion); + } + mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion); + } + else + badarg = 1; + break; + case 'i': + // ignore failure of usage attribute + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__); + break; + case 'I': + // ignore attribute for tracking purposes + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__); + break; + case 'r': + // print raw value when tracking + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); + break; + case 'R': + // track changes in raw value (forces printing of raw value) + if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) + return -1; + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__); + IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__); + break; + case 'v': + // non-default vendor-specific attribute meaning + if (!(arg=strtok(NULL,delim))) { + missingarg = 1; + } else if (parse_attribute_def(arg, &cfg->attributedefs)){ + badarg = 1; + } + break; + case 'P': + // Define use of drive-specific presets. + if (!(arg = strtok(NULL, delim))) { + missingarg = 1; + } else if (!strcmp(arg, "use")) { + cfg->ignorepresets = FALSE; + } else if (!strcmp(arg, "ignore")) { + cfg->ignorepresets = TRUE; + } else if (!strcmp(arg, "show")) { + cfg->showpresets = TRUE; + } else if (!strcmp(arg, "showall")) { + showallpresets(); + } else { + badarg = 1; + } + break; + default: + // Directive not recognized + PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", + configfile, lineno, name, token); + Directives(); + return -1; + } + if (missingarg) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n", + configfile, lineno, name, token); + } + if (badarg) { + PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n", + configfile, lineno, name, token, arg); + } + if (missingarg || badarg) { + PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token); + printoutvaliddirectiveargs(LOG_CRIT, sym); + PrintOut(LOG_CRIT, "\n"); + return -1; + } + + // If this did something to fill the mail structure, and that didn't + // already exist, create it and copy. + if (makemail) { + if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) { + PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n", + configfile, lineno, name); + EXIT(EXIT_NOMEM); + } + memcpy(cfg->mailwarn, mdat, sizeof(maildata)); + } + + return 1; +} + +// Allocate storage for a new cfgfile entry. If original!=NULL, it's +// a copy of the original, but with private data storage. Else all is +// zeroed. Returns address, and fails if non memory available. + +cfgfile *CreateConfigEntry(cfgfile *original){ + cfgfile *add; + + // allocate memory for new structure + if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile)))) + goto badexit; + + // if old structure was pointed to, copy it + if (original) + memcpy(add, original, sizeof(cfgfile)); + + // make private copies of data items ONLY if they are in use (non + // NULL) + add->name = CustomStrDup(add->name, 0, __LINE__,filenameandversion); + + if (add->testdata) { + int val; + if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo)))) + goto badexit; + memcpy(add->testdata, original->testdata, sizeof(testinfo)); + add->testdata->regex = CustomStrDup(add->testdata->regex, 1, __LINE__,filenameandversion); + // only POSIX-portable way to make fresh copy of compiled regex is + // to recompile it completely. There is no POSIX + // compiled-regex-copy command. + if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) { + char errormsg[512]; + regerror(val, &(add->testdata->cregex), errormsg, 512); + PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg); + goto badexit; + } + } + + if (add->mailwarn) { + if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata)))) + goto badexit; + memcpy(add->mailwarn, original->mailwarn, sizeof(maildata)); + add->mailwarn->address = CustomStrDup(add->mailwarn->address, 0, __LINE__,filenameandversion); + add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion); + } + + if (add->attributedefs) { + if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1))) + goto badexit; + memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM); + } + + if (add->monitorattflags) { + if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1))) + goto badexit; + memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32); + } + + if (add->smartval) { + if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values)))) + goto badexit; + } + + if (add->smartthres) { + if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt)))) + goto badexit; + } + + return add; + + badexit: + PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n"); + EXIT(EXIT_NOMEM); +} + + + +// This is the routine that adds things to the cfgentries list. To +// prevent memory leaks when re-reading the configuration file many +// times, this routine MUST deallocate any memory other than that +// pointed to within cfg-> before it returns. +// +// Return values are: +// 1: parsed a normal line +// 0: found comment or blank line +// -1: found SCANDIRECTIVE line +// -2: found an error +// +// Note: this routine modifies *line from the caller! +int ParseConfigLine(int entry, int lineno,char *line){ + char *token=NULL; + char *name=NULL; + char *delim = " \n\t"; + cfgfile *cfg=NULL; + int devscan=0; + + // get first token: device name. If a comment, skip line + if (!(name=strtok(line,delim)) || *name=='#') { + return 0; + } + + // Have we detected the SCANDIRECTIVE directive? + if (!strcmp(SCANDIRECTIVE,name)){ + devscan=1; + if (entry) { + PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile); + return -2; + } + } + + // Is there space for another entry? If not, allocate more + while (entry>=cfgentries_max) + cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device"); + + // We've got a legit entry, make space to store it + cfg=cfgentries[entry]=CreateConfigEntry(NULL); + cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion); + + // Store line number, and by default check for both device types. + cfg->lineno=lineno; + + // Try and recognize if a IDE or SCSI device. These can be + // overwritten by configuration file directives. + if (cfg->controller_type==CONTROLLER_UNKNOWN) + cfg->controller_type = guess_device_type(cfg->name); + + // parse tokens one at a time from the file. + while ((token=strtok(NULL,delim))){ + int retval=ParseToken(token,cfg); + + if (retval==0) + // No tokens left: + break; + + if (retval>0) { + // Parsed token +#if (0) + PrintOut(LOG_INFO,"Parsed token %s\n",token); +#endif + continue; + } + + if (retval<0) { + // error found on the line + return -2; + } + } + + // If we found 3ware controller, then modify device name by adding a SPACE + if (cfg->controller_port){ + int len=17+strlen(cfg->name); + char *newname; + + if (devscan){ + PrintOut(LOG_CRIT, "smartd: can not scan for 3ware devices (line %d of file %s)\n", + lineno, configfile); + return -2; + } + + if (!(newname=(char *)calloc(len,1))) { + PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno)); + EXIT(EXIT_NOMEM); + } + + // Make new device name by adding a space then RAID disk number + snprintf(newname, len, "%s [3ware_disk_%02d]", cfg->name, cfg->controller_port-1); + cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion); + cfg->name=newname; + bytes+=16; + } + + // If NO monitoring directives are set, then set all of them. + if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail || + cfg->usage || cfg->selftest || cfg->errorlog )){ + + PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n", + cfg->name, cfg->lineno, configfile); + + cfg->smartcheck=1; + cfg->usagefailed=1; + cfg->prefail=1; + cfg->usage=1; + cfg->selftest=1; + cfg->errorlog=1; + } + + // additional sanity check. Has user set -M options without -m? + if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){ + PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n", + cfg->name, cfg->lineno, configfile); + return -2; + } + + // has the user has set ? + if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"")){ + // check that -M exec is also set + if (!cfg->mailwarn->emailcmdline){ + PrintOut(LOG_CRIT,"Drive: %s, -m Directive on line %d of file %s needs -M exec Directive\n", + cfg->name, cfg->lineno, configfile); + return -2; + } + // now free memory. From here on the sign of is + // address==NULL and cfg->emailcmdline!=NULL + cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion); + } + + // set cfg->emailfreq to 1 (once) if user hasn't set it + if (cfg->mailwarn && !cfg->mailwarn->emailfreq) + cfg->mailwarn->emailfreq = 1; + + entry++; + + if (devscan) + return -1; + else + return 1; +} + +// clean up utility for ParseConfigFile() +void cleanup(FILE **fpp, int is_stdin){ + if (*fpp){ + // (*fpp != stdin) does not work here if stdin has been closed & reopened + if (!is_stdin) + fclose(*fpp); + *fpp=NULL; + } + + return; +} + + +// Parses a configuration file. Return values are: +// N=>0: found N entries +// -1: syntax error in config file +// -2: config file does not exist +// -3: config file exists but cannot be read +// +// In the case where the return value is 0, there are three +// possiblities: +// Empty configuration file ==> cfgentries==NULL +// No configuration file ==> cfgentries[0]->lineno == 0 +// SCANDIRECTIVE found ==> cfgentries[0]->lineno != 0 +int ParseConfigFile(){ + FILE *fp=NULL; + int entry=0,lineno=1,cont=0,contlineno=0; + char line[MAXLINELEN+2]; + char fullline[MAXCONTLINE+1]; + + int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here + + // Open config file, if it exists and is not + if (!is_stdin) { + fp=fopen(configfile,"r"); + if (fp==NULL && (errno!=ENOENT || configfile_alt)) { + // file exists but we can't read it or it should exist due to '-c' option + int ret = (errno!=ENOENT ? -3 : -2); + PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n", + strerror(errno),configfile); + return ret; + } + } + else // read from stdin ('-c -' option) + fp = stdin; + + // No configuration file found -- use fake one + if (fp==NULL) { + int len=strlen(SCANDIRECTIVE)+4; + char *fakeconfig=(char *)calloc(len,1); + + if (!fakeconfig || + (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) || + -1 != ParseConfigLine(entry, 0, fakeconfig) + ) { + PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s", + __LINE__, filenameandversion, reportbug); + EXIT(EXIT_BADCODE); + } + fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion); + return 0; + } + +#ifdef __CYGWIN__ + setmode(fileno(fp), O_TEXT); // Allow files with \r\n +#endif + + // configuration file exists + PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile); + + // parse config file line by line + while (1) { + int len=0,scandevice; + char *lastslash; + char *comment; + char *code; + + // make debugging simpler + memset(line,0,sizeof(line)); + + // get a line + code=fgets(line,MAXLINELEN+2,fp); + + // are we at the end of the file? + if (!code){ + if (cont) { + scandevice=ParseConfigLine(entry,contlineno,fullline); + // See if we found a SCANDIRECTIVE directive + if (scandevice==-1) { + cleanup(&fp, is_stdin); + return 0; + } + // did we find a syntax error + if (scandevice==-2) { + cleanup(&fp, is_stdin); + return -1; + } + // the final line is part of a continuation line + cont=0; + entry+=scandevice; + } + break; + } + + // input file line number + contlineno++; + + // See if line is too long + len=strlen(line); + if (len>MAXLINELEN){ + char *warn; + if (line[len-1]=='\n') + warn="(including newline!) "; + else + warn=""; + PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n", + (int)contlineno,configfile,warn,(int)MAXLINELEN); + cleanup(&fp, is_stdin); + return -1; + } + + // Ignore anything after comment symbol + if ((comment=strchr(line,'#'))){ + *comment='\0'; + len=strlen(line); + } + + // is the total line (made of all continuation lines) too long? + if (cont+len>MAXCONTLINE){ + PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n", + lineno, (int)contlineno, configfile, (int)MAXCONTLINE); + cleanup(&fp, is_stdin); + return -1; + } + + // copy string so far into fullline, and increment length + strcpy(fullline+cont,line); + cont+=len; + + // is this a continuation line. If so, replace \ by space and look at next line + if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){ + *(fullline+(cont-len)+(lastslash-line))=' '; + continue; + } + + // Not a continuation line. Parse it + scandevice=ParseConfigLine(entry,contlineno,fullline); + + // did we find a scandevice directive? + if (scandevice==-1) { + cleanup(&fp, is_stdin); + return 0; + } + // did we find a syntax error + if (scandevice==-2) { + cleanup(&fp, is_stdin); + return -1; + } + + entry+=scandevice; + lineno++; + cont=0; + } + cleanup(&fp, is_stdin); + + // note -- may be zero if syntax of file OK, but no valid entries! + return entry; +} + + +// Prints copyright, license and version information +void PrintCopyleft(void){ + debugmode=1; + PrintHead(); + PrintCVS(); + return; +} + +/* Prints the message "=======> VALID ARGUMENTS ARE: <=======\n", where + is the list of valid arguments for option opt. */ +void PrintValidArgs(char opt) { + const char *s; + + PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: "); + if (!(s = GetValidArgList(opt))) + PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt); + else + PrintOut(LOG_CRIT, (char *)s); + PrintOut(LOG_CRIT, " <=======\n"); +} + +// Parses input line, prints usage message and +// version/license/copyright messages +void ParseOpts(int argc, char **argv){ + extern char *optarg; + extern int optopt, optind, opterr; + int optchar; + int badarg; + char *tailptr; + long lchecktime; + // Please update GetValidArgList() if you edit shortopts + const char *shortopts = "c:l:q:dDi:p:r:Vh?"; +#ifdef HAVE_GETOPT_LONG + char *arg; + // Please update GetValidArgList() if you edit longopts + struct option longopts[] = { + { "configfile", required_argument, 0, 'c' }, + { "logfacility", required_argument, 0, 'l' }, + { "quit", required_argument, 0, 'q' }, + { "debug", no_argument, 0, 'd' }, + { "showdirectives", no_argument, 0, 'D' }, + { "interval", required_argument, 0, 'i' }, + { "pidfile", required_argument, 0, 'p' }, + { "report", required_argument, 0, 'r' }, +#if defined(_WIN32) || defined(__CYGWIN__) + { "service", no_argument, 0, 'S' }, +#endif + { "version", no_argument, 0, 'V' }, + { "license", no_argument, 0, 'V' }, + { "copyright", no_argument, 0, 'V' }, + { "help", no_argument, 0, 'h' }, + { "usage", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; +#endif + + opterr=optopt=0; + badarg=FALSE; + + // Parse input options. This horrible construction is so that emacs + // indents properly. Sorry. + while (-1 != (optchar = +#ifdef HAVE_GETOPT_LONG + getopt_long(argc, argv, shortopts, longopts, NULL) +#else + getopt(argc, argv, shortopts) +#endif + )) { + + switch(optchar) { + case 'q': + // when to quit + if (!(strcmp(optarg,"nodev"))) { + quit=0; + } else if (!(strcmp(optarg,"nodevstartup"))) { + quit=1; + } else if (!(strcmp(optarg,"never"))) { + quit=2; + } else if (!(strcmp(optarg,"onecheck"))) { + quit=3; + debugmode=1; + } else if (!(strcmp(optarg,"showtests"))) { + quit=4; + debugmode=1; + } else if (!(strcmp(optarg,"errors"))) { + quit=5; + } else { + badarg = TRUE; + } + break; + case 'l': + // set the log facility level + if (!strcmp(optarg, "daemon")) + facility=LOG_DAEMON; + else if (!strcmp(optarg, "local0")) + facility=LOG_LOCAL0; + else if (!strcmp(optarg, "local1")) + facility=LOG_LOCAL1; + else if (!strcmp(optarg, "local2")) + facility=LOG_LOCAL2; + else if (!strcmp(optarg, "local3")) + facility=LOG_LOCAL3; + else if (!strcmp(optarg, "local4")) + facility=LOG_LOCAL4; + else if (!strcmp(optarg, "local5")) + facility=LOG_LOCAL5; + else if (!strcmp(optarg, "local6")) + facility=LOG_LOCAL6; + else if (!strcmp(optarg, "local7")) + facility=LOG_LOCAL7; + else + badarg = TRUE; + break; + case 'd': + // enable debug mode + debugmode = TRUE; + break; + case 'D': + // print summary of all valid directives + debugmode = TRUE; + Directives(); + EXIT(0); + break; + case 'i': + // Period (time interval) for checking + // strtol will set errno in the event of overflow, so we'll check it. + errno = 0; + lchecktime = strtol(optarg, &tailptr, 10); + if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg); + PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + checktime = (int)lchecktime; + break; + case 'r': + // report IOCTL transactions + { + int i; + char *s; + + // split_report_arg() may modify its first argument string, so use a + // copy of optarg in case we want optarg for an error message. + if (!(s = strdup(optarg))) { + PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n"); + EXIT(EXIT_NOMEM); + } + if (split_report_arg(s, &i)) { + badarg = TRUE; + } else if (i<1 || i>3) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg); + PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n"); + EXIT(EXIT_BADCMD); + } else if (!strcmp(s,"ioctl")) { + con->reportataioctl = con->reportscsiioctl = i; + } else if (!strcmp(s,"ataioctl")) { + con->reportataioctl = i; + } else if (!strcmp(s,"scsiioctl")) { + con->reportscsiioctl = i; + } else { + badarg = TRUE; + } + s=CheckFree(s, __LINE__,filenameandversion); + } + break; + case 'c': + // alternate configuration file + if (strcmp(optarg,"-")) + configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion); + else // read from stdin + configfile=configfile_stdin; + break; + case 'p': + // output file with PID number + pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion); + break; +#if defined(_WIN32) || defined(__CYGWIN__) + case 'S': + // running as service +#ifdef __CYGWIN__ // On Windows, option is already handled by daemon_main(), so ignore it + is_service = 1; +#endif + break; +#endif // _WIN32 || __CYGWIN__ + case 'V': + // print version and CVS info + PrintCopyleft(); + EXIT(0); + break; + case 'h': + // help: print summary of command-line options + debugmode=1; + PrintHead(); + Usage(); + EXIT(0); + break; + case '?': + default: + // unrecognized option + debugmode=1; + PrintHead(); +#ifdef HAVE_GETOPT_LONG + // Point arg to the argument in which this option was found. + arg = argv[optind-1]; + // Check whether the option is a long option that doesn't map to -h. + if (arg[1] == '-' && optchar != 'h') { + // Iff optopt holds a valid option then argument must be missing. + if (optopt && (strchr(shortopts, optopt) != NULL)) { + PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2); + PrintValidArgs(optopt); + } else { + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2); + } + PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } +#endif + if (optopt) { + // Iff optopt holds a valid option then argument must be missing. + if (strchr(shortopts, optopt) != NULL){ + PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt); + PrintValidArgs(optopt); + } else { + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt); + } + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + Usage(); + EXIT(0); + } + + // Check to see if option had an unrecognized or incorrect argument. + if (badarg) { + debugmode=1; + PrintHead(); + // It would be nice to print the actual option name given by the user + // here, but we just print the short form. Please fix this if you know + // a clean way to do it. + PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg); + PrintValidArgs(optchar); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + } + + // non-option arguments are not allowed + if (argc > optind) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]); + PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n"); + EXIT(EXIT_BADCMD); + } + + // no pidfile in debug mode + if (debugmode && pid_file) { + debugmode=1; + PrintHead(); + PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n"); + PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file); + pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion); + EXIT(EXIT_BADCMD); + } + + // print header + PrintHead(); + + return; +} + +// Function we call if no configuration file was found or if the +// SCANDIRECTIVE Directive was found. It makes entries for device +// names returned by make_device_names() in os_OSNAME.c +int MakeConfigEntries(const char *type, int start){ + int i; + int num; + char** devlist = NULL; + cfgfile *first=cfgentries[0],*cfg=first; + + // make list of devices + if ((num=make_device_names(&devlist,type))<0) + PrintOut(LOG_CRIT,"Problem creating device name scan list\n"); + + // if no devices, or error constructing list, return + if (num<=0) + return 0; + + // loop over entries to create + for (i=0; icontroller_type = CONTROLLER_ATA; + if (!strcmp(type,"SCSI") ) + cfg->controller_type = CONTROLLER_SCSI; + + // remove device name, if it's there, and put in correct one + cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion); + // save pointer to the device name created within + // make_device_names + cfg->name=devlist[i]; + } + + // If needed, free memory used for devlist: pointers now in + // cfgentries[]->names. If num==0 we never get to this point, but + // that's OK. If we realloc()d the array length in + // make_device_names() that was ALREADY equivalent to calling + // free(). + devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion); + + return num; +} + +void CanNotRegister(char *name, char *type, int line, int scandirective){ + if( !debugmode && scandirective == 1 ) { return; } + if (line) + PrintOut(scandirective?LOG_INFO:LOG_CRIT, + "Unable to register %s device %s at line %d of file %s\n", + type, name, line, configfile); + else + PrintOut(LOG_INFO,"Unable to register %s device %s\n", + type, name); + return; +} + +// Returns negative value (see ParseConfigFile()) if config file +// had errors, else number of entries which may be zero or positive. +// If we found no configuration file, or it contained SCANDIRECTIVE, +// then *scanning is set to 1, else 0. +int ReadOrMakeConfigEntries(int *scanning){ + int entries; + + // deallocate any cfgfile data structures in memory + RmAllConfigEntries(); + + // parse configuration file configfile (normally /etc/smartd.conf) + if ((entries=ParseConfigFile())<0) { + + // There was an error reading the configuration file. + RmAllConfigEntries(); + if (entries == -1) + PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile); + return entries; + } + + // did we find entries or scan? + *scanning=0; + + // no error parsing config file. + if (entries) { + // we did not find a SCANDIRECTIVE and did find valid entries + PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile); + } + else if (cfgentries && cfgentries[0]) { + // we found a SCANDIRECTIVE or there was no configuration file so + // scan. Configuration file's first entry contains all options + // that were set + cfgfile *first=cfgentries[0]; + int doata = !(first->controller_type==CONTROLLER_SCSI); + int doscsi = !(first->controller_type==CONTROLLER_ATA); + + *scanning=1; + + if (first->lineno) + PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE); + else + PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile); + + // make config list of ATA devices to search for + if (doata) + entries+=MakeConfigEntries("ATA", entries); + // make config list of SCSI devices to search for + if (doscsi) + entries+=MakeConfigEntries("SCSI", entries); + + // warn user if scan table found no devices + if (!entries) { + PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n"); + // get rid of fake entry with SCANDIRECTIVE as name + RmConfigEntry(cfgentries, __LINE__); + } + } + else + PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile); + + return entries; +} + + +// This function tries devices from cfgentries. Each one that can be +// registered is moved onto the [ata|scsi]devices lists and removed +// from the cfgentries list, else it's memory is deallocated. +void RegisterDevices(int scanning){ + int i; + + // start by clearing lists/memory of ALL existing devices + RmAllDevEntries(); + numdevata=numdevscsi=0; + + // Register entries + for (i=0; icontroller_type!=CONTROLLER_SCSI){ + if (ATADeviceScan(ent, scanning)) + CanNotRegister(ent->name, "ATA", ent->lineno, scanning); + else { + // move onto the list of ata devices + cfgentries[i]=NULL; + while (numdevata>=atadevlist_max) + atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device"); + atadevlist[numdevata++]=ent; + } + } + + // then register SCSI devices + if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_UNKNOWN){ + int retscsi=0; + +#if SCSITIMEOUT + struct sigaction alarmAction, defaultaction; + + // Set up an alarm handler to catch USB devices that hang on + // SCSI scanning... + alarmAction.sa_handler= AlarmHandler; + alarmAction.sa_flags = SA_RESTART; + if (sigaction(SIGALRM, &alarmAction, &defaultaction)) { + // if we can't set timeout, just scan device + PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n"); + retscsi=SCSIDeviceScan(ent, scanning); + } + else { + // prepare return point in case of bad SCSI device + if (setjmp(registerscsienv)) + // SCSI device timed out! + retscsi=-1; + else { + // Set alarm, make SCSI call, reset alarm + alarm(SCSITIMEOUT); + retscsi=SCSIDeviceScan(ent, scanning); + alarm(0); + } + if (sigaction(SIGALRM, &defaultaction, NULL)){ + PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n"); + } + } +#else + retscsi=SCSIDeviceScan(ent, scanning); +#endif + + // Now scan SCSI device... + if (retscsi){ + if (retscsi<0) + PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name); + CanNotRegister(ent->name, "SCSI", ent->lineno, scanning); + } + else { + // move onto the list of scsi devices + cfgentries[i]=NULL; + while (numdevscsi>=scsidevlist_max) + scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device"); + scsidevlist[numdevscsi++]=ent; + } + } + + // if device is explictly listed and we can't register it, then + // exit unless the user has specified that the device is removable + if (cfgentries[i] && !scanning){ + if (ent->removable || quit==2) + PrintOut(LOG_INFO, "Device %s not available\n", ent->name); + else { + PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name); + EXIT(EXIT_BADDEV); + } + } + + // free up memory if device could not be registered + RmConfigEntry(cfgentries+i, __LINE__); + } + + return; +} + + +#ifndef _WIN32 +// Main function +int main(int argc, char **argv) +#else +// Windows: internal main function started direct or by service control manager +static int smartd_main(int argc, char **argv) +#endif +{ + // external control variables for ATA disks + smartmonctrl control; + + // is it our first pass through? + int firstpass=1; + + // next time to wake up + time_t wakeuptime; + + // for simplicity, null all global communications variables/lists + con=&control; + memset(con, 0,sizeof(control)); + + // parse input and print header and usage info if needed + ParseOpts(argc,argv); + + // do we mute printing from ataprint commands? + con->printing_switchable=0; + con->dont_print=debugmode?0:1; + + // don't exit on bad checksums + con->checksumfail=0; + + // the main loop of the code + while (1){ + + // are we exiting from a signal? + if (caughtsigEXIT) { + // are we exiting with SIGTERM? + int isterm=(caughtsigEXIT==SIGTERM); + int isquit=(caughtsigEXIT==SIGQUIT); + int isok=debugmode?isterm || isquit:isterm; + + PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n", + caughtsigEXIT, strsignal(caughtsigEXIT)); + + EXIT(isok?0:EXIT_SIGNAL); + } + + // Should we (re)read the config file? + if (firstpass || caughtsigHUP){ + int entries, scanning=0; + + if (!firstpass) { +#ifdef __CYGWIN__ + // Workaround for missing SIGQUIT via keyboard on Cygwin + if (caughtsigHUP==2) { + // Simulate SIGQUIT if another SIGINT arrives soon + caughtsigHUP=0; + sleep(1); + if (caughtsigHUP==2) { + caughtsigEXIT=SIGQUIT; + continue; + } + caughtsigHUP=2; + } +#endif + PrintOut(LOG_INFO, + caughtsigHUP==1? + "Signal HUP - rereading configuration file %s\n": + "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n", + configfile); + } + + // clears cfgentries, (re)reads config file, makes >=0 entries + entries=ReadOrMakeConfigEntries(&scanning); + + if (entries>=0) { + // checks devices, then moves onto ata/scsi list or deallocates. + RegisterDevices(scanning); + } + else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) { + // user has asked to continue on error in configuration file + if (!firstpass) + PrintOut(LOG_INFO,"Reusing previous configuration\n"); + } + else { + // exit with configuration file error status + int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF); + EXIT(status); + } + + // Log number of devices we are monitoring... + if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass)) + PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n", + numdevata, numdevscsi); + else { + PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n"); + EXIT(EXIT_NODEV); + } + + if (quit==4) { + // user has asked to print test schedule + PrintTestSchedule(atadevlist, scsidevlist); + EXIT(0); + } + + // reset signal + caughtsigHUP=0; + } + + // check all devices once + CheckDevicesOnce(atadevlist, scsidevlist); + + // user has asked us to exit after first check + if (quit==3) { + PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n" + "smartd is exiting (exit status 0)\n"); + EXIT(0); + } + + // fork into background if needed + if (firstpass && !debugmode) { +#ifdef __CYGWIN__ + if (!is_service) // don't fork() if running as service via cygrunsrv +#endif + DaemonInit(); + } + + // set exit and signal handlers, write PID file, set wake-up time + if (firstpass){ + Initialize(&wakeuptime); + firstpass=0; + } + + // sleep until next check time, or a signal arrives + wakeuptime=dosleep(wakeuptime); + } +} + + +#ifdef _WIN32 +// Main function for Windows +int main(int argc, char **argv){ + // Options for smartd windows service + static const daemon_winsvc_options svc_opts = { + "--service", // cmd_opt + "smartd", "SmartD Service", // servicename, displayname + // description + "Controls and monitors storage devices using the Self-Monitoring, " + "Analysis and Reporting Technology System (S.M.A.R.T.) " + "built into ATA and SCSI Hard Drives. " + PACKAGE_HOMEPAGE + }; + // daemon_main() handles daemon and service specific commands + // and starts smartd_main() direct, from a new process, + // or via service control manager + return daemon_main("smartd", &svc_opts , smartd_main, argc, argv); +} +#endif diff --git a/smartd.conf b/smartd.conf new file mode 100644 index 0000000..f821683 --- /dev/null +++ b/smartd.conf @@ -0,0 +1,102 @@ +# Sample configuration file for smartd. See man smartd.conf. + +# Home page is: http://smartmontools.sourceforge.net + +# $Id: smartd.conf,v 1.40 2006/04/12 14:00:12 ballen4705 Exp $ + +# smartd will re-read the configuration file if it receives a HUP +# signal + +# The file gives a list of devices to monitor using smartd, with one +# device per line. Text after a hash (#) is ignored, and you may use +# spaces and tabs for white space. You may use '\' to continue lines. + +# You can usually identify which hard disks are on your system by +# looking in /proc/ide and in /proc/scsi. + +# The word DEVICESCAN will cause any remaining lines in this +# configuration file to be ignored: it tells smartd to scan for all +# ATA and SCSI devices. DEVICESCAN may be followed by any of the +# Directives listed below, which will be applied to all devices that +# are found. Most users should comment out DEVICESCAN and explicitly +# list the devices that they wish to monitor. +DEVICESCAN + +# First (primary) ATA/IDE hard disk. Monitor all attributes, enable +# automatic online data collection, automatic Attribute autosave, and +# start a short self-test every day between 2-3am, and a long self test +# Saturdays between 3-4am. +#/dev/hda -a -o on -S on -s (S/../.././02|L/../../6/03) + +# Monitor SMART status, ATA Error Log, Self-test log, and track +# changes in all attributes except for attribute 194 +#/dev/hdb -H -l error -l selftest -t -I 194 + +# Linux-specific example: monitor a SATA (Serial ATA) disk which uses +# the libata driver. This requires a Linux 2.6.15 or later kernel. +# Note that the disk is addressed via a SCSI device, but the +# underlying disk type is actually ATA +# /dev/sda -a -d ata + +# A very silent check. Only report SMART health status if it fails +# But send an email in this case +#/dev/hdc -H -m admin@example.com + +# First two SCSI disks. This will monitor everything that smartd can +# monitor. Start extended self-tests Wednesdays between 6-7pm and +# Sundays between 1-2 am +#/dev/sda -d scsi -s L/../../3/18 +#/dev/sdb -d scsi -s L/../../7/01 + +# Monitor 4 ATA disks connected to a 3ware 6/7/8000 controller which uses +# the 3w-xxxx driver. Start long self-tests Sundays between 1-2, 2-3, 3-4, +# and 4-5 am. +# NOTE: starting with the Linux 2.6 kernel series, the /dev/sdX interface +# is DEPRECATED. Use the /dev/tweN character device interface instead. +# For example /dev/twe0, /dev/twe1, and so on. +#/dev/sdc -d 3ware,0 -a -s L/../../7/01 +#/dev/sdc -d 3ware,1 -a -s L/../../7/02 +#/dev/sdc -d 3ware,2 -a -s L/../../7/03 +#/dev/sdc -d 3ware,3 -a -s L/../../7/04 + +# Monitor 2 ATA disks connected to a 3ware 9000 controller which uses +# the 3w-9xxx driver. Start long self-tests Tuesdays between 1-2 and 3-4 am +#/dev/twa0 -d 3ware,0 -a -s L/../../2/01 +#/dev/twa0 -d 3ware,1 -a -s L/../../2/03 + +# HERE IS A LIST OF DIRECTIVES FOR THIS CONFIGURATION FILE. +# PLEASE SEE THE smartd.conf MAN PAGE FOR DETAILS +# +# -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N +# -T TYPE set the tolerance to one of: normal, permissive +# -o VAL Enable/disable automatic offline tests (on/off) +# -S VAL Enable/disable attribute autosave (on/off) +# -n MODE No check. MODE is one of: never, sleep, standby, idle +# -H Monitor SMART Health Status, report if failed +# -l TYPE Monitor SMART log. Type is one of: error, selftest +# -f Monitor for failure of any 'Usage' Attributes +# -m ADD Send warning email to ADD for -H, -l error, -l selftest, and -f +# -M TYPE Modify email warning behavior (see man page) +# -s REGE Start self-test when type/date matches regular expression (see man page) +# -p Report changes in 'Prefailure' Normalized Attributes +# -u Report changes in 'Usage' Normalized Attributes +# -t Equivalent to -p and -u Directives +# -r ID Also report Raw values of Attribute ID with -p, -u or -t +# -R ID Track changes in Attribute ID Raw value with -p, -u or -t +# -i ID Ignore Attribute ID for -f Directive +# -I ID Ignore Attribute ID for -p, -u or -t Directive +# -C ID Report if Current Pending Sector count non-zero +# -U ID Report if Offline Uncorrectable count non-zero +# -v N,ST Modifies labeling of Attribute N (see man page) +# -a Default: equivalent to -H -f -t -l error -l selftest -C 197 -U 198 +# -F TYPE Use firmware bug workaround. Type is one of: none, samsung +# -P TYPE Drive-specific presets: use, ignore, show, showall +# # Comment: text after a hash sign is ignored +# \ Line continuation character +# Attribute ID is a decimal integer 1 <= ID <= 255 +# except for -C and -U, where ID = 0 turns them off. +# All but -d, -m and -M Directives are only implemented for ATA devices +# +# If the test string DEVICESCAN is the first uncommented text +# then smartd will scan for devices /dev/hd[a-l] and /dev/sd[a-z] +# DEVICESCAN may be followed by any desired Directives. diff --git a/smartd.conf.5.in b/smartd.conf.5.in new file mode 100644 index 0000000..d8ddc71 --- /dev/null +++ b/smartd.conf.5.in @@ -0,0 +1,1253 @@ +.ig +Copyright (C) 2002-6 Bruce Allen + +$Id: smartd.conf.5.in,v 1.73 2006/04/12 14:03:14 ballen4705 Exp $ + +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/ +.. +.TH SMARTD.CONF 5 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE +.SH NAME +\fBsmartd.conf\fP \- SMART Disk Monitoring Daemon Configuration File\fP + +.SH FULL PATH +.B /usr/local/etc/smartd.conf + +.SH PACKAGE VERSION +CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME + +.SH DESCRIPTION +\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. + +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 +background. If \fBsmartd\fP subsequently receives a \fBHUP\fP signal, +it will then re-read the configuration file. If \fBsmartd\fP is +running in debug mode, then an \fBINT\fP signal will also make it +re-read the configuration file. This signal can be generated by typing +\fB\\fP in the terminal window where \fBsmartd\fP is +running. + +.\" DO NOT MODIFY THIS OR THE FOLLOWING TWO LINES. WHAT FOLLOWS +.\" IS AUTOMATICALLY INCLUDED FROM THE FILE smartd.8.in +.\" STARTINCLUDE + +.SH CONFIGURATION FILE /usr/local/etc/smartd.conf +In the absence of a configuration file, 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]. +Under FreeBSD, +\fBsmartd\fP +will try to open all existing ATA devices (with entries in /dev) +.B /dev/ad[0-9]+ +and all existing SCSI devices +.B /dev/da[0-9]+. +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. +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. +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. +Under Darwin, \fBsmartd\fP will open any ATA block storage device. + +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. + +One can avoid this problem, and gain more control over the types of +events monitored by +\fBsmartd\fP, +by using the configuration file +.B /usr/local/etc/smartd.conf. +This file contains a list of devices to monitor, with one device per +line. An example file is included with the +.B smartmontools +distribution. You will find this sample configuration file in +\fB/usr/local/share/doc/smartmontools-5.1/\fP. For security, the configuration file +should not be writable by anyone but root. The syntax of the file is as +follows: +.IP \(bu 4 +There should be one device listed per line, although you may have +lines that are entirely comments or white space. +.IP \(bu 4 +Any text following a hash sign \'#\' and up to the end of the line is +taken to be a comment, and ignored. +.IP \(bu 4 +Lines may be continued by using a backslash \'\e\' as the last +non-whitespace or non-comment item on a line. +.IP \(bu 4 +Note: a line whose first character is a hash sign \'#\' is treated as +a white-space blank line, \fBnot\fP as a non-existent line, and will +\fBend\fP a continuation line. +.PP 0 +.fi +Here is an example configuration file. It\'s for illustrative purposes +only; please don\'t copy it onto your system without reading to the end +of the +.B DIRECTIVES +Section below! + +.nf +.B ################################################ +.B # This is an example smartd startup config file +.B # /usr/local/etc/smartd.conf for monitoring three +.B # ATA disks, three SCSI disks, six ATA disks +.B # behind two 3ware controllers and one SATA disk +.B # +.nf +.B # First ATA disk on two different interfaces. On +.B # the second disk, start a long self-test every +.B # Sunday between 3 and 4 am. +.B # +.B \ \ /dev/hda -a -m admin@example.com,root@localhost +.B \ \ /dev/hdc -a -I 194 -I 5 -i 12 -s L/../../7/03 +.B # +.nf +.B # SCSI disks. Send a TEST warning email to admin on +.B # startup. +.B # +.B \ \ /dev/sda +.B \ \ /dev/sdb -m admin@example.com -M test +.B # +.nf +.B # Strange device. It\'s SCSI. Start a scheduled +.B # long self test between 5 and 6 am Monday/Thursday +.B \ \ /dev/weird -d scsi -s L/../../(1|4)/05 +.B # +.nf +.B # Linux-specific: SATA disk using the libata +.B # driver. This requires a 2.6.15 or greater +.B # kernel. The device entry is SCSI but the +.B # underlying disk understands ATA SMART commands +.B \ \ /dev/sda -a -d ata +.B # +.nf +.B # Four ATA disks on a 3ware 6/7/8000 controller. +.B # Start short self-tests daily between midnight and 1am, +.B # 1-2, 2-3, and 3-4 am. Starting with the Linux 2.6 +.B # kernel series, /dev/sdX is deprecated in favor of +.B # /dev/tweN. For example replace /dev/sdc by /dev/twe0 +.B # and /dev/sdd by /dev/twe1. +.B \ \ /dev/sdc -d 3ware,0 -a -s S/../.././00 +.B \ \ /dev/sdc -d 3ware,1 -a -s S/../.././01 +.B \ \ /dev/sdd -d 3ware,2 -a -s S/../.././02 +.B \ \ /dev/sdd -d 3ware,3 -a -s S/../.././03 +.B # +.nf +.B # Two ATA disks on a 3ware 9000 controller. +.B # Start long self-tests Sundays between midnight and +.B # 1am and 2-3 am +.B \ \ /dev/twa0 -d 3ware,0 -a -s L/../../7/00 +.B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02 +.B # +.nf +.B # The following line enables monitoring of the +.B # ATA Error Log and the Self-Test Error Log. +.B # It also tracks changes in both Prefailure +.B # and Usage Attributes, apart from Attributes +.B # 9, 194, and 231, and shows continued lines: +.B # +.B \ \ /dev/hdd\ -l\ error\ \e +.B \ \ \ \ \ \ \ \ \ \ \ -l\ selftest\ \e +.B \ \ \ \ \ \ \ \ \ \ \ -t\ \e\ \ \ \ \ \ # Attributes not tracked: +.B \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \e\ \ # temperature +.B \ \ \ \ \ \ \ \ \ \ \ -I\ 231\ \e\ \ # also temperature +.B \ \ \ \ \ \ \ \ \ \ \ -I 9\ \ \ \ \ \ # power-on hours +.B # +.B ################################################ +.fi + +.PP +.SH CONFIGURATION FILE DIRECTIVES +.PP + +If the first non-comment entry in the configuration file is the text +string +.B DEVICESCAN +in capital letters, then +\fBsmartd\fP +will ignore any remaining lines in the configuration file, and will +scan for devices. +.B DEVICESCAN +may optionally be followed by Directives that will apply to all +devices that are found in the scan. Please see below for additional +details. + +.sp 2 +The following are the Directives that may appear following the device +name or +.B DEVICESCAN +on any line of the +.B /usr/local/etc/smartd.conf +configuration file. Note that +.B these are NOT command-line options for +\fBsmartd\fP. +The Directives below may appear in any order, following the device +name. + +.B For an ATA device, +if no Directives appear, then the device will be monitored +as if the \'\-a\' Directive (monitor all SMART properties) had been given. + +.B If a SCSI disk is listed, +it will be monitored at the maximum implemented level: roughly +equivalent to using the \'\-H \-l selftest\' options for an ATA disk. +So with the exception of \'\-d\', \'\-m\', \'\-l selftest\', \'\-s\', and +\'\-M\', the Directives below are ignored for SCSI disks. For SCSI +disks, the \'\-m\' Directive sends a warning email if the SMART status +indicates a disk failure or problem, if the SCSI inquiry about disk +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? +or /dev/twa?) 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). + +.TP +.B \-d TYPE +Specifies the type of the device. This Directive may be used multiple +times for one device, but the arguments \fIata\fP, \fIscsi\fP, +\fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than +one is given then \fBsmartd\fP will use the last one which appears. + +If none of these three arguments is given, then \fBsmartd\fP will +first attempt to guess the device type by looking at whether the sixth +character in the device name is an \'s\' or an \'h\'. This will work for +device names like /dev/hda or /dev/sdb, and corresponds to choosing +\fIata\fP or \fIscsi\fP respectively. If +\fBsmartd\fP +can\'t guess from this sixth character, then it will simply try to +access the device using first ATA and then SCSI ioctl()s. + +The valid arguments to this Directive are: + +.I ata +\- the device type is ATA. This prevents +\fBsmartd\fP +from issuing SCSI commands to an ATA device. + +.I scsi +\- the device type is SCSI. This prevents +\fBsmartd\fP +from issuing ATA commands to a SCSI device. + +.I marvell +\- Under Linux, interact with SATA disks behind Marvell chip-set +controllers (using the Marvell rather than libata driver). + +.I 3ware,N +\- the device consists of one or more ATA disks connected to a 3ware +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 3ware_disk_XX +with XX in the range from 00 to 15 inclusive. + +This Directive may at first appear confusing, because the 3ware +controller is a SCSI device (such as /dev/sda) and should be listed as +such in the the configuration file. +However when the \'\-d 3ware,N\' +Directive is used, then the corresponding disk is addressed using +native ATA commands which are \'passed through\' the SCSI driver. All +ATA Directives listed in this man page may be used. Note that while +you may use \fBany\fP of the 3ware SCSI logical devices /dev/sd? to +address \fBany\fP of the physical disks (3ware ports), error and log +messages will make the most sense if you always list the 3ware SCSI +logical device corresponding to the particular physical disks. Please +see the \fBsmartctl\fP man page for further details. + +ATA disks behind 3ware controllers may alternatively be accessed via a +character device interface /dev/twe0-15 (3ware 6000/7000/8000 +controllers) and /dev/twa0-15 (3ware 9000 series controllers). Note +that the 9000 series controllers may \fBonly\fP be accessed using the +character device interface /dev/twa0-15 and not the SCSI device +interface /dev/sd?. Please see the \fBsmartctl\fP man page for +further details. + +Note that 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, if the SCSI interface is used, and produce these types of +harmless syslog error messages instead: \fB\'3w-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 versions. See +\fBhttp://smartmontools.sourceforge.net/\fP for instructions. +Alternatively use the character device interfaces /dev/twe0-15 (3ware +6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series +controllers). + + +.B 3ware controllers are currently ONLY supported under Linux. + +.I removable +\- the device or its media is removable. This indicates to +\fBsmartd\fP +that it should continue (instead of exiting, which is the default +behavior) if the device does not appear to be present when +\fBsmartd\fP is started. This Directive may be used in conjunction +with the other \'\-d\' Directives. + +.TP +.B \-n POWERMODE[,q] +This \'nocheck\' Directive is used to prevent a disk from being +spun-up when it is periodically polled by \fBsmartd\fP. + +ATA disks have five different power states. In order of increasing +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 +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. + +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 +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 +\fBsmartd\fP checks it. This is the default behavior if the '\-n' +Directive is not given. + +.I sleep +\- check the device unless it is in SLEEP mode. + +.I standby +\- check the device unless it is in SLEEP or STANDBY mode. In +these modes most disks are not spinning, so if you want to prevent +a laptop disk from spinning up each time that \fBsmartd\fP polls, +this is probably what you want. + +.I idle +\- check the device unless it is in SLEEP, STANDBY or IDLE mode. +In the IDLE state, most disks are still spinning, so this is probably +not what you want. + +When a periodic test is skipped, \fBsmartd\fP normally writes an +informal log message. The message can be suppressed by appending +the option \',q\' to POWERMODE (like \'\-n standby,q\'). +This prevents a laptop disk from spinning up due to this message. + +.TP +.B \-T TYPE +Specifies how tolerant +\fBsmartd\fP +should be of SMART command failures. The valid arguments to this +Directive are: + +.I normal +\- do not try to monitor the disk if a mandatory SMART command fails, but +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. + +[Please see the \fBsmartctl \-T\fP command-line option.] +.TP +.B \-o VALUE +Enables or disables SMART Automatic Offline Testing when +\fBsmartd\fP +starts up and has no further effect. The valid arguments to this +Directive are \fIon\fP and \fIoff\fP. + +The delay between tests is vendor-specific, but is typically four +hours. + +Note that SMART Automatic Offline Testing is \fBnot\fP part of the ATA +Specification. Please see the +.B smartctl \-o +command-line option documentation for further information about this +feature. +.TP +.B \-S VALUE +Enables or disables Attribute Autosave when \fBsmartd\fP +starts up and has no further effect. The valid arguments to this +Directive are \fIon\fP and \fIoff\fP. Also affects SCSI devices. +[Please see the \fBsmartctl \-S\fP command-line option.] +.TP +.B \-H +Check the SMART health status of the disk. If any Prefailure +Attributes are less than or equal to their threshold values, then disk +failure is predicted in less than 24 hours, and a message at loglevel +.B \'LOG_CRITICAL\' +will be logged to syslog. [Please see the +.B smartctl \-H +command-line option.] +.TP +.B \-l TYPE +Reports increases in the number of errors in one of the two SMART logs. The +valid arguments to this Directive are: + +.I error +\- report if the number of ATA errors reported in the ATA Error Log +has increased since the last check. + +.I selftest +\- report if the number of failed tests reported in the SMART +Self-Test Log has increased since the last check, or if the timestamp +associated with the most recent failed test has increased. Note that +such errors will \fBonly\fP be logged if you run self-tests on the +disk (and it fails a test!). Self-Tests can be run automatically by +\fBsmartd\fP: please see the \fB\'\-s\'\fP Directive below. +Self-Tests can also be run manually by using the \fB\'\-t\ short\'\fP +and \fB\'\-t\ long\'\fP options of \fBsmartctl\fP and the results of +the testing can be observed using the \fBsmartctl \'\-l\ selftest\'\fP +command-line option.] + +[Please see the \fBsmartctl \-l\fP and \fB\-t\fP command-line +options.] +.TP +.B \-s REGEXP +Run Self-Tests or Offline Immediate Tests, at scheduled times. A +Self- or Offline Immediate Test will be run at the end of periodic +device polling, if all 12 characters of the string \fBT/MM/DD/d/HH\fP +match the extended regular expression \fBREGEXP\fP. Here: +.RS 7 +.IP \fBT\fP 4 +is the type of the test. The values that \fBsmartd\fP will try to +match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a +\fBS\fPhort Self-Test, \'C\' for a \fBC\fPonveyance Self-Test (ATA +only), and \'O\' for an \fBO\fPffline Immediate Test (ATA only). As +soon as a match is found, the test will be started and no additional +matches will be sought for that device and that polling cycle. +.IP \fBMM\fP 4 +is the month of the year, expressed with two decimal digits. The +range is from 01 (January) to 12 (December) inclusive. Do \fBnot\fP +use a single decimal digit or the match will always fail! +.IP \fBDD\fP 4 +is the day of the month, expressed with two decimal digits. The +range is from 01 to 31 inclusive. Do \fBnot\fP +use a single decimal digit or the match will always fail! +.IP \fBd\fP 4 +is the day of the week, expressed with one decimal digit. The +range is from 1 (Monday) to 7 (Sunday) inclusive. +.IP \fBHH\fP 4 +is the hour of the day, written with two decimal digits, and given in +hours after midnight. The range is 00 (midnight to just before 1am) +to 23 (11pm to just before midnight) inclusive. Do \fBnot\fP use a +single decimal digit or the match will always fail! +.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. +.TP +.B \& +Some examples follow. In reading these, keep in mind that in extended +regular expressions a dot \fB\'.\'\fP matches any single character, and +a parenthetical expression such as \fB\'(A|B|C)\'\fP denotes any one of the three possibilities \fBA\fP, +\fBB\fP, or \fBC\fP. + +To schedule a short Self-Test between 2-3am every morning, use: +.nf +\fB \-s S/../.././02\fP +.fi +To schedule a long Self-Test between 4-5am every Sunday morning, use: +.nf +\fB \-s L/../../7/04\fP +.fi +To schedule a long Self-Test between 10-11pm on the first and +fifteenth day of each month, use: +.nf +\fB \-s L/../(01|15)/./22\fP +.fi +To schedule an Offline Immediate test after every midnight, 6am, +noon,and 6pm, plus a Short Self-Test daily at 1-2am and a Long +Self-Test every Saturday at 3-4am, use: +.nf +\fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP +.fi + +Scheduled tests are run immediately following the regularly-scheduled +device polling, if the current local date, time, and test type, match +\fBREGEXP\fP. By default the regularly-scheduled device polling +occurs every thirty minutes after starting \fBsmartd\fP. Take caution +if you use the \'\-i\' option to make this polling interval more than +sixty minutes: the poll times may fail to coincide with any of the +testing times that you have specified with \fBREGEXP\fP, and so the +self tests may not take place as you wish. + +Before running an offline or self-test, \fBsmartd\fP checks to be sure +that a self-test is not already running. If a self-test \fBis\fP +already running, then this running self test will \fBnot\fP be +interrupted to begin another test. + +\fBsmartd\fP will not attempt to run \fBany\fP type of test if another +test was already started or run in the same hour. + +Each time a test is run, \fBsmartd\fP will log an entry to SYSLOG. +You can use these or the '-q showtests' command-line option to verify +that you constructed \fBREGEXP\fP correctly. The matching order +(\fBL\fP before \fBS\fP before \fBC\fP before \fBO\fP) ensures that +if multiple test types are all scheduled for the same hour, the +longer test type has precedence. This is usually the desired behavior. + +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 +issue harmless informational warning messages if it detects characters +in \fBREGEXP\fP that appear to indicate that you have made this +mistake. + +.TP +.B \-m ADD +Send a warning email to the email address \fBADD\fP if the \'\-H\', +\'\-l\', \'\-f\', \'\-C\', or \'\-O\' Directives detect a failure or a +new error, or if a SMART command to the disk fails. This Directive +only works in conjunction with these other Directives (or with the +equivalent default \'\-a\' Directive). + +To prevent your email in-box from getting filled up with warning +messages, by default only a single warning will be sent for each of +the enabled alert types, \'\-H\', \'\-l\', \'\-f\', \'\-C\', or +\'\-O\' even if more than one failure or error is detected or if the +failure or error persists. [This behavior can be modified; see the +\'\-M\' Directive below.] + +To send email to more than one user, please use the following "comma +separated" form for the address: \fBuser1@add1,user2@add2,...,userN@addN\fP +(with no spaces). + +To test that email is being sent correctly, use the \'\-M test\' +Directive described below to send one test email message on +\fBsmartd\fP +startup. + +By default, email is sent using the system +.B mail +command. In order that +\fBsmartd\fP +find the mail command (normally /bin/mail) an executable named +.B \'mail\' +must be in the path of the shell or environment from which +\fBsmartd\fP +was started. If you wish to specify an explicit path to the mail +executable (for example /usr/local/bin/mail) or a custom script to +run, please use the \'\-M exec\' Directive below. + +Note that by default under Solaris, in the previous paragraph, +\'\fBmailx\fP\' and \'\fB/bin/mailx\fP\' are used, since Solaris +\'/bin/mail\' does not accept a \'\-s\' (Subject) command-line +argument. + +On Windows, the \'\fBBlat\fP\' mailer +(\fBhttp://blat.sourceforge.net/\fP) is used by default. +This mailer uses a different command line syntax, see +\'\-M exec\' below. + +Note also that there is a special argument +.B +which can be given to the \'\-m\' Directive in conjunction with the \'\-M +exec\' Directive. Please see below for an explanation of its effect. + +If the mailer or the shell running it produces any STDERR/STDOUT +output, then a snippet of that output will be copied to SYSLOG. The +remainder of the output is discarded. If problems are encountered in +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. + +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. + +.TP +.B \-M TYPE +These Directives modify the behavior of the +\fBsmartd\fP +email warnings enabled with the \'\-m\' email Directive described above. +These \'\-M\' Directives only work in conjunction with the \'\-m\' +Directive and can not be used without it. + +Multiple \-M Directives may be given. If more than one of the +following three \-M Directives are given (example: \-M once \-M daily) +then the final one (in the example, \-M daily) is used. + +The valid arguments to the \-M Directive are (one of the following +three): + +.I once +\- send only one warning email for each type of disk problem detected. This +is the default. + +.I daily +\- send additional warning reminder emails, once per day, for each type +of disk problem detected. + +.I diminishing +\- send additional warning reminder emails, after a one-day interval, +then a two-day interval, then a four-day interval, and so on for each +type of disk problem detected. Each interval is twice as long as the +previous interval. + +In addition, one may add zero or more of the following Directives: + +.I test +\- send a single test email +immediately upon +\fBsmartd\fP +startup. This allows one to verify that email is delivered correctly. + +.I exec PATH +\- run the executable PATH instead of the default mail command, when +\fBsmartd\fP +needs to send email. PATH must point to an executable binary file or +script. + +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 +/usr/local/share/doc/smartmontools-5.1/examplescripts/. + +The return status of the executable is recorded by \fBsmartd\fP in +SYSLOG. The executable is not expected to write to STDOUT or +STDERR. If it does, then this is interpreted as indicating that +something is going wrong with your executable, and a fragment of this +output is logged to SYSLOG to help you to understand the problem. +Normally, if you wish to leave some record behind, the executable +should send mail or write to a file or device. + +Before running the executable, \fBsmartd\fP sets a number of +environment variables. These environment variables may be used to +control the executable\'s behavior. The environment variables +exported by \fBsmartd\fP are: +.RS 7 +.IP \fBSMARTD_MAILER\fP 4 +is set to the argument of \-M exec, if present or else to \'mail\' +(examples: /bin/mail, mail). +.IP \fBSMARTD_DEVICE\fP 4 +is set to the device path (examples: /dev/hda, /dev/sdb). +.IP \fBSMARTD_DEVICETYPE\fP 4 +is set to the device type (possible values: ata, scsi, 3ware,N). Here +N=0,...,15 denotes the ATA disk behind a 3ware RAID controller. +.IP \fBSMARTD_DEVICESTRING\fP 4 +is set to the device description. For SMARTD_DEVICETYPE of ata or +scsi, this is the same as SMARTD_DEVICE. For 3ware RAID controllers, +the form used is \'/dev/sdc [3ware_disk_01]\'. In this case 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_FAILTYPE\fP 4 +gives the reason for the warning or message email. The possible values that +it takes and their meanings are: +.nf +.fi +\fIEmailTest\fP: this is an email test message. +.nf +.fi +\fIHealth\fP: the SMART health status indicates imminent failure. +.nf +.fi +\fIUsage\fP: a usage Attribute has failed. +.nf +.fi +\fISelfTest\fP: the number of self-test failures has increased. +.nf +.fi +\fIErrorCount\fP: the number of errors in the ATA error log has increased. +.nf +.fi +\fICurrentPendingSector\fP: one of more disk sectors could not be +read and are marked to be reallocated (replaced with spare sectors). +.nf +.fi +\fIOfflineUncorrectableSector\fP: during off\-line testing, or self\-testing, +one or more disk sectors could not be read. +.nf +.fi +\fIFailedHealthCheck\fP: the SMART health status command failed. +.nf +.fi +\fIFailedReadSmartData\fP: the command to read SMART Attribute data failed. +.nf +.fi +\fIFailedReadSmartErrorLog\fP: the command to read the SMART error log failed. +.nf +.fi +\fIFailedReadSmartSelfTestLog\fP: the command to read the SMART self-test log failed. +.nf +.fi +\fIFailedOpenDevice\fP: the open() command to the device failed. +.IP \fBSMARTD_ADDRESS\fP 4 +is determined by the address argument ADD of the \'\-m\' Directive. +If ADD is \fB\fP, then \fBSMARTD_ADDRESS\fP is not set. +Otherwise, it is set to the comma-separated-list of email addresses +given by the argument ADD, with the commas replaced by spaces +(example:admin@example.com root). If more than one email address is +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. +.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. +.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. +.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 +and no newlines, and is NOT quoted. For example: +.nf +.fi +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. +.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. +.TP +.B \& +The shell which is used to run PATH is system-dependent. For vanilla +Linux/glibc it\'s bash. For other systems, the man page for +\fBpopen\fP(3) should say what shell is used. + +If the \'\-m ADD\' Directive is given with a normal address argument, +then the executable pointed to by PATH will be run in a shell with +STDIN receiving the body of the email message, and with the same +command-line arguments: +.nf +-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS +.fi +that would normally be provided to \'mail\'. Examples include: +.nf +.B -m user@home -M exec /bin/mail +.B -m admin@work -M exec /usr/local/bin/mailto +.B -m root -M exec /Example_1/bash/script/below +.fi + +Note that on Windows, the syntax of the \'\fBBlat\fP\' mailer is +used: +.nf +- -q -subject "$SMARTD_SUBJECT" -to "$SMARTD_ADDRESS" +.fi + +If the \'\-m ADD\' Directive is given with the special address argument +.B +then the executable pointed to by PATH is run in a shell with +.B no +STDIN and +.B no +command-line arguments, for example: +.nf +.B -m -M exec /Example_2/bash/script/below +.fi +If the executable produces any STDERR/STDOUT output, then \fBsmartd\fP +assumes that something is going wrong, and a snippet of that output +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 +/usr/local/share/doc/smartmontools-5.1/examplescripts/. + +.TP +.B \-f +Check for \'failure\' of any Usage Attributes. If these Attributes are +less than or equal to the threshold, it does NOT indicate imminent +disk failure. It "indicates an advisory condition where the usage or +age of the device has exceeded its intended design life period." +[Please see the \fBsmartctl \-A\fP command-line option.] +.TP +.B \-p +Report anytime that a Prefail Attribute has changed +its value since the last check, 30 minutes ago. [Please see the +.B smartctl \-A +command-line option.] +.TP +.B \-u +Report anytime that a Usage Attribute has changed its value +since the last check, 30 minutes ago. [Please see the +.B smartctl \-A +command-line option.] +.TP +.B \-t +Equivalent to turning on the two previous flags \'\-p\' and \'\-u\'. +Tracks changes in \fIall\fP device Attributes (both Prefailure and +Usage). [Please see the \fBsmartctl\fP \-A command-line option.] +.TP +.B \-i ID +Ignore device Attribute number \fBID\fP when checking for failure of +Usage Attributes. \fBID\fP must be a decimal integer in the range +from 1 to 255. This Directive modifies the behavior of the \'\-f\' +Directive and has no effect without it. + +This is useful, for example, if you have a very old disk and don\'t +want to keep getting messages about the hours-on-lifetime Attribute +(usually Attribute 9) failing. This Directive may appear multiple +times for a single device, if you want to ignore multiple Attributes. +.TP +.B \-I ID +Ignore device Attribute \fBID\fP when tracking changes in the +Attribute values. \fBID\fP must be a decimal integer in the range +from 1 to 255. This Directive modifies the behavior of the \'\-p\', +\'\-u\', and \'\-t\' tracking Directives and has no effect without one +of them. + +This is useful, for example, if one of the device Attributes is the disk +temperature (usually Attribute 194 or 231). It\'s annoying to get reports +each time the temperature changes. This Directive may appear multiple +times for a single device, if you want to ignore multiple Attributes. +.TP +.B \-r ID +When tracking, report the \fIRaw\fP value of Attribute \fBID\fP along +with its (normally reported) \fINormalized\fP value. \fBID\fP must be +a decimal integer in the range from 1 to 255. This Directive modifies +the behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives +and has no effect without one of them. This Directive may be given +multiple times. + +A common use of this Directive is to track the device Temperature +(often ID=194 or 231). + +.TP +.B \-R ID +When tracking, report whenever the \fIRaw\fP value of Attribute +\fBID\fP changes. (Normally \fBsmartd\fP only tracks/reports changes +of the \fINormalized\fP Attribute values.) \fBID\fP must be a decimal +integer in the range from 1 to 255. This Directive modifies the +behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives and +has no effect without one of them. This Directive may be given +multiple times. + +If this Directive is given, it automatically implies the \'\-r\' +Directive for the same Attribute, so that the Raw value of the +Attribute is reported. + +A common use of this Directive is to track the device Temperature +(often ID=194 or 231). It is also useful for understanding how +different types of system behavior affects the values of certain +Attributes. + +.TP +.B \-C ID +[ATA only] Report if the current number of pending sectors is +non-zero. Here \fBID\fP is the id number of the Attribute whose raw +value is the Current Pending Sector count. The allowed range of +\fBID\fP is 0 to 255 inclusive. To turn off this reporting, use +ID\ =\ 0. If the \fB\-C ID\fP option is not given, then it defaults to +\fB\-C 197\fP (since Attribute 197 is generally used to monitor +pending sectors). + +A pending sector is a disk sector (containing 512 bytes of your data) +which the device would like to mark as ``bad" and reallocate. +Typically this is because your computer tried to read that sector, and +the read failed because the data on it has been corrupted and has +inconsistent Error Checking and Correction (ECC) codes. This is +important to know, because it means that there is some unreadable data +on the disk. The problem of figuring out what file this data belongs +to is operating system and file system specific. You can typically +force the sector to reallocate by writing to it (translation: make the +device substitute a spare good sector for the bad one) but at the +price of losing the 512 bytes of data stored there. + +.TP +.B \-U ID +[ATA only] Report if the number of offline uncorrectable sectors is +non-zero. Here \fBID\fP is the id number of the Attribute whose raw +value is the Offline Uncorrectable Sector count. The allowed range of +\fBID\fP is 0 to 255 inclusive. To turn off this reporting, use +ID\ =\ 0. If the \fB\-U ID\fP option is not given, then it defaults to +\fB\-U 198\fP (since Attribute 198 is generally used to monitor +offline uncorrectable sectors). + + +An offline uncorrectable sector is a disk sector which was not +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. + +.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: + +.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. + +.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). +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; +(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 more recent Samsung disks (firmware revisions ending in "\-23") the +number of ATA errors reported is byte swapped. Enabling this option +tells \fBsmartd\fP to evaluate this quantity in byte-reversed order. + +Note that an explicit \'\-F\' Directive will over-ride any preset +values for \'\-F\' (see the \'\-P\' option below). + + +[Please see the \fBsmartctl \-F\fP command-line option.] + +.TP +.B \-v N,OPTION +Modifies the labeling for Attribute N, for disks which use +non-standard Attribute definitions. This is useful in connection with +the Attribute tracking/reporting Directives. + +This Directive may appear multiple times. Valid arguments to this +Directive are: + +.I 9,minutes +\- Raw Attribute number 9 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 9,seconds +\- Raw Attribute number 9 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 9,halfminutes +\- Raw Attribute number 9 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 9,temp +\- Raw Attribute number 9 is the disk temperature in Celsius. + +.I 192,emergencyretractcyclect +\- Raw Attribute number 192 is the Emergency Retract Cycle Count. + +.I 193,loadunload +\- Raw Attribute number 193 contains two 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 +unload). As a rule of thumb, the mechanical stress created by one +emergency unload is equivalent to that created by one hundred normal +unloads. + +.I 194,10xCelsius +\- Raw Attribute number 194 is ten times the disk temperature in +Celsius. This is used by some Samsung disks (example: model SV1204H +with RK100-13 firmware). + +.I 194,unknown +\- Raw Attribute number 194 is NOT the disk temperature, and its +interpretation is unknown. This is primarily useful for the -P +(presets) Directive. + +.I 198,offlinescanuncsectorct +\- Raw Attribute number 198 is the Offline Scan UNC Sector Count. + +.I 200,writeerrorcount +\- Raw Attribute number 200 is the Write Error Count. + +.I 201,detectedtacount +\- Raw Attribute number 201 is the Detected TA Count. + +.I 220,temp +\- Raw Attribute number 220 is the disk temperature in Celsius. + +Note: a table of hard drive models, listing which Attribute +corresponds to temperature, can be found at: +\fBhttp://www.guzu.net/linux/hddtemp.db\fP + +.I N,raw8 +\- Print the Raw value of Attribute N as six 8-bit unsigned base-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw8\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw8\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw16 +\- Print the Raw value of Attribute N as three 16-bit unsigned base-10 +integers. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw16\' prints Raw values for ALL Attributes in this +form. The form (for example) \'123,raw16\' only prints the Raw value for +Attribute 123 in this form. + +.I N,raw48 +\- Print the Raw value of Attribute N as a 48-bit unsigned base-10 +integer. This may be useful for decoding the meaning of the Raw +value. The form \'N,raw48\' prints Raw values for ALL Attributes in +this form. The form (for example) \'123,raw48\' only prints the Raw +value for Attribute 123 in this form. + +.TP +.B \-P TYPE +Specifies whether +\fBsmartd\fP +should use any preset options that are available for this drive. The +valid arguments to this Directive are: + +.I use +\- use any presets that are available for this drive. This is the default. + +.I ignore +\- do not use any presets for this drive. + +.I show +\- show the presets listed for this drive in the database. + +.I showall +\- show the presets that are available for all drives and then exit. + +[Please see the +.B smartctl \-P +command-line option.] + +.TP +.B \-a +Equivalent to turning on all of the following Directives: +.B \'\-H\' +to check the SMART health status, +.B \'\-f\' +to report failures of Usage (rather than Prefail) Attributes, +.B \'\-t\' +to track changes in both Prefailure and Usage Attributes, +.B \'\-l\ selftest\' +to report increases in the number of Self-Test Log errors, +.B \'\-l\ error\' +to report increases in the number of ATA errors, +.B \'\-C 197\' +to report nonzero values of the current pending sector count, and +.B \'\-U 198\' +to report nonzero values of the offline pending sector count. + +Note that \-a is the default for ATA devices. If none of these other +Directives is given, then \-a is assumed. + +.TP +.B # +Comment: ignore the remainder of the line. +.TP +.B \e +Continuation character: if this is the last non-white or non-comment +character on a line, then the following line is a continuation of the current +one. +.PP +If you are not sure which Directives to use, I suggest experimenting +for a few minutes with +.B smartctl +to see what SMART functionality your disk(s) support(s). If you do +not like voluminous syslog messages, a good choice of +\fBsmartd\fP +configuration file Directives might be: +.nf +.B \-H \-l\ selftest \-l\ error \-f. +.fi +If you want more frequent information, use: +.B -a. + +.TP +.B ADDITIONAL DETAILS ABOUT DEVICESCAN +If the first 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. + +If \fBDEVICESCAN\fP is not followed by any Directives, then smartd +will scan for both ATA and SCSI devices, and will monitor all possible +SMART properties of any devices that are found. + +\fBDEVICESCAN\fP may optionally be followed by any valid Directives, +which will be applied to all devices that are found in the scan. For +example +.nf +.B DEVICESCAN -m root@example.com +.fi +will scan for all devices, and then monitor them. It will send one +email warning per device for any problems that are found. +.nf +.B DEVICESCAN -d ata -m root@example.com +.fi +will do the same, but restricts the scan to ATA devices only. +.nf +.B DEVICESCAN -H -d ata -m root@example.com +.fi +will do the same, but only monitors the SMART health status of the +devices, (rather than the default \-a, which monitors all SMART +properties). + +.TP +.B EXAMPLES OF SHELL SCRIPTS FOR \'\-M exec\' +These are two examples of shell scripts that can be used with the \'\-M +exec PATH\' Directive described previously. The paths to these scripts +and similar executables is the PATH argument to the \'\-M exec PATH\' +Directive. + +Example 1: This script is for use with \'\-m ADDRESS -M exec PATH\'. It appends +the output of +.B smartctl -a +to the output of the smartd email warning message and sends it to ADDRESS. + +.nf +\fB +#! /bin/bash + +# Save the email message (STDIN) to a file: +cat > /root/msg + +# Append the output of smartctl -a to the message: +/usr/local/sbin/smartctl -a -d $SMART_DEVICETYPE $SMARTD_DEVICE >> /root/msg + +# Now email the message to the user at address ADD: +/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg +\fP +.fi + +Example 2: This script is for use with \'\-m \-M exec +PATH\'. It warns all users about a disk problem, waits 30 seconds, and +then powers down the machine. + +.nf +\fB +#! /bin/bash + +# Warn all users of a problem +wall \'Problem detected with disk: \' "$SMARTD_DEVICESTRING" +wall \'Warning message from smartd is: \' "$SMARTD_MESSAGE" +wall \'Shutting down machine in 30 seconds... \' + +# Wait half a minute +sleep 30 + +# Power down the machine +/sbin/shutdown -hf now +\fP +.fi + +Some example scripts are distributed with the smartmontools package, +in /usr/local/share/doc/smartmontools-5.1/examplescripts/. + +Please note that these scripts typically run as root, so any files +that they read/write should not be writable by ordinary users or +reside in directories like /tmp that are writable by ordinary users +and may expose your system to symlink attacks. + +As previously described, if the scripts write to STDOUT or STDERR, +this is interpreted as indicating that there was an internal error +within the script, and a snippet of STDOUT/STDERR is logged to SYSLOG. +The remainder is flushed. + +.\" ENDINCLUDE +.\" DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE +.\" END OF THE INCLUDED SECTION FROM smartd.8.in + +.PP +.SH AUTHOR +\fBBruce Allen\fP smartmontools-support@lists.sourceforge.net +.fi +University of Wisconsin \- Milwaukee Physics Department + +.PP +.SH CONTRIBUTORS +The following have made large contributions to smartmontools: +.nf +\fBCasper Dik\fP (Solaris SCSI interface) +\fBChristian Franke\fP (Windows interface and Cygwin package) +\fBDouglas Gilbert\fP (SCSI subsystem) +\fBGuido Guenther\fP (Autoconf/Automake packaging) +\fBGeoffrey Keating\fP (Darwin ATA interface) +\fBEduard Martinescu\fP (FreeBSD interface) +\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list) +\fBKeiji Sawada\fP (Solaris ATA interface) +\fBSergey Svishchev\fP (NetBSD interface) +\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface) +\fBPhil Williams\fP (User interface and drive database) +.fi +Many other individuals have made smaller contributions and corrections. + +.PP +.SH CREDITS +.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 +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. \fBhttp://ssrc.soe.ucsc.edu/\fP . +.SH +HOME PAGE FOR SMARTMONTOOLS: +.fi +Please see the following web site for updates, further documentation, bug +reports and patches: +.nf +.B +http://smartmontools.sourceforge.net/ + +.SH +SEE ALSO: +\fBsmartd\fP(8), \fBsmartctl\fP(8), \fBsyslogd\fP(8), +\fBsyslog.conf\fP(5), \fBbadblocks\fP(8), \fBide\-smart\fP(8), \fBregex\fP(7). + +.SH +CVS ID OF THIS PAGE: +$Id: smartd.conf.5.in,v 1.73 2006/04/12 14:03:14 ballen4705 Exp $ diff --git a/smartd.h b/smartd.h new file mode 100644 index 0000000..c1b5dd1 --- /dev/null +++ b/smartd.h @@ -0,0 +1,291 @@ +/* + * smartd.h + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef SMARTD_H_ +#define SMARTD_H_ + +// Needed since some structure definitions below require POSIX +// extended regular expressions. +#include +#include + + +#ifndef SMARTD_H_CVSID +#define SMARTD_H_CVSID "$Id: smartd.h,v 1.76 2006/04/12 14:54:28 ballen4705 Exp $\n" +#endif + +// Configuration file +#define CONFIGFILENAME "smartd.conf" + +// Scan directive for configuration file +#define SCANDIRECTIVE "DEVICESCAN" + +// maximum line length in configuration file +#define MAXLINELEN 128 + +// maximum length of a continued line in configuration file +#define MAXCONTLINE 1023 + +// default for how often SMART status is checked, in seconds +#define CHECKTIME 1800 + +/* Boolean Values */ +#define TRUE 0x01 +#define FALSE 0x00 + +// Number of monitoring flags per Attribute and offsets. See +// monitorattflags below. +#define NMONITOR 4 +#define MONITOR_FAILUSE 0 +#define MONITOR_IGNORE 1 +#define MONITOR_RAWPRINT 2 +#define MONITOR_RAW 3 + + +// Number of allowed mail message types +#define SMARTD_NMAIL 12 + +typedef struct mailinfo_s { + int logged;// number of times an email has been sent + time_t firstsent;// time first email was sent, as defined by time(2) + time_t lastsent; // time last email was sent, as defined by time(2) +} mailinfo; + +// If user has requested email warning messages, then this structure +// stores the information about them, and track type/date of email +// messages. +typedef struct maildata_s { + mailinfo maillog[SMARTD_NMAIL]; // log info on when mail sent + char *emailcmdline; // script to execute + char *address; // email address, or null + unsigned char emailfreq; // Emails once (1) daily (2) diminishing (3) + unsigned char emailtest; // Send test email? +} maildata; + +// If user has requested automatic testing, then this structure stores +// their regular expression pattern, the compiled form of that regex, +// and information about the disk capabilities and when the last text +// took place + +typedef struct testinfo_s { + char *regex; // text form of regex + regex_t cregex; // compiled form of regex + unsigned short hour; // 1+hour of year when last scheduled self-test done + char testtype; // type of test done at hour indicated just above + signed char not_cap_offline; // 0==unknown OR capable of offline, 1==not capable + signed char not_cap_conveyance; + signed char not_cap_short; + signed char not_cap_long; +} testinfo; + + +// cfgfile is the main data structure of smartd. It is used in two +// ways. First, to store a list of devices/options given in the +// configuration smartd.conf or constructed with DEVICESCAN. And +// second, to point to or provide all persistent storage needed to +// track a device, if registered either as SCSI or ATA. +// +// After parsing the config file, each valid entry has a cfgfile data +// structure allocated in memory for it. In parsing the configuration +// file, some storage space may be needed, of indeterminate length, +// for example for the device name. When this happens, memory should +// be allocated and then pointed to from within the corresponding +// cfgfile structure. + +// After parsing the configuration file, each device is then checked +// to see if it can be monitored (this process is called "registering +// the device". This is done in [scsi|ata]devicescan, which is called +// exactly once, after the configuration file has been parsed and +// cfgfile data structures have been created for each of its entries. +// +// If a device can not be monitored, the memory for its cfgfile data +// structure should be freed by calling rmconfigentry(cfgfile *). In +// this case, we say that this device "was not registered". All +// memory associated with that particular cfgfile structure is thus +// freed. +// +// The remaining devices are polled in a timing look, where +// [ata|scsi]CheckDevice looks at each entry in turn. +// +// If you want to add small amounts of "private" data on a per-device +// basis, just make a new field in cfgfile. This is guaranteed zero +// on startup (when ata|scsi]scsidevicescan(cfgfile *cfg) is first +// called with a pointer to cfgfile. +// +// If you need *substantial* persistent data space for a device +// (dozens or hundreds of bytes) please add a pointer field to +// cfgfile. As before, this is guaranteed NULL when +// ata|scsi]scsidevicescan(cfgfile *cfg) is called. Allocate space for +// it in scsidevicescan or atadevicescan, if needed, and deallocate +// the space in rmconfigentry(cfgfile *cfg). Be sure to make the +// pointer NULL unless it points to an area of the heap that can be +// deallocated with free(). In other words, a non-NULL pointer in +// cfgfile means "this points to data space that should be freed if I +// stop monitoring this device." If you don't need the space anymore, +// please call free() and then SET THE POINTER IN cfgfile TO NULL. +// +// Note that we allocate one cfgfile structure per device. This is +// why substantial persisent data storage should only be pointed to +// from within cfgfile, not kept within cfgfile itself - it saves +// memory for those devices that don't need that type of persistent +// data. +// +// In general, the capabilities of devices should be checked at +// registration time within atadevicescan() and scsidevicescan(), and +// then noted within *cfg. So if device lacks some capability, this +// should be visible within *cfg after returning from +// [ata|scsi]devicescan. +// +// Devices are then checked, once per polling interval, within +// ataCheckDevice() and scsiCheckDevice(). These should only check +// the capabilities that devices already are known to have (as noted +// within *cfg). + +typedef struct configfile_s { + // FIRST SET OF ENTRIES CORRESPOND TO WHAT THE USER PUT IN THE + // CONFIG FILE. SOME ENTRIES MAY BE MODIFIED WHEN A DEVICE IS + // REGISTERED AND WE LEARN ITS CAPABILITIES. + int lineno; // Line number of entry in file + char *name; // Device name (+ optional [3ware_disk_XX]) + unsigned char controller_type; // Controller type, ATA/SCSI/3Ware/(more to come) + unsigned char controller_port; // 1 + (disk number in controller). 0 means controller only handles one disk. + char smartcheck; // Check SMART status + char usagefailed; // Check for failed Usage Attributes + char prefail; // Track changes in Prefail Attributes + char usage; // Track changes in Usage Attributes + char selftest; // Monitor number of selftest errors + char errorlog; // Monitor number of ATA errors + char permissive; // Ignore failed SMART commands + char autosave; // 1=disable, 2=enable Autosave Attributes + char autoofflinetest; // 1=disable, 2=enable Auto Offline Test + unsigned char fixfirmwarebug; // Fix firmware bug + char ignorepresets; // Ignore database of -v options + char showpresets; // Show database entry for this device + char removable; // Device may disappear (not be present) + char powermode; // skip check, if disk in idle or standby mode + char powerquiet; // skip powermode 'skipping checks' message + unsigned char selflogcount; // total number of self-test errors + unsigned short selfloghour; // lifetime hours of last self-test error + testinfo *testdata; // Pointer to data on scheduled testing + unsigned short pending; // lower 8 bits: ID of current pending sector count + // upper 8 bits: ID of offline pending sector count + + // THE NEXT SET OF ENTRIES ALSO TRACK DEVICE STATE AND ARE DYNAMIC + maildata *mailwarn; // non-NULL: info about sending mail or executing script + + // SCSI ONLY + unsigned char SmartPageSupported; // has log sense IE page (0x2f) + unsigned char TempPageSupported; // has log sense temperature page (0xd) + unsigned char Temperature; // last recorded figure (in Celsius) + unsigned char SuppressReport; // minimize nuisance reports + unsigned char modese_len; // mode sense/select cmd len: 0 (don't + // know yet) 6 or 10 + unsigned char notused2[3]; // for packing alignment + + // ATA ONLY FROM HERE ON TO THE END + int ataerrorcount; // Total number of ATA errors + + // following NMONITOR items each point to 32 bytes, in the form of + // 32x8=256 single bit flags + // valid attribute numbers are from 1 <= x <= 255 + // monitorattflags+0 set: ignore failure for a usage attribute + // monitorattflats+32 set: don't track attribute + // monitorattflags+64 set: print raw value when tracking + // monitorattflags+96 set: track changes in raw value + unsigned char *monitorattflags; + + // NULL UNLESS (1) STORAGE IS ALLOCATED WHEN CONFIG FILE SCANNED + // (SET BY USER) or (2) IT IS SET WHEN DRIVE IS AUTOMATICALLY + // RECOGNIZED IN DATABASE (WHEN DRIVE IS REGISTERED) + unsigned char *attributedefs; // -v options, see end of extern.h for def + + // ATA ONLY - SAVE SMART DATA. NULL POINTERS UNLESS NEEDED. IF + // NEEDED, ALLOCATED WHEN DEVICE REGISTERED. + struct ata_smart_values *smartval; // Pointer to SMART data + struct ata_smart_thresholds_pvt *smartthres; // Pointer to SMART thresholds + +} cfgfile; + + +typedef struct changedattribute_s { + unsigned char newval; + unsigned char oldval; + unsigned char id; + unsigned char prefail; + unsigned char sameraw; +} changedattribute_t; + +// Declare our own printing functions. Doing this provides error +// messages if the argument number/types don't match the format. +#ifndef __GNUC__ +#define __attribute__(x) /* nothing */ +#endif +void PrintOut(int priority,char *fmt, ...) __attribute__ ((format(printf, 2, 3))); + +void PrintAndMail(cfgfile *cfg, int which, int priority, char *fmt, ...) __attribute__ ((format(printf, 4, 5))); + +/* Debugging notes: to check for memory allocation/deallocation problems, use: + +export LD_PRELOAD=libnjamd.so; +export NJAMD_PROT=strict; +export NJAMD_CHK_FREE=error; +export NJAMD_DUMP_LEAKS_ON_EXIT=num; +export NJAMD_DUMP_LEAKS_ON_EXIT=3; +export NJAMD_TRACE_LIBS=1 + +*/ + +// Number of seconds to allow for registering a SCSI device. If this +// time expires without sucess or failure, then treat it as failure. +// Set to 0 to eliminate this timeout feature from the code +// (equivalent to an infinite timeout interval). +#define SCSITIMEOUT 0 + +// This is for solaris, where signal() resets the handler to SIG_DFL +// after the first signal is caught. +#ifdef HAVE_SIGSET +#define SIGNALFN sigset +#else +#define SIGNALFN signal +#endif + +#endif + +#define SELFTEST_ERRORCOUNT(x) (x & 0xff) +#define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff) + +// cfg->pending is a 16 bit unsigned quantity. If the least +// significant 8 bits are zero, this means monitor Attribute +// CUR_UNC_DEFAULT's raw value. If they are CUR_UNC_DEFAULT, this +// means DON'T MONITOR. If the most significant 8 bits are zero, this +// means monitor Attribute OFF_UNC_DEFAULT's raw value. If they are +// OFF_UNC_DEFAULT, this means DON'T MONITOR. +#define OFF_UNC_DEFAULT 198 +#define CUR_UNC_DEFAULT 197 + +#define CURR_PEND(x) (x & 0xff) +#define OFF_PEND(x) ((x >> 8) & 0xff) + +// if cfg->pending has this value, dont' monitor +#define DONT_MONITOR_UNC (256*OFF_UNC_DEFAULT+CUR_UNC_DEFAULT) diff --git a/smartd.initd.in b/smartd.initd.in new file mode 100755 index 0000000..c267f91 --- /dev/null +++ b/smartd.initd.in @@ -0,0 +1,478 @@ +#! /bin/sh + +# smartmontools init file for smartd +# Copyright (C) 2002-6 Bruce Allen +# $Id: smartd.initd.in,v 1.33 2006/04/12 14:54:28 ballen4705 Exp $ + +# For RedHat and cousins: +# chkconfig: 2345 40 40 +# description: Self Monitoring and Reporting Technology (SMART) Daemon +# processname: smartd + +# For SuSE and cousins +### BEGIN INIT INFO +# Provides: smartd +# Required-Start: $syslog +# X-UnitedLinux-Should-Start: $sendmail +# Required-Stop: $syslog +# X-UnitedLinux-Should-Stop: +# Default-Start: 2 3 5 +# Default-Stop: +# Short-Description: Monitors disk and tape health via S.M.A.R.T. +# Description: Start S.M.A.R.T. disk and tape monitor. +### END INIT INFO + +# 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/. + +# Uncomment the line below to pass options to smartd on startup. +# Note that distribution specific configuration files like +# /etc/{default,sysconfig}/smartmontools might override these +#smartd_opts="--interval=1800" + +SMARTD_BIN=/usr/local/sbin/smartd + +report_unsupported () { + echo "Currently the smartmontools package has no init script for" + echo "the $1 OS/distribution. If you can provide one or this" + echo "one works after removing some ifdefs, please contact" + echo "smartmontools-support@lists.sourceforge.net." + exit 1 +} + +# Red Hat or Yellow Dog or Mandrake +if [ -f /etc/redhat-release -o -f /etc/yellowdog-release -o -f /etc/mandrake-release -o -f /etc/whitebox-release -o -f /etc/trustix-release -o -f /etc/tinysofa-release ] ; then + +# Source function library + . /etc/rc.d/init.d/functions + +# Source configuration file. This should define the shell variable smartd_opts + [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools + + RETVAL=0 + + prog=smartd + + case "$1" in + start) + echo -n $"Starting $prog: " + daemon $SMARTD_BIN $smartd_opts + touch /var/lock/subsys/smartd + echo + ;; + stop) + echo -n $"Shutting down $prog: " + killproc $SMARTD_BIN + rm -f /var/lock/subsys/smartd + echo + ;; + reload) + echo -n $"Reloading $prog daemon configuration: " + killproc $SMARTD_BIN -HUP + RETVAL=$? + echo + ;; + report) + echo -n $"Checking SMART devices now: " + killproc $SMARTD_BIN -USR1 + RETVAL=$? + echo + ;; + restart) + $0 stop + $0 start + ;; + status) + status $prog + ;; + *) + echo $"Usage: $0 {start|stop|reload|report|restart|status}" + RETVAL=1 + esac + + exit $RETVAL + +# Slackware +elif [ -f /etc/slackware-version ] ; then + +# Source configuration file. This should define the shell variable smartd_opts. +# Email smartmontools-support@lists.sourceforge.net if there is a better choice +# of path for Slackware. + + [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools + + case "$1" in + start) + echo -n "Starting smartd: " + $SMARTD_BIN $smartd_opts + echo + ;; + stop) + echo -n "Shutting down smartd: " + killall $SMARTD_BIN + echo + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: smartd {start|stop|restart}" + exit 1 + esac + + exit 0 + +# SuSE +elif [ -f /etc/SuSE-release ] ; then + test -x $SMARTD_BIN || exit 5 + + # Existence of config file is optional + SMARTD_CONFIG=/etc/smartd.conf + +# source configuration file. This should set the shell variable smartd_opts + [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools + + # Shell functions sourced from /etc/rc.status: + # rc_check check and set local and overall rc status + # rc_status check and set local and overall rc status + # rc_status -v ditto but be verbose in local rc status + # rc_status -v -r ditto and clear the local rc status + # rc_failed set local and overall rc status to failed + # rc_reset clear local rc status (overall remains) + # rc_exit exit appropriate to overall rc status + . /etc/rc.status + + # First reset status of this service + rc_reset + + # Return values acc. to LSB for all commands but status: + # 0 - success + # 1 - misc error + # 2 - invalid or excess args + # 3 - unimplemented feature (e.g. reload) + # 4 - insufficient privilege + # 5 - program not installed + # 6 - program not configured + # + # Note that starting an already running service, stopping + # or restarting a not-running service as well as the restart + # with force-reload (in case signalling is not supported) are + # considered a success. + case "$1" in + start) + echo -n "Starting smartd" + ## Start daemon with startproc(8). If this fails + ## the echo return value is set appropriate. + + # startproc should return 0, even if service is + # already running to match LSB spec. + startproc $SMARTD_BIN $smartd_opts + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down smartd" + killproc -TERM $SMARTD_BIN + + # Remember status and be verbose + rc_status -v + ;; + restart | force-reload) + $0 stop + $0 start + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signaling, do nothing (!) + rc_failed 3 + rc_status -v + ;; + status) + echo -n "Checking for service smartd: " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + # NOTE: checkproc returns LSB compliant status values. + checkproc $SMARTD_BIN + rc_status -v + ;; + probe) + ## Optional: Probe for the necessity of a reload, print out the + ## argument to this init script which is required for a reload. + ## Note: probe is not (yet) part of LSB (as of 1.2) + + test $SMARTD_CONFIG -nt /var/run/smartd.pid && echo reload + ;; + *) + echo "Usage: $0 {start|stop|status|restart|force-reload|reload|probe}" + exit 1 + ;; + esac + + rc_exit + +# Debian case +elif [ -f /etc/debian_version ] ; then + PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + SMARTDPID=/var/run/smartd.pid + [ -x $SMARTD_BIN ] || exit 0 + RET=0 + +# source configuration file + [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools + + smartd_opts="--pidfile $SMARTDPID $smartd_opts" + + case "$1" in + start) + echo -n "Starting S.M.A.R.T. daemon: smartd" + if start-stop-daemon --start --quiet --pidfile $SMARTDPID \ + --exec $SMARTD_BIN -- $smartd_opts; then + echo "." + else + echo " (failed)" + RET=1 + fi + ;; + stop) + echo -n "Stopping S.M.A.R.T. daemon: smartd" + start-stop-daemon --stop --quiet --oknodo --pidfile $SMARTDPID + echo "." + ;; + restart|force-reload) + $0 stop + $0 start + ;; + *) + echo "Usage: /etc/init.d/smartmontools {start|stop|restart|force-reload}" + exit 1 + esac + exit $RET + +elif [ -f /etc/gentoo-release ] ; then + report_unsupported "Gentoo" + +elif [ -f /etc/turbolinux-release ] ; then + report_unsupported "Turbolinux" + +elif [ -f /etc/environment.corel ] ; then + report_unsupported "Corel" + +# PLEASE ADD OTHER LINUX DISTRIBUTIONS JUST BEFORE THIS LINE, USING elif + +elif uname -a | grep FreeBSD > /dev/null 2>&1 ; then +# following is replaced by port install + PREFIX=@@PREFIX@@ + +# Updated to try both the RCNG version of things from 5.x, or fallback to +# oldfashioned rc.conf + + if [ -r /etc/rc.subr ]; then +# This is RC-NG, pick up our values + . /etc/rc.subr + name="smartd" + rcvar="smartd_enable" + command="$SMARTD_BIN" + load_rc_config $name + elif [ -r /etc/defaults/rc.conf ]; then +# Not a 5.x system, try the default location for variables + . /etc/defaults/rc.conf + source_rc_confs + elif [ -r /etc/rc.conf ]; then +# Worst case, fallback to system config file + . /etc/rc.conf + fi + + if [ -r /etc/rc.subr ]; then +# Use new functionality from RC-NG + run_rc_command "$1" + else + PID_FILE=/var/run/smartd.pid + case "$1" in + start) + $SMARTD_BIN -p $PID_FILE $smartd_flags + echo -n " smartd" + ;; + stop) + kill `cat $PID_FILE` + echo -n " smartd" + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: smartd {start|stop|restart}" + exit 1 + esac + + exit 0 + fi +elif uname -a | grep SunOS > /dev/null 2>&1 ; then + +# Source configuration file. This should define the shell variable smartd_opts. +# Email smartmontools-support@lists.sourceforge.net if there is a better choice +# of path for Solaris + + [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools + + PID_FILE=/var/run/smartd.pid + + case "$1" in + start) + $SMARTD_BIN -p $PID_FILE $smartd_opts + echo -n "smartd " + ;; + stop) + [ -f $PID_FILE ] && kill `cat $PID_FILE` + echo -n "smartd " + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: smartd {start|stop|restart}" + exit 1 + esac + + exit 0 + +# Cygwin +elif uname | grep -i CYGWIN > /dev/null 2>&1 ; then + +# The following settings may be changed by the configuration file below + # Service Name (must be unique) + smartd_svcname=smartd + # Service display name + smartd_svcdisp="CYGWIN smartd" + # Service description + smartd_svcdesc="\ +Controls and monitors storage devices using the Self-Monitoring \ +Analysis and Reporting Technology System (S.M.A.R.T.) \ +built into ATA and SCSI Hard Drives. \ +http://smartmontools.sourceforge.net/" + +# Source configuration file. This should define the shell variable smartd_opts. +# Email smartmontools-support@lists.sourceforge.net if there is a better choice +# of path for Cygwin + + [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools + + PID_FILE=/var/run/smartd.pid + RETVAL=0 + + # Note: "[ -r $PID_FILE ]" is not used here. On Cygwin, this command may + # return success even if the file is present but cannot be read by current user. + # If smartd is running as service, smartd.pid is owned by local system account + # which is different from any user ever executing this script. + + case "$1" in + start) + if cygrunsrv -L 2>/dev/null | grep "^${smartd_svcname}$" >/dev/null 2>&1; then + echo -n "Starting service $smartd_svcname: " + cygrunsrv -S "$smartd_svcname" + else + echo -n "Starting smartd as daemon: " + $SMARTD_BIN -p $PID_FILE $smartd_opts + fi + RETVAL=$? + ;; + stop) + echo -n "Shutting down smartd: " + pid="`cat $PID_FILE 2>/dev/null`" && kill "$pid" + RETVAL=$? + ;; + reload) + echo -n "Reloading smartd configuration: " + pid="`cat $PID_FILE 2>/dev/null`" && kill -HUP "$pid" + RETVAL=$? + ;; + report) + echo -n "Checking SMART devices now: " + pid="`cat $PID_FILE 2>/dev/null`" && kill -USR1 "$pid" + RETVAL=$? + ;; + restart) + $0 stop + sleep 1 + $0 start + exit $? + ;; + install) + shift + [ $# -eq 0 ] || smartd_opts="$*" + dep= + if cygrunsrv -L 2>/dev/null | grep "^syslogd$" >/dev/null 2>&1; then + dep="-y syslogd" + else + echo "Warning: syslogd service not installed, smartd will write to windows event log."; + fi + echo "Installing service ${smartd_svcname}${smartd_opts+ with options '$smartd_opts'}:" + cygrunsrv -I "$smartd_svcname" -d "$smartd_svcdisp" -f "$smartd_svcdesc" $dep \ + -e CYGWIN="$CYGWIN" -p $SMARTD_BIN -a "--service -p ${PID_FILE}${smartd_opts+ }$smartd_opts" + RETVAL=$? + ;; + remove) + echo "Removing service $smartd_svcname:" + cygrunsrv -R "$smartd_svcname" + RETVAL=$? + ;; + status) + echo -n "Checking smartd status: " + if cygrunsrv -L 2>/dev/null | grep "^${smartd_svcname}$" >/dev/null 2>&1; then + if cygrunsrv -Q "$smartd_svcname" 2>/dev/null | grep "State *: Running" >/dev/null 2>&1; then + echo "running as service '$smartd_svcname'." + elif ps -e 2>/dev/null | grep " ${SMARTD_BIN}$" >/dev/null 2>&1; then + echo "installed as service '$smartd_svcname' but running as daemon." + else + echo "installed as service '$smartd_svcname' but not running." + RETVAL=1 + fi + elif ps -e 2>/dev/null | grep " ${SMARTD_BIN}$" >/dev/null 2>&1; then + echo "running as daemon." + else + echo "not running." + RETVAL=1 + fi + exit $RETVAL + ;; + *) + echo "Usage: $0 {start|stop|restart|reload|report|status}" + echo " $0 {install [options]|remove}" + exit 1 + esac + + if [ "$RETVAL" -eq 0 ]; then echo "done"; else echo "ERROR"; fi + exit $RETVAL + +# Add other OSes HERE, using elif... +else + report_unsupported "Unknown" +fi + +# One should NEVER arrive here, except for a badly written case above, +# that fails to exit. +echo "SOMETHING IS WRONG WITH THE SMARTD STARTUP SCRIPT" +echo "PLEASE CONTACT smartmontools-support@lists.sourceforge.net" +exit 1 diff --git a/smartmontools.spec b/smartmontools.spec new file mode 100644 index 0000000..35816d2 --- /dev/null +++ b/smartmontools.spec @@ -0,0 +1,1821 @@ +Release: 1 +Summary: smartmontools - for monitoring S.M.A.R.T. disks and devices +Summary(cs): smartmontools - pro monitorování S.M.A.R.T. diskù a zaøízení +Summary(de): smartmontools - zur Überwachung von S.M.A.R.T.-Platten und-Geräten +Summary(es): smartmontools - para el seguimiento de discos y dispositivos S.M.A.R.T. +Summary(fr): smartmontools - pour le suivi des disques et instruments S.M.A.R.T. +Summary(pt): smartmontools - para monitorar discos e dispositivos S.M.A.R.T. +Summary(it): smartmontools - per monitare dischi e dispositivi S.M.A.R.T. +Summary(pl): Monitorowanie i kontrola dysków u¿ywaj±æ S.M.A.R.T. +Name: smartmontools +Version: 5.36 +License: GPL +Group: Applications/System +Group(de): Applikationen/System +Group(es): Aplicaciones/Sistema +Group(fr): Applications/Système +Group(pt): Aplicativos/Sistema +Group(it): Applicazioni/Sistemi +Source0: %{name}-%{version}.tar.gz +URL: http://smartmontools.sourceforge.net/ +Prereq: /sbin/chkconfig +BuildRoot: %{_tmppath}/%{name}-%{version}-root +Obsoletes: smartctl +Obsoletes: smartd +Obsoletes: ucsc-smartsuite +Obsoletes: smartsuite +Packager: Bruce Allen + +%define redhat %(test ! -f /etc/redhat-release ; echo $?) +%define redhat %(test ! -f /etc/fedora-release ; echo $?) +%define mandrake %(test ! -f /etc/mandrake-release ; echo $?) +%define suse %(test ! -f /etc/SuSE-release ; echo $?) + +# Source code can be found at: +# http://ftp1.sourceforge.net/smartmontools/smartmontools-%{version}-%{release}.tar.gz + +# CVS ID of this file is: +# $Id: smartmontools.spec,v 1.167 2006/04/12 17:39:32 ballen4705 Exp $ + +# Copyright (C) 2002-6 Bruce Allen +# Home page: http://smartmontools.sourceforge.net/ +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2, or (at your option) any later +# version. +# +# You should have received a copy of the GNU General Public License (for +# example COPYING); if not, write to the Free Software Foundation, Inc., 675 +# Mass Ave, Cambridge, MA 02139, USA. +# +# 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/ + + +%description +smartmontools controls and monitors storage devices using the +Self-Monitoring, Analysis and Reporting Technology System (S.M.A.R.T.) +built into ATA and SCSI Hard Drives. This is used to check the +reliability of the hard drive and to predict drive failures. The suite +is derived from the smartsuite package, and contains two utilities. The +first, smartctl, is a command line utility designed to perform simple +S.M.A.R.T. tasks. The second, smartd, is a daemon that periodically +monitors smart status and reports errors to syslog. The package is +compatible with the ATA/ATAPI-5 specification. Future releases will be +compatible with the ATA/ATAPI-6 andATA/ATAPI-7 specifications. The +package is intended to incorporate as much "vendor specific" and +"reserved" information as possible about disk drives. man smartctl and +man smartd will provide more information. This RPM file is compatible +with all RedHat releases back to at least 6.2 and should work OK on any +modern linux distribution. The most recent versions of this package and +additional information can be found at the URL: +http://smartmontools.sourceforge.net/ + +%description -l cs +smartmontools øídí a monitorují zaøízení pro ukládání dat za pou¾ití +technologie automatického monitorování, analýzy a hlá¹ení +(Self-Monitoring, Analysis and Reporting Technology System - +S.M.A.R.T.) vestavìného do pevných diskù ATA a SCSI. Pou¾ívá se ke +kontrole pou¾itelnosti pevného disku a pøedvídání havárií diskù. +Nástroje jsou odvozeny od balíèku smartsuite a obsahují dva programy. +První, smartctl, je nástroj pro provádìní jednoduchých S.M.A.R.T. úloh +na pøíkazové øádce. Druhý, smartd, je démon, který periodicky +monitoruje stav a hlásí chyby do systémového protokolu. Balíèek je +kompatibilní se specifikací ATA/ATAPI-5. Dal¹í verze budou +kompatibilní se specifikacemi ATA/ATAPI-6 a ATA/ATAPI-7. Balíèek je +navr¾en tak, aby pokryl co nejvíce polo¾ek s informacemi "závislé na +výrobci" a "rezervováno". Více informací získáte pomocí man smartctl a +man smartd. Tento RPM balíèek je kompatibilní se v¹emi verzemi RedHatu +a mìl by fungovat na v¹ech moderních distribucích Linuxu. Aktuální +verzi najdete na URL http://smartmontools.sourceforge.net/ + +%description -l de +Die smartmontools steuern und überwachen Speichergeräte mittels des +S.M.A.R.T.-Systems (Self-Monitoring, Analysis and Reporting Technology, +Technologie zur Selbst-Überwachung, Analyse und Berichterstellung), das +in ATA- und SCSI-Festplatten eingesetzt wird. Sie werden benutzt, um +die Zuverlässigkeit der Festplatte zu prüfen und Plattenfehler +vorherzusagen. Die Suite wurde vom smartsuite-Paket abgeleitet und +enthält zwei Dienstprogramme. Das erste, smartctl, ist ein +Kommandozeilentool, das einfache S.M.A.R.T. Aufgaben ausführt. Das +zweite, smartd, ist ein Daemon, der periodisch den S.M.A.R.T.-Status +überwacht und Fehler ins Syslog protokolliert. Das Paket ist zur +ATA/ATAPI-5 Spezifikation kompatibel. Zukünftige Versionen werden auch +die ATA/ATAPI-6 und ATA/ATAPI-7 Spezifikationen umsetzen. Das Paket +versucht, so viele "herstellerspezifische" und "reservierte" Information +über Plattenlaufwerke wie möglich bereitzustellen. man smartctl und man +smartd liefern mehr Informationen über den Einsatz. Dieses RPM ist zu +allen RedHat-Versionen ab spätestens 6.2 kompatibel und sollte unter +jedem modernen Linux arbeiten. Die aktuellsten Versionen dieses Pakets +und zusätzliche Informationen sind zu finden unter der URL: +http://smartmontools.sourceforge.net/ + +%description -l es +smartmontools controla y hace el seguimiento de dispositivos de +almacenamiento usando el Self-Monitoring, Analysis and Reporting +Technology System (S.M.A.R.T.) incorporado en discos duros ATA y SCSI. +Es usado para asegurar la fiabilidad de discos duros y predecir averias. +El conjunto de programas proviene del conjunto smartsuite y contiene dos +utilidades. La primera, smartctl, es una utilidad command-line hecha +para hacer operaciones S.M.A.R.T. sencillas. La segunda, smartd, es un +programa que periodicamente chequea el estatus smart e informa de +errores a syslog. Estos programas son compatibles con el sistema +ATA/ATAPI-5. Futuras versiones seran compatibles con los sistemas +ATA/ATAPI-6 y ATA/ATAPI-7. Este conjunto de programas tiene el +proposito de incorporar la mayor cantidad posible de informacion +reservada y especifica de discos duros. Los comandos 'man smartctl' y +'man smartd' contienen mas informacion. Este fichero RPM es compatible +con todas las versiones de RedHat a partir de la 6.2 y posiblemente +funcionaran sin problemas en cualquier distribucion moderna de linux. +La version mas reciente de estos programas ademas de informacion +adicional pueden encontrarse en: http://smartmontools.sourceforge.net/ + +%description -l fr +smartmontools contrôle et fait le suivi de périphériques de stockage +utilisant le système Self-Monitoring, Analysis and Reporting +Technology (S.M.A.R.T) intégré dans les disques durs ATA et SCSI. Ce +système est utilisé pour vérifier la fiabilité du disque dur et prédire +les défaillances du lecteur. La suite logicielle dérive du paquet +smartsuite et contient deux utilitaires. Le premier, smartctl, +fonctionne en ligne de commande et permet de réaliser des tâches +S.M.A.R.T. simples. Le second, smartd, est un démon qui fait +périodiquement le suivi du statut smart et transmet les erreurs au +syslog. Ce paquet est compatible avec la spécification ATA/ATAPI-5. +Les prochaines versions seront compatibles avec les spécifications +ATA/ATAPI-6 et ATA/ATAPI-7. Ce paquet tente d'incorporer le plus +d'informations possible sur les disques durs qu'elles soient spécifiques +au constructeur ("vendor specific") ou réservées ("reserved"). man +smartctl et man smartd donnent plus de renseignements. Ce fichier RPM +est compatible avec toutes les versions de RedHat v6.2 et ultérieures, +et devrait fonctionner sur toutes les distributions récentes de Linux. +Les dernières versions de ce paquet et des informations supplémentaires +peuvent être trouvées à l'adresse URL: +http://smartmontools.sourceforge.net/ + +%description -l pt +smartmontools controla e monitora dispositivos de armazenamento +utilizando o recurso Self-Monitoring, Analysis and Reporting Technology +System (S.M.A.R.T.) integrado nos discos rígidos ATA e SCSI, cuja +finalidade é verificar a confiabilidade do disco rígido e prever falhas +da unidade. A suite é derivada do pacote smartsuite, e contém dois +utilitários. O primeiro, smartctl, é um utilitário de linha de comando +projetado para executar tarefas simples de S.M.A.R.T. O segundo, +smartd, é um daemon que monitora periodicamente estados do smart e +reporta erros para o syslog. O pacote é compatível com a especificação +ATA/ATAPI-5. Futuras versões serão compatíveis com as especificações +ATA/ATAPI-6 e ATA/ATAPI-7. O pacote pretende incorporar o maior número +possível de informações "específicas do fabricante" e "reservadas" sobre +unidades de disco. man smartctl e man smartd contém mais informações. +Este arquivo RPM é compatível com todas as versões do RedHat a partir da +6.2 e deverá funcionar perfeitamente em qualquer distribuição moderna do +Linux. As mais recentes versões deste pacote e informações adicionais +podem ser encontradas em http://smartmontools.sourceforge.net/ + +%description -l it +smartmontools controlla e monitora dischi che usano il "Self-Monitoring, +Analysis and Reporting Technology System" (S.M.A.R.T.), in hard drive +ATA e SCSI. Esso è usato per controllare l'affidabilità dei drive e +predire i guasti. La suite è derivata dal package smartsuite e contiene +due utility. La prima, smartctl, è una utility a linea di comando +progettata per eseguire semplici task S.M.A.R.T.. La seconda, smartd, è +un daemon che periodicamente monitora lo stato di smart e riporta errori +al syslog. Il package è compatibile con le specifiche ATA/ATAPI-6 e +ATA/ATAPI-7. Il package vuole incorporare tutte le possibili +informazioni riservate e "vendor specific" sui dischi. man smartctl e +man smartd danno più informazioni. Questo file RPM è compatibile con +tutte le release di RedHat, almeno dalla 6.2 e dovrebbe funzionare bene +su ogni moderna distribuzione di linux. Le versioni più recenti di +questo package e informazioni addizionali possono essere trovate al sito +http://smartmontools.sourceforge.net/ + +%description -l pl +Pakiet zawiera dwa programy (smartctl oraz smartd) do kontroli i +monitorowania systemów przechowywania danych za pomoc± S.M.A.R.T - +systemu wbudowanego w wiêkszo¶æ nowych dysków ATA oraz SCSI. Pakiet +pochodzi od oprogramowania smartsuite i wspiera dyski ATA/ATAPI-5. + +# The following sections are executed by the SRPM file +%prep + +%setup -q + +%build + %configure + make + +%install + rm -rf $RPM_BUILD_ROOT + rm -rf %{_buildroot} + %makeinstall + rm -f examplescripts/Makefile* + %if %{suse} + mkdir -p $RPM_BUILD_ROOT%{_defaultdocdir} + mv $RPM_BUILD_ROOT/usr/share/doc/%{name}-%{version} $RPM_BUILD_ROOT%{_defaultdocdir}/%{name} + ln -s ../../etc/rc.d/init.d/smartd $RPM_BUILD_ROOT%{_sbindir}/rcsmartd + %endif + +%files + %defattr(-,root,root) + %attr(755,root,root) %{_sbindir}/smartd + %attr(755,root,root) %{_sbindir}/smartctl + %if %{suse} + %attr(755,root,root) %{_sbindir}/rcsmartd + %endif + %attr(755,root,root) /etc/rc.d/init.d/smartd + %attr(644,root,root) %{_mandir}/man8/smartctl.8* + %attr(644,root,root) %{_mandir}/man8/smartd.8* + %attr(644,root,root) %{_mandir}/man5/smartd.conf.5* + %doc AUTHORS CHANGELOG COPYING INSTALL NEWS README TODO WARNINGS smartd.conf examplescripts + %config(noreplace) %{_sysconfdir}/smartd.conf + +%clean + rm -rf $RPM_BUILD_ROOT + rm -rf %{_buildroot} + rm -rf %{_builddir}/%{name}-%{version} + +# The following are executed only by the binary RPM at install/uninstall + +# since this installs the gzipped documentation files, remove +# non-gzipped ones of the same name. + +# run before installation. Passed "1" the first time package installed, else a larger number +%pre +if [ -f /usr/share/man/man8/smartctl.8 ] ; then + echo "You MUST delete (by hand) the outdated file /usr/share/man/man8/smartctl.8 to read the new manual page for smartctl." +fi +if [ -f /usr/share/man/man8/smartd.8 ] ; then + echo "You MUST delete (by hand) the outdated file /usr/share/man/man8/smartd.8 to read the new manual page for smartd." +fi +if [ -f /usr/share/man/man5/smartd.conf.5 ] ; then + echo "You MUST delete (by hand) the outdated file /usr/share/man/man5/smartd.conf.5 to read the new manual page for smartd.conf" +fi + +if [ ! -f /etc/smartd.conf ]; then + echo "Note that you can use a configuration file /etc/smartd.conf to control the" + echo "startup behavior of the smartd daemon. See man 8 smartd for details." +fi + +# run after installation. Passed "1" the first time package installed, else a larger number +%post +# if smartd is already running, restart it with the new daemon +if [ -f /var/lock/subsys/smartd ]; then + /etc/rc.d/init.d/smartd restart 1>&2 + echo "Restarted smartd services" +else +# else tell the user how to start it + echo "Run \"/etc/rc.d/init.d/smartd start\" to start smartd service now." +fi + +# Now see if we should tell user to set service to start on boot +/sbin/chkconfig --list smartd > /dev/null 2> /dev/null +printmessage=$? + +if [ $printmessage -ne 0 ] ; then + echo "Run \"/sbin/chkconfig --add smartd\", to start smartd service on system boot" +else + echo "smartd will continue to start up on system boot" +fi + + +# run before uninstallation. Passed zero when the last version uninstalled, else larger +%preun + +# if uninstalling the final copy, stop and remove any links +if [ "$1" = "0" ]; then + if [ -f /var/lock/subsys/smartd ]; then + /etc/rc.d/init.d/smartd stop 1>&2 + echo "Stopping smartd services" + fi + +# see if any links remain, and kill them if they do + /sbin/chkconfig --list smartd > /dev/null 2> /dev/null + notlinked=$? + + if [ $notlinked -eq 0 ]; then + /sbin/chkconfig --del smartd + echo "Removing chkconfig links to smartd boot-time startup scripts" + fi +fi + +# run after uninstallation. Passed zero when the last version uninstalled, else larger +# %postun + +%define date %(echo `LC_ALL="C" date +"%a %b %d %Y"`) + +# Maintainers / Developers Key: +# [BA] Bruce Allen +# [EB] Erik Inge Bolsø +# [SB] Stanislav Brabec +# [PC] Peter Cassidy +# [YD] Yuri Dario +# [CD] Capser Dik +# [CF] Christian Franke +# [GF] Guilhem Frézou +# [DG] Douglas Gilbert +# [GG] Guido Guenther +# [GK] Geoff Keating +# [DK] David Kirkby +# [KM] Kai Mäkisarai +# [EM] Eduard Martinescu +# [FM] Frédéric L. W. Meunier +# [KS] Keiji Sawada +# [SS] Sergey Svishchev +# [PW] Phil Williams +# [LW] Leon Woestenberg +# [RZ] Richard Zybert + + +%changelog +* Wed Apr 12 2006 Bruce Allen + [BA] Update copyright dates to 2006. + [DG] [SCSI] Loosen sanity check on Seagate/Hitachi factory information + log page so it is not skipped on recent Seagate SCSI disks. + [CF] Added command 'smartd -q showtests' to list test schedules. + [CF] Added command 'smartctl -P showall MODEL [FIRMWARE]' to list + database entries for specific drives and firmware. + [PW] Automatically set -v 9,minutes and -v 194,unknown for Maxtor + DiamondMax D540X-4G drives. + [DG] [SCSI] suppress various outputs when data fails sanity checks. + Correct 'last n error events' log page indexing. + [DG] [SCSI] changed smartctl exit status to reflect any problems in + the most recent 20 self test logs [Leandro Santi] + [DG] [SCSI] Fix process return value when scsiGetSmartData() fails + in smartctl [Leandro Santi] + [BA] Updated docs and error message to reflect Linux libata + support for smartmontools starting with the 2.6.15 kernel + series. Also init script support for the 'tinysofa' release. + [DG] [SCSI] Mask dpofua bit when changing mode pages. Fix failure + of 'smartctl -l error'. + [EM] Fixed a problem with FreeBSD and 3Ware 'twe' devices + [CF] Fixed a regexp in knowndrives table, added regexp syntax check + via 'smartctl -P showall'. + [CF] Cygwin & Windows: Fixed memory leak in function calling + IOCTL_IDE_PASS_THROUGH. Thanks to Fred Schmidt for the problem + report. + [CF] Cygwin: added cygrunsrv support and commands "install", "remove" + and "status" to smartd.initd. + [SS] Fix runtime problems on big-engian NetBSD platforms (patch provided + by Martin Husemann) + [CF] Cygwin smartd: Open smartd.conf in textmode to allow use of + Windows editors. + [CF] Cygwin smartd: Added option '--service' to allow smartd running + as windows service via cygrunsrv. Useful in conjunction with new + syslogd support added in Cygwin 1.5.15. + [CF] Windows: Added patch to avoid output of non-ascii timezone names. + [EM] Incorporate various patches to provide TWE support and support for + multiple 3Ware cards, Power Check Support, and FreeBSD 6.x support. + Thanks to Rudolf Cejka, Frank Behrens, and Jung-uk Kim. + [DG] Silence gcc 4.0.1 compile warning concerning the difference in + "signedness" in pointer assignments. Changes to SCSI code + and os_linux.c . + [PW] Additions to knowndrives table: added missing drive from Quantum + Fireball Plus LM series, added QUANTUM BIGFOOT TS10.0A, added + ExcelStor J680 and J880, added Western Digital Caviar RE Serial ATA + series, added missing drives from Western Digital Caviar SE series, + added Seagate Momentus 4200.2 series, added missing drives from + Maxtor DiamondMax 10 series, added Fujitsu MHG and MHH series, and + added Hitachi Travelstar 5K100 series. + [PW] Additions to knowndrives table: added Fujitsu MHU2100AT, added + Fujitsu M1623TAU, added missing drives from Seagate Barracuda + 7200.8 series, added Seagate Momentus 5400.2 series, and added + QUANTUM FIREBALL CR8.4A. + [PW] Additions to knowndrives table: added missing drive from Maxtor + MaxLine II series, added Maxtor DiamondMax 2880 Ultra ATA series, + added Maxtor DiamondMax 17 VL series, added Hitachi Deskstar 7K80 + series, and added Hitachi Deskstar 7K400 series. + [CF] Windows: Fixed unsupported 'smartctl -X' on Win2000/XP by using + IOCTL_IDE_PASS_THROUGH instead. + +* Tue Apr 20 2005 Bruce Allen + [CF] Cygwin & Windows smartd: Increased SCSI DEVICESCAN range + from ASPI adapter 0-3 to 0-9. Added diagnostic messages. + [CF] Windows smartd: Added ability to run .bat files via '-M exec' + directive. + [CF] Cygwin smartd: Added FreeConsole() after fork() to avoid hang + of terminated shell console window. + [DG] [SCSI] Add code so 'smartctl -A' outputs the number of elements + in the grown defect list. When this number is increasing a + disk has problems. N.B. Similar logic should be added to smartd. + [CF] Windows smartd: Fixed event handling to allow start of another + smartd process when service is already running. Useful for testing + service configuration changes in debug mode. + [PW] Added following drives to knowndrives table: Western Digital Raptor + family, Seagate Barracuda 7200.8 family, Maxtor DiamondMax 2160 + Ultra ATA and DiamondMax 10 families, Hitachi Travelstar E7K60 + family, Seagate Medalist 17240, 13030, 10231, 8420, and 4310, + TOSHIBA MK4018GAP and MK6022GAX, ExcelStor Technology J360, and + Western Digital Caviar AC14300. + [PW] Added missing Fujitsu MHTxxxxAT and Seagate Barracuda 7200.7 drives + to knowndrives table. + [PW] Added QUANTUM FIREBALLP LM10.2 to knowndrives table. Thanks to + Mike Fleetwood for submitting the patch. + [KS] Solaris/SPARC: fixed not to disable automatic offline test and + automatic save attributes incorrectly. Thanks to Roy Badami. + [BA] Linux: smartd init script now recognizes 'trustix' distro. + [DG] [SCSI] Medium and hardware errors were slipping through + unreported. Fix linux SCSI sense reporting via SG_IO ioctl. + [DG] [SCSI] Change lba of first failure in selftest output to + decimal (was hex) to conform with ATA output. + [GK] smartd: Detect most self-test failures even if the hour counter + has wrapped. + [BA] smartctl: list 'marvell' as option if user give invalid + -d argument + [CF] Windows: fixed SCSI timeout handling to allow long timeouts + for selftests. + [CF] Fixed buffer overflow issues in printone() and safe_vsnprintf() + which results in crash on -V option (at least on Windows). + [DG] [SCSI] Add explicit timeouts to INQUIRY and REQUEST SENSE (that + were missed in an earlier patch). Could have impacted freebsd. + [DG] When linux detects a sata_via_libata disk suggest that user try + '-d ata' (rather then '-d libata). Anticipate kernel change. + [YD] Added OS/2 and eComStation platform support. + [PW] Added Seagate U4 family, Fujitsu MHJ and MHK families, Seagate + Barracuda 5400.1, QUANTUM FIREBALLP KX27.3, QUANTUM FIREBALLP KA10.1, + and ExcelStor J340 to knowndrives table. + [DG] [SCSI] After report of Hitachi IC35L073UCDY10 disks locking up + on log page 0x7 (last n error events), check log page (and some + others) is supported (via log page 0x0) before probing. + [CF] Added safe_v?snprintf() for platforms using v?snprintf() + with non standard behaviour on overflow (Windows, old Linux) + [CF] smartd: Added message if check power mode spins up disk. + [CF] Windows: Added support for READ_LOG on WinNT4 using undocumented + pseudo SCSI command via IOCTL_SCSI_PASS_THROUGH. + [CF] smartd: Added ',q' option for '-n' directive to suppress 'skipping + checks' log message. This prevents a laptop disk from spinning up + due to this message. Thanks to Rob MacLachlan and Manfred Schwarb + for pointing out problem & solution. + [CF] Windows: Added function get_os_version_str() to show OS flavor in + copyright message. + [CF] Windows: Added function ata_identify_is_cached() to check for outdated + SMART enabled bit in identify data. + [CF] Windows: Added fix to prevent linkage of smartd specific win32 modules + to smartctl. + [PW] Added Fujitsu MPG3153AH, Hitachi Endurastar J4K20/N4K20 (formerly + DK23FA-20J), Seagate Momentus family, and Maxtor Fireball 3 family + to knowndrives table. + [PW] Added missing Maxtor DiamondMax 16, Seagate Barracuda ATA IV, and + Western Digital Caviar WDxxxAA/WDxxxBA drives to knowndrives table. + [CF] Windows: Added ATA check power mode for smartd -n directive. + [CF] Windows: Fixed use of new service status flag which causes hang + of smartd service on WinNT4. + [CF] Windows: Fixed error checking of IOCTL_IDE_PASS_THROUGH (used + for READ_LOG on 2000/XP). Added some diagnostic messages on + -r ataioctl[,2]. Thanks to Manfred Schwarb for bug report and testing. + [BA] Fixed code bug that made it impossible to enable SMART on + disks with failing health status. This would happen if the + os_*.c author made STATUS and STATUS_CHECK work the same way. + I have corrected this at a higher level; we now handle the + case where STATUS and STATUS_CHECK are identical without + issues. + [LW] Make os_linux.c/marvell_command_interface() always return 0 on STATUS. + Needed for a disk having bad SMART status. + [CF] smartctl: Added drive family printing. + [CF] autogen.sh: Allow automake 1.9, added message if automake + version is unknown. + [BA] smartctl: use locale-specific separators for printing disk + capacity. Also use AC_CHECK_HEADERS not AC_CHECK_HEADER in + configure.in. + [BA] clean-up of #include structure so that -V options to smartd + and smartctl work correctly. Please, don't #include header + files into other header files. + +* Fri Sep 10 2004 Bruce Allen + [BA] smartctl: ATA disks, if SMART ATTRIBUTE THRESHOLDS page has ID + errors with some Attributes having NULL IDs, print Attribute + info anyway (but issuing a warning to the user). + [DG] [SCSI] Decode Last n error events log page; decode track following + and positioning errors [Hitachi] + [EM] FreeBSD: another tweak, __packed__ introduced in Version 5.0040 + [EM] Cleaner tweak of fixes for FreeBSD 4.x. + [EM] Fix compilation errors under FreeBSD 4.x, as it is still using + and old GCC + [EM] Remove 3ware/FreeBSD specific files and just include pieces we need + [DG] Add logic in smartd to detect 3ware, Marvell controllers and SATA + disks behind an ATA-SCSI simulator (in Linux). If specific device + types are not given and they are picked in a general SCSI device + scan then warn and skip. + [GG] insert correct path to smartd into smartd's init script + [BA] Changed all default paths in documentation to reflect /usr/local as + default path prefix. This affects on-line man pages, primarily. + [DS] Added support for OpenBSD. + [BA] Added another environment variable SMART_FULLMESSAGE set by + the smartd mailing feature, and modified examplescripts/Example1 + to illustrate it. + [BA] Fixed potentially misleading messages of the form: + XXX failed: success + [DG] emit warning if SATA disk detected using libata in Linux; then exit + [PW] Added Seagate U10 family, Hitachi Travelstar 7K60, Fujitsu MHR2020AT, + and QUANTUM FIREBALLP AS20.5 to knowndrives table. + [DG] Detect 3ware and Marvell controllers from SCSI INQUIRY vendor string + and suggest usage of appropriate '-d' argument in smartctl. + [LW] Tested the RELEASE_5_33_WITH_MARVELL_SUPPORT branch on + actual Marvell 88SX5041 hardware, with success. + Merged into HEAD. + [BA] Fixed nasty DEVICESCAN bug + [BA] Checked in RELEASE_5_33_WITH_MARVELL_SUPPORT branch with + some Marvell support. + [BA] Additional modifications of Ed's controller scheme. Fixed + broken 3ware support under linux, problems with scanning + devices in smartd, and other small problems. + [EM] Minor change to FreeBSD inclusion of 'twe' include files. Add + code to check if they exising in /usr/include/sys to use those + in preference to ones added here + [EM] Very preliminary support attempt for 3Ware controllers under + FreeBSD. Also, switched 'escalade_type/escalade_port' to + 'controler_type/controller_port' and moved away from + 'tryata/tryscsi' to using new 'controller*' variables to + determine which controller type (ATA/SCSI/3Ware) to use. + [GK] Added initscript support for Darwin. + [CF] Windows smartd: Added ability to run smartd as a windows service, + including new commands "smartd install ..." and "smartd remove" + to install and remove the service registry entry. + [BA] smartd: warn user if -s regexp regular expression contains + characters other than 0123456789.*()|+?[-]{}:=SLCO since such + characters are 'suspicous' and may indicate a poorly formed + regexp. Extended regular expression gurus: can this list be + reduced somewhat? + [CF] Fixed bug in Windows smartd: Missing close of config file when + configuration is reloaded by smartd daemon. + [CF] Windows smartd: Added mail warning feature using the "Blat" + (http://blat.sourceforge.net/) mailer as a default. + [PW] Added Maxtor DiamondMax Plus 5120 Ultra ATA 33 series and TOSHIBA + MK3017GAP to knowndrives table. + [CF] Added fixes to build smartmontools on old Linux systems + (libc < 6, Kernel 2.0.x). + [BA] Added ATA minor version identity strings for latest ATA specification + updates: ATA/ATAPI-7 T13 1532D revision 4a and ATA/ATAPI-6 published, + ANSI INCITS 361-2002 + [PW] Added Hitachi Travelstar 5K80 family and Fujitsu MHTxxxxAH family to + knowndrives table. + [EM] Fix up compilation under FreeBSD < 5.x + [PW] Added QUANTUM FIREBALL EX3.2A and missing Western Digital Caviar SE + drives to knowndrives table. + [BA] Modified Hitachi Travelstar 80GN family regexp in drive database. + Thanks to [GK/CF] for problem & solution. + [GK] Added os_darwin.[ch] + [PW] Added the following drives to the knowndrives table: IBM Travelstar + 48GH, 30GN, and 15GN family; IBM Deskstar 37GP and 34GXP family; + Western Digital WDC WD272AA; Maxtor DiamondMax D540X-4D family; + TOSHIBA MK2016GAP, MK2018GAP, MK2018GAS, MK2023GAS; and + QUANTUM FIREBALL ST3.2A + [BA] smartd/smarctl now print build HOST/OS information as part + of startup slogan. This should make it slightly easier to + read bug reports from users. + [RZ] Fixed the DEVICESCAN to do what it was supposed to do - give + error message unless scanning is in progress. + [BA] Update documentation to describe 3ware character devices. Better + error detection for missing/malfunctioning devices behind 3ware + controllers. Now pack 3ware ioctl structures explicitly. + [BA] For ATA devices that support LBA mode, print capacity as part + of smartctl --info + [RZ] Made DEVICESCAN quiet about non-existing devices unless debug + is on. + [DG] treat "unit attention" SCSI warning as try again in some contexts + (test unit ready and mode sense) + [BA] on drives that store max/min rather than min/max, get order + correct in printing temp. + [BA] fixed typo in 'smartctl -h' output. Thanks to Gabor Z. Papp. + [BA] linux: clean-up to 3ware/AMCC support; dynamically create + or fix /dev/tw[ae][0-15] device node entries if they don't + exist or are incorrect. One can now use the character devices + /dev/twe[0-15] OR /dev/sd? for 3ware 6000/7000/8000 series + cards. One must use /dev/twa[0-15] for 3ware 9000 series cards. + Note that selective self-tests now work via /dev/tw[ae] devices. + Next step: documentation. + [BA] linux: experimental "support" for 3ware/AMCC 9000 series + controllers that use the 3w-9xxx driver. This will be in a + state of flux for a few days. Note that this requires the + character interface /dev/twa[0-15]. + [DG] linux: extend general SCSI OS interface to use the SG_IO ioctl. If + not available, use the older SCSI_IOCTL_SEND_COMMAND ioctl. + [KS] Solaris/x86: fixed system identification problem in configure + script. Thanks to Stuart Swales. + +* Mon Jul 5 2004 Bruce Allen + [BA] Update link to revised/updated IBM Deskstar Firmware + [CF] Cygwin & Windows: Added missing ASPI manager initialization + with GetASPI32SupportInfo(). Thanks to Nikolai SAOUKH for pointing + this out and providing a patch. + [BA] modified smartd init script to work on whitebox (thanks to + Michael Falzon) + [BA] removed (reverted) additional Attribute definitions from + http://smart.friko.pl/attributes.php. All (or most?) of these + appear to be return code values for the WD Digital Life Guard Utility. + [PW] Added Seagate Medalist 17242, 13032, 10232, 8422, and 4312 to + knowndrives table. Added missing Seagate U Series 5 drives. + [PW] Added the following QUANTUM models to knowndrives table: + FIREBALL EX6.4A, FIREBALLP AS10.2, FIREBALLP AS40.0, FIREBALL CR4.3A, + FIREBALLP LM15, FIREBALLP LM30, and FIREBALLlct20 30 + [PW] Added missing Western Digital Protege drives to knowndrives table. + [PW] Added Maxtor DiamondMax 40 ATA 66 series and DiamondMax 40 VL Ultra + ATA 100 series to knowndrives table. + [PW] Added the following Hitachi/IBM drives to knowndrives table: + HITACHI_DK14FA-20B, Travelstar 40GNX series, Travelstar 4LP series, + and Travelstar DK23XXB series. Added the missing Travelstar 80GN + drives. + [PW] Added Fujitsu MPB series and MPG series to knowndrives table. Added + the missing Fujitsu MHSxxxxAT drives. + [KS] Solaris: added workaround for dynamic change of time-zone. + [KS] Solaris: fixed problem that autogen.sh cannot detect absence of + auto* tools. + [BA] smartd: added time-zone bug information to man page. + Reverted CF code for _WIN32 case. + [CF] Cygwin & Windows: Added better error messages on IDE/ATA device + open error. + [BA] added additional Attribute definitions from + http://smart.friko.pl/attributes.php + [BA] smartd: reworked TimeZone bug workaround so it is only invoked + for glibc. Note: this might not be right -- a similar bug may + exist in other platform's libcs. + [DG] SCSI smartmontools documentation updated [2004/5/6]. See: + http://smartmontools.sourceforge.net/smartmontools_scsi.html + [CF] Windows: Fixed reset of TZ=GMT in glibc timezone bug workaround. + +* Tue May 4 2004 Bruce Allen + [DG] move SCSI device temperature and start-stop log page output + (smartctl) into --attributes section (was in --info section). + [GG] change default installation location to /usr/local + [CF] Cygwin smartd: Fixed crash on access of SCSI devices after fork(). + [PW] Added TOSHIBA MK4018GAS and the following Maxtor drive families + to knowndrives table: DiamondMax D540X-4G, Fireball 541DX, + DiamondMax 3400 Ultra ATA, DiamondMax Plus 6800 Ultra ATA 66. + [PW] Added missing Maxtor DiamondMax 16, DiamondMax D540X-4K, and + DiamondMax Plus 45 Ulta ATA 100 drives to knowndrives table. + [PW] Added ExcelStor J240, Hitachi Travelstar 80GN family, Fujitsu + MHTxxxxAT family, and IBM Deskstar 25GP and 22GXP families to + knowndrives table. + [CF] Cygwin smartd: Added workaround for missing SIGQUIT via keyboard: + To exit smartd in debug mode, type CONTROL-C twice. + [BA] smartctl: printing of the selective self-test log is now + controlled by a new option: -l selective + [BA] Added entries for Samsung firmware versions -25 to -39 based + on latest info about firmware bug fixes. + [PW] Added Seagate U Series X family, Seagate U8 family, and Seagate + Medalist 8641 family to knowndrives table. + [CF] smartd: Added exit values 5/6 for missing/unreadable config file. + [BA] smartd: now monitor the Current Pending Sector count (Attribute 197) + and the Offline Pending Sector Count (Attribute 198). Log a + warning (and send an email, if so configured) if the raw count + is nonzero. These are controlled by new Directives: -C and -U. + Currently they are enabled by default. + [CF] Added option -c FILE, --configfile=FILE to smartd to specify + an alternate configuration FILE or '-' for standard input. + [KS] configure.in now searches for -lnsl and -lsocket for Solaris. + [CF] Win32/native smartd: Added thread to combine several syslog output + lines into one single event log entry. + [CF] Win32 smartd: Added DEVICESCAN for SCSI/ASPI devices. + [GG] Use gethostbyname() the get the DNS domain since getdomainname() + returns the NIS domain when sending mails from smartd. + [GG] smartd.init.in: pass smartd_opts to smartd on startup, read distribution + specific configuration files if found + [SS] smartctl: added NetBSD support for Selective Self-tests. + [BA] smartd.conf example configuration file now has all examples + commented out except for 'DEVICESCAN'. + [CF] Win32/native smartd: Added ability to display warning "emails" + as message box by "-m msgbox" directive. With "-m sysmsgbox", + a system modal (always on top) message box is shown. + [BA] smartctl: printing of self-test log for disks that support + Selective self-testing now shows the status of the (optional) + read-scan after the selective self test. Also, changed format + in printing self-test log to print failing LBA in base 10 not + base 16 (more compatible with kernel error messages). Also, + in printing SMART error log, print timestamps in format + days+hours+minutes+seconds. + [CF] Win32 smartd: Added ability to log to stdout/stderr + (-l local1/2). Toggling debug console still works + if stdout is redirected. + [BA] smartctl: selective self-test log, print current status + in a more detailed way. Allow writing of selective self-test + log provided that no other self-test is underway. + [BA] Linux: eliminated dependency on kernel tree hdreg.h. + [BA] smartctl: -l selftest option now prints Selective self-test + log in addition to the normal self-test log. + Added additional options (-t pending, -t afterselect) to + control remaining Selective Self-test capabilities. Tested + with several Maxtor disks. Modified error message printing + so that munged option messages print at the end not the + start of output. + [CF] Added daemon support to Win32 native version of smartd. + The daemon can be controlled by commands similar to initd + scripts: "smartd status|stop|reload|restart|sigusr1|sigusr2". + [CF] Added minor support for option "-l local[0-7]" to Win32 native + (not Cygwin) version of smartd. If specified, the log output + is written to file "./smartd[1-7]?.log" instead of event log. + [BA] Added Selective Self-test to smartctl (-t selective,M-N). + Currently only supported under Linux; Solaris, NetBSD, FreeBSD + and Windows developers must add WRITE LOG functionality to + os_*.c + [BA] Added workaround for an annoying glibc bug: if you change + timezones, (eg, flying with a laptop from USA to Europe) + localtime() does not notice this in a running + executable, so time that appears in the system log (syslog!) + will be incorrect. See + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48184 + for additional examples of this bug. + [DG] Set explicit timeouts for SCSI commands (most default to 6 seconds). + Previously a 0 second timeout was meant to be interpreted as a + default timeout but the FreeBSD port had a problem in this area. + [CF] Fixed un-thread-safe exit signal handler for Win32 + [BA] Fixed un-thread-safe exit signal handler pointed out + by CF. + [BA] Changed configure script to eliminate warnings under + Solaris from sys/int_type.h conflicts with int64.h + Added header files for umask to smartd.c. + [BA] Man page format change from Werner LEMBERG. " " changed to \& + [CF] Added os_win32/syslogevt.* event message file tool for Win32 + smartd (native+cygwin). May also be useful for other cygwin + programs writing to syslog(). + [CF] Added Win32 version of smartd + [CF] Merged RELEASE_5_26_WIN32_BRANCH + [BA] Made some changes to man page markup suggested by + Richard Verhoeven to work around bugs in man2html. + Tested not to break anything under Linux and Solaris. + [CF] Moved PrintOut() from utility.c to smart{ctl,d}.c to avoid + syslog() output of smartctl. + [BA] Grew worried that some time-zone names could be very long (eg, + Mitteleuropaische Zeit) and put date string lengths into a + single macro in utility.c + [EM] Updated os_freebsd.c to handle older versions of FreeBSD in a + more appropriate/obvious fashion. + [EM] Modified autogen.sh as FreeBSD installs automake 1.7 as + 'automake17' and NOT 'automake-1.7' + +* Sat Mar 6 2004 Bruce Allen + [PW] Added QUANTUM FIREBALLlct15 30, QUANTUM FIREBALLlct20 40, and + Maxtor 6Y060P0 (DiamondMax Plus 9 60GB) to knowndrives table. + [PW] Added Maxtor MaXLine II family to knowndrives table (thanks to + Brett Russ for submitting the patch). + [BA] Added remaining read/write commands to detailed list of + error log commands that have text descriptions of problem + printed. For commands that support it, print number of failed + sectors at problem LBA. + [BA] Made SuSE section of smartd init script more SuSE 9 compatible. + Thanks to Hans-Peter Jansen. + [CF] Windows smartd: Added IDE/ATA device scan + Added windows device names to smartctl.8.in, smartd.8.in + [BA] smartctl/smartd: user-provided '-F samsung' and '-F samsung2' + command line options/Directives did NOT over-ride preset values + unless user specified '-P ignore'. Now they will always over-ride + preset values from the database. + [BA] Added error decoding for a few more READ and WRITE commands. + [PW] Added Maxtor MaXLine Plus II, Western Digital Caviar SE (Serial ATA) + series, Hitachi Deskstar 7K250 series, and Ultra ATA 66 models of + the Maxtor DiamondMax Plus 40 series to knowndrives table. + [BA] Added Maxtor Diamondmax 250 GB drives to database. Note that + these model numbers are not listed in Maxtor documentation, but + they exist. + [BA] Removed the 'contact developers' phrase from the Samsung disk + warning messages. + [PW] Added TOSHIBA MK2017GAP, IBM Deskstar 14GXP and 16GP series, + Fujitsu MPC series, Seagate Barracuda ATA III family, and missing + Seagate Barracuda U Series drives to knowndrives table + [BA] smartd: wrong loglevel for message: Configuration file + /etc/smartd.conf parsed. Changed to LOG_INFO from LOG_CRIT. + Thanks to Emmanuel CHANTREAU for the report. + [CF] Checked in development version of windows code base. + +* Tue Feb 24 2004 Bruce Allen + [BA] smartd: configure script did not set correct directory to search for + smartd.conf based on --prefix argument to ./configure. Thanks to + GG for identifying the problem and fix. + [BA] make clean now removes man pages (generated from *.in) files as well + as object files. + [EM] Correct copying of sense data in FreeBSD SCSI implementation. Thanks + to Sergey Svishchev for noticing the bug. + [BA] On solaris, wrong warning message if no ATA support. Warning message + concerns 3ware controller, not ATA. + [SS] Added SCSI support for NetBSD. + [BA] on big-endian linux machines, fixed interpretation of HDIO_GET_IDENTITY + to correctly identify ATAPI bit (was byte swapped). This should + eliminate some SYSLOG noise if user queries a packet device (eg, CD + ROM or DVD reader). + [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives with + A5AA/A6AA firmware. Thanks to Gerald Schnabel. + [PW] Added Toshiba TOS MK3019GAXB SUN30G to knowndrives table + [PW] Added Western Digital Caviar AC12500, AC24300, AC25100, AC36400, + and AC38400 to knowndrives table + [BA] When printing ATA error log, print the LBA at which READ + or WRITE commands failed. + [BA] Changed syntax of error message in smartctl + [BA] Added versioning info (-V options to smartd/smartctl) for + Solaris ATA module. + +* Thu Feb 12 2004 Bruce Allen + [KS] Added ATA/IDE support for Solaris/SPARC (ATA/IDE not yet for + Solaris/x86). + [BA] 3ware controllers: documented that one can monitor any of the + physical disks from any of the 3ware /dev/sd? logical devices. + Better warnings if querying a disk that does not exist. + [PW] Added Hitachi Travelstar DK23DA series, Maxtor DiamondMax Plus 40 + series, Western Digital Caviar WDxxxAA, WDxxxBA, and WDxxxAB series + to knowndrives table + [BA] missing 'pragma pack' on ATA IDENIFY DEVICE structure may have + caused odd or incorrect results on 64-bit machines. + [BA] smartctl/smartd allow inspection of self-test and error logs even + if disk firmware claims that these don't exist. This is needed + for some Maxtor disks whose firmware does not indicate log support + even though the disk DOES support it. + [BA] Improved porting instructions and documentation in os_generic.c + [PW] Add Western Digital Caviar WD136AA and SAMSUNG SP40A2H (RR100-07 + firmware) to knowndrives table. + [EM] FreeBSD: remove extra definition of FreeNonZero + [BA] smartctl: the -q silent option was printing output for some + error conditions. Fixed. Will rename relevant variables to help + avoid these errors in the future. + [SS] NetBSD port added. + [BA] more sensible error messages for devfs and devfs-like systems. + Instead of saying that the DIRECTORY does not exist, say that + the DEVICE does not exist. + [BA] smartd: added -n Directive, to prevent disk spin-up depending + upon the power mode (SLEEP, STANDBY, or IDLE). + [PW] Added Maxtor DiamondMax 20 VL series, Fujitsu MPF series, + Maxtor DiamondMax 36 series, Maxtor DiamondMax 4320 series, and + Maxtor DiamondMax 536DX series to knowndrives table. + [BA] many warning messages now give the file name AND VERSION + [BA] smartd: when the user provides multiple address recipients + to the '-m' Directive in a comma-delineated list, the commas + are stripped out before passing the list of addresses to the + mailer program. (Thanks to Calin A. Culianu for pointing this out + and providing a patch.) + [BA] smartd: when the '-M exec path' Directive is used, any stdout OR + stderr output from the executable "path" is assumed to indicate a + problem, and is echoed to SYSLOG. + [BA] Added all missing IBM/Hitachi Deskstar 180GXP models to knowndrives + table. + [PW] Added some missing IBM/Hitachi Deskstar 120GXP models to knowndrives + table. + [PW] Added IBM Travelstar 14GS to knowndrives table. + [PW] Modified knowndrives table to match entire Hitachi Travelstar + DK23BA and DK23EA series of drives (thanks to Norikatsu Shigemura + for submitting the patch). + [PW] Added some missing Fujitsu MPE series drives to knowndrives table. + [PW] Added TOSHIBA MK4019GAX, TOSHIBA MK6409MAV, and QUANTUM + FIREBALLlct15 20 to knowndrives table. + [EM] Fixup example command output for FreeBSD + [PW] Added Maxtor DiamondMax 80 family to knowndrives table. + [EM] Catch up FreeBSD code to switch PROJECTHOME to PACKAGE_HOMEPAGE + macros. + [BA] smartd: now watches stdout/stderr when trying to run mail, mailx + or mail warning script, and reports any output to SYSLOG. This + gives a clearer error message if something is wrong. + [BA] smartd: Solaris init script modified to accomodate grep that + lacks '-q' quiet option. Also check for running process to kill + on stop. + [PW] Added some missing Seagate Barracuda 7200.7 and 7200.7 Plus drives + to knowndrives table. + [PW] Added Maxtor DiamondMax Plus 60 family and Seagate U Series 5 20413 + to knowndrives table. + [BA] smartd: under Solaris, made default mailer be 'mailx' not + 'mail', since Solaris 'mail' does not accept a '-s' argument. + A workaround for Solaris users of earlier versions is to + have '-M exec /bin/mailx' in their smartd.conf config file. + [DG] some SCSI controllers don't like odd length transfers so make + sure LOG SENSE transfers are rounded up to an even number when + and odd length is reported (i.e. there is a double fetch, the + first to find the length, the second gets the data) + [BA] smartd man pages: under Solaris, correct section numbers in the + 'See also' section. + [KS/BA] smartd man page: describe how to set Solaris syslog.conf + file to catch all messages. Give correct Solaris SYSLOG default + path /var/adm/messages in man pages. + [BA] smartd: incorporated Debian startup script submitted by user. + [BA] smartctl: modified printing of self-test log entry number. Seagate + firmware can leave 'holes' in the self-test log while a test is + actually running. We now print entry numbers consistently in this + case, not assuming that entries are contiguous. + [PW] Added QUANTUM FIREBALL CX10.2A and Western Digital Caviar AC23200L + to knowndrives table. + [PW] Added QUANTUM FIREBALLlct20 20 to knowndrives table. + [PW] Added Maxtor DiamondMax Plus D740X family to knowndrives table. + [PW] Added IBM Travelstar 32GH, 30GT, and 20GN family to knowndrives + table. + [BA] Slackware init script modified to search for /etc/slackware-version + rather than /etc/slackware-release. + [PW] Added Seagate Barracuda ATA II family and TOSHIBA MK4019GAXB to + knowndrives table. + [GG] explain howto use autoreconf in autogen.sh + [KS] Makefile.am/configure.in: changed manual page sections for + Solaris. + [BA] smartd: reduced number of scheduled self-test messages if + test already run in current hour. + [PW] Added Maxtor DiamondMax Plus 8 family to knowndrives table. + [BA] linux: check for linux/hdreg.h. If it's there, use it. If + not, provide the necessary definitions ourselves. + [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives + with TXAOA5AA firmware + [PW] Added IBM Travelstar 25GS, 18GT, and 12GN family to knowndrives + table. + [PW] Added IBM/Hitachi Travelstar 60GH & 40GN family to knowndrives + table. + [BA] smartd: made '-s' Directive more efficient. Now store + compiled regex, and re-use. If device lacks certain self-test + capabilities, track it and don't try again. + [BA] smartd: made memory allocation for device lists completely + dynamic (eliminating compile-time maximum length constants). + [PW] Removed warning for SAMSUNG SP0802N with TK100-23 firmware + [PW] Added Seagate Barracuda ATA IV family to knowndrives table. + [BA] smartd: reduce per-device memory footprint by making + mail-warning info dynamically allocated. Also remove + potential memory leak if use has -m Directive twice and + keeps reloading the config file (highly unlikely this would + ever be noticed!) + [DG] smartd: added SCSI scheduled self-tests (Background + short or extended). + [BA] smartd: can now run scheduled offline immediate and + self-tests. See man page and -s Directive for details. + [GG] don't include manpages in make-dist-tarball. + [BA] smartctl: on-line examples given with -h are now correct + for solaris and linux, but wrong for freebsd. Ed? + [BA] smartd: man page now explains device scanning for solaris as + well as linux and freebsd. + [BA] smartd/smartctl: man pages now report correct CVS tag release + date, and executables '-V' options reports more build info. + +* Sat Nov 29 2003 Bruce Allen + [BA] Improved user messages that appear from 'make install' + [PW] Removed warning for SAMSUNG SP1213N with firmware TL100-23 + [BA] incorporated SuSE init script from user. + [DG] if SCSI device is read only, then open it read only. + [BA] when compiled on non-supported system (NOT linux, freebsd or solaris) then + the run-time error messages now clearly say 'your system is not supported' + and give clear directions. + [BA] ./configure script now works correctly on SuSE linux boxes + [BA] minor improvements to man pages + [BA] simplified detection of packet (ATAPI, CD) devices. + [BA] init script (redhat, mandrake, yellowdog) now uses correct + strings for translation and is slightly more standard. + [DG] smartctl: output scsi Seagate vendor pages for disks (not tapes) +* Wed Nov 19 2003 Bruce Allen + [DG] smartd/smartctl: changed scsiClearControlGLTSD() to + scsiSetControlGLTSD() with an 'enabled' argument so '-S on' + and '-S off' work for SCSI devices (if changing GLTSD supported). + [BA] smartd/smartctl: wired in scsiClearControlGLTSD(). Could still + use a corresponding Set function. Left stubs for this purpose. + [DG] scsicmds: added scsiClearControlGLTSD() [still to be wired in] + [BA] smartctl: make SCSI -T options behave the same way as the + ATA ones. + [DG] smartctl: output scsi transport protocol if available + [DG] scsi: stop device scan in smartd and smartctl if badly formed + mode response [heuristic to filter out USB devices before we + (potentially) lock them up]. + [BA] smartd: deviceclose()->CloseDevice(). Got rid of SCSIDEVELOPMENT + macro-enabled code. Added -W to list of gcc specific options to + always enable. Made code clean for -W warnings. + [PW] Added Maxtor DiamondMax VL 30 family to knowndrives table. + [DG] scsi: add warning (when '-l error' active) if Control mode page + GLTSD bit is set (global disable of saving log counters) + [DG] scsi: remember mode sense cmd length. Output trip temperature + from IE lpage (IBM extension) when unavailable from temp lpage. + [BA] smartd: for both SCSI and ATA now warns user if either + the number of self-test errors OR timestamp of most + recent self-test error have increased. + [DG] smartctl: output Seagate scsi Cache and Factory log pages (if + available) when vendor attributes chosen + [DG] smartd: add scsiCountFailedSelfTests() function. + [DG] Do more sanity checking of scsi log page responses. + [BA] smartd: now warns user if number of self-test errors has + increased for SCSI devices. + [BA] smartd: warn user if number of ATA self-test errors increases + (as before) OR if hour time stamp of most recent self-test + error changes. + [DG] More checks for well formed mode page responses. This has the side + effect of stopping scans on bad SCSI implementations (e.g. some + USB disks) prior to sending commands (typically log sense) that + locks them up. + [PW] Added Western Digital Caviar family and Caviar SE family to + knowndrives table. + [BA] smartd: added -l daemon (which is the default value if -l + is not used). + [PW] Added Seagate Barracuda ATA V family to knowndrives table. + [BA] smartd: added additional command line argument -l FACILITY + or --logfacility FACILITY. This can be used to redirect + messages from smartd to a different file than the one used + by other system daemons. + [PW] Added Seagate Barracuda 7200.7, Western Digital Protege WD400EB, + and Western Digital Caviar AC38400 to knowndrives table. + [BA] smartd: scanning should now also work correctly for + devfs WITHOUT traditional links /dev/hd[a-t] or /dev/sd[a-z]. + [PW] Added Maxtor 4W040H3, Seagate Barracuda 7200.7 Plus, + IBM Deskstar 120GXP (40GB), Seagate U Series 20410, + Fujitsu MHM2100AT, MHL2300AT, MHM2150AT, and IBM-DARA-212000 + to knowndrives table. + [PW] Added remaining Maxtor DiamondMax Plus 9 models to knowndrives + table. + [EM] smartd: If no matches found, then return 0, rather than an error + indication, as it just means no devices of the given type exist. + Adjust FreeBSD scan code to mirror Linux version. + [BA] smartd: made device scan code simpler and more robust. If + too many devices detected, warn user but scan as many + as possible. If error in scanning, warn user but don't + die right away. + [EM] smartd: To keep as consistent as possible, migrate FreeBSD + devicescan code to also use glob(3). Also verified clean + compile on a 4.7 FreeBSD system. + [BA] smartd: Modified device scan code to use glob(3). Previously + it appeared to have trouble when scanning devices on an XFS + file system, and used non-public interface to directory + entries. Problems were also reported when /dev/ was on an + ext2/3 file system, but there was a JFS partition on the same + disk. + [BA] Clearer error messages when device scanning finds no suitable + devices. + [EM] FreeBSD: Fixup code to allow for proper compilation under + -STABLE branch. + +* Fri Oct 31 2003 Bruce Allen +- [BA] smartd: didn't close file descriptors of ATA packet devices + that are scanned. Fixed. +- [BA] Added reload/report targets to the smartmontools init script. + reload: reloads config file + report: send SIGUSR1 to check devices now + +* Mon Oct 27 2003 Bruce Allen +- [EM] Fix compile issues for FreeBSD < 5-CURRENT. +- [PW] Added Fujitsu MHM2200AT to knowndrives table. +- [BA] To help catch bugs, clear ATA error structures before all + ioctl calls. Disable code that attempted to time-out on SCSI + devices when they hung (doesn't work). +- [BA] Documented STATUS/ERROR flags added by [PW] below. +- [BA] Improved algorithm to recognize ATA packet devices. Should + no longer generate SYSLOG kernel noise when user tries either + smartd or smartctl on packet device (CD-ROM or DVD). Clearer + warning messages from smartd when scanning ATA packet device. +- [PW] Added TOSHIBA MK4025GAS to knowndrives table. +- [PW] Added a textual interpretation of the status and error registers + in the SMART error log (ATA). The interpretation is + command-dependent and currently only eight commands are supported + (those which produced errors in the error logs that I happen to + have seen). +- [BA] added memory allocation tracking to solaris code. + Fixed solaris signal handling (reset handler to default + after first call to handler) by using sigset. Added + HAVE_SIGSET to configure.in +- [CD] solaris port: added SCSI functionality to solaris + stubs. +- [BA] smartd: attempt to address bug report about smartd + hanging on USB devices when scanning: + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=107615 + Set a timeout of SCSITIMEOUT (nominally 7 seconds) before + giving up. +- [EM] smartd: DEVICESCAN will follow links in a devfs filesystem and + make sure the end point is a disc. Update documentation, added + note about FreeBSD scanning +- [BA] smartd: DEVICESCAN also looks for block devices in + /dev. Updated documentation. Now scans for up to + 20 ATA devices /dev/hda-t rather than previous 12 + /dev/hda-l. +- [EM] smartd: mirror the FreeBSD DEVICESCAN logic for Linux, + so that smartd now scans only devices found in /dev/. Also, + make utility memory functions take a line number and file so + that we report errors with the correct location. +- [GG] add a note about Debian bug #208964 to WARNINGS. +- [BA] smartctl: -T verypermissive option broken. Use + -T verpermissive until the next release, please. +- [BA] Syntax mods so that code also compiles on Solaris using + Sun Workshop compiler. Need -xmemalign 1i -xCC flags + for cc. + +* Wed Oct 15 2003 Bruce Allen + [DK] Changed configure.in so -Wall is only included if gcc + is used (this is a gcc specific flag) and -fsignedchar + is not used at all (this is a gcc specific compiler + flag). + [BA] Modifications so that code now compiles under solaris. Now + all that's needed (:-) is to fill in os_solaris.[hc]. Added + os_generic.[hc] as guide to future ports. Fixed -D option + of smartd (no file name). Modified -h opt of smartd/smartctl + to work properly with solaris getopt(). + [EM] Update MAN pages with notes that 3ware drives are NOT supported + under FreeBSD. Cleanup FreeBSD warning message handling. + [EM] FreeBSD only: Fix first user found bug....I guess I was making + the wrong assumption on how to convert ATA devnames to + channel/unit numbers. + [EM] Allow for option --enable-sample to append '.sample' to installed + smartd.conf and rc script files. Also, let rc script shell setting + be determined by configure + [EM] Minor autoconf update to include -lcam for FreeBSD + [EM] Add conditional logic to allow FreeBSD to compile pre-ATAng. + -- note, not tested + Add some documentation to INSTALL for FreeBSD. + [EM] Implement SCSI CAM support for FreeBSD. NOTE: I am not an expert + in the use of CAM. It seems to work for me, but I may be doing + something horribly wrong, so please exercise caution. + [EM] Switch over to using 'atexit' rather than 'on_exit' routine. This also + meant we needed to save the exit status elsewhere so our 'Goodbye' + routine could examine it. + [EM] Move the DEVICESCAN code to os specific files. Also moved some of the + smartd Memory functions to utility.c to make available to smartctl. + [EM] Code janitor work on os_freebsd.c. + [EM] Added os_freebsd.[hc] code. Additional code janitor + work. + [BA] Code janitor working, moving OS dependent code into + os_linux.[hc]. + [GG] conditionally compile os_{freebsd,linux}.o depending on + host architecture + [PW] Print estimated completion time for tests + [BA] Added -F samsung2 flag to correct firmware byte swap. + All samsung drives with *-23 firmware revision string. + +* Sun Oct 05 2003 Bruce Allen +- [GG] Fixed broken Makefile.am (zero length smartd.conf.5 + was being created) +- [FM] Improved Slackware init script added to /etc/smartd.initd + +* Fri Oct 03 2003 Bruce Allen +- [BA] smartctl: added '-T verypermissive' option which is + equivalent to giving '-T permissive' many times. +- [BA] Try harder to identify from IDENTIFY DEVICE structure + if SMART supported/enabled. smartd now does a more + thorough job of trying to assess this before sending + a SMART status command to find out for sure. +- [BA] smartctl: it's now possible to override the program's + guess of the device type (ATA or SCSI) with -d option. +- [BA] try hard to avoid sending IDENTIFY DEVICE to packet + devices (CDROMS). They can't do SMART, and this generates + annoying syslog messages. At the same time, identify type + of Packet device. +- [BA] smartctl: Can now use permissive option more + than once, to control how far to go before giving up. +- [BA] smartd: if user asked to monitor either error or self-test + logs (-l error or -l selftest) WITHOUT monitoring any of the + Attribute values, code will SEGV. For 5.1-18 and earlier, + a good workaround is to enable Auto offline (-o on). +- [BA] smartctl: If enable auto offline command given, update auto + offline status before printing capabilities. +- [GG] Make autotools build the default, remove autotools.diff +- [GG] Add auto{conf,make} support, not enabled by default. +- [BA] Eliminated #include from code. This + should simplify porting to solaris, FreeBSD, etc. The + only linux-specific code is now isolated to three routines, + one for SCSI, one for Escalade, one for ATA. + +* Fri Aug 22 2003 Bruce Allen +- [BA] smartd: fixed serious bug - Attributes not monitored unless + user told smartd to ignore at least one of them! + +* Tue Aug 19 2003 Bruce Allen +- [BA] Default runlevels for smartd changed from 3 and 5 to + 2, 3, 4, and 5. +- [BA] Removed as much dynamic memory allocation as possible from + configuration file parsing. Reloading config file, even in + presence of syntax errors etc. should not cause memory leaks. +- [PW] It is no longer permissible for the integer part (if any) of + arguments to --report and --device to be followed by non-digits. + For example, the "foo" in --report=ioctl,2foo was previously + ignored, but now causes an error. +- [BA] smartd: added -q/--quit command line option to specify + under what circumstances smartd should exit. The old + -c/--checkonce option is now obsoleted by this more + general-purpose option. +- [BA] smartd now responds to a HUP signal by re-reading its + configuration file /etc/smartd.conf. If there are + errors in this file, then the configuration file is + ignored and smartd continues to monitor the devices that + it was monitoring prior to receiving the HUP signal. +- [BA] Now correctly get SMART status from disks behind 3ware + controllers, thanks to Adam Radford. Need 3w-xxxx driver + version 1.02.00.037 or later. Previously the smartmontools + SMART status always returned "OK" for 3ware controllers. +- [BA] Additional work on dynamic memory allocation/deallocation. + This should have no effect on smartctl, but clears that way + for smartd to dynamically add and remove entries. It should + also now be easier to modify smartd to re-read its config + file on HUP (which is easy) without leaking memory (which is + harder). The philosophy is that memory for data structures in + smartd is now allocated only on demand, the first time it + is needed. +- [BA] smartd: finished cleanup. Now use create/rm functions for + cfgentries and dynamic memory allocation almost everywhere. + Philosophy: aggresively try and provoke SEGV to help find + bad code. +- [BA] Added SAMSUNG SV0412H to knowndrives table. +- [BA] smartd: if DEVICESCAN used then knowndrives table might not set + the -v attributes correctly -- may have been the same for all + the drives. Cleaned up some data structures and memory + allocation to try and ensure segvs if such problems are + introduced again. +- [BA] Now allow -S on and -o on for the 3ware device type. For these + commands to be passed through, the stock 3ware 3w-xxxx driver + must be patched (8 lines). I'll post a patch on the smartmontools + home page after it's been tested by a few other people and 3ware + have had a chance to look it over. + +* Wed Aug 06 2003 Bruce Allen +- [BA] smartd - can now monitor ATA drives behind 3ware controllers. +- [BA] smartd - changed some FATAL out of memory error messages from + syslog level LOG_INFO to LOG_CRIT. +- [BA] smartctl - added code to look at ATA drives behind 3ware RAID + controllers using the 3w-xxxx driver. Note that for technical + reasons related to the 3w-xxxx driver, the "Enable Autosave", + "Enable Automatic Offline" commands are not implemented. + I will add this to smartd shortly. +- [BA] smartd - modified sleep loop, so that smartd no longer comes + on the run queue every second. Instead, unless interrupted, + it sleeps until the next polling time, when it wakes up. Now + smartd also tries to wake up at exactly the right + intervals (nominally 30 min) even if the user has been sending + signals to it. +- [GG] add Fujitsu MHN2300AT to vendoropts_9_seconds. +- [EB] Fujitsu change in knowndrives ... match the whole MPD and + MPE series for vendoropts_9_seconds. +- [BA] smartd bug, might cause segv if a device can not be opened. Was + due to missing comma in char* list. Consequence is that email + failure messages might have had the wrong Subject: heading for + errorcount, FAILEDhealthcheck, FAILEDreadsmartdata, FAILEDreadsmarterrorlog, + FAILEDreadsmartsefltestlog, FAILEDopendevice were all displaced by + one. And FAILEDopendevice might have caused a segv if -m was being + used as a smartd Directive. + +* Wed Jul 23 2003 Bruce Allen +- [BA] Cleaned up smartmontools.spec so that upgrading, removing + and other such operations correctly preserve running behavior + and booting behavior of smartd. +- [BA] Improved formatting of ATA Error Log printout, and added + listing of names of commands that caused the error. Added + obsolete ATA-4 SMART feature commands to table, along with + obsolete SFF-8035i SMART feature command. +- [PW] Added atacmdnames.[hc], which turn command register & + feature register pairs into ATA command names. +- [BA] Added conveyance self-test. Some code added for selective + self-tests, but #ifdefed out. +- [BA] Modified smartd exit status and log levels. If smartd is + "cleanly" terminated, for example with SIGTERM, then its + exit messages are now logged at LOG_INFO not LOG_CRIT +- [BA] Added Attribute IDs (Fujitsu) 0xCA - 0xCE. This is decimal + 202-206. Added -v switches for interpretation of Attributes + 192, 198 and 201. +- [BA] Made smartmontools work with any endian order machine for: + - SMART selftest log + - SMART ATA error log + - SMART Attributes values + - SMART Attributes thesholds + - IDENTIFY DEVICE information + - LOG DIRECTORY + Smartmontools is now free of endian bias and works correctly + on both little- and big-endian hardware. This has been tested by + three independent PPC users on a variety of ATA and SCSI hardware. +- [DG] Check that certain SCSI command responses are well formed. If + IEC mode page response is not well formed exit smartctl. This + is to protect aacraid. smartd should ignore a aacraid device. + +* Mon Jun 16 2003 Bruce Allen +- [BA] smartctl: added column to -A output to show if Attributes are + updated only during off-line testing or also during normal + operation. + +* Thu Jun 10 2003 Bruce Allen +- [BA] smartd: attempt to enable/disable automatic offline testing even + if the disk appears not to support it. Now the same logic + as smartctl. +- [BA] Added definition of Attribute 201, soft read error rate. +- [BA] Added IBM/Hitachi IC35L120AVV207-1 (GXP-180) and corresponding + 8MB Cache GXP-120 to drive database. +- [BA] smartd: if DEVICESCAN Directive used in smartd.conf, and + -I, -R or -r Directives used in conjunction with this, got + segv errors. Fixed by correcting memory allocation calls. +- [BA] smartd: enable automatic offline testing was broken due + to cut-and-paste error that disabled it instead of + enabling it. Thanks to Maciej W. Rozycki for pointing + out the problem and solution. +- [BA] Fixed "spelling" of some Attribute names to replace spaces + in names by underscores. (Fixed field width easier for awk + style parsing.) +- [BA] Added mods submitted by Guilhem Frezou to support Attribute 193 + being load/unload cycles. Add -v 193,loadunload option, useful + for Hitachi drive DK23EA-30, and add this drive to knowndrive.c + Add meaning of attribute 250 : Read error retry rate +- [BA] Added another entry for Samsung drives to knowndrive table. +- [DG] Refine SCSI log sense command to do a double fetch in most cases + (but not for the TapeAlert log page). Fix TapeAlert and Self Test + log pgae response truncation. +- [PW] Added 'removable' argument to -d Directive for smartd. This indicates + that smartd should continue (rather than exit) if the device does not + appear to be present. +- [BA] Modified smartmontools.spec [Man pages location] and + smartd.initd [Extra space kills chkconfig!] for Redhat 6.x + compatibility (thanks to Gerald Schnabel). + +* Wed May 7 2003 Bruce Allen +- [EB] Add another Fujitsu disk to knowndrives.c +- [GG] match for scsi/ and ide/ in case of devfs to exclude false postives +- [BA] If SCSI device listed in /etc/smartd.conf fails to open or do + SMART stuff correctly, or not enough space + to list all SCSI devices, fail with error unless + -DSCSIDEVELOPMENT set during compile-time. +- [BA] Added automatic recognition of /dev/i* (example: /dev/ide/...) + as an ATA device. +- [DG] Add "Device type: [disk | tape | medium changer | ...]" line to + smartctl -i output for SCSI devices. +- [PW] Fixed bug in smartd where test email would be sent regularly (for + example, daily if the user had specified -M daily) instead of just + once on startup. +- [KM] More TapeAlert work. Added translations for media changer + alerts. TapeAlert support reported according to the log page + presence. ModeSense not attempted for non-ready tapes (all + drives do not support this after all). Get peripheral type from + Inquiry even if drive info is not printed. Add QUIETON() + QUIETOFF() to TapeAlert log check. +- [BA] Stupid bug in atacmds.c minor_str[] affected ataVersionInfo(). + Two missing commas meant that minor_str[] had two few elements, + leading to output like this: + Device Model: Maxtor 6Y120L0 + Serial Number: Y40BF74E + Firmware Version: YAR41VW0 + Device is: Not in smartctl database [for details use: -P showall] + ATA Version is: 7 + ATA Standard is: 9,minutes + ^^^^^^^^^ + Missing commas inserted. +- [BA] Fixed smartd bug. On device registration, if ATA device did + not support SMART error or self-test logs but user had asked to + monitor them, an attempt would be made to read them anyway, + possibly generating "Drive Seek" errors. We now check that + the self-test and error logs are supported before trying to + access them the first time. +- [GG/BA] Fixed bug where if SMART ATA error log not supported, + command was tried anyway. Changed some error printing to use + print handlers. +- [GG] Makefile modifications to ease packaging +- [DG] Did work for TapeAlerts (SCSI). Now can detect /dev/nst0 as a + SCSI device. Also open SCSI devices O_NONBLOCK so they don't + hang on open awaiting media. The ATA side should worry about + this also: during a DEVICESCAN a cd/dvd device without media + will hang. Added some TapeAlert code suggested by Kai Makisara. + +* Mon Apr 21 2003 Bruce Allen +- [PW] Extended the -F option/Directive to potentially fix other firmware + bugs in addition to the Samsung byte-order bug. Long option name is + now --firmwarebug and the option/Directive accepts an argument + indicating the type of firmware bug to fix. +- [BA] Fixed a bug that prevented the enable automatic off-line + test feature from enabling. It also prevented the enable Attribute + autosave from working. See CVS entry for additional details. +- [PW] Modified the -r/--report option (smartctl and smartd) to allow the + user to specify the debug level as a positive integer. +- [BA] Added --log directory option to smartctl. If the disk + supports the general-purpose logging feature set (ATA-6/7) + then this option enables the Log Directory to be printed. + This Log Directory shows which device logs are available, and + their lengths in sectors. +- [PW] Added -P/--presets option to smartctl and -P Directive to smartd. +- [GG] Introduce different exit codes indicating the type of problem + encountered for smartd. +- [DG] Add non-medium error count to '-l error' and extended self test + duration to '-l selftest'. Get scsi IEs and temperature changes + working in smartd. Step over various scsi disk problems rather + than abort smartd startup. +- [DG] Support -l error for SCSI disks (and tapes). Output error counter + log pages. +- [BA] Added -F/--fixbyteorder option to smartctl. This allows us to read + SMART data from some disks that have byte-reversed two- and four- + byte quantities in their SMART data structures. +- [BA] Fixed serious bug: the -v options in smartd.conf were all put + together and used together, not drive-by-drive. +- [PW] Added knowndrives.h and knowndrives.c. The knowndrives array + supersedes the drivewarnings array. +- [GG] add {-p,--pidfile} option to smartd to write a PID file on + startup. Update the manpage accordingly. +- [DG] Fix scsi smartd problem detecting SMART support. More cleaning + and fix (and rename) scsiTestUnitReady(). More scsi renaming. +- [BA] Fixed smartd so that if a disk that is explictily listed is not + found, then smartd will exit with nonzero status BEFORE forking. + If a disk can't be registered, this will also be detected before + forking, so that init scripts can react correctly. +- [BA] Replaced all linux-specific ioctl() calls in atacmds.c with + a generic handler smartcommandhandler(). Now the only routine + that needs to be implemented for a given OS is os_specific_handler(). + Also implemented the --report ataioctl. This provides + two levels of reporting. Using the option once gives a summary + report of device IOCTL transactions. Using the option twice give + additional info (a printout of ALL device raw 512 byte SMART + data structures). This is useful for debugging. +- [DG] more scsi cleanup. Output scsi device serial number (VPD page + 0x80) if available as part of '-i'. Implement '-t offline' as + default self test (only self test older disks support). +- [BA] Changed crit to info in loglevel of smartd complaint to syslog + if DEVICESCAN enabled and device not found. +- [BA] Added -v 194,10xCelsius option/Directive. Raw Attribute number + 194 is ten times the disk temperature in Celsius. +- [DG] scsicmds.[hc] + scsiprint.c: clean up indentation, remove tabs. + Introduce new intermediate interface based on "struct scsi_cmnd_io" + to isolate SCSI generic commands + responses from Linux details; + should help port to FreeBSD of SCSI part of smartmontools. + Make SCSI command builders more parametric. + +* Thu Mar 13 2003 Bruce Allen +- [BA] smartctl: if HDIO_DRIVE_TASK ioctl() is not implemented (no + kernel support) then try to assess drive health by examining + Attribute values/thresholds directly. +- [BA] smartd/smartctl: added -v 200,writeerrorcount option/Directive + for Fujitsu disks. +- [BA] smartd: Now send email if any of the SMART commands fails, + or if open()ing the device fails. This is often noted + as a common disk failure mode. +- [BA] smartd/smartctl: Added -v N,raw8 -v N,raw16 and -v N,raw48 + Directives/Options for printing Raw Attributes in different + Formats. +- [BA] smartd: Added -r ID and -R ID for reporting/tracking Raw + values of Attributes. +- [BA] smartd/smartctl: Changed printing of spin-up-time attribute + raw value to reflect current/average as per IBM standard. +- [BA] smartd/smartctl: Added -v 9,seconds option for disks which + use Attribute 9 for power-on lifetime in seconds. +- [BA] smartctl: Added a warning message so that users of some IBM + disks are warned to update their firmware. Note: we may want + to add a command-line flag to disable the warning messages. + I have done this in a general way, using regexp, so that we + can add warnings about any type of disk that we wish.. + +* Wed Feb 12 2003 Bruce Allen +- [BA] smartd: Created a subdirectory examplescripts/ of source + directory that contains executable scripts for the -M exec PATH + Directive of smartd. +- [BA] smartd: DEVICESCAN in /etc/smartd.conf + can now be followed by all the same Directives as a regular + device name like /dev/hda takes. This allows one to use + (for example): + DEVICESCAN -m root@example.com + in the /etc/smartd.conf file. +- [BA] smartd: Added -c (--checkonce) command-line option. This checks + all devices once, then exits. The exit status can be + used to learn if devices were detected, and if smartd is + functioning correctly. This is primarily for Distribution + scripters. +- [BA] smartd: Implemented -M exec Directive for + smartd.conf. This makes it possible to run an + arbitrary script or mailing program with the + -m option. +- [PW] smartd: Modified -M Directive so that it can be given + multiple times. Added -M exec Directive. + +* Tue Jan 21 2003 Bruce Allen +- [BA] Fixed bug in smartctl pointed out by Pierre Gentile. + -d scsi didn't work because tryata and tryscsi were + reversed -- now works on /devfs SCSI devices. +- [BA] Fixed bug in smartctl pointed out by Gregory Goddard + . Manual says that bit 6 of return + value turned on if errors found in smart error log. But + this wasn't implemented. +- [BA] Modified printing format for 9,minutes to read + Xh+Ym not X h + Y m, so that fields are fixed width. +- [BA] Added Attribute 240 "head flying hours" + +* Sun Jan 12 2003 Bruce Allen +- [BA] As requested, local time/date now printed by smartctl -i + +* Thu Jan 9 2003 Bruce Allen +- [PW] Added 'help' argument to -v for smartctl +- [PW] Added -D, --showdirectives option to smartd + +* Sat Jan 4 2003 Bruce Allen +- [DG] add '-l selftest' capability for SCSI devices (update smartctl.8) +- [BA] smartd,smartctl: added additional Attribute modification option + -v 220,temp and -v 9,temp. +- [PW] Renamed smartd option -X to -d +- [PW] Changed smartd.conf Directives -- see man page +- [BA/DG] Fixed uncommented comment in smartd.conf +- [DG] Correct 'Recommended start stop count' for SCSI devices +- [PW] Replaced smartd.conf directive -C with smartd option -i +- [PW] Changed options for smartctl -- see man page. +- [BA] Use strerror() to generate system call error messages. +- [BA] smartd: fflush() all open streams before fork(). +- [BA] smartctl, smartd simplified internal handling of checksums + for simpler porting and less code. + +* Sun Dec 8 2002 Bruce Allen +- [PW] smartd --debugmode changed to --debug +- [BA] smartd/smartctl added attribute 230 Head Amplitude from + IBM DPTA-353750. +- [PW] Added list of proposed new options for smartctl to README. +- [PW] smartd: ParseOpts() now uses getopt_long() if HAVE_GETOPT_LONG is + defined and uses getopt() otherwise. This is controlled by CPPFLAGS in + the Makefile. +- [BA] smartd: Fixed a couple of error messages done with perror() + to redirect them as needed. +- [BA] smartctl: The -O option to enable an Immediate off-line test + did not print out the correct time that the test would take to + complete. This is because the test timer is volatile and not + fixed. This has been fixed, and the smartctl.8 man page has been + updated to explain how to track the Immediate offline test as it + progresses, and to further emphasize the differences between the + off-line immediate test and the self-tests. +- [BA] smartd/smartctl: Added new attribute (200) Multi_Zone_Error_Rate +- [BA] smartctl: modified so that arguments could have either a single - + as in -ea or multiple ones as in -e -a. Improved warning message for + device not opened, and fixed error in redirection of error output of + HD identity command. +- [PW] smartd: added support for long options. All short options are still + supported; see manpage for available long options. +- [BA] smartctl. When raw Attribute value was 2^31 or larger, did + not print correctly. + +* Fri Nov 22 2002 Bruce Allen +- Allen: smartd: added smartd.conf Directives -T and -s. The -T Directive + enables/disables Automatic Offline Testing. The -s Directive + enables/disables Attribute Autosave. Documentation and + example configuration file updated to agree. +- Allen: smartd: user can make smartd check the disks at any time + (ie, interrupt sleep) by sending signal SIGUSR1 to smartd. This + can be done for example with: + kill -USR1 + where is the process ID number of smartd. +- Bolso: scsi: don't trust the data we receive from the drive too + much. It very well might have errors (like zero response length). + Seen on Megaraid logical drive, and verified in the driver source. +- Allen: smartd: added Directive -m for sending test email and + for modifying email reminder behavior. Updated manual, and sample + configuration file to illustrate & explain this. +- Allen: smartd: increased size of a continued smartd.conf line to + 1023 characters. +- Allen: Simplified Directive parsers and improved warning/error + messages. + +* Sun Nov 17 2002 Bruce Allen +- Fixed bug in smartd where testunitready logic inverted + prevented functioning on scsi devices. +- Added testunitnotready to smartctl for symmetry with smartd. +- Brabec: added Czech descriptions to .spec file +- Brabec: corrected comment in smartd.conf example +- Changed way that entries in the ATA error log are printed, + to make it clearer which is the most recent error and + which is the oldest one. +- Changed Temperature_Centigrade to Temperature_Celsius. + The term "Centigrade" ceased to exist in 1948. (c.f + http://www.bartleby.com/64/C004/016.html). + +* Wed Nov 13 2002 Bruce Allen +- smartd SCSI devices: can now send warning email message on failure +- Added a new smartd configuration file Directive: -M ADDRESS. + This sends a single warning email to ADDRESS for failures or + errors detected with the -c, -L, -l, or -f Directives. + +* Mon Nov 11 2002 Bruce Allen +- Modified perror() statements in atacmds.c so that printout for SMART + commands errors is properly suppressed or queued depending upon users + choices for error reporting modes. +- Added Italian descriptions to smartmontools.spec file. +- Started impementing send-mail-on-error for smartd; not yet enabled. + +* Sun Nov 10 2002 Bruce Allen +- Added -P (Permissive) Directive to smartd.conf file to allow SMART monitoring of + pre-ATA-3 Rev 4 disks that have SMART but do not have a SMART capability bit. + +* Thu Nov 7 2002 Bruce Allen +- Added a Man section 5 page for smartd.conf +- Changed Makefile so that the -V option does not reflect file state + before commit! +- modified .spec file so that locale information now contains + character set definition. Changed pt_BR to pt since we do not use any + aspect other than language. See man setlocale. +- smartctl: added new options -W, -U, and -P to control if and how the + smartctl exits if an error is detected in either a SMART data + structure checksum, or a SMART command returns an error. +- modified manual page to break options into slightly more logical + categories. +- reformatted 'usage' message order to agree with man page ordering + +* Mon Nov 4 2002 Bruce Allen +- smartctl: added new options -n and -N to force device to be ATA or SCSI +- smartctl: no longer dies silently if device path does not start/dev/X +- smartctl: now handles arbitrary device paths +- Added additional macros for manual and sbin paths in this SPEC file. +- Modified Makefile to install /etc/smartd.conf, but without overwriting existing config file +- Modified this specfile to do the same, and to not remove any files that it did not install + +* Thu Oct 30 2002 Bruce Allen +- Fixed typesetting error in man page smartd.8 +- Removed redundant variable (harmless) from smartd.c + +* Wed Oct 29 2002 Bruce Allen +- Added a new directive for the configuration file. If the word + DEVICESCAN appears before any non-commented material in the + configuration file, then the confi file will be ignored and the + devices wil be scanned. +- Note: it has now been confirmed that the code modifications between + 5.0.23 and 5.0.24 have eliminated the GCC 3.2 problems. Note that + there is a GCC bug howerver, see #8404 at + http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8404 +- Added new Directive for Configuration file: + -C This sets the time in between disk checks to be + seconds apart. Note that although you can give + this Directive multiple times on different lines of + the configuration file, only the final value that + is given has an effect, and applies to all the + disks. The default value of is 1800 sec, and + the minimum allowed value is ten seconds. +- Problem wasn't the print format. F.L.W. Meunier <0@pervalidus.net> + sent me a gcc 3.2 build and I ran it under a debugger. The + problem seems to be with passing the very large (2x512+4) byte + data structures as arguments. I never liked this anyway; it was + inherited from smartsuite. So I've changed all the heavyweight + functions (ATA ones, anyone) to just passing pointers, not hideous + kB size structures on the stack. Hopefully this will now build OK + under gcc 3.2 with any sensible compilation options. +- Because of reported problems with GCC 3.2 compile, I have gone + thorough the code and explicitly changed all print format + parameters to correspond EXACTLY to int unless they have to be + promoted to long longs. To quote from the glibc bible: [From + GLIBC Manual: Since the prototype doesn't specify types for + optional arguments, in a call to a variadic function the default + argument promotions are performed on the optional argument + values. This means the objects of type char or short int (whether + signed or not) are promoted to either int or unsigned int, as + required. +- smartd, smartctl now warn if they find an attribute whose ID + number does not match between Data and Threshold structures. +- Fixed nasty bug which led to wrong number of arguments for a + varargs statement, with attendent stack corruption. Sheesh! + Have added script to CVS attic to help find such nasties in the + future. + +* Tue Oct 29 2002 Bruce Allen +- Eliminated some global variables out of header files and other + minor cleanup of smartd. +- Did some revision of the man page for smartd and made the usage + messages for Directives consistent. +- smartd: prints warning message when it gets SIGHUP, saying that it is + NOT re-reading the config file. +- smartctl: updated man page to say self-test commands -O,x,X,s,S,A + appear to be supported in the code. [I can't test these, can anyone + report?] +- smartctl: smartctl would previously print the LBA of a self-test + if it completed, and the LBA was not 0 or 0xff...f However + according to the specs this is not correct. According to the + specs, if the self-test completed without error then LBA is + undefined. This version fixes that. LBA value only printed if + self-test encountered an error. +- smartd has changed significantly. This is the first CVS checkin of + code that extends the options available for smartd. The following + options can be placed into the /etc/smartd.conf file, and control the + behavior of smartd. +- Configuration file Directives (following device name): + -A Device is an ATA device + -S Device is a SCSI device + -c Monitor SMART Health Status + -l Monitor SMART Error Log for changes + -L Monitor SMART Self-Test Log for new errors + -f Monitor for failure of any 'Usage' Attributes + -p Report changes in 'Prefailure' Attributes + -u Report changes in 'Usage' Attributes + -t Equivalent to -p and -u Directives + -a Equivalent to -c -l -L -f -t Directives + -i ID Ignore Attribute ID for -f Directive + -I ID Ignore Attribute ID for -p, -u or -t Directive + # Comment: text after a hash sign is ignored + \ Line continuation character +- cleaned up functions used for printing CVS IDs. Now use string + library, as it should be. +- modified length of device name string in smartd internal structure + to accomodate max length device name strings +- removed un-implemented (-e = Email notification) option from + command line arg list. We'll put it back on when implemeneted. +- smartd now logs serious (fatal) conditions in its operation at + loglevel LOG_CRIT rather than LOG_INFO before exiting with error. +- smartd used to open a file descriptor for each SMART enabled +- device, and then keep it open the entire time smartd was running. + This meant that some commands, like IOREADBLKPART did not work, + since the fd to the device was open. smartd now opens the device + when it needs to read values, then closes it. Also, if one time + around it can't open the device, it simply prints a warning + message but does not give up. Have eliminated the .fd field from + data structures -- no longer gets used. +- smartd now opens SCSI devices as well using O_RDONLY rather than + O_RDWR. If someone can no longer monitor a SCSI device that used + to be readable, this may well be the reason why. +- smartd never checked if the number of ata or scsi devices detected + was greater than the max number it could monitor. Now it does. + +* Fri Oct 25 2002 Bruce Allen +- changes to the Makefile and spec file so that if there are ungzipped manual + pages in place these will be removed so that the new gzipped man pages are + visible. +- smartd on startup now looks in the configuration file /etc/smartd.conf for + a list of devices which to include in its monitoring list. See man page + (man smartd) for syntax. If not found, try all ata and ide devices. +- smartd: close file descriptors of SCSI device if not SMART capable + Closes ALL file descriptors after forking to daemon. +- added new temperature attribute (231, temperature) +- smartd: now open ATA disks using O_RDONLY + +* Thu Oct 24 2002 Bruce Allen +- smartd now prints the name of a failed or changed attribute into logfile, + not just ID number +- Changed name of -p (print version) option to -V +- Minor change in philosophy: if a SMART command fails or the device + appears incapable of a SMART command that the user has asked for, + complain by printing an error message, but go ahead and try + anyway. Since unimplemented SMART commands should just return an + error but not cause disk problems, this should't cause any + difficulty. +- Added two new flags: q and Q. q is quiet mode - only print: For + the -l option, errors recorded in the SMART error log; For the -L + option, errors recorded in the device self-test log; For the -c + SMART "disk failing" status or device attributes (pre-failure or + usage) which failed either now or in the past; For the -v option + device attributes (pre-failure or usage) which failed either now + or in the past. Q is Very Quiet mode: Print no ouput. The only + way to learn about what was found is to use the exit status of + smartctl. +- smartctl now returns sensible values (bitmask). See smartctl.h + for the values, and the man page for documentation. +- The SMART status check now uses the correct ATA call. If failure + is detected we search through attributes to list the failed ones. + If the SMART status check shows GOOD, we then look to see if their + are any usage attributes or prefail attributes have failed at any + time. If so we print them. +- Modified function that prints vendor attributes to say if the + attribute has currently failed or has ever failed. +- -p option now prints out license info and CVS strings for all + modules in the code, nicely formatted. +- Previous versions of this code (and Smartsuite) only generate + SMART failure errors if the value of an attribute is below the + threshold and the prefailure bit is set. However the ATA Spec + (ATA4 <=Rev 4) says that it is a SMART failure if the value of an + attribute is LESS THAN OR EQUAL to the threshold and the + prefailure bit is set. This is now fixed in both smartctl and + smartd. Note that this is a troubled subject -- the original + SFF 8035i specification defining SMART was inconsistent about + this. One section says that Attribute==Threshold is pass, + and another section says it is fail. However the ATA specs are + consistent and say Attribute==Threshold is a fail. +- smartd did not print the correct value of any failing SMART attribute. It + printed the index in the attribute table, not the attribute + ID. This is fixed. +- when starting self-tests in captive mode ioctl returns EIO because + the drive has been busied out. Detect this and don't return an eror + in this case. Check this this is correct (or how to fix it?) + - fixed possible error in how to determine ATA standard support + for devices with no ATA minor revision number. +- device opened only in read-only not read-write mode. Don't need R/W + access to get smart data. Check this with Andre. +- smartctl now handles all possible choices of "multiple options" + gracefully. It goes through the following phases of operation, + in order: INFORMATION, ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS. + Documentation has bee updated to explain the different phases of + operation. Control flow through ataPrintMain() + simplified. +- If reading device identity information fails, try seeing if the info + can be accessed using a "DEVICE PACKET" command. This way we can + at least get device info. +- Modified Makefile to automatically tag CVS archive on issuance of + a release +- Modified drive detection so minor device ID code showing ATA-3 rev + 0 (no SMART) is known to not be SMART capable. +- Now verify the checksum of the device ID data structure, and of the + attributes threshold structure. Before neither of these + structures had their checksums verified. +- New behavior vis-a-vis checksums. If they are wrong, we log + warning messages to stdout, stderr, and syslog, but carry on + anyway. All functions now call a checksumwarning routine if the + checksum doesn't vanish as it should. +- Changed Read Hard Disk Identity function to get fresh info from + the disk on each call rather than to use the values that were read + upon boot-up into the BIOS. This is the biggest change in this + release. The ioctl(device, HDIO_GET_IDENTITY, buf ) call should + be avoided in such code. Note that if people get garbled strings + for the model, serial no and firmware versions of their drives, + then blame goes here (the BIOS does the byte swapping for you, + apparently!) +- Function ataSmartSupport now looks at correct bits in drive + identity structure to verify first that these bits are valid, + before using them. +- Function ataIsSmartEnabled() written which uses the Drive ID state + information to tell if SMART is enabled or not. We'll carry this + along for the moment without using it. +- Function ataDoesSmartWork() guaranteed to work if the device + supports SMART. +- Replace some numbers by #define MACROS +- Wrote Function TestTime to return test time associated with each + different type of test. +- Thinking of the future, have added a new function called + ataSmartStatus2(). Eventually when I understand how to use the + TASKFILE API and am sure that this works correctly, it will + replace ataSmartStatus(). This queries the drive directly to + see if the SMART status is OK, rather than comparing thresholds to + attribute values ourselves. But I need to get some drives that fail + their SMART status to check it. + +* Thu Oct 17 2002 Bruce Allen +- Removed extraneous space before some error message printing. +- Fixed some character buffers that were too short for contents. + Only used for unrecognized drives, so probably damage was minimal. + +* Wed Oct 16 2002 Bruce Allen +- Initial release. Code is derived from smartsuite, and is + intended to be compatible with the ATA/ATAPI-5 specifications. +- For IBM disks whose raw temp data includes three temps. print all + three +- print timestamps for error log to msec precision +- added -m option for Hitachi disks that store power on life in + minutes +- added -L option for printing self-test error logs +- in -l option, now print power on lifetime, so that one can see + when the error took place +- updated SMART structure definitions to ATA-5 spec +- added -p option +- added -f and -F options to enable/disable autosave threshold + parameters + diff --git a/utility.c b/utility.c new file mode 100644 index 0000000..19c9f80 --- /dev/null +++ b/utility.c @@ -0,0 +1,681 @@ +/* + * utility.c + * + * Home page of code is: http://smartmontools.sourceforge.net + * + * Copyright (C) 2002-6 Bruce Allen + * Copyright (C) 2000 Michael Cornwell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code was originally developed as a Senior Thesis by Michael Cornwell + * at the Concurrent Systems Laboratory (now part of the Storage Systems + * Research Center), Jack Baskin School of Engineering, University of + * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ + * + */ + +// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO +// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD, +// SMARTCTL, OR BOTH. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include // _mbsinc() +#endif + +#include "config.h" +#include "int64.h" +#include "utility.h" + +// Any local header files should be represented by a CVSIDX just below. +const char* utility_c_cvsid="$Id: utility.c,v 1.61 2006/04/12 14:54:28 ballen4705 Exp $" +CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID; + +const char * packet_types[] = { + "Direct-access (disk)", + "Sequential-access (tape)", + "Printer", + "Processor", + "Write-once (optical disk)", + "CD/DVD", + "Scanner", + "Optical memory (optical disk)", + "Medium changer", + "Communications", + "Graphic arts pre-press (10)", + "Graphic arts pre-press (11)", + "Array controller", + "Enclosure services", + "Reduced block command (simplified disk)", + "Optical card reader/writer" +}; + +// Whenever exit() status is EXIT_BADCODE, please print this message +const char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n"; + + +// hang on to exit code, so we can make use of more generic 'atexit()' +// functionality and still check our exit code +int exitstatus = 0; + +// command-line argument: are we running in debug mode?. +unsigned char debugmode = 0; + + +// Solaris only: Get site-default timezone. This is called from +// UpdateTimezone() when TZ environment variable is unset at startup. +#if defined (__SVR4) && defined (__sun) +static const char *TIMEZONE_FILE = "/etc/TIMEZONE"; + +static char *ReadSiteDefaultTimezone(){ + FILE *fp; + char buf[512], *tz; + int n; + + tz = NULL; + fp = fopen(TIMEZONE_FILE, "r"); + if(fp == NULL) return NULL; + while(fgets(buf, sizeof(buf), fp)) { + if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line + continue; + n = strlen(buf) - 1; + if (buf[n] == '\n') buf[n] = 0; + if (tz) free(tz); + tz = strdup(buf); + } + fclose(fp); + return tz; +} +#endif + +// Make sure that this executable is aware if the user has changed the +// time-zone since the last time we polled devices. The cannonical +// example is a user who starts smartd on a laptop, then flies across +// time-zones with a laptop, and then changes the timezone, WITHOUT +// restarting smartd. This is a work-around for a bug in +// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and +// thanks to Ian Redfern for posting a workaround. + +// Please refer to the smartd manual page, in the section labeled LOG +// TIMESTAMP TIMEZONE. +void FixGlibcTimeZoneBug(){ +#if __GLIBC__ + if (!getenv("TZ")) { + putenv("TZ=GMT"); + tzset(); + putenv("TZ"); + tzset(); + } +#elif _WIN32 + if (!getenv("TZ")) { + putenv("TZ=GMT"); + tzset(); + putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing + tzset(); + } +#elif defined (__SVR4) && defined (__sun) + // In Solaris, putenv("TZ=") sets null string and invalid timezone. + // putenv("TZ") does nothing. With invalid TZ, tzset() do as if + // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at + // first tzset() call. Conclusion: Unlike glibc, dynamic + // configuration of timezone can be done only by changing actual + // value of TZ environment value. + enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; + static enum tzstate state = NOT_CALLED_YET; + + static struct stat prev_stat; + static char *prev_tz; + struct stat curr_stat; + char *curr_tz; + + if(state == NOT_CALLED_YET) { + if(getenv("TZ")) { + state = USER_TIMEZONE; // use supplied timezone + } else { + state = TRACK_TIMEZONE; + if(stat(TIMEZONE_FILE, &prev_stat)) { + state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever + } else { + prev_tz = ReadSiteDefaultTimezone(); // track timezone file change + if(prev_tz) putenv(prev_tz); + } + } + tzset(); + } else if(state == TRACK_TIMEZONE) { + if(stat(TIMEZONE_FILE, &curr_stat) == 0 + && (curr_stat.st_ctime != prev_stat.st_ctime + || curr_stat.st_mtime != prev_stat.st_mtime)) { + // timezone file changed + curr_tz = ReadSiteDefaultTimezone(); + if(curr_tz) { + putenv(curr_tz); + if(prev_tz) free(prev_tz); + prev_tz = curr_tz; prev_stat = curr_stat; + } + } + tzset(); + } +#endif + // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO + // KEEP THEM INDEPENDENT. + return; +} + +#ifdef _WIN32 +// Fix strings in tzname[] to avoid long names with non-ascii characters. +// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the +// national language timezone names returned by GetTimezoneInformation(). +static char * fixtzname(char * dest, int destsize, const char * src) +{ + int i = 0, j = 0; + while (src[i] && j < destsize-1) { + int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src; + if (i2 > i+1) + i = i2; // Ignore multibyte chars + else { + if ('A' <= src[i] && src[i] <= 'Z') + dest[j++] = src[i]; // "Pacific Standard Time" => "PST" + i++; + } + } + if (j < 2) + j = 0; + dest[j] = 0; + return dest; +} +#endif // _WIN32 + +// This value follows the peripheral device type value as defined in +// SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in +// the ATA standard for packet devices to define the device type. +const char *packetdevicetype(int type){ + if (type<0x10) + return packet_types[type]; + + if (type<0x20) + return "Reserved"; + + return "Unknown"; +} + + +// Returns 1 if machine is big endian, else zero. This is a run-time +// rather than a compile-time function. We could do it at +// compile-time but in principle there are architectures that can run +// with either byte-ordering. +int isbigendian(){ + short i=0x0100; + char *tmp=(char *)&i; + return *tmp; +} + +// Utility function prints date and time and timezone into a character +// buffer of length>=64. All the fuss is needed to get the right +// timezone info (sigh). +void dateandtimezoneepoch(char *buffer, time_t tval){ + struct tm *tmval; + char *timezonename; + char datebuffer[DATEANDEPOCHLEN]; + int lenm1; +#ifdef _WIN32 + char tzfixbuf[6+1]; +#endif + + FixGlibcTimeZoneBug(); + + // Get the time structure. We need this to determine if we are in + // daylight savings time or not. + tmval=localtime(&tval); + + // Convert to an ASCII string, put in datebuffer + // same as: asctime_r(tmval, datebuffer); + strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); + datebuffer[DATEANDEPOCHLEN-1]='\0'; + + // Remove newline + lenm1=strlen(datebuffer)-1; + datebuffer[lenm1>=0?lenm1:0]='\0'; + + // correct timezone name + if (tmval->tm_isdst==0) + // standard time zone + timezonename=tzname[0]; + else if (tmval->tm_isdst>0) + // daylight savings in effect + timezonename=tzname[1]; + else + // unable to determine if daylight savings in effect + timezonename=""; + +#ifdef _WIN32 + // Fix long non-ascii timezone names + if (!getenv("TZ")) + timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename); +#endif + + // Finally put the information into the buffer as needed. + snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename); + + return; +} + +// Date and timezone gets printed into string pointed to by buffer +void dateandtimezone(char *buffer){ + + // Get the epoch (time in seconds since Jan 1 1970) + time_t tval=time(NULL); + + dateandtimezoneepoch(buffer, tval); + return; +} + +// These are two utility functions for printing CVS IDs. Massagecvs() +// returns distance that it has moved ahead in the input string +int massagecvs(char *out, const char *cvsid){ + char *copy,*filename,*date,*version; + int retVal=0; + const char delimiters[] = " ,$"; + + // make a copy on the heap, go to first token, + if (!(copy=strdup(cvsid))) + return 0; + + if (!(filename=strtok(copy, delimiters))) + goto endmassage; + + // move to first instance of "Id:" + while (strcmp(filename,"Id:")) + if (!(filename=strtok(NULL, delimiters))) + goto endmassage; + + // get filename, skip "v", get version and date + if (!( filename=strtok(NULL, delimiters) ) || + !( strtok(NULL, delimiters) ) || + !( version=strtok(NULL, delimiters) ) || + !( date=strtok(NULL, delimiters) ) ) + goto endmassage; + + sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date); + retVal = (date-copy)+strlen(date); + + endmassage: + free(copy); + return retVal; +} + +// prints a single set of CVS ids +void printone(char *block, const char *cvsid){ + char strings[CVSMAXLEN]; + const char *here=cvsid; + int bi=0, len=strlen(cvsid)+1; + + // check that the size of the output block is sufficient + if (len>=CVSMAXLEN) { + pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1); + EXIT(1); + } + + // loop through the different strings + while (bi= 0; i++) { + switch (pattern[i]) { + case '(': level++; break; + case ')': level--; break; + } + } + return level; +} + +// A wrapper for regcomp(). Returns zero for success, non-zero otherwise. +int compileregex(regex_t *compiled, const char *pattern, int cflags) +{ + int errorcode; + + if ( (errorcode = regcomp(compiled, pattern, cflags)) + || check_regex_nesting(pattern) < 0 ) { + pout("Internal error: unable to compile regular expression \"%s\" ", pattern); + if (errorcode) + printregexwarning(errorcode, compiled); + else + pout("Unmatched ')'\n"); + pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n"); + return 1; + } + return 0; +} + +// Splits an argument to the -r option into a name part and an (optional) +// positive integer part. s is a pointer to a string containing the +// argument. After the call, s will point to the name part and *i the +// integer part if there is one or 1 otherwise. Note that the string s may +// be changed by this function. Returns zero if successful and non-zero +// otherwise. +int split_report_arg(char *s, int *i) +{ + if ((s = strchr(s, ','))) { + // Looks like there's a name part and an integer part. + char *tailptr; + + *s++ = '\0'; + if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive + return 1; + errno = 0; + *i = (int) strtol(s, &tailptr, 10); + if (errno || *tailptr != '\0') + return 1; + } else { + // There's no integer part. + *i = 1; + } + + return 0; +} + +// same as above but sets *i to -1 if missing , argument +int split_report_arg2(char *s, int *i){ + char *tailptr; + s+=6; + + if (*s=='\0' || !isdigit((int)*s)) { + // What's left must be integer + *i=-1; + return 1; + } + + errno = 0; + *i = (int) strtol(s, &tailptr, 10); + if (errno || *tailptr != '\0') { + *i=-1; + return 1; + } + + return 0; +} + +#ifndef HAVE_STRTOULL +// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0) +// Functionality reduced to split_selective_arg()'s requirements. + +static uint64_t strtoull(const char * p, char * * endp, int base) +{ + uint64_t result, maxres; + int i = 0; + char c = p[i++]; + // assume base == 0 + if (c == '0') { + if (p[i] == 'x' || p[i] == 'X') { + base = 16; i++; + } + else + base = 8; + c = p[i++]; + } + else + base = 10; + + result = 0; + maxres = ~(uint64_t)0 / (unsigned)base; + for (;;) { + unsigned digit; + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('A' <= c && c <= 'Z') + digit = c - 'A' + 10; + else if ('a' <= c && c <= 'z') + digit = c - 'a' + 10; + else + break; + if (digit >= (unsigned)base) + break; + if (!( result < maxres + || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) { + result = ~(uint64_t)0; errno = ERANGE; // return on overflow + break; + } + result = result * (unsigned)base + digit; + c = p[i++]; + } + *endp = (char *)p + i - 1; + return result; +} +#endif // HAVE_STRTOLL + +// Splits an argument to the -t option that is assumed to be of the form +// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex) +// are allowed). The first long long int is assigned to *start and the second +// to *stop. Returns zero if successful and non-zero otherwise. +int split_selective_arg(char *s, uint64_t *start, + uint64_t *stop) +{ + char *tailptr; + + if (!(s = strchr(s, ','))) + return 1; + if (!isdigit((int)(*++s))) + return 1; + errno = 0; + // Last argument to strtoull (the base) is 0 meaning that decimal is assumed + // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used. + *start = strtoull(s, &tailptr, 0); + + s = tailptr; + if (errno || *s++ != '-') + return 1; + *stop = strtoull(s, &tailptr, 0); + if (errno || *tailptr != '\0') + return 1; + return 0; +} + +int64_t bytes = 0; +// Helps debugging. If the second argument is non-negative, then +// decrement bytes by that amount. Else decrement bytes by (one plus) +// length of null terminated string. +void *FreeNonZero(void *address, int size, int line, const char* file){ + if (address) { + if (size<0) + bytes-=1+strlen(address); + else + bytes-=size; + return CheckFree(address, line, file); + } + return NULL; +} + +// To help with memory checking. Use when it is known that address is +// NOT null. +void *CheckFree(void *address, int whatline, const char* file){ + if (address){ + free(address); + return NULL; + } + + PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", + whatline, file, reportbug); + EXIT(EXIT_BADCODE); +} + +// A custom version of calloc() that tracks memory use +void *Calloc(size_t nmemb, size_t size) { + void *ptr=calloc(nmemb, size); + + if (ptr) + bytes+=nmemb*size; + + return ptr; +} + +// A custom version of strdup() that keeps track of how much memory is +// being allocated. If mustexist is set, it also throws an error if we +// try to duplicate a NULL string. +char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file){ + char *tmp; + + // report error if ptr is NULL and mustexist is set + if (ptr==NULL){ + if (mustexist) { + PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", + whatline, file, reportbug); + EXIT(EXIT_BADCODE); + } + else + return NULL; + } + + // make a copy of the string... + tmp=strdup(ptr); + + if (!tmp) { + PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file); + EXIT(EXIT_NOMEM); + } + + // and track memory usage + bytes+=1+strlen(ptr); + + return tmp; +} + +// Returns nonzero if region of memory contains non-zero entries +int nonempty(unsigned char *testarea,int n){ + int i; + for (i=0;i + * Copyright (C) 2000 Michael Cornwell + * + * 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/ + * + */ + +#ifndef UTILITY_H_ +#define UTILITY_H_ + +#define UTILITY_H_CVSID "$Id: utility.h,v 1.43 2006/04/12 14:54:28 ballen4705 Exp $\n" + +#include +#include // for regex.h (according to POSIX) +#include + +#ifndef HAVE_WORKING_SNPRINTF +// Substitute by safe replacement functions +#include +int safe_snprintf(char *buf, int size, const char *fmt, ...); +int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap); +#define snprintf safe_snprintf +#define vsnprintf safe_vsnprintf +#endif + +// Utility function prints current date and time and timezone into a +// character buffer of length>=64. All the fuss is needed to get the +// right timezone info (sigh). +#define DATEANDEPOCHLEN 64 +void dateandtimezone(char *buffer); +// Same, but for time defined by epoch tval +void dateandtimezoneepoch(char *buffer, time_t tval); + +// utility function for printing out CVS strings +#define CVSMAXLEN 1024 +void printone(char *block, const char *cvsid); + +// like printf() except that we can control it better. Note -- +// although the prototype is given here in utility.h, the function +// itself is defined differently in smartctl and smartd. So the +// function definition(s) are in smartd.c and in smartctl.c. +#ifndef __GNUC__ +#define __attribute__(x) /* nothing */ +#endif +void pout(char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +// replacement for perror() with redirected output. +void syserror(const char *message); + +// Prints a warning message for a failed regular expression compilation from +// regcomp(). +void printregexwarning(int errcode, regex_t *compiled); + +// A wrapper for regcomp(). Returns zero for success, non-zero otherwise. +int compileregex(regex_t *compiled, const char *pattern, int cflags); + +// 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); + + +// Guess device type (ata or scsi) based on device name +// Guessing will now use Controller Type defines below + +int guess_device_type(const char * dev_name); + +// Create and return the list of devices to probe automatically +// if the DEVICESCAN option is in the smartd config file +int make_device_names (char ***devlist, const char* name); + + +#define EXIT(x) { exitstatus = (x); exit((x)); } + +// replacement for calloc() that tracks memory usage +void *Calloc(size_t nmemb, size_t size); + +// Utility function to free memory +void *FreeNonZero(void* address, int size, int whatline, const char* file); + +// A custom version of strdup() that keeps track of how much memory is +// being allocated. If mustexist is set, it also throws an error if we +// try to duplicate a NULL string. +char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file); + +// To help with memory checking. Use when it is known that address is +// NOT null. +void *CheckFree(void *address, int whatline, const char* file); + +// This function prints either to stdout or to the syslog as needed + +// [From GLIBC Manual: Since the prototype doesn't specify types for +// optional arguments, in a call to a variadic function the default +// argument promotions are performed on the optional argument +// values. This means the objects of type char or short int (whether +// signed or not) are promoted to either int or unsigned int, as +// appropriate.] +void PrintOut(int priority,char *fmt, ...); + +// run time, determine byte ordering +int isbigendian(); + +// This value follows the peripheral device type value as defined in +// SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in +// the ATA standard for packet devices to define the device type. +const char *packetdevicetype(int type); + +int deviceopen(const char *pathname, char *type); + +int deviceclose(int fd); + +// Optional functions of os_*.c +#ifdef HAVE_GET_OS_VERSION_STR +// Return build host and OS version as static string +const char * get_os_version_str(void); +#endif + +// returns 1 if any of the n bytes are nonzero, else zero. +int nonempty(unsigned char *testarea,int n); + +// needed to fix glibc bug +void FixGlibcTimeZoneBug(); + +// convert time in msec to a text string +void MsecToText(unsigned int msec, char *txt); + +// Exit codes +#define EXIT_BADCMD 1 // command line did not parse +#define EXIT_BADCONF 2 // syntax error in config file +#define EXIT_STARTUP 3 // problem forking daemon +#define EXIT_PID 4 // problem creating pid file +#define EXIT_NOCONF 5 // config file does not exist +#define EXIT_READCONF 6 // config file exists but cannot be read + +#define EXIT_NOMEM 8 // out of memory +#define EXIT_BADCODE 10 // internal error - should NEVER happen + +#define EXIT_BADDEV 16 // we can't monitor this device +#define EXIT_NODEV 17 // no devices to monitor + +#define EXIT_SIGNAL 254 // abort on signal + + +// macros to control printing +#define PRINT_ON(control) {if (control->printing_switchable) control->dont_print=0;} +#define PRINT_OFF(control) {if (control->printing_switchable) control->dont_print=1;} + +// possible values for controller_type in extern.h +#define CONTROLLER_UNKNOWN 0x00 +#define CONTROLLER_ATA 0x01 +#define CONTROLLER_SCSI 0x02 +#define CONTROLLER_3WARE 0x03 // set by -d option, but converted to one of three types below +#define CONTROLLER_3WARE_678K 0x04 // NOT set by guess_device_type() +#define CONTROLLER_3WARE_9000_CHAR 0x05 // set by guess_device_type() +#define CONTROLLER_3WARE_678K_CHAR 0x06 // set by guess_device_type() +#define CONTROLLER_MARVELL_SATA 0x07 // SATA drives behind Marvell controllers + + +#endif -- 2.39.5