smartmontools-7.0/0000755000175000010010000000000013412155413011266 500000000000000smartmontools-7.0/.editorconfig0000644000175000010010000000152313043134546013670 00000000000000# smartmontools indent style settings for EditorConfig # http://editorconfig.org/ # $Id: .editorconfig 4385 2017-01-28 15:31:50Z chrfranke $ # top-most file root = true [*] indent_style = space indent_size = 2 tab_width = 8 [/ChangeLog] indent_style = tab indent_size = 8 [Makefile*] # Rule recipes require tab characters (spaces are used elsewhere) indent_style = tab indent_size = 8 # TODO: Fix files with other indent styles [/scsiata.cpp] # indent_size = 2 and 4 [/scsicmds.*] indent_size = 4 [/scsiprint.cpp] indent_size = 4 [/os_win32/syslog_win32.cpp] indent_style = tab indent_size = 4 tab_width = 4 # Keep indent style of imported files as is [/*_nvme_ioctl.h] indent_style = tab indent_size = 8 [/csmisas.h] indent_size = 3 [/getopt/getopt.*] indent_style = tab indent_size = 2 [/regex/reg*.*] indent_style = tab indent_size = 2 smartmontools-7.0/aacraid.h0000644000175000010010000001045513336335341012755 00000000000000/* aacraid.h * Copyright (C) 2014 Raghava Aditya * Copyright (C) 2015 Nidhi Malhotra * * SPDX-License-Identifier: GPL-2.0-or-later */ // Check windows #if defined(_WIN32) || defined(_WIN64) #ifdef _WIN64 #define ENVIRONMENT64 #else #define ENVIRONMENT32 #endif #endif // Check GCC #if __GNUC__ #if __x86_64__ || __ppc64__ #define ENVIRONMENT64 #else #define ENVIRONMENT32 #endif #endif #define METHOD_BUFFERED 0 #define METHOD_NEITHER 3 #if defined(_WIN32) || defined(_WIN64) #define FSAMPCTL_SCSI_BASE IOCTL_SCSI_BASE #define ARCIOCTL_SEND_RAW_SRB CTL_CODE(FSAMPCTL_SCSI_BASE, 2201, METHOD_BUFFERED, FILE_ANY_ACCESS) #define AACRAID_SAS_SIGNATURE "ARCSAS" #define SRB_FLAGS_DATA_IN 0x00000040 #define SRB_FLAGS_DATA_OUT 0x00000080 #define SRB_FLAGS_NO_DATA_TRANSFER 0x00000000 #else #define CTL_CODE(function, method) ((4<< 16) | ((function) << 2) | (method) ) #define FSACTL_SEND_RAW_SRB CTL_CODE(2067, METHOD_BUFFERED) #endif #define SRB_FUNCTION_EXECUTE_SCSI 0X00 #define SRB_DataIn 0x0040 #define SRB_DataOut 0x0080 #define SRB_NoDataXfer 0x0000 typedef struct { uint32_t lo32; uint32_t hi32; } address64; typedef struct { address64 addr64; uint32_t length; /* Length. */ } user_sgentry64; typedef struct { uint32_t addr32; uint32_t length; } user_sgentry32; typedef struct { uint32_t count; user_sgentry64 sg64[1]; } user_sgmap64; typedef struct { uint32_t count; user_sgentry32 sg32[1]; } user_sgmap32; #if defined(_WIN32) || defined(_WIN64) typedef struct _SCSI_REQUEST_BLOCK { USHORT Length; // offset 0 UCHAR Function; // offset 2 UCHAR SrbStatus; // offset 3 UCHAR ScsiStatus; // offset 4 UCHAR PathId; // offset 5 UCHAR TargetId; // offset 6 UCHAR Lun; // offset 7 UCHAR QueueTag; // offset 8 UCHAR QueueAction; // offset 9 UCHAR CdbLength; // offset a UCHAR SenseInfoBufferLength; // offset b ULONG SrbFlags; // offset c ULONG DataTransferLength; // offset 10 ULONG TimeOutValue; // offset 14 PVOID DataBuffer; // offset 18 PVOID SenseInfoBuffer; // offset 1c struct _SCSI_REQUEST_BLOCK *NextSrb; // offset 20 PVOID OriginalRequest; // offset 24 PVOID SrbExtension; // offset 28 union { ULONG InternalStatus; // offset 2c ULONG QueueSortKey; // offset 2c }; #if defined(_WIN64) // // Force PVOID alignment of Cdb // ULONG Reserved; #endif UCHAR Cdb[16]; // offset 30 } SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK; #define SCSI_REQUEST_BLOCK_SIZE sizeof(SCSI_REQUEST_BLOCK) #else typedef struct { uint32_t function; //SRB_FUNCTION_EXECUTE_SCSI 0x00 uint32_t channel; //bus uint32_t id; //use the ID number this is wrong uint32_t lun; //Logical unit number uint32_t timeout; uint32_t flags; //Interesting stuff I must say uint32_t count; // Data xfer size uint32_t retry_limit; // We shall see uint32_t cdb_size; // Length of CDB uint8_t cdb[16]; // The actual cdb command user_sgmap64 sg64; // pDatabuffer and address of Databuffer } user_aac_srb64; typedef struct { uint32_t function; //SRB_FUNCTION_EXECUTE_SCSI 0x00 uint32_t channel; //bus uint32_t id; //use the ID number this is wrong uint32_t lun; //Logical unit number uint32_t timeout; uint32_t flags; //Interesting stuff I must say uint32_t count; // Data xfer size uint32_t retry_limit; // We shall see uint32_t cdb_size; // Length of CDB uint8_t cdb[16]; // The actual cdb command user_sgmap32 sg32; // pDatabuffer and address of Databuffer } user_aac_srb32; typedef struct { uint32_t status; uint32_t srb_status; uint32_t scsi_status; uint32_t data_xfer_length; uint32_t sense_data_size; uint8_t sense_data[30]; } user_aac_reply; #endif smartmontools-7.0/aclocal.m40000644000175000010010000012706413412155333013061 00000000000000# generated automatically by aclocal 1.15.1 -*- Autoconf -*- # Copyright (C) 1996-2017 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. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2017 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. # 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. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Figure out how to run the assembler. -*- Autoconf -*- # Copyright (C) 2001-2017 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. # 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_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl ]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2017 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. # 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], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2017 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. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl 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])]) # Copyright (C) 1999-2017 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. # 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", "OBJC", "OBJCXX", "UPC", or "GJC". # 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 m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$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". rm -rf conftest.dir 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 am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) 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 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # 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. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; 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 ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj 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], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2017 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. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf 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. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/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"` # 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'`; 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"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2017 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 macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # 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.65])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 if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi 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], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])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]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). 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])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # 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_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2017 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. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2017 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. # 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. -*- Autoconf -*- # From Jim Meyering # Copyright (C) 1996-2017 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. # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. # Default is to disable them, unless 'enable' is passed literally. # For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), [enable], [m4_define([am_maintainer_other], [disable])], [disable], [m4_define([am_maintainer_other], [enable])], [m4_define([am_maintainer_other], [enable]) m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], am_maintainer_other[ make rules and dependencies not useful (and sometimes confusing) to the casual installer])], [USE_MAINTAINER_MODE=$enableval], [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST([MAINT])dnl ] ) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2017 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. # 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 this is the am__doit target .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 # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2017 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. # 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 is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2017 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. # _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], [m4_foreach_w([_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])]) # Copyright (C) 1999-2017 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. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2017 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. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2017 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. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # 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 ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file 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 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 if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done 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]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2017 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. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2017 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. # AM_PROG_INSTALL_STRIP # --------------------- # 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="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2017 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. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2017 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. # _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. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} 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 m4_include([m4/pkg.m4]) smartmontools-7.0/atacmdnames.cpp0000644000175000010010000003461413336335341014204 00000000000000/* * atacmdnames.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 Philip Williams * Copyright (C) 2012 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "atacmdnames.h" #include #include const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 4760 2018-08-19 18:45:53Z chrfranke $" ATACMDNAMES_H_CVSID; const char cmd_reserved[] = "[RESERVED]"; const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]"; const char cmd_reserved_sa[] = "[RESERVED FOR SERIAL ATA]"; const char cmd_reserved_cf[] = "[RESERVED FOR COMPACTFLASH ASSOCIATION]"; const char cmd_reserved_mcpt[] = "[RESERVED FOR MEDIA CARD PASS THROUGH]"; // ACS-3: Reserved const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]"; const char cmd_seek_ret4[] = "SEEK [RET-4]"; // Tables B.3 and B.4 of T13/2161-D (ACS-3) Revision 4, September 4, 2012 const char * const command_table[] = { /*-------------------------------------------------- 00h-0Fh -----*/ "NOP", cmd_reserved, cmd_reserved, "CFA REQUEST EXTENDED ERROR", cmd_reserved, cmd_reserved, "DATA SET MANAGEMENT", // ACS-2 cmd_reserved, "DEVICE RESET", cmd_reserved, cmd_reserved, "REQUEST SENSE DATA EXT", // ACS-2 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 [OBS-4]", "READ LONG (w/o retry) [OBS-4]", "READ SECTOR(S) EXT", "READ DMA EXT", "READ DMA QUEUED EXT [OBS-ACS-2]", "READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]", cmd_reserved, "READ MULTIPLE EXT", "READ STREAM DMA", "READ STREAM", cmd_reserved, cmd_reserved, cmd_reserved, "READ LOG EXT", /*-------------------------------------------------- 30h-3Fh -----*/ "WRITE SECTOR(S)", "WRITE SECTOR(S) (w/o retry) [OBS-5]", "WRITE LONG [OBS-4]", "WRITE LONG (w/o retry) [OBS-4]", "WRITE SECTORS(S) EXT", "WRITE DMA EXT", "WRITE DMA QUEUED EXT [OBS-ACS-2]", "SET NATIVE MAX ADDRESS EXT [OBS-ACS-3]", "CFA WRITE SECTORS WITHOUT ERASE", "WRITE MULTIPLE EXT", "WRITE STREAM DMA", "WRITE STREAM", "WRITE VERIFY [OBS-4]", "WRITE DMA FUA EXT", "WRITE DMA QUEUED FUA EXT [OBS-ACS-2]", "WRITE LOG EXT", /*-------------------------------------------------- 40h-4Fh -----*/ "READ VERIFY SECTOR(S)", "READ VERIFY SECTOR(S) (w/o retry) [OBS-5]", "READ VERIFY SECTOR(S) EXT", cmd_reserved, cmd_reserved, "WRITE UNCORRECTABLE EXT", // ATA-8 cmd_reserved, "READ LOG DMA EXT", // ATA-8 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, "WRITE LOG DMA EXT", // ATA-8 cmd_reserved, cmd_reserved, cmd_reserved, "TRUSTED NON-DATA", // ATA-8 "TRUSTED RECEIVE", // ATA-8 "TRUSTED RECEIVE DMA", // ATA-8 "TRUSTED SEND", // ATA-8 "TRUSTED SEND DMA", // ATA-8 /*-------------------------------------------------- 60h-6Fh -----*/ "READ FPDMA QUEUED", // ATA-8 "WRITE FPDMA QUEUED", // ATA-8 cmd_reserved_sa, "NCQ QUEUE MANAGEMENT", // ACS-3 "SEND FPDMA QUEUED", // ACS-3 "RECEIVE FPDMA QUEUED", // ACS-3 cmd_reserved_sa, cmd_reserved_sa, cmd_reserved, 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, "SET DATE & TIME EXT", // ACS-3 "ACCESSIBLE MAX ADDRESS CONFIGURATION", // ACS-3 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", "DOWNLOAD MICROCODE DMA", // ACS-2 "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 [OBS-ACS-2]", 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 [OBS-ACS-3]", cmd_reserved, cmd_reserved, "SANITIZE DEVICE", // ACS-2 cmd_reserved, "NV CACHE [OBS-ACS-3]", // ATA-8 cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, cmd_reserved_cf, cmd_reserved, cmd_reserved, cmd_reserved, cmd_reserved, /*-------------------------------------------------- C0h-CFh -----*/ "CFA ERASE SECTORS [VS IF NO CFA]", cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific, "READ MULTIPLE", "WRITE MULTIPLE", "SET MULTIPLE MODE", "READ DMA QUEUED [OBS-ACS-2]", "READ DMA", "READ DMA (w/o retry) [OBS-5]", "WRITE DMA", "WRITE DMA (w/o retry) [OBS-5]", "WRITE DMA QUEUED [OBS-ACS-2]", "CFA WRITE MULTIPLE WITHOUT ERASE", "WRITE MULTIPLE FUA EXT", cmd_reserved, /*-------------------------------------------------- D0h-DFh -----*/ cmd_reserved, "CHECK MEDIA CARD TYPE [OBS-ACS-2]", cmd_reserved_mcpt, cmd_reserved_mcpt, cmd_reserved_mcpt, cmd_reserved, cmd_reserved, cmd_reserved, cmd_reserved, cmd_reserved, "GET MEDIA STATUS [OBS-8]", "ACKNOWLEDGE MEDIA CHANGE [RET-4]", "BOOT POST-BOOT [RET-4]", "BOOT PRE-BOOT [RET-4]", "MEDIA LOCK [OBS-8]", "MEDIA UNLOCK [OBS-8]", /*-------------------------------------------------- E0h-EFh -----*/ "STANDBY IMMEDIATE", "IDLE IMMEDIATE", "STANDBY", "IDLE", "READ BUFFER", "CHECK POWER MODE", "SLEEP", "FLUSH CACHE", "WRITE BUFFER", "READ BUFFER DMA", // ACS-2 (was: WRITE SAME [RET-4]) "FLUSH CACHE EXT", "WRITE BUFFER DMA", // ACS-2 "IDENTIFY DEVICE", "MEDIA EJECT [OBS-8]", "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 [OBS-ACS-3]", "SET MAX ADDRESS [OBS-ACS-3]", cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific, cmd_vendor_specific }; typedef char ASSERT_command_table_size[ sizeof(command_table)/sizeof(command_table[0]) == 256 ? 1 : -1]; /* Returns the name of the command (and possibly sub-command) with the given command code and feature register values. For most command codes this simply returns the corresponding entry in the command_table array, but for 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) { 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] [OBS-ACS-2]"; default: return "NOP [Reserved subcommand] [OBS-ACS-2]"; } case 0x92: /* DOWNLOAD MICROCODE */ switch (f_reg) { case 0x01: return "DOWNLOAD MICROCODE [Temporary] [OBS-8]"; case 0x03: return "DOWNLOAD MICROCODE [Save with offsets]"; // ATA-8 case 0x07: return "DOWNLOAD MICROCODE [Save]"; case 0x0e: return "DOWNLOAD MICROCODE [Save for future use]"; // ACS-3 case 0x0f: return "DOWNLOAD MICROCODE [Activate]"; // ACS-3 default: return "DOWNLOAD MICROCODE [Reserved subcommand]"; } 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 "SMART [Vendor specific subcommand]"; else return "SMART [Reserved subcommand]"; } case 0xB1: /* DEVICE CONFIGURATION */ switch (f_reg) { case 0xC0: return "DEVICE CONFIGURATION RESTORE [OBS-ACS-3]"; case 0xC1: return "DEVICE CONFIGURATION FREEZE LOCK [OBS-ACS-3]"; case 0xC2: return "DEVICE CONFIGURATION IDENTIFY [OBS-ACS-3]"; case 0xC3: return "DEVICE CONFIGURATION SET [OBS-ACS-3]"; default: return "DEVICE CONFIGURATION [Reserved subcommand] [OBS-ACS-3]"; } case 0xEF: /* SET FEATURES */ switch (f_reg) { case 0x01: return "SET FEATURES [Enable 8-bit PIO] [OBS-3]"; // Now CFA 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)] [OPS-ACS-3]"; case 0x0A: return "SET FEATURES [Enable CFA power mode 1]"; case 0x10: return "SET FEATURES [Enable SATA feature]"; // ACS-3 case 0x20: return "SET FEATURES [Set Time-ltd R/W WCT]"; case 0x21: return "SET FEATURES [Set Time-ltd R/W EH]"; case 0x31: return "SET FEATURES [Disable Media Status Notf] [OBS-8]"; case 0x33: return "SET FEATURES [Disable retry] [OBS-4]"; case 0x41: return "SET FEATURES [Enable Free-fall Control]"; // ATA-8 case 0x42: return "SET FEATURES [Enable AAM] [OBS-ACS-2]"; case 0x43: return "SET FEATURES [Set Max Host I/F S Times]"; case 0x44: return "SET FEATURES [Length of VS data] [OBS-4]"; case 0x4a: return "SET FEATURES [Ext. Power Conditions]"; // ACS-2 case 0x54: return "SET FEATURES [Set cache segs] [OBS-4]"; case 0x55: return "SET FEATURES [Disable read look-ahead]"; case 0x5D: return "SET FEATURES [Enable release interrupt] [OBS-ACS-2]"; case 0x5E: return "SET FEATURES [Enable SERVICE interrupt] [OBS-ACS-2]"; case 0x66: return "SET FEATURES [Disable revert defaults]"; case 0x69: return "SET FEATURES [LPS Error Reporting Control]"; // ACS-2 case 0x77: return "SET FEATURES [Disable ECC] [OBS-4]"; case 0x81: return "SET FEATURES [Disable 8-bit PIO] [OBS-3]"; // Now CFA 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 [Disable SATA feature]"; // ACS-3 case 0x95: return "SET FEATURES [Enable Media Status Notf] [OBS-8]"; 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 0xC1: return "SET FEATURES [Disable Free-fall Control]"; // ATA-8 case 0xC2: return "SET FEATURES [Disable AAM] [OBS-ACS-2]"; case 0xC3: return "SET FEATURES [Sense Data Reporting]"; // ACS-2 case 0xCC: return "SET FEATURES [Enable revert to defaults]"; case 0xDD: return "SET FEATURES [Disable release interrupt] [OBS-ACS-2]"; case 0xDE: return "SET FEATURES [Disable SERVICE interrupt] [OBS-ACS-2]"; case 0xE0: return "SET FEATURES [Vendor specific] [OBS-7]"; 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 [OBS-ACS-3]"; case 0x02: return "SET MAX LOCK [OBS-ACS-3]"; case 0x03: return "SET MAX UNLOCK [OBS-ACS-3]"; case 0x04: return "SET MAX FREEZE LOCK [OBS-ACS-3]"; default: return "SET MAX [Reserved subcommand] [OBS-ACS-3]"; } default: return command_table[c_code]; } } smartmontools-7.0/atacmdnames.h0000644000175000010010000000123413336335341013641 00000000000000/* * 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://www.smartmontools.org * * Copyright (C) 2003-8 Philip Williams * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ATACMDNAMES_H_ #define ATACMDNAMES_H_ #define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 4760 2018-08-19 18:45:53Z chrfranke $\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 smartmontools-7.0/atacmds.cpp0000644000175000010010000025745213401001476013342 00000000000000/* * atacmds.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * Copyright (C) 2000 Andre Hedrick * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include #include "atacmds.h" #include "knowndrives.h" // get_default_attr_defs() #include "utility.h" #include "dev_ata_cmd_set.h" // for parsed_ata_device const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 4842 2018-12-02 16:07:26Z chrfranke $" ATACMDS_H_CVSID; // Print ATA debug messages? unsigned char ata_debugmode = 0; // Suppress serial number? // (also used in scsiprint.cpp) bool dont_print_serial_number = false; #define SMART_CYL_LOW 0x4F #define SMART_CYL_HI 0xC2 // SMART RETURN STATUS yields SMART_CYL_HI,SMART_CYL_LOW to indicate drive // is healthy and SRET_STATUS_HI_EXCEEDED,SRET_STATUS_MID_EXCEEDED to // indicate that a threshold exceeded condition has been detected. // Those values (byte pairs) are placed in ATA register "LBA 23:8". #define SRET_STATUS_HI_EXCEEDED 0x2C #define SRET_STATUS_MID_EXCEEDED 0xF4 // Get ID and increase flag of current pending or offline // uncorrectable attribute. unsigned char get_unc_attr_id(bool offline, const ata_vendor_attr_defs & defs, bool & increase) { unsigned char id = (!offline ? 197 : 198); const ata_vendor_attr_defs::entry & def = defs[id]; if (def.flags & ATTRFLAG_INCREASING) increase = true; // '-v 19[78],increasing' option else if (def.name.empty() || (id == 198 && def.name == "Offline_Scan_UNC_SectCt")) increase = false; // no or '-v 198,offlinescanuncsectorct' option else id = 0; // other '-v 19[78],...' option return id; } #if 0 // TODO: never used // 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; } } #endif // Table of raw print format names struct format_name_entry { const char * name; ata_attr_raw_format format; }; const format_name_entry format_names[] = { {"raw8" , RAWFMT_RAW8}, {"raw16" , RAWFMT_RAW16}, {"raw48" , RAWFMT_RAW48}, {"hex48" , RAWFMT_HEX48}, {"raw56" , RAWFMT_RAW56}, {"hex56" , RAWFMT_HEX56}, {"raw64" , RAWFMT_RAW64}, {"hex64" , RAWFMT_HEX64}, {"raw16(raw16)" , RAWFMT_RAW16_OPT_RAW16}, {"raw16(avg16)" , RAWFMT_RAW16_OPT_AVG16}, {"raw24(raw8)" , RAWFMT_RAW24_OPT_RAW8}, {"raw24/raw24" , RAWFMT_RAW24_DIV_RAW24}, {"raw24/raw32" , RAWFMT_RAW24_DIV_RAW32}, {"sec2hour" , RAWFMT_SEC2HOUR}, {"min2hour" , RAWFMT_MIN2HOUR}, {"halfmin2hour" , RAWFMT_HALFMIN2HOUR}, {"msec24hour32" , RAWFMT_MSEC24_HOUR32}, {"tempminmax" , RAWFMT_TEMPMINMAX}, {"temp10x" , RAWFMT_TEMP10X}, }; const unsigned num_format_names = sizeof(format_names)/sizeof(format_names[0]); // Table to map old to new '-v' option arguments const char * const map_old_vendor_opts[][2] = { { "9,halfminutes" , "9,halfmin2hour,Power_On_Half_Minutes"}, { "9,minutes" , "9,min2hour,Power_On_Minutes"}, { "9,seconds" , "9,sec2hour,Power_On_Seconds"}, { "9,temp" , "9,tempminmax,Temperature_Celsius"}, {"192,emergencyretractcyclect" , "192,raw48,Emerg_Retract_Cycle_Ct"}, {"193,loadunload" , "193,raw24/raw24"}, {"194,10xCelsius" , "194,temp10x,Temperature_Celsius_x10"}, {"194,unknown" , "194,raw48,Unknown_Attribute"}, {"197,increasing" , "197,raw48+,Total_Pending_Sectors"}, // '+' sets flag {"198,offlinescanuncsectorct" , "198,raw48,Offline_Scan_UNC_SectCt"}, // see also get_unc_attr_id() above {"198,increasing" , "198,raw48+,Total_Offl_Uncorrectabl"}, // '+' sets flag {"200,writeerrorcount" , "200,raw48,Write_Error_Count"}, {"201,detectedtacount" , "201,raw48,Detected_TA_Count"}, {"220,temp" , "220,tempminmax,Temperature_Celsius"}, }; const unsigned num_old_vendor_opts = sizeof(map_old_vendor_opts)/sizeof(map_old_vendor_opts[0]); // Parse vendor attribute display def (-v option). // Return false on error. bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs, ata_vendor_def_prior priority) { // Map old -> new options unsigned i; for (i = 0; i < num_old_vendor_opts; i++) { if (!strcmp(opt, map_old_vendor_opts[i][0])) { opt = map_old_vendor_opts[i][1]; break; } } // Parse option int len = strlen(opt); int id = 0, n1 = -1, n2 = -1; char fmtname[32+1], attrname[32+1], hddssd[3+1]; attrname[0] = hddssd[0] = 0; if (opt[0] == 'N') { // "N,format[,name]" if (!( sscanf(opt, "N,%32[^,]%n,%32[^,]%n", fmtname, &n1, attrname, &n2) >= 1 && (n1 == len || n2 == len))) return false; } else { // "id,format[+][,name[,HDD|SSD]]" int n3 = -1; if (!( sscanf(opt, "%d,%32[^,]%n,%32[^,]%n,%3[DHS]%n", &id, fmtname, &n1, attrname, &n2, hddssd, &n3) >= 2 && 1 <= id && id <= 255 && ( n1 == len || n2 == len // ",HDD|SSD" for DEFAULT settings only || (n3 == len && priority == PRIOR_DEFAULT)))) return false; } unsigned flags = 0; // For "-v 19[78],increasing" above if (fmtname[strlen(fmtname)-1] == '+') { fmtname[strlen(fmtname)-1] = 0; flags = ATTRFLAG_INCREASING; } // Split "format[:byteorder]" char byteorder[8+1] = ""; if (strchr(fmtname, ':')) { if (priority == PRIOR_DEFAULT) // TODO: Allow Byteorder in DEFAULT entry return false; n1 = n2 = -1; if (!( sscanf(fmtname, "%*[^:]%n:%8[012345rvwz]%n", &n1, byteorder, &n2) >= 1 && n2 == (int)strlen(fmtname))) return false; fmtname[n1] = 0; if (strchr(byteorder, 'v')) flags |= (ATTRFLAG_NO_NORMVAL|ATTRFLAG_NO_WORSTVAL); if (strchr(byteorder, 'w')) flags |= ATTRFLAG_NO_WORSTVAL; } // Find format name for (i = 0; ; i++) { if (i >= num_format_names) return false; // Not found if (!strcmp(fmtname, format_names[i].name)) break; } ata_attr_raw_format format = format_names[i].format; // 64-bit formats use the normalized and worst value bytes. if (!*byteorder && (format == RAWFMT_RAW64 || format == RAWFMT_HEX64)) flags |= (ATTRFLAG_NO_NORMVAL|ATTRFLAG_NO_WORSTVAL); // ",HDD|SSD" suffix for DEFAULT settings if (hddssd[0]) { if (!strcmp(hddssd, "HDD")) flags |= ATTRFLAG_HDD_ONLY; else if (!strcmp(hddssd, "SSD")) flags |= ATTRFLAG_SSD_ONLY; else return false; } if (!id) { // "N,format" -> set format for all entries for (i = 0; i < MAX_ATTRIBUTE_NUM; i++) { if (defs[i].priority >= priority) continue; if (attrname[0]) defs[i].name = attrname; defs[i].priority = priority; defs[i].raw_format = format; defs[i].flags = flags; snprintf(defs[i].byteorder, sizeof(defs[i].byteorder), "%s", byteorder); } } else if (defs[id].priority <= priority) { // "id,format[,name]" if (attrname[0]) defs[id].name = attrname; defs[id].raw_format = format; defs[id].priority = priority; defs[id].flags = flags; snprintf(defs[id].byteorder, sizeof(defs[id].byteorder), "%s", byteorder); } return true; } // Return a multiline string containing a list of valid arguments for // parse_attribute_def(). The strings are preceded by tabs and followed // (except for the last) by newlines. std::string create_vendor_attribute_arg_list() { std::string s; unsigned i; for (i = 0; i < num_format_names; i++) s += strprintf("%s\tN,%s[:012345rvwz][,ATTR_NAME]", (i>0 ? "\n" : ""), format_names[i].name); for (i = 0; i < num_old_vendor_opts; i++) s += strprintf("\n\t%s", map_old_vendor_opts[i][0]); return s; } // Parse firmwarebug def (-F option). // Return false on error. bool parse_firmwarebug_def(const char * opt, firmwarebug_defs & firmwarebugs) { if (!strcmp(opt, "none")) firmwarebugs.set(BUG_NONE); else if (!strcmp(opt, "nologdir")) firmwarebugs.set(BUG_NOLOGDIR); else if (!strcmp(opt, "samsung")) firmwarebugs.set(BUG_SAMSUNG); else if (!strcmp(opt, "samsung2")) firmwarebugs.set(BUG_SAMSUNG2); else if (!strcmp(opt, "samsung3")) firmwarebugs.set(BUG_SAMSUNG3); else if (!strcmp(opt, "xerrorlba")) firmwarebugs.set(BUG_XERRORLBA); else return false; return true; } // Return a string of valid argument words for parse_firmwarebug_def() const char * get_valid_firmwarebug_args() { return "none, nologdir, samsung, samsung2, samsung3, xerrorlba"; } // swap two bytes. Point to low address void swap2(char *location){ char tmp=*location; *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; } // When using the overloaded swapx() function with member of packed ATA structs, // it is required to pass a possibly unaligned pointer as argument. // Clang++ 4.0 prints -Waddress-of-packed-member warning in this case. // The SWAPV() macro below is a replacement which prevents the use of such pointers. template static T get_swapx_val(T x) { swapx(&x); return x; } #define SWAPV(x) ((x) = get_swapx_val(x)) // Invalidate serial number and WWN and adjust checksum in IDENTIFY data static void invalidate_serno(ata_identify_device * id) { unsigned char sum = 0; unsigned i; for (i = 0; i < sizeof(id->serial_no); i++) { sum += id->serial_no[i]; sum -= id->serial_no[i] = 'X'; } unsigned char * b = (unsigned char *)id; for (i = 2*108; i < 2*112; i++) { // words108-111: WWN sum += b[i]; sum -= b[i] = 0x00; } if (isbigendian()) SWAPV(id->words088_255[255-88]); if ((id->words088_255[255-88] & 0x00ff) == 0x00a5) id->words088_255[255-88] += sum << 8; if (isbigendian()) SWAPV(id->words088_255[255-88]); } static const char * const 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" }; static const char * preg(const ata_register & r, char (& buf)[8]) { if (!r.is_set()) //return "n/a "; return "...."; snprintf(buf, sizeof(buf), "0x%02x", r.val()); return buf; } static void print_regs(const char * prefix, const ata_in_regs & r, const char * suffix = "\n") { char bufs[7][8]; pout("%s FR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, CMD=%s%s", prefix, preg(r.features, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), preg(r.command, bufs[6]), suffix); } static void print_regs(const char * prefix, const ata_out_regs & r, const char * suffix = "\n") { char bufs[7][8]; pout("%sERR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, STS=%s%s", prefix, preg(r.error, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), preg(r.status, bufs[6]), suffix); } static void prettyprint(const unsigned char *p, const char *name){ pout("\n===== [%s] DATA START (BASE-16) =====\n", name); for (int i=0; i<512; i+=16, p+=16) #define P(n) (' ' <= p[n] && p[n] <= '~' ? (int)p[n] : '.') // print complete line to avoid slow tty output and extra lines in syslog. pout("%03d-%03d: %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x" " |%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c|" "%c", i, i+16-1, p[ 0], p[ 1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7], p[ 8], p[ 9], p[10], p[11], p[12], p[13], p[14], p[15], P( 0), P( 1), P( 2), P( 3), P( 4), P( 5), P( 6), P( 7), P( 8), P( 9), P(10), P(11), P(12), P(13), P(14), P(15), '\n'); #undef P 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(ata_device * device, smart_command_set command, int select, char *data){ // TODO: Rework old stuff below // 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 (ata_debugmode) { // 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: Device=%s Command=%s", device->get_dev_name(), 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 requested, pretty-print the input data structure if (ata_debugmode > 1 && sendsdata) //pout("REPORT-IOCTL: Device=%s Command=%s\n", device->get_dev_name(), commandstrings[command]); prettyprint((unsigned char *)data, commandstrings[command]); // now execute the command int retval = -1; { ata_cmd_in in; // Set common register values switch (command) { default: // SMART commands in.in_regs.command = ATA_SMART_CMD; in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW; break; case IDENTIFY: case PIDENTIFY: case CHECK_POWER_MODE: // Non SMART commands break; } // Set specific values switch (command) { case IDENTIFY: in.in_regs.command = ATA_IDENTIFY_DEVICE; in.set_data_in(data, 1); break; case PIDENTIFY: in.in_regs.command = ATA_IDENTIFY_PACKET_DEVICE; in.set_data_in(data, 1); break; case CHECK_POWER_MODE: in.in_regs.command = ATA_CHECK_POWER_MODE; in.out_needed.sector_count = true; // Powermode returned here break; case READ_VALUES: in.in_regs.features = ATA_SMART_READ_VALUES; in.set_data_in(data, 1); break; case READ_THRESHOLDS: in.in_regs.features = ATA_SMART_READ_THRESHOLDS; in.in_regs.lba_low = 1; // TODO: CORRECT ??? in.set_data_in(data, 1); break; case READ_LOG: in.in_regs.features = ATA_SMART_READ_LOG_SECTOR; in.in_regs.lba_low = select; in.set_data_in(data, 1); break; case WRITE_LOG: in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR; in.in_regs.lba_low = select; in.set_data_out(data, 1); break; case ENABLE: in.in_regs.features = ATA_SMART_ENABLE; in.in_regs.lba_low = 1; // TODO: CORRECT ??? break; case DISABLE: in.in_regs.features = ATA_SMART_DISABLE; in.in_regs.lba_low = 1; // TODO: CORRECT ??? break; case STATUS_CHECK: in.out_needed.lba_high = in.out_needed.lba_mid = true; // Status returned here /* FALLTHRU */ case STATUS: in.in_regs.features = ATA_SMART_STATUS; break; case AUTO_OFFLINE: in.in_regs.features = ATA_SMART_AUTO_OFFLINE; in.in_regs.sector_count = select; // Caution: Non-DATA command! break; case AUTOSAVE: in.in_regs.features = ATA_SMART_AUTOSAVE; in.in_regs.sector_count = select; // Caution: Non-DATA command! break; case IMMEDIATE_OFFLINE: in.in_regs.features = ATA_SMART_IMMEDIATE_OFFLINE; in.in_regs.lba_low = select; break; default: pout("Unrecognized command %d in smartcommandhandler()\n" "Please contact " PACKAGE_BUGREPORT "\n", command); device->set_err(ENOSYS); return -1; } if (ata_debugmode) print_regs(" Input: ", in.in_regs, (in.direction==ata_cmd_in::data_in ? " IN\n": in.direction==ata_cmd_in::data_out ? " OUT\n":"\n")); ata_cmd_out out; int64_t start_usec = -1; if (ata_debugmode) start_usec = smi()->get_timer_usec(); bool ok = device->ata_pass_through(in, out); if (start_usec >= 0) { int64_t duration_usec = smi()->get_timer_usec() - start_usec; if (duration_usec >= 500) pout(" [Duration: %.3fs]\n", duration_usec / 1000000.0); } if (ata_debugmode && out.out_regs.is_set()) print_regs(" Output: ", out.out_regs); if (ok) switch (command) { default: retval = 0; break; case CHECK_POWER_MODE: if (out.out_regs.sector_count.is_set()) { data[0] = out.out_regs.sector_count; retval = 0; } else { pout("CHECK POWER MODE: incomplete response, ATA output registers missing\n"); device->set_err(ENOSYS); retval = -1; } break; case STATUS_CHECK: // Cyl low and Cyl high unchanged means "Good SMART status" if ((out.out_regs.lba_high == SMART_CYL_HI) && (out.out_regs.lba_mid == SMART_CYL_LOW)) retval = 0; // These values mean "Bad SMART status" else if ((out.out_regs.lba_high == SRET_STATUS_HI_EXCEEDED) && (out.out_regs.lba_mid == SRET_STATUS_MID_EXCEEDED)) retval = 1; else if (out.out_regs.lba_mid == SMART_CYL_LOW) { retval = 0; if (ata_debugmode) pout("SMART STATUS RETURN: half healthy response sequence, " "probable SAT/USB truncation\n"); } else if (out.out_regs.lba_mid == SRET_STATUS_MID_EXCEEDED) { retval = 1; if (ata_debugmode) pout("SMART STATUS RETURN: half unhealthy response sequence, " "probable SAT/USB truncation\n"); } else if (!out.out_regs.is_set()) { device->set_err(ENOSYS, "Incomplete response, ATA output registers missing"); retval = -1; } else { // We haven't gotten output that makes sense; print out some debugging info pout("SMART Status command failed\n"); pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE); pout("Register values returned from SMART Status command are:\n"); print_regs(" ", out.out_regs); device->set_err(ENOSYS, "Invalid ATA output register values"); retval = -1; } break; } } // If requested, invalidate serial number before any printing is done if ((command == IDENTIFY || command == PIDENTIFY) && !retval && dont_print_serial_number) invalidate_serno( reinterpret_cast(data) ); // If reporting is enabled, say what output was produced by the command if (ata_debugmode) { if (device->get_errno()) pout("REPORT-IOCTL: Device=%s Command=%s returned %d errno=%d [%s]\n", device->get_dev_name(), commandstrings[command], retval, device->get_errno(), device->get_errmsg()); else pout("REPORT-IOCTL: Device=%s Command=%s returned %d\n", device->get_dev_name(), commandstrings[command], retval); // if requested, pretty-print the output data structure if (ata_debugmode > 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; } // Get capacity and sector sizes from IDENTIFY data void ata_get_size_info(const ata_identify_device * id, ata_size_info & sizes) { sizes.sectors = sizes.capacity = 0; sizes.log_sector_size = sizes.phy_sector_size = 0; sizes.log_sector_offset = 0; // Return if no LBA support if (!(id->words047_079[49-47] & 0x0200)) return; // Determine 28-bit LBA capacity unsigned lba28 = (unsigned)id->words047_079[61-47] << 16 | (unsigned)id->words047_079[60-47] ; // Determine 48-bit LBA capacity if supported uint64_t lba48 = 0; if ((id->command_set_2 & 0xc400) == 0x4400) lba48 = (uint64_t)id->words088_255[103-88] << 48 | (uint64_t)id->words088_255[102-88] << 32 | (uint64_t)id->words088_255[101-88] << 16 | (uint64_t)id->words088_255[100-88] ; // Return if capacity unknown (ATAPI CD/DVD) if (!(lba28 || lba48)) return; // Determine sector sizes sizes.log_sector_size = sizes.phy_sector_size = 512; unsigned short word106 = id->words088_255[106-88]; if ((word106 & 0xc000) == 0x4000) { // Long Logical/Physical Sectors (LLS/LPS) ? if (word106 & 0x1000) // Logical sector size is specified in 16-bit words sizes.log_sector_size = sizes.phy_sector_size = ((id->words088_255[118-88] << 16) | id->words088_255[117-88]) << 1; if (word106 & 0x2000) // Physical sector size is multiple of logical sector size sizes.phy_sector_size <<= (word106 & 0x0f); unsigned short word209 = id->words088_255[209-88]; if ((word209 & 0xc000) == 0x4000) sizes.log_sector_offset = (word209 & 0x3fff) * sizes.log_sector_size; } // Some early 4KiB LLS disks (Samsung N3U-3) return bogus lba28 value if (lba48 >= lba28 || (lba48 && sizes.log_sector_size > 512)) sizes.sectors = lba48; else sizes.sectors = lba28; sizes.capacity = sizes.sectors * sizes.log_sector_size; } // 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(const void * data) { unsigned char sum = 0; for (int i = 0; i < 512; i++) sum += ((const unsigned char *)data)[i]; return sum; } // Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents // bytes. static void swapbytes(char * out, const char * in, size_t n) { for (size_t 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. static void trim(char * out, const char * in) { // Find the first non-space character (maybe none). int first = -1; int i; 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--) ; int last = i; strncpy(out, in+first, last-first+1); out[last-first+1] = '\0'; } // Convenience function for formatting strings from ata_identify_device void ata_format_id_string(char * out, const unsigned char * in, int n) { char tmp[65]; n = n > 64 ? 64 : n; swapbytes(tmp, (const char *)in, n); tmp[n] = '\0'; trim(out, tmp); } // 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(ata_device * device) { unsigned char result; if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result))) return -1; return (int)result; } // Issue a no-data ATA command with optional sector count register value bool ata_nodata_command(ata_device * device, unsigned char command, int sector_count /* = -1 */) { ata_cmd_in in; in.in_regs.command = command; if (sector_count >= 0) in.in_regs.sector_count = sector_count; return device->ata_pass_through(in); } // Issue SET FEATURES command with optional sector count register value bool ata_set_features(ata_device * device, unsigned char features, int sector_count /* = -1 */) { ata_cmd_in in; in.in_regs.command = ATA_SET_FEATURES; in.in_regs.features = features; if (sector_count >= 0) in.in_regs.sector_count = sector_count; return device->ata_pass_through(in); } // 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 ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id, unsigned char * raw_buf /* = 0 */) { unsigned short *rawshort=(unsigned short *)buf; unsigned char *rawbyte =(unsigned char *)buf; // See if device responds either to IDENTIFY DEVICE or IDENTIFY // PACKET DEVICE bool packet = false; if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){ if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){ return -1; } packet = true; } if (fix_swapped_id) { // Swap ID strings unsigned i; for (i = 0; i < sizeof(buf->serial_no)-1; i += 2) swap2((char *)(buf->serial_no+i)); for (i = 0; i < sizeof(buf->fw_rev)-1; i += 2) swap2((char *)(buf->fw_rev+i)); for (i = 0; i < sizeof(buf->model)-1; i += 2) swap2((char *)(buf->model+i)); } // If requested, save raw data before endianness adjustments if (raw_buf) memcpy(raw_buf, buf, sizeof(*buf)); // if machine is big-endian, swap byte order as needed if (isbigendian()){ // swap various capability words that are needed unsigned i; for (i=0; i<33; i++) swap2((char *)(buf->words047_079+i)); for (i=80; i<=87; i++) swap2((char *)(rawshort+i)); for (i=0; i<168; i++) swap2((char *)(buf->words088_255+i)); } // If there is a checksum there, validate it if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte)) checksumwarning("Drive Identity Structure"); // AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) // T13/1699-D Revision 6a (Final Draft), September 6, 2008. // Sections 7.16.7 and 7.17.6: // // Word 0 of IDENTIFY DEVICE data: // Bit 15 = 0 : ATA device // // Word 0 of IDENTIFY PACKET DEVICE data: // Bits 15:14 = 10b : ATAPI device // Bits 15:14 = 11b : Reserved // Bits 12:8 : Device type (SPC-4, e.g 0x05 = CD/DVD) // CF+ and CompactFlash Specification Revision 4.0, May 24, 2006. // Section 6.2.1.6: // // Word 0 of IDENTIFY DEVICE data: // 848Ah = Signature for CompactFlash Storage Card // 044Ah = Alternate value turns on ATA device while preserving all retired bits // 0040h = Alternate value turns on ATA device while zeroing all retired bits // Assume ATA if IDENTIFY DEVICE returns CompactFlash Signature if (!packet && rawbyte[1] == 0x84 && rawbyte[0] == 0x8a) return 0; // If this is a PACKET DEVICE, return device type if (rawbyte[1] & 0x80) return 1+(rawbyte[1] & 0x1f); // Not a PACKET DEVICE return 0; } // Get World Wide Name (WWN) fields. // Return NAA field or -1 if WWN is unsupported. // Table 34 of T13/1699-D Revision 6a (ATA8-ACS), September 6, 2008. // (WWN was introduced in ATA/ATAPI-7 and is mandatory since ATA8-ACS Revision 3b) int ata_get_wwn(const ata_identify_device * id, unsigned & oui, uint64_t & unique_id) { // Don't use word 84 to be compatible with some older ATA-7 disks unsigned short word087 = id->csf_default; if ((word087 & 0xc100) != 0x4100) return -1; // word not valid or WWN support bit 8 not set unsigned short word108 = id->words088_255[108-88]; unsigned short word109 = id->words088_255[109-88]; unsigned short word110 = id->words088_255[110-88]; unsigned short word111 = id->words088_255[111-88]; oui = ((word108 & 0x0fff) << 12) | (word109 >> 4); unique_id = ((uint64_t)(word109 & 0xf) << 32) | (unsigned)((word110 << 16) | word111); return (word108 >> 12); } // Get nominal media rotation rate. // Returns: 0 = not reported, 1 = SSD, >1 = HDD rpm, < 0 = -(Unknown value) int ata_get_rotation_rate(const ata_identify_device * id) { // Table 37 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 // Table A.31 of T13/2161-D (ACS-3) Revision 3b, August 25, 2012 unsigned short word217 = id->words088_255[217-88]; if (word217 == 0x0000 || word217 == 0xffff) return 0; else if (word217 == 0x0001) return 1; else if (word217 > 0x0400) return word217; else return -(int)word217; } // returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell int ataSmartSupport(const ata_identify_device * drive) { unsigned short word82=drive->command_set_1; unsigned short word83=drive->command_set_2; // check if words 82/83 contain valid info if ((word83>>14) == 0x01) // return value of SMART support bit return word82 & 0x0001; // since we can're rely on word 82, we don't know if SMART supported return -1; } // returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell int ataIsSmartEnabled(const 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(ata_device * device, struct ata_smart_values *data){ if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){ return -1; } // compute checksum if (checksum(data)) checksumwarning("SMART Attribute Data Structure"); // swap endian order if needed if (isbigendian()){ int i; swap2((char *)&(data->revnumber)); swap2((char *)&(data->total_time_to_complete_off_line)); swap2((char *)&(data->smart_capability)); SWAPV(data->extend_test_completion_time_w); 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 static void fixsamsungselftestlog(ata_smart_selftestlog * data) { // 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 (int i = 0; i < 21; i++) swap2((char *)&(data->selftest_struct[i].selftestnumber)); return; } // Reads the Self Test Log (log #6) int ataReadSelfTestLog (ata_device * device, ata_smart_selftestlog * data, firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){ return -1; } // compute its checksum, and issue a warning if needed if (checksum(data)) checksumwarning("SMART Self-Test Log Structure"); // fix firmware bugs in self-test log if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungselftestlog(data); // swap 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; } // Print checksum warning for multi sector log static void check_multi_sector_sum(const void * data, unsigned nsectors, const char * msg) { unsigned errs = 0; for (unsigned i = 0; i < nsectors; i++) { if (checksum((const unsigned char *)data + i*512)) errs++; } if (errs > 0) { if (nsectors == 1) checksumwarning(msg); else checksumwarning(strprintf("%s (%u/%u)", msg, errs, nsectors).c_str()); } } // Read SMART Extended Self-test Log bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log, unsigned nsectors) { if (!ataReadLogExt(device, 0x07, 0x00, 0, log, nsectors)) return false; check_multi_sector_sum(log, nsectors, "SMART Extended Self-test Log Structure"); if (isbigendian()) { SWAPV(log->log_desc_index); for (unsigned i = 0; i < nsectors; i++) { for (unsigned j = 0; j < 19; j++) SWAPV(log->log_descs[i].timestamp); } } return true; } // Write GP Log page(s) bool ataWriteLogExt(ata_device * device, unsigned char logaddr, unsigned page, void * data, unsigned nsectors) { ata_cmd_in in; in.in_regs.command = ATA_WRITE_LOG_EXT; in.set_data_out(data, nsectors); in.in_regs.lba_low = logaddr; in.in_regs.lba_mid_16 = page; in.set_data_out(data, nsectors); ata_cmd_out out; if (!device->ata_pass_through(in, out)) { // TODO: Debug output if (nsectors <= 1) { pout("ATA_WRITE_LOG_EXT (addr=0x%02x, page=%u, n=%u) failed: %s\n", logaddr, page, nsectors, device->get_errmsg()); return false; } // Recurse to retry with single sectors, // multi-sector reads may not be supported by ioctl. for (unsigned i = 0; i < nsectors; i++) { if (!ataWriteLogExt(device, logaddr, page + i, (char *)data + 512*i, 1)) return false; } } return true; } // Read GP Log page(s) bool ataReadLogExt(ata_device * device, unsigned char logaddr, unsigned char features, unsigned page, void * data, unsigned nsectors) { ata_cmd_in in; in.in_regs.command = ATA_READ_LOG_EXT; in.in_regs.features = features; // log specific in.set_data_in_48bit(data, nsectors); in.in_regs.lba_low = logaddr; in.in_regs.lba_mid_16 = page; if (!device->ata_pass_through(in)) { // TODO: Debug output if (nsectors <= 1) { pout("ATA_READ_LOG_EXT (addr=0x%02x:0x%02x, page=%u, n=%u) failed: %s\n", logaddr, features, page, nsectors, device->get_errmsg()); return false; } // Recurse to retry with single sectors, // multi-sector reads may not be supported by ioctl. for (unsigned i = 0; i < nsectors; i++) { if (!ataReadLogExt(device, logaddr, features, page + i, (char *)data + 512*i, 1)) return false; } } return true; } // Read SMART Log page(s) bool ataReadSmartLog(ata_device * device, unsigned char logaddr, void * data, unsigned nsectors) { ata_cmd_in in; in.in_regs.command = ATA_SMART_CMD; in.in_regs.features = ATA_SMART_READ_LOG_SECTOR; in.set_data_in(data, nsectors); in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW; in.in_regs.lba_low = logaddr; if (!device->ata_pass_through(in)) { // TODO: Debug output pout("ATA_SMART_READ_LOG failed: %s\n", device->get_errmsg()); return false; } return true; } // Reads the SMART or GPL Log Directory (log #0) int ataReadLogDirectory(ata_device * device, ata_smart_log_directory * data, bool gpl) { if (!gpl) { // SMART Log directory if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)) return -1; } else { // GP Log directory if (!ataReadLogExt(device, 0x00, 0x00, 0, data, 1)) return -1; } // swap endian order if needed if (isbigendian()) SWAPV(data->logversion); return 0; } // Reads the selective self-test log (log #9) int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data){ // get data from device if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){ return -1; } // compute its checksum, and issue a warning if needed if (checksum(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)); } return 0; } // Writes the selective self-test log (log #9) int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_args & args, const ata_smart_values * sv, uint64_t num_sectors, const ata_selective_selftest_args * prev_args) { // Disk size must be known if (!num_sectors) { pout("Disk size is unknown, unable to check selective self-test spans\n"); return -1; } // Read log struct ata_selective_self_test_log sstlog, *data=&sstlog; unsigned char *ptr=(unsigned char *)data; if (ataReadSelectiveSelfTestLog(device, data)) { pout("SMART Read Selective Self-test Log failed: %s\n", device->get_errmsg()); pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n"); return -1; } // Set log version data->logversion = 1; // 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("SMART Selective or other Self-test in progress\n"); return -4; } // Set start/end values based on old spans for special -t select,... options int i; for (i = 0; i < args.num_spans; i++) { int mode = args.span[i].mode; uint64_t start = args.span[i].start; uint64_t end = args.span[i].end; if (mode == SEL_CONT) {// redo or next depending on last test status switch (sv->self_test_exec_status >> 4) { case 1: case 2: // Aborted/Interrupted by host pout("Continue Selective Self-Test: Redo last span\n"); mode = SEL_REDO; break; default: // All others pout("Continue Selective Self-Test: Start next span\n"); mode = SEL_NEXT; break; } } if ( (mode == SEL_REDO || mode == SEL_NEXT) && prev_args && i < prev_args->num_spans && !data->span[i].start && !data->span[i].end) { // Some drives do not preserve the selective self-test log across // power-cyles. If old span on drive is cleared use span provided // by caller. This is used by smartd (first span only). data->span[i].start = prev_args->span[i].start; data->span[i].end = prev_args->span[i].end; } switch (mode) { case SEL_RANGE: // -t select,START-END break; case SEL_REDO: // -t select,redo... => Redo current start = data->span[i].start; if (end > 0) { // -t select,redo+SIZE end--; end += start; // [oldstart, oldstart+SIZE) } else // -t select,redo end = data->span[i].end; // [oldstart, oldend] break; case SEL_NEXT: // -t select,next... => Do next if (data->span[i].end == 0) { start = end = 0; break; // skip empty spans } start = data->span[i].end + 1; if (start >= num_sectors) start = 0; // wrap around if (end > 0) { // -t select,next+SIZE end--; end += start; // (oldend, oldend+SIZE] } else { // -t select,next uint64_t oldsize = data->span[i].end - data->span[i].start + 1; end = start + oldsize - 1; // (oldend, oldend+oldsize] if (end >= num_sectors) { // Adjust size to allow round-robin testing without future size decrease uint64_t spans = (num_sectors + oldsize-1) / oldsize; uint64_t newsize = (num_sectors + spans-1) / spans; uint64_t newstart = num_sectors - newsize, newend = num_sectors - 1; pout("Span %d changed from %" PRIu64 "-%" PRIu64 " (%" PRIu64 " sectors)\n", i, start, end, oldsize); pout(" to %" PRIu64 "-%" PRIu64 " (%" PRIu64 " sectors) (%" PRIu64 " spans)\n", newstart, newend, newsize, spans); start = newstart; end = newend; } } break; default: pout("ataWriteSelectiveSelfTestLog: Invalid mode %d\n", mode); return -1; } // Range check if (start < num_sectors && num_sectors <= end) { if (end != ~(uint64_t)0) // -t select,N-max pout("Size of self-test span %d decreased according to disk size\n", i); end = num_sectors - 1; } if (!(start <= end && end < num_sectors)) { pout("Invalid selective self-test span %d: %" PRIu64 "-%" PRIu64 " (%" PRIu64 " sectors)\n", i, start, end, num_sectors); return -1; } // Return the actual mode and range to caller. args.span[i].mode = mode; args.span[i].start = start; args.span[i].end = end; } // Clear spans for (i=0; i<5; i++) memset(data->span+i, 0, sizeof(struct test_span)); // Set spans for testing for (i = 0; i < args.num_spans; i++){ data->span[i].start = args.span[i].start; data->span[i].end = args.span[i].end; } // host must initialize to zero before initiating selective self-test data->currentlba=0; data->currentspan=0; // Perform off-line scan after selective test? if (args.scan_after_select == 1) // NO data->flags &= ~SELECTIVE_FLAG_DOSCAN; else if (args.scan_after_select == 2) // 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 (args.pending_time) data->pendingtime = (unsigned short)(args.pending_time-1); // Set checksum to zero, then compute checksum data->checksum=0; unsigned char cksum=0; for (i=0; i<512; i++) cksum+=ptr[i]; cksum=~cksum; cksum+=1; data->checksum=cksum; // swap endian order if needed if (isbigendian()){ swap2((char *)&(data->logversion)); for (int b = 0; b < 5; b++) { swap8((char *)&(data->span[b].start)); swap8((char *)&(data->span[b].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)){ pout("Write Selective Self-test Log failed: %s\n", device->get_errmsg()); return -3; } return 0; } // This corrects some quantities that are byte reversed in the SMART // ATA ERROR LOG. static void fixsamsungerrorlog(ata_smart_errorlog * data) { // 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 (int i = 0; i < 5; i++){ // step through 5 command data structures for (int 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 static void fixsamsungerrorlog2(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 (ata_device * device, ata_smart_errorlog *data, firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){ return -1; } // compute its checksum, and issue a warning if needed if (checksum(data)) checksumwarning("SMART ATA Error Log Structure"); // Some disks have the byte order reversed in some SMART Summary // Error log entries if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungerrorlog(data); else if (firmwarebugs.is_set(BUG_SAMSUNG2)) fixsamsungerrorlog2(data); // swap endian order if needed 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; } // Fix LBA byte ordering of Extended Comprehensive Error Log // if little endian instead of ATA register ordering is provided template static inline void fix_exterrlog_lba_cmd(T & cmd) { T org = cmd; cmd.lba_mid_register_hi = org.lba_high_register; cmd.lba_low_register_hi = org.lba_mid_register_hi; cmd.lba_high_register = org.lba_mid_register; cmd.lba_mid_register = org.lba_low_register_hi; } static void fix_exterrlog_lba(ata_smart_exterrlog * log, unsigned nsectors) { for (unsigned i = 0; i < nsectors; i++) { for (int ei = 0; ei < 4; ei++) { ata_smart_exterrlog_error_log & entry = log[i].error_logs[ei]; fix_exterrlog_lba_cmd(entry.error); for (int ci = 0; ci < 5; ci++) fix_exterrlog_lba_cmd(entry.commands[ci]); } } } // Read Extended Comprehensive Error Log bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, unsigned page, unsigned nsectors, firmwarebug_defs firmwarebugs) { if (!ataReadLogExt(device, 0x03, 0x00, page, log, nsectors)) return false; check_multi_sector_sum(log, nsectors, "SMART Extended Comprehensive Error Log Structure"); if (isbigendian()) { SWAPV(log->device_error_count); SWAPV(log->error_log_index); for (unsigned i = 0; i < nsectors; i++) { for (unsigned j = 0; j < 4; j++) { for (unsigned k = 0; k < 5; k++) SWAPV(log[i].error_logs[j].commands[k].timestamp); SWAPV(log[i].error_logs[j].error.timestamp); } } } if (firmwarebugs.is_set(BUG_XERRORLBA)) fix_exterrlog_lba(log, nsectors); return true; } int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt *data){ // get data from device if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){ return -1; } // compute its checksum, and issue a warning if needed if (checksum(data)) checksumwarning("SMART Attribute Thresholds Structure"); // swap endian order if needed if (isbigendian()) swap2((char *)&(data->revnumber)); return 0; } int ataEnableSmart (ata_device * device ){ if (smartcommandhandler(device, ENABLE, 0, NULL)){ return -1; } return 0; } int ataDisableSmart (ata_device * device ){ if (smartcommandhandler(device, DISABLE, 0, NULL)){ return -1; } return 0; } int ataEnableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){ return -1; } return 0; } int ataDisableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){ 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 (ata_device * device){ /* timer hard coded to 4 hours */ if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ return -1; } return 0; } // Another Obsolete Command. See comments directly above, associated // with the corresponding Enable command. int ataDisableAutoOffline (ata_device * device){ if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){ 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(ata_device * 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(ata_device * 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. // TODO: Move to ataprint.cpp ? int ataSmartTest(ata_device * device, int testtype, bool force, const ata_selective_selftest_args & selargs, const ata_smart_values * sv, uint64_t num_sectors) { char cmdmsg[128]; const char *type, *captive; int 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 = 0; // Check whether another test is already running if (type && (sv->self_test_exec_status >> 4) == 0xf) { if (!force) { pout("Can't start self-test without aborting current test (%d0%% remaining),\n" "%srun 'smartctl -X' to abort test.\n", sv->self_test_exec_status & 0x0f, (!select ? "add '-t force' option to override, or " : "")); return -1; } } else force = false; // If doing a selective self-test, first use WRITE_LOG to write the // selective self-test log. ata_selective_selftest_args selargs_io = selargs; // filled with info about actual spans if (select && (retval = ataWriteSelectiveSelfTestLog(device, selargs_io, sv, num_sectors))) { 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) snprintf(cmdmsg, sizeof(cmdmsg), "Abort SMART off-line mode self-test routine"); else if (!type) snprintf(cmdmsg, sizeof(cmdmsg), "SMART EXECUTE OFF-LINE IMMEDIATE subcommand 0x%02x", testtype); else snprintf(cmdmsg, sizeof(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 < selargs_io.num_spans; i++) pout(" %d %20" PRId64 " %20" PRId64 "\n", i, selargs_io.span[i].start, selargs_io.span[i].end); } // Now send the command to test if (smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL)) { if (!(cap && device->get_errno() == EIO)) { pout("Command \"%s\" failed: %s\n", cmdmsg, device->get_errmsg()); return -1; } } // Since the command succeeded, tell user if (testtype==ABORT_SELF_TEST) pout("Self-testing aborted!\n"); else { pout("Drive command \"%s\" successful.\n", cmdmsg); if (type) pout("Testing has begun%s.\n", (force ? " (previous test aborted)" : "")); } return 0; } /* Test Time Functions */ int TestTime(const 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: if (data->extend_test_completion_time_b == 0xff && data->extend_test_completion_time_w != 0x0000 && data->extend_test_completion_time_w != 0xffff) return data->extend_test_completion_time_w; // ATA-8 else return data->extend_test_completion_time_b; 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). bool isSmartErrorLogCapable(const ata_smart_values * data, const 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 true; if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01)) return true; // 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. bool isSmartTestLogCapable(const ata_smart_values * data, const 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 true; if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02)) return true; // otherwise we'll use the poorly documented capability bit return !!(data->errorlog_capability & 0x01); } bool isGeneralPurposeLoggingCapable(const 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 false; } // Get attribute state ata_attr_state ata_get_attr_state(const ata_smart_attribute & attr, int attridx, const ata_smart_threshold_entry * thresholds, const ata_vendor_attr_defs & defs, unsigned char * threshval /* = 0 */) { if (!attr.id) return ATTRSTATE_NON_EXISTING; // Normalized values (current,worst,threshold) not valid // if specified by '-v' option. // (Some SSD disks uses these bytes to store raw value). if (defs[attr.id].flags & ATTRFLAG_NO_NORMVAL) return ATTRSTATE_NO_NORMVAL; // Normally threshold is at same index as attribute int i = attridx; if (thresholds[i].id != attr.id) { // Find threshold id in table for (i = 0; thresholds[i].id != attr.id; ) { if (++i >= NUMBER_ATA_SMART_ATTRIBUTES) // Threshold id missing or thresholds cannot be read return ATTRSTATE_NO_THRESHOLD; } } unsigned char threshold = thresholds[i].threshold; // Return threshold if requested if (threshval) *threshval = threshold; // Don't report a failed attribute if its threshold is 0. // ATA-3 (X3T13/2008D Revision 7b) declares 0x00 as the "always passing" // threshold (Later ATA versions declare all thresholds as "obsolete"). // In practice, threshold value 0 is often used for usage attributes. if (!threshold) return ATTRSTATE_OK; // Failed now if current value is below threshold if (attr.current <= threshold) return ATTRSTATE_FAILED_NOW; // Failed in the past if worst value is below threshold if (!(defs[attr.id].flags & ATTRFLAG_NO_WORSTVAL) && attr.worst <= threshold) return ATTRSTATE_FAILED_PAST; return ATTRSTATE_OK; } // Get attribute raw value. uint64_t ata_get_attr_raw_value(const ata_smart_attribute & attr, const ata_vendor_attr_defs & defs) { const ata_vendor_attr_defs::entry & def = defs[attr.id]; // TODO: Allow Byteorder in DEFAULT entry // Use default byteorder if not specified const char * byteorder = def.byteorder; if (!*byteorder) { switch (def.raw_format) { case RAWFMT_RAW64: case RAWFMT_HEX64: byteorder = "543210wv"; break; case RAWFMT_RAW56: case RAWFMT_HEX56: case RAWFMT_RAW24_DIV_RAW32: case RAWFMT_MSEC24_HOUR32: byteorder = "r543210"; break; default: byteorder = "543210"; break; } } // Build 64-bit value from selected bytes uint64_t rawvalue = 0; for (int i = 0; byteorder[i]; i++) { unsigned char b; switch (byteorder[i]) { case '0': b = attr.raw[0]; break; case '1': b = attr.raw[1]; break; case '2': b = attr.raw[2]; break; case '3': b = attr.raw[3]; break; case '4': b = attr.raw[4]; break; case '5': b = attr.raw[5]; break; case 'r': b = attr.reserv; break; case 'v': b = attr.current; break; case 'w': b = attr.worst; break; default : b = 0; break; } rawvalue <<= 8; rawvalue |= b; } return rawvalue; } // Helper functions for RAWFMT_TEMPMINMAX static inline int check_temp_word(unsigned word) { if (word <= 0x7f) return 0x11; // >= 0, signed byte or word if (word <= 0xff) return 0x01; // < 0, signed byte if (0xff80 <= word) return 0x10; // < 0, signed word return 0x00; } static bool check_temp_range(int t, unsigned char ut1, unsigned char ut2, int & lo, int & hi) { int t1 = (signed char)ut1, t2 = (signed char)ut2; if (t1 > t2) { int tx = t1; t1 = t2; t2 = tx; } if ( -60 <= t1 && t1 <= t && t <= t2 && t2 <= 120 && !(t1 == -1 && t2 <= 0) ) { lo = t1; hi = t2; return true; } return false; } // Format attribute raw value. std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, const ata_vendor_attr_defs & defs) { // Get 48 bit or 64 bit raw value uint64_t rawvalue = ata_get_attr_raw_value(attr, defs); // Split into bytes and words unsigned char raw[6]; raw[0] = (unsigned char) rawvalue; raw[1] = (unsigned char)(rawvalue >> 8); raw[2] = (unsigned char)(rawvalue >> 16); raw[3] = (unsigned char)(rawvalue >> 24); raw[4] = (unsigned char)(rawvalue >> 32); raw[5] = (unsigned char)(rawvalue >> 40); unsigned word[3]; word[0] = raw[0] | (raw[1] << 8); word[1] = raw[2] | (raw[3] << 8); word[2] = raw[4] | (raw[5] << 8); // Get print format ata_attr_raw_format format = defs[attr.id].raw_format; if (format == RAWFMT_DEFAULT) { // Get format from DEFAULT entry format = get_default_attr_defs()[attr.id].raw_format; if (format == RAWFMT_DEFAULT) // Unknown Attribute format = RAWFMT_RAW48; } // Print std::string s; switch (format) { case RAWFMT_RAW8: s = strprintf("%d %d %d %d %d %d", raw[5], raw[4], raw[3], raw[2], raw[1], raw[0]); break; case RAWFMT_RAW16: s = strprintf("%u %u %u", word[2], word[1], word[0]); break; case RAWFMT_RAW48: case RAWFMT_RAW56: case RAWFMT_RAW64: s = strprintf("%" PRIu64, rawvalue); break; case RAWFMT_HEX48: s = strprintf("0x%012" PRIx64, rawvalue); break; case RAWFMT_HEX56: s = strprintf("0x%014" PRIx64, rawvalue); break; case RAWFMT_HEX64: s = strprintf("0x%016" PRIx64, rawvalue); break; case RAWFMT_RAW16_OPT_RAW16: s = strprintf("%u", word[0]); if (word[1] || word[2]) s += strprintf(" (%u %u)", word[2], word[1]); break; case RAWFMT_RAW16_OPT_AVG16: s = strprintf("%u", word[0]); if (word[1]) s += strprintf(" (Average %u)", word[1]); break; case RAWFMT_RAW24_OPT_RAW8: s = strprintf("%u", (unsigned)(rawvalue & 0x00ffffffULL)); if (raw[3] || raw[4] || raw[5]) s += strprintf(" (%d %d %d)", raw[5], raw[4], raw[3]); break; case RAWFMT_RAW24_DIV_RAW24: s = strprintf("%u/%u", (unsigned)(rawvalue >> 24), (unsigned)(rawvalue & 0x00ffffffULL)); break; case RAWFMT_RAW24_DIV_RAW32: s = strprintf("%u/%u", (unsigned)(rawvalue >> 32), (unsigned)(rawvalue & 0xffffffffULL)); break; case RAWFMT_MIN2HOUR: { // minutes int64_t temp = word[0]+(word[1]<<16); int64_t tmp1 = temp/60; int64_t tmp2 = temp%60; s = strprintf("%" PRIu64 "h+%02" PRIu64 "m", tmp1, tmp2); if (word[2]) s += strprintf(" (%u)", word[2]); } break; case RAWFMT_SEC2HOUR: { // seconds int64_t hours = rawvalue/3600; int64_t minutes = (rawvalue-3600*hours)/60; int64_t seconds = rawvalue%60; s = strprintf("%" PRIu64 "h+%02" PRIu64 "m+%02" PRIu64 "s", hours, minutes, seconds); } break; case RAWFMT_HALFMIN2HOUR: { // 30-second counter int64_t hours = rawvalue/120; int64_t minutes = (rawvalue-120*hours)/2; s += strprintf("%" PRIu64 "h+%02" PRIu64 "m", hours, minutes); } break; case RAWFMT_MSEC24_HOUR32: { // hours + milliseconds unsigned hours = (unsigned)(rawvalue & 0xffffffffULL); unsigned milliseconds = (unsigned)(rawvalue >> 32); unsigned seconds = milliseconds / 1000; s = strprintf("%uh+%02um+%02u.%03us", hours, seconds / 60, seconds % 60, milliseconds % 1000); } break; case RAWFMT_TEMPMINMAX: // Temperature { // Search for possible min/max values // [5][4][3][2][1][0] raw[] // [ 2 ] [ 1 ] [ 0 ] word[] // xx HH xx LL xx TT (Hitachi/HGST) // xx LL xx HH xx TT (Kingston SSDs) // 00 00 HH LL xx TT (Maxtor, Samsung, Seagate, Toshiba) // 00 00 00 HH LL TT (WDC) // CC CC HH LL xx TT (WDC, CCCC=over temperature count) // (xx = 00/ff, possibly sign extension of lower byte) int t = (signed char)raw[0]; int lo = 0, hi = 0; int tformat; int ctw0 = check_temp_word(word[0]); if (!word[2]) { if (!word[1] && ctw0) // 00 00 00 00 xx TT tformat = 0; else if (ctw0 && check_temp_range(t, raw[2], raw[3], lo, hi)) // 00 00 HL LH xx TT tformat = 1; else if (!raw[3] && check_temp_range(t, raw[1], raw[2], lo, hi)) // 00 00 00 HL LH TT tformat = 2; else tformat = -1; } else if (ctw0) { if ( (ctw0 & check_temp_word(word[1]) & check_temp_word(word[2])) != 0x00 && check_temp_range(t, raw[2], raw[4], lo, hi) ) // xx HL xx LH xx TT tformat = 3; else if ( word[2] < 0x7fff && check_temp_range(t, raw[2], raw[3], lo, hi) && hi >= 40 ) // CC CC HL LH xx TT tformat = 4; else tformat = -2; } else tformat = -3; switch (tformat) { case 0: s = strprintf("%d", t); break; case 1: case 2: case 3: s = strprintf("%d (Min/Max %d/%d)", t, lo, hi); break; case 4: s = strprintf("%d (Min/Max %d/%d #%d)", t, lo, hi, word[2]); break; default: s = strprintf("%d (%d %d %d %d %d)", raw[0], raw[5], raw[4], raw[3], raw[2], raw[1]); break; } } break; case RAWFMT_TEMP10X: // ten times temperature in Celsius s = strprintf("%d.%d", word[0]/10, word[0]%10); break; default: s = "?"; // Should not happen break; } return s; } // Get attribute name std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs, int rpm /* = 0 */) { if (!defs[id].name.empty()) return defs[id].name; else { const ata_vendor_attr_defs::entry & def = get_default_attr_defs()[id]; if (def.name.empty()) return "Unknown_Attribute"; else if ((def.flags & ATTRFLAG_HDD_ONLY) && rpm == 1) return "Unknown_SSD_Attribute"; else if ((def.flags & ATTRFLAG_SSD_ONLY) && rpm > 1) return "Unknown_HDD_Attribute"; else return def.name; } } // Find attribute index for attribute id, -1 if not found. int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval) { if (!id) return -1; for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { if (smartval.vendor_attributes[i].id == id) return i; } return -1; } // Return Temperature Attribute raw value selected according to possible // non-default interpretations. If the Attribute does not exist, return 0 unsigned char ata_return_temperature_value(const ata_smart_values * data, const ata_vendor_attr_defs & defs) { for (int i = 0; i < 4; i++) { static const unsigned char ids[4] = {194, 190, 9, 220}; unsigned char id = ids[i]; const ata_attr_raw_format format = defs[id].raw_format; if (!( ((id == 194 || id == 190) && format == RAWFMT_DEFAULT) || format == RAWFMT_TEMPMINMAX || format == RAWFMT_TEMP10X)) continue; int idx = ata_find_attr_index(id, *data); if (idx < 0) continue; uint64_t raw = ata_get_attr_raw_value(data->vendor_attributes[idx], defs); unsigned temp; // ignore possible min/max values in high words if (format == RAWFMT_TEMP10X) // -v N,temp10x temp = ((unsigned short)raw + 5) / 10; else temp = (unsigned char)raw; if (!(0 < temp && temp < 128)) continue; return temp; } // No valid attribute found return 0; } // Read SCT Status int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts) { // read SCT status via SMART log 0xe0 memset(sts, 0, sizeof(*sts)); if (smartcommandhandler(device, READ_LOG, 0xe0, (char *)sts)){ pout("Read SCT Status failed: %s\n", device->get_errmsg()); return -1; } // swap endian order if needed if (isbigendian()){ SWAPV(sts->format_version); SWAPV(sts->sct_version); SWAPV(sts->sct_spec); SWAPV(sts->ext_status_code); SWAPV(sts->action_code); SWAPV(sts->function_code); SWAPV(sts->over_limit_count); SWAPV(sts->under_limit_count); SWAPV(sts->smart_status); SWAPV(sts->min_erc_time); } // Check format version if (!(sts->format_version == 2 || sts->format_version == 3)) { pout("Unknown SCT Status format version %u, should be 2 or 3.\n", sts->format_version); return -1; } return 0; } // Read SCT Temperature History Table int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh, ata_sct_status_response * sts) { // Initial SCT status must be provided by caller // Do nothing if other SCT command is executing if (sts->ext_status_code == 0xffff) { pout("Another SCT command is executing, abort Read Data Table\n" "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", sts->ext_status_code, sts->action_code, sts->function_code); return -1; } ata_sct_data_table_command cmd; memset(&cmd, 0, sizeof(cmd)); // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) cmd.action_code = 5; // Data table command cmd.function_code = 1; // Read table cmd.table_id = 2; // Temperature History Table // swap endian order if needed if (isbigendian()) { SWAPV(cmd.action_code); SWAPV(cmd.function_code); SWAPV(cmd.table_id); } // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ pout("Write SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } // read SCT data via SMART log page 0xe1 memset(tmh, 0, sizeof(*tmh)); if (smartcommandhandler(device, READ_LOG, 0xe1, (char *)tmh)){ pout("Read SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } // re-read and check SCT status if (ataReadSCTStatus(device, sts)) return -1; if (!(sts->ext_status_code == 0 && sts->action_code == 5 && sts->function_code == 1)) { pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts->ext_status_code, sts->action_code, sts->function_code); return -1; } // swap endian order if needed if (isbigendian()){ SWAPV(tmh->format_version); SWAPV(tmh->sampling_period); SWAPV(tmh->interval); SWAPV(tmh->cb_index); SWAPV(tmh->cb_size); } return 0; } // Common function for Get/Set SCT Feature Control: // Write Cache, Write Cache Reordering, etc. static int ataGetSetSCTFeatureControl(ata_device * device, unsigned short feature_code, unsigned short state, bool persistent, bool set) { // Check initial status ata_sct_status_response sts; if (ataReadSCTStatus(device, &sts)) return -1; // Do nothing if other SCT command is executing if (sts.ext_status_code == 0xffff) { pout("Another SCT command is executing, abort Feature Control\n" "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } ata_sct_feature_control_command cmd; memset(&cmd, 0, sizeof(cmd)); // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) cmd.action_code = 4; // Feature Control command cmd.function_code = (set ? 1 : 2); // 1=Set, 2=Get cmd.feature_code = feature_code; cmd.state = state; cmd.option_flags = (persistent ? 0x01 : 0x00); // swap endian order if needed if (isbigendian()) { SWAPV(cmd.action_code); SWAPV(cmd.function_code); SWAPV(cmd.feature_code); SWAPV(cmd.state); SWAPV(cmd.option_flags); } // write command via SMART log page 0xe0 // TODO: Debug output ata_cmd_in in; in.in_regs.command = ATA_SMART_CMD; in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW; in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR; in.in_regs.lba_low = 0xe0; in.set_data_out(&cmd, 1); if (!set) // Time limit returned in ATA registers in.out_needed.sector_count = in.out_needed.lba_low = true; ata_cmd_out out; if (!device->ata_pass_through(in, out)) { pout("Write SCT (%cet) Feature Control Command failed: %s\n", (!set ? 'G' : 'S'), device->get_errmsg()); return -1; } state = out.out_regs.sector_count | (out.out_regs.lba_low << 8); // re-read and check SCT status if (ataReadSCTStatus(device, &sts)) return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 4 && sts.function_code == (set ? 1 : 2))) { pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } return state; } // Get/Set Write Cache Reordering int ataGetSetSCTWriteCacheReordering(ata_device * device, bool enable, bool persistent, bool set) { return ataGetSetSCTFeatureControl(device, 2 /* Enable/Disable Write Cache Reordering */, (enable ? 1 : 2), persistent, set); } // Get/Set Write Cache (force enable, force disable, int ataGetSetSCTWriteCache(ata_device * device, unsigned short state, bool persistent, bool set) { return ataGetSetSCTFeatureControl(device, 1 /* Enable/Disable Write Cache */, state, persistent, set); } // Set SCT Temperature Logging Interval int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent) { // Check initial status ata_sct_status_response sts; if (ataReadSCTStatus(device, &sts)) return -1; // Do nothing if other SCT command is executing if (sts.ext_status_code == 0xffff) { pout("Another SCT command is executing, abort Feature Control\n" "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } ata_sct_feature_control_command cmd; memset(&cmd, 0, sizeof(cmd)); // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) cmd.action_code = 4; // Feature Control command cmd.function_code = 1; // Set state cmd.feature_code = 3; // Temperature logging interval cmd.state = interval; cmd.option_flags = (persistent ? 0x01 : 0x00); // swap endian order if needed if (isbigendian()) { SWAPV(cmd.action_code); SWAPV(cmd.function_code); SWAPV(cmd.feature_code); SWAPV(cmd.state); SWAPV(cmd.option_flags); } // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ pout("Write SCT Feature Control Command failed: %s\n", device->get_errmsg()); return -1; } // re-read and check SCT status if (ataReadSCTStatus(device, &sts)) return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 4 && sts.function_code == 1)) { pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } return 0; } // Get/Set SCT Error Recovery Control static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, bool set, unsigned short & time_limit) { // Check initial status ata_sct_status_response sts; if (ataReadSCTStatus(device, &sts)) return -1; // Do nothing if other SCT command is executing if (sts.ext_status_code == 0xffff) { pout("Another SCT command is executing, abort Error Recovery Control\n" "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } ata_sct_error_recovery_control_command cmd; memset(&cmd, 0, sizeof(cmd)); // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) cmd.action_code = 3; // Error Recovery Control command cmd.function_code = (set ? 1 : 2); // 1=Set timer, 2=Get timer cmd.selection_code = type; // 1=Read timer, 2=Write timer if (set) cmd.time_limit = time_limit; // swap endian order if needed if (isbigendian()) { SWAPV(cmd.action_code); SWAPV(cmd.function_code); SWAPV(cmd.selection_code); SWAPV(cmd.time_limit); } // write command via SMART log page 0xe0 // TODO: Debug output ata_cmd_in in; in.in_regs.command = ATA_SMART_CMD; in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW; in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR; in.in_regs.lba_low = 0xe0; in.set_data_out(&cmd, 1); if (!set) // Time limit returned in ATA registers in.out_needed.sector_count = in.out_needed.lba_low = true; ata_cmd_out out; if (!device->ata_pass_through(in, out)) { pout("Write SCT (%cet) Error Recovery Control Command failed: %s\n", (!set ? 'G' : 'S'), device->get_errmsg()); return -1; } // re-read and check SCT status if (ataReadSCTStatus(device, &sts)) return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == (set ? 1 : 2))) { pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } if (!set) { // Check whether registers are properly returned by ioctl() if (!(out.out_regs.sector_count.is_set() && out.out_regs.lba_low.is_set())) { // TODO: Output register support should be checked within each ata_pass_through() // implementation before command is issued. pout("SMART WRITE LOG does not return COUNT and LBA_LOW register\n"); return -1; } if ( out.out_regs.sector_count == in.in_regs.sector_count && out.out_regs.lba_low == in.in_regs.lba_low ) { // 0xe001 (5734.5s) - this is most likely a broken ATA pass-through implementation pout("SMART WRITE LOG returns COUNT and LBA_LOW register unchanged\n"); return -1; } // Return value to caller time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8); } return 0; } // Get SCT Error Recovery Control int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit) { return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit); } // Set SCT Error Recovery Control int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit) { return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit); } ///////////////////////////////////////////////////////////////////////////// // Pseudo-device to parse "smartctl -r ataioctl,2 ..." output and simulate // an ATA device with same behaviour namespace { class parsed_ata_device : public /*implements*/ ata_device_with_command_set { public: parsed_ata_device(smart_interface * intf, const char * dev_name); virtual ~parsed_ata_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); virtual bool ata_identify_is_cached() const; protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); private: // Table of parsed commands, return value, data struct parsed_ata_command { smart_command_set command; int select; int retval, errval; char * data; }; enum { max_num_commands = 32 }; parsed_ata_command m_command_table[max_num_commands]; int m_num_commands; int m_next_replay_command; bool m_replay_out_of_sync; bool m_ata_identify_is_cached; }; static const char * nextline(const char * s, int & lineno) { for (s += strcspn(s, "\r\n"); *s == '\r' || *s == '\n'; s++) { if (*s == '\r' && s[1] == '\n') s++; lineno++; } return s; } static int name2command(const char * s) { for (int i = 0; i < (int)(sizeof(commandstrings)/sizeof(commandstrings[0])); i++) { if (!strcmp(s, commandstrings[i])) return i; } return -1; } static bool matchcpy(char * dest, size_t size, const char * src, const regular_expression::match_range & srcmatch) { if (srcmatch.rm_so < 0) return false; size_t n = srcmatch.rm_eo - srcmatch.rm_so; if (n >= size) n = size-1; memcpy(dest, src + srcmatch.rm_so, n); dest[n] = 0; return true; } static inline int matchtoi(const char * src, const regular_expression::match_range & srcmatch, int defval) { if (srcmatch.rm_so < 0) return defval; return atoi(src + srcmatch.rm_so); } parsed_ata_device::parsed_ata_device(smart_interface * intf, const char * dev_name) : smart_device(intf, dev_name, "ata", ""), m_num_commands(0), m_next_replay_command(0), m_replay_out_of_sync(false), m_ata_identify_is_cached(false) { memset(m_command_table, 0, sizeof(m_command_table)); } parsed_ata_device::~parsed_ata_device() throw() { parsed_ata_device::close(); } bool parsed_ata_device::is_open() const { return (m_num_commands > 0); } // Parse stdin and build command table bool parsed_ata_device::open() { const char * pathname = get_dev_name(); if (strcmp(pathname, "-")) return set_err(EINVAL); pathname = ""; // Fill buffer char buffer[64*1024]; int size = 0; while (size < (int)sizeof(buffer)) { int nr = fread(buffer, 1, sizeof(buffer), stdin); if (nr <= 0) break; size += nr; } if (size <= 0) return set_err(ENOENT, "%s: Unexpected EOF", pathname); if (size >= (int)sizeof(buffer)) return set_err(EIO, "%s: Buffer overflow", pathname); buffer[size] = 0; // Regex to match output from "-r ataioctl,2" static const char pattern[] = "^" "(" // (1 "REPORT-IOCTL: DeviceF?D?=[^ ]+ Command=([A-Z ]*[A-Z])" // (2) "(" // (3 "( InputParameter=([0-9]+))?" // (4 (5)) "|" "( returned (-?[0-9]+)( errno=([0-9]+)[^\r\n]*)?)" // (6 (7) (8 (9))) ")" // ) "[\r\n]" // EOL match necessary to match optional parts above "|" "===== \\[([A-Z ]*[A-Z])\\] DATA START " // (10) "|" " *(En|Dis)abled status cached by OS, " // (11) ")"; // ) // Compile regex const regular_expression regex(pattern); // Parse buffer const char * errmsg = 0; int i = -1, state = 0, lineno = 1; for (const char * line = buffer; *line; line = nextline(line, lineno)) { // Match line if (!(line[0] == 'R' || line[0] == '=' || line[0] == ' ')) continue; const int nmatch = 1+11; regular_expression::match_range match[nmatch]; if (!regex.execute(line, nmatch, match)) continue; char cmdname[40]; if (matchcpy(cmdname, sizeof(cmdname), line, match[2])) { // "REPORT-IOCTL:... Command=%s ..." int nc = name2command(cmdname); if (nc < 0) { errmsg = "Unknown ATA command name"; break; } if (match[7].rm_so < 0) { // "returned %d" // Start of command if (!(state == 0 || state == 2)) { errmsg = "Missing REPORT-IOCTL result"; break; } if (++i >= max_num_commands) { errmsg = "Too many ATA commands"; break; } m_command_table[i].command = (smart_command_set)nc; m_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d" state = 1; } else { // End of command if (!(state == 1 && (int)m_command_table[i].command == nc)) { errmsg = "Missing REPORT-IOCTL start"; break; } m_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d" m_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d" state = 2; } } else if (matchcpy(cmdname, sizeof(cmdname), line, match[10])) { // "===== [%s] DATA START " // Start of sector hexdump int nc = name2command(cmdname); if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)m_command_table[i].command == nc)) { errmsg = "Unexpected DATA START"; break; } line = nextline(line, lineno); char * data = (char *)malloc(512); unsigned j; for (j = 0; j < 32; j++) { unsigned b[16]; unsigned u1, u2; int n1 = -1; if (!(sscanf(line, "%3u-%3u: " "%2x %2x %2x %2x %2x %2x %2x %2x " "%2x %2x %2x %2x %2x %2x %2x %2x%n", &u1, &u2, b+ 0, b+ 1, b+ 2, b+ 3, b+ 4, b+ 5, b+ 6, b+ 7, b+ 8, b+ 9, b+10, b+11, b+12, b+13, b+14, b+15, &n1) == 18 && n1 >= 56 && u1 == j*16 && u2 == j*16+15)) break; for (unsigned k = 0; k < 16; k++) data[j*16+k] = b[k]; line = nextline(line, lineno); } if (j < 32) { free(data); errmsg = "Incomplete sector hex dump"; break; } m_command_table[i].data = data; if (nc != WRITE_LOG) state = 0; } else if (match[11].rm_so > 0) { // "(En|Dis)abled status cached by OS" m_ata_identify_is_cached = true; } } if (!(state == 0 || state == 2)) errmsg = "Missing REPORT-IOCTL result"; if (!errmsg && i < 0) errmsg = "No information found"; m_num_commands = i+1; m_next_replay_command = 0; m_replay_out_of_sync = false; if (errmsg) { close(); return set_err(EIO, "%s(%d): Syntax error: %s", pathname, lineno, errmsg); } return true; } // Report warnings and free command table bool parsed_ata_device::close() { if (m_replay_out_of_sync) pout("REPLAY-IOCTL: Warning: commands replayed out of sync\n"); else if (m_next_replay_command != 0) pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", m_num_commands-m_next_replay_command); for (int i = 0; i < m_num_commands; i++) { if (m_command_table[i].data) { free(m_command_table[i].data); m_command_table[i].data = 0; } } m_num_commands = 0; m_next_replay_command = 0; m_replay_out_of_sync = false; return true; } bool parsed_ata_device::ata_identify_is_cached() const { return m_ata_identify_is_cached; } // Simulate ATA command from command table int parsed_ata_device::ata_command_interface(smart_command_set command, int select, char * data) { // Find command, try round-robin if out of sync int i = m_next_replay_command; for (int j = 0; ; j++) { if (j >= m_num_commands) { pout("REPLAY-IOCTL: Warning: Command not found\n"); errno = ENOSYS; return -1; } if (m_command_table[i].command == command && m_command_table[i].select == select) break; if (!m_replay_out_of_sync) { m_replay_out_of_sync = true; pout("REPLAY-IOCTL: Warning: Command #%d is out of sync\n", i+1); } if (++i >= m_num_commands) i = 0; } m_next_replay_command = i; if (++m_next_replay_command >= m_num_commands) m_next_replay_command = 0; // Return command data switch (command) { case IDENTIFY: case PIDENTIFY: case READ_VALUES: case READ_THRESHOLDS: case READ_LOG: if (m_command_table[i].data) memcpy(data, m_command_table[i].data, 512); break; case WRITE_LOG: if (!(m_command_table[i].data && !memcmp(data, m_command_table[i].data, 512))) pout("REPLAY-IOCTL: Warning: WRITE LOG data does not match\n"); break; case CHECK_POWER_MODE: data[0] = (char)0xff; default: break; } if (m_command_table[i].errval) errno = m_command_table[i].errval; return m_command_table[i].retval; } } // namespace ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name) { return new parsed_ata_device(intf, dev_name); } smartmontools-7.0/atacmds.h0000644000175000010010000010667213402014526013005 00000000000000/* * atacmds.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2008-17 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ATACMDS_H_ #define ATACMDS_H_ #define ATACMDS_H_CVSID "$Id: atacmds.h 4848 2018-12-05 18:30:46Z chrfranke $" #include "dev_interface.h" // ata_device // 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_CHECK_POWER_MODE 0xe5 #define ATA_IDENTIFY_DEVICE 0xec #define ATA_IDENTIFY_PACKET_DEVICE 0xa1 #define ATA_IDLE 0xe3 #define ATA_SMART_CMD 0xb0 #define ATA_SECURITY_FREEZE_LOCK 0xf5 #ifndef ATA_SET_FEATURES #define ATA_SET_FEATURES 0xef #endif #define ATA_STANDBY 0xe2 #define ATA_STANDBY_IMMEDIATE 0xe0 // SET_FEATURES subcommands #define ATA_DISABLE_AAM 0xc2 #define ATA_DISABLE_APM 0x85 #define ATA_DISABLE_WRITE_CACHE 0x82 #define ATA_DISABLE_READ_LOOK_AHEAD 0x55 #define ATA_ENABLE_AAM 0x42 #define ATA_ENABLE_APM 0x05 #define ATA_ENABLE_WRITE_CACHE 0x02 #define ATA_ENABLE_READ_LOOK_AHEAD 0xaa #define ATA_ENABLE_DISABLE_DSN 0x63 // 48-bit commands #define ATA_READ_LOG_EXT 0x2F #define ATA_WRITE_LOG_EXT 0x3f // 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) // 6-15: Reserved for future use #define ATTRIBUTE_FLAGS_OTHER(x) ((x) & 0xffc0) // Format of data returned by SMART READ DATA // Table 62 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 #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 current 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_b; // If 0xff, use 16-bit value below unsigned char conveyance_test_completion_time; unsigned short extend_test_completion_time_w; // e04130r2, added to T13/1699-D Revision 1c, April 2005 unsigned char reserved_377_385[9]; 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); // Extended Comprehensive SMART Error Log data structures // See Section A.7 of // AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) // T13/1699-D Revision 6a (Working Draft), September 6, 2008. // Command data structure // Table A.9 of T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_exterrlog_command { unsigned char device_control_register; unsigned char features_register; unsigned char features_register_hi; unsigned char count_register; unsigned char count_register_hi; unsigned char lba_low_register; unsigned char lba_low_register_hi; unsigned char lba_mid_register; unsigned char lba_mid_register_hi; unsigned char lba_high_register; unsigned char lba_high_register_hi; unsigned char device_register; unsigned char command_register; unsigned char reserved; unsigned int timestamp; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_command, 18); // Error data structure // Table A.10 T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_exterrlog_error { unsigned char device_control_register; unsigned char error_register; unsigned char count_register; unsigned char count_register_hi; unsigned char lba_low_register; unsigned char lba_low_register_hi; unsigned char lba_mid_register; unsigned char lba_mid_register_hi; unsigned char lba_high_register; unsigned char lba_high_register_hi; unsigned char device_register; unsigned char status_register; unsigned char extended_error[19]; unsigned char state; unsigned short timestamp; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error, 34); // Error log data structure // Table A.8 of T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_exterrlog_error_log { ata_smart_exterrlog_command commands[5]; ata_smart_exterrlog_error error; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error_log, 124); // Ext. Comprehensive SMART error log // Table A.7 of T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_exterrlog { unsigned char version; unsigned char reserved1; unsigned short error_log_index; ata_smart_exterrlog_error_log error_logs[4]; unsigned short device_error_count; unsigned char reserved2[9]; unsigned char checksum; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog, 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); // Extended SMART Self-test log data structures // See Section A.8 of // AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) // T13/1699-D Revision 6a (Working Draft), September 6, 2008. // Extended Self-test log descriptor entry // Table A.13 of T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_extselftestlog_desc { unsigned char self_test_type; unsigned char self_test_status; unsigned short timestamp; unsigned char checkpoint; unsigned char failing_lba[6]; unsigned char vendorspecific[15]; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_extselftestlog_desc, 26); // Extended Self-test log data structure // Table A.12 of T13/1699-D Revision 6a #pragma pack(1) struct ata_smart_extselftestlog { unsigned char version; unsigned char reserved1; unsigned short log_desc_index; struct ata_smart_extselftestlog_desc log_descs[19]; unsigned char vendor_specifc[2]; unsigned char reserved2[11]; unsigned char chksum; } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_smart_extselftestlog, 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) // SCT (SMART Command Transport) data structures // See Sections 8.2 and 8.3 of: // AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) // T13/1699-D Revision 3f (Working Draft), December 11, 2006. // SCT Status response (read with SMART_READ_LOG page 0xe0) // Table 194 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017 #pragma pack(1) struct ata_sct_status_response { unsigned short format_version; // 0-1: Status response format version number (2, 3) unsigned short sct_version; // 2-3: Vendor specific version number unsigned short sct_spec; // 4-5: SCT level supported (1) unsigned int status_flags; // 6-9: Status flags (Bit 0: Segment initialized, Bits 1-31: reserved) unsigned char device_state; // 10: Device State (0-5) unsigned char bytes011_013[3]; // 11-13: reserved unsigned short ext_status_code; // 14-15: Status of last SCT command (0xffff if executing) unsigned short action_code; // 16-17: Action code of last SCT command unsigned short function_code; // 18-19: Function code of last SCT command unsigned char bytes020_039[20]; // 20-39: reserved uint64_t lba_current; // 40-47: LBA of SCT command executing in background unsigned char bytes048_199[152]; // 48-199: reserved signed char hda_temp; // 200: Current temperature in Celsius (0x80 = invalid) signed char min_temp; // 201: Minimum temperature this power cycle signed char max_temp; // 202: Maximum temperature this power cycle signed char life_min_temp; // 203: Minimum lifetime temperature signed char life_max_temp; // 204: Maximum lifetime temperature signed char max_op_limit; // 205: Specified maximum operating temperature (ACS-4) unsigned int over_limit_count; // 206-209: # intervals since last reset with temperature > Max Op Limit unsigned int under_limit_count; // 210-213: # intervals since last reset with temperature < Min Op Limit unsigned short smart_status; // 214-215: LBA(32:8) of SMART RETURN STATUS (0, 0x2cf4, 0xc24f) (ACS-4) unsigned short min_erc_time; // 216-217: Minimum supported value for ERC (ACS-4) unsigned char bytes216_479[479-218+1]; // 218-479: reserved unsigned char vendor_specific[32];// 480-511: vendor specific } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_status_response, 512); // SCT Error Recovery Control command (send with SMART_WRITE_LOG page 0xe0) // Table 88 of T13/1699-D Revision 6a #pragma pack(1) struct ata_sct_error_recovery_control_command { unsigned short action_code; // 3 = Error Recovery Control unsigned short function_code; // 1 = Set, 2 = Return unsigned short selection_code; // 1 = Read Timer, 2 = Write Timer unsigned short time_limit; // If set: Recovery time limit in 100ms units unsigned short words004_255[252]; // reserved } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_error_recovery_control_command, 512); // SCT Feature Control command (send with SMART_WRITE_LOG page 0xe0) // Table 72 of T13/1699-D Revision 3f #pragma pack(1) struct ata_sct_feature_control_command { unsigned short action_code; // 4 = Feature Control unsigned short function_code; // 1 = Set, 2 = Return, 3 = Return options unsigned short feature_code; // 3 = Temperature logging interval unsigned short state; // Interval unsigned short option_flags; // Bit 0: persistent, Bits 1-15: reserved unsigned short words005_255[251]; // reserved } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_feature_control_command, 512); // SCT Data Table command (send with SMART_WRITE_LOG page 0xe0) // Table 73 of T13/1699-D Revision 3f #pragma pack(1) struct ata_sct_data_table_command { unsigned short action_code; // 5 = Data Table unsigned short function_code; // 1 = Read Table unsigned short table_id; // 2 = Temperature History unsigned short words003_255[253]; // reserved } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_data_table_command, 512); // SCT Temperature History Table (read with SMART_READ_LOG page 0xe1) // Table 75 of T13/1699-D Revision 3f #pragma pack(1) struct ata_sct_temperature_history_table { unsigned short format_version; // 0-1: Data table format version number (2) unsigned short sampling_period; // 2-3: Temperature sampling period in minutes unsigned short interval; // 4-5: Timer interval between history entries signed char max_op_limit; // 6: Maximum recommended continuous operating temperature signed char over_limit; // 7: Maximum temperature limit signed char min_op_limit; // 8: Minimum recommended continuous operating limit signed char under_limit; // 9: Minimum temperature limit unsigned char bytes010_029[20]; // 10-29: reserved unsigned short cb_size; // 30-31: Number of history entries (range 128-478) unsigned short cb_index; // 32-33: Index of last updated entry (zero-based) signed char cb[478]; // 34-(34+cb_size-1): Circular buffer of temperature values } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_temperature_history_table, 512); // Possible values for span_args.mode enum { SEL_RANGE, // MIN-MAX SEL_REDO, // redo this SEL_NEXT, // do next range SEL_CONT // redo or next depending of last test status }; // Arguments for selective self-test struct ata_selective_selftest_args { // Arguments for each span struct span_args { uint64_t start; // First block uint64_t end; // Last block int mode; // SEL_*, see above span_args() : start(0), end(0), mode(SEL_RANGE) { } }; span_args span[5]; // Range and mode for 5 spans int num_spans; // Number of spans int pending_time; // One plus time in minutes to wait after powerup before restarting // interrupted offline scan after selective self-test. int scan_after_select; // 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. ata_selective_selftest_args() : num_spans(0), pending_time(0), scan_after_select(0) { } }; // Priority for vendor attribute defs enum ata_vendor_def_prior { PRIOR_DEFAULT, PRIOR_DATABASE, PRIOR_USER }; // Raw attribute value print formats enum ata_attr_raw_format { RAWFMT_DEFAULT, RAWFMT_RAW8, RAWFMT_RAW16, RAWFMT_RAW48, RAWFMT_HEX48, RAWFMT_RAW56, RAWFMT_HEX56, RAWFMT_RAW64, RAWFMT_HEX64, RAWFMT_RAW16_OPT_RAW16, RAWFMT_RAW16_OPT_AVG16, RAWFMT_RAW24_OPT_RAW8, RAWFMT_RAW24_DIV_RAW24, RAWFMT_RAW24_DIV_RAW32, RAWFMT_SEC2HOUR, RAWFMT_MIN2HOUR, RAWFMT_HALFMIN2HOUR, RAWFMT_MSEC24_HOUR32, RAWFMT_TEMPMINMAX, RAWFMT_TEMP10X, }; // Attribute flags enum { ATTRFLAG_INCREASING = 0x01, // Value not reset (for reallocated/pending counts) ATTRFLAG_NO_NORMVAL = 0x02, // Normalized value not valid ATTRFLAG_NO_WORSTVAL = 0x04, // Worst value not valid ATTRFLAG_HDD_ONLY = 0x08, // DEFAULT setting for HDD only ATTRFLAG_SSD_ONLY = 0x10, // DEFAULT setting for SSD only }; // Vendor attribute display defs for all attribute ids class ata_vendor_attr_defs { public: struct entry { std::string name; // Attribute name, empty for default ata_attr_raw_format raw_format; // Raw value print format ata_vendor_def_prior priority; // Setting priority unsigned flags; // ATTRFLAG_* char byteorder[8+1]; // String [012345rvwz] to define byte order entry() : raw_format(RAWFMT_DEFAULT), priority(PRIOR_DEFAULT), flags(0) { byteorder[0] = 0; } }; entry & operator[](unsigned char id) { return m_defs[id]; } const entry & operator[](unsigned char id) const { return m_defs[id]; } private: entry m_defs[256]; }; // Possible values for firmwarebugs enum firmwarebug_t { BUG_NONE = 0, BUG_NOLOGDIR, BUG_SAMSUNG, BUG_SAMSUNG2, BUG_SAMSUNG3, BUG_XERRORLBA }; // Set of firmware bugs class firmwarebug_defs { public: firmwarebug_defs() : m_bugs(0) { } bool is_set(firmwarebug_t bug) const { return !!(m_bugs & (1 << bug)); } void set(firmwarebug_t bug) { m_bugs |= (1 << bug); } void set(firmwarebug_defs bugs) { m_bugs |= bugs.m_bugs; } private: unsigned m_bugs; }; // Print ATA debug messages? extern unsigned char ata_debugmode; // Suppress serial number? extern bool dont_print_serial_number; // Get information from drive int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id, unsigned char * raw_buf = 0); int ataCheckPowerMode(ata_device * device); // Issue a no-data ATA command with optional sector count register value bool ata_nodata_command(ata_device * device, unsigned char command, int sector_count = -1); // Issue SET FEATURES command with optional sector count register value bool ata_set_features(ata_device * device, unsigned char features, int sector_count = -1); /* Read S.M.A.R.T information from drive */ int ataReadSmartValues(ata_device * device,struct ata_smart_values *); int ataReadSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *); int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, firmwarebug_defs firmwarebugs); int ataReadSelfTestLog(ata_device * device, ata_smart_selftestlog * data, firmwarebug_defs firmwarebugs); int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data); int ataReadLogDirectory(ata_device * device, ata_smart_log_directory *, bool gpl); // Write GP Log page(s) bool ataWriteLogExt(ata_device * device, unsigned char logaddr, unsigned page, void * data, unsigned nsectors); // Read GP Log page(s) bool ataReadLogExt(ata_device * device, unsigned char logaddr, unsigned char features, unsigned page, void * data, unsigned nsectors); // Read SMART Log page(s) bool ataReadSmartLog(ata_device * device, unsigned char logaddr, void * data, unsigned nsectors); // Read SMART Extended Comprehensive Error Log bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, unsigned page, unsigned nsectors, firmwarebug_defs firmwarebugs); // Read SMART Extended Self-test Log bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log, unsigned nsectors); // Read SCT information int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts); int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh, ata_sct_status_response * sts); // Set SCT temperature logging interval int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent); // Get/Set SCT Error Recovery Control int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit); int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit); /* Enable/Disable SMART on device */ int ataEnableSmart (ata_device * device); int ataDisableSmart (ata_device * device); int ataEnableAutoSave(ata_device * device); int ataDisableAutoSave(ata_device * device); /* Automatic Offline Testing */ int ataEnableAutoOffline (ata_device * device); int ataDisableAutoOffline (ata_device * device); /* S.M.A.R.T. test commands */ int ataSmartTest(ata_device * device, int testtype, bool force, const ata_selective_selftest_args & args, const ata_smart_values * sv, uint64_t num_sectors); int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_args & args, const ata_smart_values * sv, uint64_t num_sectors, const ata_selective_selftest_args * prev_spans = 0); // Get World Wide Name (WWN) fields. // Return NAA field or -1 if WWN is unsupported. int ata_get_wwn(const ata_identify_device * id, unsigned & oui, uint64_t & unique_id); // Get nominal media rotation rate. // Returns: 0 = not reported, 1 = SSD, >1 = HDD rpm, < 0 = -(Unknown value) int ata_get_rotation_rate(const ata_identify_device * id); // If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0. int ataDoesSmartWork(ata_device * device); // returns 1 if SMART supported, 0 if not supported or can't tell int ataSmartSupport(const ata_identify_device * drive); // Return values: // 1: Write Cache Reordering enabled // 2: Write Cache Reordering disabled // -1: error int ataGetSetSCTWriteCacheReordering(ata_device * device, bool enable, bool persistent, bool set); // Return values: // 1: Write cache controlled by ATA Set Features command // 2: Force enable write cache // 3: Force disable write cache int ataGetSetSCTWriteCache(ata_device * device, unsigned short state, bool persistent, bool set); // Return values: // 1: SMART enabled // 0: SMART disabled // -1: can't tell if SMART is enabled -- try issuing ataDoesSmartWork command to see int ataIsSmartEnabled(const ata_identify_device * drive); int ataSmartStatus2(ata_device * device); bool isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity); bool isSmartTestLogCapable(const ata_smart_values * data, const ata_identify_device * identity); bool isGeneralPurposeLoggingCapable(const ata_identify_device * identity); // 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). inline bool isSupportExecuteOfflineImmediate(const ata_smart_values *data) { return !!(data->offline_data_collection_capability & 0x01); } // TODO: Remove uses of this check. Bit 1 is vendor specific since ATA-4. // Automatic timer support was only documented for very old IBM drives // (for example IBM Travelstar 40GNX). inline bool isSupportAutomaticTimer(const ata_smart_values * data) { return !!(data->offline_data_collection_capability & 0x02); } inline bool isSupportOfflineAbort(const ata_smart_values *data) { return !!(data->offline_data_collection_capability & 0x04); } inline bool isSupportOfflineSurfaceScan(const ata_smart_values * data) { return !!(data->offline_data_collection_capability & 0x08); } inline bool isSupportSelfTest(const ata_smart_values * data) { return !!(data->offline_data_collection_capability & 0x10); } inline bool isSupportConveyanceSelfTest(const ata_smart_values * data) { return !!(data->offline_data_collection_capability & 0x20); } inline bool isSupportSelectiveSelfTest(const ata_smart_values * data) { return !!(data->offline_data_collection_capability & 0x40); } inline bool isSCTCapable(const ata_identify_device *drive) { return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support inline bool isSCTErrorRecoveryControlCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x09) == 0x09); } // 0x08 = SCT Error Recovery Control support inline bool isSCTFeatureControlCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x11) == 0x11); } // 0x10 = SCT Feature Control support inline bool isSCTDataTableCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x21) == 0x21); } // 0x20 = SCT Data Table support int TestTime(const ata_smart_values * data, int testtype); // Attribute state enum ata_attr_state { ATTRSTATE_NON_EXISTING, // No such Attribute ATTRSTATE_NO_NORMVAL, // Normalized value not valid ATTRSTATE_NO_THRESHOLD, // Unknown or no threshold ATTRSTATE_OK, // Never failed ATTRSTATE_FAILED_PAST, // Failed in the past ATTRSTATE_FAILED_NOW // Failed now }; // Get attribute state ata_attr_state ata_get_attr_state(const ata_smart_attribute & attr, int attridx, const ata_smart_threshold_entry * thresholds, const ata_vendor_attr_defs & defs, unsigned char * threshval = 0); // Get attribute raw value. uint64_t ata_get_attr_raw_value(const ata_smart_attribute & attr, const ata_vendor_attr_defs & defs); // Format attribute raw value. std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, const ata_vendor_attr_defs & defs); // Get attribute name std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs, int rpm = 0); // External handler function, for when a checksum is not correct. Can // simply return if no action is desired, or can print error messages // as needed, or exit. Is passed a string with the name of the Data // Structure with the incorrect checksum. void checksumwarning(const char *string); // Find attribute index for attribute id, -1 if not found. int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval); // Return Temperature Attribute raw value selected according to possible // non-default interpretations. If the Attribute does not exist, return 0 unsigned char ata_return_temperature_value(const ata_smart_values * data, const ata_vendor_attr_defs & defs); #define MAX_ATTRIBUTE_NUM 256 // Parse vendor attribute display def (-v option). // Return false on error. bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs, ata_vendor_def_prior priority); // Get ID and increase flag of current pending or offline // uncorrectable attribute. unsigned char get_unc_attr_id(bool offline, const ata_vendor_attr_defs & defs, bool & increase); // Return a multiline string containing a list of valid arguments for // parse_attribute_def(). std::string create_vendor_attribute_arg_list(); // Parse firmwarebug def (-F option). // Return false on error. bool parse_firmwarebug_def(const char * opt, firmwarebug_defs & firmwarebugs); // Return a string of valid argument words for parse_firmwarebug_def() const char * get_valid_firmwarebug_args(); // These are two of the functions that are defined in os_*.c and need // to be ported to get smartmontools onto another OS. // Moved to C++ interface //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); //int highpoint_command_interface(int device, smart_command_set command, int select, char *data); //int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data); // This function is exported to give low-level capability int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data); // Get capacity and sector sizes from IDENTIFY data struct ata_size_info { uint64_t sectors; uint64_t capacity; unsigned log_sector_size; unsigned phy_sector_size; unsigned log_sector_offset; }; void ata_get_size_info(const ata_identify_device * id, ata_size_info & sizes); // Convenience function for formatting strings from ata_identify_device. void ata_format_id_string(char * out, const unsigned char * in, int n); // Utility routines. unsigned char checksum(const void * data); // Return pseudo-device to parse "smartctl -r ataioctl,2 ..." output // and simulate an ATA device with same behaviour ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name); #endif /* ATACMDS_H_ */ smartmontools-7.0/ataidentify.cpp0000644000175000010010000006471213336335341014232 00000000000000/* * ataidentify.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2012-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include "ataidentify.h" const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 4760 2018-08-19 18:45:53Z chrfranke $" ATAIDENTIFY_H_CVSID; #include "utility.h" #include // Table 12 of X3T10/0948D (ATA-2) Revision 4c, March 18, 1996 // Table 9 of X3T13/2008D (ATA-3) Revision 7b, January 27, 1997 // Tables 11 and 13 of T13/1153D (ATA/ATAPI-4) revision 18, August 19, 1998 // Tables 20 and 22 of T13/1321D (ATA/ATAPI-5) Revision 3, February 29, 2000 // Tables 27 and 29 of T13/1410D (ATA/ATAPI-6) Revision 3b, February 26, 2002 // Tables 16 and 18 of T13/1532D (ATA/ATAPI-7) Volume 1 Revision 4b, April 21, 2004 // Tables 29 and 39 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 // Tables 50 and 61 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 // Tables 45 and 50 of T13/2161-D (ACS-3) Revision 5, October 28, 2013 // Table 55 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017 (ATAPI removed) const char * const identify_descriptions[] = { " 0 General configuration", ". 15 Device identifier: 0 = ATA, 1 = ATAPI", ". 14:8 ATA: Vendor specific [RET-3]", ". 14 ATAPI: Must be set to 0", ". 13 ATAPI: Reserved", ". 12:8 ATAPI: Command set: 0x05 = CD/DVD", ". 7 Removable media device [OBS-8]", ". 6 ATA: Not removable controller and/or device [OBS-6]", ". 5:3 ATA: Vendor specific [RET-3]", ". 6:5 ATAPI: DRQ after PACKET cmd: 0x0 = 3ms, 0x2 = 50us", ". 4:3 ATAPI: Reserved", ". 2 Response incomplete", ". 1 ATA: Vendor specific [RET-3]", ". 0 ATA: Reserved", ". 1:0 ATAPI: Packet size: 0x0 = 12 byte, 0x1 = 16 byte", " 1 Cylinders [OBS-6]", " 2 Specific configuration (0x37c8/738c/8c73/c837)", " 3 Heads [OBS-6]", " 4 Vendor specific [RET-3]", " 5 Vendor specific [RET-3]", " 6 Sectors per track [OBS-6]", " 7-8 Reserved for CFA (Sectors per card)", " 9 Vendor specific [RET-4]", " 10-19 Serial number (String)", " 20 Vendor specific [RET-3]", " 21 Vendor specific [RET-3]", " 22 Vendor specific bytes on READ/WRITE LONG [OBS-4]", " 23-26 Firmware revision (String)", " 27-46 Model number (String)", " 47 READ/WRITE MULTIPLE support", ". 15:8 Must be set to 0x80", ". 7:0 Maximum sectors per DRQ on READ/WRITE MULTIPLE", " 48 Trusted Computing feature set options", ". 15:14 Must be set to 0x1", ". 13:1 Reserved for the Trusted Computing Group", ". 0 Trusted Computing feature set supported", " 49 Capabilities", ". 15:14 ATA: Reserved for IDENTIFY PACKET DEVICE", ". 15 ATAPI: Interleaved DMA supported [OBS-8]", ". 14 ATAPI: Command queuing supported [OBS-8]", ". 13 ATA: Standard standby timer values supported", ". 13 ATAPI: Overlap operation supported [OBS-8]", ". 12 ATA: Reserved for IDENTIFY PACKET DEVICE", ". 12 ATAPI: ATA software reset required [OBS-5]", ". 11 IORDY supported", ". 10 IORDY may be disabled", ". 9 LBA supported", ". 8 DMA supported", ". 7:2 Reserved", // ATA-3: Vendor specific, ATA-8: Retired ". 1:0 Long Phy Sector Alignment Error reporting", // ACS-2 " 50 Capabilities", ". 15:14 Must be set to 0x1", ". 13:2 Reserved", ". 1 Reserved [OBS-6]", ". 0 Vendor specific minimum standby timer value", " 51 PIO data transfer mode [OBS-5]", " 52 Single Word DMA data transfer mode [OBS-3]", " 53 Field validity / Free-fall Control", ". 15:8 Free-fall Control sensitivity", ". 7:3 Reserved", ". 2 Word 88 (Ultra DMA modes) is valid", ". 1 Words 64-70 (PIO modes) are valid", ". 0 Words 54-58 (CHS) are valid [OBS-6]", " 54 Current cylinders [OBS-6]", " 55 Current heads [OBS-6]", " 56 Current sectors per track [OBS-6]", " 57-58 Current capacity in sectors (DWord) [OBS-6]", " 59 Sanitize Device - READ/WRITE MULTIPLE support", ". 15 BLOCK ERASE EXT supported", ". 14 OVERWRITE EXT supported", ". 13 CRYPTO SCRAMBLE EXT supported", ". 12 Sanitize Device feature set supported", ". 11 Cmds during sanitize as specified by this standard", // ACS-3 ". 10 SANITIZE ANTIFREEZE LOCK EXT supported", // ACS-3 ". 9 Reserved", ". 8 Bits 7:0 are valid [OBS-ACS-4]", ". 7:0 Current number of sectors per DRQ [OBS-ACS-4]", " 60-61 User addressable sectors for 28-bit commands (DWord)", " 62 Single Word DMA modes [OBS-3]", " 63 Multiword DMA modes", ". 15:11 Reserved", ". 10 Multiword DMA mode 2 selected", ". 9 Multiword DMA mode 1 selected", ". 8 Multiword DMA mode 0 selected", ". 7:3 Reserved", ". 2 Multiword DMA mode 2 and below supported", ". 1 Multiword DMA mode 1 and below supported", ". 0 Multiword DMA mode 0 supported", " 64 PIO modes", ". 15:2 Reserved", ". 1 PIO mode 4 supported", ". 0 PIO mode 3 supported", " 65 Minimum Multiword DMA cycle time per word in ns", " 66 Recommended Multiword DMA cycle time in ns", " 67 Minimum PIO cycle time without flow control in ns", " 68 Minimum PIO cycle time with IORDY flow control in ns", " 69 Additional support", ". 15 CFast specification supported", ". 14 Deterministic data after trim supported", ". 13 LPS Alignment Error Reporting Control supported", ". 12 DCO IDENTIFY/SET DMA supported [OBS-ACS-3]", ". 11 READ BUFFER DMA supported", ". 10 WRITE BUFFER DMA supported", ". 9 SET MAX SET PASSWORD/UNLOCK DMA supported [OBS-ACS-3]", ". 8 DOWNLOAD MICROCODE DMA supported", ". 7 Reserved for IEEE 1667", ". 6 Optional ATA device 28-bit commands supported", ". 5 Trimmed LBA range(s) returning zeroed data supported", ". 4 Device encrypts all user data", ". 3 Extended number of user addressable sectors supported", ". 2 All write cache is non-volatile", // ACS-3 ". 1:0 Zoned Capabilities", // ACS-4 " 70 Reserved", " 71-74 ATA: Reserved for IDENTIFY PACKET DEVICE", " 71 ATAPI: Time in ns from PACKET to bus release [OBS-8]", " 72 ATAPI: Time in ns from SERVICE to BSY cleared [OBS-8]", " 73-74 ATAPI: Reserved", " 75 Queue depth", ". 15:5 Reserved", ". 4:0 Maximum queue depth - 1", " 76 Serial ATA capabilities", ". 15 READ LOG DMA EXT as equiv to READ LOG EXT supported", ". 14 Device Auto Partial to Slumber transitions supported", ". 13 Host Auto Partial to Slumber transitions supported", ". 12 NCQ priority information supported", ". 11 Unload while NCQ commands are outstanding supported", ". 10 Phy Event Counters supported", ". 9 Receipt of host initiated PM requests supported", ". 8 NCQ feature set supported", ". 7:4 Reserved for Serial ATA", ". 3 SATA Gen3 signaling speed (6.0 Gb/s) supported", ". 2 SATA Gen2 signaling speed (3.0 Gb/s) supported", ". 1 SATA Gen1 signaling speed (1.5 Gb/s) supported", ". 0 Must be set to 0", " 77 Serial ATA additional capabilities", // ACS-3 ". 15:9 Reserved for Serial ATA", ". 8 Power Disable feature always enabled", // ACS-4 ". 7 DevSleep to ReducedPwrState supported", // ACS-4 ". 6 RECEIVE/SEND FPDMA QUEUED supported", ". 5 NCQ Queue Management supported", ". 4 NCQ Streaming supported", ". 3:1 Current Serial ATA signal speed", ". 0 Must be set to 0", " 78 Serial ATA features supported", ". 15:13 Reserved for Serial ATA", ". 12 Power Disable feature supported", // ACS-4 ". 11 Rebuild Assist feature set supported", // ACS-4 ". 10 Reserved for Serial ATA", ". 9 Hybrid Information supported", // ACS-4 ". 8 Device Sleep feature supported", // ACS-4 ". 7 NCQ Autosense supported", // ACS-3 ". 6 Software Settings Preservation supported", ". 5 Hardware Feature Control supported", // ACS-3 ". 4 In-order data delivery supported", ". 3 Device initiated power management supported", ". 2 DMA Setup auto-activation supported", ". 1 Non-zero buffer offsets supported", ". 0 Must be set to 0", " 79 Serial ATA features enabled", ". 15:12 Reserved for Serial ATA", ". 11 Rebuild Assist feature set enabled", // ACS-4 ". 10 Power Disable feature enabled", // ACS-4 ". 9 Hybrid Information enabled", // ACS-4 ". 8 Device Sleep feature enabled", // ACS-4 ". 7 Automatic Partial to Slumber transitions enabled", // ACS-3 ". 6 Software Settings Preservation enabled", ". 5 Hardware Feature Control enabled", // ACS-3 ". 4 In-order data delivery enabled", ". 3 Device initiated power management enabled", ". 2 DMA Setup auto-activation enabled", ". 1 Non-zero buffer offsets enabled", ". 0 Must be set to 0", " 80 Major version number", ". 15:12 Reserved", ". 11 ACS-4 supported", ". 10 ACS-3 supported", ". 9 ACS-2 supported", ". 8 ATA8-ACS supported", ". 7 ATA/ATAPI-7 supported [OBS-ACS-4]", ". 6 ATA/ATAPI-6 supported [OBS-ACS-4]", ". 5 ATA/ATAPI-5 supported [OBS-ACS-4]", ". 4 ATA/ATAPI-4 supported [OBS-8]", ". 3 ATA-3 supported [OBS-7]", ". 2 ATA-2 supported [OBS-6]", ". 1 ATA-1 supported [OBS-5]", ". 0 Reserved", " 81 Minor version number", " 82 Commands and feature sets supported", ". 15 IDENTIFY DEVICE DMA supported [OBS-4]", // ATA-4 r07-r14 only ". 14 NOP supported", ". 13 READ BUFFER supported", ". 12 WRITE BUFFER supported", ". 11 WRITE VERIFY supported [OBS-4]", // ATA-4 r07-r13 only ". 10 HPA feature set supported [OBS-ACS-3]", ". 9 DEVICE RESET supported", // ATA:0, ATAPI:1 ". 8 SERVICE interrupt supported [OBS-ACS-2]", ". 7 Release interrupt supported [OBS-ACS-2]", ". 6 Read look-ahead supported", ". 5 Volatile write cache supported", ". 4 PACKET feature set supported", // ATA:0, ATAPI:1 ". 3 Power Management feature set supported", ". 2 Removable Media feature set supported [OBS-8]", ". 1 Security feature set supported", ". 0 SMART feature set supported", " 83 Commands and feature sets supported", ". 15:14 Must be set to 0x1", ". 13 FLUSH CACHE EXT supported", ". 12 FLUSH CACHE supported", ". 11 DCO feature set supported [OBS-ACS-3]", ". 10 48-bit Address feature set supported", ". 9 AAM feature set supported [OBS-ACS-2]", ". 8 SET MAX security extension supported [OBS-ACS-3]", ". 7 Reserved for Addr Offset Resvd Area Boot [OBS-ACS-3]", ". 6 SET FEATURES subcommand required to spin-up", ". 5 PUIS feature set supported", ". 4 Removable Media Status Notification supported [OBS-8]", ". 3 APM feature set supported", ". 2 CFA feature set supported", ". 1 TCQ feature set supported [OBS-ACS-2]", ". 0 DOWNLOAD MICROCODE supported", " 84 Commands and feature sets supported", ". 15:14 Must be set to 0x1", ". 13 IDLE IMMEDIATE with UNLOAD feature supported", ". 12:11 Reserved for TLC [OBS-ACS-3]", ". 10 URG bit for WRITE STREAM (DMA) EXT supported [OBS-8]", ". 9 URG bit for READ STREAM (DMA) EXT supported [OBS-8]", ". 8 64-bit World Wide Name supported", ". 7 WRITE DMA QUEUED FUA EXT supported [OBS-ACS-2]", ". 6 WRITE DMA/MULTIPLE FUA EXT supported", ". 5 GPL feature set supported", ". 4 Streaming feature set supported", ". 3 Media Card Pass Through Command supported [OBS-ACS-2]", ". 2 Media serial number supported [RES-ACS-3]", ". 1 SMART self-test supported", ". 0 SMART error logging supported", " 85 Commands and feature sets supported or enabled", ". 15 IDENTIFY DEVICE DMA supported [OBS-4]", // ATA-4 r07-r14 only ". 14 NOP supported", ". 13 READ BUFFER supported", ". 12 WRITE BUFFER supported", ". 11 WRITE VERIFY supported [OBS-4]", // ATA-4 r07-r13 only ". 10 HPA feature set supported [OBS-ACS-3]", ". 9 DEVICE RESET supported", // ATA:0, ATAPI:1 ". 8 SERVICE interrupt enabled [OBS-ACS-2]", ". 7 Release interrupt enabled [OBS-ACS-2]", ". 6 Read look-ahead enabled", ". 5 Write cache enabled", ". 4 PACKET feature set supported", // ATA:0, ATAPI:1 ". 3 Power Management feature set supported", ". 2 Removable Media feature set supported [OBS-8]", ". 1 Security feature set enabled", ". 0 SMART feature set enabled", " 86 Commands and feature sets supported or enabled", ". 15 Words 119-120 are valid", ". 14 Reserved", ". 13 FLUSH CACHE EXT supported", ". 12 FLUSH CACHE supported", ". 11 DCO feature set supported [OBS-ACS-3]", ". 10 48-bit Address features set supported", ". 9 AAM feature set enabled [OBS-ACS-2]", ". 8 SET MAX security extension enabled [OBS-ACS-3]", ". 7 Reserved for Addr Offset Resvd Area Boot [OBS-ACS-3]", ". 6 SET FEATURES subcommand required to spin-up", ". 5 PUIS feature set enabled", ". 4 Removable Media Status Notification enabled [OBS-8]", ". 3 APM feature set enabled", ". 2 CFA feature set supported", ". 1 TCQ feature set supported [OBS-ACS-2]", ". 0 DOWNLOAD MICROCODE supported", " 87 Commands and feature sets supported or enabled", ". 15:14 Must be set to 0x1", ". 13 IDLE IMMEDIATE with UNLOAD FEATURE supported", ". 12:11 Reserved for TLC [OBS-ACS-3]", ". 10 URG bit for WRITE STREAM (DMA) EXT supported [OBS-8]", ". 9 URG bit for READ STREAM (DMA) EXT supported [OBS-8]", ". 8 64-bit World Wide Name supported", ". 7 WRITE DMA QUEUED FUA EXT supported [OBS-ACS-2]", ". 6 WRITE DMA/MULTIPLE FUA EXT supported", ". 5 GPL feature set supported", ". 4 Valid CONFIGURE STREAM has been executed [OBS-8]", ". 3 Media Card Pass Through Command supported [OBS-ACS-2]", ". 2 Media serial number is valid", ". 1 SMART self-test supported", ". 0 SMART error logging supported", " 88 Ultra DMA modes", ". 15 Reserved", ". 14 Ultra DMA mode 6 selected", ". 13 Ultra DMA mode 5 selected", ". 12 Ultra DMA mode 4 selected", ". 11 Ultra DMA mode 3 selected", ". 10 Ultra DMA mode 2 selected", ". 9 Ultra DMA mode 1 selected", ". 8 Ultra DMA mode 0 selected", ". 7 Reserved", ". 6 Ultra DMA mode 6 and below supported", ". 5 Ultra DMA mode 5 and below supported", ". 4 Ultra DMA mode 4 and below supported", ". 3 Ultra DMA mode 3 and below supported", ". 2 Ultra DMA mode 2 and below supported", ". 1 Ultra DMA mode 1 and below supported", ". 0 Ultra DMA mode 0 supported", " 89 SECURITY ERASE UNIT time", ". 15 Bits 14:8 of value are valid", // ACS-3 ". 14:0 SECURITY ERASE UNIT time value", // value*2 minutes " 90 ENHANCED SECURITY ERASE UNIT time", ". 15 Bits 14:8 of value are valid", // ACS-3 ". 14:0 ENHANCED SECURITY ERASE UNIT time value", // value*2 minutes " 91 Current APM level", ". 15:8 Reserved", // ACS-3 ". 7:0 Current APM level value", " 92 Master Password Identifier", // ATA-7: Master Password Revision Code " 93 Hardware reset result (PATA)", ". 15:14 Must be set to 0x1", ". 13 Device detected CBLID- above(1)/below(0) ViHB", ". 12 Reserved", ". 11 Device 1 asserted PDIAG-", ". 10:9 Device 1 detection method: -, Jumper, CSEL, other", ". 8 Must be set to 1", ". 7 Reserved", ". 6 Device 0 responds when device 1 selected", ". 5 Device 0 detected the assertion of DASP-", ". 4 Device 0 detected the assertion of PDIAG-", ". 3 Device 0 passed diagnostics", ". 2:1 Device 0 detection method: -, Jumper, CSEL, other", ". 0 Must be set to 1", " 94 AAM level [OBS-ACS-2]", ". 15:8 Recommended AAM level [OBS-ACS-2]", ". 7:0 Current AAM level [OBS-ACS-2]", " 95 Stream Minimum Request Size", " 96 Streaming Transfer Time - DMA", " 97 Streaming Access Latency - DMA and PIO", " 98-99 Streaming Performance Granularity (DWord)", "100-103 User addressable sectors for 48-bit commands (QWord)", "104 Streaming Transfer Time - PIO", "105 Max blocks of LBA Range Entries per DS MANAGEMENT cmd", "106 Physical sector size / logical sector size", ". 15:14 Must be set to 0x1", ". 13 Multiple logical sectors per physical sector", ". 12 Logical Sector longer than 256 words", ". 11:4 Reserved", ". 3:0 2^X logical sectors per physical sector", "107 Inter-seek delay for ISO 7779 acoustic testing", "108-111 World Wide Name", "112-115 Reserved", // ATA-7: Reserved for world wide name extension to 128 bits "116 Reserved for TLC [OBS-ACS-3]", "117-118 Logical sector size (DWord)", "119 Commands and feature sets supported", ". 15:14 Must be set to 0x1", ". 13:10 Reserved", ". 9 DSN feature set supported", // ACS-3 ". 8 Accessible Max Address Config feature set supported", // ACS-3 ". 7 Extended Power Conditions feature set supported", ". 6 Sense Data Reporting feature set supported", ". 5 Free-fall Control feature set supported", ". 4 DOWNLOAD MICROCODE with mode 3 supported", ". 3 READ/WRITE LOG DMA EXT supported", ". 2 WRITE UNCORRECTABLE EXT supported", ". 1 Write-Read-Verify feature set supported", ". 0 Reserved for DDT [OBS-ACS-3]", "120 Commands and feature sets supported or enabled", ". 15:14 Must be set to 0x1", ". 13:10 Reserved", ". 9 DSN feature set enabled", // ACS-3 ". 8 Reserved", ". 7 Extended Power Conditions feature set enabled", ". 6 Sense Data Reporting feature set enabled", ". 5 Free-fall Control feature set enabled", ". 4 DOWNLOAD MICROCODE with mode 3 supported", ". 3 READ/WRITE LOG DMA EXT supported", ". 2 WRITE UNCORRECTABLE EXT supported", ". 1 Write-Read-Verify feature set enabled", ". 0 Reserved for DDT [OBS-ACS-3]", "121-126 ATA: Reserved", "121-124 ATAPI: Reserved", "125 ATAPI: Byte count = 0 behavior", "126 ATAPI: Byte count = 0 behavior [OBS-6]", "127 Removable Media Status Notification [OBS-8]", ". 15:1 Reserved", ". 0 Removable Media Status Notification supported", "128 Security status", ". 15:9 Reserved", ". 8 Master password capability: 0 = High, 1 = Maximum", ". 7:6 Reserved", ". 5 Enhanced security erase supported", ". 4 Security count expired", ". 3 Security frozen", ". 2 Security locked", ". 1 Security enabled", ". 0 Security supported", "129-159 Vendor specific", "160 CFA power mode", // ". 15 Word 160 supported", // ". 14 Reserved", // ". 13 CFA power mode 1 is required for some commands", // ". 12 CFA power mode 1 disabled", // ". 11:0 Maximum current in mA", "161-167 Reserved for CFA", "168 Form factor", ". 15:4 Reserved", ". 3:0 Nominal form factor: -, 5.25, 3.5, 2.5, 1.8, ...", // <1.8, ACS-4: mSATA, M.2, ... "169 DATA SET MANAGEMENT command support", ". 15:1 Reserved", ". 0 Trim bit in DATA SET MANAGEMENT command supported", "170-173 Additional product identifier (String)", "174-175 Reserved", "176-205 Current media serial number (String)", "206 SCT Command Transport", ". 15:12 Vendor specific", ". 11:8 Reserved", ". 7 Reserved for Serial ATA", ". 6 Reserved", ". 5 SCT Data Tables supported", ". 4 SCT Feature Control supported", ". 3 SCT Error Recovery Control supported", ". 2 SCT Write Same supported", ". 1 SCT Read/Write Long supported [OBS-ACS-2]", ". 0 SCT Command Transport supported", "207-208 Reserved", // ATA-8: Reserved for CE-ATA "209 Alignment of logical sectors", ". 15:14 Must be set to 0x1", ". 13:0 Logical sector offset", "210-211 Write-Read-Verify sector count mode 3 (DWord)", "212-213 Write-Read-Verify sector count mode 2 (DWord)", "214 NV Cache capabilities [OBS-ACS-3]", ". 15:12 NV Cache feature set version [OBS-ACS-3]", ". 11:8 NV Cache Power Mode feature set version [OBS-ACS-3]", ". 7:5 Reserved [OBS-ACS-3]", ". 4 NV Cache feature set enabled [OBS-ACS-3]", ". 3:2 Reserved", ". 1 NV Cache Power Mode feature set enabled [OBS-ACS-3]", ". 0 NV Cache Power Mode feature set supported [OBS-ACS-3]", "215-216 NV Cache size in logical blocks (DWord) [OBS-ACS-3]", "217 Nominal media rotation rate", "218 Reserved", "219 NV Cache options [OBS-ACS-3]", ". 15:8 Reserved [OBS-ACS-3]", ". 7:0 Estimated time to spin up in seconds [OBS-ACS-3]", "220 Write-Read-Verify mode", ". 15:8 Reserved", ". 7:0 Write-Read-Verify feature set current mode", "221 Reserved", "222 Transport major version number", ". 15:12 Transport: 0x0 = Parallel, 0x1 = Serial, 0xe = PCIe", // PCIe: ACS-4 ". 11:9 Reserved | Reserved", ". 8 Reserved | SATA 3.3", // ACS-4 ". 7 Reserved | SATA 3.2", // ACS-4 ". 6 Reserved | SATA 3.1", // ACS-3 ". 5 Reserved | SATA 3.0", // ACS-2 ". 4 Reserved | SATA 2.6", ". 3 Reserved | SATA 2.5", ". 2 Reserved | SATA II: Extensions", ". 1 ATA/ATAPI-7 | SATA 1.0a", ". 0 ATA8-APT | ATA8-AST", "223 Transport minor version number", "224-229 Reserved", "230-233 Extended number of user addressable sectors (QWord)", "234 Minimum blocks per DOWNLOAD MICROCODE mode 3 command", "235 Maximum blocks per DOWNLOAD MICROCODE mode 3 command", "236-254 Reserved", "255 Integrity word", ". 15:8 Checksum", ". 7:0 Signature" }; const int num_identify_descriptions = sizeof(identify_descriptions)/sizeof(identify_descriptions[0]); static inline unsigned short get_word(const void * id, int word) { const unsigned char * p = ((const unsigned char *)id) + 2 * word; return p[0] + (p[1] << 8); } void ata_print_identify_data(const void * id, bool all_words, int bit_level) { // ATA or ATAPI ? unsigned short w = get_word(id, 0); bool is_atapi = ((w & 0x8000) && (w != 0x848a/*CompactFlash Signature*/)); int prev_word = -1, prev_bit = -1; pout("Word %s Value Description\n", (bit_level >= 0 ? "Bit " : " ")); for (int i = 0; i < num_identify_descriptions; i++) { // Parse table entry const char * desc = identify_descriptions[i]; int word = prev_word, word2 = -1; int bit = -1, bit2 = -1; int nc; unsigned v1, v2; if (word >= 0 && sscanf(desc, ". %u:%u %n", &v1, &v2, (nc=-1, &nc)) == 2 && nc > 0 && 16 > v1 && v1 > v2) { bit = v1; bit2 = v2; } else if (word >= 0 && sscanf(desc, ". %u %n", &v1, (nc=-1, &nc)) == 1 && nc > 0 && v1 < 16) { bit = v1; } else if (sscanf(desc, "%u-%u %n", &v1, &v2, (nc=-1, &nc)) == 2 && nc > 0 && v1 < v2 && v2 < 256) { word = v1, word2 = v2; } else if (sscanf(desc, "%u %n", &v1, (nc=-1, &nc)) == 1 && nc > 0 && v1 < 256) { word = v1; } else { pout("Error: #%d: Syntax\n", i); continue; } desc += nc; // Check for ATA/ATAPI specific entries if (str_starts_with(desc, "ATA: ")) { if (is_atapi) continue; desc += sizeof("ATA: ")-1; } else if (str_starts_with(desc, "ATAPI: ")) { if (!is_atapi) continue; } // Check table entry if (bit < 0) { if (word != prev_word+1) { pout("Error: #%d: Missing word %d\n", i, prev_word+1); return; } else if (prev_bit > 0) { pout("Error: #%d: Missing bit 0 from word %d\n", i, prev_word); return; } } else if (!((prev_bit < 0 && bit == 15) || bit == prev_bit-1)) { pout("Error: #%d: Missing bit %d from word %d\n", i, bit+1, word); return; } w = get_word(id, word); bool w_is_set = (w != 0x0000 && w != 0xffff); if (bit >= 0) { int b; if (bit2 >= 0) b = (w >> bit2) & ~(~0U << (bit-bit2+1)); else b = (w >> bit) & 1; if ( (bit_level >= 0 && b) || (bit_level >= 1 && w_is_set) || (bit_level >= 2 && all_words)) { if (bit2 >= 0) { // Print bitfield char valstr[20]; snprintf(valstr, sizeof(valstr), "0x%0*x", (bit - bit2 + 4) >> 2, b); pout("%4d %2d:%-2d %6s %s\n", word, bit, bit2, valstr, desc); } else { // Print bit pout("%4d %2d %u %s\n", word, bit, b, desc); } } prev_bit = (bit2 >= 0 ? bit2 : bit); } else { if (word2 >= 0) { for (int j = word+1; !w_is_set && j <= word2; j++) { if (get_word(id, j) != w) w_is_set = true; } // Print word array if (all_words || w_is_set) { pout("%s%4d-%-3d %s", (bit_level >= 0 ? "\n" : ""), word, word2, (bit_level >= 0 ? "- " : "")); if (!w_is_set) { pout("0x%02x... %s\n", w & 0xff, desc); } else { bool is_str = !!strstr(desc, "(String)"); pout(". %s", desc); for (int j = word; j <= word2; j += 4) { if (j + 2*4 < word2 && !nonempty((const unsigned char *)id + 2*j, 2*(word2-j+1))) { // Remaining words are null pout("\n%4d-%-3d %s0x0000:0000:0000:00...", j, word2, (bit_level >= 0 ? ". " : "")); break; } // Print 4 words in a row pout("\n%4d-%-3d %s0x", j, (j+3 <= word2 ? j+3 : word2), (bit_level >= 0 ? ". " : "")); int k; for (k = 0; k < 4 && j+k <= word2; k++) pout("%s%04x", (k == 0 ? "" : ":"), get_word(id, j+k)); if (is_str) { // Append little endian string pout("%*s \"", 20 - 5 * k, ""); for (k = 0; k < 4 && j+k <= word2; k++) { char c2 = ((const char *)id)[2*(j+k) ]; char c1 = ((const char *)id)[2*(j+k) + 1]; pout("%c%c", (' ' <= c1 && c1 <= '~' ? c1 : '.'), (' ' <= c2 && c2 <= '~' ? c2 : '.') ); } pout("\""); } } // Print decimal value of D/QWords if (word + 1 == word2 && strstr(desc, "(DWord)")) pout(" (%u)\n", ((unsigned)get_word(id, word2) << 16) | w); else if (word + 3 == word2 && strstr(desc, "(QWord)")) pout(" (%" PRIu64 ")\n", ((uint64_t)get_word(id, word + 3) << 48) | ((uint64_t)get_word(id, word + 2) << 32) | ((unsigned)get_word(id, word + 1) << 16) | (unsigned)w); else pout("\n"); } } } else { // Print word if (all_words || w_is_set) pout("%s%4d %s0x%04x %s\n", (bit_level >= 0 ? "\n" : ""), word, (bit_level >= 0 ? "- " : ""), w, desc); } prev_word = (word2 >= 0 ? word2 : word); prev_bit = -1; } } pout("\n"); } smartmontools-7.0/ataidentify.h0000644000175000010010000000063013336335341013664 00000000000000/* * ataidentify.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2012 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ATAIDENTIFY_H #define ATAIDENTIFY_H #define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 4760 2018-08-19 18:45:53Z chrfranke $" void ata_print_identify_data(const void * id, bool all_words, int bit_level); #endif // ATAIDENTIFY_H smartmontools-7.0/ataprint.cpp0000644000175000010010000047172413401001476013550 00000000000000/* * ataprint.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include #include "atacmdnames.h" #include "atacmds.h" #include "ataidentify.h" #include "dev_interface.h" #include "ataprint.h" #include "smartctl.h" #include "sg_unaligned.h" #include "utility.h" #include "knowndrives.h" const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4842 2018-12-02 16:07:26Z chrfranke $" ATAPRINT_H_CVSID; static const char * infofound(const char *output) { return (*output ? output : "[No Information Found]"); } // Return true if '-T permissive' is specified, // used to ignore missing capabilities static bool is_permissive() { if (!failuretest_permissive) return false; failuretest_permissive--; return true; } /* 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). If the meanings of the flags of * the error register are not known for the given command then it returns an * empty string. * * The meanings of the flags of the error register for all commands are * described in the ATA spec and could all be supported here in theory. * 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. */ static std::string format_st_er_desc( unsigned char CR, unsigned char FR, unsigned char ST, unsigned char ER, unsigned short SC, const ata_smart_errorlog_error_struct * lba28_regs, const ata_smart_exterrlog_error * lba48_regs ) { 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 COMPLETION 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; std::string str; 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=SC; 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=SC; break; case 0x25: // READ DMA EXT case 0x26: // READ DMA QUEUED EXT case 0xC7: // READ DMA QUEUED case 0xC8: // READ DMA (with retries) case 0xC9: // READ DMA (without retries, obsolete since ATA-5) case 0x60: // READ FPDMA QUEUED (NCQ) 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=SC; 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 str; // "" break; } break; case 0xB1: /* DEVICE CONFIGURATION */ switch (FR) { case 0xC0: /* DEVICE CONFIGURATION RESTORE */ error_flag[2] = abrt; break; default: return str; // "" break; } break; case 0xCA: // WRITE DMA (with retries) case 0xCB: // WRITE DMA (without retries, obsolete since ATA-5) 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 case 0x61: // WRITE FPDMA QUEUED (NCQ) 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=SC; break; case 0xE4: // READ BUFFER case 0xE8: // WRITE BUFFER error_flag[2] = abrt; break; default: return str; // "" } /* We ignore any status flags other than Device Fault and Error */ if (uses_device_fault && (ST & (1 << 5))) { str = "Device Fault"; if (ST & 1) // Error flag str += "; "; } if (ST & 1) { // Error flag int count = 0; str += "Error: "; for (i = 7; i >= 0; i--) if ((ER & (1 << i)) && (error_flag[i])) { if (count++ > 0) str += ", "; str += error_flag[i]; } } // If the error was a READ or WRITE error, print the Logical Block // Address (LBA) at which the read or write failed. if (print_lba) { // print number of sectors, if known, and append to print string if (print_sector) str += strprintf(" %d sectors", print_sector); if (lba28_regs) { unsigned lba; // bits 24-27: bits 0-3 of DH lba = 0xf & lba28_regs->drive_head; lba <<= 8; // bits 16-23: CH lba |= lba28_regs->cylinder_high; lba <<= 8; // bits 8-15: CL lba |= lba28_regs->cylinder_low; lba <<= 8; // bits 0-7: SN lba |= lba28_regs->sector_number; str += strprintf(" at LBA = 0x%08x = %u", lba, lba); } else if (lba48_regs) { // This assumes that upper LBA registers are 0 for 28-bit commands // (TODO: detect 48-bit commands above) uint64_t lba48; lba48 = lba48_regs->lba_high_register_hi; lba48 <<= 8; lba48 |= lba48_regs->lba_mid_register_hi; lba48 <<= 8; lba48 |= lba48_regs->lba_low_register_hi; lba48 |= lba48_regs->device_register & 0xf; lba48 <<= 8; lba48 |= lba48_regs->lba_high_register; lba48 <<= 8; lba48 |= lba48_regs->lba_mid_register; lba48 <<= 8; lba48 |= lba48_regs->lba_low_register; str += strprintf(" at LBA = 0x%08" PRIx64 " = %" PRIu64, lba48, lba48); } } return str; } static inline std::string format_st_er_desc( const ata_smart_errorlog_struct * data) { return format_st_er_desc( data->commands[4].commandreg, data->commands[4].featuresreg, data->error_struct.status, data->error_struct.error_register, data->error_struct.sector_count, &data->error_struct, (const ata_smart_exterrlog_error *)0); } static inline std::string format_st_er_desc( const ata_smart_exterrlog_error_log * data) { return format_st_er_desc( data->commands[4].command_register, data->commands[4].features_register, data->error.status_register, data->error.error_register, data->error.count_register_hi << 8 | data->error.count_register, (const ata_smart_errorlog_error_struct *)0, &data->error); } static const char * get_form_factor(unsigned short word168) { // Bits 0:3 are the form factor // Table A.32 of T13/2161-D (ACS-3) Revision 4p, September 19, 2013 // Table 236 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014 switch (word168 & 0xF) { case 0x1: return "5.25 inches"; case 0x2: return "3.5 inches"; case 0x3: return "2.5 inches"; case 0x4: return "1.8 inches"; case 0x5: return "< 1.8 inches"; case 0x6: return "mSATA"; // ACS-4 case 0x7: return "M.2"; // ACS-4 case 0x8: return "MicroSSD"; // ACS-4 case 0x9: return "CFast"; // ACS-4 default : return 0; } } static int find_msb(unsigned short word) { for (int bit = 15; bit >= 0; bit--) if (word & (1 << bit)) return bit; return -1; } static const char * get_ata_major_version(const ata_identify_device * drive) { // Table 13 of T13/1153D (ATA/ATAPI-4) revision 18, August 19, 1998 // Table 48 of T13/BSR INCITS 529 (ACS-4) Revision 16, February 21, 2017 switch (find_msb(drive->major_rev_num)) { case 14: return "ACS >4 (14)"; case 13: return "ACS >4 (13)"; case 12: return "ACS >4 (12)"; case 11: return "ACS-4"; case 10: return "ACS-3"; case 9: return "ACS-2"; case 8: return "ATA8-ACS"; case 7: return "ATA/ATAPI-7"; case 6: return "ATA/ATAPI-6"; case 5: return "ATA/ATAPI-5"; case 4: return "ATA/ATAPI-4"; case 3: return "ATA-3"; case 2: return "ATA-2"; case 1: return "ATA-1"; default: return 0; } } static const char * get_ata_minor_version(const ata_identify_device * drive) { // Table 10 of X3T13/2008D (ATA-3) Revision 7b, January 27, 1997 // Table 28 of T13/1410D (ATA/ATAPI-6) Revision 3b, February 26, 2002 // Table 31 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 // Table 46 of T13/BSR INCITS 529 (ACS-4) Revision 08, April 28, 2015 switch (drive->minor_rev_num) { case 0x0001: return "ATA-1 X3T9.2/781D prior to revision 4"; case 0x0002: return "ATA-1 published, ANSI X3.221-1994"; case 0x0003: return "ATA-1 X3T9.2/781D revision 4"; case 0x0004: return "ATA-2 published, ANSI X3.279-1996"; case 0x0005: return "ATA-2 X3T10/948D prior to revision 2k"; case 0x0006: return "ATA-3 X3T10/2008D revision 1"; case 0x0007: return "ATA-2 X3T10/948D revision 2k"; case 0x0008: return "ATA-3 X3T10/2008D revision 0"; case 0x0009: return "ATA-2 X3T10/948D revision 3"; case 0x000a: return "ATA-3 published, ANSI X3.298-1997"; case 0x000b: return "ATA-3 X3T10/2008D revision 6"; // 1st ATA-3 revision with SMART case 0x000c: return "ATA-3 X3T13/2008D revision 7 and 7a"; case 0x000d: return "ATA/ATAPI-4 X3T13/1153D revision 6"; case 0x000e: return "ATA/ATAPI-4 T13/1153D revision 13"; case 0x000f: return "ATA/ATAPI-4 X3T13/1153D revision 7"; case 0x0010: return "ATA/ATAPI-4 T13/1153D revision 18"; case 0x0011: return "ATA/ATAPI-4 T13/1153D revision 15"; case 0x0012: return "ATA/ATAPI-4 published, ANSI NCITS 317-1998"; case 0x0013: return "ATA/ATAPI-5 T13/1321D revision 3"; case 0x0014: return "ATA/ATAPI-4 T13/1153D revision 14"; case 0x0015: return "ATA/ATAPI-5 T13/1321D revision 1"; case 0x0016: return "ATA/ATAPI-5 published, ANSI NCITS 340-2000"; case 0x0017: return "ATA/ATAPI-4 T13/1153D revision 17"; case 0x0018: return "ATA/ATAPI-6 T13/1410D revision 0"; case 0x0019: return "ATA/ATAPI-6 T13/1410D revision 3a"; case 0x001a: return "ATA/ATAPI-7 T13/1532D revision 1"; case 0x001b: return "ATA/ATAPI-6 T13/1410D revision 2"; case 0x001c: return "ATA/ATAPI-6 T13/1410D revision 1"; case 0x001d: return "ATA/ATAPI-7 published, ANSI INCITS 397-2005"; case 0x001e: return "ATA/ATAPI-7 T13/1532D revision 0"; case 0x001f: return "ACS-3 T13/2161-D revision 3b"; case 0x0021: return "ATA/ATAPI-7 T13/1532D revision 4a"; case 0x0022: return "ATA/ATAPI-6 published, ANSI INCITS 361-2002"; case 0x0027: return "ATA8-ACS T13/1699-D revision 3c"; case 0x0028: return "ATA8-ACS T13/1699-D revision 6"; case 0x0029: return "ATA8-ACS T13/1699-D revision 4"; case 0x0031: return "ACS-2 T13/2015-D revision 2"; case 0x0033: return "ATA8-ACS T13/1699-D revision 3e"; case 0x0039: return "ATA8-ACS T13/1699-D revision 4c"; case 0x0042: return "ATA8-ACS T13/1699-D revision 3f"; case 0x0052: return "ATA8-ACS T13/1699-D revision 3b"; case 0x005e: return "ACS-4 T13/BSR INCITS 529 revision 5"; case 0x006d: return "ACS-3 T13/2161-D revision 5"; case 0x0082: return "ACS-2 published, ANSI INCITS 482-2012"; case 0x0107: return "ATA8-ACS T13/1699-D revision 2d"; case 0x010a: return "ACS-3 published, ANSI INCITS 522-2014"; case 0x0110: return "ACS-2 T13/2015-D revision 3"; case 0x011b: return "ACS-3 T13/2161-D revision 4"; default: return 0; } } static const char * get_pata_version(unsigned short word222, char (& buf)[32]) { switch (word222 & 0x0fff) { default: snprintf(buf, sizeof(buf), "Unknown (0x%03x)", word222 & 0x0fff); return buf; case 0x001: case 0x003: return "ATA8-APT"; case 0x002: return "ATA/ATAPI-7"; } } static const char * get_sata_version(unsigned short word222) { switch (find_msb(word222 & 0x0fff)) { case 11: return "SATA >3.3 (11)"; case 10: return "SATA >3.3 (10)"; case 9: return "SATA >3.3 (9)"; case 8: return "SATA 3.3"; case 7: return "SATA 3.2"; case 6: return "SATA 3.1"; case 5: return "SATA 3.0"; case 4: return "SATA 2.6"; case 3: return "SATA 2.5"; case 2: return "SATA II Ext"; case 1: return "SATA 1.0a"; case 0: return "ATA8-AST"; default: return 0; } } static const char * get_sata_speed(int speed) { if (speed <= 0) return 0; switch (speed) { default: return ">6.0 Gb/s (7)"; case 6: return ">6.0 Gb/s (6)"; case 5: return ">6.0 Gb/s (5)"; case 4: return ">6.0 Gb/s (4)"; case 3: return "6.0 Gb/s"; case 2: return "3.0 Gb/s"; case 1: return "1.5 Gb/s"; } } static void jset_sata_speed(const char * key, int value, int speed, const char * str) { if (speed <= 0) return; json::ref jref = jglb["interface_speed"][key]; jref["sata_value"] = value; if (str) jref["string"] = str; int ups; switch (speed) { case 3: ups = 60; break; case 2: ups = 30; break; case 1: ups = 15; break; default: return; } jref["units_per_second"] = ups; jref["bits_per_unit"] = 100000000; } static void print_sata_version_and_speed(unsigned short word222, unsigned short word076, unsigned short word077) { int allspeeds = (!(word076 & 0x0001) ? (word076 & 0x00fe) : 0); int maxspeed = (allspeeds ? find_msb(allspeeds) : 0); int curspeed = (!(word077 & 0x0001) ? ((word077 >> 1) & 0x7) : 0); const char * verstr = get_sata_version(word222); const char * maxstr = get_sata_speed(maxspeed); const char * curstr = get_sata_speed(curspeed); jout("SATA Version is: %s%s%s%s%s%s\n", (verstr ? verstr : "Unknown"), (maxstr ? ", " : ""), (maxstr ? maxstr : ""), (curstr ? " (current: " : ""), (curstr ? curstr : ""), (curstr ? ")" : "")); if (verstr) jglb["sata_version"]["string"] = verstr; jglb["sata_version"]["value"] = word222 & 0x0fff; jset_sata_speed("max", allspeeds, maxspeed, maxstr); jset_sata_speed("current", curspeed, curspeed, curstr); } static void print_drive_info(const ata_identify_device * drive, const ata_size_info & sizes, int rpm, const drive_settings * dbentry) { // format drive information (with byte swapping as needed) char model[40+1], serial[20+1], firmware[8+1]; ata_format_id_string(model, drive->model, sizeof(model)-1); ata_format_id_string(serial, drive->serial_no, sizeof(serial)-1); ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); // Print model family if known if (dbentry && *dbentry->modelfamily) { jout("Model Family: %s\n", dbentry->modelfamily); jglb["model_family"] = dbentry->modelfamily; } jout("Device Model: %s\n", infofound(model)); jglb["model_name"] = model; if (!dont_print_serial_number) { jout("Serial Number: %s\n", infofound(serial)); jglb["serial_number"] = serial; unsigned oui = 0; uint64_t unique_id = 0; int naa = ata_get_wwn(drive, oui, unique_id); if (naa >= 0) { jout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id); jglb["wwn"]["naa"] = naa; jglb["wwn"]["oui"] = oui; jglb["wwn"]["id"] = unique_id; } } // Additional Product Identifier (OEM Id) string in words 170-173 // (e08130r1, added in ACS-2 Revision 1, December 17, 2008) if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) { char add[8+1]; ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1); if (add[0]) { jout("Add. Product Id: %s\n", add); jglb["ata_additional_product_id"] = add; } } jout("Firmware Version: %s\n", infofound(firmware)); jglb["firmware_version"] = firmware; if (sizes.capacity) { // Print capacity char num[64], cap[32]; jout("User Capacity: %s bytes [%s]\n", format_with_thousands_sep(num, sizeof(num), sizes.capacity), format_capacity(cap, sizeof(cap), sizes.capacity)); jglb["user_capacity"]["blocks"].set_unsafe_uint64(sizes.sectors); jglb["user_capacity"]["bytes"].set_unsafe_uint64(sizes.capacity); // Print sector sizes. if (sizes.phy_sector_size == sizes.log_sector_size) jout("Sector Size: %u bytes logical/physical\n", sizes.log_sector_size); else { jout("Sector Sizes: %u bytes logical, %u bytes physical", sizes.log_sector_size, sizes.phy_sector_size); if (sizes.log_sector_offset) pout(" (offset %u bytes)", sizes.log_sector_offset); jout("\n"); } jglb["logical_block_size"] = sizes.log_sector_size; jglb["physical_block_size"] = sizes.phy_sector_size; } // Print nominal media rotation rate if reported if (rpm) { if (rpm == 1) jout("Rotation Rate: Solid State Device\n"); else if (rpm > 1) jout("Rotation Rate: %d rpm\n", rpm); else pout("Rotation Rate: Unknown (0x%04x)\n", -rpm); if (rpm > 0) jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm); } // Print form factor if reported unsigned short word168 = drive->words088_255[168-88]; if (word168) { const char * form_factor = get_form_factor(word168); if (form_factor) jout("Form Factor: %s\n", form_factor); else jout("Form Factor: Unknown (0x%04x)\n", word168); jglb["form_factor"]["ata_value"] = word168; jglb["form_factor"]["name"] = form_factor; } // See if drive is recognized jout("Device is: %s\n", !dbentry ? "Not in smartctl database [for details use: -P showall]": "In smartctl database [for details use: -P show]"); jglb["in_smartctl_database"] = !!dbentry; // Print ATA version std::string ataver; if ( (drive->major_rev_num != 0x0000 && drive->major_rev_num != 0xffff) || (drive->minor_rev_num != 0x0000 && drive->minor_rev_num != 0xffff)) { const char * majorver = get_ata_major_version(drive); const char * minorver = get_ata_minor_version(drive); if (majorver && minorver && str_starts_with(minorver, majorver)) { // Major and minor strings match, print minor string only ataver = minorver; } else { if (majorver) ataver = majorver; else ataver = strprintf("Unknown(0x%04x)", drive->major_rev_num); if (minorver) ataver += strprintf(", %s", minorver); else if (drive->minor_rev_num != 0x0000 && drive->minor_rev_num != 0xffff) ataver += strprintf(" (unknown minor revision code: 0x%04x)", drive->minor_rev_num); else ataver += " (minor revision not indicated)"; } } jout("ATA Version is: %s\n", infofound(ataver.c_str())); if (!ataver.empty()) { jglb["ata_version"]["string"] = ataver; jglb["ata_version"]["major_value"] = drive->major_rev_num; jglb["ata_version"]["minor_value"] = drive->minor_rev_num; } // Print Transport specific version unsigned short word222 = drive->words088_255[222-88]; if (word222 != 0x0000 && word222 != 0xffff) switch (word222 >> 12) { case 0x0: // PATA { char buf[32] = ""; pout("Transport Type: Parallel, %s\n", get_pata_version(word222, buf)); } break; case 0x1: // SATA print_sata_version_and_speed(word222, drive->words047_079[76-47], drive->words047_079[77-47]); break; case 0xe: // PCIe (ACS-4) pout("Transport Type: PCIe (0x%03x)\n", word222 & 0x0fff); break; default: pout("Transport Type: Unknown (0x%04x)\n", word222); break; } // print current time and date and timezone time_t now = time(0); char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now); jout("Local Time is: %s\n", timedatetz); jglb["local_time"]["time_t"] = now; jglb["local_time"]["asctime"] = timedatetz; // Print warning message, if there is one if (dbentry && *dbentry->warningmsg) pout("\n==> WARNING: %s\n\n", dbentry->warningmsg); } static 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"; else return "is in a Reserved state"; } } // prints verbose value Off-line data collection status byte static void PrintSmartOfflineStatus(const ata_smart_values * data) { json::ref jref = jglb["ata_smart_data"]["offline_data_collection"]["status"]; jout("Offline data collection status: (0x%02x)\t", (int)data->offline_data_collection_status); jref["value"] = data->offline_data_collection_status; // Off-line data collection status byte is not a reserved // or vendor specific value jout("Offline data collection activity\n" "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status)); jref["string"] = OfflineDataCollectionStatus(data->offline_data_collection_status); switch (data->offline_data_collection_status & 0x7f) { case 0x02: jref["passed"] = true; break; case 0x06: jref["passed"] = false; break; } // 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; } static void PrintSmartSelfExecStatus(const ata_smart_values * data, firmwarebug_defs firmwarebugs) { unsigned char status = data->self_test_exec_status; jout("Self-test execution status: "); switch (data->self_test_exec_status >> 4) { case 0: jout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status); jout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n"); break; case 1: jout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t", status); jout("the host.\n"); break; case 2: jout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t", status); jout("by the host with a hard or soft reset.\n"); break; case 3: jout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t", status); jout("occurred while the device was executing\n\t\t\t\t\t"); jout("its self-test routine and the device \n\t\t\t\t\t"); jout("was unable to complete the self-test \n\t\t\t\t\t"); jout("routine.\n"); break; case 4: jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status); jout("a test element that failed and the test\n\t\t\t\t\t"); jout("element that failed is not known.\n"); break; case 5: jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status); jout("the electrical element of the test\n\t\t\t\t\t"); jout("failed.\n"); break; case 6: jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status); jout("the servo (and/or seek) element of the \n\t\t\t\t\t"); jout("test failed.\n"); break; case 7: jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status); jout("the read element of the test failed.\n"); break; case 8: jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status); jout("a test element that failed and the\n\t\t\t\t\t"); jout("device is suspected of having handling\n\t\t\t\t\t"); jout("damage.\n"); break; case 15: if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) { pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status); pout("with unknown result or self-test in\n\t\t\t\t\t"); pout("progress with less than 10%% remaining.\n"); } else { jout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t", status); jout("%1d0%% of test remaining.\n", status & 0x0f); } break; default: jout("(%4d)\tReserved.\n", status); break; } json::ref jref = jglb["ata_smart_data"]["self_test"]["status"]; jref["value"] = status; const char * msg; // TODO: Use common function for smartctl/smartd switch (status >> 4) { case 0x0: msg = "completed without error"; break; case 0x1: msg = "was aborted by the host"; break; case 0x2: msg = "was interrupted by the host with a reset"; break; case 0x3: msg = "could not complete due to a fatal or unknown error"; break; case 0x4: msg = "completed with error (unknown test element)"; break; case 0x5: msg = "completed with error (electrical test element)"; break; case 0x6: msg = "completed with error (servo/seek test element)"; break; case 0x7: msg = "completed with error (read test element)"; break; case 0x8: msg = "completed with error (handling damage?)"; break; default: msg = 0; } if (msg) { jref["string"] = msg; switch (status >> 4) { case 0x1: case 0x2: case 0x3: break; // aborted -> unknown default: jref["passed"] = ((status >> 4) == 0x0); } } else if ((status >> 4) == 0xf) { jref["string"] = strprintf("in progress, %u0%% remaining", status & 0xf); jref["remaining_percent"] = (status & 0xf) * 10; } } static void PrintSmartTotalTimeCompleteOffline (const ata_smart_values * data) { jout("Total time to complete Offline \n"); jout("data collection: \t\t(%5d) seconds.\n", (int)data->total_time_to_complete_off_line); jglb["ata_smart_data"]["offline_data_collection"]["completion_seconds"] = data->total_time_to_complete_off_line; } static void PrintSmartOfflineCollectCap(const ata_smart_values *data) { json::ref jref = jglb["ata_smart_data"]["capabilities"]; jout("Offline data collection\n"); jout("capabilities: \t\t\t (0x%02x) ", (int)data->offline_data_collection_capability); jref["values"][0] = data->offline_data_collection_capability; if (data->offline_data_collection_capability == 0x00){ jout("\tOffline data collection not supported.\n"); } else { jout( "%s\n", isSupportExecuteOfflineImmediate(data)? "SMART execute Offline immediate." : "No SMART execute Offline immediate."); jref["exec_offline_immediate_supported"] = isSupportExecuteOfflineImmediate(data); // TODO: Bit 1 is vendor specific pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? "Auto Offline data collection on/off support.": "No Auto Offline data collection support."); jout( "\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."); jref["offline_is_aborted_upon_new_cmd"] = isSupportOfflineAbort(data); jout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? "Offline surface scan supported.": "No Offline surface scan supported."); jref["offline_surface_scan_supported"] = isSupportOfflineSurfaceScan(data); jout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? "Self-test supported.": "No Self-test supported."); jref["self_tests_supported"] = isSupportSelfTest(data); jout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? "Conveyance Self-test supported.": "No Conveyance Self-test supported."); jref["conveyance_self_test_supported"] = isSupportConveyanceSelfTest(data); jout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? "Selective Self-test supported.": "No Selective Self-test supported."); jref["selective_self_test_supported"] = isSupportSelectiveSelfTest(data); } } static void PrintSmartCapability(const ata_smart_values *data) { json::ref jref = jglb["ata_smart_data"]["capabilities"]; jout("SMART capabilities: "); jout("(0x%04x)\t", (int)data->smart_capability); jref["values"][1] = data->smart_capability; if (data->smart_capability == 0x00) jout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n"); else { jout("%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."); jref["attribute_autosave_enabled"] = !!(data->smart_capability & 0x01); // TODO: Info possibly invalid or misleading // ATA-3 - ATA-5: Bit shall be set // ATA-6 - ACS-3: Bit shall be set to indicate support for // SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE if (data->smart_capability & 0x02) pout("\t\t\t\t\tSupports SMART auto save timer.\n"); } } static void PrintSmartErrorLogCapability(const ata_smart_values * data, const ata_identify_device * identity) { bool capable = isSmartErrorLogCapable(data, identity); jout("Error logging capability: (0x%02x)\tError logging %ssupported.\n", data->errorlog_capability, (capable ? "" : "NOT ")); jglb["ata_smart_data"]["capabilities"]["error_logging_supported"] = capable; } static void PrintSmartShortSelfTestPollingTime(const ata_smart_values * data) { jout("Short self-test routine \n"); if (isSupportSelfTest(data)) { jout("recommended polling time: \t (%4d) minutes.\n", (int)data->short_test_completion_time); jglb["ata_smart_data"]["self_test"]["polling_minutes"]["short"] = data->short_test_completion_time; } else jout("recommended polling time: \t Not Supported.\n"); } static void PrintSmartExtendedSelfTestPollingTime(const ata_smart_values * data) { jout("Extended self-test routine\n"); if (isSupportSelfTest(data)) { jout("recommended polling time: \t (%4d) minutes.\n", TestTime(data, EXTEND_SELF_TEST)); jglb["ata_smart_data"]["self_test"]["polling_minutes"]["extended"] = TestTime(data, EXTEND_SELF_TEST); } else jout("recommended polling time: \t Not Supported.\n"); } static void PrintSmartConveyanceSelfTestPollingTime(const ata_smart_values * data) { jout("Conveyance self-test routine\n"); if (isSupportConveyanceSelfTest(data)) { jout("recommended polling time: \t (%4d) minutes.\n", (int)data->conveyance_test_completion_time); jglb["ata_smart_data"]["self_test"]["polling_minutes"]["conveyance"] = data->conveyance_test_completion_time; } else jout("recommended polling time: \t Not Supported.\n"); } // Check SMART attribute table for Threshold failure // onlyfailed=0: are or were any age or prefailure attributes <= threshold // onlyfailed=1: are any prefailure attributes <= threshold now static int find_failed_attr(const ata_smart_values * data, const ata_smart_thresholds_pvt * thresholds, const ata_vendor_attr_defs & defs, int onlyfailed) { for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const ata_smart_attribute & attr = data->vendor_attributes[i]; ata_attr_state state = ata_get_attr_state(attr, i, thresholds->thres_entries, defs); if (!onlyfailed) { if (state >= ATTRSTATE_FAILED_PAST) return attr.id; } else { if (state == ATTRSTATE_FAILED_NOW && ATTRIBUTE_FLAGS_PREFAILURE(attr.flags)) return attr.id; } } return 0; } static void set_json_globals_from_smart_attrib(int id, const char * name, const ata_vendor_attr_defs & defs, uint64_t rawval) { switch (id) { case 9: if (!str_starts_with(name, "Power_On_")) return; { int minutes = -1; switch (defs[id].raw_format) { case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64: case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break; case RAWFMT_SEC2HOUR: minutes = (rawval / 60) % 60; rawval /= 60*60; break; case RAWFMT_MIN2HOUR: minutes = rawval % 60; rawval /= 60; break; case RAWFMT_HALFMIN2HOUR: minutes = (rawval / 2) % 60; rawval /= 2*60; break; case RAWFMT_MSEC24_HOUR32: minutes = (int)(rawval >> 32) / (1000*60); if (minutes >= 60) minutes = -1; rawval &= 0xffffffffULL; break; default: return; } if (rawval > 0x00ffffffULL) return; // assume bogus value jglb["power_on_time"]["hours"] = rawval; if (minutes >= 0) jglb["power_on_time"]["minutes"] = minutes; } break; case 12: if (strcmp(name, "Power_Cycle_Count")) return; switch (defs[id].raw_format) { case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64: case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break; default: return; } if (rawval > 0x00ffffffULL) return; // assume bogus value jglb["power_cycle_count"] = rawval; break; //case 194: // Temperature set separately from ata_return_temperature_value() below } } // 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 static void PrintSmartAttribWithThres(const ata_smart_values * data, const ata_smart_thresholds_pvt * thresholds, const ata_vendor_attr_defs & defs, int rpm, int onlyfailed, unsigned char format) { bool brief = !!(format & ata_print_options::FMT_BRIEF); bool hexid = !!(format & ata_print_options::FMT_HEX_ID); bool hexval = !!(format & ata_print_options::FMT_HEX_VAL); bool needheader = true; // step through all vendor attributes for (int i = 0, ji = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const ata_smart_attribute & attr = data->vendor_attributes[i]; // Check attribute and threshold unsigned char threshold = 0; ata_attr_state state = ata_get_attr_state(attr, i, thresholds->thres_entries, defs, &threshold); if (state == ATTRSTATE_NON_EXISTING) continue; // These break out of the loop if we are only printing certain entries... if (onlyfailed == 1 && !(ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) && state == ATTRSTATE_FAILED_NOW)) continue; if (onlyfailed == 2 && state < ATTRSTATE_FAILED_PAST) continue; // print header only if needed if (needheader) { if (!onlyfailed) { jout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber); jglb["ata_smart_attributes"]["revision"] = data->revnumber; jout("Vendor Specific SMART Attributes with Thresholds:\n"); } if (!brief) jout("ID#%s ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n", (!hexid ? "" : " ")); else jout("ID#%s ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE\n", (!hexid ? "" : " ")); needheader = false; } // Format value, worst, threshold std::string valstr, worstr, threstr; if (state > ATTRSTATE_NO_NORMVAL) valstr = (!hexval ? strprintf("%.3d", attr.current) : strprintf("0x%02x", attr.current)); else valstr = (!hexval ? "---" : "----"); if (!(defs[attr.id].flags & ATTRFLAG_NO_WORSTVAL)) worstr = (!hexval ? strprintf("%.3d", attr.worst) : strprintf("0x%02x", attr.worst)); else worstr = (!hexval ? "---" : "----"); if (state > ATTRSTATE_NO_THRESHOLD) threstr = (!hexval ? strprintf("%.3d", threshold) : strprintf("0x%02x", threshold)); else threstr = (!hexval ? "---" : "----"); // Print line for each valid attribute std::string idstr = (!hexid ? strprintf("%3d", attr.id) : strprintf("0x%02x", attr.id)); std::string attrname = ata_get_smart_attr_name(attr.id, defs, rpm); std::string rawstr = ata_format_attr_raw_value(attr, defs); char flagstr[] = { (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? 'P' : '-'), (ATTRIBUTE_FLAGS_ONLINE(attr.flags) ? 'O' : '-'), (ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags) ? 'S' : '-'), (ATTRIBUTE_FLAGS_ERRORRATE(attr.flags) ? 'R' : '-'), (ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags) ? 'C' : '-'), (ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags) ? 'K' : '-'), (ATTRIBUTE_FLAGS_OTHER(attr.flags) ? '+' : ' '), 0 }; if (!brief) jout("%s %-24s0x%04x %-4s %-4s %-4s %-10s%-9s%-12s%s\n", idstr.c_str(), attrname.c_str(), attr.flags, valstr.c_str(), worstr.c_str(), threstr.c_str(), (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? "Pre-fail" : "Old_age"), (ATTRIBUTE_FLAGS_ONLINE(attr.flags) ? "Always" : "Offline"), (state == ATTRSTATE_FAILED_NOW ? "FAILING_NOW" : state == ATTRSTATE_FAILED_PAST ? "In_the_past" : " -" ) , rawstr.c_str()); else jout("%s %-24s%s %-4s %-4s %-4s %-5s%s\n", idstr.c_str(), attrname.c_str(), flagstr, valstr.c_str(), worstr.c_str(), threstr.c_str(), (state == ATTRSTATE_FAILED_NOW ? "NOW" : state == ATTRSTATE_FAILED_PAST ? "Past" : "-" ), rawstr.c_str()); if (!jglb.is_enabled()) continue; json::ref jref = jglb["ata_smart_attributes"]["table"][ji++]; jref["id"] = attr.id; jref["name"] = attrname; if (state > ATTRSTATE_NO_NORMVAL) jref["value"] = attr.current; if (!(defs[attr.id].flags & ATTRFLAG_NO_WORSTVAL)) jref["worst"] = attr.worst; if (state > ATTRSTATE_NO_THRESHOLD) { jref["thresh"] = threshold; jref["when_failed"] = (state == ATTRSTATE_FAILED_NOW ? "now" : state == ATTRSTATE_FAILED_PAST ? "past" : "" ); } json::ref jreff = jref["flags"]; jreff["value"] = attr.flags; jreff["string"] = flagstr; jreff["prefailure"] = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags); jreff["updated_online"] = !!ATTRIBUTE_FLAGS_ONLINE(attr.flags); jreff["performance"] = !!ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags); jreff["error_rate"] = !!ATTRIBUTE_FLAGS_ERRORRATE(attr.flags); jreff["event_count"] = !!ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags); jreff["auto_keep"] = !!ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags); if (ATTRIBUTE_FLAGS_OTHER(attr.flags)) jreff["other"] = ATTRIBUTE_FLAGS_OTHER(attr.flags); uint64_t rawval = ata_get_attr_raw_value(attr, defs); jref["raw"]["value"] = rawval; jref["raw"]["string"] = rawstr; set_json_globals_from_smart_attrib(attr.id, attrname.c_str(), defs, rawval); } if (!needheader) { if (!onlyfailed && brief) { int n = (!hexid ? 28 : 29); jout("%*s||||||_ K auto-keep\n" "%*s|||||__ C event count\n" "%*s||||___ R error rate\n" "%*s|||____ S speed/performance\n" "%*s||_____ O updated online\n" "%*s|______ P prefailure warning\n", n, "", n, "", n, "", n, "", n, "", n, ""); } pout("\n"); } if (!jglb.is_enabled()) return; // Protocol independent temperature unsigned char t = ata_return_temperature_value(data, defs); if (t) jglb["temperature"]["current"] = t; } // Print SMART related SCT capabilities static void ataPrintSCTCapability(const ata_identify_device *drive) { unsigned short sctcaps = drive->words088_255[206-88]; if (!(sctcaps & 0x01)) return; json::ref jref = jglb["ata_sct_capabilities"]; jout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps); jref["value"] = sctcaps; if (sctcaps & 0x08) jout("\t\t\t\t\tSCT Error Recovery Control supported.\n"); jref["error_recovery_control_supported"] = !!(sctcaps & 0x08); if (sctcaps & 0x10) jout("\t\t\t\t\tSCT Feature Control supported.\n"); jref["feature_control_supported"] = !!(sctcaps & 0x10); if (sctcaps & 0x20) jout("\t\t\t\t\tSCT Data Table supported.\n"); jref["data_table_supported"] = !!(sctcaps & 0x20); } static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_identify_device *drive, firmwarebug_defs firmwarebugs) { jout("General SMART Values:\n"); PrintSmartOfflineStatus(data); if (isSupportSelfTest(data)){ PrintSmartSelfExecStatus(data, firmwarebugs); } PrintSmartTotalTimeCompleteOffline(data); PrintSmartOfflineCollectCap(data); PrintSmartCapability(data); PrintSmartErrorLogCapability(data, drive); jout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)? "General Purpose Logging supported.": "No General Purpose Logging support."); jglb["ata_smart_data"]["capabilities"]["gp_logging_supported"] = isGeneralPurposeLoggingCapable(drive); if (isSupportSelfTest(data)){ PrintSmartShortSelfTestPollingTime (data); PrintSmartExtendedSelfTestPollingTime (data); } if (isSupportConveyanceSelfTest(data)) PrintSmartConveyanceSelfTestPollingTime (data); ataPrintSCTCapability(drive); jout("\n"); } // Get # sectors of a log addr, 0 if log does not exist. static unsigned GetNumLogSectors(const ata_smart_log_directory * logdir, unsigned logaddr, bool gpl) { if (!logdir) return 0; if (logaddr > 0xff) return 0; if (logaddr == 0) return 1; unsigned n = logdir->entry[logaddr-1].numsectors; if (gpl) // GP logs may have >255 sectors n |= logdir->entry[logaddr-1].reserved << 8; return n; } // Get name of log. static const char * GetLogName(unsigned logaddr) { // Table A.2 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 // Table 112 of Serial ATA Revision 3.2, August 7, 2013 // Table A.2 of T13/2161-D (ACS-3) Revision 5, October 28, 2013 // Table 204 of T13/BSR INCITS 529 (ACS-4) Revision 16, February 21, 2017 switch (logaddr) { case 0x00: return "Log Directory"; case 0x01: return "Summary SMART error log"; case 0x02: return "Comprehensive SMART error log"; case 0x03: return "Ext. Comprehensive SMART error log"; case 0x04: return "Device Statistics log"; case 0x05: return "Reserved for CFA"; // ACS-2 case 0x06: return "SMART self-test log"; case 0x07: return "Extended self-test log"; case 0x08: return "Power Conditions log"; // ACS-2 case 0x09: return "Selective self-test log"; case 0x0a: return "Device Statistics Notification"; // ACS-3 case 0x0b: return "Reserved for CFA"; // ACS-3 case 0x0c: return "Pending Defects log"; // ACS-4 case 0x0d: return "LPS Mis-alignment log"; // ACS-2 case 0x0e: return "Reserved for ZAC-2"; // ACS-4 case 0x0f: return "Sense Data for Successful NCQ Cmds log"; // ACS-4 case 0x10: return "NCQ Command Error log"; case 0x11: return "SATA Phy Event Counters log"; //case 0x12: return "SATA NCQ Queue Management log"; // SATA 3.0/3.1, ACS-3 case 0x12: return "SATA NCQ Non-Data log"; // SATA 3.2, ACS-4 case 0x13: return "SATA NCQ Send and Receive log"; // SATA 3.1, ACS-3 case 0x14: return "Hybrid Information log"; // SATA 3.2, ACS-4 case 0x15: return "Rebuild Assist log"; // SATA 3.2, ACS-4 case 0x16: case 0x17: return "Reserved for Serial ATA"; case 0x19: return "LBA Status log"; // ACS-3 case 0x20: return "Streaming performance log [OBS-8]"; case 0x21: return "Write stream error log"; case 0x22: return "Read stream error log"; case 0x23: return "Delayed sector log [OBS-8]"; case 0x24: return "Current Device Internal Status Data log"; // ACS-3 case 0x25: return "Saved Device Internal Status Data log"; // ACS-3 case 0x2f: return "Set Sector Configuration";; // ACS-4 case 0x30: return "IDENTIFY DEVICE data log"; // ACS-3 case 0xe0: return "SCT Command/Status"; case 0xe1: return "SCT Data Transfer"; default: if (0xa0 <= logaddr && logaddr <= 0xdf) return "Device vendor specific log"; if (0x80 <= logaddr && logaddr <= 0x9f) return "Host vendor specific log"; return "Reserved"; } /*NOTREACHED*/ } // Get log access permissions static const char * get_log_rw(unsigned logaddr) { if ( ( logaddr <= 0x08) || (0x0c <= logaddr && logaddr <= 0x0d) || (0x0f <= logaddr && logaddr <= 0x14) || (0x19 == logaddr) || (0x20 <= logaddr && logaddr <= 0x25) || (0x30 == logaddr)) return "R/O"; if ( (0x09 <= logaddr && logaddr <= 0x0a) || (0x15 == logaddr) || (0x80 <= logaddr && logaddr <= 0x9f) || (0xe0 <= logaddr && logaddr <= 0xe1)) return "R/W"; if (0xa0 <= logaddr && logaddr <= 0xdf) return "VS"; // Vendor specific return "-"; // Unknown/Reserved } // Init a fake log directory, assume that standard logs are supported const ata_smart_log_directory * fake_logdir(ata_smart_log_directory * logdir, const ata_print_options & options) { memset(logdir, 0, sizeof(*logdir)); logdir->logversion = 255; logdir->entry[0x01-1].numsectors = 1; logdir->entry[0x03-1].numsectors = (options.smart_ext_error_log + (4-1)) / 4; logdir->entry[0x04-1].numsectors = 8; logdir->entry[0x06-1].numsectors = 1; logdir->entry[0x07-1].numsectors = (options.smart_ext_selftest_log + (19-1)) / 19; logdir->entry[0x09-1].numsectors = 1; logdir->entry[0x11-1].numsectors = 1; return logdir; } // Print SMART and/or GP Log Directory static void PrintLogDirectories(const ata_smart_log_directory * gplogdir, const ata_smart_log_directory * smartlogdir) { json::ref jref = jglb["ata_log_directory"]; if (gplogdir) { jout("General Purpose Log Directory Version %u\n", gplogdir->logversion); jref["gp_dir_version"] = gplogdir->logversion; } if (smartlogdir) { jout("SMART %sLog Directory Version %u%s\n", (gplogdir ? " " : ""), smartlogdir->logversion, (smartlogdir->logversion==1 ? " [multi-sector log support]" : "")); jref["smart_dir_version"] = smartlogdir->logversion; jref["smart_dir_multi_sector"] = (smartlogdir->logversion == 1); } jout("Address Access R/W Size Description\n"); for (unsigned i = 0, ji = 0; i <= 0xff; i++) { // Get number of sectors unsigned smart_numsect = GetNumLogSectors(smartlogdir, i, false); unsigned gp_numsect = GetNumLogSectors(gplogdir , i, true ); if (!(smart_numsect || gp_numsect)) continue; // Log does not exist const char * acc; unsigned size; if (smart_numsect == gp_numsect) { acc = "GPL,SL"; size = gp_numsect; } else if (!smart_numsect) { acc = "GPL"; size = gp_numsect; } else if (!gp_numsect) { acc = " SL"; size = smart_numsect; } else { acc = 0; size = 0; } unsigned i2 = i; if (acc && ((0x80 <= i && i < 0x9f) || (0xa0 <= i && i < 0xdf))) { // Find range of Host/Device vendor specific logs with same size unsigned imax = (i < 0x9f ? 0x9f : 0xdf); for (unsigned j = i+1; j <= imax; j++) { unsigned sn = GetNumLogSectors(smartlogdir, j, false); unsigned gn = GetNumLogSectors(gplogdir , j, true ); if (!(sn == smart_numsect && gn == gp_numsect)) break; i2 = j; } } const char * name = GetLogName(i); const char * rw = get_log_rw(i); if (i2 > i) jout("0x%02x-0x%02x %-6s %-3s %5u %s\n", i, i2, acc, rw, size, name); else if (acc) jout( "0x%02x %-6s %-3s %5u %s\n", i, acc, rw, size, name); else { // GPL and SL support different sizes jout( "0x%02x %-6s %-3s %5u %s\n", i, "GPL", rw, gp_numsect, name); jout( "0x%02x %-6s %-3s %5u %s\n", i, "SL", rw, smart_numsect, name); } for (;;) { json::ref jrefi = jref["table"][ji++]; jrefi["address"] = i; jrefi["name"] = name; if (rw[0] == 'R' && rw[1] && rw[2]) { jrefi["read"] = true; jrefi["write"] = (rw[2] == 'W'); } if (gp_numsect) jrefi["gp_sectors"] = gp_numsect; if (smart_numsect) jrefi["smart_sectors"] = smart_numsect; if (i >= i2) break; i++; } } jout("\n"); } // Print hexdump of log pages. // Format is compatible with 'xxd -r'. static void PrintLogPages(const char * type, const unsigned char * data, unsigned char logaddr, unsigned page, unsigned num_pages, unsigned max_pages) { pout("%s Log 0x%02x [%s], Page %u-%u (of %u)\n", type, logaddr, GetLogName(logaddr), page, page+num_pages-1, max_pages); for (unsigned i = 0; i < num_pages * 512; i += 16) { const unsigned char * p = data+i; pout("%07x: %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x ", (page * 512) + i, p[ 0], p[ 1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7], p[ 8], p[ 9], p[10], p[11], p[12], p[13], p[14], p[15]); #define P(n) (' ' <= p[n] && p[n] <= '~' ? (int)p[n] : '.') pout("|%c%c%c%c%c%c%c%c" "%c%c%c%c%c%c%c%c|\n", P( 0), P( 1), P( 2), P( 3), P( 4), P( 5), P( 6), P( 7), P( 8), P( 9), P(10), P(11), P(12), P(13), P(14), P(15)); #undef P if ((i & 0x1ff) == 0x1f0) pout("\n"); } } /////////////////////////////////////////////////////////////////////// // Device statistics (Log 0x04) // Section A.5 of T13/2161-D (ACS-3) Revision 5, October 28, 2013 // Section 9.5 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017 struct devstat_entry_info { short size; // #bytes of value, -1 for signed char const char * name; }; const devstat_entry_info devstat_info_0x00[] = { { 2, "List of supported log pages" }, { 0, 0 } }; const devstat_entry_info devstat_info_0x01[] = { { 2, "General Statistics" }, { 4, "Lifetime Power-On Resets" }, { 4, "Power-on Hours" }, { 6, "Logical Sectors Written" }, { 6, "Number of Write Commands" }, { 6, "Logical Sectors Read" }, { 6, "Number of Read Commands" }, { 6, "Date and Time TimeStamp" }, // ACS-3 { 4, "Pending Error Count" }, // ACS-4 { 2, "Workload Utilization" }, // ACS-4 { 6, "Utilization Usage Rate" }, // ACS-4 (TODO: field provides 3 values) { 2, "Resource Availability" }, // ACS-4 { 1, "Random Write Resources Used" }, // ACS-4 { 0, 0 } }; const devstat_entry_info devstat_info_0x02[] = { { 2, "Free-Fall Statistics" }, { 4, "Number of Free-Fall Events Detected" }, { 4, "Overlimit Shock Events" }, { 0, 0 } }; const devstat_entry_info devstat_info_0x03[] = { { 2, "Rotating Media Statistics" }, { 4, "Spindle Motor Power-on Hours" }, { 4, "Head Flying Hours" }, { 4, "Head Load Events" }, { 4, "Number of Reallocated Logical Sectors" }, { 4, "Read Recovery Attempts" }, { 4, "Number of Mechanical Start Failures" }, { 4, "Number of Realloc. Candidate Logical Sectors" }, // ACS-3 { 4, "Number of High Priority Unload Events" }, // ACS-3 { 0, 0 } }; const devstat_entry_info devstat_info_0x04[] = { { 2, "General Errors Statistics" }, { 4, "Number of Reported Uncorrectable Errors" }, //{ 4, "Number of Resets Between Command Acceptance and Command Completion" }, { 4, "Resets Between Cmd Acceptance and Completion" }, { 4, "Physical Element Status Changed" }, // ACS-4 { 0, 0 } }; const devstat_entry_info devstat_info_0x05[] = { { 2, "Temperature Statistics" }, { -1, "Current Temperature" }, { -1, "Average Short Term Temperature" }, { -1, "Average Long Term Temperature" }, { -1, "Highest Temperature" }, { -1, "Lowest Temperature" }, { -1, "Highest Average Short Term Temperature" }, { -1, "Lowest Average Short Term Temperature" }, { -1, "Highest Average Long Term Temperature" }, { -1, "Lowest Average Long Term Temperature" }, { 4, "Time in Over-Temperature" }, { -1, "Specified Maximum Operating Temperature" }, { 4, "Time in Under-Temperature" }, { -1, "Specified Minimum Operating Temperature" }, { 0, 0 } }; const devstat_entry_info devstat_info_0x06[] = { { 2, "Transport Statistics" }, { 4, "Number of Hardware Resets" }, { 4, "Number of ASR Events" }, { 4, "Number of Interface CRC Errors" }, { 0, 0 } }; const devstat_entry_info devstat_info_0x07[] = { { 2, "Solid State Device Statistics" }, { 1, "Percentage Used Endurance Indicator" }, { 0, 0 } }; const devstat_entry_info * devstat_infos[] = { devstat_info_0x00, devstat_info_0x01, devstat_info_0x02, devstat_info_0x03, devstat_info_0x04, devstat_info_0x05, devstat_info_0x06, devstat_info_0x07 // TODO: 0x08 Zoned Device Statistics (T13/f16136r7, January 2017) }; const int num_devstat_infos = sizeof(devstat_infos)/sizeof(devstat_infos[0]); static const char * get_device_statistics_page_name(int page) { if (page < num_devstat_infos) return devstat_infos[page][0].name; if (page == 0xff) return "Vendor Specific Statistics"; // ACS-4 return "Unknown Statistics"; } static void set_json_globals_from_device_statistics(int page, int offset, int64_t val) { switch (page) { case 1: switch (offset) { case 0x008: jglb["power_cycle_count"] = val; break; // ~= Lifetime Power-On Resets case 0x010: jglb["power_on_time"]["hours"]= val; break; } break; case 5: switch (offset) { case 0x008: jglb["temperature"]["current"] = val; break; case 0x020: jglb["temperature"]["lifetime_max"] = val; break; case 0x028: jglb["temperature"]["lifetime_min"] = val; break; case 0x050: jglb["temperature"]["lifetime_over_limit_minutes"] = val; break; case 0x058: jglb["temperature"]["op_limit_max"] = val; break; case 0x060: jglb["temperature"]["lifetime_under_limit_minutes"] = val; break; case 0x068: jglb["temperature"]["op_limit_min"] = val; break; } break; } } static void print_device_statistics_page(const json::ref & jref, const unsigned char * data, int page) { const devstat_entry_info * info = (page < num_devstat_infos ? devstat_infos[page] : 0); const char * name = get_device_statistics_page_name(page); // Check page number in header static const char line[] = " ===== = = === == "; if (!data[2]) { pout("0x%02x%s%s (empty) ==\n", page, line, name); return; } if (data[2] != page) { pout("0x%02x%s%s (invalid page 0x%02x in header) ==\n", page, line, name, data[2]); return; } int rev = data[0] | (data[1] << 8); jout("0x%02x%s%s (rev %d) ==\n", page, line, name, rev); jref["number"] = page; jref["name"] = name; jref["revision"] = rev; // Print entries int ji = 0; for (int i = 1, offset = 8; offset < 512-7; i++, offset+=8) { // Check for last known entry if (info && !info[i].size) info = 0; // Skip unsupported entries unsigned char flags = data[offset+7]; if (!(flags & 0x80)) continue; // Stop if unknown entries contain garbage data due to buggy firmware if (!info && (data[offset+5] || data[offset+6])) { pout("0x%02x 0x%03x - - [Trailing garbage ignored]\n", page, offset); break; } // Get value name const char * valname = (info ? info[i].name : (page == 0xff) ? "Vendor Specific" // ACS-4 : "Unknown" ); // Get value size, default to max if unknown int size = (info ? info[i].size : 7); // Get flags (supported flag already checked above) bool valid = !!(flags & 0x40); bool normalized = !!(flags & 0x20); bool supports_dsn = !!(flags & 0x10); // ACS-3 bool monitored_condition_met = !!(flags & 0x08); // ACS-3 unsigned char reserved_flags = (flags & 0x07); // Format value int64_t val = 0; char valstr[32]; if (valid) { // Get value if (size < 0) { val = (signed char)data[offset]; } else { for (int j = 0; j < size; j++) val |= (int64_t)data[offset+j] << (j*8); } snprintf(valstr, sizeof(valstr), "%" PRId64, val); } else { // Value not known (yet) valstr[0] = '-'; valstr[1] = 0; } char flagstr[] = { (valid ? 'V' : '-'), // JSON only (normalized ? 'N' : '-'), (supports_dsn ? 'D' : '-'), (monitored_condition_met ? 'C' : '-'), (reserved_flags ? '+' : ' '), 0 }; jout("0x%02x 0x%03x %d %15s %s %s\n", page, offset, abs(size), valstr, flagstr+1, valname); if (!jglb.is_enabled()) continue; json::ref jrefi = jref["table"][ji++]; jrefi["offset"] = offset; jrefi["name"] = valname; jrefi["size"] = abs(size); if (valid) jrefi["value"] = val; // TODO: May be unsafe JSON int if size > 6 json::ref jreff = jrefi["flags"]; jreff["value"] = flags; jreff["string"] = flagstr; jreff["valid"] = valid; jreff["normalized"] = normalized; jreff["supports_dsn"] = supports_dsn; jreff["monitored_condition_met"] = monitored_condition_met; if (reserved_flags) jreff["other"] = reserved_flags; if (valid) set_json_globals_from_device_statistics(page, offset, val); } } static bool print_device_statistics(ata_device * device, unsigned nsectors, const std::vector & single_pages, bool all_pages, bool ssd_page, bool use_gplog) { // Read list of supported pages from page 0 unsigned char page_0[512] = {0, }; int rc; if (use_gplog) rc = ataReadLogExt(device, 0x04, 0, 0, page_0, 1); else rc = ataReadSmartLog(device, 0x04, page_0, 1); if (!rc) { jerr("Read Device Statistics page 0x00 failed\n\n"); return false; } unsigned char nentries = page_0[8]; if (!(page_0[2] == 0 && nentries > 0)) { jerr("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries); return false; } // Prepare list of pages to print std::vector pages; unsigned i; if (all_pages) { // Add all supported pages for (i = 0; i < nentries; i++) { int page = page_0[8+1+i]; if (page) pages.push_back(page); } ssd_page = false; } // Add manually specified pages bool print_page_0 = false; for (i = 0; i < single_pages.size() || ssd_page; i++) { int page = (i < single_pages.size() ? single_pages[i] : 0x07); if (!page) print_page_0 = true; else if (page >= (int)nsectors) pout("Device Statistics Log has only 0x%02x pages\n", nsectors); else pages.push_back(page); if (page == 0x07) ssd_page = false; } json::ref jref = jglb["ata_device_statistics"]; // Print list of supported pages if requested if (print_page_0) { pout("Device Statistics (%s Log 0x04) supported pages\n", use_gplog ? "GP" : "SMART"); jout("Page Description\n"); for (i = 0; i < nentries; i++) { int page = page_0[8+1+i]; const char * name = get_device_statistics_page_name(page); jout("0x%02x %s\n", page, name); jref["supported_pages"][i]["number"] = page; jref["supported_pages"][i]["name"] = name; } jout("\n"); } // Read & print pages if (!pages.empty()) { pout("Device Statistics (%s Log 0x04)\n", use_gplog ? "GP" : "SMART"); jout("Page Offset Size Value Flags Description\n"); int max_page = 0; if (!use_gplog) for (i = 0; i < pages.size(); i++) { int page = pages[i]; if (max_page < page && page < 0xff) max_page = page; } raw_buffer pages_buf((max_page+1) * 512); if (!use_gplog && !ataReadSmartLog(device, 0x04, pages_buf.data(), max_page+1)) { jerr("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page); return false; } int ji = 0; for (i = 0; i < pages.size(); i++) { int page = pages[i]; if (use_gplog) { if (!ataReadLogExt(device, 0x04, 0, page, pages_buf.data(), 1)) { jerr("Read Device Statistics page 0x%02x failed\n\n", page); return false; } } else if (page > max_page) continue; int offset = (use_gplog ? 0 : page * 512); print_device_statistics_page(jref["pages"][ji++], pages_buf.data() + offset, page); } jout("%32s|||_ C monitored condition met\n", ""); jout("%32s||__ D supports DSN\n", ""); jout("%32s|___ N normalized value\n\n", ""); } return true; } /////////////////////////////////////////////////////////////////////// // Pending Defects log (Log 0x0c) // Section 9.26 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017 static bool print_pending_defects_log(ata_device * device, unsigned nsectors, unsigned max_entries) { // Read #entries from page 0 unsigned char page_buf[512] = {0, }; if (!ataReadLogExt(device, 0x0c, 0, 0, page_buf, 1)) { pout("Read Pending Defects log page 0x00 failed\n\n"); return false; } jout("Pending Defects log (GP Log 0x0c)\n"); unsigned nentries = sg_get_unaligned_le32(page_buf); json::ref jref = jglb["ata_pending_defects_log"]; jref["size"] = nsectors * 32 - 1; jref["count"] = nentries; if (!nentries) { jout("No Defects Logged\n\n"); return true; } // Print entries jout("Index LBA Hours\n"); for (unsigned i = 0, pi = 1, page = 0; i < nentries && i < max_entries; i++, pi++) { // Read new page if required if (pi >= 32) { if (++page >= nsectors) { pout("Pending Defects count %u exceeds log size (#pages=%u)\n\n", nentries, nsectors); return false; } if (!ataReadLogExt(device, 0x0c, 0, page, page_buf, 1)) { pout("Read Pending Defects log page 0x%02x failed\n\n", page); return false; } pi = 0; } const unsigned char * entry = page_buf + 16 * pi; unsigned hours = sg_get_unaligned_le32(entry); char hourstr[32]; if (hours != 0xffffffffU) snprintf(hourstr, sizeof(hourstr), "%u", hours); else hourstr[0] = '-', hourstr[1] = 0; uint64_t lba = sg_get_unaligned_le64(entry + 8); jout("%5u %18" PRIu64 " %8s\n", i, lba, hourstr); json::ref jrefi = jref["table"][i]; jrefi["lba"].set_unsafe_uint64(lba); if (hours != 0xffffffffU) jrefi["power_on_hours"] = hours; } if (nentries > max_entries) pout("... (%u entries not shown)\n", nentries - max_entries); jout("\n"); return true; } /////////////////////////////////////////////////////////////////////// // Print log 0x11 static void PrintSataPhyEventCounters(const unsigned char * data, bool reset) { if (checksum(data)) checksumwarning("SATA Phy Event Counters"); jout("SATA Phy Event Counters (GP Log 0x11)\n"); if (data[0] || data[1] || data[2] || data[3]) pout("[Reserved: 0x%02x 0x%02x 0x%02x 0x%02x]\n", data[0], data[1], data[2], data[3]); jout("ID Size Value Description\n"); for (unsigned i = 4, ji = 0; ; ) { // Get counter id and size (bits 14:12) unsigned id = data[i] | (data[i+1] << 8); unsigned size = ((id >> 12) & 0x7) << 1; id &= 0x8fff; // End of counter table ? if (!id) break; i += 2; if (!(2 <= size && size <= 8 && i + size < 512)) { pout("0x%04x %u: Invalid entry\n", id, size); break; } // Get value uint64_t val = 0, max_val = 0; for (unsigned j = 0; j < size; j+=2) { val |= (uint64_t)(data[i+j] | (data[i+j+1] << 8)) << (j*8); max_val |= (uint64_t)0xffffU << (j*8); } i += size; // Get name const char * name; switch (id) { case 0x001: name = "Command failed due to ICRC error"; break; // Mandatory case 0x002: name = "R_ERR response for data FIS"; break; case 0x003: name = "R_ERR response for device-to-host data FIS"; break; case 0x004: name = "R_ERR response for host-to-device data FIS"; break; case 0x005: name = "R_ERR response for non-data FIS"; break; case 0x006: name = "R_ERR response for device-to-host non-data FIS"; break; case 0x007: name = "R_ERR response for host-to-device non-data FIS"; break; case 0x008: name = "Device-to-host non-data FIS retries"; break; case 0x009: name = "Transition from drive PhyRdy to drive PhyNRdy"; break; case 0x00A: name = "Device-to-host register FISes sent due to a COMRESET"; break; // Mandatory case 0x00B: name = "CRC errors within host-to-device FIS"; break; case 0x00D: name = "Non-CRC errors within host-to-device FIS"; break; case 0x00F: name = "R_ERR response for host-to-device data FIS, CRC"; break; case 0x010: name = "R_ERR response for host-to-device data FIS, non-CRC"; break; case 0x012: name = "R_ERR response for host-to-device non-data FIS, CRC"; break; case 0x013: name = "R_ERR response for host-to-device non-data FIS, non-CRC"; break; default: name = ((id & 0x8000) ? "Vendor specific" : "Unknown"); break; } // Counters stop at max value, add '+' in this case jout("0x%04x %u %12" PRIu64 "%c %s\n", id, size, val, (val == max_val ? '+' : ' '), name); json::ref jref = jglb["sata_phy_event_counters"]["table"][ji++]; jref["id"] = id; jref["name"] = name; jref["size"] = size; jref["value"] = val; jref["overflow"] = (val == max_val); } if (reset) jout("All counters reset\n"); jout("\n"); jglb["sata_phy_event_counters"]["reset"] = reset; } // Format milliseconds from error log entry as "DAYS+H:M:S.MSEC" static std::string format_milliseconds(unsigned msec) { unsigned days = msec / 86400000U; msec -= days * 86400000U; unsigned hours = msec / 3600000U; msec -= hours * 3600000U; unsigned min = msec / 60000U; msec -= min * 60000U; unsigned sec = msec / 1000U; msec -= sec * 1000U; std::string str; if (days) str = strprintf("%2ud+", days); str += strprintf("%02u:%02u:%02u.%03u", hours, min, sec, msec); return str; } // Get description for 'state' value from SMART Error Logs static const char * get_error_log_state_desc(unsigned state) { state &= 0x0f; switch (state){ case 0x0: return "in an unknown state"; case 0x1: return "sleeping"; case 0x2: return "in standby mode"; case 0x3: return "active or idle"; case 0x4: return "doing SMART Offline or Self-test"; default: return (state < 0xb ? "in a reserved state" : "in a vendor specific state"); } } // returns number of errors static int PrintSmartErrorlog(const ata_smart_errorlog *data, firmwarebug_defs firmwarebugs) { json::ref jref = jglb["ata_smart_error_log"]["summary"]; jout("SMART Error Log Version: %d\n", (int)data->revnumber); jref["revision"] = data->revnumber; // if no errors logged, return if (!data->error_log_pointer){ jout("No Errors Logged\n\n"); jref["count"] = 0; return 0; } print_on(); // 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 && !firmwarebugs.is_set(BUG_SAMSUNG2)) { pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n", data->ata_error_count,data->error_log_pointer); } // starting printing error log info if (data->ata_error_count<=5) jout( "ATA Error Count: %d\n", (int)data->ata_error_count); else jout( "ATA Error Count: %d (device log contains only the most recent five errors)\n", (int)data->ata_error_count); jref["count"] = data->ata_error_count; jref["logged_count"] = (data->ata_error_count <= 5 ? data->ata_error_count : 5); print_off(); jout("\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 (int k = 4, ji = 0; k >= 0; k--) { // The error log data structure entries are a circular buffer int i = (data->error_log_pointer + k) % 5; const ata_smart_errorlog_struct * elog = data->errorlog_struct+i; const ata_smart_errorlog_error_struct * summary = &(elog->error_struct); // Spec says: unused error log structures shall be zero filled if (nonempty(elog, sizeof(*elog))){ // Table 57 of T13/1532D Volume 1 Revision 3 const char *msgstate = get_error_log_state_desc(summary->state); int days = (int)summary->timestamp/24; // See table 42 of ATA5 spec print_on(); jout("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(); json::ref jrefi = jref["table"][ji++]; jrefi["error_number"] = data->ata_error_count + k - 4; jrefi["lifetime_hours"] = summary->timestamp; jout(" When the command that caused the error occurred, the device was %s.\n\n", msgstate); jout(" 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); { json::ref jrefir = jrefi["completion_registers"]; jrefir["error"] = summary->error_register; jrefir["status"] = summary->status; jrefir["count"] = summary->sector_count; jrefir["lba"] = (summary->sector_number ) | (summary->cylinder_low << 8) | (summary->cylinder_high << 16); jrefir["device"] = summary->drive_head; } // Add a description of the contents of the status and error registers // if possible std::string st_er_desc = format_st_er_desc(elog); if (!st_er_desc.empty()) { jout(" %s", st_er_desc.c_str()); jrefi["error_description"] = st_er_desc; } jout("\n\n"); jout(" 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 (int j = 4, jj = 0; j >= 0; j--) { const ata_smart_errorlog_command_struct * thiscommand = elog->commands+j; // Spec says: unused data command structures shall be zero filled if (nonempty(thiscommand, sizeof(*thiscommand))) { const char * atacmd = look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg); jout(" %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, format_milliseconds(thiscommand->timestamp).c_str(), atacmd); json::ref jrefic = jrefi["previous_commands"][jj++]; json::ref jreficr = jrefic["registers"]; jreficr["command"] = thiscommand->commandreg; jreficr["features"] = thiscommand->featuresreg, jreficr["count"] = thiscommand->sector_count; jreficr["lba"] = (thiscommand->sector_number ) | (thiscommand->cylinder_low << 8) | (thiscommand->cylinder_high << 16); jreficr["device"] = thiscommand->drive_head; jreficr["device_control"] = thiscommand->devicecontrolreg; jrefic["powerup_milliseconds"] = thiscommand->timestamp; jrefic["command_name"] = atacmd; } } jout("\n"); } } print_on(); if (printing_is_switchable) pout("\n"); print_off(); return data->ata_error_count; } // Print SMART Extended Comprehensive Error Log (GP Log 0x03) static int PrintSmartExtErrorLog(ata_device * device, const firmwarebug_defs & firmwarebugs, const ata_smart_exterrlog * log, unsigned nsectors, unsigned max_errors) { json::ref jref = jglb["ata_smart_error_log"]["extended"]; jout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n", log->version, nsectors); jref["revision"] = log->version; jref["sectors"] = nsectors; if (!log->device_error_count) { jout("No Errors Logged\n\n"); jref["count"] = 0; return 0; } print_on(); // Check index unsigned nentries = nsectors * 4; unsigned erridx = log->error_log_index; if (!(1 <= erridx && erridx <= nentries)){ // Some Samsung disks (at least SP1614C/SW100-25, HD300LJ/ZT100-12) use the // former index from Summary Error Log (byte 1, now reserved) and set byte 2-3 // to 0. if (!(erridx == 0 && 1 <= log->reserved1 && log->reserved1 <= nentries)) { pout("Invalid Error Log index = 0x%04x (reserved = 0x%02x)\n", erridx, log->reserved1); return 0; } pout("Invalid Error Log index = 0x%04x, trying reserved byte (0x%02x) instead\n", erridx, log->reserved1); erridx = log->reserved1; } // Index base is not clearly specified by ATA8-ACS (T13/1699-D Revision 6a), // it is 1-based in practice. erridx--; // Calculate #errors to print unsigned errcnt = log->device_error_count; if (errcnt <= nentries) jout("Device Error Count: %u\n", log->device_error_count); else { errcnt = nentries; jout("Device Error Count: %u (device log contains only the most recent %u errors)\n", log->device_error_count, errcnt); } jref["count"] = log->device_error_count; jref["logged_count"] = errcnt; if (max_errors < errcnt) errcnt = max_errors; print_off(); jout("\tCR = Command Register\n" "\tFEATR = Features Register\n" "\tCOUNT = Count (was: Sector Count) Register\n" "\tLBA_48 = Upper bytes of LBA High/Mid/Low Registers ] ATA-8\n" "\tLH = LBA High (was: Cylinder High) Register ] LBA\n" "\tLM = LBA Mid (was: Cylinder Low) Register ] Register\n" "\tLL = LBA Low (was: Sector Number) Register ]\n" "\tDV = Device (was: Device/Head) Register\n" "\tDC = Device Control Register\n" "\tER = Error register\n" "\tST = Status register\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"); // Recently read log page ata_smart_exterrlog log_buf; unsigned log_buf_page = ~0; // Iterate through circular buffer in reverse direction for (unsigned i = 0, errnum = log->device_error_count; i < errcnt; i++, errnum--, erridx = (erridx > 0 ? erridx - 1 : nentries - 1)) { // Read log page if needed const ata_smart_exterrlog * log_p; unsigned page = erridx / 4; if (page == 0) log_p = log; else { if (page != log_buf_page) { memset(&log_buf, 0, sizeof(log_buf)); if (!ataReadExtErrorLog(device, &log_buf, page, 1, firmwarebugs)) break; log_buf_page = page; } log_p = &log_buf; } const ata_smart_exterrlog_error_log & entry = log_p->error_logs[erridx % 4]; json::ref jrefi = jref["table"][i]; jrefi["error_number"] = errnum; jrefi["log_index"] = erridx; // Skip unused entries if (!nonempty(&entry, sizeof(entry))) { jout("Error %u [%u] log entry is empty\n", errnum, erridx); continue; } // Print error information print_on(); const ata_smart_exterrlog_error & err = entry.error; jout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n", errnum, erridx, err.timestamp, err.timestamp / 24, err.timestamp % 24); print_off(); jrefi["lifetime_hours"] = err.timestamp; const char * msgstate = get_error_log_state_desc(err.state); jout(" When the command that caused the error occurred, the device was %s.\n\n", msgstate); jrefi["device_state"]["value"] = err.state; jrefi["device_state"]["string"] = msgstate; // Print registers jout(" After command completion occurred, registers were:\n" " ER -- ST COUNT LBA_48 LH LM LL DV DC\n" " -- -- -- == -- == == == -- -- -- -- --\n" " %02x -- %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", err.error_register, err.status_register, err.count_register_hi, err.count_register, err.lba_high_register_hi, err.lba_mid_register_hi, err.lba_low_register_hi, err.lba_high_register, err.lba_mid_register, err.lba_low_register, err.device_register, err.device_control_register); { json::ref jrefir = jrefi["completion_registers"]; jrefir["error"] = err.error_register; jrefir["status"] = err.status_register, jrefir["count"] = (err.count_register_hi << 8) | err.count_register; jrefir["lba"] = ((uint64_t)err.lba_high_register_hi << 40) | ((uint64_t)err.lba_mid_register_hi << 32) | ((uint64_t)err.lba_low_register_hi << 24) | ((unsigned)err.lba_high_register << 16) | ((unsigned)err.lba_mid_register << 8) | ((unsigned)err.lba_low_register ); jrefir["device"] = err.device_register; jrefir["device_control"] = err.device_control_register; } // Add a description of the contents of the status and error registers // if possible std::string st_er_desc = format_st_er_desc(&entry); if (!st_er_desc.empty()) { jout(" %s", st_er_desc.c_str()); jrefi["error_description"] = st_er_desc; } jout("\n\n"); // Print command history jout(" Commands leading to the command that caused the error were:\n" " CR FEATR COUNT LBA_48 LH LM LL DV DC Powered_Up_Time Command/Feature_Name\n" " -- == -- == -- == == == -- -- -- -- -- --------------- --------------------\n"); for (int ci = 4, cji = 0; ci >= 0; ci--) { const ata_smart_exterrlog_command & cmd = entry.commands[ci]; // Skip unused entries if (!nonempty(&cmd, sizeof(cmd))) continue; // Print registers, timestamp and ATA command name const char * atacmd = look_up_ata_command(cmd.command_register, cmd.features_register); jout(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n", cmd.command_register, cmd.features_register_hi, cmd.features_register, cmd.count_register_hi, cmd.count_register, cmd.lba_high_register_hi, cmd.lba_mid_register_hi, cmd.lba_low_register_hi, cmd.lba_high_register, cmd.lba_mid_register, cmd.lba_low_register, cmd.device_register, cmd.device_control_register, format_milliseconds(cmd.timestamp).c_str(), atacmd); json::ref jrefic = jrefi["previous_commands"][cji++]; json::ref jreficr = jrefic["registers"]; jreficr["command"] = cmd.command_register; jreficr["features"] = (cmd.features_register_hi << 8) | cmd.features_register; jreficr["count"] = (cmd.count_register_hi << 8) | cmd.count_register; jreficr["lba"] = ((uint64_t)cmd.lba_high_register_hi << 40) | ((uint64_t)cmd.lba_mid_register_hi << 32) | ((uint64_t)cmd.lba_low_register_hi << 24) | ((unsigned)cmd.lba_high_register << 16) | ((unsigned)cmd.lba_mid_register << 8) | ((unsigned)cmd.lba_low_register ); jreficr["device"] = cmd.device_register; jreficr["device_control"] = cmd.device_control_register; jrefic["powerup_milliseconds"] = cmd.timestamp; jrefic["command_name"] = atacmd; } jout("\n"); } print_on(); if (printing_is_switchable) pout("\n"); print_off(); return log->device_error_count; } // Print one self-test log entry. // Returns: // -1: self-test failed // 1: extended self-test completed without error // 0: otherwise static int ataPrintSmartSelfTestEntry(const json::ref & jref, unsigned testnum, unsigned char test_type, unsigned char test_status, unsigned short timestamp, uint64_t failing_lba, bool print_error_only, bool & print_header) { // Check status and type for return value int retval = 0; switch (test_status >> 4) { case 0x0: if ((test_type & 0x7f) == 0x02) retval = 1; // extended self-test completed without error break; case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: case 0x8: retval = -1; // self-test failed break; } if (retval >= 0 && print_error_only) return retval; std::string msgtest; switch (test_type) { case 0x00: msgtest = "Offline"; break; case 0x01: msgtest = "Short offline"; break; case 0x02: msgtest = "Extended offline"; break; case 0x03: msgtest = "Conveyance offline"; break; case 0x04: msgtest = "Selective offline"; break; case 0x7f: msgtest = "Abort offline test"; break; case 0x81: msgtest = "Short captive"; break; case 0x82: msgtest = "Extended captive"; break; case 0x83: msgtest = "Conveyance captive"; break; case 0x84: msgtest = "Selective captive"; break; default: if ((0x40 <= test_type && test_type <= 0x7e) || 0x90 <= test_type) msgtest = strprintf("Vendor (0x%02x)", test_type); else msgtest = strprintf("Reserved (0x%02x)", test_type); } std::string msgstat; switch (test_status >> 4) { case 0x0: msgstat = "Completed without error"; break; case 0x1: msgstat = "Aborted by host"; break; case 0x2: msgstat = "Interrupted (host reset)"; break; case 0x3: msgstat = "Fatal or unknown error"; break; case 0x4: msgstat = "Completed: unknown failure"; break; case 0x5: msgstat = "Completed: electrical failure"; break; case 0x6: msgstat = "Completed: servo/seek failure"; break; case 0x7: msgstat = "Completed: read failure"; break; case 0x8: msgstat = "Completed: handling damage??"; break; case 0xf: msgstat = "Self-test routine in progress"; break; default: msgstat = strprintf("Unknown status (0x%x)", test_status >> 4); } // Print header once if (print_header) { print_header = false; jout("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); } char msglba[32]; if (retval < 0 && failing_lba < 0xffffffffffffULL) snprintf(msglba, sizeof(msglba), "%" PRIu64, failing_lba); else { msglba[0] = '-'; msglba[1] = 0; } jout("#%2u %-19s %-29s %1d0%% %8u %s\n", testnum, msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba); jref["type"]["value"] = test_type; jref["type"]["string"] = msgtest; jref["status"]["value"] = test_status; jref["status"]["string"] = msgstat; if (test_status & 0x0f) jref["status"]["remaining_percent"] = (test_status & 0x0f) * 10; switch (test_status >> 4) { case 0x1: case 0x2: case 0x3: break; // aborted -> unknown default: jref["status"]["passed"] = (retval >= 0); } jref["lifetime_hours"] = timestamp; if (retval < 0 && failing_lba < 0xffffffffffffULL) jref["lba"] = failing_lba; return retval; } // Print SMART Self-test log, return error count static int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * log, bool allentries, firmwarebug_defs firmwarebugs) { json::ref jref = jglb["ata_smart_self_test_log"]["standard"]; if (allentries) jout("SMART Self-test log structure revision number %d\n", log->revnumber); jref["revision"] = log->revnumber; if (log->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_SAMSUNG)) pout("Warning: ATA Specification requires self-test log structure revision number = 1\n"); if (!log->mostrecenttest){ if (allentries) jout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n"); jref["count"] = 0; return 0; } bool noheaderprinted = true; int errcnt = 0, igncnt = 0; int testnum = 1, ext_ok_testnum = -1; // Iterate through circular buffer in reverse direction for (int i = 20, ji = 0; i >= 0; i--) { int j = (i + log->mostrecenttest) % 21; const ata_smart_selftestlog_struct & entry = log->selftest_struct[j]; // Skip unused entries if (!nonempty(&entry, sizeof(entry))) continue; // Get LBA if valid uint64_t lba48 = (entry.lbafirstfailure < 0xffffffff ? entry.lbafirstfailure : 0xffffffffffffULL); // Print entry int state = ataPrintSmartSelfTestEntry(jref["table"][ji++], testnum, entry.selftestnumber, entry.selfteststatus, entry.timestamp, lba48, !allentries, noheaderprinted); if (state < 0) { // Self-test showed an error if (ext_ok_testnum < 0) errcnt++; else // Newer successful extended self-test exits igncnt++; } else if (state > 0 && ext_ok_testnum < 0) { // Latest successful extended self-test ext_ok_testnum = testnum; } testnum++; } if (igncnt) jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n", igncnt, igncnt+errcnt, ext_ok_testnum); jref["count"] = testnum - 1; jref["error_count_total"] = igncnt + errcnt; jref["error_count_outdated"] = igncnt; if (!allentries && !noheaderprinted) jout("\n"); return errcnt; } // Print SMART Extended Self-test Log (GP Log 0x07) static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log, unsigned nsectors, unsigned max_entries) { json::ref jref = jglb["ata_smart_self_test_log"]["extended"]; jout("SMART Extended Self-test Log Version: %u (%u sectors)\n", log->version, nsectors); jref["revision"] = log->version; jref["sectors"] = nsectors; if (!log->log_desc_index){ jout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n\n"); jref["count"] = 0; return 0; } // Check index unsigned nentries = nsectors * 19; unsigned logidx = log->log_desc_index; if (logidx > nentries) { pout("Invalid Self-test Log index = 0x%04x (reserved = 0x%02x)\n", logidx, log->reserved1); return 0; } // Index base is not clearly specified by ATA8-ACS (T13/1699-D Revision 6a), // it is 1-based in practice. logidx--; bool print_header = true; int errcnt = 0, igncnt = 0; int ext_ok_testnum = -1; unsigned testnum = 1; // Iterate through circular buffer in reverse direction for (unsigned i = 0, ji = 0; i < nentries && testnum <= max_entries; i++, logidx = (logidx > 0 ? logidx - 1 : nentries - 1)) { const ata_smart_extselftestlog_desc & entry = log[logidx / 19].log_descs[logidx % 19]; // Skip unused entries if (!nonempty(&entry, sizeof(entry))) continue; // Get LBA const unsigned char * b = entry.failing_lba; uint64_t lba48 = b[0] | ( b[1] << 8) | ( b[2] << 16) | ((uint64_t)b[3] << 24) | ((uint64_t)b[4] << 32) | ((uint64_t)b[5] << 40); // Print entry int state = ataPrintSmartSelfTestEntry(jref["table"][ji++], testnum, entry.self_test_type, entry.self_test_status, entry.timestamp, lba48, false /*!print_error_only*/, print_header); if (state < 0) { // Self-test showed an error if (ext_ok_testnum < 0) errcnt++; else // Newer successful extended self-test exits igncnt++; } else if (state > 0 && ext_ok_testnum < 0) { // Latest successful extended self-test ext_ok_testnum = testnum; } testnum++; } if (igncnt) jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n", igncnt, igncnt+errcnt, ext_ok_testnum); jref["count"] = testnum - 1; jref["error_count_total"] = igncnt + errcnt; jref["error_count_outdated"] = igncnt; jout("\n"); return errcnt; } static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log, const ata_smart_values * sv) { json::ref jref = jglb["ata_smart_selective_self_test_log"]; // print data structure revision number jout("SMART Selective self-test log data structure revision number %d\n", log->logversion); jref["revision"] = log->logversion; if (1 != log->logversion) pout("Note: revision number not 1 implies that no selective self-test has ever been run\n"); const char *msg; 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... uint64_t maxl = 0, maxr = 0; uint64_t current = log->currentlba; uint64_t currentend = current + 0xffff; if (log->currentspan>5) { maxl=current; maxr=currentend; } for (int 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 accommodate the // labels int field1,field2; char tmp[64]; 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 jout(" SPAN %*s %*s CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA"); for (int i = 0; i < 5; i++) { uint64_t start=log->span[i].start; uint64_t end=log->span[i].end; bool active = (i + 1 == log->currentspan); if (active) // this span is currently under test jout(" %d %*" PRIu64 " %*" PRIu64 " %s [%01d0%% left] (%" PRIu64 "-%" PRIu64 ")\n", i + 1, field1, start, field2, end, msg, (sv->self_test_exec_status & 0xf), current, currentend); else // this span is not currently under test jout(" %d %*" PRIu64 " %*" PRIu64 " Not_testing\n", i + 1, field1, start, field2, end); json::ref jrefi = jref["table"][i]; jrefi["lba_min"] = start; jrefi["lba_max"] = end; jrefi["status"]["value"] = sv->self_test_exec_status; jrefi["status"]["string"] = (active ? msg : "Not_testing"); if (active) { jrefi["status"]["remaining_percent"] = sv->self_test_exec_status & 0xf; jrefi["current_lba_min"] = current; jrefi["current_lba_max"] = currentend; } } // if we are currently read-scanning, print LBAs and the status of // the read scan if (log->currentspan > 5) { const char * ost = OfflineDataCollectionStatus(sv->offline_data_collection_status); jout("%5d %*" PRIu64 " %*" PRIu64 " Read_scanning %s\n", log->currentspan, field1, current, field2, currentend, ost); json::ref jrefc = jref["current_read_scan"]; jrefc["lba_min"] = current; jrefc["lba_max"] = currentend; jrefc["status"]["value"] = sv->offline_data_collection_status; jrefc["status"]["string"] = ost; } /* 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 */ jout("Selective self-test flags (0x%x):\n", (unsigned)log->flags); json::ref jreff = jref["flags"]; jreff["value"] = log->flags; jreff["remainder_scan_enabled"] = !!(log->flags & SELECTIVE_FLAG_DOSCAN); if (log->flags & SELECTIVE_FLAG_DOSCAN) { if (log->flags & SELECTIVE_FLAG_ACTIVE) jout(" Currently read-scanning the remainder of the disk.\n"); else if (log->flags & SELECTIVE_FLAG_PENDING) jout(" Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n", log->pendingtime); else jout(" After scanning selected spans, read-scan remainder of disk.\n"); jreff["remainder_scan_active"] = !!(log->flags & SELECTIVE_FLAG_ACTIVE); jreff["power_up_scan_pending"] = !!(log->flags & SELECTIVE_FLAG_PENDING); } else jout(" After scanning selected spans, do NOT read-scan remainder of disk.\n"); // print pending time jout("If Selective self-test is pending on power-up, resume after %d minute delay.\n", log->pendingtime); jref["power_up_scan_resume_minutes"] = log->pendingtime; } // Format SCT Temperature value static const char * sct_ptemp(signed char x, char (& buf)[20]) { if (x == -128 /*0x80 = unknown*/) return " ?"; snprintf(buf, sizeof(buf), "%2d", x); return buf; } static void sct_jtemp2(const json::ref & jref, const char * name, signed char x) { if (x == -128 /*0x80 = unknown*/) return; jglb["temperature"][name] = x; jref["temperature"][name] = x; } static const char * sct_pbar(int x, char (& buf)[64]) { if (x <= 19) x = 0; else x -= 19; bool ov = false; if (x > 40) { x = 40; ov = true; } if (x > 0) { memset(buf, '*', x); if (ov) buf[x-1] = '+'; buf[x] = 0; } else { buf[0] = '-'; buf[1] = 0; } return buf; } static const char * sct_device_state_msg(unsigned char state) { switch (state) { case 0: return "Active"; case 1: return "Stand-by"; case 2: return "Sleep"; case 3: return "DST executing in background"; case 4: return "SMART Off-line Data Collection executing in background"; case 5: return "SCT command executing in background"; default:return "Unknown"; } } // Print SCT Status static int ataPrintSCTStatus(const ata_sct_status_response * sts) { json::ref jref = jglb["ata_sct_status"]; jout("SCT Status Version: %u\n", sts->format_version); jref["format_version"] = sts->format_version; jout("SCT Version (vendor specific): %u (0x%04x)\n", sts->sct_version, sts->sct_version); jref["sct_version"] = sts->sct_version; // SCT Support Level (1) from original SCT draft was later declared obsolete in ATA-8 ACS. // Drives typically return 0 or 1. Print only if unknown value is returned. if (sts->sct_spec > 1) pout("SCT Support Level: %u\n", sts->sct_spec); const char * statestr = sct_device_state_msg(sts->device_state); jout("Device State: %s (%u)\n", statestr, sts->device_state); jref["device_state"]["value"] = sts->device_state; jref["device_state"]["string"] = statestr; // If "Reserved" fields not set, assume "old" format version 2: // Table 11 of T13/1701DT-N (SMART Command Transport) Revision 5, February 2005 // Table 54 of T13/1699-D (ATA8-ACS) Revision 3e, July 2006 // ... else assume "new" format version 2 or version 3: // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006 // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006 (format version 2) // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3) // Table 194 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017 // (max_op_limit, smart_status, min_erc_time) bool old_format_2 = ( !sts->min_temp && !sts->life_min_temp && !sts->under_limit_count && !sts->over_limit_count); char buf1[20], buf2[20]; jout("Current Temperature: %s Celsius\n", sct_ptemp(sts->hda_temp, buf1)); sct_jtemp2(jref, "current", sts->hda_temp); jout("Power Cycle Min/Max Temperature: %s/%s Celsius\n", (!old_format_2 ? sct_ptemp(sts->min_temp, buf1) : "--"), sct_ptemp(sts->max_temp, buf2)); if (!old_format_2) sct_jtemp2(jref, "power_cycle_min", sts->min_temp); sct_jtemp2(jref, "power_cycle_max", sts->max_temp); jout("Lifetime Min/Max Temperature: %s/%s Celsius\n", (!old_format_2 ? sct_ptemp(sts->life_min_temp, buf1) : "--"), sct_ptemp(sts->life_max_temp, buf2)); if (!old_format_2) sct_jtemp2(jref, "lifetime_min", sts->life_min_temp); sct_jtemp2(jref, "lifetime_max", sts->life_max_temp); if (old_format_2) return 0; if (sts->max_op_limit > 0) { // e06152r0-2: "Average Temperature" jout("Specified Max Operating Temperature: %3d Celsius\n", sts->max_op_limit); sct_jtemp2(jref, "op_limit_max", sts->max_op_limit); } jout("Under/Over Temperature Limit Count: %2u/%u\n", sts->under_limit_count, sts->over_limit_count); jref["temperature"]["under_limit_count"] = sts->under_limit_count; jref["temperature"]["over_limit_count"] = sts->over_limit_count; if (sts->smart_status) { // ACS-4 int passed = (sts->smart_status == 0x2cf4 ? 0 : sts->smart_status == 0xc24f ? 1 : -1); jout("SMART Status: 0x%04x (%s)\n", sts->smart_status, (passed == 0 ? "FAILED" : passed > 0 ? "PASSED" : "Reserved")); if (passed >= 0) { jref["smart_status"]["passed"] = !!passed; jglb["smart_status"]["passed"] = !!passed; } else jref["smart_status"]["reserved_value"] = sts->smart_status; } if (sts->min_erc_time) // ACS-4 pout("Minimum supported ERC Time Limit: %d (%0.1f seconds)\n", sts->min_erc_time, sts->min_erc_time/10.0); if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) { jout("Vendor specific:\n"); for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++) { jout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n')); jref["vendor_specific"][i] = sts->vendor_specific[i]; } } return 0; } // Print SCT Temperature History Table static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) { json::ref jref = jglb["ata_sct_temperature_history"]; char buf1[20], buf2[20], buf3[64]; jout("SCT Temperature History Version: %u%s\n", tmh->format_version, (tmh->format_version != 2 ? " (Unknown, should be 2)" : "")); jref["version"] = tmh->format_version; jout("Temperature Sampling Period: %u minute%s\n", tmh->sampling_period, (tmh->sampling_period==1?"":"s")); jref["sampling_period_minutes"] = tmh->sampling_period; jout("Temperature Logging Interval: %u minute%s\n", tmh->interval, (tmh->interval==1?"":"s")); jref["logging_interval_minutes"] = tmh->interval; jout("Min/Max recommended Temperature: %s/%s Celsius\n", sct_ptemp(tmh->min_op_limit, buf1), sct_ptemp(tmh->max_op_limit, buf2)); sct_jtemp2(jref, "op_limit_min", tmh->min_op_limit); sct_jtemp2(jref, "op_limit_max", tmh->max_op_limit); jout("Min/Max Temperature Limit: %s/%s Celsius\n", sct_ptemp(tmh->under_limit, buf1), sct_ptemp(tmh->over_limit, buf2)); sct_jtemp2(jref, "limit_min", tmh->under_limit); sct_jtemp2(jref, "limit_max", tmh->over_limit); jout("Temperature History Size (Index): %u (%u)\n", tmh->cb_size, tmh->cb_index); jref["size"] = tmh->cb_size; jref["index"] = tmh->cb_index; if (!(0 < tmh->cb_size && tmh->cb_size <= sizeof(tmh->cb) && tmh->cb_index < tmh->cb_size)) { if (!tmh->cb_size) pout("Temperature History is empty\n"); else pout("Invalid Temperature History Size or Index\n"); return 0; } // Print table jout("\nIndex Estimated Time Temperature Celsius\n"); unsigned n = 0, i = (tmh->cb_index+1) % tmh->cb_size; unsigned interval = (tmh->interval > 0 ? tmh->interval : 1); time_t t = time(0) - (tmh->cb_size-1) * interval * 60; t -= t % (interval * 60); while (n < tmh->cb_size) { // Find range of identical temperatures unsigned n1 = n, n2 = n+1, i2 = (i+1) % tmh->cb_size; while (n2 < tmh->cb_size && tmh->cb[i2] == tmh->cb[i]) { n2++; i2 = (i2+1) % tmh->cb_size; } // Print range while (n < n2) { if (n == n1 || n == n2-1 || n2 <= n1+3) { char date[30]; // TODO: Don't print times < boot time strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&t)); jout(" %3u %s %s %s\n", i, date, sct_ptemp(tmh->cb[i], buf1), sct_pbar(tmh->cb[i], buf3)); } else if (n == n1+1) { jout(" ... ..(%3u skipped). .. %s\n", n2-n1-2, sct_pbar(tmh->cb[i], buf3)); } if (tmh->cb[i] != -128) jref["table"][n] = tmh->cb[i]; t += interval * 60; i = (i+1) % tmh->cb_size; n++; } } //assert(n == tmh->cb_size && i == (tmh->cb_index+1) % tmh->cb_size); return 0; } // Print SCT Error Recovery Control timers static void ataPrintSCTErrorRecoveryControl(bool set, unsigned short read_timer, unsigned short write_timer) { json::ref jref = jglb["ata_sct_erc"]; jout("SCT Error Recovery Control%s:\n", (set ? " set to" : "")); jref["read"]["enabled"] = !!read_timer; if (!read_timer) jout(" Read: Disabled\n"); else { jout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0); jref["read"]["deciseconds"] = read_timer; } jref["write"]["enabled"] = !!write_timer; if (!write_timer) jout(" Write: Disabled\n"); else { jout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0); jref["write"]["deciseconds"] = write_timer; } } static void print_aam_level(const char * msg, int level, int recommended = -1) { // Table 56 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 // Obsolete since T13/2015-D (ACS-2) Revision 4a, December 9, 2010 const char * s; if (level == 0) s = "vendor specific"; else if (level < 128) s = "unknown/retired"; else if (level == 128) s = "quiet"; else if (level < 254) s = "intermediate"; else if (level == 254) s = "maximum performance"; else s = "reserved"; if (recommended >= 0) jout("%s%d (%s), recommended: %d\n", msg, level, s, recommended); else jout("%s%d (%s)\n", msg, level, s); json::ref jref = jglb["ata_aam"]; jref["enabled"] = true; jref["level"] = level; jref["string"] = s; if (recommended >= 0) jref["recommended_level"] = recommended; } static void print_apm_level(const char * msg, int level) { // Table 120 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 const char * s; if (!(1 <= level && level <= 254)) s = "reserved"; else if (level == 1) s = "minimum power consumption with standby"; else if (level < 128) s = "intermediate level with standby"; else if (level == 128) s = "minimum power consumption without standby"; else if (level < 254) s = "intermediate level without standby"; else s = "maximum performance"; jout("%s%d (%s)\n", msg, level, s); json::ref jref = jglb["ata_apm"]; jref["enabled"] = true; jref["level"] = level; jref["string"] = s; if (1 <= level && level <= 254) { jref["max_performance"] = (level == 254); jref["min_power"] = (level == 1 || level == 128); jref["with_standby"] = (level < 128); } } static void print_ata_security_status(const char * msg, unsigned short state) { // Table 6 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 if (!(state & 0x0001)) { pout("%sUnavailable\n", msg); return; } const char * s1, * s2 = "", * s3 = "", * s4 = ""; bool enabled = false, locked = false; if (!(state & 0x0002)) { s1 = "Disabled, "; if (!(state & 0x0008)) s2 = "NOT FROZEN [SEC1]"; else s2 = "frozen [SEC2]"; } else { enabled = true; s1 = "ENABLED, PW level "; if (!(state & 0x0100)) s2 = "HIGH"; else s2 = "MAX"; if (!(state & 0x0004)) { s3 = ", not locked, "; if (!(state & 0x0008)) s4 = "not frozen [SEC5]"; else s4 = "frozen [SEC6]"; } else { locked = true; s3 = ", **LOCKED** [SEC4]"; if (state & 0x0010) s4 = ", PW ATTEMPTS EXCEEDED"; } } jout("%s%s%s%s%s\n", msg, s1, s2, s3, s4); json::ref jref = jglb["ata_security"]; jref["state"] = state; jref["string"] = strprintf("%s%s%s%s", s1, s2, s3, s4); jref["enabled"] = enabled; if (!enabled || !locked) jref["frozen"] = !!(state & 0x0008); if (enabled) { jref["pw_level_max"] = !!(state & 0x0100); jref["locked"] = locked; if (locked) jref["pw_attempts_exceeded"] = !!(state & 0x0010); } } static void print_standby_timer(const char * msg, int timer, const ata_identify_device & drive) { const char * s1 = 0; int hours = 0, minutes = 0 , seconds = 0; // Table 63 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 if (timer == 0) s1 = "disabled"; else if (timer <= 240) seconds = timer * 5, minutes = seconds / 60, seconds %= 60; else if (timer <= 251) minutes = (timer - 240) * 30, hours = minutes / 60, minutes %= 60; else if (timer == 252) minutes = 21; else if (timer == 253) s1 = "between 8 hours and 12 hours"; else if (timer == 255) minutes = 21, seconds = 15; else s1 = "reserved"; const char * s2 = "", * s3 = ""; if (!(drive.words047_079[49-47] & 0x2000)) s2 = " or vendor-specific"; if (timer > 0 && (drive.words047_079[50-47] & 0xc001) == 0x4001) s3 = ", a vendor-specific minimum applies"; if (s1) pout("%s%d (%s%s%s)\n", msg, timer, s1, s2, s3); else pout("%s%d (%02d:%02d:%02d%s%s)\n", msg, timer, hours, minutes, seconds, s2, s3); } int ataPrintMain (ata_device * device, const ata_print_options & options) { // If requested, check power mode first const char * powername = 0; bool powerchg = false; if (options.powermode) { unsigned char powerlimit = 0xff; int powermode = ataCheckPowerMode(device); // TODO: Move to new function used by smartctl and smartd. switch (powermode) { case -1: if (device->is_syscall_unsup()) { pout("CHECK POWER MODE not implemented, ignoring -n option\n"); break; } powername = "SLEEP"; powerlimit = 2; break; // Table 215 of T13/2015-D (ACS-2) Revision 7, June 22, 2011 // Table 293 of T13/BSR INCITS 529 (ACS-4) Revision 12, February 18, 2016 case 0x00: // PM2:Standby, EPC unavailable or Standby_z power condition powername = "STANDBY"; powerlimit = 3; break; case 0x01: // PM2:Standby, Standby_y power condition powername = "STANDBY_Y"; powerlimit = 3; break; case 0x80: // PM1:Idle, EPC unavailable powername = "IDLE"; powerlimit = 4; break; case 0x81: // PM1:Idle, Idle_a power condition powername = "IDLE_A"; powerlimit = 4; break; case 0x82: // PM1:Idle, Idle_b power condition powername = "IDLE_B"; powerlimit = 4; break; case 0x83: // PM1:Idle, Idle_c power condition powername = "IDLE_C"; powerlimit = 4; break; // 0x40/41 were declared obsolete in ACS-3 Revision 1 case 0x40: // PM0:Active, NV Cache power mode enabled, spun down powername = "ACTIVE_NV_DOWN"; break; case 0x41: // PM0:Active, NV Cache power mode enabled, spun up powername = "ACTIVE_NV_UP" ; break; case 0xff: // PM0:Active or PM1:Idle powername = "ACTIVE or IDLE"; break; default: pout("CHECK POWER MODE returned unknown value 0x%02x, ignoring -n option\n", powermode); break; } if (powername) { if (options.powermode >= powerlimit) { jinf("Device is in %s mode, exit(%d)\n", powername, options.powerexit); return options.powerexit; } powerchg = (powermode != 0xff); // SMART tests will spin up drives } } // SMART values needed ? bool need_smart_val = ( options.smart_check_status || options.smart_general_values || options.smart_vendor_attrib || options.smart_error_log || options.smart_selftest_log || options.smart_selective_selftest_log || options.smart_ext_error_log || options.smart_ext_selftest_log || options.smart_auto_offl_enable || options.smart_auto_offl_disable || options.smart_selftest_type != -1 ); // SMART must be enabled ? bool need_smart_enabled = ( need_smart_val || options.smart_auto_save_enable || options.smart_auto_save_disable ); // SMART feature set needed ? bool need_smart_support = ( need_smart_enabled || options.smart_enable || options.smart_disable ); // SMART and GP log directories needed ? bool need_smart_logdir = ( options.smart_logdir || options.devstat_all_pages // devstat fallback to smartlog if needed || options.devstat_ssd_page || !options.devstat_pages.empty() ); bool need_gp_logdir = ( options.gp_logdir || options.smart_ext_error_log || options.smart_ext_selftest_log || options.devstat_all_pages || options.devstat_ssd_page || !options.devstat_pages.empty() || options.pending_defects_log ); unsigned i; for (i = 0; i < options.log_requests.size(); i++) { if (options.log_requests[i].gpl) need_gp_logdir = true; else need_smart_logdir = true; } // SCT commands needed ? bool need_sct_support = ( options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int || options.sct_erc_get || options.sct_erc_set || options.sct_wcache_reorder_get || options.sct_wcache_reorder_set || options.sct_wcache_sct_get || options.sct_wcache_sct_set ); // Exit if no further options specified if (!( options.drive_info || options.show_presets || need_smart_support || need_smart_logdir || need_gp_logdir || need_sct_support || options.sataphy || options.identify_word_level >= 0 || options.get_set_used )) { if (powername) pout("Device is in %s mode\n", powername); else pout("ATA device successfully opened\n\n" "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n"); return 0; } // Start by getting Drive ID information. We need this, to know if SMART is supported. int returnval = 0; ata_identify_device drive; memset(&drive, 0, sizeof(drive)); unsigned char raw_drive[sizeof(drive)]; memset(&raw_drive, 0, sizeof(raw_drive)); device->clear_err(); int retid = ata_read_identity(device, &drive, options.fix_swapped_id, raw_drive); if (retid < 0) { pout("Read Device Identity failed: %s\n\n", (device->get_errno() ? device->get_errmsg() : "Unknown error")); failuretest(MANDATORY_CMD, returnval|=FAILID); } else if (!nonempty(&drive, sizeof(drive))) { pout("Read Device Identity failed: empty IDENTIFY data\n\n"); failuretest(MANDATORY_CMD, returnval|=FAILID); } // If requested, show which presets would be used for this drive and exit. if (options.show_presets) { show_presets(&drive); return 0; } // Use preset vendor attribute options unless user has requested otherwise. ata_vendor_attr_defs attribute_defs = options.attribute_defs; firmwarebug_defs firmwarebugs = options.firmwarebugs; const drive_settings * dbentry = 0; if (!options.ignore_presets) dbentry = lookup_drive_apply_presets(&drive, attribute_defs, firmwarebugs); // Get capacity, sector sizes and rotation rate ata_size_info sizes; ata_get_size_info(&drive, sizes); int rpm = ata_get_rotation_rate(&drive); // Print ATA IDENTIFY info if requested if (options.identify_word_level >= 0) { pout("=== ATA IDENTIFY DATA ===\n"); // Pass raw data without endianness adjustments ata_print_identify_data(raw_drive, (options.identify_word_level > 0), options.identify_bit_level); } // Print most drive identity information if requested if (options.drive_info) { pout("=== START OF INFORMATION SECTION ===\n"); print_drive_info(&drive, sizes, rpm, dbentry); } // Check and print SMART support and state int smart_supported = -1, smart_enabled = -1; if (need_smart_support || options.drive_info) { // 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)); } else { // Disk device: SMART supported and enabled ? smart_supported = ataSmartSupport(&drive); smart_enabled = ataIsSmartEnabled(&drive); if (smart_supported < 0) pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n"); if (smart_supported && smart_enabled < 0) { pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n"); if (need_smart_support) { failuretest(MANDATORY_CMD, returnval|=FAILSMART); // check SMART support by trying a command pout(" Checking to be sure by trying SMART RETURN STATUS command.\n"); if (ataDoesSmartWork(device)) smart_supported = smart_enabled = 1; } } else if (smart_supported < 0 && (smart_enabled > 0 || dbentry)) // Assume supported if enabled or in drive database smart_supported = 1; if (smart_supported < 0) pout("SMART support is: Unknown - Try option -s with argument 'on' to enable it."); else if (!smart_supported) pout("SMART support is: Unavailable - device lacks SMART capability.\n"); else { if (options.drive_info) pout("SMART support is: Available - device has SMART capability.\n"); if (smart_enabled >= 0) { if (device->ata_identify_is_cached()) { if (options.drive_info) pout(" %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n", (smart_enabled?"En":"Dis")); smart_enabled = ataDoesSmartWork(device); } if (options.drive_info) pout("SMART support is: %s\n", (smart_enabled ? "Enabled" : "Disabled")); } } } } // Print AAM status if (options.get_aam) { if ((drive.command_set_2 & 0xc200) != 0x4200) // word083 pout("AAM feature is: Unavailable\n"); else if (!(drive.word086 & 0x0200)) { jout("AAM feature is: Disabled\n"); jglb["ata_aam"]["enabled"] = false; } else print_aam_level("AAM level is: ", drive.words088_255[94-88] & 0xff, drive.words088_255[94-88] >> 8); } // Print APM status if (options.get_apm) { if ((drive.command_set_2 & 0xc008) != 0x4008) // word083 pout("APM feature is: Unavailable\n"); else if (!(drive.word086 & 0x0008)) { jout("APM feature is: Disabled\n"); jglb["ata_apm"]["enabled"] = false; } else print_apm_level("APM level is: ", drive.words088_255[91-88] & 0xff); } // Print read look-ahead status if (options.get_lookahead) { if ( (drive.command_set_2 & 0xc000) != 0x4000 // word083 || !(drive.command_set_1 & 0x0040) ) // word082 pout("Rd look-ahead is: Unavailable\n"); else { bool enabled = !!(drive.cfs_enable_1 & 0x0040); // word085 jout("Rd look-ahead is: %sabled\n", (enabled ? "En" : "Dis")); jglb["read_lookahead"]["enabled"] = enabled; } } // Print write cache status if (options.get_wcache) { if ( (drive.command_set_2 & 0xc000) != 0x4000 // word083 || !(drive.command_set_1 & 0x0020) ) // word082 pout("Write cache is: Unavailable\n"); else { bool enabled = !!(drive.cfs_enable_1 & 0x0020); // word085 jout("Write cache is: %sabled\n", (enabled ? "En" : "Dis")); jglb["write_cache"]["enabled"] = enabled; } } // Print DSN status unsigned short word120 = drive.words088_255[120-88]; unsigned short word119 = drive.words088_255[119-88]; if (options.get_dsn) { if (!(drive.word086 & 0x8000) // word086 || ((word119 & 0xc200) != 0x4200) // word119 || ((word120 & 0xc000) != 0x4000)) // word120 pout("DSN feature is: Unavailable\n"); else { bool enabled = !!(word120 & 0x200); jout("DSN feature is: %sabled\n", (enabled ? "En" : "Dis")); jglb["ata_dsn"]["enabled"] = enabled; } } // Check for ATA Security LOCK unsigned short word128 = drive.words088_255[128-88]; bool locked = ((word128 & 0x0007) == 0x0007); // LOCKED|ENABLED|SUPPORTED // Print ATA Security status if (options.get_security) print_ata_security_status("ATA Security is: ", word128); // Print write cache reordering status if (options.sct_wcache_reorder_get) { if (!isSCTFeatureControlCapable(&drive)) pout("Wt Cache Reorder: Unavailable\n"); else if (locked) pout("Wt Cache Reorder: Unknown (SCT not supported if ATA Security is LOCKED)\n"); else { int wcache_reorder = ataGetSetSCTWriteCacheReordering(device, false /*enable*/, false /*persistent*/, false /*set*/); if (-1 <= wcache_reorder && wcache_reorder <= 2) pout("Wt Cache Reorder: %s\n", (wcache_reorder == -1 ? "Unknown (SCT Feature Control command failed)" : wcache_reorder == 0 ? "Unknown" : // not defined in standard but returned on some drives if not set wcache_reorder == 1 ? "Enabled" : "Disabled")); else pout("Wt Cache Reorder: Unknown (0x%02x)\n", wcache_reorder); } } const char * sct_write_cache_state_desc[4] = { "Unknown", // 0: not defined in standard but returned on some drives if not set "Controlled by ATA", // 1: controlled ATA Set Features command "Force Enabled", // 2 "Force Disabled" // 3 }; // Print SCT feature control of write cache if (options.sct_wcache_sct_get) { if (!isSCTFeatureControlCapable(&drive)) pout("SCT Write Cache Control: Unavailable\n"); else if (locked) pout("SCT Write Cache Control: Unknown (SCT not supported if ATA Security is LOCKED)\n"); else { int state = ataGetSetSCTWriteCache(device, 1, false /*persistent*/, false /*set*/); if (-1 <= state && state <= 3) pout("SCT Write Cache Control: %s\n", (state == -1 ? "Unknown (SCT Feature Control command failed)" : sct_write_cache_state_desc[state])); else pout("SCT Write Cache Control: Unknown (0x%02x)\n", state); } } // Print remaining drive info if (options.drive_info) { // Print the (now possibly changed) power mode if available if (powername) pout("Power mode %s %s\n", (powerchg?"was:":"is: "), powername); pout("\n"); } // Exit if SMART is not supported but must be available to proceed if (smart_supported <= 0 && need_smart_support) failuretest(MANDATORY_CMD, returnval|=FAILSMART); // START OF THE ENABLE/DISABLE SECTION OF THE CODE if ( options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable || options.smart_auto_offl_disable || options.smart_auto_offl_enable || options.set_aam || options.set_apm || options.set_lookahead || options.set_wcache || options.set_security_freeze || options.set_standby || options.sct_wcache_reorder_set || options.sct_wcache_sct_set || options.set_dsn) pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); // Enable/Disable AAM if (options.set_aam) { if (options.set_aam > 0) { if (!ata_set_features(device, ATA_ENABLE_AAM, options.set_aam-1)) { pout("AAM enable failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else print_aam_level("AAM set to level ", options.set_aam-1); } else { if (!ata_set_features(device, ATA_DISABLE_AAM)) { pout("AAM disable failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else pout("AAM disabled\n"); } } // Enable/Disable APM if (options.set_apm) { if (options.set_apm > 0) { if (!ata_set_features(device, ATA_ENABLE_APM, options.set_apm-1)) { pout("APM enable failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else print_apm_level("APM set to level ", options.set_apm-1); } else { if (!ata_set_features(device, ATA_DISABLE_APM)) { pout("APM disable failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else pout("APM disabled\n"); } } // Enable/Disable read look-ahead if (options.set_lookahead) { bool enable = (options.set_lookahead > 0); if (!ata_set_features(device, (enable ? ATA_ENABLE_READ_LOOK_AHEAD : ATA_DISABLE_READ_LOOK_AHEAD))) { pout("Read look-ahead %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); returnval |= FAILSMART; } else pout("Read look-ahead %sabled\n", (enable ? "en" : "dis")); } // Enable/Disable write cache if (options.set_wcache) { bool enable = (options.set_wcache > 0); if (!ata_set_features(device, (enable ? ATA_ENABLE_WRITE_CACHE : ATA_DISABLE_WRITE_CACHE))) { pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); returnval |= FAILSMART; } else pout("Write cache %sabled\n", (enable ? "en" : "dis")); } // Enable/Disable DSN if (options.set_dsn) { bool enable = (options.set_dsn > 0); if (!ata_set_features(device, ATA_ENABLE_DISABLE_DSN, (enable ? 0x1 : 0x2))) { pout("DSN %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); returnval |= FAILSMART; } else pout("DSN %sabled\n", (enable ? "en" : "dis")); } // Enable/Disable write cache reordering if (options.sct_wcache_reorder_set) { bool enable = (options.sct_wcache_reorder_set > 0); if (!isSCTFeatureControlCapable(&drive)) pout("Write cache reordering %sable failed: SCT Feature Control command not supported\n", (enable ? "en" : "dis")); else if (locked) pout("Write cache reordering %sable failed: SCT not supported if ATA Security is LOCKED\n", (enable ? "en" : "dis")); else if (ataGetSetSCTWriteCacheReordering(device, enable, options.sct_wcache_reorder_set_pers, true /*set*/) < 0) { pout("Write cache reordering %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); returnval |= FAILSMART; } else pout("Write cache reordering %sabled (%s)\n", (enable ? "en" : "dis"), (options.sct_wcache_reorder_set_pers ? "persistent" : "volatile")); } // Enable/Disable write cache in SCT if (options.sct_wcache_sct_set) { if (!isSCTFeatureControlCapable(&drive)) pout("SCT Feature Control of write cache failed: SCT Feature Control command not supported\n"); else if (locked) pout("SCT Feature Control of write cache failed: SCT not supported if ATA Security is LOCKED\n"); else if (ataGetSetSCTWriteCache(device, options.sct_wcache_sct_set, options.sct_wcache_sct_set_pers, true /*set*/) < 0) { pout("SCT Feature Control of write cache failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else pout("Write cache SCT Feature Control is set to: %s (%s)\n", sct_write_cache_state_desc[options.sct_wcache_sct_set], (options.sct_wcache_sct_set_pers ? "persistent" : "volatile")); } // Freeze ATA security if (options.set_security_freeze) { if (!ata_nodata_command(device, ATA_SECURITY_FREEZE_LOCK)) { pout("ATA SECURITY FREEZE LOCK failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else pout("ATA Security set to frozen mode\n"); } // Set standby timer unless immediate standby is also requested if (options.set_standby && !options.set_standby_now) { if (!ata_nodata_command(device, ATA_IDLE, options.set_standby-1)) { pout("ATA IDLE command failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else print_standby_timer("Standby timer set to ", options.set_standby-1, drive); } // Enable/Disable SMART commands if (options.smart_enable) { if (ataEnableSmart(device)) { pout("SMART Enable failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else { pout("SMART Enabled.\n"); smart_enabled = 1; } } // Turn off SMART on device if (options.smart_disable) { if (ataDisableSmart(device)) { pout("SMART Disable failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD,returnval|=FAILSMART); } } // Exit if SMART is disabled but must be enabled to proceed if (options.smart_disable || (smart_enabled <= 0 && need_smart_enabled && !is_permissive())) { pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n"); if (!options.smart_disable) pout("(override with '-T permissive' option)\n"); return returnval; } // Enable/Disable Auto-save attributes if (options.smart_auto_save_enable) { if (ataEnableAutoSave(device)){ pout("SMART Enable Attribute Autosave failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else pout("SMART Attribute Autosave Enabled.\n"); } if (options.smart_auto_save_disable) { if (ataDisableAutoSave(device)){ pout("SMART Disable Attribute Autosave failed: %s\n\n", device->get_errmsg()); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } else pout("SMART Attribute Autosave Disabled.\n"); } // Read SMART values and thresholds if necessary ata_smart_values smartval; memset(&smartval, 0, sizeof(smartval)); ata_smart_thresholds_pvt smartthres; memset(&smartthres, 0, sizeof(smartthres)); bool smart_val_ok = false, smart_thres_ok = false; if (need_smart_val) { if (ataReadSmartValues(device, &smartval)) { pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { smart_val_ok = true; if (options.smart_check_status || options.smart_vendor_attrib) { if (ataReadSmartThresholds(device, &smartthres)){ pout("Read SMART Thresholds failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else smart_thres_ok = true; } } } // Enable/Disable Off-line testing bool needupdate = false; if (options.smart_auto_offl_enable) { if (!isSupportAutomaticTimer(&smartval)){ pout("SMART Automatic Timers not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } needupdate = smart_val_ok; if (ataEnableAutoOffline(device)){ pout("SMART Enable Automatic Offline failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else pout("SMART Automatic Offline Testing Enabled every four hours.\n"); } if (options.smart_auto_offl_disable) { if (!isSupportAutomaticTimer(&smartval)){ pout("SMART Automatic Timers not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } needupdate = smart_val_ok; if (ataDisableAutoOffline(device)){ pout("SMART Disable Automatic Offline failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else pout("SMART Automatic Offline Testing Disabled.\n"); } if (needupdate && ataReadSmartValues(device, &smartval)){ pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); smart_val_ok = false; } // all this for a newline! if ( options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable || options.smart_auto_offl_disable || options.smart_auto_offl_enable || options.set_aam || options.set_apm || options.set_lookahead || options.set_wcache || options.set_security_freeze || options.set_standby || options.sct_wcache_reorder_set || options.set_dsn) pout("\n"); // START OF READ-ONLY OPTIONS APART FROM -V and -i if ( options.smart_check_status || options.smart_general_values || options.smart_vendor_attrib || options.smart_error_log || options.smart_selftest_log || options.smart_selective_selftest_log || options.smart_ext_error_log || options.smart_ext_selftest_log || options.sct_temp_sts || options.sct_temp_hist ) pout("=== START OF READ SMART DATA SECTION ===\n"); // Check SMART status if (options.smart_check_status) { switch (ataSmartStatus2(device)) { case 0: // The case where the disk health is OK jout("SMART overall-health self-assessment test result: PASSED\n"); jglb["smart_status"]["passed"] = true; if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) { if (options.smart_vendor_attrib) pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); else { print_on(); pout("Please note the following marginal Attributes:\n"); PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 2, options.output_format); } returnval|=FAILAGE; } else pout("\n"); break; case 1: // The case where the disk health is NOT OK print_on(); jout("SMART overall-health self-assessment test result: FAILED!\n" "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); jglb["smart_status"]["passed"] = false; print_off(); if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) { returnval|=FAILATTR; if (options.smart_vendor_attrib) pout("See vendor-specific Attribute list for failed Attributes.\n\n"); else { print_on(); pout("Failed Attributes:\n"); PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 1, options.output_format); } } else pout("No failed Attributes found.\n\n"); returnval|=FAILSTATUS; print_off(); break; case -1: default: // Something went wrong with the SMART STATUS command. // The ATA SMART RETURN STATUS command provides the result in the ATA output // registers. Buggy ATA/SATA drivers and SAT Layers often do not properly // return the registers values. pout("SMART Status %s: %s\n", (device->is_syscall_unsup() ? "not supported" : "command failed"), device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); if (!(smart_val_ok && smart_thres_ok)) { print_on(); pout("SMART overall-health self-assessment test result: UNKNOWN!\n" "SMART Status, Attributes and Thresholds cannot be read.\n\n"); } else if (find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) { print_on(); jout("SMART overall-health self-assessment test result: FAILED!\n" "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n"); jwrn("Warning: This result is based on an Attribute check.\n"); jglb["smart_status"]["passed"] = false; print_off(); returnval|=FAILATTR; returnval|=FAILSTATUS; if (options.smart_vendor_attrib) pout("See vendor-specific Attribute list for failed Attributes.\n\n"); else { print_on(); pout("Failed Attributes:\n"); PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 1, options.output_format); } } else { jout("SMART overall-health self-assessment test result: PASSED\n"); jwrn("Warning: This result is based on an Attribute check.\n"); jglb["smart_status"]["passed"] = true; if (find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) { if (options.smart_vendor_attrib) pout("See vendor-specific Attribute list for marginal Attributes.\n\n"); else { print_on(); pout("Please note the following marginal Attributes:\n"); PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, 2, options.output_format); } returnval|=FAILAGE; } else pout("\n"); } print_off(); break; } // end of switch statement print_off(); } // end of checking SMART Status // Print general SMART values if (smart_val_ok && options.smart_general_values) PrintGeneralSmartValues(&smartval, &drive, firmwarebugs); // Print vendor-specific attributes if (smart_val_ok && options.smart_vendor_attrib) { print_on(); PrintSmartAttribWithThres(&smartval, &smartthres, attribute_defs, rpm, (printing_is_switchable ? 2 : 0), options.output_format); print_off(); } // If GP Log is supported use smart log directory for // error and selftest log support check. bool gp_log_supported = isGeneralPurposeLoggingCapable(&drive); if ( gp_log_supported && ( options.smart_error_log || options.smart_selftest_log || options.retry_error_log || options.retry_selftest_log)) need_smart_logdir = true; ata_smart_log_directory smartlogdir_buf, gplogdir_buf; const ata_smart_log_directory * smartlogdir = 0, * gplogdir = 0; // Read SMART Log directory if (need_smart_logdir) { if (firmwarebugs.is_set(BUG_NOLOGDIR)) smartlogdir = fake_logdir(&smartlogdir_buf, options); else if (ataReadLogDirectory(device, &smartlogdir_buf, false)) { pout("Read SMART Log Directory failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else smartlogdir = &smartlogdir_buf; } // Read GP Log directory if (need_gp_logdir) { if (firmwarebugs.is_set(BUG_NOLOGDIR)) gplogdir = fake_logdir(&gplogdir_buf, options); else if (!gp_log_supported && !is_permissive()) { if (options.gp_logdir) pout("General Purpose Log Directory not supported\n\n"); } else if (ataReadLogDirectory(device, &gplogdir_buf, true)) { pout("Read GP Log Directory failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else gplogdir = &gplogdir_buf; } // Print log directories if ((options.gp_logdir && gplogdir) || (options.smart_logdir && smartlogdir)) { if (firmwarebugs.is_set(BUG_NOLOGDIR)) pout("Log Directories not read due to '-F nologdir' option\n\n"); else PrintLogDirectories(gplogdir, smartlogdir); } // Print log pages for (i = 0; i < options.log_requests.size(); i++) { const ata_log_request & req = options.log_requests[i]; const char * type; unsigned max_nsectors; if (req.gpl) { type = "General Purpose"; max_nsectors = GetNumLogSectors(gplogdir, req.logaddr, true); } else { type = "SMART"; max_nsectors = GetNumLogSectors(smartlogdir, req.logaddr, false); } if (!max_nsectors) { if (!is_permissive()) { pout("%s Log 0x%02x does not exist (override with '-T permissive' option)\n", type, req.logaddr); continue; } max_nsectors = req.page+1; } if (max_nsectors <= req.page) { pout("%s Log 0x%02x has only %u sectors, output skipped\n", type, req.logaddr, max_nsectors); continue; } unsigned ns = req.nsectors; if (ns > max_nsectors - req.page) { if (req.nsectors != ~0U) // "FIRST-max" pout("%s Log 0x%02x has only %u sectors, output truncated\n", type, req.logaddr, max_nsectors); ns = max_nsectors - req.page; } // SMART log don't support sector offset, start with first sector unsigned offs = (req.gpl ? 0 : req.page); raw_buffer log_buf((offs + ns) * 512); bool ok; if (req.gpl) ok = ataReadLogExt(device, req.logaddr, 0x00, req.page, log_buf.data(), ns); else ok = ataReadSmartLog(device, req.logaddr, log_buf.data(), offs + ns); if (!ok) failuretest(OPTIONAL_CMD, returnval|=FAILSMART); else PrintLogPages(type, log_buf.data() + offs*512, req.logaddr, req.page, ns, max_nsectors); } // Print SMART Extendend Comprehensive Error Log bool do_smart_error_log = options.smart_error_log; if (options.smart_ext_error_log) { bool ok = false; unsigned nsectors = GetNumLogSectors(gplogdir, 0x03, true); if (!nsectors) pout("SMART Extended Comprehensive Error Log (GP Log 0x03) not supported\n\n"); else { // Read only first sector to get error count and index // Print function will read more sectors as needed ata_smart_exterrlog log_03; memset(&log_03, 0, sizeof(log_03)); if (!ataReadExtErrorLog(device, &log_03, 0, 1, firmwarebugs)) { pout("Read SMART Extended Comprehensive Error Log failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { if (PrintSmartExtErrorLog(device, firmwarebugs, &log_03, nsectors, options.smart_ext_error_log)) returnval |= FAILERR; ok = true; } } if (!ok) { if (options.retry_error_log) do_smart_error_log = true; else if (!do_smart_error_log) pout("Try '-l [xerror,]error' to read traditional SMART Error Log\n"); } } // Print SMART error log if (do_smart_error_log) { if (!( GetNumLogSectors(smartlogdir, 0x01, false) || ( !(smartlogdir && gp_log_supported) && isSmartErrorLogCapable(&smartval, &drive)) || is_permissive() )) { pout("SMART Error Log not supported\n\n"); } else { ata_smart_errorlog smarterror; memset(&smarterror, 0, sizeof(smarterror)); if (ataReadErrorLog(device, &smarterror, firmwarebugs)) { pout("Read SMART Error Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { // quiet mode is turned on inside PrintSmartErrorLog() if (PrintSmartErrorlog(&smarterror, firmwarebugs)) returnval|=FAILERR; print_off(); } } } // Print SMART Extendend Self-test Log bool do_smart_selftest_log = options.smart_selftest_log; if (options.smart_ext_selftest_log) { bool ok = false; unsigned nsectors = GetNumLogSectors(gplogdir, 0x07, true); if (!nsectors) pout("SMART Extended Self-test Log (GP Log 0x07) not supported\n\n"); else if (nsectors >= 256) pout("SMART Extended Self-test Log size %u not supported\n\n", nsectors); else { raw_buffer log_07_buf(nsectors * 512); ata_smart_extselftestlog * log_07 = reinterpret_cast(log_07_buf.data()); if (!ataReadExtSelfTestLog(device, log_07, nsectors)) { pout("Read SMART Extended Self-test Log failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { if (PrintSmartExtSelfTestLog(log_07, nsectors, options.smart_ext_selftest_log)) returnval |= FAILLOG; ok = true; } } if (!ok) { if (options.retry_selftest_log) do_smart_selftest_log = true; else if (!do_smart_selftest_log) pout("Try '-l [xselftest,]selftest' to read traditional SMART Self Test Log\n"); } } // Print SMART self-test log if (do_smart_selftest_log) { if (!( GetNumLogSectors(smartlogdir, 0x06, false) || ( !(smartlogdir && gp_log_supported) && isSmartTestLogCapable(&smartval, &drive)) || is_permissive() )) { pout("SMART Self-test Log not supported\n\n"); } else { ata_smart_selftestlog smartselftest; memset(&smartselftest, 0, sizeof(smartselftest)); if (ataReadSelfTestLog(device, &smartselftest, firmwarebugs)) { pout("Read SMART Self-test Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { print_on(); if (ataPrintSmartSelfTestlog(&smartselftest, !printing_is_switchable, firmwarebugs)) returnval |= FAILLOG; print_off(); pout("\n"); } } } // Print SMART selective self-test log if (options.smart_selective_selftest_log) { ata_selective_self_test_log log; if (!isSupportSelectiveSelfTest(&smartval)) pout("Selective Self-tests/Logging not supported\n\n"); else if(ataReadSelectiveSelfTestLog(device, &log)) { pout("Read SMART Selective Self-test Log failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { print_on(); // If any errors were found, they are logged in the SMART Self-test log. // So there is no need to print the Selective Self Test log in silent // mode. if (!printing_is_switchable) ataPrintSelectiveSelfTestLog(&log, &smartval); print_off(); pout("\n"); } } // Check if SCT commands available bool sct_ok = isSCTCapable(&drive); if ( options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int || options.sct_erc_get || options.sct_erc_set ) { if (!sct_ok) pout("SCT Commands not supported\n\n"); else if (locked) { pout("SCT Commands not supported if ATA Security is LOCKED\n\n"); sct_ok = false; } } // Print SCT status and temperature history table if (sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int)) { for (;;) { bool sct_temp_hist_ok = isSCTDataTableCapable(&drive); ata_sct_status_response sts; if (options.sct_temp_sts || (options.sct_temp_hist && sct_temp_hist_ok)) { // Read SCT status if (ataReadSCTStatus(device, &sts)) { pout("\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } if (options.sct_temp_sts) { ataPrintSCTStatus(&sts); pout("\n"); } } if (!sct_temp_hist_ok && (options.sct_temp_hist || options.sct_temp_int)) { pout("SCT Data Table command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } if (options.sct_temp_hist) { // Read SCT temperature history, // requires initial SCT status from above ata_sct_temperature_history_table tmh; if (ataReadSCTTempHist(device, &tmh, &sts)) { pout("Read SCT Temperature History failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } ataPrintSCTTempHist(&tmh); pout("\n"); } if (options.sct_temp_int) { // Set new temperature logging interval if (!isSCTFeatureControlCapable(&drive)) { pout("SCT Feature Control command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } if (ataSetSCTTempInterval(device, options.sct_temp_int, options.sct_temp_int_pers)) { pout("Write Temperature Logging Interval failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); break; } pout("Temperature Logging Interval set to %u minute%s (%s)\n", options.sct_temp_int, (options.sct_temp_int == 1 ? "" : "s"), (options.sct_temp_int_pers ? "persistent" : "volatile")); } break; } } // SCT Error Recovery Control if (sct_ok && (options.sct_erc_get || options.sct_erc_set)) { if (!isSCTErrorRecoveryControlCapable(&drive)) { pout("SCT Error Recovery Control command not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else { bool sct_erc_get = options.sct_erc_get; if (options.sct_erc_set) { // Set SCT Error Recovery Control if ( ataSetSCTErrorRecoveryControltime(device, 1, options.sct_erc_readtime ) || ataSetSCTErrorRecoveryControltime(device, 2, options.sct_erc_writetime)) { pout("SCT (Set) Error Recovery Control command failed\n"); if (!( (options.sct_erc_readtime == 70 && options.sct_erc_writetime == 70) || (options.sct_erc_readtime == 0 && options.sct_erc_writetime == 0))) pout("Retry with: 'scterc,70,70' to enable ERC or 'scterc,0,0' to disable\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); sct_erc_get = false; } else if (!sct_erc_get) ataPrintSCTErrorRecoveryControl(true, options.sct_erc_readtime, options.sct_erc_writetime); } if (sct_erc_get) { // Print SCT Error Recovery Control unsigned short read_timer, write_timer; if ( ataGetSCTErrorRecoveryControltime(device, 1, read_timer ) || ataGetSCTErrorRecoveryControltime(device, 2, write_timer)) { pout("SCT (Get) Error Recovery Control command failed\n"); if (options.sct_erc_set) { pout("The previous SCT (Set) Error Recovery Control command succeeded\n"); ataPrintSCTErrorRecoveryControl(true, options.sct_erc_readtime, options.sct_erc_writetime); } failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else ataPrintSCTErrorRecoveryControl(false, read_timer, write_timer); } pout("\n"); } } // Print Device Statistics if (options.devstat_all_pages || options.devstat_ssd_page || !options.devstat_pages.empty()) { bool use_gplog = true; unsigned nsectors = 0; if (gplogdir) nsectors = GetNumLogSectors(gplogdir, 0x04, true); else if (smartlogdir){ // for systems without ATA_READ_LOG_EXT nsectors = GetNumLogSectors(smartlogdir, 0x04, false); use_gplog = false; } if (!nsectors) pout("Device Statistics (GP/SMART Log 0x04) not supported\n\n"); else if (!print_device_statistics(device, nsectors, options.devstat_pages, options.devstat_all_pages, options.devstat_ssd_page, use_gplog)) failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } // Print Pending Defects log if (options.pending_defects_log) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x0c, true); if (!nsectors) pout("Pending Defects log (GP Log 0x0c) not supported\n\n"); else if (!print_pending_defects_log(device, nsectors, options.pending_defects_log)) failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } // Print SATA Phy Event Counters if (options.sataphy) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true); // Packet interface devices do not provide a log directory, check support bit if (!nsectors && (drive.words047_079[76-47] & 0x0401) == 0x0400) nsectors = 1; if (!nsectors) pout("SATA Phy Event Counters (GP Log 0x11) not supported\n\n"); else if (nsectors != 1) pout("SATA Phy Event Counters with %u sectors not supported\n\n", nsectors); else { unsigned char log_11[512] = {0, }; unsigned char features = (options.sataphy_reset ? 0x01 : 0x00); if (!ataReadLogExt(device, 0x11, features, 0, log_11, 1)) { pout("Read SATA Phy Event Counters failed\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else PrintSataPhyEventCounters(log_11, options.sataphy_reset); } } // Set to standby (spindown) mode and set standby timer if not done above // (Above commands may spinup drive) if (options.set_standby_now) { if (options.set_standby) { if (!ata_nodata_command(device, ATA_STANDBY, options.set_standby-1)) { pout("ATA STANDBY command failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else { print_standby_timer("Standby timer set to ", options.set_standby-1, drive); pout("Device placed in STANDBY mode\n"); } } else { if (!ata_nodata_command(device, ATA_STANDBY_IMMEDIATE)) { pout("ATA STANDBY IMMEDIATE command failed: %s\n", device->get_errmsg()); returnval |= FAILSMART; } else pout("Device placed in STANDBY mode\n"); } } // START OF THE TESTING SECTION OF THE CODE. IF NO TESTING, RETURN if (!smart_val_ok || options.smart_selftest_type == -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 (options.smart_selftest_type) { case OFFLINE_FULL_SCAN: if (!isSupportExecuteOfflineImmediate(&smartval)){ pout("Execute Offline Immediate function not supported\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("Self-test functions not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } break; case CONVEYANCE_SELF_TEST: case CONVEYANCE_CAPTIVE_SELF_TEST: if (!isSupportConveyanceSelfTest(&smartval)){ pout("Conveyance Self-test functions not supported\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } break; case SELECTIVE_SELF_TEST: case SELECTIVE_CAPTIVE_SELF_TEST: if (!isSupportSelectiveSelfTest(&smartval)){ pout("Selective Self-test functions not supported\n\n"); failuretest(MANDATORY_CMD, returnval|=FAILSMART); } break; default: break; // Vendor specific type } // Now do the test. Note ataSmartTest prints its own error/success // messages if (ataSmartTest(device, options.smart_selftest_type, options.smart_selftest_force, options.smart_selective_args, &smartval, sizes.sectors )) 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 (options.smart_selftest_type == OFFLINE_FULL_SCAN) { if (isSupportOfflineAbort(&smartval)) pout("Note: giving further SMART commands will abort Offline testing\n"); else if (ataReadSmartValues(device, &smartval)){ pout("Read SMART Data failed: %s\n\n", device->get_errmsg()); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } } // Now say how long the test will take to complete int timewait = TestTime(&smartval, options.smart_selftest_type); if (timewait) { time_t t=time(NULL); if (options.smart_selftest_type == 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 ( options.smart_selftest_type != SHORT_CAPTIVE_SELF_TEST && options.smart_selftest_type != EXTEND_CAPTIVE_SELF_TEST && options.smart_selftest_type != CONVEYANCE_CAPTIVE_SELF_TEST && options.smart_selftest_type != SELECTIVE_CAPTIVE_SELF_TEST ) pout("Use smartctl -X to abort test.\n"); } } return returnval; } smartmontools-7.0/ataprint.h0000644000175000010010000001262713367127350013221 00000000000000/* * ataprint.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-09 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef ATAPRINT_H_ #define ATAPRINT_H_ #define ATAPRINT_H_CVSID "$Id: ataprint.h 4826 2018-11-02 20:09:12Z chrfranke $\n" #include // Request to dump a GP or SMART log struct ata_log_request { bool gpl; // false: SMART, true: GP unsigned char logaddr; // Log address unsigned page; // First page (sector) unsigned nsectors; // # Sectors ata_log_request() : gpl(false), logaddr(0), page(0), nsectors(0) { } }; // Options for ataPrintMain struct ata_print_options { bool drive_info; int identify_word_level, identify_bit_level; bool smart_check_status; bool smart_general_values; bool smart_vendor_attrib; bool smart_error_log; bool smart_selftest_log; bool smart_selective_selftest_log; bool gp_logdir, smart_logdir; unsigned smart_ext_error_log; unsigned smart_ext_selftest_log; bool retry_error_log, retry_selftest_log; std::vector log_requests; bool devstat_all_pages, devstat_ssd_page; std::vector devstat_pages; unsigned pending_defects_log; bool sct_temp_sts, sct_temp_hist; bool sct_erc_get; bool sct_erc_set; unsigned sct_erc_readtime, sct_erc_writetime; bool sataphy, sataphy_reset; bool smart_disable, smart_enable; bool smart_auto_offl_disable, smart_auto_offl_enable; bool smart_auto_save_disable, smart_auto_save_enable; int smart_selftest_type; // OFFLINE_FULL_SCAN, ..., see atacmds.h. -1 for no test bool smart_selftest_force; // Ignore already running test ata_selective_selftest_args smart_selective_args; // Extra args for selective self-test unsigned sct_temp_int; bool sct_temp_int_pers; enum { FMT_BRIEF = 0x01, FMT_HEX_ID = 0x02, FMT_HEX_VAL = 0x04 }; unsigned char output_format; // FMT_* flags firmwarebug_defs firmwarebugs; // -F options bool fix_swapped_id; // Fix swapped ID strings returned by some buggy drivers ata_vendor_attr_defs attribute_defs; // -v options bool ignore_presets; // Ignore presets from drive database bool show_presets; // Show presets and exit unsigned char powermode; // Skip check, if disk in idle or standby mode unsigned char powerexit; // exit() code for low power mode bool get_set_used; // true if any get/set command is used bool get_aam; // print Automatic Acoustic Management status int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management bool get_apm; // print Advanced Power Management status int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management bool get_lookahead; // print read look-ahead status int set_lookahead; // disable(-1), enable(1) read look-ahead int set_standby; // set(1..255->0..254) standby timer bool set_standby_now; // set drive to standby bool get_security; // print ATA security status bool set_security_freeze; // Freeze ATA security bool get_wcache; // print write cache status int set_wcache; // disable(-1), enable(1) write cache bool sct_wcache_reorder_get; // print write cache reordering status int sct_wcache_reorder_set; // disable(-1), enable(1) write cache reordering bool sct_wcache_reorder_set_pers; bool sct_wcache_sct_get; // print SCT Feature Control of write cache status int sct_wcache_sct_set; // determined by ata set features command(1), force enable(2), force disable(3) bool sct_wcache_sct_set_pers; // persistent or volatile bool get_dsn; // print DSN status int set_dsn; // disable(02h), enable(01h) DSN ata_print_options() : drive_info(false), identify_word_level(-1), identify_bit_level(-1), smart_check_status(false), smart_general_values(false), smart_vendor_attrib(false), smart_error_log(false), smart_selftest_log(false), smart_selective_selftest_log(false), gp_logdir(false), smart_logdir(false), smart_ext_error_log(0), smart_ext_selftest_log(0), retry_error_log(false), retry_selftest_log(false), devstat_all_pages(false), devstat_ssd_page(false), pending_defects_log(0), sct_temp_sts(false), sct_temp_hist(false), sct_erc_get(false), sct_erc_set(false), sct_erc_readtime(0), sct_erc_writetime(0), sataphy(false), sataphy_reset(false), smart_disable(false), smart_enable(false), smart_auto_offl_disable(false), smart_auto_offl_enable(false), smart_auto_save_disable(false), smart_auto_save_enable(false), smart_selftest_type(-1), smart_selftest_force(false), sct_temp_int(0), sct_temp_int_pers(false), output_format(0), fix_swapped_id(false), ignore_presets(false), show_presets(false), powermode(0), powerexit(0), get_set_used(false), get_aam(false), set_aam(0), get_apm(false), set_apm(0), get_lookahead(false), set_lookahead(0), set_standby(0), set_standby_now(false), get_security(false), set_security_freeze(false), get_wcache(false), set_wcache(0), sct_wcache_reorder_get(false), sct_wcache_reorder_set(0), sct_wcache_reorder_set_pers(false), sct_wcache_sct_get(false), sct_wcache_sct_set(0), sct_wcache_sct_set_pers(false), get_dsn(false), set_dsn(0) { } }; int ataPrintMain(ata_device * device, const ata_print_options & options); #endif smartmontools-7.0/AUTHORS0000644000175000010010000000365413401007254012263 00000000000000$Id: AUTHORS 4844 2018-12-02 16:56:12Z chrfranke $ Developers / Maintainers / Contributors: Raghava Aditya <...> Bruce Allen <...> Casey Biemiller Erik Inge Bolsø <...> Stanislav Brabec Peter Cassidy Praveen Chidambaram Jonghwan Choi Yuri Dario Casper Dik <...> Christian Franke Guilhem Frézou <...> Thomas Gatterweh Douglas Gilbert Guido Guenther Jordan Hargrave Joerg Hering <...> Geoff Keating Dr. David Kirkby <...> Song Liu Dan Lukes Kai Mäkisara Nidhi Malhotra Harry Mallon Eduard Martinescu Frédéric L. W. Meunier <...> Kimihiro Nonaka <...> Gabriele Pohl Alex Samorukov Keiji Sawada Manfred Schwarb Tomas Smetana David Snyder Sergey Svishchev Tommy Vestermark Roger Willcocks Phil Williams <...> Hank Wu Shengfeng Zhou Richard Zybert The first smartmontools code was derived from the smartsuite package, written by Michael Cornwell and Andre Hedrick. smartmontools-7.0/autogen.sh0000755000175000010010000000464213377277146013237 00000000000000#!/bin/sh # $Id: autogen.sh 4838 2018-11-27 17:27:02Z chrfranke $ # # Generate ./configure from configure.ac and Makefile.in from Makefile.am. # This also adds files like missing,depcomp,install-sh to the source # directory. To update these files at a later date use: # autoreconf -f -i -v force=; warnings= while [ $# -gt 0 ]; do case $1 in --force) force=$1; shift ;; --warnings=?*) warnings="${warnings} $1"; shift ;; *) echo "Usage: $0 [--force] [--warnings=CATEGORY ...]"; exit 1 ;; esac; done # Cygwin? test -x /usr/bin/uname && /usr/bin/uname | grep -i CYGWIN >/dev/null && { # 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 } # Find automake if [ -n "$AUTOMAKE" ]; then ver=$("$AUTOMAKE" --version) || exit 1 else maxver= for v in 1.16 1.15 1.14 1.13 1.12 1.11 1.10; do minver=$v; test -n "$maxver" || maxver=$v ver=$(automake-$v --version 2>/dev/null) || continue AUTOMAKE="automake-$v" break done if [ -z "$AUTOMAKE" ]; then echo "GNU Automake $minver (up to $maxver) is required to bootstrap smartmontools from SVN." exit 1; fi fi ver=$(echo "$ver" | sed -n '1s,^.*[^.0-9]\([12]\.[0-9][-.0-9pl]*\).*$,\1,p') if [ -z "$ver" ]; then echo "$AUTOMAKE: Unable to determine automake version." exit 1 fi # Check aclocal if [ -z "$ACLOCAL" ]; then ACLOCAL="aclocal$(echo "$AUTOMAKE" | sed -n 's,^.*automake\(-[.0-9]*\),\1,p')" fi "$ACLOCAL" --version >/dev/null || exit 1 # Warn if Automake version was not tested amwarnings=$warnings case "$ver" in 1.10|1.10.[123]|1.11|1.11.[1-6]|1.12.[2-6]|1.13.[34]) # OK ;; 1.14|1.14.1|1.15|1.15.1|1.16|1.16.1) # TODO: Enable 'subdir-objects' in configure.ac # For now, suppress 'subdir-objects' forward-incompatibility warning test -n "$warnings" || amwarnings="--warnings=no-unsupported" ;; *) echo "Note: GNU Automake version ${ver} was not tested by the developers." echo "Please report success/failure to the smartmontools-support mailing list." esac # required for aclocal-1.10 --install test -d m4 || mkdir m4 || exit 1 set -e # stops on error status test -z "$warnings" || set -x ${ACLOCAL} -I m4 --install $force $warnings autoheader $force $warnings ${AUTOMAKE} --add-missing --copy ${force:+--force-missing} $amwarnings autoconf $force $warnings smartmontools-7.0/cciss.cpp0000644000175000010010000001600413405511237013021 00000000000000/* * cciss.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2007 Sergey Svishchev * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include "config.h" #if defined(linux) || defined(__linux__) # include # ifdef HAVE_LINUX_COMPILER_H # include # endif # if defined(HAVE_LINUX_CCISS_IOCTL_H) # include # define _HAVE_CCISS # endif # include # ifndef be32toh # define be32toh __be32_to_cpu # endif #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) # include # include CISS_LOCATION # define _HAVE_CCISS #endif #ifdef _HAVE_CCISS #include "cciss.h" #include "scsicmds.h" #include "utility.h" const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4858 2018-12-16 17:59:59Z chrfranke $" CCISS_H_CVSID; typedef struct _ReportLUNdata_struct { uint32_t LUNListLength; /* always big-endian */ uint32_t reserved; uint8_t LUN[CISS_MAX_LUN][8]; } ReportLunData_struct; /* Structure/defines of Report Physical LUNS of drive */ #ifndef CISS_MAX_LUN #define CISS_MAX_LUN 16 #endif #define CISS_MAX_PHYS_LUN 1024 #define CISS_REPORT_PHYS 0xc3 #define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */ #define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */ static int cciss_getlun(int device, int target, unsigned char *physlun, int report); static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB, unsigned int CDBlen, char *buff, unsigned int size, unsigned int LunID, unsigned char *scsi3addr, int fd); /* This is an interface that uses the cciss passthrough to talk to the SMART controller on the HP system. The cciss driver provides a way to send SCSI cmds through the CCISS passthrough. */ int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int report) { unsigned char pBuf[512] = {0}; unsigned char phylun[8] = {0}; int iBufLen = 512; int len = 0; // used later in the code. int status = cciss_getlun(device, target, phylun, report); if (report > 0) printf(" cciss_getlun(%d, %d) = 0x%x; scsi3addr: %02x %02x %02x %02x %02x %02x %02x %02x\n", device, target, status, phylun[0], phylun[1], phylun[2], phylun[3], phylun[4], phylun[5], phylun[6], phylun[7]); if (status) { return -ENXIO; /* give up, assume no device there */ } status = cciss_sendpassthru( 2, iop->cmnd, iop->cmnd_len, (char*) pBuf, iBufLen, 1, phylun, device); if (0 == status) { if (report > 0) printf(" status=0\n"); if (DXFER_FROM_DEVICE == iop->dxfer_dir) { memcpy(iop->dxferp, pBuf, iop->dxfer_len); if (report > 1) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; printf(" 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; } 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, pBuf, len); iop->resp_sense_len = iBufLen; if (report > 1) { printf(" >>> Sense buffer, len=%d:\n", (int)len); dStrHex((const uint8_t *)pBuf, len , 1); } } if (report) { if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { printf(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff, pBuf[2] & 0xf, pBuf[12], pBuf[13]); } else printf(" status=0x%x\n", status); } if (iop->scsi_status > 0) return 0; else { if (report > 0) printf(" ioctl status=0x%x but scsi status=0, fail with ENXIO\n", status); return -ENXIO; /* give up, assume no device there */ } } static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB, unsigned int CDBlen, char *buff, unsigned int size, unsigned int LunID, unsigned char *scsi3addr, int fd) { int err ; IOCTL_Command_struct iocommand; memset(&iocommand, 0, sizeof(iocommand)); if (cmdtype == 0) { // To controller; nothing to do } else if (cmdtype == 1) { iocommand.LUN_info.LogDev.VolId = LunID; iocommand.LUN_info.LogDev.Mode = 1; } else if (cmdtype == 2) { memcpy(&iocommand.LUN_info.LunAddrBytes,scsi3addr,8); iocommand.LUN_info.LogDev.Mode = 0; } else { fprintf(stderr, "cciss_sendpassthru: bad cmdtype\n"); return 1; } memcpy(&iocommand.Request.CDB[0], CDB, CDBlen); iocommand.Request.CDBLen = CDBlen; iocommand.Request.Type.Type = TYPE_CMD; iocommand.Request.Type.Attribute = ATTR_SIMPLE; iocommand.Request.Type.Direction = XFER_READ; iocommand.Request.Timeout = 0; iocommand.buf_size = size; iocommand.buf = (unsigned char *)buff; if ((err = ioctl(fd, CCISS_PASSTHRU, &iocommand))) { fprintf(stderr, "CCISS ioctl error %d (fd %d CDBLen %u buf_size %u)\n", fd, err, CDBlen, size); } return err; } static int cciss_getlun(int device, int target, unsigned char *physlun, int report) { unsigned char CDB[16]= {0}; ReportLunData_struct *luns; int reportlunsize = sizeof(*luns) + CISS_MAX_PHYS_LUN * 8; int ret; luns = (ReportLunData_struct *)malloc(reportlunsize); memset(luns, 0, reportlunsize); /* Get Physical LUN Info (for physical device) */ CDB[0] = CISS_REPORT_PHYS; CDB[6] = (reportlunsize >> 24) & 0xFF; /* MSB */ CDB[7] = (reportlunsize >> 16) & 0xFF; CDB[8] = (reportlunsize >> 8) & 0xFF; CDB[9] = reportlunsize & 0xFF; if ((ret = cciss_sendpassthru(0, CDB, 12, (char *)luns, reportlunsize, 0, NULL, device))) { free(luns); return ret; } if (report > 1) { unsigned int i,j; unsigned char *stuff = (unsigned char *)luns; pout("\n===== [%s] DATA START (BASE-16) =====\n", "LUN DATA"); for (i=0; i<(sizeof(_ReportLUNdata_struct)+15)/16; 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 (%u Bytes) =====\n\n", "LUN DATA", (unsigned)sizeof(_ReportLUNdata_struct)); } if (target >= 0 && target < (int) be32toh(luns->LUNListLength) / 8) { memcpy(physlun, luns->LUN[target], 8); free(luns); return 0; } free(luns); return 1; } #endif smartmontools-7.0/cciss.h0000644000175000010010000000061413336613560012473 00000000000000/* * cciss.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2007 Sergey Svishchev * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef CCISS_H_ #define CCISS_H_ #define CCISS_H_CVSID "$Id: cciss.h 4761 2018-08-20 19:33:04Z chrfranke $" int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int report); #endif /* CCISS_H_ */ smartmontools-7.0/ChangeLog0000644000175000010010000040127313412155326012772 00000000000000$Id: ChangeLog 4883 2018-12-30 14:48:54Z chrfranke $ 2018-12-30 Christian Franke smartmontools 7.0 2018-12-29 Christian Franke smartctl.8.in: Remove extra quote. INSTALL: Update or remove various outdated info. 2018-12-28 Christian Franke configure.ac: Set drivedb.h branch to 7.0. update-smart-drivedb.in: Update public key block. update-smart-drivedb.8.in: Update key ID. Create new branch RELEASE_7_0_DRIVEDB. Sign drivedb.h using new key ID 721042C5. 2018-12-27 Christian Franke do_release: Add quotes to AC_INIT regex. configure.ac: Update PACKAGE_HOMEPAGE. configure.ac: Set release number to 7.0 smartctl.cpp: Set JSON format version to 1.0 (#766). scsiprint.cpp: Omit JSON values for unavailable counters from Format Status log page. This ensures that each JSON value always has the same type. drivedb.h: - SandForce Driven SSDs: Kingston E50 (#756) - WDC HGST Ultrastar He10 (#959, #997, #1093, #1111) - Toshiba 2.5" HDD MQ04UBF... (USB 3.0) (#937) - Seagate Barracuda 7200.10: HP OEM 160GB (#1037) - Seagate Constellation ES.3: HP OEM 4TB - Seagate Exos 5E8 (#1058) - Seagate IronWolf Pro (#1076, GH issues/10, GH issues/14) - WD Blue and Green SSDs: Rename, add Green (#980, #1073) 2018-12-20 Donald Pierce <...> drivedb.h: - Dell Certified Intel S3520 Series SSDs (#1147) - Dell Certified Intel S4x00/D3-S4x10 Series SSDs (#1148) 2018-12-20 Christian Franke drivedb.h: - SandForce Driven SSDs: Kingston HyperX Fury (#805) - Phison Driven SSDs: PNY CS2211 (#992) - JMicron based SSDs: ADATA SX390 (#818), KingSpec KDM-SA.51-008GMJ (#741) - SiliconMotion based SSDs: KingSpec KSD, KingSpec T60, Team Group L5Lite 3D (#1144), Transcend ESD400 - USB: Transcend ESD400 (0x2174:0x2000) smartd.cpp: Remove unneeded '.c_str()' call. Update a comment. configure.ac: Use AS_HELP_STRING instead of AC_HELP_STRING as suggested by autoupdate. Add missing check for 'enableval'. 2018-12-16 Christian Franke smartd.8.in: Don't use empty lines before '.SH' macros. smartd.cpp: [_WIN32] Remove check for '-m [sys]msgbox'. nvmeprint.cpp: Don't print NSID in SMART/Health Information title line. This log is always read with broadcast NSID. 2018-12-16 Giuseppe Iuculano cciss.cpp: Fix kFreeBSD build (Debian kfreebsd.patch). smartd.service.in: Declaring After=syslog.target is unnecessary by now because syslog is socket-activated and will therefore be started when needed (Debian removesyslogtarget.patch). 2018-12-11 Christian Franke smartd.conf.5.in: Update DEVICESCAN info and move it up to a new section. Add section header for DEFAULT SETTINGS. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Remove EXPERIMENTAL notes for features added before 6.5. os_linux.cpp: Call realpath() with full /sys/* path instead of device name (GH pull/23). This fixes detection of hpsa devices (regression from r4603). 2018-12-11 Harry Mallon scsinvme.cpp: Fix debug message. 2018-12-05 Christian Franke smartctl.8.in, smartd.conf.5.in: Mark '-d sntjmicron' as EXPERIMENTAL. drivedb.h: Enable JMicron JMS583 entry, use an internal -d option. scsinvme.cpp: Detect this internal -d option and ask user to test '-d sntjmicron'. scsinvme.cpp: Add missing include of config.h. 2018-12-05 Harry Mallon Add '-d sntjmicron[,NSID]' device type for NVMe drives behind JMicron USB to NVMe bridges (JMS583). 2018-12-04 Christian Franke os_linux.cpp: Add '-d by-id' option to device scanning. If specified, scan '/dev/disk/by-id/*' for symlinks to '/dev/sdX' and remove duplicates. 2018-12-02 Christian Franke drivedb.h: - Samsung based SSDs: CM851 (#1109), SM863a (#1140) - SiliconMotion based SSDs: Transcend 420K (GH issues/20), Transcend 630 (#1038) - Western Digital Gold: Re-add 8TB *2 variant - USB: Buffalo HD-PNTU3 (0x0411:0x01e7), HD-LC3 (0x0411:0x027e) - USB: ADATA NH13 (0x125f:0xa13a), HD710P (0x125f:0xa75a) - USB: Verbatim External Hard Drive (0x18a5:0x0408) (#1107) AUTHORS: Add Harry Mallon. 2018-12-02 Harry Mallon drivedb.h: USB: LaCie Rugged Mini HDD (0x059f:0x106b) Fix many typos. ataprint.cpp: Fix Form Factor string with bits set in reserved area - Happens with APPLE SSD SD0256F 2018-11-27 Christian Franke os_linux.cpp: Add USB ID detection for '/dev/sgN'. smartd_warning.sh.in: Fix typo (#1138). 2018-11-27 Harry Mallon autogen.sh: allow automake 1.16 and 1.16.1. 2018-11-25 Christian Franke drivedb.h: - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: Micron 1100 alternative ID string (#1131) - SandForce Driven SSDs: Comay BladeDrive E28 (#823), MX-DS FUSION (#900), OCZ Deneva 2 *.C (#1119), OCZ-VERTEX3 LT - Phison Driven SSDs: Kingston A400 with extra space in ID (#801) - Samsung based SSDs: SM951 *HDGM variant (patch from #1113) - SiliconMotion based SSDs: KingDian S400 (#1116) - Western Digital Gold: 1TB, 2TB (#1035, #1047), 8TB (#1033), 12TB, attribute 22 "Helium_Level" (patch from #1115) 2018-11-25 Cameron Costa drivedb.h: Intel S4510 M.2 (#1121, #1122, #1123, #1133) 2018-11-13 Christian Franke os_linux.cpp: Drop device scan support for obsolete devfs. Implement new version of scan_smart_devices(). This avoids duplicates if multiple '-d TYPE' options are specified. dev_interface.cpp, dev_interface.h: Add default implementation for old version of scan_smart_devices(). 2018-11-02 Oleksii Samorukov os_darwin.cpp, os_freebsd.cpp: fix return value in error paths patch provided by rikard.falkeborn (github) 2018-11-02 Christian Franke json.cpp: Allow UTF-8 characters in strings. ataprint.cpp: Add JSON support for '-l defects'. Add numeric values to JSON 'interface_speed' info. Replace local 'le*_to_uint()' with 'sg_get_unaligned_le*()'. ataprint.cpp, ataprint.h: Remove request to send '-l defects' output. Remove 'pending_defects_info' flag. smartctl.cpp, smartctl.8.in: Add '-l defects' to '-x' output. 2018-10-25 Christian Franke json.cpp, json.h: Add 'pretty' print option. smartctl.cpp, smartctl.8.in: Add '--json=c' option to disable pretty-printing. ataprint.cpp, nvmeprint.cpp, smartctl.cpp: Use const references for json::ref function parameters. json.cpp, json.h: Clean up usage of 'int64_t' and 'long long'. Use PRI?64 instead of "ll?" in printf() format strings. This re-enables build on older versions of MinGW. 2018-10-23 Christian Franke json.cpp: Remove extra space after JSON key names. json.cpp, json.h: Remove return of self reference from operator=(). json.cpp, json.h: Change handling of unsafe and 128-bit integers: Output as string 'KEY_s' and LE byte array 'KEY_le' if range exceeded or verbose mode enabled. smartctl.cpp, smartctl.8.in: Add '--json=v' option. 2018-10-17 Christian Franke os_win32/popen_win32.cpp, os_win32/popen.h: New popen()/pclose() for Windows. Unlike MSVCRT _popen(), it does not open a new console. os_win32.cpp: Remove run_cmd(), use popen() instead. os_win32/daemon_win32.cpp, os_win32/daemon_win32.h: Remove daemon_spawn(). smartd.cpp: Remove _WIN32 specific usage of daemon_spawn(), use generic code with popen() also on Windows. Place quotes around warning script path on Windows. Makefile.am, os_win32/vc14/smart*.vcxproj*: Add new files. 2018-10-17 Rick Chen scsiprint.cpp: Add SCSI information to JSON output as below: - Drive trip temperature (#1079) - Error counter log read/write/verify (#1079) - Grown defect list (#1082) - Percentage used endurance indicator (#1083) 2018-10-14 Christian Franke drivedb.h: - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: MX500 M.2 - Samsung based SSDs: Samsung SM841 (#1043), PM841 (#1052), Samsung 860 EVO (#1034, #1040, #1051, #1059), Samsung 860 PRO (#1010, #1068, #1102, #1103, #1104), Samsung Portable SSD T5 (#1050) - USB: Samsung Portable SSD T5 (0x04e8:0x61f5) (#1050) os_darwin.cpp: Add missing braces to SMART RETURN STATUS LBA register setting. Detected by g++ 7.3 -Wmisleading-indentation. 2018-10-11 Christian Franke os_win32.cpp: Decode Windows 10 1809 and Server 2019 build number. Move "(64)" to end of version info. os_linux.cpp: Fix '-d megaraid' open crash on missing /proc/devices. There is no /proc/devices on ESXi (see #800) and WSL. 2018-10-09 Christian Franke smartd.cpp: Move code for '--capabilities' to separate functions. smartd.cpp: Rework main loop. smartctl.cpp, smartd.cpp, os_linux.cpp, os_solaris.cpp: Replace all uses of EXIT() macro. Use early return where possible, use throw otherwise. utility.h: Remove EXIT() macro. utility.cpp: Detect more C++ language versions for -V option. drivedb.h: - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: Rename, BX500 (#1095) - Seagate Samsung SpinPoint F4 EG (AF) (#1090) - Seagate Momentus 5400.6: Add '-F xerrorlba' (#1094) - USB: JMicron JM562 (0x152d:0x0562) (IDENTIFY only, see #966) - USB: VIA VL715 (0x2109:0x0715) (#1098) 2018-10-09 Anthony D'Atri drivedb.h: (#1096) - Samsung based SSDs: Samsung PM863a (#951, #952, #961, #962, #972) - Intel 730 and DC S35x0/3610/3700 Series SSDs: Dell-flavor S3500 2018-10-09 Thomas Niedermeier drivedb.h: Samsung PM883 and SM883 (GH pull/19) 2018-09-27 Christian Franke INSTALL: Update list of default ./configure options. utility.cpp: Add check of sg_get_unaligned_[bl]e16() and *32 to check_endianness(). utility.cpp, utility.h: Optionally use C++11 'std::regex' instead of POSIX regex(3). configure.ac: Add option '--with-cxx11-regex'. utility.cpp, utility.h: Simplify 'class regular_expression', remove unneeded flag parameters, remove unused function. atacmds.cpp, knowndrives.cpp, os_win32.cpp, smartd.cpp: Adjust usage accordingly. configure.ac, utility.cpp, utility.h: Remove replacement for missing 'strtoull()'. configure.ac: Change default for '--with-nvme-devicescan' to 'yes' on Linux and Windows. Keep 'no' on FreeBSD, NetBSD and Darwin. 2018-09-26 Christian Franke configure.ac: Print warning if systemd(1) is present but libsystemd-dev package is missing. smartd.cpp: Notify READY=1 to systemd just before first sleep() to ensure that the signal handlers are set. smartd.cpp: Always ignore failure of ATA SMART ENABLE command if '-T permissive' is specified. Useful for testing on virtual machines. 2018-09-21 Christian Franke configure.ac, os_linux.cpp: Remove redundant define WITH_SELINUX. configure.ac: Check for 'libcap-ng' only on Linux. Rework __USE_MINGW_ANSI_STDIO test for MinGW runtime. Print 'deprecated' warning for '--without-working-snprintf'. Add systemd(1) notify support to smartd (#1081): configure.ac: Add option '--with-libsystemd'. Makefile.am: Add linker flag and man page conditional. smartd.cpp: If environment variable NOTIFY_SOCKET is set, use sd_notify(3) to inform the service manager about state changes. smartd.service.in: Set 'Type=notify'. smartd.8.in: Document new functionality. 2018-09-16 Christian Franke atacmds.cpp: Avoid possible virtual call in dtor (cppcheck 1.84: virtualCallInConstructor). os_win32.cpp: Use unsigned int for bit shifts (cppcheck 1.84: shiftTooManyBitsSigned). Makefile.am: Set HAVE_WORKING_SNPRINTF also in VC14 config.h. os_netbsd.cpp: Add spaces between string literals and macros for C++11 (g++ -Wliteral-suffix). ataprint.cpp: Add JSON support for '-l selective'. drivedb.h: Update or remove links in warning messages. drivedb.h: Crucial/Micron BX300, MX1/2/3/500, M5/600, 1100 SSDs: - Rename, - Crucial BX300 (GH pull/16, #963), - Crucial MX300 750GB, - Crucial MX500 (#977, #994, #995, #1004, #1024), - Micron M500IT (#958), - Micron 1100 OEM (GH pull/17), - fix name of attribute 202 and 248. 2018-09-12 Christian Franke ataprint.cpp: Get JSON values 'temperature.op_limit_min/max' from Device Statistics. atacmds.h, ataprint.cpp: Print ACS-4 max operating temperature from SCT Status. Makefile.am: Remove define of 'HAVE_GETOPT_LONG'. os*.cpp: Remove remaining checks for 'HAVE_GETOPT_LONG'. configure.ac: Remove check for 'uname()'. os_generic.cpp, os_qnxnto.cpp: Remove function 'unsupported()'. drivedb.h: - HGST Deskstar NAS: *6040ALE614 (#935, #1089) - HGST Ultrastar DC HC520 (He12) (#1086) 2018-09-12 Anthony D'Atri drivedb.h: Micron 5100 Pro / 5200 SSDs (#1071) 2018-09-11 Oleksii Samorukov os_freebsd.cpp: Fix build on FreeBSD 12, patch by fernape@ 2018-09-10 Christian Franke drivedb.h: - Seagate Enterprise Capacity 3.5 HDD: V5.1 (#1087) - Seagate Exos X12 HDD (#1042, #1046) - Western Digital VelociRaptor (AF): WD5000BHTZ (patch from #1041) 2018-09-10 David Purdy drivedb.h: Phison Driven SSDs: Kingston A400 (#801) 2018-09-02 Christian Franke dev_intelliprop.h: Fix copyright info. ataprint.cpp, nvmeprint.cpp: Change JSON value 'power_on_hours' to 'power_on_time.hours'. Add '.minutes' if available. scsiprint.cpp: Add JSON values 'power_on_time.hours/minutes' from Seagate factory lpage or from background scan lpage. 2018-08-20 Christian Franke Add missing license headers to some source files. 2018-08-19 Christian Franke Add SPDX-License-Identifier to all files with GPL header (#919). Remove GPL headers. Remove outdated info about smartsuite. getopt/*, regex/*: Replace with current version from glibc 2.28 (2018-08-01). Add _GETOPT/REGEX*_STANDALONE configurations. Makefile.am, os_win32/vc14/smart*.vcxproj*: Set *_STANDALONE. Add new files. examplescripts/README: Update mailing list address. os_solaris_ata.s: Remove old mailing list address. os_win32/wbemcli_small.h: Remove this file. The file is usually provided by recent MinGW packages. configure.ac: Remove check for . Makefile.am, os_win32/wmiquery.h, os_win32/vc14/smart*.vcxproj*: Remove usage of 'wbemcli_small.h'. ataprint.cpp, nvmeprint.cpp: Add JSON values 'power_cycle_count' and 'power_on_hours'. json.cpp, json.h: Add 'set_if_safe_*' member functions. 2018-08-13 Christian Franke ataprint.cpp: Add JSON support for '-l devstat'. Add JSON support also for old SCT Status format. 2018-08-10 Christian Franke smartctl.cpp, os_win32/wmiquery.h: Add missing printf() format checks. This also silences -Wformat-nonliteral warnings from clang++ 5.0. os_win32.cpp: Increase IOCTL_ATA_PASS_THROUGH timeout to 60 seconds. 2018-08-10 Zhdan Bybin drivedb.h: - Intel S3520 Series SSDs (#985) - Intel S4510/S4610/S4500/S4600 Series SSDs (#912, #928, #1000) 2018-08-04 Christian Franke Remove int64.h, use or instead. configure.ac, utility.cpp, utility.h: Add 128-bit unsigned integer to string conversion. Provides full integer precision if compiler supports '__int128' (e.g. x86_64 GCC and CLang). json.cpp, nvmeprint.cpp: Use these new functions. Makefile.am: Adjust config-vc14 target. 2018-08-02 Christian Franke scsicmds.h, scsiprint.cpp: Add support for SAS host managed drives (patch from #1045). 2018-08-01 Christian Franke dev_interface.cpp, scsiata.cpp, smartctl.8.in, smartd.conf.5.in: Add option '-d scsi+TYPE' to disable SAT auto detection. Useful in conjunction with TYPEs 'aacraid' and 'cciss' (#871). 2018-07-31 Christian Franke drivedb.h: - Phison Driven SSDs: Kingston DC400 (#933, #1011), move GOODRAM to ... - Phison Driven OEM SSDs: ... here, PC Engines msata16d (#967), INTENSO SATA III TOP (#1053) - USB: Iomega MDHD500-U (0x059b:0x0274) (#1003) - USB: Freecom (0x07ab:0xfc17) (#1049) - USB: JMicron JMS539 (0x152d:0x0539/0x2801) (patch from #970) - USB: JMicron (0x152d:0x0561) (#945) - USB: JMicron JMS567 (0x152d:0x2567) (#948) - USB: JMicron (0x152d:0x578e) (#987) json.cpp: Add missing ';' to '--json=g' output of 128-bit values. 2018-07-29 Christian Franke os_win32.cpp: Decode Windows Server 1803 build number. Improve search for actual CSMI port number. 2018-06-21 Christian Franke os_linux.cpp: Rework handling of glob() return code. Don't abort device scan on missing '/dev/discs' (#1036). os_win32.cpp: Decode Windows 10 1803 build number. Silence g++ 7.3 -Wformat-truncation warning. 2018-04-19 Christian Franke utility.cpp, utility.h: Use array reference for buffer parameter of dateandtimezoneepoch(). Remove no longer used dateandtimezone(). utility.cpp: Add check of sg_get_unaligned_[bl]e64() to check_endianness(). 2018-04-16 Douglas Gilbert switch usage of unaligned.h to sg_unaligned.h which is functionally the same. sg_unaligned.h is the same header used by libsgutils which is the basis of the sg3_utils, sdparm and ddpt packages available on many of the same architectures as smartmontools is. This change introduces a "sg_" prefix on the inline functions defined sg_unaligned.h . The new header has specializations for big and little endian machines that depends on the non-standard bswap_16(), bswap_32() and bswap_64() calls. They are defined in the byteswap.h header which is a GNU extension. According to the 'net both gcc and clang use intrinsics {assembler ?} to implement these calls. If the byteswap.h header is not present on the build machine, the generic implementations will be used for the "unaligned" family of functions. Additionally the generic implementations can be imposed with './configure --disable-fast-lebe'. Developers may need to use './autogen.sh' prior to their normal build sequence. Please report any problems to the author. 2018-03-28 Christian Franke ataprint.cpp, nvmeprint.cpp, scsiprint.cpp: Output JSON 'user_capacity' as 'blocks' and 'bytes'. Handle both as unsafe ints. smartd.cpp: Ignore remaining percentage in initial check of self-test execution status. scsiata.cpp: Fix device type info for 'usbcypress'. os_linux.cpp: Fix device scan crash on missing /proc/devices. update-smart-drivedb.in, update-smart-drivedb.8.in: Add option '-u github'. 2018-03-20 Christian Franke nvmeprint.cpp: Add initial JSON support for '-i', '-H' and '-A'. json.cpp, json.h: Add support for 64 and 128 bit unsigned integers. Add 'set_unsafe_*()' member functions to print unsigned integers >= 53 bit as JSON number and string. 2018-03-07 Douglas Gilbert smartd.cpp: - continue to use READ CAPACITY(10) first on unseen SCSI devices but once we discover the need for READ CAPACITY(16) use it for subsequent accesses dev_interface.h: - struct scsi_device: add set_rcap16_first() and use_rcap16() const methods scsicmds.cpp: - use scsi_device::set_rcap16_first() when READ CAPACITY(10) reports 32 bits can't represent the number of blocks 2018-03-06 Alex Samorukov drivedb.h: - add Transcend PSD SSD family (#979) - add Toshiba HK4R Series SSD (#898) - extend Western Digital Re regexp (#896) - extend Wester Digital Se regexp (#953) - add Smartbuy ignition plus (#976) 2018-03-05 Gabriele Pohl drivedb.h: - Add Seagate IronWolf 12TB ST12000VN0007-2GS116 (#988) 2018-03-05 Alex Samorukov drivedb.h: add Seagate Barracuda Pro family (#981) 2018-03-01 Alex Samorukov os_freebsd.cpp: Fix build under -CURRENT (patch by cy@) 2018-02-28 Alex Samorukov drivedb.h: - Add SanDisk SDSSDH2128G (#982) 2018-02-27 Alex Samorukov drivedb.h: - extend PLEXTOR PX regexp (#934) - add Seagate Enterprise NAS HDD family (#946) - add SanDisk SDSA6MM-* family (#965) - fix Seagate Laptop HDD regexp (#955) - add Seagate Barracuda Compute series (#927) - extend Seagate Enterprise Capacity 3.5 HDD regexp (#956) 2018-02-26 Alex Samorukov drivedb.h: - add Seagate XF1230 SSD (GH: issues/4) - add Intel SSD Pro 5400s Series (GH: pull/5) - add SanDisk-SD8SN8U-256G-1006 (GH: pull/3) - add Toshiba Q300 SSD series (#932) - extend HGST Deskstar NAS regexp (#975) - add KINGSTON SNS4151S316GD SSD (#902) 2018-02-20 Christian Franke os_netbsd.cpp: Apply patch-os_netbsd.cpp 1.1 (2017-12-15) from pkgsrc.se/sysutils/smartmontools: Add missing . configure.ac, int64.h: Remove support for pre-C99 environments without and . configure.ac: Add '-Wformat=2 -fstack-protector-strong' unless CXXFLAGS include other '-W' or '-f' options. 2018-02-16 Christian Franke drivedb.h: - USB: Default to '-d sat' for Toshiba (0x0480), Seagate (0x0bc2), Western Digital (0x1058), Initio (0x13fd), ASMedia (0x174c). Keep known exceptions. Merge some entries. 2018-02-08 Douglas Gilbert nvme on windows: just some code comments. Seems as though W10 tries to completely neuter the idea of a pass-through. 2018-01-06 Douglas Gilbert scsi subsystem: improve dStrHex() signature, adjust invocations. Adjust scsi_format_id_string() signature. Add smartctl support for Pending Defects (sub-)log page; seems similar to 'smartctl -l defects' but that is ATA only. Needs to be generalized (as it will probably appear in NVMe also). 2018-01-04 Douglas Gilbert scsi subsystem: preparation for decoding more log pages. 2018-01-01 Alex Samorukov os_freebsd.cpp: fix build with CLANG/6. Patch provided by Dimitry Andric, PR 224826 2018-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2017-12-30 Douglas Gilbert scsi subsystem: add code to check for both log pages and subpages, subpages were not checked for previously. Add decoding for Format Status log page. Associated cleanups. Tighten checking for Seagate and Hitachi vendor specific log pages; '-T permissive' will relax checks back to the situation before this patch 2017-12-29 Douglas Gilbert unaligned.h: fix inconsistency in function argument of get_unaligned_be24() 2017-12-29 Douglas Gilbert Add --enable-scsi-cdb-check option to ./configure that results in a SCSI cdb sanity check prior to SCSI generic pass-through in Linux. [So it does not sanity check Megaraid and 3ware (etc) pass-throughs (but could).] When selected defines SCSI_CDB_CHECK in config.h . This may be temporary. This patch is an attempt to catch strange frames (perhaps SATA FIS) being sent to the SCSI pass-through. 2017-12-29 Douglas Gilbert Rework scsiGetSize() and remove scsiGetProtPBInfo(). Convert scsicmds.cpp to use unaligned.h get and put. 2017-12-27 Alex Samorukov Add unaligned.h header file to the Makefile.am 2017-12-27 Douglas Gilbert Add unaligned.h header file; has get and put variants of unaligned be16,24,32,48,64 and le16,24,32,48,64 copies plus all_zeros() and all_ffs() helpers. All inline. 2017-12-27 Douglas Gilbert Remove UINT8, INT8, UINT32 and INT32 typedefs in favour of the types from ; for example uint8_t 2017-12-27 Douglas Gilbert nvmecmds.cpp: according to NVMe 1.3a spec, the SMART/ health information log page is global and should take the global nsid (all ff_s). It also says the Error info lpage is "global. Broke WD Black PCIe (NVMe) SSD but worked on Intel SSDs. Fix; could break others. 2017-12-27 Douglas Gilbert os_freebsd.cpp: on error was setting set_nvme_err() to 1, not the actual NVMe status value; fix. 2017-12-24 Alex Samorukov CircleCI: add FreeBSD cross compilation 2017-12-22 Alex Samorukov configure.ac: add -lsbuf to FreeBSD libs to fix static builds. 2017-12-21 Douglas Gilbert scsiprint.cpp: Start some JSON work. Other cleanups and helper functions; potentially new header for those helpers. 2017-12-17 Christian Franke ataprint.cpp: Add JSON support for '-g all', '-l scterc' and '-l scttemp'. ataprint.cpp: Don't print obsolete SCT Support Level (#940). 2017-12-14 Christian Franke ataprint.cpp: JSON '-A' output: Add booleans and string for attribute flags. Remove string array. Rename table. ataprint.cpp: Add JSON support for '-l [x]error'. 2017-12-13 Christian Franke smartctl.cpp, smartctl.8.in: Rename '--json=a' to '--json=o'. smartctl.cpp: Show command line error messages in JSON output. ataprint.cpp: Add JSON support for '-l [x]selftest' and '-l directory'. atacmds.cpp, atacmds.h: Move self-test print functions to ... ataprint.cpp: ... here. smartd.cpp: Rework self-test error counting. ataprint.cpp: Add JSON support for '-c'. atacmds.cpp, atacmds.h: Change return type of capability checks to bool. Declare simple checks inline. 2017-12-07 Christian Franke json.cpp: Avoid 'cbegin()' and 'cend()' to fix build with older libstdc++. json.cpp, json.h, smartctl.cpp, smartctl.8.in: Add '--json=s' option. Outputs JSON object elements sorted by key. Add '--json=g' option. Outputs JSON structure suitable for grep. 2017-12-05 Christian Franke ataprint.cpp: Add JSON support for '-l sataphy'. smartctl.cpp: Add JSON support for '--scan'. Add similar device info to regular JSON output. ataprint.cpp, scsiprint.cpp: Remove now duplicate "protocol" element. smartctl.cpp: Silence false positive warnings from clang analyzer. 2017-12-02 Christian Franke Add initial support for smartctl JSON output mode (#766): json.cpp, json.h: New files with JSON support class. Makefile.am, os_win32/vc14/smartctl.vcxproj*: Add new files. ataprint.cpp: Add initial JSON support for -i, -H, -A and -l [x]error. scsiprint.cpp: Add initial JSON support for -i and -H. smartctl.cpp, smartctl.h: Add '-j, --json' option, global JSON object and new print functions. smartctl.8.in: Document new functionality. atacmds.cpp: Remove no longer needed variable 'must_swap'. os_win32.cpp: Remove include of smartctl.h, add extern declaration. Decode Windows Server 1709 build number. configure.ac, os_linux.cpp: Always include if available. This silences a 'deprecated' warning from glibc 2.25 headers. 2017-11-20 Alex Samorukov os_netbsd.cpp (fix regressions in smartmontools 6.6) - fix BE platforms support, tested on sparc64 (#943) - fix name based device type detection (#943) - Add byte-swapping for IDENTIFY command and remove related hacks from the atacmds.cpp (#117) 2017-11-18 Alex Samorukov drivedb.h: - Add Swissbit C440 industrial cf card series (#942) - Fix Innolite Satadom D150QV entry (#939) 2017-11-16 Christian Franke smartd.initd.in: Remove FreeBSD section. os_linux.cpp: Add missing braces to 3ware SELinux code. This possibly harmless bug was introduced ~10 years ago in r2510. It is now detected by g++ 6.3 -Wmisleading-indentation warning. update-smart-drivedb.in: Include configured PATH in help and error messages. Allow digits in SVN Id user name. configure.ac: Prepend '/usr/local/bin' to default for '--with-scriptpath' (#941). 2017-11-15 Christian Franke smartd.cpp: Use 'sigaction()' instead of deprecated 'sigset()' or 'signal()'. configure.ac: Add '--with-signal-func' to select old function if needed. configure.ac: Remove '-with-initscriptdir=[auto|yes]' heuristics. The default init script is no longer useful on most platforms. INSTALL: Update related documentation. configure.ac, Makefile.am: Use smartd.cygwin.initd.in on Cygwin. smartd.cygwin.initd.in: New file. smartd.initd.in: Remove Cygwin section. configure.ac: Make some header checks platform specific. Print '--with-nvme-devicescan' warning also on FreeBSD. Remove '--with-solaris-sparc-ata' warning. examplescripts/Example6: Add enhancements from Fedora package. 2017-11-13 Christian Franke drivedb.h: - Western Digital Red: WD80EZZX - USB: WD My Book (0x1058:0x25ee) (Red Hat Bugzilla 1446533) 2017-11-13 Matt Coates drivedb.h: USB: Seagate Backup Plus 4TB (0x0bc2:0xab43) (#926) 2017-11-10 Alex Samorukov drivedb.h: add SanDisk iSSD SDIS6BM (#923) 2017-11-08 Christian Franke Makefile.am, os_win32/installer.nsi: Add VERSIONINFO resource to installer. os_win32/installer.nsi: Remove get/set of old 'Install_Dir' registry entry. No longer needed for recent versions of GSmartControl. Remove deletion of old .exe.manifest files. Search also for 64-bit version of Notepad++. ataprint.cpp: Fix detection of Device Statistics log with 256 sectors (#922). os_linux.cpp: Use 'realpath()' (BSD, POSIX) instead of 'canonicalize_file_name()' (GNU extension). This fixes build on systems with musl libc (#921). 2017-11-06 Alex Samorukov os_freebsd.cpp: implement NVMe device scan (#687) os_freebsd.cpp: show hint if /dev/nvd* is specified as device name 2017-11-05 Christian Franke configure.ac: Add separate version number for drivedb.h branch. 2017-11-05 Christian Franke smartmontools 6.6 2017-11-04 Christian Franke drivedb.h: - Apple SD/SM/TS...E/F/G SSDs: Rename, add 1TB - Innodisk 3IE3/3ME3/3ME4 SSDs: Rename, add 3ME4 - Intel 730 and DC S35x0/3610/3700 Series SSDs: 150GB, *G7 (ticket #750) - USB: Toshiba Canvio (0x0480:0xa202, 0xa207) - USB: Seagate Expansion Desktop (0x0bc2:0x3330) - USB: Maxtor D3 Station 3TB (0x0bc2:0x6123) - USB: Seagate Backup Plus 4TB (0x0bc2:0xab1e) - USB: JMicron (0x152d:0x0579) - USB: Hitachi Touro Mobile (0x4971:0x1023) - USB: JMicron JMS566 (0xa152:0xb566) - USB: LogiLink PCCloneEX Lite (0xabcd:0x6104) smartd.conf.5.in: Fix conditionals of platform specific samples. smartctl.8.in, smartd.conf.5.in: Shorten or remove info about very old 3ware controllers. smartctl.8.in: Add '-g' to '-x' documentation. Avoid a very long line. smartctl.cpp: Improve help text formatting. 2017-11-03 Christian Franke update-smart-drivedb.8.in: Update mailing list link. update-smart-drivedb.in: Update mailing list comment. utility.cpp: Silence g++ 7.1 -Wformat-truncation warning. atacmds.cpp, dev_areca.cpp, os_linux.cpp: Add comments to silence g++ 7.1 -Wimplicit-fallthrough=[1-4] warnings. os_linux.cpp: Fix indentation (g++ 6.3: -Wmisleading-indentation). nvmeprint.cpp: Print IEEE EUI-64 of namespace. 2017-10-29 Christian Franke smartctl.8.in: Add notes about SMART commands obsoleted in ACS-4. Remove some outdated info. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Enable NVMe sections for Darwin. os_win32/installer.nsi: Update links. Remove outdated uninstall commands. INSTALL: Update ./configure description and OS info. ataidentify.cpp, ataprint.cpp: Minor ACS-4 additions. ataprint.cpp, ataprint.h, smartctl.cpp: Add option '-l defects' to print ATA ACS-4 Pending Defects log (ticket #909). smartctl.8.in: Document '-l defects'. 2017-10-25 Christian Franke drivedb.h: - Samsung based SSDs: PM871b (tickets #895, #903) - Seagate Enterprise Capacity 3.5 HDD: 4TB (fix for #913) - Western Digital Red Pro: 6TB (ticket #785) os_win32/smartd_warning.cmd: Add ability to run PowerShell scripts with '-M exec'. smartd.conf.5.in: Document new functionality. Fix typo. 2017-10-25 Alex Samorukov drivedb.h: - add SATA Voyager GTX (#893) 2017-10-24 Christian Franke do_release: Update code signing key id. update-smart-drivedb.in: Add new mailing list address to database signing key. 2017-10-24 Alex Samorukov drivedb.h: - add USB Voyager GTX (#893) - add Phison based OEM SSD based on the firmware name (#853, #831) - add Ultrastar 7K2 series (#892) - add LITEON ZETA (LMH-*V2M-*) (#794) 2017-10-22 Christian Franke os_win32.cpp: Decode Windows 10 1709 build number. configure.ac: Fail instead of warn if no compiler option to accept C++11 found and '--with-cxx11-option' is not specified. 2017-10-19 Alex Samorukov scsicmds.h: increase SCSI_TIMEOUT_DEFAULT to 1 minute to work on the big JBOD arrays (#917) 2017-10-15 Christian Franke smartd.cpp: Use also device identify information to detect for duplicate devices (ticket #313). atacmds.cpp: Don't pass possibly unaligned pointers to swapx(). This silences '-Waddress-of-packed-member' warning from clang++ 4.0 (ticket #915). 2017-10-12 Alex Samorukov os_linux.cpp: implemented support for the SG_IO V4 API. This should fix kernel warnings and other issues on the /dev/bsg SCSI devices. Based on the patch created by Circuitsoft (#782) 2017-10-11 Alex Samorukov os_darwin.cpp: fix crash on --scan (regression from r4549) 2017-10-10 Christian Franke configure.ac, os_darwin.cpp: Align Darwin NVMe device scanning with other platforms: Disable unless '--with-nvme-devicescan' or '-d nvme' is specified. Print related configure warning. 2017-10-09 Alex Samorukov drivedb.h: - Extend Seagate Barracuda 7200.12 regexp (#910) - Extend Seagate NAS HDD regexp (#778) - Extend Seagate Surveillance regexp (#807) - Extend Seagate Enterprise Capacity 3.5 HDD regexp (#864, #913) - Fix Seagate Barracuda 2.5 5400 regexp to add new models and avoid false matches (#796) - Add Seagate IronWolf HDD series (#760) - Fix attribute 183 for the Seagate Barracuda 2.5 5400 HDD (#816) - Added Mushkin Triactor series (#905) - Extend Samsung PM830 regexp (#897) 2017-10-08 Alex Samorukov drivedb.h: - Add TOSHIBA MQ03UBB... series (#901) - extend TOSHIBA THNSF regexp (#790) 2017-10-08 Christian Franke configure.ac: Check for compiler option to accept C++11. If none found, print warning and ask user to provide info. Add '--with-cxx11-option' to suppress this warning. This is intended to check whether C++11 could be used in some future smartmontools release. The current build is not affected. configure.ac, Makefile.am: Add Windows VERSIONINFO resource also to runcmd*.exe and wtssendmsg.exe. Include application manifests if needed. This also fixes manifests with older MinGW binutils which do not support more than one resource objects. os_win32/smart*_res.rc.in: Replace by os_win32/versioninfo.rc.in. 2017-10-06 Christian Franke Makefile.am: Add PDF man page formatting. os_win32/installer.nsi: Add PDF man pages, remove TXT man pages. drivedb.h: - SMART Modular Technologies mSATA XL+ SSDs (patch from ticket #802) - StorFly CFast SATA: Add missing space. - Fix regexp from tickets #882, #885. 2017-10-05 Alex Samorukov drivedb.h: add StorFly CFast SATA 6Gbps SSDs (#911) 2017-10-05 Christian Franke drivedb.h: - Western Digital Red: WD80EFAX (tickets #857, #899) - USB: Toshiba Canvio (0x0480:0xb207) - USB: Apple/TOSHIBA MQ01UBB200 (0x05ac:0x8406) - USB: Seagate Expansion Portable 2TB (0x0bc2:0x231a) - USB: Maxtor M3 Portable 4TB (0x0bc2:0x61b7) (ticket #875) - USB: WD Elements / My Passport (0x1058:0x259f) (ticket #833) - USB: WD Elements / My Passport (0x1058:0x25e2) - USB: WD Elements / My Passport (0x1058:0x25fa) (ticket #840) - USB: WD My Book / Easystore (0x1058:0x1230) (ticket #835) - USB: WD My Book / Easystore (0x1058:0x25fb) (tickets #857, #899) - USB: JMicron JMS561U (0x152d:0x8561) (ticket #860) - USB: Innostor IS888 (0x1f75:0x0888): -d sat works (ticket #827) 2017-10-04 Alex Samorukov smartctl.8.in: update information about NVMe in Darwin and OS/2 support smartd.cpp, utility.cpp: fix compiler warnings related to vprintf 2017-10-03 Christian Franke nvmeprint.cpp: Print new NVMe 1.3 feature flags. 2017-10-02 Christian Franke smartd.cpp: Add strict tests of /dev/null redirection and chdir("/"). configure.ac: Use '-fstack-protector' if '-strong' is not supported. 2017-10-01 Christian Franke configure.ac: Set default LDFLAGS for MinGW only if LDFLAGS is unset. Add '-Wformat=2 -fstack-protector-strong' if supported and CXXFLAGS is unset. drivedb.h: - Fix regexp from tickets #714, #721, #759, #789, #797, #798, #806, #824, #825, #866, #872, #880. - SK hynix SATA SSDs (based on patch from ticket #874) 2017-09-25 Alex Samorukov NVME: - extend controller and smart log page structures to match 1.3 specification. - Print thermal temperature transition statistic drivedb.h: - Added support for more LaCie and Freecom devices (patch from #891) 2017-09-24 Alex Samorukov drivedb.h: - Added Toshiba MK..34GSX series (#886) and MK..32GSX series (#887) - Added GOODRAM CX200 SSD (#838) - Added Mushkin SSD family (#797) - Added Samsung PM871 to the Samsung SSD family (#798) - Added PNY CS1311 family (#890) - Added 0x152d:0x0578 Jmicron USB->SATA - Added Transcend MTS800 drives (#787) - Added Transcend MSA 630 series (#759) - Extended Hitachi Deskstar 7K3000 regexp (#858) 2017-09-23 Alex Samorukov drivedb.h: - Fix HGST HDS724040ALE640 (#885) - Add Toshiba MQ03ABB300 (#884) - Fixed Hitachi 7K1000 (#883) - Added Seagate Barracuda 2.5 5400 series (#882) - Added new Seagate Barracuda 3.5 7200 series (#880) - Added Toshiba P300 series (#881) - Added SK hynix SSD SC300 series (#699) - Added Toshiba HG6 Series SSD (#721) - Added Hynix SSD series - Added AMD Radeon Solid State Drives (#762) - Added USB Bridge 0x3538:0x0064 (#855) - Added Seagate ST4000NM0085 to the Capacity family - Added Sandisk SATA Cloudspeed Max and GEN2 ESS SSDs and Sandisk SATA CS1K GEN1 ESS SSDs (#846) - Added Seagate FireCuda drives (#825) - Added Transcend MTS400 drives (#847) - Added Transcend MTS420 drives (#869) - Added Transcend SSD230 drives (#879) - Added Transcend SSD220S drives (#821) - Added Intel 540 Series SSDs (#803) - Added Intel 3710 Series SSDs (#824) - Added Micron 5100 ECO, PRO, and MAX Models (#861) - Added Samsung EVO SSD series - make regexp match less strict (#806) - Added Hitachi CinemaStar 5K1000 series (#758) - Added WDC WD4004FZWX disk to the Digital Black family (#765) - Added Samsung SSD 845DC EVO series (#866) - Added SK hynix SL308 family (#808) - Added WD Blue PC SSD family (#767) - Corrected Crucial M4 drivedb entry to include 32Gb model (#844) 2017-09-20 Alex Samorukov os_freebsd: use /dev/nvme/nvme.h on the recent versions os_darwin: - initial NVMe support for the darwin platform. - NVMe device scan support - Add device type autodetection 2017-08-08 Christian Franke ataprint.cpp: Fix ATA Security Level check. configure.ac: Detect MinGW libstdc++ problems with high '--image-base'. Update smartmontools-support mailing list address. Remove old mailing list address from all source files. 2017-05-03 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in: Rework vertical space and '.nf...fi' (no-fill) sections for better formatting with various tools (groff, mandoc, man2html) and output formats (text, pdf, html). Use default vertical space instead of an empty line between paragraphs. Use '.br' instead of '.nf...fi' where applicable. Use CW font in remaining no-fill sections. smartctl.8.in: Replace UTF-8 quotes. 2017-05-02 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in: Various man/groff syntax fixes (ticket #656): Split long lines. Insert two spaces or newline between sentences. Use ' for apostrophes. Use groff extension \(aq (apostrophe quote, ASCII 0x27) or ' for quotes. Use \- (minus sign) for options and examples. Use \(en (en-dash) for numeric ranges. Protect . with \& if not at end of sentence. 2017-04-24 Alex Samorukov os_freebsd.cpp: remove duplicated code which checks ATA SMART status (#746) 2017-04-23 Alex Samorukov os_os2.cpp: - code cleanup - add os2ahci driver initial support - fix selftest command - add device scan support 2017-04-19 Alex Samorukov OS/2 - many fixes: - autodetect and build os_os2.o on OS/2 - fix os_os2.cpp/os_os2.h compilation (thanks to franke@) - get rid from the os_os/hdreg.h - use constants from the atacmd.h - remove most of the dead code and unused functions 2017-04-17 Christian Franke os_win32.cpp: Decode Windows 10 1703 build number. atacmds.h, ataprint.cpp: Use STANDBY instead of IDLE command if '-s standby,[N|off]' and '-s standby,now' are both specified. smartctl.8.in: Document new behaviour of '-s standby,*'. 2017-04-01 Christian Franke atacmds.cpp, atacmds.h, ataprint.cpp: Print minimum supported ERC Time Limit from SCT Status. ataidentify.cpp, ataprint.cpp: Add ACS-4 and SATA 3.3 major versions, log pages, device statistic values and feature bits. 2017-03-27 Christian Franke scsiprint.cpp: Suppress "SAS address" if '-q noserial' is specified (ticket #822). scsicmds.cpp: Remove useless variable (cppcheck 1.77: knownConditionTrueFalse). smartd.cpp: Always suppress "failed to read Temperature" message if SCSI device does not support temperature (ticket #817). Fix initial check for SCSI temperature support. Log SCSI temperature regardless of its origin. 2017-03-11 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in: Update EXPERIMENTAL notes. Update links. Update or remove various outdated info. smartctl.8.in: Fix documentation of the '-g all' option. smartctl.cpp: Add '-g dsn' to '-x' output. 2017-03-11 Jonghwan Choi ataprint.cpp: Fix false positive DSN support detection. 2017-03-09 Jean Delvare <...> smartctl.8.in: Fix documentation of the '-q' option. 2017-03-09 Christian Franke AUTHORS: Add Jonghwan Choi. 2017-03-09 Jonghwan Choi Add options to get/set ATA DSN (Device Statistics Notification) feature (ticket #815): atacmds.h: Add DSN feature subcommand code. ataprint.cpp, ataprint.h, smartctl.cpp: Add '-g/s dsn' options. smartd.cpp: Add '-e dsn' directive. smartctl.8.in, smartd.conf.5.in: Document the new options. 2017-03-04 Christian Franke smartctl.cpp, smartd.cpp: Fix help text for '-B' option. smartd.cpp: Unify indent style, replace tabs. Move ATA/SCSI/NVMe device open to new common function. Suppress warning emails and repeated log messages on open error if '-d removable' is specified (Debian Bug 770872, Ubuntu Bug 1451572). smartd.conf.5.in: Document new behaviour of '-d removable'. 2017-03-02 Christian Franke smartd.cpp: Move single device registration to new function. Exit smartd on device open error unless '-q never' or '-d removable' is specified (regression from r2602). Prevent retry if registration failed and '-q never' is specified. Add enum for '-q, --quit' option. 2017-02-27 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300/P300: Rename, add P300, remove M500 - Crucial/Micron RealSSD m4/C400/P400: P400e micro SATA - Crucial/Micron MX1/2/300, M5/600, 1100 Client SSDs: Rename, add MX300 (tickets #763, #791), M550 M.2 (ticket #810), 1100 (ticket #783) 2017-02-22 Christian Franke configure.ac: Set various default LDFLAGS for MinGW builds: Link statically, indicate DEP and TS compatibility, enable ASLR. Add '--with-mingw-aslr' option. 2017-02-20 Christian Franke os_win32.cpp: Decode Windows Server 2016 build number. os_win32.cpp: Rework CSMI port mapping. This fixes access to ports != 0 behind IRST driver 15.2 (ticket #804). 2017-01-30 Alex Samorukov os_freebsd.cpp: unblock 48bit ATACAM commands for the legacy controllers if FreeBSD version is >= 9.2-RELEASE, tested on FreeBSD 10.3 2017-01-28 Christian Franke ataidentify.cpp: Don't shift negative values (g++ 6.3: -Wshift-negative-value, cppcheck 1.77: shiftNegativeLHS). os_win32.cpp, scsiata.cpp, scsicmds.cpp, scsiprint.cpp: Fix 'if' and 'else' clause indentations (g++ 6.3: -Wmisleading-indentation). Add indent style configuration for EditorConfig (http://editorconfig.org/): .editorconfig: New file. Makefile.am: Add new file to source tarball. 2017-01-21 Christian Franke drivedb.h: - Marvell based SanDisk SSDs: X300 OEM (ticket #747), X400 (ticket #715), Ultra II (ticket #744) - USB: Renesas uPD720231A (0x045b:0x0229) - USB: Maxtor D3 Station 5TB (0x0bc2:0x6126) - USB: Seagate Backup Plus 8TB (0x0bc2:0xab38) (ticket #786) - USB: WD Elements / My Passport (0x1058:0x107d) (ticket #772) - USB: WD Elements / My Passport (0x1058:0x25a1) (ticket #773) - USB: WD My Book 4TB (0x1058:0x25a3) (ticket #784) - USB: WD Elements / My Passport: Merge entries - USB: WD My Book: Merge entries 2017-01-14 Christian Franke scsiata.cpp: Remove redundant assignment (cppcheck: redundantAssignment). ataprint.cpp, ataprint.h, smartctl.cpp, smartctl.8.in: Add STATUS parameter to '-n POWERMODE' option (ticket #697). 2017-01-13 Christian Franke configure.ac: Rework CXXFLAGS settings, use shell intrinsics. os_win32.cpp: Fix harmless buffer overflow bug (found by VC14 code analyser). 2017-01-12 Christian Franke drivedb.h: - Innodisk 1ME3/3ME/3SE SSDs: Rename, add 1ME3 (ticket #713), 3SE - Innodisk 3IE2/3ME2/3MG2/3SE2 SSDs: Rename, add 3ME2 - Samsung based SSDs: 750 EVO, PM810(470), 840, PM830, PM851, CM871 (ticket #754), CM871a, PM871a (tickets #745, #775), SM951 (ticket #704) 2017-01-11 Christian Franke smartctl.8.in: Make '-d intelliprop' visible on all platforms. Add warning. smartd.conf.5.in: Document '-d intelliprop'. os_win32/vc14/smart*.vcxproj*: Add new files. AUTHORS: Add Casey Biemiller 2017-01-11 Casey Biemiller Add '-d intelliprop' device type for drives behind IntelliProp RAID controllers (ticket #730): atacmds.cpp, atacmds.h: Add function ataWriteLogExt(). dev_intelliprop.cpp, dev_intelliprop.h: New files. dev_interface.cpp: Add '-d intelliprop,N[+TYPE]' option. Makefile.am: Add new files. smartctl.8.in, smartd.conf.5.in: Document it. 2017-01-09 Alex Samorukov os_freebsd.cpp: fix panic on INVARIANTS enabled kernel, patch provided (#780) by Oliver Pinter 2017-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2016-11-12 Christian Franke atacmds.h, freebsd_nvme_ioctl.h: Apply patch-atacmds.h 1.1 and patch-freebsd_nvme_ioctl.h 1.1 (2016-11-04) from pkgsrc.se/sysutils/smartmontools: Build fix for FreeBSD-11 and newer. Don't redefine now existing things, ATA_SET_FEATURES and nvme_command. 2016-11-10 Christian Franke os_linux.cpp: Don't detect devices behind hpsa driver as regular SCSI devices. Suggest to use '-d cciss,N' instead. Based on patch provided by Stanislav Brabec. 2016-11-05 Christian Franke update-smart-drivedb.in: Fix 'mv' error on first update with new script. configure.ac, update-smart-drivedb.in: Add '--with-gnupg' option. configure.ac: Add '--with-update-smart-drivedb=X.Y' option to backport drive database update script and man page to older version X.Y. configure.ac: Remove checks for no longer supported options --disable-drivedb, --enable-savestates and --enable-attributelog. 2016-11-04 Christian Franke Add authentication to update-smart-drivedb (ticket #751): Create missing branches RELEASE_6_5_DRIVEDB and RELEASE_6_6_DRIVEDB. Add signature files drivedb.h.raw.asc to each maintained branch. update-smart-drivedb.in: Include new public key block ID DFD22559. Download also drivedb.h.raw.asc. Do no longer download from trunk if branch does not exist. Create drivedb.h.raw. Verify signature. Add options '--trunk', '--no-verify' and '--export-key'. update-smart-drivedb.8.in: Document new behaviour and options. 2016-10-23 Christian Franke smartd.8.in: Document Windows PARAMCHANGE service control command. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Enable NVMe sections for NetBSD. configure.ac, os_netbsd.cpp: Add --with-nvme-devicescan for NetBSD. drivedb.h: - Toshiba 3.5" MG04ACA... Enterprise HDD (ticket #732) - Toshiba X300 (ticket #716) - Seagate Laptop HDD: Rename, add 3/4TB (ticket #738) - Seagate Constellation ES: HP OEM - Western Digital RE4: *ABYZ variant - Western Digital Re: Add attribute 16 (ticket #742) - Western Digital Black: Remove *BEK[TX] variants - Western Digital Black Mobile: 1TB, *BEKT, *LPLX variants - Western Digital Elements / My Passport (USB, AF): 4TB - USB: Neodio Technologies (0x0aec:0x3050) - USB: Dura Micro (0x0c0b:0xb136) - USB: My Passport Ultra 4TB (0x1058:0x2599) 2016-10-17 Christian Franke configure.ac: Add --with-scriptpath option. smartd_warning.sh.in, update-smart-drivedb.in: Set PATH variable. 2016-10-03 Christian Franke os_win32/vc14/*.vcxproj: Add platform x64. os_win32.cpp: Use new enhanced version of IOCTL_STORAGE_QUERY_PROPERTY to access NVMe info. This works with Windows 10 NVMe driver (stornvme.sys) (ticket #691). smartctl.8.in, smartd.8.in: Document device names. 2016-09-28 Christian Franke drivedb.h: - USB: Buffalo MiniStation HD-PZU3 (0x0411:0x01f9) (ticket #739) - USB: Iomega Prestige (0x059b:0x0571) - USB: LaCie P9223 (0x059f:0x1070) - USB: Seagate Expansion Desktop (0x0bc2:0x331a) (ticket #725) - USB: Seagate Backup Plus (0x0bc2:0xab28) (ticket #738) - USB: WD My Passport Ultra (0x1058:0x259d) (ticket #736) - USB: ASMedia ASM1351 (0x174c:0x1351) 2016-09-25 Christian Franke AUTHORS: Add Kimihiro Nonaka. 2016-09-25 Kimihiro Nonaka <...> os_netbsd.cpp: Migrate to new dev_interface (ticket #101). Add NVMe support (ticket #728). Implement netbsd_ata_device::ata_pass_through(). netbsd_nvme_ioctl.h: New file based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources. Makefile.am: Add new file. 2016-09-07 Christian Franke Makefile.am: clean-vc14 targets. os_win32.cpp: Decode Windows 10 build number. os_win32/smartd_warning.cmd: Use delayed variable expansion. os_win32/smartd_mailer.ps1: Use domainname for default sender address. os_win32/smartd_mailer.conf.sample.ps1: Update related comment. os_win32/smartd_warning.cmd: Remove trailing '\r' from USERDNSDOMAIN. 2016-08-28 Christian Franke os_win32/installer.nsi: Fix quoting of EDITOR shortcuts. Send warning mails via PowerShell script on Windows (ticket #731): Makefile.am, os_win32/installer.nsi: Add new files. os_win32/smartd_mailer.ps1: New PowerShell script using Send-MailMessage cmdlet to send mail. os_win32/smartd_mailer.conf.sample.ps1: New sample config file. os_win32/smartd_warning.cmd: Call new script if configured. Improve error handling. Add setlocal. smartd.conf.5.in: Document it. 2016-08-17 Christian Franke AUTHORS: Add Song Liu. smartctl.cpp: Reduce scope of 'persistent' flag (cppcheck: variableScope). 2016-08-17 Song Liu ataprint.cpp, ataprint.h, smartctl.cpp, smartctl.8.in: Add persistent option ',p' to '-s wcreorder,on|off' (ticket #726). atacmds.cpp, atacmds.h, ataprint.cpp, ataprint.h, smartctl.cpp, smartctl.8.in: Add ability to control ATA drive write cache through SCT Feature control. The new smartctl options are '-s wcache-sct,ata|on|off[,p]' and '-g wcache-sct' (ticket #723). 2016-08-06 Christian Franke os_win32.cpp: Add Windows 10 build number to get_os_version_str(). Update MSVC10 (VS2010) for VC14 (VS2015): os_win32/vc14/*: Move from os_win32/vc10/*. os_win32/vc14/*.vcxproj: Update for VC14. Remove '__func__' workaround (revert r4225). Makefile.am: Rename and update config-vc14 target. utility.cpp: Add workaround for missing 'tzname'. drivedb.h: - OCZ/Toshiba Trion SSDs: Rename, add TOSHIBA-TR150 (ticket #722) - HGST Ultrastar 7K6000 (ticket #708) - HGST Ultrastar He10 - Seagate Desktop HDD.15: 6TB, 8TB - Seagate Enterprise Capacity 3.5 HDD: 8TB, 10TB (ticket #717), attribute 240 - Seagate SV35: 4TB - Western Digital Gold (ticket #711) - USB: LaCie (0x059f:0x1075) (ticket #718) - USB: Seagate Expansion External (0x0bc2:0x3322) (ticket #706) - USB: Seagate FreeAgent GoFlex (0x0bc2:0x5030) (ticket #720) - USB: Seagate Backup Plus Desktop (0x0bc2:0xab34) (ticket #700) 2016-05-31 Christian Franke drivedb.h: - Intel 311/313 Series SSDs: mSATA, *H (HP) variant - Intel 520 Series SSDs: *L (Lenovo) variant - HGST Ultrastar He6/He8: attribute 22 "Helium_Level" - Western Digital Red: 8TB, attribute 22 "Helium_Level" - USB: WD My Passport Ultra (0x1058:0x0837) (ticket #696) - USB: WD My Passport (0x1058:0x083a) - USB: WD My Book (0x1058:0x111d) 2016-05-10 Christian Franke os_openbsd.cpp: Compile fix (regression from r4156). os_netbsd.cpp: Apply patch-os__netbsd.cpp 1.3 (2016-05-08) from pkgsrc.se/sysutils/smartmontools: - Compile fix (regression from r4156). - Use a raw disk device file on NetBSD. 2016-05-07 Christian Franke smartmontools 6.5 2016-05-06 Christian Franke drivedb.h: - Samsung SpinPoint P80 SD: *J/P variant - Seagate Samsung SpinPoint M7E - Hitachi/HGST Travelstar Z5K500: *E680 variant - Hitachi Travelstar 7K500: HITACHI variant - Hitachi Ultrastar 7K3000: *A641 variant - HGST Ultrastar He8 - Toshiba 2.5" HDD MQ01ABD...: *V variant - Seagate Desktop HDD.15: 5TB - Seagate SV35.3 - Seagate SV35: *0001 variant - Seagate DB35: SATA variant - Western Digital Blue: 2-6TB, *Z variant - Western Digital RE4-GP: *2003* variant - Western Digital Re: Rename, 2-6TB - Western Digital Caviar Green: SATA 6Gb/s variant - Western Digital Caviar Black: *7501AAES* - Western Digital Blue Mobile: 2TB - Western Digital Elements / My Passport (USB, AF): *7500B*, 3TB 2016-05-01 Christian Franke drivedb.h: - Samsung based SSDs: 840 EVO 750GB (ticket #692), 850 EVO M.2, SM843T *HCFV* variant - USB: WD My Passport (0x1058:0x07ae) (ticket #686) - USB: JMicron JMS561 (0x152d:0x9561) nvmecmds.cpp: Enhance debug hex dump to sizeof Identify structs. Do not dump trailing zero bytes. 2016-04-27 Christian Franke nvmeprint.cpp, nvmeprint.h, smartctl.cpp, smartctl.8.in: Add NVMe support for 'smartctl -c'. Print various drive and namespace capabilites. Remove related info from '-i' output. 2016-04-24 Christian Franke nvmeprint.cpp: Fix formatting of error log with unset LBA fields. utility.cpp, utility.h: Skip leading blanks in format_char_array(). Some NVMe devices return right aligned text fields. configure.ac, smartd.cpp: Remove include of netdb.h. No longer needed since r3712. smartd.cpp, smartd.conf.5.in: Remove support for '-m [sys]msgbox'. 2016-04-23 Christian Franke drivedb.h: - Innodisk 3ME SSDs - Innodisk 3IE2/3MG2/3SE2-P SSDs: Rename, add 3SE2-P - Innodisk 3IE3/3ME3 SSDs: Rename, add 3IE3 - USB: Buffalo MiniStation HD-PNFU3 (0x0411:0x0251) (ticket #683) - USB: Renesas uPD720231A (0x045b:0x022a) - USB: Toshiba Canvio (0x0480:0x0210, 0x0480:0xa20c) - USB: Samsung G2 Portable (0x04e8:0x6032): 2nd entry with -d sat - USB: Iomega LDHD-UPS (0x059b:0x0278) - USB: Iomega LPHD-UP (0x059b:0x0470) - USB: LaCie Desktop Hard Drive (0x059f:0x1016) - USB: SanDisk SDCZ80 Flash Drive (0x0781:0x5588) - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xab2[05]) - USB: WD My Passport Ultra (0x1058:0x0822) - USB: WD Elements (0x1058:0x25a2) - USB: JMicron JMS561 (0x152d:0x1561) - USB: VIA VL711 (0x2109:0x0711): change to -d sat (ticket #594) - USB: Sharkoon QuickPort XT USB 3.0 (0x357d:0x7788) 2016-04-16 Christian Franke smartctl.cpp: Allow NVMe debug messages during --scan. Suppress "Device open changed type ..." message unless debug mode is enabled. atacmds.cpp: Remove duplicate POWER MODE error message. smartd.cpp: Remove dead increment (cppcheck: unreadVariable). Do not write localized decimal point to syslog(). configure.ac, Makefile.am: Add '--with-update-smart-drivedb=no' option to disable drive database update script. Useful if maintainers do not want the script due to security concerns and/or want to provide database updates as a separate package (Debian bug 804299, FreeBSD Bugzilla 208398). smartctl.8.in, smartd.8.in: Hide references to script if disabled. nvmeprint.cpp: Add Power State and Namespace info to '-i' output. Do not print unset or duplicate info unless debug mode is enabled. nvmecmds.cpp, nvmecmds.h: Add Identify Namespace support. 2016-04-15 Christian Franke os_linux.cpp: Fix harmless bug in errno check of HPTIO_CTL ioctl() calls. Bug was introduced 10 years ago in r2237. 2016-04-15 Yuriy M. Kaminskiy os_linux.cpp: Fix harmless bug in errno check of HDIO_DRIVE_TASK* ioctl() calls. Bug was introduced 12 years ago in r1609, the fix in r4003 was incomplete. 2016-04-14 Christian Franke nvmeprint.cpp: Fix size factor of Data Units Read/Written counters. os_win32.cpp: Fix device count in win_nvme_device::open(). Thanks to Oliver Bruchmann for bug reports and testing. 2016-04-12 Douglas Gilbert scsiprint.cpp: improve handling when no tape cartridge is in the tape drive. 2016-04-12 Alex Samorukov scsiprint.cpp, smartd.cpp: workaround for the buggy ST8000NM0075/E001, request log page list with a fixed length (ticket #678). 2016-04-11 Alex Samorukov drivedb.h: add Samsung SM863 series, ticket #681 2016-04-10 Christian Franke os_win32.cpp: Include also unknown and unsupported USB devices in device scan result. Move USB device handling to new function. Add Windows Server 2016 to get_os_version_str(). AUTHORS: Add Thomas Gatterweh. smartd.cpp: Check is_powered_down() also with '-n sleep'. 2016-04-10 Thomas Gatterweh Prevent drive spin up by '-n standby' check on Windows (ticket #677): dev_interface.cpp, dev_interface.h: Add smart_device::is_powered_down(). os_win32.cpp: Add win_ata_device::is_powered_down(). Open device without READ or WRITE access to prevent spin up. smartctl.cpp, smartd.cpp: Add check for is_powered_down(). 2016-04-09 Christian Franke configure.ac, os_win32.cpp, smartd.8.in: Add NVMe DEVICESCAN support for Windows. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Document NVMe support for Windows. nvmecmds.cpp, os_win32.cpp: Use NSID=0 for Identify Controller command. This fixes NVMe access via Samsung driver on Windows. 2016-04-08 Christian Franke os_win.cpp: Add initial NVMe support for Windows. Successfully tested with Intel driver. Does not work with Samsung driver. Thanks to Minkyu Kim for testing. 2016-04-02 Christian Franke Fix memory leak if get_sat_device() is called with unknown 'type': scsiata.cpp: get_sat_device(): Delete 'scsidev' on error. dev_interface.h: Update documentation of get_sat_device(). dev_interface.cpp: Fix use of get_sat_device(). (All other uses of get_sat_device() are already sane). dev_interface.cpp, dev_interface.h: Add counter for objects derived from 'smart_device'. smartctl.cpp, smartd.cpp: Print error message if any objects remain on exit. os_linux.cpp: linux_megaraid_device: Remove unused member variable 'm_busnum' (clang++: -Wunused-private-field) and the related ctor parameter. os_linux.cpp: Fixes suggested by clang analyser: Add or remove inconsistent nullptr checks. Remove dead increments. 2016-04-01 Douglas Gilbert scsiprint.cpp: add missing commas in peripheral_dt_arr and add number of elements (2**5) so that won't happen again. 2016-03-31 Alex Samorukov drivedb.h: - add samsung SAMSUNG-MZ7PC series (ticket #679) - add KINGSTON SKC400S37128G (SSDNow KC400) (ticket #673, patch provided by the reporter) - add SanDisk SSD Plus series (ticket #674) - add XceedIOPS SSD series (ticket #672) - add Crucial BX200 SSD (ticket #643) 2016-03-30 Christian Franke Add support for multiple '-d TYPE' options for device scanning: dev_interface.cpp, dev_interface.cpp: Add new version of scan_smart_devices() which accepts list of types. smartctl.cpp, smartd.cpp: Allow multiple '-d TYPE' options. Use new scan_smart_devices(). smartctl.8.in, smartd.conf.5.in: Document it. Makefile.am: Add man page support for --with-nvme-devicescan. smartd.8.in: Document NVMe DEVICESCAN for Linux. configure.ac: Use `...` instead of $(...) due to possible parsing problems since r4260. Remove workaround for related bash bug. 2016-03-28 Christian Franke Add NVMe DEVICESCAN support for Linux: configure.ac: Add --with-nvme-devicescan option. os_linux.cpp: Scan for '/dev/nvme[0-99]' if '-d nvme' is specified or --with-nvme-devicescan is set. smartctl.cpp: Add "NVMe" to --scan info. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Enable NVMe sections also for FreeBSD. configure.ac: Write configuration summary also to config.log. 2016-03-28 Alex Samorukov os_freebsd.cpp: Add initial FreeBSD NVMe support (ticket #657) 2016-03-27 Christian Franke ataprint.cpp: Support POWER MODE values introduced in ATA ACS-2 (ticket #184, smartctl only). 2016-03-27 Thomas Gatterweh atacmds.cpp, smartd.cpp: Support POWER MODE values introduced in ATA ACS-2 (ticket #184, smartd only). 2016-03-26 Christian Franke os_win32.cpp: Rearrange code such that no forward declarations are needed. os_freebsd.cpp, os_netbsd.cpp, os_openbsd.cpp, os_solaris.cpp, utility.cpp: Remove variable 'bytes'. Only used for a memory leak check which was removed in r2629 (2008-08-29). os_solaris.cpp, utility.cpp, utility.h: Remove CustomStrDup(), use strdup() instead. dev_legacy.cpp, utility.cpp, utility.h: Remove FreeNonZero(), use free() instead. smartctl.cpp, smartd.cpp, utility.cpp, utility.h: Remove split_report_arg(), use sscanf() instead. Add basic NVMe support for smartd (-H -l error -W): Makefile.am, os_win32/vc10/smartd.vcxproj: Add nvmecmds.cpp to smartd. smartd.cpp: Add NVMeDeviceScan() and NVMeCheckDevice(). smartd.8.in, smartd.conf.5.in: Document NVMe support. nvmeprint.cpp: Remove ary_to_str(). utility.cpp, utility.h: Add format_char_array(). 2016-03-24 Christian Franke dev_interface.cpp: Add missing 'usbprolific' to help text. nvmecmds.cpp, nvmeprint.cpp: Add support for '-q noserial'. smartd.cpp: Remove outdated declaration of getdomainname(). utility.cpp: Add C++ language version to output of -V option. 2016-03-20 Christian Franke nvmecmds.cpp, nvmecmds.h, nvmeprint.cpp, nvmeprint.h, smartctl.cpp: Add options '-l error[,NUM]' and '-l nvmelog,PAGE,SIZE' for NVMe devices. scsicmds.cpp: dStrHex(): Don't print trailing spaces. smartctl.8.in: Document '-l error[,NUM]', '-l nvmelog,PAGE,SIZE' and '-r nvmeioctl'. 2016-03-18 Christian Franke Add basic NVMe support for smartctl (-i -H -A) on Linux: Makefile.am: Add new files. dev_interface.cpp, dev_interface.h: Add class nvme_device. linux_nvme_ioctl.h: New file imported from Linux kernel sources (include/uapi/linux/nvme_ioctl.h 9d99a8d 2015-10-09). nvmecmds.cpp, nvmecmds.h: New module with NVMe command wrapper functions for smartctl and smartd. nvmeprint.cpp, nvmeprint.h: New module with nvmePrintMain(). smartctl.cpp: Add nvmePrintMain() support. os_linux.cpp: Add class linux_nvme_device. os_win32/vc10/smart*.vcxproj*: Add new files. smartctl.8.in: Document NVMe support. 2016-03-14 Douglas Gilbert scsiprint.cpp: work on LB provisioning corner cases; LBPRZ now 3 bits wide (in response to ticket #664) 2016-03-14 Alex Samorukov drivedb.h: - extend Apple SSD regexp (ticket #668) - Add OCZ VeloDrive R (ticket #667) 2016-03-12 Alex Samorukov drivedb.h: Add Phison Driven SSDs: - Kingston UV300 SSD series (ticket #663) - Kingston SSDNow KC310/V310 - HyperX Savage 2016-03-11 Alex Samorukov drivedb.h: Add Kingston UV300 SSD series 2016-03-06 Christian Franke drivedb.h: Samsung based SSDs: Fix PM863 regexp, attribute IDs and name length (regression from r4227). 2016-03-03 Alex Samorukov drivedb.h: Adata HD710 1TB USB3 (ticket #662) 2016-02-29 Alex Samorukov drivedb.h: PM863 Series (ticket #661) 2016-02-28 Alex Samorukov drivedb.h: OWC Aura Pro 480 GB (ticket #660) 2016-02-26 Christian Franke update-smart-drivedb.in: Use HTTPS for '-u sf' (ticket #659). Improve file modification check. update-smart-drivedb.8.in: Document changed URL. os_win32/vc10/smartctl.vcxproj: Workaround for missing support of '__func__' (included in C99 and C++11, but not in C++03). 2016-02-15 Alex Samorukov drivedb.h: APPLE SSD TS064E (ticket #655) 2016-02-02 Douglas Gilbert scsiprint.cpp: output unavailable rather than 255C for Drive Trip temperature; skip background scan lpage for tape drives 2016-02-02 Christian Franke drivedb.h: - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs: 250GB MX200 (ticket #644), M500 mSATA and M.2 - OCZ Trion SSDs: Rename, add Trion 150 - Innodisk 3ME3 SSDs: SATADOM-SL 3IE3 2016-01-25 Alex Samorukov os_darwin: add launchctl script for the smartd and remove depricated one. "On current systems there is only one recommend way: launchd" 2016-01-24 Alex Samorukov os_freebsd.cpp: fix possible reallocf with 0 bytes arg (ticket #640) drivedb.h: add Corsair Extreme SSD (ticket #642) os_darwin.cpp: fix error reporting if open fails 2016-01-23 Alex Samorukov os_darwin.cpp: do not print bogus memory allocation error message if there are no devices found 2016-01-22 Christian Franke Various fixes suggested by clang analyser (ticket #640): dev_areca.cpp: Fix check of ARCMSR_READ_RQBUFFER result. knowndrives.cpp: Add missing member initialization. smartd.cpp: Fix crash on missing argument to '-s' directive. Add missing variable initialization. Remove redundant assignment. 2016-01-21 Alex Samorukov drivedb.h: Added ADATA SP550 SSD (ticket #638) os_freebsd.cpp: Reduce variable scope where possible (cppcheck: variableScope) os_openbsd/os_netbsd - removed never used warning code defines (cppcheck) 2016-01-21 Christian Franke ataprint.cpp, smartd.cpp: Don't issue SCT commands if ATA Security is locked (ticket #637). 2016-01-19 Alex Samorukov drivedb.h: - Samsung PM871 SSD family (ticket #636) - Fixed detection for Samsung SSD 850 EVO mSATA 120GB (ticket #635) - Fixed Western Digital Caviar Black regexp, extended WD Black (ticket #631) 2016-01-06 Christian Franke drivedb.h: - SandForce Driven SSDs: Extra warning entry for buggy Corsair Force LS (ticket #628) - Innodisk 3MG2-P SSDs: 1.8" variant - Innodisk 3ME3 SSDs - USB: Seagate Expansion Portable (0x0bc2:0x2322) (ticket #627) - USB: Jess-Link (0x0dbf:0x9001) 2016-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2015-12-19 Christian Franke Makefile.am: Fix path of 'smart-pkg-uninstall' (Regression from r4190). update-smart-drivedb.8.in: Fix platform specific formatting. 2015-12-18 Alex Samorukov os_netbsd.cpp, os_openbsd.cpp: fix ioctl returtn value check os_darwin.cpp: fix error handling os_darwin: use /usr/local/ prefix to install on 10.11 (El Capitan) 2015-12-16 Douglas Gilbert scsiprint.cpp: stop tape drive looking for Solid State media log page (ticket #314). 2015-12-14 Douglas Gilbert scsiprint.cpp: fix compiler warning for is_tape. Clean code around handling of tape drives. 2015-12-14 Christian Franke drivedb.h: - Intel 320 Series SSDs: 1.8" microSATA - Intel 53x and Pro 2500 Series SSDs: Rename, add 535 (ticket #625), add Pro 2500 - Intel 730 and DC S35x0/3610/3700 Series SSDs: Rename, add S3510/3610, 1.2TB, 1.6TB - USB: LaCie (0x059f:0x106f) (ticket #624) - USB: WD My Passport (0x1058:0x071a, 0x1058:0x0816) - USB: Initio (0x13fd:0x1650) - USB: Unknown (0xabcd:0x6103) update-smart-drivedb.in: Add '-s SMARTCTL' option. update-smart-drivedb.8.in: Document it. 2015-12-07 Christian Franke configure.ac: Append 'svn' to list of download tools. update-smart-drivedb.in: Use HTTPS download by default. Add options '-t TOOL', '-u LOCATION', '--cacert FILE', '--capath DIR', '--insecure' and '--dryrun'. Add 'svn' as new download tool. Ignore differences in SVN Id string (re-added). Remove usage of 'which' command. update-smart-drivedb.8.in: Document the new options. 2015-11-23 Christian Franke atacmds.cpp: parse_attribute_def(): Init buffers before sscanf() call (cppcheck-1.71: uninitvar). scsiprint.cpp: Fix GLTSD bit set/cleared info messages (ticket #621). 2015-11-22 Christian Franke Makefile.am: Add NEWS file to svnversion.h target. os_win32/installer.nsi: Select 64-bit version on 64-bit Windows. Fix installation of runcmda.exe. Update links. 2015-11-15 Christian Franke configure.ac: Check whether MinGW adds an application manifest. Makefile.am: Add default manifest for MinGW builds. os_win32/default.manifest: New default application manifest. Remove external application manifests. os_win32/installer.nsi: Use macros from 'LogicLib.nsh' where possible. Add missing MessageBox /SD options. Remove external application manifests. 2015-11-07 Christian Franke drivedb.h: - Micron M500DC/M510DC Enterprise SSDs: Rename, add M510DC - SandForce Driven SSDs: Mushkin Chronos 7mm/MX/G2, Enhanced ECO2 - Innodisk 3MG2-P SSDs - SiliconMotion based SSDs: Crucial BX100 (ticket #597) 2015-10-31 Christian Franke atacmds.cpp, atacmds.h, knowndrives.cpp, knowndrives.h: Read default SMART attribute settings from drivedb.h (ticket #465). Remove hard-coded attribute names and format settings. drivedb.h: Uncomment default settings to create the "DEFAULT" entry. Add ",HDD" or ",SSD" to HDD/SSD specific settings. smartctl.cpp, smartd.cpp: Use new database initialization function. Create branch RELEASE_6_4_DRIVEDB with last drivedb.h file compatible with smartmontools 6.4. 2015-10-22 Paul Grabinar drivedb.h: - SandForce Driven SSDs: OCZ RevoDrive 350, Z-Drive 4500 - Indilinx Barefoot 3 based SSDs: Add attributes, OCZ ARC 100, Saber 1000, Vector 180, Vertex 460A - OCZ Intrepid 3000 SSDs: Intrepid 3700 - OCZ Trion 2015-10-20 Christian Franke Reduce variable scope where possible (cppcheck: variableScope). Makefile.am: Remove *.s from files used to generate svnversion.h. 2015-10-18 Alex Samorukov fixes suggested by cppcheck: Check realloc result to avoid memory leak (memleakOnRealloc) Fix printf() signednsess (invalidPrintfArgType_sint) 2015-10-17 Christian Franke Various fixes suggested by cppcheck: Close FILE pointer before reopening it (cppcheck: publicAllocationError). Add missing member initializations to ctors (cppcheck: uninitMemberVar). Remove redundant nullptr check (cppcheck: nullPointerRedundantCheck). Remove redundant assignments (cppcheck: redundantAssignment). Clarify calculation precedence (cppcheck: clarifyCalculation). Use C++-style casts for pointer types (cppcheck: cstyleCast). Remove duplicate on both sides of '||' (cppcheck: duplicateExpression). Declare ctors with one argument as 'explicit' (cppcheck: noExplicitConstructor). Remove unread variables and assignments (cppcheck: unreadVariable). Fix signedness of sscanf() formats strings (cppcheck: invalidScanfArgType_int). 2015-10-14 Christian Franke configure.ac: Disable os_solaris_ata.o by default. Add --with-solaris-sparc-ata option to enable. Makefile.am: Exclude os_solaris_ata.s from source tarball (Debian bug 729842). os_solaris.cpp: Check for WITH_SOLARIS_SPARC_ATA instead of __sparc. 2015-10-13 Christian Franke Makefile.am: Fix error handling in various shell scripts. 2015-10-13 Casper Dik <...> os_solaris.cpp: Detect SATA devices as SCSI devices. This adds support for auto detection of SATA devices behind SAT layer. Set USCSI_SILENT flag to suppress /dev/console messages on command error. 2015-10-11 Christian Franke drivedb.h: SiliconMotion based SSDs: Transcend SSD370S, SSD420, update attribute 245 (ticket #595, ticket #602). 2015-10-10 Christian Franke Makefile.am: Use MKDIR_P to create directories (available since automake 1.10). os_win32.cpp: Detect USB ID if WMI reports type name "SCSI" instead of "USBSTOR". Detect USB ID also if drive letter is specified as device name. 2015-10-04 Christian Franke drivedb.h: - USB: Genesys Logic (0x05e3:0x0735) - USB: Addonics (0x0bf6:0x1001): unsupported (ticket #609) - USB: Initio (0x13fd:0x3920) - USB: JMicron JMS539 (0x152d:0x0539, 0x0100): Set from -d usbjmicron to unsupported because some devices may require -d sat instead (ticket #552). - USB: JMicron (0x152d:0x0565) (ticket #607) - USB: VIA VL711 (0x2109:0x0711): unsupported (ticket #594) - USB: Hitachi Touro Mobile (0x4971:0x1024) 2015-09-25 Christian Franke scsiata.cpp: Ignore SAT ATA PASS-THROUGH fixed format sense data if no ATA status bit is set (ticket #612). 2015-09-23 Alex Samorukov drivedb.h: Innostor USB3.0 to SATAIII bridge (#611) 2015-09-21 Alex Samorukov drivedb.h: decode 188 attribute for the "Seagate Enterprise Capacity 3.5 HDD" drives family, (see #551). 2015-09-04 Alex Samorukov Makefile.am: integrate darwin dmg build process to the Makefile 2015-09-03 Alex Samorukov os_darwin: Initial import of the files required to build OSX/smartmontools native package (see #555). 2015-08-27 Alex Samorukov Homepage URL updated from the sourceforge to smartmontools.org (r4120) 2015-08-26 Alex Samorukov os_darwin.cpp: Implement get_os_version_str() for the darwin. 2015-08-17 Christian Franke scsiata.cpp: Ignore bogus SCSI sense_key if ATA status in SAT ATA Return Descriptor indicates success (ticket #548). 2015-08-08 Christian Franke os_win32.cpp: Fix get_os_version_str() for Windows >= 8.1. Add Windows 10 Final. 2015-08-02 Christian Franke configure.ac: Remove '--disable-drivedb', '--enable-savestates', '--enable-attributelog'. Print error message if used. 2015-07-15 Christian Franke autogen.sh: Drop support for automake 1.7 - 1.9.x. Rework search for automake-VERSION. configure.ac: Drop support for autoconf 2.5x. Drop support for automake 1.7 - 1.9.x. Remove --with-docdir option. 2015-06-24 Alex Samorukov drivedb.h: - USB: SimpleTech 3.0 bridge (0x4971:0x8017), reported in #554 2015-06-04 Christian Franke smartmontools 6.4 2015-06-03 Christian Franke drivedb.h: - InnoDisk iCF 9000 CompactFlash Cards - SanDisk based SSDs: ReadyCache SSD - Seagate Barracuda 7200.14 (AF): Apple OEM - USB: Toshiba Canvio Basics (0x0480:0xa200) ataprint.cpp: Read General Purpose Log Directory only if GPL feature set is supported. Improve support check of old logs for older drives which return empty SMART Log Directory. 2015-06-01 Christian Franke Makefile.am, smartd.8.in: Hide initscript documentation if initscriptdir is not configured. smartd.conf.5.in: Remove outdated info about default shell. 2015-05-30 Christian Franke Fixes for aacraid patch: aacraid.h: Fix _WIN32/_WIN64 checks. os_win32.cpp: Clarify copyright info in GPL header. Improve source code formatting. Fix build on Cygwin. Fix HKEY leak. Fix member initialization order. Fix info_name and dev_type parameter order. Improve error handling. Avoid unsafe sprintf(). Remove unused variables. Add help text. Use 0 as number of first aacraid controller as on Linux. smartctl.8.in, smartd.conf.5.in: Update '-d aacraid' documentation. AUTHORS: Add Nidhi Malhotra. 2015-05-30 Nidhi Malhotra aacraid.h, os_win32.cpp: Add aacraid support for Windows (ticket #496). 2015-05-27 Christian Franke INSTALL: Update ./configure description. Remove info about old Linux kernel series. Update Windows info. 2015-05-19 Christian Franke ataprint.cpp: Print the Additional Product Identifier (OEM Id) regardless of '-q noserial' option. smartctl.8.in, smartd.conf.5.in: Clarify '-H' option and directive. 2015-05-17 Christian Franke drivedb.h: - USB: ViPowER USB3.0 Storage (0x0350:0x0038) - USB: Buffalo DriveStation HD-LBU2 (0x0411:0x01ea) - USB: Toshiba Stor.E Basics; (0x0480:0xa00e) - USB: Toshiba Canvio Desktop (0x0480:0xd011) - USB: Samsung M3 Portable USB 3.0 (0x04e8:0x61b3) - USB: Iomega (0x059b:0x0575) - USB: Genesys Logic GL3310 (0x05e3:0x0731) - USB: Freecom HD (0x07ab:0xfcd6) - USB: Apricorn SATA Wire (0x0984:0x0040) - USB: WD My Passport (0x1058:0x0830) - USB: WD My Book: Merge entries, add 0x1058:0x0900, 0x1058:0x1104 - USB: Initio (0x13fd:0x3940) - USB: Super Top (0x14cd:0x6116): change to -d sat - USB: JMicron (0x152d:0x2590) (ticket #550) - USB: ASMedia ASM1053/1153 (0x174c:0x1[01]53) - USB: Verbatim Pocket Hard Drive (0x18a5:0x0237) - USB: Verbatim External Hard Drive (0x18a5:0x0400) - USB: VIA VL701 (0x2109:0x0701) - USB: Unknown (0x2537:0x106[68]) - USB: Hitachi Touro Mobile (0x4971:0x1020) 2015-05-16 Christian Franke drivedb.h: - Samsung SpinPoint T166: 250GB - Seagate Samsung SpinPoint M8 (AF): Rename, add Apple OEM - Seagate Samsung SpinPoint M9T - Seagate Samsung SpinPoint M9TU (USB) - Hitachi/HGST Travelstar Z5K320 - HGST Travelstar Z5K1000 - HGST Deskstar NAS: 128MB cache variants - HGST Ultrastar He6 - Toshiba 2.5" HDD MK..51GSY - Toshiba 2.5" HDD MK..61GSY[N]: -v 9,minutes - Toshiba 2.5" HDD MK..61GSYB - Toshiba 2.5" HDD MK..75GSX - Toshiba 2.5" HDD MQ01ABB... - Toshiba 2.5" HDD MQ01ABC... - Toshiba 2.5" HDD MQ01ABF... - Toshiba 2.5" HDD MQ01UBB... (USB 3.0) - Toshiba 3.5" MD04ACA... Enterprise HDD - Toshiba 3.5" DT01ABA... Desktop HDD - Seagate Laptop Thin HDD: 7200 rpm variants - Seagate Constellation ES.2 (SATA 6Gb/s): HP OEM - Seagate Constellation.2 (SATA): HP OEM - Seagate Enterprise Capacity 3.5 HDD - Seagate Archive HDD - Western Digital AV-GP (AF): 500MB, EURX variants - Western Digital Red Pro - Western Digital Purple 2015-05-14 Christian Franke drivedb.h: - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs: MX200 *00 sizes (ticket #545) - Samsung based SSDs: PM851, SM841N, 850 EVO - Marvell based SanDisk SSDs: Extreme Pro, Ultra II (ticket #544) - Marvell based SanDisk SSDs: X110 mSATA, X300 - SanDisk based SSDs: pSSD (USB), U110 - USB: Samsung D3 Station 4TB (0x04e8:0x6125) (ticket #549) - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xa003) - USB: Seagate Backup Plus Desktop USB 3.0 5TB (0x0bc2:0xab31) - USB: JMicron (0x152d:0x3569) (ticket #546) 2015-05-10 Christian Franke scsicmds.cpp, scsicmds.h: Remove unused functions scsiReceiveDiagnostic() and scsiSmartIBMOfflineTest(). Found by cppcheck. 2015-05-05 Christian Franke ataprint.cpp: Print ACS-3 device statistics DSN flags. Print device statistics page numbers in hex. smartctl.cpp: Allow hex argument for '-l devstat,PAGE'. 2015-05-02 Christian Franke ataprint.cpp: Print Transport Type for PATA and PCIe. Print diagnostic values if SATA version or speed is unknown. smartctl.8.in, smartd.8.in: Add Volker Kuhlmann to AUTHORS section. 2015-05-01 Christian Franke ataidentify.cpp: ACS-3/4 updates. ataprint.cpp: Add recent ACS-3/4 minor revisions. Add ACS-4 log 0x0f. Add ACS-4 device statistics values and vendor specific statistics page. 2015-04-28 Christian Franke os_win32/installer.nsi: Fix possible loss of user PATH environment variable with length greater than NSIS max string length. 2015-04-26 Christian Franke do_release: New Signing Key. Makefile.am: Use make variables instead of autoconf variables if possible. 2015-04-24 Christian Franke smartctl.8.in, smartd.8.in: Rework AUTHORS section. INSTALL, Makefile.am, os_win32/installer.nsi: Remove WARNINGS file. WARNINGS: Remove this file. 2015-04-23 Christian Franke configure.ac: Add '--with-systemdenvfile=auto' option as new default. Remove no longer needed ENABLE_CAPABILITIES conditional. Makefile.am: Silence build of smartd.service file. Integrate all ENABLE_* conditionals in MAN_FILTER script. 2015-04-21 Christian Franke configure.ac: Print 'deprecated' warning for '--disable-drivedb', '--enable-savestates', '--enable-attributelog' options. Add 'yes|no' support to corresponding '--with-...' options. 2015-04-19 Christian Franke AUTHORS: Remove smartmontools-support list address. Remove defunct mail addresses. Update smartsuite info. Add recent contributors. README: Refer to AUTHORS. 2015-04-18 Christian Franke os_win32.cpp: Add SAT autodetection based on vendor string from IOCTL_STORAGE_QUERY_PROPERTY. smartd.cpp: If SMART ENABLE command failed, continue if SMART is already enabled. 2015-04-17 Christian Franke os_win32.cpp: Detect SAT layer of certain Intel AHCI drivers. 2015-04-15 Christian Franke smartctl.8.in, smartd.8.in, update-smart-drivedb.8.in: Add REPORTING BUGS section. smartctl.8.in, smartd.8.in: Rename RETURN VALUE section to EXIT STATUS. smartd.8.in: Remove no longer used exit status 9. 2015-04-14 Christian Franke autogen.sh: automake 1.15 works. Print 'deprecated' warning if automake < 1.10 is used. 2015-04-08 Christian Franke configure.ac: Print 'deprecated' warning if autoconf 2.5x or --with-docdir option is used. Add comments to fix vim syntax coloring. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Remove EXPERIMENTAL notes for features added before 6.3. 2015-03-29 Christian Franke ataprint.cpp: Read only required log pages of Extended Comprehensive Error log. This adds support for logs with many pages (ticket #498). atacmds.cpp, atacmds.h, smartd.cpp: Add 'page' parameter to function ataReadExtErrorLog(). 2015-03-22 Christian Franke os_linux.cpp, smartctl.8.in, smartd.8.in, smartd.conf, smartd.conf.5.in, smartd.cpp: Remove old Linux IDE device names (/dev/hdX) in man pages and help texts. 2015-03-21 Christian Franke smartd.8.in, smartd.cpp: Clarify smartd '--capabilities' option (ticket #523). 2015-03-20 Christian Franke drivedb.h: - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs: Rename, add MX200 - Sandforce Driven SSDs: ATP Velocity MIV, Mushkin Chronos Enhanced - Indilinx Barefoot 3 based SSDs: OCZ VERTEX 460, OCZ AMD Radeon R7 - Intel 530 Series SSDs: mSATA variant - JMicron based SSDs: ADATA SP310 - Plextor M3/M5/M6 Series SSDs: Rename, add M6M, M6S 2015-03-13 Douglas Gilbert scsiata.cpp - SCSI to ATA translation: from SAT-2 and later a SAT layer may return ATA registers via fixed format sense data. Change to additionally accept (partial) fixed format sense. In response to ticket #296 and FreeBSD Bug 191717. 2015-03-10 Douglas Gilbert scsicmds.cpp, scsiprint.cpp - SCSI: when READ DEFECT yields sense of "... defect list not found" bypass the corresponding report quietly. (ticket #343) 2015-02-08 Christian Franke drivedb.h: - USB: Buffalo Drivestation Duo (0x0411:0x01ce) - USB: Toshiba Canvio Basics (0x0480:0x0201, 0xa00d) - USB: Toshiba Stor.E Basics (0x0480:0xa00c) - USB: Toshiba Canvio ALU (0x0480:0xa100) - USB: Toshiba Canvio Desktop (0x0480:0xd000) - USB: Samsung S2 Portable (0x04e8:0x1f0a) - USB: Samsung S3 Portable (0x04e8:0x61c8) - USB: LaCie Rugged Triple Interface (0x059f:0x100c) - USB: Initio (0x13fd:0x3910) - USB: ASMedia (0x174c:0x5516) - USB: Innostor IS611 (0x1f75:0x0611) 2015-02-02 Christian Franke drivedb.h: - USB: Seagate FreeAgent XTreme (0x0bc2:0x3101) - USB: Seagate Expansion Portable (0x0bc2:0x232[01]) - USB: Seagate Expansion External (0x0bc2:0x3321) - USB: Seagate FreeAgent GoFlex (0x0bc2:0x5070, 0x50a7, 0x6121) - USB: Seagate Slim Portable Drive (0x0bc2:0xab00) (ticket #517) - USB: Seagate Backup Plus Slim (0x0bc2:0xab21) - USB: ADATA HD650 (0x125f:0xa35a) - USB: JMicron JMS567 (0x152d:0x3562) (ticket #508) - USB: Innostor IS621 (0x1f75:0x0621) (ticket #517) 2015-01-25 Christian Franke drivedb.h: - JMicron based SSDs: Transcend SSD340 (ticket #348) - SiliconMotion based SSDs: Transcend SSD370 (ticket #468) 2015-01-24 Christian Franke os_win32.cpp: Add Windows 10 to get_os_version_str(). 2015-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2014-12-13 Christian Franke drivedb.h: - USB: SanDisk SDCZ80 Flash Drive (0x0781:0x5580) - USB: WD My Passport: Merge entries, add 0x1058:0x0810 - USB: WD Elements Desktop: Merge entries, add 0x1058:0x107c - USB: WD Elements: Merge entries - USB: JMicron JMS539 (0x152d:0x0539): 2.06 and 28.03 support SAT (ticket #504) - USB: JMicron JMS567 (0x152d:0x0567) (ticket #504) - USB: JMicron JMS566 (0x152d:0x2566) - USB: Hitachi Touro (0x4971:0x1014) 2014-12-13 Christian Franke utility.cpp, utility.h: Remove unused functions Calloc() and CheckFree(). 2014-12-10 Christian Franke drivedb.h: - Western Digital Blue: Rename, *AZLX variant - Western Digital RE4: *FBYZ variant - Western Digital Green: Rename, add 5TB, 6TB - Western Digital AV: Rename, add 1TB, *BUCT variant - Western Digital Red: Rename, add 750GB, 5TB, 6TB - Western Digital Black Mobile 2014-12-08 Christian Franke drivedb.h: - Hitachi Travelstar 5K500.B: *SA00 variant - Hitachi/HGST Travelstar Z5K500: Hitachi variant, Apple OEM - HGST Travelstar 5K1000 - HGST Travelstar 5K1500 - Hitachi Travelstar 7K500: *A360 variant - Hitachi CinemaStar 5K320 - Hitachi Deskstar 7K1000.C: SATA 6Gb/s variants - HGST Deskstar NAS - Hitachi/HGST Ultrastar 7K4000: Rename, add HGST - HGST MegaScale 4000 2014-12-07 Christian Franke os_linux.cpp: Fix fd leak in megasas_dcmd_cmd(). Found by cppcheck. 2014-12-07 Christian Franke drivedb.h: - Crucial/Micron MX100/M500/M510/M550/M600 Client SSDs: M600 EE variant - SandForce Driven SSDs: Kingston KC300 180GB - Indilinx Barefoot 3 based SSDs: OCZ Vector 150 - JMicron based SSDs: Kingston SSDNow V+ - Plextor M3/M5 (Pro) Series SSDs: M5P - Samsung based SSDs: 850 PRO, SM853T Series 2014-12-06 Christian Franke Makefile.am: Add quotes to parameters of INSTALL commands to allow path names with spaces (this is supported since automake 1.8). update-smart-drivedb.in: Add quotes to SMARTCTL variable (ticket #502). 2014-11-30 Christian Franke drivedb.h: - Crucial/Micron RealSSD m4/C400/P400: C400 *MAM variant - Crucial/Micron MX100/M500/M510/M550/M600 Client SSDs: Rename, add Crucial M500/M550, Micron M600 - SandForce Driven SSDs: ADATA SX900 (ticket #490), Mushkin Atlas - Intel 311/313 Series SSDs: Rename, add 311 Series 2014-11-30 Christian Franke drivedb.h: USB: Prolific PL2571, PL2771, PL2775 (0x067b:0x2.7.) (ticket #499). smartctl.8.in, smartd.conf.5.in: Update '-d usbprolific' documentation. 2014-11-29 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in: Add package title to page header. Move PACKAGE VERSION section to bottom of page. Remove SVN ID section header. 2014-11-29 Tommy Vestermark scsiata.cpp: Add DATA OUT support for Prolific (ticket #482). Add more ATA output registers. SCT commands are now supported. 2014-11-29 Christian Franke os_win32.cpp: Add strnicmp() compatibility macro for newer Cygwin releases. 2014-11-16 Tommy Vestermark drivedb.h: USB: Prolific PL2773 (0x067b:0x2773) (ticket #482). 2014-11-16 Christian Franke Create branches RELEASE_6_[1-3]_DRIVEDB with last drivedb.h file compatible with smartmontools 6.[1-3]. 2014-11-10 Tommy Vestermark scsiata.cpp: Add class usbprolific_device to support Prolific PL2773 USB bridges (ticket #482). smartctl.8.in, smartd.conf.5.in: Document '-d usbprolific'. 2014-11-09 Roger Willcocks os_linux.cpp: linux_aacraid_device: Fix ioctl data count if dxfer_len == 0. Return scsi sense data. Together these allow the SMART STATUS command to operate correctly. Improve SRB status checks. linux_ata_device: Fix very old bug in the error handling of HDIO_DRIVE_TASKFILE. 2014-10-07 Alex Samorukov drivedb.h: Added more attributes for SanDisk based SSDs based on SSD Dashboard tool data (#463) 2014-10-06 Christian Franke ataprint.cpp: Add form factors from ACS-4. Add ACS-2 and ACS-3 minor versions. Update SATA log names. Add SATA 3.2. Avoid crash on device statistics page 0xff if SMART READ LOG is used. Print vendor specific bytes from SCT Status. atacmds.cpp, atacmds.h, ataprint.cpp: Print SMART STATUS info from SCT Status. 2014-10-06 Alex Samorukov drivedb.h: Exteneded regexp for SanDisk X300s (#463) 2014-09-29 Alex Samorukov drivedb.h: Added Seagate Backup Plus Slim Portable USB 3.0 drive 2014-08-29 Christian Franke drivedb.h: Fix regex syntax error (regression from r3988). 2014-08-22 Alex Samorukov drivedb.h: - fixed SanDisk X210 regular expression 2014-08-21 Alex Samorukov drivedb.h: - added SanDisk X300s SSD - extended Apacer SSD support based on APSDM004G13AN-AT user report 2014-08-16 Alex Samorukov ataprint.cpp: '-l devstat' - workaround for buggy firmware by provided Christian Franke 2014-08-15 Alex Samorukov ataprint.cpp: device statistic - use smart log if GP log is not available 2014-08-15 Alex Samorukov os_darwin.cpp: - Migrated to the new interface - Added multisector support - Fixed smart autosave processing 2014-07-26 Christian Franke smartmontools 6.3 2014-07-25 Christian Franke drivedb.h: - Apple SD/SM/TS...E/F SSDs: Rename, add TS*[EF] - JMicron based SSDs: Fix regex for Apple TS*C - Marvell based SanDisk SSDs: X210 2014-07-25 Alex Samorukov drivedb.h: Apple SM* SSD - add attribute 173 description (guessed) 2014-07-23 Christian Franke ataprint.cpp: Print SCT Status regardless of SCT Data Table support. atacmds.cpp: ataReadSCTTempHist(): Do not reread initial SCT Status. configure.ac: Fix typo in help text. Add MinGW comment. 2014-07-22 Christian Franke drivedb.h: - Apple SD/SM...E/F SSDs (ticket #342) - Apple SSD SM128, Asus-Phison SSD: Remove (missing attribute info) 2014-07-20 Christian Franke atacmds.cpp: Rework heuristics for 'tempminmax' format. Now supports negative values (ticket #291) and WDC over temperature counter. Change default for Head_Flying_Hours to 'raw24(raw8)'. This provides more reasonable output for Seagate HDDs missing in drivedb.h. drivedb.h: Comment new default for Head_Flying_Hours. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Fix usage of line breaks and empty lines. 2014-07-19 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in: Add FILES section. Move FULL PATH info to FILES section. Rename REFERENCES section. Move HOME PAGE info to REFERENCES section. Remove AUTHORS section from smartd.conf man page. Update or remove various outdated info. 2014-07-18 Christian Franke configure.ac: Use 'email' instead of 'mail' on Cygwin. Remove outdated '-mno-cygwin' error check. Makefile.am, smartd.conf.5.in: Replace 'mail' by actual platform specific mailer. examplescripts/README, examplescripts/Example[123]: Remove bashisms. Use '/usr/bin/mail' instead of '/bin/mail'. os_win32/daemon_win32.cpp: Support older MinGW headers with missing struct SERVICE_DELAYED_AUTO_START_INFO. 2014-07-17 Christian Franke drivedb.h: - Crucial/Micron MX100/M500/M510/M550 Client SSDs: Rename, add MX100, update MX510/550 - Indilinx Barefoot based SSDs: OCZ Vertex 1.10 - Intel 320 Series SSDs: 'L' variant - JMicron based SSDs: Transcend *18M-M variant - Plextor M3/M5 (Pro) Series SSDs: M5M (mSATA) variant - Samsung based SSDs: 840 EVO 2014-07-16 Christian Franke drivedb.h: - Marvell based SanDisk SSDs: Extreme II (ticket #334), others - SanDisk based SSDs: iSSD P4 (ticket #272), U100 (ticket #337), others - USB: Iomega (0x059b:0x047a) - USB: WD My Passport: Merge entries - USB: WD My Passport USB 3.0 (0x1058:0x074a, 0x1058:0x0820) - USB: ADATA (0x125f:0xa[13]1a) - USB: JMicron JMS539 (0x152d:0x0539): New FW supports SAT (ticket #338) - USB: TrekStor Datastation (0x1e68:0x0050) (Red Hat Bugzilla 954162) 2014-07-13 Christian Franke atacmds.cpp: Add missing const and initialization. Don't print extra '\n' if self-test log is empty. ataprint.cpp: Add new ACS-4 log. cciss.cpp: Fix C++11 builds on Linux. GCC and CLang do not predefine 'linux' when in '-std=c++11' mode. smartd.cpp: Update description of Windows smartd service. README: Update license info. Remove outdated ATA references. 2014-07-10 Christian Franke Makefile.am: Rework build of Solaris specific man pages. This fixes some bogus and some missing replacements. smartctl.8.in, smartd.8.in, smartd.conf.5.in: Minor typo and syntax fixes. 2014-07-09 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in: Avoid '.SH' macros with no argument. Remove colons from section names. Merge sections CONTRIBUTORS and CREDITS with AUTHORS. Update SEE ALSO sections. 2014-07-05 Christian Franke configure.ac: Remove snprintf() compile time test. Add '--with-working-snprintf' configure option. Add __USE_MINGW_ANSI_STDIO test for MinGW GCC. utility.cpp: Add snprintf() runtime test. Add GCC version to output of -V option. Makefile.am: Add update-smart-drivedb.1m for Solaris. 2014-06-30 Christian Franke configure.ac: Update macros as suggested by 'autoconf --warnings=obsolete'. Makefile.am: Add creation of empty directories to install targets. 2014-06-29 Christian Franke configure.ac, Makefile.am, smartd.cpp, smartd_warning.sh.in: Add '--with-smartdscriptdir' configure option to change location of smartd_warning.sh (Debian bug 710815). Add '--with-smartdplugindir' configure option to change (or disable) smartd_warning.sh plugin location. smartd.conf.5.in: Optionally hide the plugin documentation. 2014-06-27 Christian Franke Makefile.am: Add update-smart-drivedb.8 target. update-smart-drivedb.8.in: Add copyright and version info. Adjust path names for make target. Add FreeBSD/OpenBSD specific info. 2014-06-27 Hannes von Haugwitz update-smart-drivedb.8.in: New man page (Debian bug 708433). 2014-06-27 Christian Franke configure.ac: Suppress pkg-config warnings about missing 'systemd.pc'. Makefile.am: Silence build of man pages and svnversion.h. This makes '--enable-silent-rules' or 'make V=0' more effective (available since automake 1.13). 2014-06-27 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300/M500: New attributes (ticket #326) - SandForce Driven SSDs: ADATA XM11, Corsair Force LS, OWC Aura Pro 6G OWC Mercury Electra Pro 3G, PNY Prevail Elite, Transcend SSD320/720 2014-06-25 Christian Franke os_win32.cpp: Fix calculation of SCSI resid. 2014-06-23 Christian Franke scsiata.cpp: usbjmicron_device: Fix SMART Status check for USB bridges which always return 0x01. Add JMicron specific error messages. 2014-06-22 Christian Franke atacmds.cpp, ataprint.cpp: Improve messages for unsupported SMART Status command. ataprint.cpp: Print form factor. 2014-06-21 Christian Franke drivedb.h: - Crucial/Micron M500/M510/M550 Client SSDs - Micron M500DC Enterprise SSDs Based on patch provided by Clayton Hawkings from Micron. 2014-06-20 Christian Franke autogen.sh: automake 1.14.1 works. 2014-06-20 Christian Franke scsiata.cpp: usbjmicron_device: Check SCSI resid for SMART STATUS. Some (Prolific) USB bridges do not transfer a status byte. os_win32.cpp: Include SCSI resid in debug output. 2014-06-19 Douglas Gilbert scsiprint.cpp: - minor comment clean-up 2014-06-19 Christian Franke drivedb.h: - Intel 730 and DC S3500/S3700 Series SSDs: rename, add 730 and S3700. Remove extra S3700 entry. Based on patch provided by Tim Small. 2014-06-18 Christian Franke os_win32.cpp: Fix CSMI support for older Intel RST drivers which set bPortIdentifier=0xff (regression from r3888). os_win32/installer.nsi: Create standard InstallLocation registry entry. Keep old Install_Dir entry if needed for GSmartControl. Update links in registry and shortcuts. 2014-06-18 Christian Franke drivedb.h: - USB: Buffalo MiniStationHD-PCFU3 (0x0411:0x0240) - USB: Toshiba Stor.E Plus (0x0480:0xa00a) (Debian bug 734395) - USB: Samsung D3 Station (0x04e8:0x6124) (ticket #332) - USB: Samsung M3 Portable (0x04e8:0x61b[45]) - USB: Seagate Expansion Portable (0x0bc2:0x2312) - USB: Seagate Expansion External (0x0bc2:0x3312) (ticket #320) - USB: WD Elements (0x1058:0x10[ab]8) (ticket #331) - USB: ASMedia AS2105 (0x174c:0x5136) 2014-06-16 Christian Franke drivedb.h: - Seagate Laptop Thin HDD - Seagate Barracuda 7200.14 (AF): *DM000 variant - Seagate Barracuda Green (AF): no warnings for newer firmware versions - Seagate Constellation.2 (SATA) - Seagate NAS HDD - Seagate Video 3.5 HDD 2014-06-15 Christian Franke drivedb.h, smartctl.8.in, smartd.8.in, INSTALL, NEWS, TODO, WARNINGS: Fix old Trac links. 2014-05-23 Alex Samorukov os_freebsd.cpp: fixed #321 (compiler warning on 32 bit architectures), patch provided by tijl 2014-05-01 Christian Franke os_linux.cpp: Clarify copyright info in GPL header. smartctl.8.in, smartd.conf.5.in: Update '-d aacraid' info. 2014-04-30 Douglas Gilbert scsiprint.cpp: - Lowest aligned LBA > 0 not common so only output in that case 2014-04-28 Christian Franke autogen.sh: Allow automake 1.14, suppress 'subdir-objects' warning. Makefile.am: Add new 'compile' script to target 'maintainer-clean'. 2014-04-28 Douglas Gilbert scsicmds.h, scsicmds.cpp, scsiprint.h: - improve handling of modern SCSI disks (SAS SSDs) show compliance (SCSI version), show 12 Gbps SAS-3 speed, and flag ZBC presence 2014-04-27 Alex Samorukov drivedb.h: - Toshiba 3.5" MG03ACAxxx(Y) Enterprise HDD 2014-04-27 Christian Franke Fixes for aacraid patch: aacraid.h: Fix typo which breaks 32-bit build. os_linux.cpp: Remove useless member variable afd. Fix error handling of /proc/devices parsing. Avoid unsafe sprintf(). Fix help text. 2014-04-27 Raghava Aditya os_linux.cpp: - Added support for aacraid drivers - Created a new interface for aacraid smartctl -d aacraid,H,L,ID /dev/sdx 2014-04-18 Douglas Gilbert scsicmds.cpp: - supported_vpd_pages(): lower response length to stop sense data noise on old disks (pre SPC-3) 2014-04-17 Christian Franke drivedb.h: - Western Digital RE4 (SATA 6Gb/s): WD2000FYYX - Western Digital Se - Western Digital Caviar Green (AF, SATA 6Gb/s): 4TB - Western Digital Black: Rename, add 3TB, AF, remove extra AF entry - Western Digital Red: 4TB (ticket #322) - Western Digital Blue Mobile 2014-04-10 Christian Franke os_win32.cpp: Rework CSMI port scanning. Use bPortIdentifier instead of Phy array index for addressing. Ignore possibly bogus bNumberOfPhys (ticket #325). 2014-04-09 Douglas Gilbert scsiprint.cpp: - add guard to scsiPrintSasPhy() invocation; resolve ticket #204 2014-04-06 Christian Franke WARNINGS: Remove all entries. Add link to Warnings page in Wiki. 2014-03-13 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300/M500: *SSD1 variant - SandForce Driven SSDs: ADATA SP300, ADATA SP800, ADATA SP900 DL2, Corsair Force SSD, Kingston SE50S3, Kingston SKC380S3, Smart Storage XceedIOPS2, VisionTek GoDrive - Indilinx Barefoot 3 based SSDs: OCZ VERTEX 450 - JMicron based SSDs: ADATA SP600 - Plextor M3/M5 (Pro) Series SSDs: Rename, add M5S (ticket #297), M5Pro 2014-03-06 Christian Franke drivedb.h: - OCZ Intrepid 3000 SSDs - Intel 320 Series SSDs: 'D' variant (ticket #315) - Intel DC S3500 Series SSDs: 'T' variant (ticket #315) 2014-03-05 Christian Franke ataprint.cpp: Check SCT Feature Control support bit for '-g/-s wcreorder'. This prevents bogus error messages if SCT support excludes SCT Feature Control command. atacmds.cpp: Fix error message text for SCT Feature Control command. 2014-03-03 Christian Franke smartctl.8.in, smartd.8.in, smartd.conf.5.in: Remove bashisms from shell script examples. 2014-03-03 Christian Franke Makefile.am, os_win32/smart*_res.rc.in: Set Copyright year in Windows VERSIONINFO resource. 2014-03-03 Christian Franke os_linux.cpp: Fix glob(3) max path count (ticket #317). 2014-03-03 Christian Franke configure.ac, Makefile.am: Add '--with-systemdenvfile=[FILE|no]' configure option to change or remove (ticket #316) the systemd EnvironmentFile setting. smartd.service.in: Add a reference to documentation (ticket #316). 2014-02-18 Alex Samorukov os_freebsd.cpp: use %lu for iop->resp_sense_len 2014-02-16 Alex Samorukov os_freebsd.cpp: mass updates, provided by Tijl Coosemans - Remove some unused private fields from some classes (found by Clang) - In freebsd_scsi_device::scsi_pass_through: * Make sure this function returns false on error instead of an error code that gets converted to true. * Put printing of the "Incoming data" debug info right after the cam_send_ccb() call and before the error checking to make debugging easier. * When copying sense data make sure the fields in the CCB are actually valid with CAM_AUTOSNS_VALID. Also make sure that the size of the sense data doesn't overflow max_sense_len. This was the real cause for the crash in ports/181836. * Add some debug printing on the sense data. 2014-02-03 Christian Franke dev_areca.cpp: Check cmds index before use (ticket #312). Make cmds array static const. 2014-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2013-12-21 Christian Franke drivedb.h: - Intel 525 Series SSDs - Intel 530 Series SSDs (ticket #308) 2013-12-19 Christian Franke drivedb.h: - Seagate Samsung Spinpoint F4 - Seagate Desktop SSHD - Seagate Constellation CS - Western Digital Red: *JFCX variant - Western Digital Green Mobile - Western Digital Elements / My Passport (USB): rename 2013-12-19 Christian Franke autogen.sh: automake 1.13.3 works. 2013-12-14 Christian Franke drivedb.h: - Toshiba 2.5" HDD MK..65GSX: "... H" (USB?) variant - Toshiba 2.5" HDD MQ01UBD... (USB 3.0) - USB: Toshiba Stor.E Slim USB 3.0 (0x0480:0x0100) - USB: Toshiba Stor.E Basics (0x0480:0xa009) - USB: Toshiba Stor.E (0x0939:0x0b15) - USB: Seagate FreeAgent GoFlex (0x0bc2:0x5020) - USB: WD My Passport Ultra (0x1058:0x0741) - USB: WD Elements (0x1058:0x1048) - USB: Initio (0x13fd:0x1640) (ticket #295) - USB: LucidPORT (0x1759:0x5100) 2013-12-08 Christian Franke drivedb.h: - Apacer SDM4: SFDDA01C firmware (ticket #304). - Crucial/Micron RealSSD m4/C400/P400: M4 SSD1 (ticket #306). - Seagate Barracuda 7200.14: Check part number to avoid bogus firmware bug warning (ticket #298). 2013-11-23 Christian Franke configure.ac, utility.cpp: Remove __DATE__, __TIME__ and SMARTMONTOOLS_CONFIGURE_DATE. This obsoletes OpenSUSE nobuild-date.patch. Reproducible builds are now supported. 2013-11-15 Alex Samorukov os_freebsd.cpp: Fix crash on FreeBSD 9.2 caused by wrong SCSI status check condition. os_freebsd.cpp: Print debug info on errors only if requested. 2013-11-07 Matt Kraai <...> smartctl.cpp: Add missing stdlib.h. This fixes build on QNX 6.3.2 (ticket #300). 2013-11-07 Roger Röhrig <...> drivedb.h: Intel DC S3500 Series SSDs: Add -F xerrorlba. 2013-11-07 Roger Röhrig <...> atacmds.cpp: Fix Extended Comprehensive Error Log timestamp byte order on big endian machines. 2013-09-12 Christoph Egger dev_areca.h: Fix build on kFreeBSD (Debian bug 717567). This obsoletes Debian kfreebsd.patch. 2013-08-17 Christian Franke examplescripts: Add scripts from Debian and Fedora packages. 2013-08-17 Christian Franke Add spaces between string literals and macro identifiers. This avoids the interpretation as user-defined literals if C++11 is enabled (g++ -std=gnu++11). 2013-08-15 Dan Lukes drivedb.h: Intel DC S3500 Series SSDs 2013-08-12 Christian Franke drivedb.h: Intel 320 Series SSDs: Add attribute 183 and 199. 2013-08-10 Christian Franke autogen.sh: automake 1.10.3, 1.12.6, and 1.13.4 work. The new automake 1.14 is left out for now due to the 'subdir-objects' warning and the new 'compile' script. Add options '--force' and '--warnings=CATEGORY'. 2013-07-26 Christian Franke smartmontools 6.2 2013-07-25 Christian Franke drivedb.h: - SandForce Driven SSDs: ADATA SP900 - Transcend CompactFlash Cards: *GCF150 - Hitachi/HGST Travelstar 5K750: Apple OEM - Hitachi/HGST Travelstar Z7K500 - Hitachi/HGST Travelstar 7K750 - Hitachi Deskstar 5K3000: *BLE630 OEM - Seagate Constellation ES.3 - Western Digital Caviar Blue (SATA): Rename, add WD1602ABKS - Western Digital Caviar Blue (SATA 6Gb/s): Rename, add WD10EZEX - USB: Toshiba Canvio 3.0 Portable Hard Drive (0x0480:0xa007) - USB: Toshiba Canvio Desktop (0x0480:0xd010) - USB: Seagate FreeAgent Desk (0x0bc2:0x3008) - USB: Sharkoon 2-Bay RAID Box (0x6795:0x2756) 2013-07-21 Christian Franke utility.cpp: Add check for empty subexpressions in regular expressions. 2013-07-21 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300/M500: Rename, add M500 - SandForce Driven SSDs: Kingston KC300, MS200 - Intel 320 Series SSDs: *A variant - Intel 330/335 Series SSDs: Rename, add 335 Series - Toshiba 2.5" HDD MK..46GSX - Toshiba 2.5" HDD MK..61GSY[N]: Rename, add *GSY variant - Toshiba 2.5" HDD MK..65GSX: *GSXF variant - Toshiba 3.5" HDD DT01ACA... - Seagate Laptop SSHD - Seagate Constellation ES.2: 2GB - USB: Seagate Expansion External (0x0bc2:0x3320) - USB: Seagate Backup Plus Desktop USB 3.0 (0x0bc2:0xa0a1) - USB: WD Elements (0x1058:0x10a2) 2013-07-20 Christian Franke dev_areca.cpp: Fix possible segfault on empty port. 2013-07-20 Christian Franke os_win32/daemon_win32.cpp: Do not install the service as interactive. This is no longer supported since Vista and produces misleading error messages in event log. 2013-07-20 Christian Franke ataprint.cpp: Do not print 'SCT Commands not supported' if SCT is not used (regression from r3825 r3826). smartctl.8.in: Mark '-g/-s wcreorder' as EXPERIMENTAL. 2013-07-18 Christian Franke os_win32.cpp: Add Win-8.1 and 2012r2 to get_os_version_str(), remove 9x/ME and NT4. 2013-07-08 Alex Samorukov Add Automake 1.12.2 to the list of supported versions 2013-07-07 Christian Franke configure.ac: Support SVN 1.8 working copy format. 2013-07-06 Alex Samorukov smartctl: Added ATA Write Cache Reordering control using "-g wcreorder" and "-s wcreorder[,on|off]" options (bug #221) smartctl: minor formatting fixes 2013-07-05 Alex Samorukov HPT RAID support: maximum disk number now is 128 (#281) 2013-06-28 Alex Samorukov drivedb.h: - Apacer SDM4 2Gb SSD 2013-06-17 Alex Samorukov scsicmds.cpp: fix build on RedHat 9 os_freebsd.cpp: skip port multipliers on FreeBSD drivedb.h: - OWC Mercury EXTREME Pro 6G SSD (from #277) - USB: Fujitsu SATA-to-USB3.0 bridge chip (#280) 2013-06-12 Alex Samorukov drivedb.h: - JMicron SSD: P400e/P400m series 2013-06-09 Christian Franke INSTALL, NEWS, README, WARNINGS: Update SVN repository URLs. 2013-06-09 Christian Franke os_win32/smartd_warning.cmd: Using %DATE% in temp file names breaks the script if localized date contains '/' (This fix is already included in smartmontools-6.1-2.win32-setup.exe). 2013-06-06 Christian Franke os_win32/update-smart-drivedb.nsi: Use new SVN repository for download. 2013-06-04 Christian Franke update-smart-drivedb.in: Use new sourceforge code browser for download. 2013-04-20 Christian Franke drivedb.h: - InnoDisk InnoLite SATADOM D150QV-L SSDs - Intel 313 Series SSDs - Intel 330 Series SSDs: 240GB - JMicron based SSDs: Kingston V200 (ticket #267) - Samsung based SSDs: SM843T Series 2013-04-20 Christian Franke configure.ac: Linux: Try 'hostname -y' if 'nishostname' is missing. 2013-04-18 Christian Franke configure.ac, smartd_warning.sh.in: Add platform specific commands for host and domain names. os_win32/smartd_warning.cmd: Use WMI for DNS domain name. 2013-04-18 Christian Franke scsicmds.cpp, scsiprint.cpp: Silence -Wmaybe-uninitialized warning (g++ 4.8.0 with -flto). 2013-03-29 Christian Franke os_darwin.cpp: Silence -Wself-assign warning (ticket #266). os_darwin.cpp, os_netbsd.cpp, os_os2.cpp, os_qnxnto.cpp, os_solaris.cpp: Remove dummy functions no longer called since r3192. 2013-03-27 Christian Franke os_win32.cpp: Silence -Wunused-local-typedefs warning. 2013-03-24 Christian Franke dev_areca.cpp: Add casts to silence C++11 -Wnarrowing warning from g++ 4.8. 2013-03-24 Christian Franke Windows: Compile fixes for 64-bit Cygwin. It uses LP64 model instead of LLP64 (64-bit MSVC, MinGW). 2013-03-16 Christian Franke smartmontools 6.1 2013-03-15 Christian Franke os_win32.cpp: Support device names /dev/sd[a-z][a-z] (ticket #240). Enhance DEVICESCAN to 128 drives. Add '-d [TYPE,]pd' option. smartctl.8.in, smartd.8.in: Document these enhancements. 2013-03-14 Christian Franke drivedb.h: - Seagate Barracuda 7200.14: Fix regex for new firmware version. 2013-03-13 Christian Franke drivedb.h: - USB: Prolific PL3507 (0x067b:0x3507): works with '-d usbjmicron,p' 2013-03-13 Christian Franke Create branch RELEASE_6_0_DRIVEDB with last drivedb.h file compatible with smartmontools 6.0. 2013-03-13 Christian Franke drivedb.h: - SandForce Driven SSDs: Fix format of attribute 198 (ticket #258). - SandForce Driven SSDs: Corsair Force GS - Indilinx Barefoot_2/Everest/Martini based SSDs: OCZ VERTEX PLUS R2 - Samsung/Seagate SpinPoint M8: 320GB, 640GB - Seagate Momentus Thin - Quantum Fireball EX: 10.2GB 2013-03-07 Christian Franke ataidentify.cpp, ataprint.cpp: ACS-3 updates. ataprint.cpp: Improve device statistics error messages. 2013-03-06 Christian Franke smartd_warning.sh.in: Support BSD variant of 'hostname' command which prints FQDN. Add Windows domain name (Cygwin). 2013-03-01 Douglas Gilbert scsicmds.h, scsicmds.cpp, scsiprint.cpp: - for SCSI disks prefer READ DEFECT(12) for finding the grown defect list length (previously used READ DEFECT(10) only) 2013-03-01 Christian Franke drivedb.h: - SandForce Driven SSDs: Transcend SSD320 - Intel 520 Series SSDs: OEM variant - JMicron based SSDs: Transcend SSD25 IDE - HGST Travelstar 7K1000 - Seagate Desktop HDD.15 - Seagate LD25.2 - Western Digital RE4 (SATA 6Gb/s) - USB: Fujitsu/Zalman ZM-VE300 (0x04c5:0x2028) 2013-02-23 Christian Franke drivedb.h: Crucial/Micron RealSSD C300: Remove bogus trailing '|' from regex (Regression from r3772). 2013-02-16 Douglas Gilbert scsicmds.h, scsicmds.cpp, scsiprint.h, scsiprint.cpp: - for SCSI disks, in 'smartctl --info' report physical block size and lowest LBA alignement (if PB size different from LB size); logical block provisioning status (if any); and disk protection (a.k.a. DIF) type 2013-02-19 Alex Samorukov atacmds.cpp: fixed scttemphist on LE machines, including PPC. Patch and report provided by Roger Roehrig. 2013-02-16 Douglas Gilbert scsicmds.h, scsicmds.cpp, scsiprint.h, scsiprint.cpp: - SCSI VPD work; improve rotation rate reporting and add form factor 2013-02-14 Christian Franke drivedb.h: - SandForce Driven SSDs: Kingston V+ 200, Mushkin Chronos deluxe, OCZ Talos 2 - Plextor M3 (Pro) Series SSDs 2013-02-13 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300: new separate entry - Crucial/Micron RealSSD m4/C400: firmware bug warning 2013-02-10 Alex Samorukov os_freebsd.cpp: adding device type fix for devices on MPT controllers. 2013-02-06 Christian Franke drivedb.h: - Seagate Samsung SpinPoint M8U (USB) - Hitachi/HGST Travelstar Z5K500 - Hitachi/HGST Travelstar 5K750 - Hitachi/HGST Deskstar 7K4000 - Toshiba 2.5" HDD MK..37GSX - Toshiba 2.5" HDD MK..65GSX: GSXN variant - Toshiba 2.5" HDD MQ01ABD... - Seagate Momentus 7200.5 - Western Digital Caviar Green (AF, SATA 6Gb/s): 2TB - USB: Samsung M3 Portable USB 3.0 (0x04e8:0x61b6) - USB: LaCie Rugged Mini USB 3.0 (0x059f:0x1051) - Change short attribute names required before r3343. 2013-02-05 Christian Franke smartd.cpp: Fix allocation of buffer passed to putenv(). Using putenv("NAME") to unset NAME is not portable. 2013-02-05 Christian Franke do_release: New Signing Key. 2013-01-31 Christian Franke dev_areca.h: Use the C++ way to specify unused arguments. This silences -Wself-assign warning from clang++. 2013-01-30 Christian Franke configure.ac: Use AC_CHECK_TOOL for winmc and windres. 2013-01-30 Christian Franke Windows smartd: Install service with delayed auto start enabled. 2013-01-26 Christian Franke Windows smartd: Add eventlog MESSAGETABLE resource. Install/remove smartd.exe as event message file. Remove syslogevt.exe tool. 2013-01-26 Christian Franke Windows: Add required string CompanyName to VERSIONINFO. 2013-01-23 Christian Franke Windows: Add VERSIONINFO resource to exe files. 2013-01-23 Christian Franke drivedb.h: - Crucial/Micron RealSSD C300/C400/m4: m4 mSATA variant - Indilinx Barefoot 3 based SSDs - Intel DC S3700 Series SSDs - Samsung based SSD: Samsung SSD 840 Series 2013-01-18 Christian Franke AUTHORS: Convert to UTF-8. Sort names. Replace tabs. 2013-01-18 Christian Franke Rename configure.in to configure.ac to silence warning from new automake. autogen.sh: automake 1.12.5 is OK. 2013-01-16 Christian Franke atacmds.cpp: Fix assignment of BYTEORDER from -v option (Regression from r3719). 2013-01-13 Ole Jørgen LegÃ¥rd os_qnxnto.cpp: Fix include of errno.h. 2013-01-12 Christian Franke drivedb.h: - SandForce Driven SSDs: Mushkin Callisto deluxe, SuperSSpeed S301 - Intel 320 Series SSDs: 'B' (7mm) variant (ticket #257) - SAMSUNG SpinPoint F1 EG - SAMSUNG SpinPoint P80: SP0401N/TJ100-30 - Western Digital Caviar Black: 4TB - Western Digital Caviar Black (AF): Remove non-AF models - Western Digital My Passport (USB, AF): 5000L, 10J variants - USB: WD My Passport USB 3.0 (0x1058:0x07a8) - USB: WD My Book Studio II (0x1058:0x1105) 2013-01-02 Christian Franke drivedb.h: - SandForce Driven SSDs: ADATA S396, Kingston 3K, V+ - Indilinx Everest/Martini based SSDs: OCZ VERTEX PLUS - Samsung based SSD: Samsung SSD 840 PRO Series 2013-01-02 Christian Franke Add '-d usbjmicron,p' device type for Prolific USB bridges. Based on patch provided by Edward Sheldrake. 2013-01-01 Christian Franke smartd: Use Attribute 190 for temperature (-W) if 194 is not present. 2013-01-01 Christian Franke Happy New Year! Update copyright year in version info. 2012-12-16 Alex Samorukov os_freebsd.cpp: WRITE LOG on LSI/Megaraid should work fine, disable check, problem was linux related. os_linux.cpp: Implemented autoscan for the megaraid SAS controolers. os_linux.cpp: fix WRITE LOG command in SAT layer for -d megaraid. Reason was direction flag always set to READ. os_linux.cpp: unblock autodetection for the SAT drives in -d megaraid. 2012-12-14 Christian Franke man pages: Fix usage of Hyphen (-) and Minus sign (\-). 2012-12-13 Christian Franke man pages: Update EXPERIMENTAL notes. Fix spelling (Red Hat Bugzilla 665028). 2012-12-13 Christian Franke ataprint.cpp: Print Additional Product Identifier (OEM Id). 2012-12-13 Stanislav Brabec Update FSF postal address in all files. 2012-12-12 Christian Franke smartctl.cpp: Remove include for QNXNTO. Should only be needed if placement new is used. smartd.cpp: Remove very old _GNU_SOURCE define. It was added 10 years ago in r147. It is not (or no longer) needed and has an unwanted side effect (__USE_MINGW_ANSI_STDIO) on MinGW. 2012-12-11 Christian Franke smartd.cpp: Add '-w PATH, --warnexec=PATH' option. smartd.8.in: Document this option. 2012-12-11 Christian Franke smartd.cpp: Add '-d ignore' directive. smartd.conf.5.in: Document '-d ignore'. Add DEVICESCAN example. Remove duplicate and outdated info about device scanning. smartd.8.in: Add notes about RAID controllers to device scanning info. 2012-12-11 Stanislav Brabec * smartd.initd.in: SUSE: Added sysconfig options to disable persistent state writes, attribute log and set arbitrary smartd options. 2012-12-03 Christian Franke Avoid usage of strcpy(), strcat(), sprintf(). Use snprintf() instead or change type to std::string. Use array references instead of char pointers for parameters. 2012-12-03 Christian Franke smartd.cpp: Ignore a device from DEVICESCAN if a preceding smartd.conf entry for the same device exists. 2012-11-28 Christian Franke smartd.conf.5.in: Document smartd_warning.sh/cmd scripts and the new environment variables. Makefile.am: Replace smartd_warning.* paths on man pages. Reformat long sed commands. 2012-11-27 Christian Franke smartd.cpp: Remove trailing newlines from some MailWarning() strings. os_win32/smartd_warning.cmd: Fix SMARTD_MESSAGE with parentheses. 2012-11-25 Alex Samorukov OpenBSD: remove dummy functions 2012-11-24 Christian Franke Windows: Add tool wtssendmsg.exe based on no longer used module os_win32/wtssendmsg.cpp. os_win32/smartd_warning.cmd: Fix wtssendmsg call. os_win32/installer.nsi: Install smartd_warning.cmd and wtssendmsg.exe. Fix uninstall of old ChangeLog. 2012-11-23 Christian Franke Move MSVC10 project files to new directory os_win32/vc10. 2012-11-22 Christian Franke smartd: Move warning message formatting and mailer/command startup to new script SYSCONFDIR/smartd_warning.sh (Windows: smartd_warning.cmd). Add environment variables SMARTD_PREVCNT and SMARTD_NEXTDAYS. Remove host/domainname related code from smartd.cpp and configure.in 2012-11-22 Alex Samorukov smartctl: implemeted support for -g/-s rcache and -g/-s wcache for SCSI devices to control read/write device cache. 2012-11-19 Alex Samorukov smartctl: supports progress indicator on selftests smartctl: prints rotation speed for SCSI drives, if supported smartctl: add headers to SCSI output, fix data blocks formatting, trim identification data os_linux.cpp: add autodetection for PERC H700 array smartd: trim SCSI vendor/model/serial before creating state files 2012-11-18 Alex Samorukov smartd.cpp: implement error counters and temperature saving to the attrlog file for SCSI devices. smartd.cpp: added reset_warning_mail() if device is working for SCSI 2012-11-18 Christian Franke drivedb.h: Western Digital Caviar Green: Add -F xerrorlba 2012-11-17 Alex Samorukov smartd.cpp: print lu_id for SPC devices, it is supported by standard smartd.cpp: added initial state file support for the SCSI devices smartd.cpp: add S/N to SCSI device identifier, lu_id is not available on some drives. smartd.cpp: fix warning for SCSI drives with self test in progress (#249) drivedb.h: added -F xerrorlba flag Seagate Barracuda LP/CC32 2012-11-09 Christian Franke Windows smartd: Allow quoting of '-M exec' argument to support path names with spaces. 2012-11-09 Christian Franke ataprint.cpp: Rework smartctl -l directory output. Add R/W, R/O info. Report identical logs in one line. 2012-11-09 Alex Samorukov os_freebsd.cpp: adding handling of SCSI devices exported with mfip driver. FreeBSD changing PDT code to 0x1f and we are changing it back to 0x00 (direct-access block device). os_freebsd.cpp: improved error handling for the ATA devices 2012-11-04 Christian Franke drivedb.h: - SandForce Driven SSDs: Mushkin Chronos - Indilinx Everest/Martini based SSDs: OCZ AGILITY4 - Intel 710 Series SSDs: Add attribute 174 - JMicron based SSDs: KINGSTON SSDNOW 30GB - Hitachi Deskstar 7K1000.C: *CLA330 - Seagate DiamondMax 23, Barracuda 7200.12, 7200.14 (AF), LP, Green (AF): no warnings for newer firmware versions - Western Digital Caviar Green (AF, SATA 6Gb/s): rename, add 1TB - USB: Toshiba Stor.E (0x0930:0x0b1[9a]) - USB: Verbatim Store'n'Go (0x18a5:0x022b) 2012-11-02 Alex Samorukov os_freebsd.cpp: disabling 48bit commands on legacy ATA controllers in ATACAM mode because of kernel bug. 2012-10-31 Christian Franke atacmdnames.cpp: Update for ATA-8-ACS, ACS-2, ACS-3. ataidentify.cpp: Mark retired/obsolete values. ataprint.cpp: Add new ACS-3 logs, mark obsolete logs. 2012-10-27 Alex Samorukov os_freebsd.cpp: Have smartd prefer real device names over passN. Patch provided by dnelson, see ticket #21 os_freebsd.cpp: fix 48-bit support for ATA legacy controllers in ATACAM mode, patch provided by Alexander Motin 2012-10-25 Christian Franke atacmds.cpp: Return error for get SCT ERC if ATA registers are unchanged after SMART_WRITE_LOG command (see ticket #245). 2012-10-24 Christian Franke dev_areca.cpp: Add missing parameter check to ata_pass_through(). Update Areca info on man pages. 2012-10-24 Christian Franke dev_interface: Rework ATA parameter checks, use new flags ata_device::supports_* for new ata_cmd_is_supported(). Replace ata_cmd_is_ok() by ata_cmd_is_supported() in scsiata.cpp and os_win32.cpp. 2012-10-19 Alex Samorukov os_freebsd.cpp - fixed 3ware twe controller support broken by inerface migration. 2012-10-18 Christian Franke utility.cpp: Add missing errno clear in split_selective_arg() (Debian bug 690108). Remove unused function split_report_arg2(). 2012-10-18 Christian Franke os_win32.cpp: define _WIN32. This fixes build on Cygwin with new w32api-headers. 2012-10-18 Alex Samorukov Compile fixes for Areca patch on FreeBSD. Added support for the /dev/twsX (3ware 9750) controller on FreeBSD. Manual pages updated with /dev/twsX device FreeBSD: Migrate 3ware interface to ata_pass_through() FreeBSD: fix missing drives detection on -d 3ware FreeBSD: 3ware - do not pass buffers direcly, use memcpy() instead FreeBSD: improved detection of 3ware/LSI controllers 2012-10-16 Christian Franke Compile fixes for Areca patch: Add missing includes. Add GPL header. Add dev_areca.* to configure.in and Makefile.am. 2012-10-16 Hank Wu Move common Areca code from os_freebsd.cpp, os_linux.cpp, os_win32.cpp to new files dev_areca.h, dev_areca.cpp. Add SAS support for FreeBSD and Linux. 2012-10-10 Christian Franke Rename old CHANGELOG to ChangeLog-5.0-6.0. Start new ChangeLog. 2012-10-10 Christian Franke smartmontools 6.0 smartmontools-7.0/ChangeLog-5.0-6.00000644000175000010010000061572012035317436013601 00000000000000CHANGELOG for smartmontools 5.0 to 6.0 $Id: ChangeLog-5.0-6.0 3645 2012-10-10 16:15:26Z chrfranke $ Maintainers / Developers Key (alphabetic order): [AS] Alex Samorukov [BA] Bruce Allen [OB] Oliver Bock [EB] Erik Inge Bolsø [SB] Stanislav Brabec [PC] Peter Cassidy [MC] Matthieu Castet [YD] Yuri Dario [CD] Casper Dik [CF] Christian Franke [GF] Guilhem Frézou [DG] Douglas Gilbert [GG] Guido Guenther [JPH] Jordan Powell Hargrave [JH] Joerg Hering [GK] Geoff Keating [DK] Dr. David Kirkby [DL] Dan Lukes [KM] Kai Mäkisara [EM] Eduard Martinescu [FM] Frédéric L. W. Meunier [GP] Gabriele Pohl [AR] Adam Radford [KS] Keiji Sawada [MS] Manfred Schwarb [TS] Tomas Smetana [DS] David Snyder [SS] Sergey Svishchev [PW] Phil Williams [LW] Leon Woestenberg [SZ] Shengfeng Zhou [RZ] Richard Zybert smartmontools 6.0 2012-10-10 [CF] do_release: Fix for minor rev number 0. [CF] drivedb.h updates: - SandForce Driven SSDs: Corsair Force 115GB - Hitachi Ultrastar 7K4000 - Seagate Barracuda 7200.7 and 7200.7 Plus: IBM OEM variants - Western Digital Caviar Black (AF) [CF] man pages: Update introduction. Update ATA standards. Remove some outdated info. [CF] man pages: Unify license headers. [CF] smartctl: Do not abort SCT status output on unknown temperature history format version. [CF] smartctl: Remove duplicate note about selective self-test log version. [CF] smartctl: Add '-l devstat' to '-x, --xall' output. [CF] smartctl: Rework ATA error messages and 'not supported' messages. Avoid misleading warnings on unsupported features (ticket #182). Avoid duplicate error messages. [CF] atacmds.h: Remove nonexistent functions. [CF] Windows installer: Add support for /S(ilent) install/uninstall. [CF] Windows installer: Update examples. Remove some doc shortcuts. [CF] Prepare release 6.0. Change Copyright output line. Change AUTHORS sections on man pages. [CF] smartctl: Rework "ATA Version" output. Print major and minor revision in one output line. Remove "ATA Standard" line. [CF] drivedb.h updates: - Add firmware warnings for various Seagate series (ticket #239): DiamondMax 23, Barracuda 7200.12, 7200.14 (AF), LP, Green (AF) - Seagate Barracuda 7200.14 (AF): 2.5TB [CF] drivedb.h updates: - SandForce Driven SSDs: SanDisk Extreme - Indilinx Everest/Martini based SSDs: OCZ-VERTEX4, fix Attribute 232 - STEC Mach2 CompactFlash Cards - Toshiba 2.5" HDD MK..55GSX: *55GSXF variants - Western Digital VelociRaptor (AF) [CF] Windows: Remove EXPERIMENTAL notes for 64-bit version. [CF] autogen.sh: automake 1.11.6 and 1.12.3 are OK. [CF] smartctl: Fix '--identify' for big-endian CPUs. [CF] ataidentify.cpp: Document some older (now obsolete) features. [CF] ataidentify.cpp: Add some recent ACS-3 features. [CF] smartctl: Support '-l sataphy' also for Packet interface devices. [CF] atacmds.cpp: Add new ATA ACS-3 minor revision. [CF] smartctl: Print SATA version and speed in '-i' output. [CF] drivedb.h: Minor reordering of Seagate entries. [CF] drivedb.h: Use "AF" for Advanced Format (4KiB LPS). [CF] drivedb.h updates: - Seagate Barracuda SpinPoint F3 - SAMSUNG SpinPoint F3 RE - Seagate Barracuda 7200.12: ST3750525AS - Seagate Barracuda 7200.14 (AF): change name, add -v options - Western Digital Red (AF) - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xa013) (ticket #235) - USB: Seagate Backup Plus Desktop USB 3.0 (0x0bc2:0xa0a4) [CF] os_win32.cpp: Add support for SAS disks behind Areca SAS controllers. This includes SAS/SATA autodetection. Patch was provided by Hank Wu from Areca. [CF] ataidentify.cpp: Add some recent SATA features. [CF] smartctl: Add '--identify[=wnvb]' option. Add new source files ataidentify.h/cpp. [CF] Makefile.am: Reformat lists of sources. [CF] Do not print HDD/SSD specific default attribute names if identify data reports SSD/HDD device. [CF] drivedb.h updates: - Intel 320 and 710 Series SSDs: Set '-F nologdir' - Seagate Barracuda ES.2: Set '-F xerrorlba' [CF] Create branches RELEASE_5_4[0-3]_DRIVEDB with last drivedb.h file compatible with smartmontools 5.4[0-3]. [CF] drivedb.h updates: - SAMSUNG SpinPoint M40/60/80: HM120IC - USB: Oxford (0x0928:0x0010) - USB: Seagate External Drive/Cypress (0x0bc2:0x0503) - USB: 0x1f75:0x0888 is Innostor IS888 [CF] smartctl: Print nominal media rotation rate in '-i' output (ATA). [CF] knowndrives.cpp: Fix missing '-F xerrorlba' in '-P show' output. [CF] os_win32.cpp: Use WMI to get serial number if IOCTL_STORAGE_QUERY_PROPERTY is used. [CF] os_win32.cpp: Remove more Win9x/ME/NT4 specific code: ATA drive number, GetDevicePowerState() handling. [CF] Add '-F xerrorlba' option/directive. [CF] Rework '-F' option handling. Add support for multiple '-F' options and directives. [CF] Makefile.am: Fix typo in ACLOCAL_AMFLAGS. [CF] smartd.cpp: MailWarning(): Move variable declarations, use sizeof() instead of numbers. [CF] smartd.cpp: Rework dnsname(). Print "[None]" instead of "[Unknown]" if domain is not set. Print NIS domain only if supported. [CF] Windows smartd: Use gethostname/gethostbyname() from winsock. Remove os_win32/hostname_win32.*. [CF] smartd: Include device identify info in warning emails (ticket #185). Add SMARTD_DEVICEINFO environment variable. [CF] Add '-F nologdir' option/directive. Prevents freeze of some Intel SSDs (ticket #214). [CF] smartd: Don't log ignored -W directive as critical. [CF] drivedb.h updates: - Smart Storage Systems Xcel-10 SSDs: Move entry, change name - Samsung: Remove very old and already commented out entries - Seagate Momentus XT (Adv. Format) - WD My Passport: 3 -> 2 entries, add 2TB - USB: Imation (0x0718:0x1000) (ticket #231) - USB: Initio (0x13fd:0x1040): unsupported - USB: ASMedia USB 3.0 (0x174c:0x55aa): unsupported -> -d sat - USB: PQI H560 (0x3538:0x0902) (ticket #232) [CF] smartctl: Override SMART disabled state with '-T permissive'. [CF] os_win32/daemon_win32.cpp: Drop remaining WinNT4 compatibility. [CF] Windows smartd: Add smartd.conf directives '-m console', '-m active', '-m connected'. Send warning messages via WTSSendMessage(). Remove use of MessageBox() which does no longer work for services since Vista/2008. [CF] Fix 'smartctl -P show'. Regression from r3249. [CF] smartd.cpp: Fix setting of temporary environment in MailWarning(). Stack space was passed to putenv() but variable was not unset before return. Very old bug introduced 2003 in r1114. [CF] smartd.cpp: Add fflush() to support redirection of debug output (Debian bug 681349). [CF] os_generic.cpp: Add missing int64.h (Debian bug 619208) This obsoletes Debian patch fix-generic.diff. [CF] cciss.cpp: Fix build on GNU/kFreeBSD (Debian bug 676142). This obsoletes Debian kfreebsd.patch. [CF] Windows: Drop backward compatibility with WinNT4. [CF] Windows: Drop backward compatibility with Win9x/ME. smartmontools 5.43 2012-06-30 [CF] drivedb.h USB updates: - Toshiba Canvio Basics (0x0480:0xa006) - A-DATA DashDrive (0x125f:0xa94a) [CF] drivedb.h: Hitachi Travelstar 7K500: *A362/3 variants [CF] Windows: Add Windows Server 2012 to get_os_version_str(). [CF] drivedb.h updates: - Sandforce Driven SSDs: OWC Mercury Electra 3/6G SSD - Seagate Momentus SpinPoint M8 - Hitachi Deskstar 5K4000 - Toshiba 2.5" HDD MK..61GSYN - Seagate Barracuda (SATA 3Gb/s, 4K Sectors): 1TB, *DM003-* variant [CF] smartctl.8.in: Note performance impact of self-tests. [CF] os_win32.cpp: Add support for older Areca drivers which used a different target id. Patch was provided by Hank Wu from Areca. [CF] smartctl.8.in: Add info about HP Smart Array controllers. Original patch was provided by Don Brace from HP. [CF] os_freebsd.cpp: add SAT autodetection to '-d cciss,N' device type (ticket #202). Add missing freebsd_areca_device::m_encnum (regression from r3542). Patch was provided by Don Brace from HP. [CF] os_linux.cpp: add SAT autodetection to '-d cciss,N' device type (ticket #202). [CF] Makefile.am: FIXHTML modified for newer man2html versions. [CF] autogen.sh: automake 1.11.5 is OK. [CF] man pages: Minor updates and syntax fixes. [CF] smartd.service.in: Add ExecReload and StandardOutput. Make EnvironmentFile optional (ticket #194). [CF] drivedb.h USB updates: - HP Desktop HD BD07 (0x03f0:0xbd07) - Iomega Prestige Desktop USB 3.0 (0x059b:0x0070) - Prolific PL2507 (0x067b:0x2507): unsupported -> -d usbjmicron,0 - WD My Passport USB 3.0 (0x1058:0x0748) - WD My Book Essential USB 3.0 (0x1058:0x1140) - Sharkoon SATA QuickDeck Pro (0x1f75:0x0888): unsupported - Hitachi Touro Desk (0x4971:0x1015) [CF] Move function str_starts_with() to utility.h. [CF] smartctl.8.in, smartd.conf.5.in: Note required Areca SAS firmware version. [CF] INSTALL, smartctl.8.in: Announce OS X SAT SMART Driver (ticket #25). [CF] Add smart_device::is_syscall_unsup(). [CF] os_win32.cpp: Avoid ENOTSUP which is not provided by some versions of MinGW. [DG] os_linux.cpp: Fix scsi pass-through SG_INFO_CHECK mask logic (ticket #225) [CF] drivedb.h updates: - Sandforce Driven SSDs: OCZ-NOCTI - Intel 330 Series SSDs (ticket #227) [CF] smartctl.8.in, smartd.conf.5.in: Document '-d areca N[/E]' support for Windows. [CF] os_win32.cpp: Add help text and error messages for '-d areca,N[/E]'. [CF] os_win32.cpp win_areca_device: Disable full 48-bit ATA support. Add missing set_err() calls. Remove unused function and parameter. [CF] os_win32.cpp: Add support for SATA disks behind Areca SATA and SAS controllers. Requires '-d areca,N[/E]' as type and '[/dev/]arcmsrX' as device name. Patch was provided by Hank Wu from Areca. [CF] Windows installer: Make name of checksum file 32-/64-bit specific. [CF] Windows installer: Add support for combined 32-/64-bit installer. [CF] Windows installer: Drop support for UBCD4Win. [AS] os_freebsd.cpp: sync Areca code with linux version by adding optional enclosure number. [CF] smartctl.8.in, smartd.conf.5.in: Add brief doc for '-d areca N/E'. [CF] os_linux.cpp: Add optional enclosure number to '-d areca' option. This adds support for SATA disks behind Areca SAS controllers. Patch was provided by Hank Wu from Areca. [CF] smartctl: Add log addresses and statistics value from ACS-3 revision 2. [CF] drivedb.h updates: - Crucial/Micron RealSSD C300/C400/m4: m4 512GB - Indilinx Everest/Martini based SSDs: OCZ-PETROL - SAMSUNG SpinPoint F4 EG (AFT): Fix link - Seagate Momentus 4200.2: ST960812A - Seagate Momentus 5400.2: ST960821A (from 4200.2) - Seagate Barracuda 7200.12: ST3500413AS - Western Digital RE3 Serial ATA: WD7502ABYS - Western Digital AV-GP: WD....AV[CD]S, split entry - Western Digital AV-GP (Adv. Format): WD10EU[CR]X [CF] autogen.sh: Set svn:eol-style=LF to be compatible with Cygwin bash. [CF] autogen.sh: automake 1.11.3 is OK. [CF] drivedb.h updates: - Sandforce Driven SSDs: Smart Storage Systems XceedSTOR, XceedIOPS2, Xcel-200 - Smart Storage Systems XceedSecure2 SSDs - Smart Storage Systems XceedUltraX/Adtron A25FBX SSDs - Smart Storage Systems Adtron A25FB 2xN SSDs - Smart Storage Systems Adtron A25FB 3xN SSDs Original patch was provided by Rusty Carruth [CF] drivedb.h updates: - Remove outdated IBM links - Update all links to Seagate Knowledge Base - Hitachi Deskstar 7K1000.D [CF] drivedb.h USB update: - Seagate Expansion External (0x0bc2:0x3332) (ticket #223) [CF] drivedb.h USB updates: - Samsung Story Station (0x04e8:0x5f05) - Toshiba STOR.E (0x0930:0x0b1b) [CF] smartctl: Add options '-f hex' and '-f hex,[id|val]' to print attribute IDs and/or values as hex. [CF] smartd.8.in: Fix signal name (Debian bug 661801). [CF] Add 'raw56', 'hex56', 'raw24(raw8)' attribute print formats. Change default for Power_On_Hours to 'raw24(raw8)'. This provides more reasonable output for SandForce based devices missing in drivedb.h. [CF] configure.in, Makefile.am: Support new SVN 1.7 working copy format. [CF] drivedb.h update: - Intel 520 Series SSDs: Add units to attributes 241, 242, 249. [AS] drivedb.h: fixed identifier for Seagate SV35 series. [CF] Print command duration in ATA debug output. Add smart_interface::get_timer_usec(). [CF] drivedb.h updates: - SandForce Driven SSDs: G.SKILL Phoenix Pro - Intel 520 Series SSDs - SAMSUNG SpinPoint F4 EG: Update firmware download link [CF] drivedb.h updates: - Add comment with default settings. - Samsung based SSDs: Fix attribute 240 [CF] Windows: Add Win8 to get_os_version_str(). [CF] Windows: Remove MSVC specific pragma, disable warning in project file. [CF] Add '-d sat,auto[,N]' option for controller independent SAT detection. [CF] dev_interface.h: Replace this_is_ata/scsi(*) by hide_ata/scsi(bool). [CF] smartctl: Allow '-d test' in conjunction with other '-d TYPE' options. [AS] FreeBSD: sync init script with one from ports repository. [CF] drivedb.h updates: - SandForce Driven SSDs: ADATA S510 - JMicron based SSDs: Toshiba THNSNC128GMLJ - Samsung based SSDs: 830 Series - Hitachi Deskstar E7K1000 - Hitachi Ultrastar A7K1000: Fix name, allow trailing characters - Hitachi Ultrastar A7K2000: Remove duplicate entry - Toshiba 2.5" HDD MK..55GSX - Western Digital AV-GP: WD..EURS variants [CF] drivedb.h USB updates: - Buffalo MiniStation HD-PCTU2 (0x0411:0x01d9) (ticket #211) - Philips SDE3273VC/97 (0x0471:0x2021) (ticket #212) - Samsung M2 Portable 3.0 (0x04e8:0x60c5) - Iomega GDHDU2 (0x059b:0x0475) - LaCie minimus USB 3.0 (0x059f:0x104a) - Seagate FreeAgent GoFlex Desk USB 3.0 (0x0bc2:0x50a5) - Maxtor BlackArmor Portable (0x0d49:0x7550) - WD My Passport Essential SE USB 3.0 (0x1058:0x0742) - Initio (0x13fd:0x1e40) - Verbatim External Hard Drive 2TB (0x18a5:0x022a) - Hitachi Touro Desk (0x4971:0x1011) [CF] smartd: Add smartd.conf directive '-e' to set ATA settings on startup: aam, apm, lookahead, security-freeze, standby, wcache. [CF] drivedb.h updates: - SandForce Driven SSDs: Corsair Force GT - Indilinx Barefoot based SSDs: Corsair Nova - SAMSUNG SpinPoint M8 - Seagate SV35.5 [CF] smartctl: Change short option for '--set' from '-e' to '-s'. Keep backward compatibility with short option for '--smart'. [CF] smartctl: Print description of APM level. [CF] smartctl: Add option '-e standby,[N|off|now]' to set standby timer or standby mode. [CF] smartctl: Add options '-g security' and '-e security-freeze' to get/freeze ATA security settings. [CF] smartctl: Add options '-g/e lookahead' and '-g/e wcache' to get/set read look-ahead and write cache feature. [CF] smartctl: Add options '-g aam' and '-e aam,[N|off]' to get/set ATA Automatic Acoustic Management feature. Add '-g all'. [CF] os_win32.cpp: Prevent warnings from gcc option -Wformat-security. [CF] smartctl: Add options '-g, --get apm' and '-e, --set apm,[N|off]' to get/set ATA Advanced Power Management feature. Original patch was provided by Marcus Sorensen. [AS] os_freebsd.cpp - do not skip ATA devices from cam list. Starting from FreeBSD 9.0 such devices are exported ONLY as camdev`s, so DEVICESCAN was broken. Its possible to get duplicates now on some old systems. [CF] drivedb.h updates: - SandForce Driven SSDs: Add OCZ Solid 3, OCZ Deneva 2 C/R - Seagate Momentus 5400.7 [CF] Happy New Year! Update copyright year in version info. [CF] drivedb.h updates: - SandForce Driven SSDs: Add Patriot Pyro - Intel 320 Series SSDs: Fix 40GB - Seagate Barracuda XT: Add 4TB [CF] drivedb.h updates: - SandForce Driven SSDs: Add Corsair Force 3 - Hitachi Travelstar 5K320: Add SA00 and SA02 models - Western Digital Caviar SE SATA: Add 300GB [CF] Cygwin smartd: Remove SIGQUIT workaround, no longer needed with current Cygwin tty emulation. [CF] smartd: Disable auto standby also after start of scheduled self-test. [CF] smartd: Add smartd.conf DEFAULT directive. Allows to set default settings for multiple devices. [CF] smartd: Re-enable auto standby if smartd.conf is re-read. [AS] drivedb.h update: Seagate Barracuda (SATA 3Gb/s, 4K Sectors) [AS] drivedb.h update: Seagate Constellation ES.2 (SATA 6Gb/s) [CF] drivedb.h updates: - Sandforce Driven SSDs: Add OCZ Vertex 3 Max IOPS (ticket #209) - Seagate ST1.2 CompactFlash (found in ticket #125) [CF] Fix GPL version reported by '-V' option. Now reports GPLv2+ which is consistent with file headers. Patch was provided by Stanislav Brabec. [CF] drivedb.h updates: - Sandforce Driven SSDs: Add OCZ Deneva 2 Async variant, 60GB, 480GB - Indilinx Martini based SSDs: OCZ VERTEX-PLUS only [CF] smartd: Add '-l offlinests,ns' and '-l selfteststs,ns' directives. dev_interface: Add smart_interface::disable_system_auto_standby(). os_win32.cpp: Implement disable_system_auto_standby(). [CF] dev_interface: Let smart_interface::set_err() return false. [CF] drivedb.h updates: - SAMSUNG SpinPoint M8U (USB) - Toshiba 3.5" HDD MKx002TSKB: Fix typo [CF] smartctl: Print average temperature from SCT status only if value is reasonable. Field is not part of ATA-8. [CF] smartd: Report ignored '-r' and '-R' directives. [CF] smartctl: Use 16-bit value (ATA-8) for extended self-test polling time if 8-bit value is 0xff (ticket #207). [CF] drivedb.h updates: - SandForce Driven SSDs: Add OCZ-REVODRIVE3, OCZ Z-DRIVE R4 - Hitachi Travelstar Z7K320 - Toshiba 2.5" HDD MK..56GSY - Toshiba 2.5" HDD MKx002TSKB - Seagate U9 - Seagate U*: sort entries, unify names, remove duplicate - Seagate Constellation ES (SATA 6Gb/s) - Seagate DB35 - Seagate DB35.2 - Western Digital Scorpio Black: Add 500GB - Western Digital Scorpio Black (Adv. Format) [CF] drivedb.h USB updates: - Samsung S2 (0x04e8:0x1f05) - Toshiba Stor.E (0x0939:0x0b16) (ticket #206) - Seagate FreeAgent (0x0bc2:0x5040) - Initio/Thermaltake BlacX (0x13fd:0x0840) [DG] [SCSI] smartd: skip non-storage devices (e.g. SES devices) [AS] drivedb.h updates: Added Seagate SV35 Series [CF] smartctl: Don't start ATA self-test if another test is already running (ticket #40). Add option '-t force' to allow override. [CF] atacmds.h: Remove bogus ataSmart*Test*() prototypes. [CF] Define __attribute_format_printf() for functions with printf() style arguments. Allow MinGW build with __USE_MINGW_ANSI_STDIO enabled. [CF] Makefile.am: Replace sed compound command in MAN_FILTER. This fixes build on Solaris (ticket #203). [AS] os_freebsd.cpp: Dereference symlinks before guess of device type (problem reported by email). [CF] drivedb.h USB updates: - LG Mini HXD5 (0x043e:0x70f1) - Freecom/Intel (0x07ab:0xfc8e) - Dura Micro (0x0c0b:0xb001) (Debian bug 643928) - Initio 6Y120L0 (0x13fd:0x1150): unsupported [CF] drivedb.h USB update: - Seagate FreeAgent GoFlex Desk USB 3.0 (0x0bc2:0x50a1): Revert to -d sat,12 (ticket #151). [AS] os_freebsd.cpp - fixed crash on FreeBSD9-RC1 caused by r225950 [AS] smartctl.8 - added information about -d areca on FreeBSD [AS] os_freebsd.cpp: backport quirks for the LSI controllers with SATA disks to the FreeBSD. Tested with DELL Perc/6i controller. [AS] os_freebsd.cpp: disable SAT autodetection on megaraid controllers [AS] drivedb.h update: - Hitachi Ultrastar 7K2000 [CF] drivedb.h update: - Seagate Momentus XT: Add bug warning for firmware SD24 and SD25 [CF] Don't include pkg-config macros in aclocal.m4, copy to m4/pkg.m4 instead. Allow builds from SVN without pkg-config installed but prevent 'make dist' when pkg-config support is missing. [CF] Move automake --foreign option from autogen.sh to configure.in. This fixes autoreconf support. [CF] Replace COPYING file with current (2010-03-24) version from http://www.gnu.org/licenses/gpl-2.0.txt smartmontools 5.42 2011-10-20 [CF] Windows installer: Add install dir to PATH in CMD shortcut. [CF] drivedb.h updates: - SAMSUNG SpinPoint MP5 - Seagate Barracuda 7200.11: Change warning text, Seagate apparently released fixed firmware without changing version number (Debian bug 632758) - Western Digital RE4 GP - Western Digital VelociRaptor: Add 150GB, 300GB LHX variants - Western Digital Scorpio Blue Serial ATA (Adv. Format): Add 1TB JPVT variant [CF] drivedb.h USB update: - WD Elements SE USB 3.0 (0x1058:0x1042) [CF] Windows installer: Rework to support UAC. Replace *-run.bat files by runcmd?.exe wrappers. Run drive menu entries elevated (ticket #173). [CF] smartctl.8.in: Add example script which prints all status bits (ticket #191). [CF] Cygwin smartd: Remove '--service' option, update man page. [CF] smartd: Require absolute path name also for '-p' option. Allow relative path names for '-A', '-s', '-p' in Windows version only. [CF] smartd: Log model family from drive database if known. [CF] drivedb.h update: - SMART Xcel-10 2.5 SATA SSD: Shorten names, document supported default attributes. [CF] smartctl -P showall: Report error if attribute name is too long. [AS] freebsd: use system ciss header if available, it is added to the base system by recent commit. [CF] smartd.conf.5.in: Update Windows 'msgbox' info. Add missing IF/ENDIF for Solaris and Windows. [CF] man pages: Remove reference to T13 web site. It does no longer provide links to the ATA documents. [CF] smartctl: Replace '-t scttempint,N[,p]' option by '-l scttempint,N[,p]'. [CF] drivedb.h USB update: - Oxford (0x0928:0x0000): unsupported, see https://bugs.freedesktop.org/show_bug.cgi?id=24951 [CF] Minor cleanup to prevent warnings from new gcc 4.6 options -Wunused-but-set-parameter/variable. [CF] Windows smartd: Fix format string for 64-bit version. [CF] Remove EXPERIMENTAL notes for features already present in 5.40. [CF] smartctl: Add new log addresses from ACS-3 revision 1. [CF] smartctl: Print ATA ACS-x versions properly (ticket #183). [CF] smartctl: Add option '-l devstat[,PAGE]', print ATA Device Statistics log pages (ticket #106). Thanks to David Boreham for providing access to a machine for testing. [AS] man pages: trivial man page syntax fixes (ticket #199) [CF] drivedb.h update: - SMART Xcel-10 2.5 SATA SSD: Fix syntax error (ticket #200) [AS] drivedb.h update: - SMART Xcel-10 2.5 SATA SSD [DG] [SCSI] document 'ssd' list option in man page and smartctl usage. [CF] Windows: Fix device type detection for Intel ICHxR RAID Volumes. [CF] smartd: Resend warning emails if problem reappears (ticket #167). [CF] smartd: Add separate directives '-l offlinests' and '-l selfteststs' to enable tracking of status changes. Disable '-l offlinests' by default to avoid misleading messages (see Debian bug 636078). [CF] drivedb.h updates: - Crucial/Micron RealSSD C300/C400: Add m4 series (ticket #192) - SandForce Driven SSDs: Add OCZ-AGILITY3 - Indilinx Barefoot based SSDs: Add RENICE Z2 - Intel 710 Series SSDs [CF] Windows smartd: Fix quoting of service command line. [CF] Cygwin smartd: Remove FreeConsole() after fork(). No longer needed for recent versions of Cygwin DLL. [CF] smartd: Add some sleep() time after machine standby mode. Some drivers (Intel ICHxR Windows driver) report failures if pass-through is accessed immediately after wake up. [AS] -d hpt on linux/freebsd - increased max channel number to 16, fixed documentation. (see http://permalink.gmane.org/gmane.linux.utilities.smartmontools/7846) [AS] os_linux.cpp - disabling SMART WRITE LOG SECTOR command on megaraid interface for SATA disks. [AS] os_freebsd.cpp: -l scterc was broken on FreeBSD, fixed for atacam and ata drivers (bug #198). [CF] drivedb.h updates: - Crucial/Micron RealSSD C300/C400: Add C400 - SandForce Driven SSDs: Add Kingston HyperX, OCZ-REVODRIVE, OCZ Deneva 2 - Intel X18-M/X25-M/X25-V G2 SSDs: Add 120GB - Hitachi Travelstar 7K200: Match capital letters also - Hitachi Ultrastar 7K3000 - Seagate Barracuda Green: Add ST2000DL001-* (ticket #195) - WD My Passport Essential SE: Add WD10TMVW-* [CF] drivedb.h USB updates: - Seagate FreeAgent GoFlex USB 3.0 (0x0bc2:0x5071) (ticket #195) - Seagate FreeAgent GoFlex Desk USB 3.0 (0x0bc2:0x50a1): Enable -d sat,16 (ticket #151). - Oyen Digital MiniPro USB 3.0 (0x0dc4:0x020a) (ticket #193) - WD My Passport Essential SE USB 3.0 (0x1058:0x0740) [CF] Windows: Add MSVC10 support, remove MSVC8 project files. [DG] [SCSI] smartctl output Solid State Media (SSD) percentage used endurance indicator. Add '-l ssd', useful for SATA SSDs? [CF] atacmds.cpp: Rework search for temperature min/max values in attributes 190/194. This fixes temperature tracking for recent WDC drives. [CF] drivedb.h USB updates: - LaCie rikiki USB 3.0 (0x059f:0x1057) - Freecom Mobile Drive XXS (0x07ab:0xfc88) - WD Elements SE (0x1058:0x1023) [CF] drivedb.h updates: - Indilinx Barefoot based SSDs: Add G.Skill Falcon - JMicron based SSDs (JMF61x): Add Kingston SSDNow V100 Series - Transcend CompactFlash Cards: Add 8, 16GB - Toshiba 1.8" HDD MD..29GSG - SAMSUNG SpinPoint M7U - Western Digital Caviar Green (Adv. Format): Add SATA 6Gb/s variants - Western Digital My Passport USB: Shorten names [DG] [SCSI] smartd initial log entry for each drive now shows INQUIRY strings and optionally the LU (logical unit) id and capacity [AS] os_freebsd.cpp: fixed return type in autodetect_smart_device. [CF] drivedb.h USB updates: - WD My Book Essential (0x1058:0x0910, Debian bug 633724) - Atech (0x11b0:0x6298) [CF] drivedb.h update: - Seagate Barracuda ES.2: Add Dell firmware versions (ticket #189) [CF] drivedb.h updates: - Seagate Maxtor DiamondMax 21: Add STM380215AS - Seagate Barracuda 7200.12: Add ST3250312AS, ST31000524AS - Toshiba 2.5" HDD MK..50GACY - Toshiba 2.5" HDD MK..76GSX [AS] smartd.8 - removed configuration file information from this manual, added reference to smartd.conf.5. [AS] smartd.conf.5 - added more platform-specific sections, corrected "areca" device information, corrected sample configuration. [AS] os_freebsd.cpp: detecting access to /dev/mfidX devices to show help (#97) [CF] Update configure options in INSTALL file, remove outdated info. [CF] int64.h: Remove outdated uint64_to_double() workaround for MSVC6. [CF] os_win32/update-smart-drivedb.nsi: Add support for /S(ilent) option. [CF] configure.in: Don't search for initddir and systemdsystemunitdir when cross-compiling. [CF] Makefile.am: Use same syntax also for ENABLE_* man page sections. [CF] Add experimental support for platform-specific man pages. [CF] Windows: Move '-I os_win32' from configure.in to Makefile.am. [CF] configure.in: Fix check for __attribute__((packed)). [CF] drivedb.h USB update: - Verbatim Portable Hard Drive (0x18a5:0x0214) [CF] drivedb.h update: - SandForce Driven SSDs: Add OWC Mercury Extreme Pro RE (ticket #168) [CF] os_linux.cpp: Let MegaRAID autodetect_open() fail for SATA devices. MegaRAID SAT layer has serious bugs as reported by AS. [AS] os_freebsd.cpp: Implement 48bit support for the new "atacam" interface. Tested on FreeBSD 8.2 and works fine. [CF] os_win32.cpp: Fix USB ID detection if two devices with the same name exist (ticket #178). [AS] os_freebsd.cpp: including ciss headers to the base, we can not rely on the header sources in the build time. Also this file was changed last time > 2 yrs. ago and it is unlikely that it will be changed in the feature. This will fix FreeBSD PR 150235. [AS] drivedb.h update: Added Samsung Story Station 3.0 USB. [AS] os_linux.cpp: Areca code converted to the new interface. Patch is based on os_freebsd.cpp patch and is not tested yet. [AS] os_freebsd.cpp: Areca code converted to the new interface. [AS] os_freebsd.cpp: Added support for the Areca RAID controllers. Support is basesd on Linux code, but using IOCTL on areca control device instead of SCSI commands to talk with the drives. Hardware access was provided by Andrej Binder. [CF] Don't use isprint() for ASCII character check as it may be affected by setlocale(). [AS] os_freebsd.cpp: Remove all referenced to the FreeBSD 5.0. It is unsupported for a very long time and probably will not compile and work anyway. Also this will fix bug #154. smartmontools 5.41 2011-06-09 [MS] drivedb.h: revert attribute 190 to default for Samsung SSD controllers, some 470 series SSDs seem to have some temperature information at this location. [MS] drivedb.h update: add attribute details for Samsung controllers, centralize entries [MS] drivedb.h update: add attribute details for JMicron JMF61x controllers [CF] drivedb.h update: - SandForce Driven SSDs: Add OCZ DENEVA [CF] os_win32.cpp: Ignore vendor ID "ATA" if returned by IOCTL_STORAGE_QUERY_PROPERTY. [CF] Add ATA NCQ commands to error register decoding. [CF] Re-enable '--with-initscriptdir=auto' as default. Change search for initddir and systemdsystemunitdir such that default ./configure does never overwrite system files. [MS] drivedb.h update: disentangle Transcend SSD versions [MS] drivedb.h update: add attribute details for Crucial C300 [MS] smartd.initd.in: fix for debian, cleanup. Based on patch of CF. [AS] --with-initscriptdir default changed to "no" from "auto" to avoid filesystem pollution. [MS] drivedb.h cleanup: harmonize family names, add AF information into name [MS] drivedb.h update: - OCZ Vertex 3 - Seagate Barracuda Green 1TB variant [CF] Windows: Avoid '%n' printf format specifier because it is always disabled in recent versions of msvcrt.dll. This fixes truncation of smartd warning email (ticket #174). [MS] smartd.initd.in: cleanup, provide targets "reload" and "report" for all platforms [CF] drivedb.h update: - JMicron based SSD (JMicron JMF602?): rename from Kingston SSDNow V Series, move Transcend IDE and SATA entries to here. [CF] Support ':BYTEORDER' for all attribute print formats. [CF] drivedb.h update: - Kingston SSDNow V Series SSDs (ticket #171) [CF] Increase size of drive database option parse buffer to allow long '-v N,FORMAT:BYTEORDER,NAME' options. [MS] drivedb.h update: - Western Digital Scorpio Blue Advanced Format variants [MS] drivedb.h update: correct typo for Cowon iAudio X5 [MS] drivedb.h USB updates: - Maxtor OneTouch 200GB (unsupported) - LaCie Little Disk [AS] FreeBSD: Added native rc.conf style script to the package. Modifications to the configure script to use correct template and path. [AS] freebsd_os.cpp: Fix memory leak in the ata detection code (added free()) Using bzero in cam code to clear structure (fixing varnish varning) [MS] drivedb.h update: Kingston SSDNow S100 Series [MS] drivedb.h USB update: - Samsung S1 Portable - LaCie rikiki USB 3.0 - Seagate FreeAgent GoFlex USB 3.0 - Cowon iAudio X5 - Oxford OXU921DS chip (unsupported) [CF] Windows: Add debug output of SCSI sense data. [CF] Add 'smartd.service' file for systemd. Add configure option '--with-systemdsystemunitdir'. Disable initd script if systemd is used. [MS] drivedb.h update: - Western Digital AV-25 family [MS] drivedb.h update: JMicron based SSDs: Add Kingston SSDNow V, Kingston SSDNow V+100, TOSHIBA THNS128GG4BBAA, APPLE SSD TS*, ADATA S596 Turbo [CF] drivedb.h update: - Intel 510 Series SSDs (ticket #170) [CF] smartctl: Don't issue SMART DISABLE command to 3ware controllers when the port number was not specified (ticket #165). [CF] Use get_errmsg() from device instead of errno or syserror() for printing error messages. [MS] drivedb.h updates: - G.Skill FALCON II SSD (Indilinx) - HP 250GB SATA disk VB0250EAVER - SAMSUNG SpinPoint M5 HM160HC - SAMSUNG SpinPoint MT2 HM100UI - SAMSUNG HM100UX - Hitachi Deskstar 5K3000 Series - Seagate Barracuda Green (Adv. Format) - Seagate Barracuda XT 3TB variant - Western Digital RE4 Serial ATA family - Western Digital Caviar Green WD20EACS - Western Digital Caviar Black family, SATA 3.0 variants - QUANTUM FIREBALLlct20 10 - QUANTUM FIREBALLP AS60.0 [CF] drivedb.h update: - SandForce Driven SSDs: Add more OCZ SF-1200 and SF-1500 based drives Thanks to Sudhir Verman from OCZ Technology for providing this info. [CF] drivedb.h USB updates: - Seagate Expansion External (0x0bc2:0x3300) (Debian bug 621411) - ASMedia USB 3.0 (0x174c:0x55aa) (unsupported) [CF] smartctl.8.in: Clarify '-t vendor,N' (ticket #169). Update Intel info (ticket #168). [CF] drivedb.h update: - Intel 320 Series SSDs (ticket #168) [CF] smartctl: Always print sector size in '-i' output (ticket #166). [CF] os_linux.cpp: Shorten version string. [CF] smartctl: Add option '-f brief' to select new attribute output format. This format includes additional attribute flags (ticket #109) and fits in 80 columns (ticket #158). This format is now the default for '-x'. [CF] smartd: Log changes of offline data collection status if '-l selftest' is specified. [CF] drivedb.h updates: - SandForce Driven SSDs: Add ADATA S599 64GB, OWC Mercury Extreme Pro - Kingston branded X25-V SSDs (ticket #156) - Transcend SATA Solid State Drive: Truncate attribute name [CF] drivedb.h USB updates: - LaCie (0x059f:0x1029) (ticket #153) - WD My Book Office Edition (0x1058:0x1101) - JMicron USB 3.0 (0x152d:0x0539) [CF] drivedb.h USB update: - Verbatim Pocket Hard Drive (0x18a5:0x0227) (ticket #159) [CF] drivedb.h update: - SAMSUNG SpinPoint N3U-3 (USB, 4KiB LLS) (ticket #159) [CF] Add support for ATA Long Logical Sectors (LLS) (ticket #159). [DG] [SCSI] smartctl: (re-)use capacity formatting in utility.cpp [CF] configure.in: Remove '-Wno-format' for MinGW. Recent MinGW versions support MSVCRT printf format strings. [CF] Print ATA disk capacity with SI prefix. Add/move capacity formatting to utility.cpp [CF] Add error messages if ATA pass-through does not return required ATA output registers (for SMART RETURN STATUS, GET POWER MODE). This prevents misleading 'SMART Status command failed' messages (see ticket #155). [CF] Fix WWN support check for older ATA-7 disks. [DG] [SCSI] smartctl: add 'Logical Unit id' from the Device Identification VPD page (0x83) [DG] [SCSI] smartctl: add 'User Capacity' (disk size) in human readable form [CF] smartctl, smartd: Print World Wide Name (WWN) of ATA device. [CF] smartctl: Print more specific error message if IDENTIFY DEVICE failed (ticket #61). Add check for empty IDENTIFY data. [CF] Windows installer: Add help message box. [CF] Windows installer: Request admin rights, select 'All Users' section. This fixes shortcut removal under Vista and later. Add '/SO' option to select components for unattended install. Patch was provided by József Fejes. [CF] Windows: Add update-smart-drivedb.nsi NSIS script to build drivedb.h update tool. [CF] Windows: Move search for NSIS compiler from Makefile.am to configure.in. [CF] update-smart-drivedb.in: Move DRIVEDB_BRANCH name creation from script to configure.in. [CF] os_linux.cpp: Replace printf() by pout(). Disable unused function dumpdata(). [CF] Windows: Include CSMI (for Intel RAID) in default DEVICESCAN. [CF] configure.in: Remove info messages about old defaults. [CF] drivedb.h: Set unneeded USB bcdDevice patterns to empty. [CF] Rework USB ID drivedb search. Stop search at first matching entry with empty bcd_device pattern. [CF] Move handling of '-F swapid' from formatting to identity read function. Remove unneeded 'fix_swapped_id' parameters. [CF] smartd: Log warning from drive database if present. smartctl: Do not search drive database twice. [MS] drivedb.h USB updates: - Samsung S2 Portable variant (0x04e8:0x1f08) - Lacie rikiki (0x059f:0x102a) - Toshiba Stor.E Steel series (0x0930:0x0b11) - Super Top generic enclosure (0x14cd:0x6116) [CF] Let constructor of regular_expression throw on error by default. [CF] smartd: Preserve last selective self-test span in '.state' file and use it if the selective self-test log was cleared (ticket #88). [CF] smartctl --scan-open: Make output compatible with smartd.conf (ticket #108). Fix possible crash if autodetect_open() returns new object. [CF] do_release: Re-add signing of tarball. [CF] os_linux.cpp: Change '-d sat' to '-d sat,12' for USB only if kernel is older than 2.6.29. Add kernel release to version info. [CF] smartd: Add '-l scterc,READTIME,WRITETIME' directive (ticket #150). [CF] smartctl: Fix exit status of '-l xerror' and '-l xselftest' (ticket #144). [CF] smartd: Use '-M daily' as default if state persistence is enabled. This avoids that emails are suppressed forever (ticket #35). [CF] smartd: Log identify information of each ATA device. [CF] smartd: Disable '-C' and '-U' monitoring if raw values are very large (ticket #148). [CF] smartd: Write reserved attribute byte to '.state' file (ticket #118). [MS] drivedb.h USB updates: - Seagate FreeAgent Go Flex Desk USB 3.0 - Toshiba Canvio 500GB [MS] drivedb.h USB updates: - Freecom HD 500GB (0x07ab:0xfcda) - Generic JMicron adapter (0x152d:0x2337) - RaidSonic ICY BOX IB-110StU3-B (0x1759:0x500[02]) - Connectland BE-USB2-35BP-LCM (0x040d:0x6204) - Freecom Classic HD 120GB (0x07ab:0xfccd) - OCZ THROTTLE OCZESATATHR8G (0x152d:0x0602) - Vantec NST-400MX-SR (0x1a4a:0x1670) - Intenso Memory Station 2.5" (0x13fd:0x1840) [CF] Don't report failed self-tests outdated by a newer successful extended self-test as errors (ticket #147). This affects smartctl exit status and smartd syslog output and warning email. Only implemented for ATA. [CF] os_linux.cpp: Don't use buffer of size PATH_MAX for the result of realpath(). This also fixes compilation on Debian Lenny. [CF] smartd man pages: Add some missing [ATA only]. [CF] os_linux.cpp: Dereference symlinks before guess of device type (ticket #146). Minor rework of autodetect_smart_device(). [CF] smartctl -l scterc: Don't get ERC if only set is requested. This prevent misleading error messages if ATA output registers are not supported. [CF] Windows: Prevent warnings from gcc 4.5.1. [CF] os_netbsd.cpp, os_openbsd.cpp: Add missing [CF] os_freebsd.cpp: Add missing [CF] dev_legacy.cpp: Add missing [CF] Linux megaraid: Fix pass-through of non-data ATA commands (ticket #149). Only reject commands which require ATA output registers. [CF] configure.in: Remove '-fno-strict-aliasing' from CXXFLAGS. This reverts r2992 (see ticket #23). [CF] Linux megaraid: Avoid strict-aliasing warnings. Patch was provided by Stanislav Brabec (2009-06-03). [MS] Make functions without prototypes static. [MS] Remove unnecessary includes, move inclusion of errno.h from scsicmds.h to the appropriate *.cpp files. Add cciss.h to cciss.cpp. [MS] os_linux.cpp: rename variables to please "-Wshadow" utility.cpp: remove unused variable "start" os_win32/syslogevt.c: plug resource leak [CF] Rename variables to prevent warnings if '-Wshadow' is set. Remove unnecessary includes. Fix some comments. [CF] drivedb.h updates: - Intel X18-M/X25-M/X25-V G2 SSDs: Add firmware bug warning - Samsung SpinPoint M6 - Samsung SpinPoint M7E (AFT) - Samsung PM800 SSDs - Samsung PM810 (470 series) SSDs [CF] Windows: Add experimental CSMI support for disks behind Intel Matrix RAID driver. Accessed through new device names '/dev/csmi[0-9],N'. Experimental DEVICESCAN can be enabled by '-d csmi'. [MS] - ataprint.cpp: adjust print format for insanely large offline data collection times (e.g. WD drives). - getopt: change config.h #include format from angle brackets to quotes [MS] drivedb.h update: - Fujitsu MJA2 BH series - Toshiba MK..59GSXP series (Adv. Format) - Toshiba MK..59GSM series (Adv. Format) - Western Digital Caviar Blue SATA 3.0 variants - Seagate Barracuda XT [CF] smartctl: Print help message if no option is specified (ticket #39). Don't issue any other ATA command if only '-n POWERMODE' is specified. [CF] smartd: Output multiple lines via separate syslog(3) calls (ticket #135). [CF] smartctl: Add new ATA minor revisions and log addresses from ACS-2 revision 4a. Replace runtime asserts by compile time asserts. [CF] smartd: Remove "default: /var/log/messages" hint from warning mail. This obsoletes Debian patch 60_remove-redhatism.diff. [CF] Windows: Include USB devices in DEVICESCAN (ticket #116). [CF] Windows: Use direct WMI access to detect USB IDs (ticket #115). This replaces 'wmic' runs and speeds up USB detection. [CF] configure.in: Rework platform-specific settings. [CF] configure.in: Remove some no longer used settings: -lselinux (duplicate), NEED_SOLARIS_ATA_CODE, OS_FREEBSD. [CF] Makefile.am: Remove SUBDIRS. Recursive targets are no longer used. [CF] Use log directory to check for old error and self-test log support (ticket #89). [CF] drivedb.h USB update: - WD My Book Essential 3TB USB 3.0 [CF] Fix usb header includes for DragonFly BSD (ticket #141). [CF] smartctl: Print physical and logical sector sizes (ticket #62). [CF] drivedb.h updates: - Fujitsu MHT: Add AC variant - Fujitsu MHW2 AC - Samsung SpinPoint T166: Needs '-v 197,increasing' - Seagate Barracuda 7200.11: Add firmware SD81 as buggy - WD Scorpio Blue EIDE: Add 320GB [CF] drivedb.h USB updates: - Samsung S2 Portable (ticket #136) - Move Verbatim 0x152d:0x2351 to JMicron section [AS] drivedb.h updates: - Verbatim Portable Hard Drive eSATA & USB 2.0 Combo 500GB [CF] Happy New Year! Update copyright year in version info. [CF] drivedb.h updates: - Hitachi Deskstar 7K3000 - Hitachi Travelstar 7K320: Add ...362 variant - Seagate Maxtor DiamondMax 21: Add STM3250310AS - Toshiba 2.5" HDD MK..65GSX - WD Caviar Green (Adv. Format): Add 750GB, 2.5TB, 3TB [CF] drivedb.h USB updates: - Micron USB SSD (unsupported, ticket #133) - Samsung G2 Portable (ticket #132) - Samsung Story Station 3.0 (ticket #130) - Seagate FreeAgent GoFlex (ticket #131) [CF] update-smart-drivedb.in: Add workaround for OpenBSD shell bug: 'set -e; if eval false; ...' aborts script (ticket #128). [CF] update-smart-drivedb.in: Add platform specific download tools: 'fetch' on FreeBSD (ticket #127), 'ftp' on OpenBSD. [CF] drivedb.h USB updates: - JMicron 0x152d:0x2509 - WD My Passport 0730 [CF] drivedb.h updates: - Samsung SpinPoint F3 EG: Add 2TB - SandForce Driven SSDs: Add ADATA S599, SuperTalent TeraDrive CT - Seagate Constellation (SATA) - Seagate Constellation ES (SATA) - WDC My Passport: Add WD5000BMVW [CF] drivedb.h update: - Samsung SpinPoint F4 EG: Add 1.5TB, update firmware bug warning. [DG] [SCSI] Fix log page sanity check problem if the DS bit set in response. Caused '-l background' to fail. [CF] drivedb.h updates: - Samsung SpinPoint F4 EG: Warning about bad blocks [CF] update-smart-drivedb.in: Replace ERE by BRE. Script does no longer require GNU sed (Ticket #126). [DG] In '-r ioctl' show vendor specific SCSI commands as such rather than 'unknown'. [CF] Add check for CompactFlash Signature in ATA IDENTIFY data. This avoids that older CF microdrives are detected as ATAPI devices (Ticket #125). [CF] drivedb.h updates: - Apple SSDs TS* - Crucial RealSSD C300 Series - Kingston SSDNow V Series - Indilinx Barefoot based SSDs: Add OCZ-ONYX - SandForce Driven SSDs: Add OCZ VERTEX2-PRO - Transcend CompactFlash Cards: Add TS4GCF133 [CF] Windows installer: Add missing quotes in smartctl-run.bat and smartd-run.bat (Ticket #124). [CF] OpenBSD: Fix DEVICESCAN for OpenBSD >= 4.8 (Ticket #123). [CF] daemon_win32.cpp: Remove duplicate assignment (Ticket #120). [CF] Makefile.am: Do not overwrite existing smartd.conf file (Ticket #122). If smartd.conf exists and differs from the default then smartd.conf.sample is installed instead If smartd.conf.sample exists on uninstall then smartd.conf is preserved. [CF] Linux megaraid: Fix segfault on non-data commands (Ticket #78). The /dev/megaraid_sas_ioctl_node driver does not allow sge_count = 1 and sgl[0].iov_len = 0. [CF] Remove EXPERIMENTAL notes for features already present in 5.39. [CF] Rework '-d TYPE' documentation on man pages. [CF] drivedb.h updates: - Seagate Maxtor DiamondMax 21: Add 80GB - Western Digital Caviar Black: Add 1TB/64MB [CF] drivedb.h USB updates: - iRiver iHP-120/140 (Ticket #119) - ASMedia ASM1051 [CF] Makefile.am: Handle examplescripts in main Makefile. Remove 'examplescripts/Makefile.am'. [CF] configure.in: New option '--with-exampledir' allows to change path of 'DOCDIR/examplescripts' directory. (Debian package uses '/usr/share/doc/smartmontools/examples') [CF] Replace global 'con->dont_print/...' variables by 'printing_is_*'. Remove global 'con'trol pointer. Remove file 'extern.h'. [CF] Replace global 'con->reportata/scsiioctl' variables by '*_debugmode'. [CF] Replace global 'con->conservative/permissive' variables by 'failuretest_*'. Move failuretest() function to smartctl.cpp. [CF] Remove unused CONTROLLER_* defines. [CF] Remove unused controller support from dev_legacy adapter module. [CF] Make 'debugmode' variable local to smartd.cpp. smartmontools 5.40 2010-10-16 [CF] examplescripts/Example3: Use stdin to pass message to 'wall' command (ticket #114). [CF] smartd: Fix setting of SMARTD_DEVICE and SMARTD_DEVICETYPE environment variables (ticket #113). Regression was introduced by rework of smartd data structures. SMARTD_DEVICE is now set to the plain device name. SMARTD_DEVICETYPE is now set to 'auto' if no '-d' directive is specified. Smartctl now accepts '-d auto' for this purpose. [CF] Remove "Lifetime" from Min/Max temperature attribute output (ticket #111). Interval is device specific. [CF] configure.in: Print resource/message compiler info for Windows only. [CF] FreeBSD: Rework get_dev_names_cam() to support more than 26 devices. [CF] drivedb.h updates: - Seagate Barracuda 7200.10: Add 360GB - USB: Iomega MDHD-UE Patch provided by Rob Marissen. [DL] Standby mode not detected properly on FreeBSD (ticket #91). [MS] os_linux.cpp: fix "gcc -flto" build error by including stddef.h [CF] drivedb.h update: - Indilinx Barefoot based SSDs: Add OCZ-VERTEX 1199 and -TURBO [CF] TODO file: Move open entries to tickets #106, #107, #108, #109, #110. Remove outdated entries. [CF] drivedb.h USB update: - SunPlus 0x04fc:0x0c05 [CF] drivedb.h update: - SandForce Driven SSDs: Add Corsair Force, fix typo [CF] Print hex values of unknown self-test type or status. [CF] drivedb.h updates: - SandForce Driven SSDs: Fix regex for Unigen UG99SGC - Seagate Momentus XT series - Quantum Bigfoot: Add 12.7GB [CF] drivedb.h updates: - SandForce Driven SSDs: Add 11 attributes of new FW, add Unigen UG99PGC - WD AV ATA family: Add 250GB, 320GB - WD AV SATA family [CF] Windows: Build syslogevt.exe with MinGW. Now possible because binutils provides windmc. [CF] Makefile.am: Remove install message about smartd startup. It might be misleading because it is not correct for all platforms. [CF] configure.in: Minor fix of '--enable-drivedb' new defaults detection. [CF] Update links, configure and OS info in INSTALL file. Replace tabs by spaces. [CF] configure.in: Fix '--enable-sample' and '--with-selinux'. Fix obsolete use of AC_DEFINE(). [CF] drivedb.h updates: - IBM Deskstar 60GXP, 40GV & 75GXP: Update link (ticket #99) - Seagate Barracuda 7200.12: Add ST31000523AS and others - WD Caviar Black: Add 2TB - WD VelociRaptor: Add 6 Gb/s models [CF] Windows installer: Fix smartctl-run.bat for drive menu (ticket #31). [CF] Windows: Create md5/sha1/sha256 checksums of the binaries. Add checksums.txt file to binary distribution. [CF] Windows: Include drivedb.h into binary distribution. [CF] drivedb.h updates: - Intel X18-M/X25-M/X25-V G2: Add X25-V 40GB - Transcend CompactFlash Cards [CF] drivedb.h updates: - Seagate Momentus 7200 FDE.2: Add ST9160414ASG - Seagate Pipeline HD 5900.1 and 5900.2 Based on patch provided by Marcin Falkiewicz. [CF] Remove unused variable 'reportbug'. [CF] Make function PrintOut() local to smartd.cpp, remove it from smartctl.cpp. [CF] Windows: Improve compatibility with MinGW variants. Add configure check for DDK include files. Drop support for '-mno-cygwin' from old Cygwin gcc. [AS] smartctl.8.in minor update: adding FreeBSD ahci/scsi device hints [CF] Fix build if SVN Id keywords are not expanded (ticket #94). [CF] Windows: Remove "." from DLL search path to prevent DLL preloading attacks. [CF] drivedb.h USB update: - JMicron 0x152d:0x0551 (ticket #95) Add note about port multipliers to smartctl man page. [CF] drivedb.h updates: - SandForce Driven SSDs: Add Unigen drives - Indilinx Barefoot based SSDs: Add ASAX Leopard Hunt II [CF] drivedb.h update: - Intel X18-M/X25-M G2: Add names of timed workload attributes. Document attribute clear command '-t vendor,0x40' on smartctl man page. Thanks to Artem Danielov from Intel for providing the required information and drives for testing. [CF] drivedb.h update: - SandForce Driven SSDs: Add OCZ drives with form factor info. [CF] drivedb.h update: - Intel X25-E, X18-M/X25-M (add X18-M, update attributes) [CF] configure.in: '--enable-drivedb' is now the default. [CF] drivedb.h update: - Indilinx Barefoot based SSDs (combine and update 5 SSD entries using this controller) [CF] drivedb.h update: - SandForce Driven SSDs (Demo Drive, OCZ-Agility2/Vertex2/Vertex-LE) Thanks to Jeremy Werner (jwerner@sandforce.com) from SandForce for providing the required information and a demo drive for testing. [CF] drivedb.h update: - Add 1.5TB drive to SAMSUNG SpinPoint F3 EG series [CF] Add print formats '-v ID,msec24hour32' and '-v ID,raw24/raw32'. Used by SSDs with SandForce controller. [CF] Allow SMART threshold entries at positions different from attribute table. This fixes attribute output for recent SSDs with SandForce controller. [CF] smartctl: Add option '-t vendor,N' to issue ATA command SMART EXECUTE OFF-LINE IMMEDIATE with a vendor specific subcommand. [CF] drivedb.h update: - SAMSUNG SpinPoint V80 series (ticket #85) [CF] Linux: Support SATA drives on LSI 3ware 9750 controllers. Patch provided by Victor Payno (ticket #86). Modified to avoid duplicate code. [CF] drivedb.h update: - SAMSUNG SpinPoint M7 series [CF] drivedb.h USB update: - Buffalo JustStore Portable HD-PVU2 [CF] drivedb.h USB updates: - Iomega LDHD-UP (ticket #83) - WD Elements Desktop 2TB - Maxtor OneTouch (0x0d49:0x7300) [MS] drivedb.h updates: - Intel X25-M SSD first Generation - ExcelStor J8160 - OCZ Agility2 [CF] drivedb.h updates: - Transcend Solid State Drives (ticket #80) [CF] drivedb.h USB update: - LaCie Rugged Hard Drive [CF] smartctl: Add options '--scan, --scan-open'. [CF] Windows: Use also VendorId from IOCTL_STORAGE_QUERY_PROPERTY. [CF] smartd: Change defaults of '-C' and '-U' directives to 0 (disabled) if attribute name is changed by '-v 19[78],...' directive. [CF] configure.in: Fix include path for MinGW. [CF] Move 'posix/reg*' to 'regex/reg*'. Add configure check for regex. [MS] cciss.cpp: avoid redefining be32toh megaraid.h: replace use of undefined preprocessor macro BITS_PER_LONG by union construct (thanks to [DL]). Add assert for sizeof(ptr_t) == 8 (thanks to [CF]). [CF] Makefile.am: Add os_qnxnto.* to EXTRA_smart*_SOURCES. [MS] drivedb.h update: - WD My Passport Essential SE 1TB variant (USB interface) [CF] Use getopt_long() from getopt/getopt* if necessary. Add missing cast to os_qnxnto.cpp. This fixes build on QNX (ticket #1). Thanks to Stefan (stevestereo) for testing. [CF] drivedb.h update: - WD Caviar Green (Adv. Format) family [CF] drivedb.h USB update: - Verbatim External Hard Drive 47519 [DL] Fix regression in smartctl option '-t select,M-N' which prevents that more than one test span can be specified (ticket #75). [CF] drivedb.h updates: - Add raw64 attributes 1, 210-213 to all SSD drives with 64-bit attribute format. [CF] Support smartd '-l xerror' also for disks which use reserved byte as log index. [CF] Fix initialization of values missing in smartd '.state' files. [CF] Add smartd directive '-l xerror' to check error count from the Extended Comprehensive SMART Error Log (ticket #34). [CF] Fix max number of cciss devices, 128 devices are supported again (ticket #49). Regression was introduced during migration to new interface. [CF] Update man pages (include Debian patch 60_remove-redhatism.diff and Debian Bug 570892). [CF] Add SVN revision number to man pages. [CF] Windows: Read default drivedb.h and smartd.conf from exe directory instead of current directory. [CF] drivedb.h update: - SAMSUNG SpinPoint M series [CF] Replace runtime check of byte ordering by compile time check. [CF] drivedb.h USB updates: - ALi M5621 (unsupported) - LaCie with JMicron (ticket #69) - JMicron (0x2352) - Enable 48-bit commands for Hitachi drive [CF] Read USB ID info from drivedb.h (ticket #44). [CF] Create branch RELEASE_5_39_DRIVEDB with last drivedb.h file compatible with smartmontools 5.39[.1]. [MS] drivedb.h updates: - WD Raptor 80GB variant - correct Regex for some WD AV-GP variants - Hitachi Ultrastar A7K2000 - Hitachi Travelstar 5K500.B - Hitachi Deskstar 7K1000.C - adjust naming of Hitachi Travelstar and Deskstar drives [CF] Move 'posix/getopt*' to 'getopt/getopt*'. Can be used for platforms with regex() but without getopt_long() (QNX, ticket #1). [CF] smartd '-l selftest' directive: Print info if error count decreased. Avoid misleading warning if error count decreased to zero (ticket #67). [CF] smartctl: Rework ataPrintMain(). Issue ATA SMART commands only if necessary. Improve handling of SMART STATUS command failure when ATA output registers are missing (ticket #27). [CF] USB ID updates: - A-DATA SH93 - Hitachi/SimpleTech 1TB [CF] configure.in: Print configuration summary. [CF] smartctl -l xselftest,selftest: Print old log if extended self-test log index is out of range. Workaround for bad log data from Intel X25-M G2 (ticket #66). [CF] USB ID updates: - LaCie Desktop Hard Drive - Prolific PL2507 (unsupported) - Seagate FreeAgent Go FW - WD My Book Essential [CF] Linux: Add '/dev/sd[a-c][a-z]' to smartd DEVICESCAN. [CF] smartd: Other config entries may precede DEVICESCAN. Very first step towards a more flexible device scanning. [CF] Windows: Use '.win64' in names of 64-bit binary packages. Use correct 'strip' program when cross-compiling. [CF] Add update script to make targets 'dist' and 'clean', set +x permission, update svn:ignore. [CF] Add 'update-smart-drivedb' script (ticket #59). The script updates the drive database from SVN. It is installed if '--enable-drivedb' is configured. [MS] drivedb.h updates: - Seagate Medalist 1720 - SuperTalent UltraDrive GX SSD - Intel X25-M SSD [CF] Makefile.am: Fix unix2dos and makensis parameters to allow to build the Windows installer on Linux also. [CF] Makefile.am: Use a separate build rule for each man page to avoid compatibility problems with BSD make. [AS] drivedb.h updates: - Fujitsu MHZ2 BK series [MS] drivedb.h updates: - SAMSUNG SpinPoint F3 series - SAMSUNG SpinPoint F3 EG series - SAMSUNG SpinPoint M5 series - Western Digital Caviar Green 6400AADS - more Western Digital VelociRaptor variants [AS] FreeBSD: disable 48-bit commands in the ata_pass_through interface, there is no 48-bit support in the IOCATAREQUEST ioctl. [CF] smartctl: Add option '-l scterc[,READTIME,WRITETIME]' to get/set the SCT Error Recovery Control time limit (ticket #50). Patch was provided by Richard Gregory: http://www.csc.liv.ac.uk/~greg/projects/erc/ Modified for new ata_pass_through() interface. Linux HPT fixes ommitted for now. [CF] Fix SCT temperature table commands on big endian CPUs. [MS] drivedb.h updates: - more Seagate Momentus 5400.6 drives - HP 500GB drive MM0500EANCR [CF] Windows: Cleanup I/O-control declarations, rely on include files if possible. [CF] Windows: Compile fixes for 64-bit exe (EXPERIMENTAL). Update build info in INSTALL file. [CF] drivedb.h update: - Patriot Torqx SSD (patch provided by Gianpaolo Cugola) [CF] Makefile.am: Avoid duplication of man page filter script. [CF] smartd: Add option '-C, --capabilities' if libcap-ng is available (ticket #45). Support is added if libcap-ng is found during build. This can be overridden by configure option '--with-libcap-ng=[auto|yes|no]'. Based on Debian patch: http://patch-tracker.debian.org/patch/series/view/smartmontools/5.39-3/62_lowcap.patch Modified to fix regression (ticket #41, Debian bug 564876). [CF] Bugfix release 5.39.1. [CF] Linux: Fix spin-up of SATA drive if '-n standby' is used (ticket #37). For some reason, this happens if the SCSI/SAT device is opened with O_RDWR instead of O_RDONLY. [CF] Windows: Fix parsing of 'tw_cli' output for 3ware 9.5.x release (ticket #43). [CF] Add USB IDs of Seagate FreeAgent Go, Seagate Expansion Portable and WD My Passport (IDE). [CF] autogen.sh: Fix version regexp, allow automake 1.11.1. [CF] Linux: Allow smartd 'DEVICESCAN -d sat' (ticket #13). Detects (S)ATA devices behind a standard SAT layer (Vendor ID: "ATA "), but not USB bridges with SAT support. Only added for backward compatibility with 5.38. No longer needed as 'DEVICESCAN' without '-d' includes these devices. [CF] Add USB ID of Seagate FreeAgent Desktop. [CF] smartd: Fix directive '-l selftest' (ticket #36) Regression was introduced with r2773. [CF] smartd: Don't disable attribute tracking if read thresholds fails. Windows: Don't return dummy thresholds if IOCTL_STORAGE_QUERY_PROPERTY or 3ware CLI is used to read SMART data. [CF] Windows: Print warning if admin rights are missing. [CF] Replace some 'EXIT(status)' calls by 'return status'. Remove unnecessary casts from 'nonempty()' calls. [CF] Windows: Set ata_device::ata_identify_is_cached() return value according to I/O-control actually used. [CF] Print ATA output registers if SMART status command returns bogus register values. [CF] Windows: Don't return false ATA version info if IDENTIFY data is build from IOCTL_STORAGE_QUERY_PROPERTY result or from 3ware CLI output. smartctl: Handle missing info about ATA version in '-i' output. [CF] smartctl: Don't print log directory if '-q errorsonly' is specified. [CF] smartctl: Fix option '-q, --quietmode' (ticket #11). Regression was introduced with r2807. [CF] drivedb.h update: - SAMSUNG SpinPoint F2 EG series [CF] Add USB ID of Samsung Story Station. [MS] drivedb.h update: - Hitachi Travelstar 5K320: some EA models miss last 2 "0" in model string - Seagate Barracuda LP series [CF] drivedb.h update: - Crucial M225 SSD [CF] drivedb.h updates: - WDC Scorpio Blue Serial ATA (640GB, 750GB, 1TB) - WDC My Passport Essential SE [CF] Add USB ID of Toshiba PX1270E-1G16. [CF] Happy New Year! Update copyright year in version info. [CF] drivedb.h update: - SAMSUNG SpinPoint M40/60/80 series [CF] Add direct access to 48-bit LBA register in 'ata_in/out_regs_48bit'. [DL] drivedb.h updates: - WDC My Passport Essential/USB (capacity 250GB, 400GB & 500GB) [DL] -r ataioctl,2: print text representation of data also (ticket #32) [DL] FreeBSD: freebsd_ata_device::ata_pass_through implemented (part of ticket #18) [CF] drivedb.h updates: - Hitachi Travelstar 7K320 (ticket #28) - Hitachi Travelstar 7K500 [DL] -l gpllog,...: print text representation of data also (ticket #30) [DL] FreeBSD: check reallocf() result for failures [AS] FreeBSD: fixing crash on kFreeBSD (#29), patch provided by Petr Salinger [CF] Makefile.am: 'make check' now tests the syntax of drivedb.h. [CF] Cygwin: Open drive database files in text mode. [CF] Cygwin: Check for 'syslogd' and 'syslog-ng' in initd script. [CF] Windows: Disable Win9x/ME specific code if no longer supported by compiler. [CF] Add '-v ID,FORMAT:BYTEORDER[,NAME]' to specify byte order of attribute raw value. [CF] configure.in: Change --with-docdir default from 'PREFIX/share/doc/smartmontools-VERSION' to 'DATADIR/doc/smartmontools' to make it consistent with --docdir option added in autoconf 2.6x (ticket #24). Autoconf 2.5x is still supported. [CF] Move drive database entries from 'knowndrives.cpp' to new file 'drivedb.h'. This allows to update the drive database from SVN if installation was configured with '--enable-drivedb'. Remove the Makefile target to create 'drivedb.h'. [CF] do_release: Add support to release from a dir below 'branches'. Accept partial checkouts. smartmontools 5.39.1 2010-01-28 [CF] Linux: Fix spin-up of SATA drive if '-n standby' is used (ticket #37). For some reason, this happens if the SCSI/SAT device is opened with O_RDWR instead of O_RDONLY. [CF] Windows: Fix parsing of 'tw_cli' output for 3ware 9.5.x release (ticket #43). [CF] Linux: Allow smartd 'DEVICESCAN -d sat' (ticket #13). Detects (S)ATA devices behind a standard SAT layer (Vendor ID: "ATA "), but not USB bridges with SAT support. Only added for backward compatibility with 5.38. No longer needed as 'DEVICESCAN' without '-d' includes these devices. [CF] smartd: Fix directive '-l selftest' (ticket #36) Regression was introduced with r2773. [CF] smartctl: Don't print log directory if '-q errorsonly' is specified. [CF] smartctl: Fix option '-q, --quietmode' (ticket #11). Regression was introduced with r2807. [CF] Happy New Year! Update copyright year in version info. [DL] FreeBSD: check reallocf() result for failures [AS] FreeBSD: fixing crash on kFreeBSD (#29), patch provided by Petr Salinger [CF] do_release: Add support to release from a dir below 'branches'. Accept partial checkouts. smartmontools 5.39 2009-12-09 [CF] do_release: Commit CHANGELOG and NEWS also. Allow to review changes. [CF] Linux: Add workaround for Adaptec series 2, 5 and 5Z controllers with firmware >= 17380. Patch was provided by Phil Wilson, see: http://linux.adaptec.com/2009/07/24/using-smartmontools-538-with-series-255z-controllers-with-firmware-17380-onwards [CF] configure.in: Add '-fno-strict-aliasing' to CXXFLAGS if supported. This suppresses gcc 4.4.1 warnings on Linux and avoids possible unsafe optimizations (ticket #23). Patch was provided by Manfred Schwarb. [CF] Avoid truncation of configure arguments in '-V' output. [AS] Added USB IDs of WD Passport USB Portable [CF] Linux: Fix segfault in 3ware interface (ticket #22). [MS] knowndrives.cpp updates: - Hitachi Deskstar 7K2000 - Seagate Momentus 7200 FDE.2 series [CF] Add USB ID of WD My Passport 070A. knowndrives.cpp update: - WD My Passport hard drive (USB interface) [CF] smartd: Write 'worst' attribute value to '.state' file also. This allows to use state persistence with 'raw64' attributes. [CF] Rework ATA SMART attribute check in smartctl and smartd. smartd: Ignore normalized attribute value and threshold if 'raw64' or 'hex64' format is selected. [CF] Add USB IDs of Iomega LPHD080-0, 2 Genesys Logic bridges and Initio 316000. [MS] knowndrives.cpp update: Hitachi Travelstar 5K320 series [CF] smartctl: Ignore normalized attribute value and threshold if 'raw64' or 'hex64' format is selected. [CF] knowndrives.cpp updates: - add OCZ-Vertex raw64 attributes - add OCZ-Agility Thanks to Marcin Marszalek for the patch. [CF] Add '-v ID,hex*' print formats. Fix '-v N,FORMAT,NAME' parsing. [CF] Add '-v ID,raw64[,...]' print format based on a patch provided by Marcin Marszalek. [CF] Add '-v ID,RAW_FORMAT[,ATTR_NAME]' option. This allows to add new attributes without the need to enhance the '-v' option. Rework attribute name and raw value formatting. [CF] Fix auto_ptr initialization in linux_scsi_device::autodetect_open(). [CF] Remove duplicate function smart_device_list::add(). Replace calls with push_back(). [MS] attribute update: trim attribute names to 23 chars [CF] Add smart pointer class template to manage device object pointers. Remove related 'delete' calls and 'try/catch' blocks. [CF] do_release: Replace generation of '*.asc' by '*.md5' and '*.sha1'. [MS] attribute updates: - change attributes 202,204,205 to the meanings as found in wdidle3.exe retain old entries as comments (possible Fujitsu use) - add attribute 240 as found in Fujitsu MHY2xxxBH [MS] attributes updates: - attributes 225, 232 and 233 for Intel X25-E SSD - non-conflicting attributes extracted from wdidle3.exe (thanks to Franc Zabkar and Dan Lukes) [CF] Update Windows and ./configure info in INSTALL file. [CF] Update 'do_release' script for SVN. [MS] knowndrives.cpp updates: - Western Digital MyPassport Essential hard drive (USB interface) - Seagate Momentus 7200.4 series - Western Digital Raptor X - Intel X25-E SSD [CF] knowndrives.cpp updates: - New Seagate 7200.11 firmware version - Update IBM link [CF] smartctl: Use printf() instead of pout() for exception error messages to avoid access to bogus 'con->dont_print'. [CF] smartd: Add missing help texts for '-A', '-B' and '-s'. [CF] Add missing check for log page 0x11 support to smartctl '-l sataphy' option. [CF] Add USB ID of Freecom Hard Drive XS. [AS] Linux: Autodetect DELL PERC and MegaRAID controllers. Hiding debug messages coming from megaraid code. [AS] Linux: Fixed SATA drives support on megaraid device (see ticket #15). [AS] FreeBSD: Removed all old detection code, moving everything to the objects. Now we are using CAM/ATA enumerators to guess device type. [AS] FreeBSD: Added autodetection for the ada disks (untested). Code for USB device detection refactored. [AS] FreeBSD: cam_get_umassno rewritten using XPT_PATH_INQ [AS] FreeBSD: do not open/close cam device on every request for SCSI disks. Use com->camdev both for SCSI and ATAPICAM. [AS] FreeBSD: added support for the ada disks, based on agapon patch [CF] Add names for attributes 184 and 188, see ticket #17. [CF] configure.in: Change configure date syntax. Add message to '-mno-cygwin' option check. [GK] Add names for some attributes used in MLC flash drives: 175, 176, 177, 181, 182 [CF] Windows: Check support of gcc '-mno-cygwin' option in configure. This option has been removed in Cygwin gcc 4.x. Update INSTALL instructions accordingly. [CF] Increase SCSI_TIMEOUT_DEFAULT from 6 to 20 seconds to avoid timeouts when a disk spins up from standby mode. [CF] Add USB ID of AcomData 504 (OnSpec USB bridge). [AS] Correcting manual pages (FreeBSD related) [AS] FreeBSD: fix FTBFS on GNU/kFreeBSD (reported by derevko). [AS] FreeBSD: Add USB autodetection to smartd DEVICESCAN directive. [CF] Add USB ID of Myson Century CS8818, add some comments. [CF] Return info strings from 'smart_interface::get_*()' functions as 'std::string' instead of 'const char *'. Static buffers are no longer needed. [SZ] FreeBSD: Fix highpoint type detection and ioctl failed for parameter error. [CF] Linux: Add USB autodetection to smartd DEVICESCAN directive. [CF] Add USB IDs of Maxtor Basics Desktop and ISD-300A1. [AS] Use malloc() to ensure that the read buffer lands on a single page. This avoids some bugs seen on LSI controlers under FreeBSD. [CF] Add missing help text for '-d usb*' options. [CF] Linux: Dereference '/dev/disk/by-*/*' symlink before device type autodetection. [AS] FreeBSD: Support SATA disks attached to a SAS controller (based on patch from freebsd ports tree). [AS] FreeBSD: Added FreeBSD 8 libusb2 device autodetecion, new configure check for -lusb. [AS] FreeBSD: Added USB device autodetection and fixed -d switch behavior. [AS] FreeBSD: Migrate os_freebsd.cpp to new interface. [CF] Fix max number of 3ware devices, 128 devices are supported again. Regression was introduced during migration to new interface. Thanks to Michael Holweg for the problem report. [CF] Windows installer: Add 'DisplayVersion' to uninstall registry key. [MS] knowndrives.cpp updates: - Marvell SSD SD88SA024BA0 - Fujitsu MHZ2 BH series - Fujitsu MHZ2 BJ series - Seagate Maxtor DiamondMax 23 - WD Caviar Green: Add some 32MB cache variants - relax OCZ-Vertex pattern [CF] Add USB ID of Verbatim FW/USB160. [CF] Fix data type bug in checksum test for multi sector logs. [CF] Add USB ID of Seagate FreeAgent Go. [MS] Add experimental feature to log attribute values at each check cycle (ATA only), activated with the smartd option "-A PREFIX" / "--attributelog=PREFIX". Introduce configure options "--enable-attributelog" and "--with-attributelog=PREFIX" to enable feature by default. [DG] [SAT] Heads up about a non backwardly compatible change introduced in draft SAT-2 (sat2r8b.pdf) that will break our existing SAT processing code. Action needed if change stands. [MS] smartd.cpp: Adjust umask [CF] Makefile.am: Remove 'uninstall-docsDATA' target to fix 'make distcheck' with automake 1.11. The 'make uninstall' of examplescripts fails if docdir does no longer exist. [CF] Remove 'scsiata.h'. The 'scsiata.cpp' module now implements parts of 'dev_interface.h'. [CF] smartctl: Don't report an attribute as failed if threshold is 0. [CF] Print only one warning on checksum errors in multi sector log. Remove casts from calls of checksum(). [DG] minor changes to SCSI background scan strings [MS] knowndrives.cpp updates: - Fujitsu MHW2 BJ series - WD Caviar Black family [MS] Makefile.am: Make creation of svnversion.h independent of locale settings [CF] Require to specify PORT parameter of '-d usbjmicron' if two disks are connected. [CF] smartctl: Limit default number of printed entries for '-l xerror' to 8, for '-l xselftest' to 25. [CF] smartctl: Fix number of entries in '-l xselftest' output. [CF] Add USB IDs of a SunplusIT bridge, three WD drives, and an unsupported Iomega drive. [CF] Makefile.am: Use 'svnversion' instead of 'svn info' to get the revision number. This also checks for mixed and modified working copies. [CF] Remove CVS Id strings from '-V, --version' output. [CF] Update CONTRIBUTORS section on man pages. [CF] Makefile.am: 'make maintainer-clean' now removes also files generated by './autogen.sh'. [CF] Invalidate 'do_release' script, it needs some rework for SVN. [CF] Update documentation files for SVN. [CF] Rename trunk/sm5 to trunk/smartmontools. [CF] Print SVN revision number instead of time in version info line. Get SVN revision number from svn (if available) or guess from Id strings. Rename generated file to svnversion.h. [CF] Makefile.am: Modify generation of cvsversion.h for SVN. [GP] Convert CVS repository to SVN. [CF] smartd: Fix size of monitor flag array from previous commit. [CF] Makefile.am: Add missing 'megaraid.h'. [CF] smartd: Add '!' flag to '-r' and '-R' directives. If specified, message is logged as LOG_CRIT and warning mail is sent if attribute normalized or raw value changes. [CF] Replace global 'con->...' variables used for selective self-tests by local variables. [GK] Add names for some attributes used in Samsung MLC drives: 178-180 & 183 [CF] smartctl: Add option '-x, --xall' to print all info including extended SMART logs and non-SMART info. [CF] smartctl: Add '-l xerror,error' and '-l xselftest,selftest' to print the old logs if the extended logs are not supported. [MS] knowndrives.cpp updates: - Western Digital AV-GP series - Transcend Solid-State Drive and Transcend Solid-State Drive V series - Seagate Momentus 5400.5 series [CF] Disable 48-bit ATA commands for JMicron USB bridges by default because these commands do not work with all devices. Add '-d usbjmicron,x' to enable 48-bit commands. Thanks to Alexander Shaduri for the problem report. [CF] smartd: Don't ignore the '-n' directive when a self-test is scheduled. Start the self-test later when the disk is active again. [DG] SCSI (SAS): implement '-l sasphy,reset' (reset part was stub prior to this) [DG] add 'ATA, SCSI command sets and SAT' section to smartctl.8 . [SCSI] add 'number of background medium scans' field [DG] SCSI (SAS): add '-l sasphy' and '-l sasphy,reset' into smartctl to output SAS device phy information (from the Protocol specific log page) [CF] autogen.sh: Remove 'CYGWIN=check_case:strict', this does no longer work on Cygwin 1.7. Print warning if Automake version cannot handle case insensitive filesystems. [CF] Remove '#define TRUE/FALSE', use 'bool' and 'true/false'. [CF] Add 'options' parameter to SCSI printing routine. Move global 'con->...' smartctl variables to 'options' parameters of printing routines. [CF] Windows: Remove outdated entry about undocumented system calls from WARNINGS file. [CF] Print General Purpose Logs even if GPL feature bit is missing. Needed for some older disks which implement READ LOG EXT but do not report the GPL feature set. Change order of the extended log outputs ('-l xerror', '-l xselftest', '-l sataphy'). Extended logs are now printed before their old versions. [CF] autogen.sh: automake 1.10.2 and 1.11 are OK. [CF] Fix syntax error in prototype of 'safe_snprintf()'. Thanks to Alexander Shaduri for bug report and patch. [DG] SCSI: Fetch load-unload cycle counts. [CF] Windows: Add Win-7 and Win2008 to get_os_version_str(). [CF] smartd: Fix '-M test' directive in conjunction with '-s' option. Thanks to Matthias Becher for the problem report. [MS] knowndrives.cpp updates: - Add Seagate Barracuda 7200.12 series - Add Seagate Momentus 5400.4 series - Add Hitachi Deskstar 7K1000.B series - Add Transcend SSD TS32GSSD25-M - Add OCZ Vertex 1199 [CF] knowndrives.cpp updates: Add Samsung S250 series. Add '-v 198,increasing' to Samsung P80. Replace '#if/#endif' by comment to fix configure option '--enable-drivedb'. [CF] knowndrives.cpp update: Add Seagate 7200.11 with 'CC' firmware which is unaffected by the bug. Thanks to Bas Mevissen for the patch. [CF] Replace global 'con->...' variables used for drive presets by local variables. [CF] Simplify '-v' vendor attribute option parsing. Add '-v 197,increasing' and '-v 198,increasing' options to specifiy that an uncorrectable count is never reset. This modifies the printed attribute names and smartd's default setting of '-C' and '-U' directives. Both '-v' options can also be preset in the drive database. [CF] Add '+' modifier to smartd '-C' and '-U' directives. If specified, a warning is only printed if the raw value increases. [CF] Add smartctl option '-l xselftest[,NUM]' to print ATA SMART Extended Self-test Log (GP Log 0x07). [CF] Add experimental option '-d usbsunplus' for drives behind SunplusIT USB bridges. Tested on WinXP with SPIF215(?) in TrekStor DataStation maxi m.u.. Many thanks to SunplusIT tech support for providing the required information. [CF] Windows: Provide a non-console version of smartctl.exe as smartctl-nc.exe. This prevents that a new console is opened when smartctl is run from a GUI program with stdio redirected. Used by GSmartControl (http://gsmartcontrol.berlios.de/). [CF] Remove support for platforms without getopt_long() in smartctl.cpp and smartd.cpp. If getopt_long() is missing, ./configure aborts with an explanatory message. For now, short option help texts are only removed from os_linux.cpp and os_win32.cpp. HAVE_GETOPT_LONG is still defined in config.h. [CF] Add smartctl '-d test' option to print the result of the device type detection. [CF] Enhance USB device type autodetection, use bcdDevice if known. Add Cypress CY7C68300B/C (AT2LP) to the table. [CF] Linux: Add experimental USB device type autodetection. Uses USB ID info found through symlink "/sys/block/sdX/device". [CF] Windows: Add experimental USB device type autodetection. Uses WMI command line tool 'wmic' to query USB ID. [CF] Add function smart_interface::get_usb_dev_type_by_id() to map USB vendor:product IDs to '-d type' names. Can be used by platform dependent layer to autodetect USB devices if ID of USB bridge is known. [CF] smartd: Log changes of self-test execution status if '-l selftest'is specified. [CF] knowndrives.cpp update: Samsung SpinPoint F1 RE series [MS] knowndrives.cpp update: Seagate Momentus 5400.6 series [CF] Add forgotten SCSI sense checks to class usbjmicron_device. [CF] Add new SMART STATUS check command for JMicron USB bridges. Should support also older chip versions and prevents a race condition. [CF] Windows: Fix win_scsi_device::scsi_pass_through() for single byte data transfers. Required for JMicron SMART STATUS check. [MS] knowndrives.cpp update: Add Hitachi Travelstar C4K60 family (1.8" slim drives) [MS] Workaround for huge raw values of attribute 9, needed for Hitachi Travelstar C4K60. For the Power_On_Minutes case, clip the display to 4 bytes and show the remaining part, if existent, in parens. [CF] Add experimental option '-d usbjmicron[,PORT]' for drives behind JMicron USB bridges. Tested on WinXP with JM20336 in AixCase AIX-ESU35CD. Many thanks to JMicron tech support for providing the required information. [MS] knowndrives.cpp update: Add WD Caviar Green 8MB and 32MB cache variants, stretch to 2TB. [CF] knowndrives.cpp updates: Add more entries for Samsung P80 disks with old and unknown firmware. Remove old entries which would match any new Samsung model reusing old firmware version number. [CF] Windows: Add a workaround for missing multi-sector support for ATA READ LOG EXT command. [CF] Fix Extended Comprehensive Error Log index base. Add workaround for Samsung disks using reserved byte as index. [CF] knowndrives.cpp updates: Update bug warnings for Seagate 7200.11, ES.2 and DiamondMax 22. Add new entries for fixed firmware versions. [CF] Add smartctl option '-l xerror[,NUM]' to print ATA SMART Extended Comprehensive Error Log (GP Log 0x03). [MS] knowndrives.cpp update: Added remaining WD Scorpio Blue SATA II drives [CF] Minor fix to remove ID 0 from 'smartctl -l sataphy ...' output. [CF] knowndrives.cpp updates: Add warnings about possible firmware bugs to Seagate 7200.11, ES.2 and DiamondMax 22 entries. [CF] knowndrives.cpp updates: Add Samsung SpinPoint F1 series. [CF] Windows: Fix return value of scsi_pass_through(). Regression was introduced during migration to new interface. SAT over USB now works on XP (both '-d sat,12' and '-d sat,16'). [MS] knowndrives.cpp updates: - Added Western Digital RE2-GP family - Added Hitachi Travelstar E5K160 family - Allow uppercase variants of Hitachi 5K160 drives [CF] Fix smartctl crash on '-l directory,[gs]'. Allow to override missing GPL feature bit or missing log dir entry with '-T permissive' option. [SZ] os_freebsd.cpp, os_freebsd.h updates: Support HighPoint RocketRAID controller under FreeBSD [MS] knowndrives.cpp updates: - Added Western Digital RE3 32MB cache variants - Added WD Caviar Green 32MB cache variant (WD10EADS) - Added WD Scorpio Black family [DG] Accept half healthy (and half unhealthy) indication from the SMART RETURN STATUS. This makes allowance for SAT implementations (e.g. via USB) that truncate the SCSI sense buffer to 18 bytes. This truncation causes the SMART RETURN STATUS indication to be half health or unhealthy. If the half indication is used, then warn if '-r ioctl' is given. [MS] knowndrives.cpp updates: - Added Apple SSD - Added Seagate U8 family [DL] os_freebsd.cpp: Added support for CHECK_POWER_MODE and WRITE_LOG commands [MS] knowndrives.cpp update: There seem to exist WD Raptors with SATA II interface, add them. [MS] knowndrives.cpp updates: - Added remaining Seagate Barracuda 7200.11 drives - Added HP 1TB SATA disk [MS] knowndrives.cpp updates: - Added Maxtor 92040U6 (DiamondMax Plus 6800) - Added Seagate Maxtor DiamondMax 21 500GB version - Added QUANTUM FIREBALLlct15 22 - Added QUANTUM FIREBALL CR6.4A - Added QUANTUM FIREBALLP LM20.4 - Added SUN branded Toshiba MK4019GAX - Added TOSHIBA MK1016GAP and relatives: MK1[05]1[67]GAP - Added Western Digital WD800AB and WD2500AB - Some Hitachi 7K160 drives have garbage at end of name: permit it [CF] Add smartd '-n powermode,N' directive parameter to limit the number of skipped checks. Thanks to Michal Hlavinka for the patch. [MS] knowndrives.cpp updates: - Added Hitachi Endurastar J4K30/N4K30 - Added Hitachi Travelstar 4K120 series - Some Hitachi 7K80 drives have garbage at end of name: permit it - IBM Travelstar 6GN series [MS] knowndrives.cpp updates: - Added Quantum Fireball ST4300A - Added Asus-Phison SSD (solid state disk) - Added Seagate DB35.3 Series - Added remaining disks of the Seagate SV35.2 Series [MS] Fix trivial compile error with "-pedantic" [MS] Workaround for huge raw values of Reallocated_Sector_Ct and Reallocated_Event_Ct for newer Fujitsu disks (only the lower 16 bits seem to be meaningful). Clip the display to 16 bits and show the remaining part, if existent, in parens. Patch by [CF]. [CF] smartd DEVICESCAN: Fix autodetection of SAT devices. Thanks to Stanislav Brabec for bug report and testing. [MS] knowndrives.cpp update: Convert file to full string regex: remove "^$" from pattern [MS] knowndrives.cpp updates: - Added Seagate Momentus 5400 PSD series (hybrid drives) - Added Seagate Momentus 7200.3 series - Added Hitachi Deskstar 7K250 (SUN branded) - There are Hitachi Travelstar 5K250 drives with capital "HITACHI" - Correct regex for Maxtor VL 30 drives [CF] Add configure options '--enable-savestates' and '--with-savestates=PREFIX' to enable smartd persistence ('-s' option) by default. [CF] smartd: Add '-s ([cnr]/../.././..)' directive to run scheduled selective self-tests. Useful to perform full tests of large disks not running 24x7. [CF] Allow to read local drive database entries from optional file '${sysconfdir}/smart_drivedb.h'. Add configure options '--enable-drivedb' and '--with-drivedbdir=DIR'. If specified, drive database is read from '${drivedbdir}/drivedb.h'. (default '${prefix}/share/smartmontools/drivedb.h'). This file is build from knowndrives.cpp. [MS] knowndrives.cpp updates: - Added 640GB variants of Western Digital AAKS and AACS drives - Added Western Digital AV ATA family - Added 160GB variant of Hitachi P7K500 - Added 500GB variant of Hitachi 7K1000 - Some cleanup for Quantum disks - Added Seagate Maxtor DiamondMax 22 family [CF] Use full string match for regexp in drive database. [CF] Add option '-d sat+TYPE' to use SAT with controllers which require option '-d TYPE'. Should work with '-d sat+megaraid,N'. As a side effect, '-d usbcypress+TYPE' is also supported. [CF] Add parser to read drive database from a file. Add '-B' option to smartctl and smartd to specify database file name. File syntax is identical to the C/C++ syntax used to inialize the internal database array. [CF] New syntax for drive database: Specify presets by strings with '-v' and '-F' options. Use empty strings instead of NULL. [JPH] Added Linux support for viewing disks behind MegaRAID controllers [CF] smartd: Improve min/max temperature recording in conjunction with '-s' option. [CF] Add a wrapper class for FILE *. [CF] smartd: Add experimental support for state persistence (ATA only). Add option '-s' to specify path prefix for state files. Rework scheduled self-test detection to support persistence. If any test schedules are within downtime, the highest priority test is run after next startup. [CF] Remove casts from 'format_ata_string()' calls. [CF] Minor changes to fix errors and warnings from Cygwin gcc 4.3.0. [CF] smartd: Remove SCSITIMEOUT code. According to smartd.h 1.54 CVS log from 2003-10-27, it did never work. [CF] Remove dependencies ataprint.cpp and scsiprint.cpp from smartd. Move common ATA functions from ataprint.cpp to atacmds.cpp. Module scsiprint.cpp was apparently never used in smartd. [CF] Move smartd local declarations from smartd.h and utility.h to smartd.cpp. Remove smartd.h. [CF] Fixed extra '\n' in "Offline data collection status" output. Thanks to Alexander Shaduri for the patch. [CF] smartd: Separate device configuration data from device state data. Use references instead of pointers for configuration and state data. [CF] Add const-correctness and static to ATA support functions. [CF] Add a wrapper class for regex. [CF] Simplify 'create_vendor_attribute_arg_list()'. [CF] smartd: Rework of main data structures. Remove explicit memory allocations, use STL containers and structs with value semantics instead. Remove old malloc/free based memory management helper functions unless old interface is still in use. [CF] Linux: Cleanup device scan, remove name list, create objects directly. [CF] Linux: Cleanup smart_device::open(), type strings are no longer used. [CF] Remove CONTROLLER_* defines and variables unless old interface is still in use. [CF] Linux: Migrate 3ware interface to 'ata_pass_through()'. Multi-sector support is not complete yet. 48-bit commands possibly work. WARNING: Not tested, please review code before first test! [CF] Linux: Migrate os_linux.cpp to new interface. [CF] Add direct access to 16-bit registers in 'ata_in/out_regs_48bit'. [CF] Add 'ata_cmd_is_ok()' parameter check, remove 'ata_pass_through_28/48bit()' functions. [CF] Add CVS date/time from cvsversion.h to man pages also. [CF] Add configure option '--with-os-deps='os_module.o ...' to specify alternate OS interface modules. Useful for testing during migration. [CF] Remove declarations of 'optarg', 'optind', ..., include instead. This fixes 'auto-importing' linker warnings on Cygwin. [CF] Add '-l sataphy[,reset]' to print SATA Phy Event Counters. [CF] Add '-l gplog,ADDR[,FIRST[-LAST|+SIZE]]' and '-l smartlog,...' to dump any log page accessible via GP or SMART read log commands. [CF] Enhance '-l directory' to print both GP and SMART Log directories. Add '-l directory[,gs]' modifiers to select GP or SMART log. Enhance 'ata_cmd_in' parameter struct for 48-bit commands. [CF] Windows: Add full ATA pass through support including 48-bit commands. [CF] Windows: Migrate os_win32.cpp to new interface. [CF] SAT: Add full ATA pass through support including 48-bit commands. [MS] knowndrives.cpp update - Added FUJITSU MHZ2250BS G2 and family [MS] knowndrives.cpp updates - Added Maxtor DiamondMax 60 94098H6 - Added Maxtor DiamondMax 1280 84000A6 and family - Added Maxtor DiamondMax VL 30 31536H2 (ATA100) and family - Some Seagate Barracuda 7200.9 have garbage at end of name: permit it - Added Seagate Barracuda ATA ST320430A and family - Regression from previous checkin: add WD RE2 WD...0ABYS again - Added WD RE3 WD5002ABYS and family - Added Quantum Fireball CR13.0A - Added Hitachi Travelstar 5K250 HTS542525K9SA00 and family - Added WD AC420400D and add whole range of AC.... which have 5400rpm or higher (i.e. PIO-only drives omitted) [MS] knowndrives.cpp updates - WD: Separated entries for EIDE and SATA - WD: Separated entries for Caviar SE, SE16, RE, RE2 - WD Named: WD Caviar AC series - WD Renamed: WD Caviar RE/RE2 -> WD RE/RE2 - WD Renamed: WD Caviar SE/SE16 WD....AA[A-Z][A-Z] -> WD Caviar Blue - WD Renamed: WD Scorpio WD....BEV[A-Z] -> WD Scorpio Blue - Added WD Scorpio Blue WD3200BEVT - Added WD RE2 WD5001ABYS and family - Added WD Caviar Green WD5000AACS and family - Added WD VelociRaptor WD3000GLFS and family - Added Seagate Barracuda ES.2 ST31000340NS and family - Added Samsung SP80A4H - Added Maxtor DiamondMax 21 STM3160215AS and STM3320620AS - Added Seagate Barracuda 7200.7 ST380819AS - Added Maxtor DiamondMax 10 6B100P0 - Added Seagate SV35.2 Series - Added Fujitsu MHY2120BH and family - Added Fujitsu MHW2080BH PL (PL variant) - Added Toshiba MK3252GSX and family [BA] Fix smartctl bug: when running in silent mode '-q errorsonly' do not print the Selective Self-test log. Any errors will ALREADY appear in the SMART Self-test log. [CF] Add missing 'const' and other minor fixes to prevent gcc warnings. [OB] Added information message about supported Areca firmware versions. It's displayed in case the ATA device identification fails. [CF] Add configuration file for Doxygen. [CF] Add new object oriented interface to access ATA and SCSI devices. smartctl and smartd are modified to use the new classes in 'dev_interface.{h,cpp}'. The template class in 'dev_tunnelled.h' is used in 'scsiata.cpp'. The code in 'dev_ata_cmd_set.{h,cpp}' supports migration from old function 'ata_command_interface()'. All existing 'os_*.cpp' modules should still work without any changes. The required adapter classes from 'dev_legacy.cpp' are automatically added by configure if necessary. [BA] Updated smartd and smartctl and smartd.conf man-page documentation to reflect support for Areca SATA RAID controller cards. [OB] Added support for Areca controllers to smartd. Extensive tests as well as documentation are still pending however. [OB] Implemented device locking for Areca controllers in smartctl [BA] Fixed selective self-test code. Data structure revision number may be != 1 if no selective self-test has ever been run. Host MUST set this value (at least at the first selective self-test instance). Thanks to Curtis Stevens of WDC for clarification. [MC] usbcypress autodetection [BA] Starting to commit Areca code. For now just smartctl. More changes and documentation coming soon. Need Areca firmware version 1.45 dated 10 June 2008 or later. May need changes in opening /dev/sg and file locking. Many thanks to Hank Wu! [CF] smartd: Fix too small name buffer for 3ware with >100 devices. [JH] now C++ Support for QNX Target already tested for QNX 6.3.2 on x86 and armle target [CF] Allow to set BUILD_INFO from make command line. [CF] Windows: Add MSVC8 support, remove MSVC6 project files. [MC] Add usbcypress device support for smartd. [CF] Add output of latest CVS date/time stamp to version info. New file cvsversion.h is generated by Makefile. Move formatting of version info to utility.cpp. [AR] Fix bug in 3ware node creation where nodes would be created then deleted, then recreated. [BA] Add missing CCISS cvs version tags to '-V' printouts. [TS] Linux: Ensure the 3ware device nodes are created with a correct SELinux security context. [AR] Add support for up to 128 devices on 3ware controllers. [CF] C++: Added new main() with exception handlers, replaced exit(x) with throw(x), removed global variable 'exitstatus'. Necessary for future changes, because exit() does not call any destructors. [SS] FreeBSD: plug file descriptor leak in smartd (only happens with CISS devices). Reported by Vadim Ostranitsyn. [SS] FreeBSD: allow smartctl to interact with SCSI /dev/pass devices, thus enabling it to work with RAID controllers that expose disks via these devices. From scottl@ via FreeBSD ports. [MC] Add usbcypress device support for smartctl. [KS] INSTALL on Solaris: updated description to use C++ compiler. [KS] configure.in on Solaris: added options for Sun's compiler to suppress trivial warnings. [KS] configure.in on Solaris: added direction to search suitable libraries for getaddrinfo(). [TS] smartd on Linux: remove forgotten return from deviceopen(); SCSI device descriptors had the FD_CLOEXEC flag unset. [CF] Added 'const' to avoid warning on depreciated string constant to 'char *' conversion. [CF] autogen.sh: automake 1.10.1 is OK. Updated message texts. [BA] removed smartmontools.spec file (now in CVS Attic). This was not being used by RH or FC anyhow, and was out of date and not maintained. [BA] smartd on Linux: sets FD_CLOEXEC on the opened device file descriptor. The descriptor is otherwise leaked to other applications (mail sender) which may be considered a security risk and may result in AVC messages on SELinux-enabled systems. Thanks to: Tomá Smetana" . [BA] smartd: when sending email, to gather information about the host for the body of the email, eliminate gethostbyname() in favor of the IPv6-friendly function getaddrinfo() if the latter is available. Info may be found here http://udrepper.livejournal.com/16116.html and here http://people.redhat.com/drepper/userapi-ipv6.html Thanks to: Tomá Smetana" . Smartmontools developers: please check that smartd still LINKS properly on your systems. [BA] Fix ugly syntax bug in os_freebsd.cpp. How did this go undetected for so long?? SMARTMONTOOLS STABLE RELEASE 5.38 2008/03/10 [KS] Solaris/x86: modified configure.in for Sun's compiler. [BA] smartd.initd.in addition from Erwan Velu [BA] smartd fixes: - On Linux, DEVICESCAN now automatically recognizes SATA devices behind libata, and SATA devices behind the Marvell driver, and treats them correctly. - On Linux, a '-d sat' or '-d marvell' is automatically added if libata or the marvell driver are recognized behind a SCSI device type [SS] (Maybe) fix attribute autosave in FreeBSD. [SS] Major NetBSD-specific bugfixes: - handle actual SCSI and ATA errors and not only ioctl() errors; - set up I/O request properly for AUTO_OFFLINE and AUTOSAVE commands (inspired by similar change in os_freebsd.cpp); - handle AUTO_OFFLINE and AUTOSAVE like STATUS_CHECK (like os_linux.cpp does). [GG] add kfreebsd gnu support to configure.in [BA] Fix auto-offline support in FreeBSD. Thanks to Cyrus Rahman for the patch, and Eduard Martinescu for blessing it. [DG] smartd re-opens "SCSI" devices as ATA devices if a SAT layer is detected (smartd bug IMO). In Linux this upsets scsi generic device nodes (e.g. /dev/sg0). Detect the re-open in os_linux.cpp and set the O_RDWR flag (ATA uses the O_RDONLY flag). [CF] Drive database: Added Fujitsu MHW2 BH, Maxtor DiamondMax 17, 20, 21, Hitachi Travelstar 4K40, 5K120, 7K200, Deskstar 7K160, T7K500, T7K1000, Toshiba 1.8", Seagate Momentus 5400.3, 5400.3 ED, 7200.2, Barracuda 7200.11 and ES. Updated Toshiba 2.5", Seagate Barracuda 7200.9 and 7200.10. Added missing "(Hitachi )?" to Travelstar entries. [CF] Drive database: Added several Western Digital Caviar and Scorpio drives, added Caviar RE EIDE family. [CF] Drive database: Added Samsung P80 series with *-25 firmware. [CF] Replaced 'head [-n] -1' by 'sed 1...' in autogen.sh to avoid portability issues. [BA] Fixed autogen.sh script so that it uses 'grep -n 1' if 'grep -1' fails. Needed for Mac OS X 10.4. Uck. [CF] Windows: Added IOCTL_STORAGE_PREDICT_FAILURE. This allows access to ATA SMART status and data if other calls do not work. Thanks to Jaroslaw Kowalski for pointing this out. Added support to use this function without admin rights. [CF] Added ATA-8 revision 4c message text. [CF] Added compiler.h to cciss_ioctl.h header check to prevent configure warning. [SS] DragonFly support added (using os_freebsd code; untested). [CF] smartctl: Fixed ATA identify byte swapping issue on big-endian platforms. This regression was introduced by the change for '-F swapid'. Thanks to Matthew Butch for bug report and testing. [DG] SAT/SCSI: Improve SAT error processing code. Aborted commands from ATA devices (typically because SMART was disabled) were not being properly detected. [GG] smartd: wait for the pid file to show up, return an error if it doesn't [JH] fix bad return code (get STATUS) for QNX Part [JH] initial porting to QNX Neutrino 6.3.2 need at this time a prerelease devb-eide driver and libcam.so.2 only tested for X86 Target, but devb-eide and lobcam.so.2 available for X86/ARM the officional driver coming soon with the next QNX release create two new source files os_qnxnto.[c..h] [CF] smartd: Added option '-n, --no-fork' so that smartd works better with modern init methods. Thanks to Enrico Scholz for the patch from 2005-12-24. [CF] Windows: Improved ATA/SCSI device type detection and DEVICESCAN. This also fixes a regression in 3ware DEVICESCAN. [CF] smartd: Don't start self tests in first pass to avoid performance problems during boot. https://bugzilla.novell.com/show_bug.cgi?id=192591 [CF] Fixed regression in SMART STATUS command on Win9x/ME. [BA] Fixed 3ware issue with new controllers. Documentation said that one could address up to 24 disks on a single controller, but in fact one was limited to 16 disks. This is now fixed: up to 32 disks can be addressed. Thanks to Adam Radford. NOTE1: I have patched the Linux and FreeBSD code but not modified the Win32 code (it already supports up to 32 disks). NOTE2: NOT TESTED ON LINUX. Do not use this on a production box! I will remove this NOTE2 as soon as some positive test reports are recieved. NOTE3: NOT TESTED ON FREEBSD. Do not use this on a production box! I will remove this NOTE3 as soon as some positive test reports are recieved. [CF] Windows installer: Added explorer drive menu, CMD window, UBCD4Win plugin, smartd service update and other minor improvements. [CF] Windows: Modified drive letter handling for explorer drive context menu: try SCSI if type is unknown, allow 'X:\.' syntax. [CF] Windows: Added automatic ATA/SCSI device type detection and SCSI device scanning. The device names '/dev/sdX' and '/dev/pd' now work for both ATA and SCSI disks. [CF] smartctl: Added ability to parse '-r ataioctl,2' output from stdin ('-') and simulate the ATA commands for testing purposes. [BA] SMART Attributes: added 187, 189, more accurate name for 190. [CF] Windows: Added drive letters 'X:' as alternate disk device names. [CF] smartctl: Added '-F swapid' to fix ATA identify string byte ordering. Added '-q noserial' to suppress serial number output. [CF] Windows: Added '/dev/n?st' as alternate device names for SCSI tapes. These names are also used by Cygwin's /dev emulation layer. Thanks to Corinna Vinschen (Cygwin project lead) for pointing this out. [CF] Windows: Added IOCTL_SCSI_MINIPORT_*SMART* for commands not handled properly by SMART_IOCTL in disk class driver. This allows to use READ_LOG, WRITE_LOG and ABORT_SELFTEST even if the driver does not support ATA_PASS_THROUGH. [CF] Added ATA-8 revision 4, fixed WRITE LOG '-r ioctl' output. [BA] Updated smartctl and smartd so that they can be used with the latest 3ware controllers which have 24 ports. Also updated docs. Thanks to Tim Bell at CERN. [GG] bit 4 in smartctl's return code might be set even when the dist check didn't return "DISK OK" [CF] Drive database: added '-F samsung3' for Samsung P80 firmware BH100-35. [SS] Applied patch from Dean Bennett to fix scheduled tests on Highpoint RAID controllers. [BA] Added patch from Tejun Heo http://thread.gmane.org/gmane.linux.ide/13222/focus=13235 to fix broken auto-offline and auto-save via libata. Very clean fix: does it "the right way". Thanks Tejun! [CF] Added message text for ATA-7 self-test execution status 8 ("... handling damage"). [GG] cciss: support more than 16 disks (patch taken from http://cciss.sourceforge.net/smartmontools_cciss_more_than_16_drives.patch and adjusted for smartd) [DG] Solaris: [SCSI] add USCSI_RQENABLE flag to uscsi pass-through so sense buffer is made available. Expand reporting at this level. [GK] Darwin: Improve handling of powered-down drives. [SS] CCISS physical drive enumeration method changed (incompatibly). [CF] Fixed smartd crash on missing '-s' directive argument. [SS] Support CCISS on FreeBSD (kernel source is required until FreeBSD PR 109813 is fixed). [DG] SCSI/TAPE: some IBM tape drives don't react properly to a LOG SENSE with an allocation length of 4; work around for that case. [CF] Applied Guido's patch to fix CCISS LUN array bounds check (openSUSE bug #239956) and remove trailing spaces in os_linux.cpp. [CF] Fixed 64-bit compilation issue in SCT status struct. [DG] SAT/SCSI: make real SCSI disks visible to DEVICESCAN in smartd again. [CF] Fixed check of SCT temperature table size. [CF] Added ATA-8 draft revisions, added SCT status format 3. [CF] Drive database: added Samsung T166 series. [CF] ATA: Added ',p' option for '-t scttempint,N' to make setting persistent. [CF] ATA: Added '-t scttempint,N' option to set SCT temperature logging interval. [CF] ATA: Added '-l scttemp[sts,hist]' options to print disk temperature information and history table provided by SMART Command Transport (SCT) Feature Set. [CF] ATA: Added '-t selective,{redo,next,cont}' commands to perform tests based on the last ranges still stored on disk. Added 'N+SIZE' and 'N-max' format for LBA range specification. [CF] Added Min/Max Temperature format used in attribute 190 of recent Maxtor disks (DiamondMax 20). [CF] Linux: Added check for to allow build (without CCISS support) also when this file is missing. [CF] Added -F samsung3 option to correct firmware bug reporting completed self-tests as still in progress. Thanks to Manfred Schwarb for the patch. [CF] Added missing const specifiers (undetected by gcc 3.4 and 4.X) to fix compilation with gcc 2.X. [CF] Linux: compile fix for SuSE, config.h must be included first. smartmontools 5.37 Experimental Release [CF] Windows: Added alternate method for (limited) monitoring of 3ware controllers by parsing the output of CLI or 3DM. Either "tw_cli" can be run internally ("/dev/tw_cli/cx/py"), or data can be read from standard input ("/dev/tw_cli/stdin") or clipboard ("/dev/tw_cli/clip"). [DG] Remove linux specific libata detect code; rely on general SAT code. smartd should now generate a sensible log message for ATA devices behind a SAT layer on all architectures. [BA] Increased max line length MAXLINELEN for /etc/smartd.conf from 128 to 256 characters to handle long strings in /dev/disk/by-id. Thanks to Martin Krafft. [PW] Drive database: added missing drives from Seagate Momentus 5400.2 family [BA] Finished Christian's fix (next item below) by removing LINUX_86_64 hack from configure.in. [CF] Fixed inclusion of PRI?64 macros from inttypes.h. [CF] Windows: Added WRITE LOG to support selective self tests. [CF] Fix selective self test log revision number if '-T permissive' is specified (Tested with Samsung HD401LJ). [CF] Windows: Fixed int64 printf format for MinGW runtime. [PW] Drive database: added Seagate Barracuda 7200.10 family, Seagate Momentus 42 family, Maxtor DiamondMax 60 ATA 66 family, Maxtor DiamondMax 60 ATA 100 family, and Western Digital Caviar Serial ATA family [PW] Drive database: added missing drives from Seagate Barracuda 7200.9 family, Seagate Barracuda 7200.7 family, Seagate Momentus 7200.1 family, Toshiba 2.5" HDD family (80 GB and above), Western Digital Caviar RE Serial ATA family, Hitachi Deskstar 7K80 family, and Maxtor DiamondMax 4320 Ultra ATA family [BA] Linux: compile fix for SuSE. Check for existence of linux/compiler.h and include in os_linux.h if present. Thanks to SB. [BA] smartd: DEVICESCAN will now pick up SATA/SAT devices attached to a SCSI device tree via SAT translation. Note: this is a bit of a hack. I will document it once I know if this needs to be Linux only or can have more general application. [BA] Added a couple SATA commands to the tables -- thanks DG! Phil -- how about going through and systematically adding these new commands to atacmdnames.cpp? [BA] Linux s86_64: get rid of some compiler warnings on x86_64 Linux systems. [CF] Windows: Added missing support for READ_LOG, ABORT_SELFTEST and CHECK_POWER_STATE for 3ware 9000 controllers. Thanks to Greg de Valois for implementing this new ioctl in the driver. [PW] Drive database: added Seagate NL35 SATA family. Thanks to Kai Harrekilde-Petersen for providing a patch. [DG] [SCSI, Windows] add SPT interface for NT and later. New device names are "pd", "sd" and "tape". [PW] Drive database: added Western Digital Scorpio family, Fujitsu MHV family, Maxtor MaXLine Pro 500 family, and Maxtor DiamondMax 11 family [PW] Drive database: added missing drives from Toshiba 2.5" HDD (30-60 GB) family, Maxtor DiamondMax 10 family, Seagate Barracuda 7200.8 family, Fujitsu MHT family, and Maxtor DiamondMax Plus 8 family [SB] Added examplescripts/Example4 using powersave-notify. [SB] More temperature monitoring examples in smartd.conf with DEVICESCAN. [SB] Minor improvements of SuSE part of init script. [CF] Drive database: added Samsung P80 series, P120 series, SP8004H and T series. [GG] Add CCISS (Compaq Smart Array Controller) support with contributions from Praveen Chidambaram, Douglas Gilbert, Guido Guenther and Frédéric BOITEUX [PW] Drive database: added Hitachi Deskstar T7K250 and Hitachi Deskstar 7K500 series. Thanks to L. J. Wu for providing a patch [PW] Drive database: added Maxtor MaXLine III family, Seagate U7 family, Seagate ST34321A, FUJITSU MHM2060AT, FUJITSU MHT2040AS, Western Digital Caviar SE16 family, IBM Travelstar 4GT family, QUANTUM FIREBALLP KA9.1, QUANTUM FIREBALL SE4.3A, TOSHIBA MK1032GAX, TOSHIBA MK4026GAX [PW] Drive database: added missing drives from Western Digital Caviar SE (Serial ATA) and WD Raptor families [CF] Windows: Added support for 3ware 9000 controllers using extended SMART functionality in new 3ware driver. This includes DEVICESCAN support for at most 2 controllers. Thanks to Greg de Valois from AMCC/3ware for new driver features, development support and hardware for testing. [SZ] smartd: Support HighPoint RocketRAID controller under GNU/linux [DG] [SCSI] First cut for '-l background' to show background scan results log [SZ] smartctl: Support HighPoint RocketRAID controller under GNU/linux [KS] C++ compile fixes for Solaris with a few cleanups. [BA] C++ compile fixes for Darwin (thanks to CF) [CF] Removed old *.c files (now in CVS Attic). [CF] Added changes for C++ to platform independent and Windows related files. [BA] Tagged last .c Version with PRE_MOVE_TO_CPP. Copied *.c,v to *.cpp,v in CVS repository to preserve history of source files. Removed sm5_Darwin repository. [CF] smartctl: Added -n option to skip checks when disk is in low-power mode. [CF] Windows: Added alternate system call for power state check because the PASS THROUGH calls may spin up the disk. [CF] smartd: Modified power state logging to report state changes instead of standby condition. [CF] smartd: Ignore -n directive on scheduled self tests. [DG] [SCSI] Make start stop cycle counter log page decoding more robust [DG] Modify smartctl (but not smartd) to detect probable ATA devices behind a SAT layer. In the absence of an explicit device type, change to device type 'sat'. [DG] Add indication that controller (device) type has been explicitly set. Preparation for automatic detection of 'sat' device type unless user specifies a device type. [SS] NetBSD: Deliver strings from ata_identify_device properly on little- and big-endian platforms. [BA] Added published ANSI ATA-7 spec to list of recognized ATA versions. [BA] Code janitor: added missing header strings for '-V' option. [DG] [SATA] Extend 'sat' device type to allow either 12 or 16 byte variant of the SAT ATA PASS THROUGH SCSI command. Syntax is '-d sat,' where can be 0, 12 or 16 . The ',' part is optional. Currently defaults to 16 byte variant but that could be made platform or even device dependent. [DG] [SATA] Add new 'sat' device type for SATA disks behind a SCSI to ATA Translation (SAT) Layer (SATL). Uses the ATA PASS THROUGH (16) SCSI command thence the generic SCSI passthrough for each platform. [CF] Windows: Added script and make targets to create installer with NSIS (http://nsis.sourceforge.net/) [CF] Updated hostname and links for new SourceForge CVS service. [CF] smartd: Added '-W' directive to track temperature changes and warn if temperature limits are reached. [CF] Windows: Added IOCTL_ATA_PASS_THROUGH (Win2003, XP SP2) for commands unsupported by SMART_IOCTL. Added device specific options to select subset and ordering of the ATA IOCTLs actually used. These options are specified as modifiers of the device name (/dev/hd[a-j]:[saic]+) [CF] Windows: Added support for drives 4-7 (/dev/hd[e-h]) via SMARTVSE.VXD on Win9x/ME. Thanks to Dariusz Rzonca for patch and testing. [DG] [SCSI/SATA linux] from lk 2.6.17 SATA disk identification in libata will change. Expand LibAta detection to see old identifier and new variant (VPD page 0x83). [BA] Identified Attribute 190 for Western Digital disks. This stores temperature in Celsius, just like Attribute 194. But it has a failure threshold set to correspond to the maximum design operating temperature of the disk, which is 55 Celsius on the WD800JD drives that I studied. So if this Attribute has 'failed in the past' this means that the maximum disk operating temperature has been exceeded. [GK] Darwin: Add support for AHCI drivers found in Intel-based Macs. 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 smartmontools-7.0/cissio_freebsd.h0000644000175000010010000001441611576377613014371 00000000000000/*- * Copyright (c) 2001 Michael Smith * 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. * * $FreeBSD: src/sys/dev/ciss/cissio.h,v 1.6.2.1.6.1 2010/12/21 17:09:25 kensmith Exp $ */ /* * Driver ioctl interface. * * Note that this interface is API-compatible with the Linux implementation * except as noted, and thus this header bears a striking resemblance to * the Linux driver's cciss_ioctl.h. * */ #include #pragma pack(1) typedef struct { u_int8_t bus; u_int8_t dev_fn; u_int32_t board_id; } cciss_pci_info_struct; typedef struct { u_int32_t delay; u_int32_t count; } cciss_coalint_struct; typedef char NodeName_type[16]; typedef u_int32_t Heartbeat_type; #define CISS_PARSCSIU2 0x0001 #define CISS_PARCSCIU3 0x0002 #define CISS_FIBRE1G 0x0100 #define CISS_FIBRE2G 0x0200 typedef u_int32_t BusTypes_type; typedef char FirmwareVer_type[4]; typedef u_int32_t DriverVer_type; /* passthrough command definitions */ #define SENSEINFOBYTES 32 #define CISS_MAX_LUN 16 #define LEVEL2LUN 1 #define LEVEL3LUN 0 /* command status value */ #define CMD_SUCCESS 0x0000 #define CMD_TARGET_STATUS 0x0001 #define CMD_DATA_UNDERRUN 0x0002 #define CMD_DATA_OVERRUN 0x0003 #define CMD_INVALID 0x0004 #define CMD_PROTOCOL_ERR 0x0005 #define CMD_HARDWARE_ERR 0x0006 #define CMD_CONNECTION_LOST 0x0007 #define CMD_ABORTED 0x0008 #define CMD_ABORT_FAILED 0x0009 #define CMD_UNSOLICITED_ABORT 0x000A #define CMD_TIMEOUT 0x000B #define CMD_UNABORTABLE 0x000C /* transfer direction */ #define XFER_NONE 0x00 #define XFER_WRITE 0x01 #define XFER_READ 0x02 #define XFER_RSVD 0x03 /* task attribute */ #define ATTR_UNTAGGED 0x00 #define ATTR_SIMPLE 0x04 #define ATTR_HEADOFQUEUE 0x05 #define ATTR_ORDERED 0x06 #define ATTR_ACA 0x07 /* CDB type */ #define TYPE_CMD 0x00 #define TYPE_MSG 0x01 /* command list structure */ typedef union { struct { u_int8_t Dev; u_int8_t Bus:6; u_int8_t Mode:2; } __packed PeripDev; struct { u_int8_t DevLSB; u_int8_t DevMSB:6; u_int8_t Mode:2; } __packed LogDev; struct { u_int8_t Dev:5; u_int8_t Bus:3; u_int8_t Targ:6; u_int8_t Mode:2; } __packed LogUnit; } SCSI3Addr_struct; typedef struct { u_int32_t TargetId:24; u_int32_t Bus:6; u_int32_t Mode:2; SCSI3Addr_struct Target[2]; } __packed PhysDevAddr_struct; typedef struct { u_int32_t VolId:30; u_int32_t Mode:2; u_int8_t reserved[4]; } __packed LogDevAddr_struct; typedef union { u_int8_t LunAddrBytes[8]; SCSI3Addr_struct SCSI3Lun[4]; PhysDevAddr_struct PhysDev; LogDevAddr_struct LogDev; } __packed LUNAddr_struct; typedef struct { u_int8_t CDBLen; struct { u_int8_t Type:3; u_int8_t Attribute:3; u_int8_t Direction:2; } __packed Type; u_int16_t Timeout; u_int8_t CDB[16]; } __packed RequestBlock_struct; typedef union { struct { u_int8_t Reserved[3]; u_int8_t Type; u_int32_t ErrorInfo; } __packed Common_Info; struct { u_int8_t Reserved[2]; u_int8_t offense_size; u_int8_t offense_num; u_int32_t offense_value; } __packed Invalid_Cmd; } __packed MoreErrInfo_struct; typedef struct { u_int8_t ScsiStatus; u_int8_t SenseLen; u_int16_t CommandStatus; u_int32_t ResidualCnt; MoreErrInfo_struct MoreErrInfo; u_int8_t SenseInfo[SENSEINFOBYTES]; } __packed ErrorInfo_struct; typedef struct { LUNAddr_struct LUN_info; /* 8 */ RequestBlock_struct Request; /* 20 */ ErrorInfo_struct error_info; /* 48 */ u_int16_t buf_size; /* 2 */ u_int8_t *buf; /* 4 */ } __packed IOCTL_Command_struct; #ifdef __amd64__ typedef struct { LUNAddr_struct LUN_info; /* 8 */ RequestBlock_struct Request; /* 20 */ ErrorInfo_struct error_info; /* 48 */ u_int16_t buf_size; /* 2 */ u_int32_t buf; /* 4 */ } __packed IOCTL_Command_struct32; #endif /************************************************************************ * Command queue statistics */ #define CISSQ_FREE 0 #define CISSQ_NOTIFY 1 #define CISSQ_COUNT 2 struct ciss_qstat { uint32_t q_length; uint32_t q_max; }; union ciss_statrequest { uint32_t cs_item; struct ciss_qstat cs_qstat; }; /* * Note that we'd normally pass the struct in directly, but * this code is trying to be compatible with other drivers. */ #define CCISS_GETPCIINFO _IOR ('C', 200, cciss_pci_info_struct) #define CCISS_GETINTINFO _IOR ('C', 201, cciss_coalint_struct) #define CCISS_SETINTINFO _IOW ('C', 202, cciss_coalint_struct) #define CCISS_GETNODENAME _IOR ('C', 203, NodeName_type) #define CCISS_SETNODENAME _IOW ('C', 204, NodeName_type) #define CCISS_GETHEARTBEAT _IOR ('C', 205, Heartbeat_type) #define CCISS_GETBUSTYPES _IOR ('C', 206, BusTypes_type) #define CCISS_GETFIRMVER _IOR ('C', 207, FirmwareVer_type) #define CCISS_GETDRIVERVER _IOR ('C', 208, DriverVer_type) #define CCISS_REVALIDVOLS _IO ('C', 209) #define CCISS_PASSTHRU _IOWR ('C', 210, IOCTL_Command_struct) #ifdef __amd64 #define CCISS_PASSTHRU32 _IOWR ('C', 210, IOCTL_Command_struct32) #endif #define CCISS_GETQSTATS _IOWR ('C', 211, union ciss_statrequest) #pragma pack() smartmontools-7.0/compile0000755000175000010010000001632613412155342012575 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1999-2017 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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, see . # 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. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # 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-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: smartmontools-7.0/config.guess0000755000175000010010000012573113412155342013540 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2017 Free Software Foundation, Inc. timestamp='2017-03-05' # 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 3 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, see . # # 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. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . 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 1992-2017 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 ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # 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 "$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 ; set_cc_for_build= ;' # 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 case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # 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 tuples: *-*-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=`(uname -p 2>/dev/null || \ /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 ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-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) and ABI. case "${UNAME_MACHINE_ARCH}" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ 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 # Determine ABI tags. case "${UNAME_MACHINE_ARCH}" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` ;; 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/[-_].*//' | cut -d. -f1,2` ;; 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}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; *:Sortix:*:*) echo ${UNAME_MACHINE}-unknown-sortix exit ;; 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` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; 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 ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; 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 ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; 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 ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; 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 ;; 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 ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; 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 ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # 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 ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; 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 && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; 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 ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????: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 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; 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 ;; *: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 if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi 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 ;; *:AIX:*:[4567]) 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/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 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 eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 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 && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; 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 ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; 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 ;; 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 ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; 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 ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; 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 -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; e2k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; k1om:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #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-${LIBC}"; exit; } ;; mips64el:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; 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-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; 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 ;; 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 ;; 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 ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; 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 ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. 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 ;; 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 ;; 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 i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; 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 ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 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; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' 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; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *: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 ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; 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 ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *: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 ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; NSX-?:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *: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 ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac cat >&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: smartmontools-7.0/config.h.in0000644000175000010010000001252613412155334013241 00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* freebsd ciss header location */ #undef CISS_LOCATION /* smartmontools CVS Tag */ #undef CONFIG_H_CVSID /* Define to 1 if C++ compiler supports __attribute__((packed)) */ #undef HAVE_ATTR_PACKED /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the header file. */ #undef HAVE_CAP_NG_H /* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the header file. */ #undef HAVE_DDK_NTDDDISK_H /* Define to 1 if you have the header file. */ #undef HAVE_DEV_ATA_ATAVAR_H /* Define to 1 if you have the header file. */ #undef HAVE_DEV_CISS_CISSIO_H /* Define to 1 if you have the `ftime' function. */ #undef HAVE_FTIME /* Define to 1 if you have the `getopt_long' function. */ #undef HAVE_GETOPT_LONG /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `cap-ng' library (-lcap-ng). */ #undef HAVE_LIBCAP_NG /* Define to 1 if you have the `selinux' library (-lselinux). */ #undef HAVE_LIBSELINUX /* Define to 1 if you have the `systemd' library (-lsystemd). */ #undef HAVE_LIBSYSTEMD /* Define to 1 if you have the `usb' library (-lusb). */ #undef HAVE_LIBUSB /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_CCISS_IOCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_COMPILER_H /* Define to 1 if you have the header file. */ #undef HAVE_LOCALE_H /* Define to 1 if the type `long double' works and has more range or precision than `double'. */ #undef HAVE_LONG_DOUBLE_WIDER /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file. */ #undef HAVE_NTDDDISK_H /* Define to 1 if you have the `regcomp' function. */ #undef HAVE_REGCOMP /* Define to 1 if you have the header file. */ #undef HAVE_SELINUX_SELINUX_H /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* 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 header file. */ #undef HAVE_SYSTEMD_SD_DAEMON_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_SYSMACROS_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 you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if the `snprintf' function is sane. */ #undef HAVE_WORKING_SNPRINTF /* Define to 1 if the system has the type `__int128'. */ #undef HAVE___INT128 /* Define to 1 to use generic LE/BE code instead */ #undef IGNORE_FAST_LEBE /* Define to 1 if os_*.cpp still uses the old interface */ #undef OLD_INTERFACE /* 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 home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 to enable check on each SCSI cdb */ #undef SCSI_CDB_CHECK /* smartmontools Build Host */ #undef SMARTMONTOOLS_BUILD_HOST /* smartmontools Configure Arguments */ #undef SMARTMONTOOLS_CONFIGURE_ARGS /* 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 /* Define to 1 to use C++11 std::regex instead of POSIX regex(3) */ #undef WITH_CXX11_REGEX /* Define to 1 to include NVMe devices in smartd DEVICESCAN. */ #undef WITH_NVME_DEVICESCAN /* Define to 1 to enable legacy ATA support on Solaris SPARC. */ #undef WITH_SOLARIS_SPARC_ATA /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif smartmontools-7.0/config.sub0000755000175000010010000010711413412155343013177 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2017 Free Software Foundation, Inc. timestamp='2017-02-07' # 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 3 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, see . # # 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. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # 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. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # 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 or 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 1992-2017 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 ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # 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 ;; * ) 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-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) 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 | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -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 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -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/'` ;; -sco5v6*) # 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*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -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 \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # 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-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # 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 ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; 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 ;; cr16 | cr16-*) basic_machine=cr16-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 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` os=$os"spe" ;; 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*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 ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; 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 ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; 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 ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; 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 ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; 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 | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) 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) 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 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; 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 ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; 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 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; 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 ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; 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 ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-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 ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) 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. -auroraux) os=-auroraux ;; -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* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -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* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -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* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) # 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* | -haiku* \ | -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 ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -ios) ;; -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 score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) 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 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; 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 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-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 ;; -cnk*|-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 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: smartmontools-7.0/configure0000755000175000010010000100322413412155344013122 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for smartmontools 7.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 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 more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= 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 IFS=$as_save_IFS ;; 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 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: smartmontools-support@listi.jpberlin.de about your $0: system, including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; 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 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # 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 as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # 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 sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # 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'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='smartmontools' PACKAGE_TARNAME='smartmontools' PACKAGE_VERSION='7.0' PACKAGE_STRING='smartmontools 7.0' PACKAGE_BUGREPORT='smartmontools-support@listi.jpberlin.de' PACKAGE_URL='' ac_unique_file="smartctl.cpp" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS OS_WIN64_FALSE OS_WIN64_TRUE OS_WIN32_NSIS_FALSE OS_WIN32_NSIS_TRUE OS_WIN32_MINGW_FALSE OS_WIN32_MINGW_TRUE OS_WIN32_FALSE OS_WIN32_TRUE OS_SOLARIS_FALSE OS_SOLARIS_TRUE OS_DARWIN_FALSE OS_DARWIN_TRUE DRIVEDB_BRANCH os_win32_manifest os_man_filter os_nisdomainname os_dnsdomainname os_hostname os_mailer os_dltools os_libs os_deps with_nvme_devicescan smartmontools_release_time smartmontools_release_date releaseversion NEED_REGEX_FALSE NEED_REGEX_TRUE systemdenvfile INSTALL_SYSTEMDUNIT_FALSE INSTALL_SYSTEMDUNIT_TRUE systemdsystemunitdir SYSTEMD_LDADD CAPNG_LDADD smartd_suffix ENABLE_ATTRIBUTELOG_FALSE ENABLE_ATTRIBUTELOG_TRUE attributelogdir attributelog ENABLE_SAVESTATES_FALSE ENABLE_SAVESTATES_TRUE savestatesdir savestates ENABLE_SCRIPTPATH_FALSE ENABLE_SCRIPTPATH_TRUE scriptpath smartdplugindir smartdscriptdir gnupg ENABLE_UPDATE_SMART_DRIVEDB_FALSE ENABLE_UPDATE_SMART_DRIVEDB_TRUE with_update_smart_drivedb ENABLE_DRIVEDB_FALSE ENABLE_DRIVEDB_TRUE drivedbdir exampledir initdfile INSTALL_INITSCRIPT_FALSE INSTALL_INITSCRIPT_TRUE initddir ASFLAGS gcc_have_attr_packed NEED_GETOPT_LONG_FALSE NEED_GETOPT_LONG_TRUE EGREP GREP CXXCPP IS_SVN_BUILD_FALSE IS_SVN_BUILD_TRUE svn_deps host_os host_vendor host_cpu host build_os build_vendor build_cpu build MAKENSIS WINDRES WINDMC PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG am__fastdepCCAS_FALSE am__fastdepCCAS_TRUE CCASDEPMODE CCASFLAGS CCAS am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE ac_ct_CC CFLAGS CC am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CXX CPPFLAGS LDFLAGS CXXFLAGS CXX MAINT MAINTAINER_MODE_FALSE MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_maintainer_mode enable_dependency_tracking with_initscriptdir with_exampledir with_drivedbdir with_update_smart_drivedb with_gnupg with_smartdscriptdir with_smartdplugindir with_scriptpath with_savestates with_attributelog enable_sample enable_scsi_cdb_check enable_fast_lebe with_os_deps with_selinux with_libcap_ng with_libsystemd with_systemdsystemunitdir with_systemdenvfile with_nvme_devicescan with_solaris_sparc_ata with_signal_func with_working_snprintf with_mingw_aslr with_cxx11_option with_cxx11_regex ' ac_precious_vars='build_alias host_alias target_alias CXX CXXFLAGS LDFLAGS LIBS CPPFLAGS CCC CC CFLAGS CCAS CCASFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR WINDMC WINDRES MAKENSIS CXXCPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # 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. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= 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 case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -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) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$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 ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$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 ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) 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 ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=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 ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_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'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" 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 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 ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # 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 the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | 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 test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # 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 7.0 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 \`..'] 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] --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] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/smartmontools] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _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 7.0:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-maintainer-mode enable make rules and dependencies not useful (and sometimes confusing) to the casual installer --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-sample Enables appending .sample to the installed smartd rc script and configuration file --enable-scsi-cdb-check do sanity check on each SCSI cdb --disable-fast-lebe use generic little-endian/big-endian code instead Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-initscriptdir=[DIR|no] Location of init scripts [no] --with-exampledir=DIR Location of example scripts [DOCDIR/examplescripts] --with-drivedbdir[=DIR|yes|no] Location of drive database file [DATADIR/smartmontools] --with-update-smart-drivedb[=yes|no|X.Y] Install update-smart-drivedb script (and backport it to branches/RELEASE_X_Y_DRIVEDB) [yes] --with-gnupg[=FILE|yes|no] GnuPG used to verify drivedb.h [gpg] --with-smartdscriptdir=DIR Location of smartd_warning.sh script [SYSCONFDIR] --with-smartdplugindir=[DIR|no] Location of smartd_warning.sh plugin scripts [SMARTDSCRIPTDIR/smartd_warning.d] --with-scriptpath=[PATH|no] PATH variable set within scripts [/usr/local/bin:/usr/bin:/bin] --with-savestates[=PREFIX|yes|no] Enable default smartd state files [no] (yes=LOCALSTATEDIR/lib/smartmontools/smartd.) --with-attributelog[=PREFIX|yes|no] Enable default smartd attribute log files [no] (yes=LOCALSTATEDIR/lib/smartmontools/attrlog.) --with-os-deps='os_module.o ...' Specify OS dependent module(s) [guessed] --with-selinux[=yes|no] Enables SELinux support [no] --with-libcap-ng[=auto|yes|no] Add Libcap-ng support to smartd [auto] --with-libsystemd[=auto|yes|no] Add systemd 'Type=notify' support to smartd [auto] --with-systemdsystemunitdir[=DIR|auto|yes|no] Location of systemd service files [auto] --with-systemdenvfile[=FILE|auto|yes|no] Path of systemd EnvironmentFile [auto] --with-nvme-devicescan[=yes|no] Include NVMe devices in smartd DEVICESCAN [Linux,Windows:yes;Others:no] --with-solaris-sparc-ata[=yes|no] Enable legacy ATA support on Solaris SPARC (requires os_solaris_ata.s from SVN repository) [no] --with-signal-func=[sigaction|sigset|signal] Function to set signal(2) action [sigaction] --with-working-snprintf[=yes|no] Function snprintf() handles output truncation as specified by C99 [yes] --with-mingw-aslr[=auto|yes|low|no] Enable ASLR for MinGW executables [auto] --with-cxx11-option=[OPTION|auto|no] Compiler option to enable C++11 support for future versions of smartmontools, 'no' if unsupported [auto] --with-cxx11-regex[=yes|no] Use C++11 std::regex instead of POSIX regex(3) [no] Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CC C compiler command CFLAGS C compiler flags CCAS assembler compiler command (defaults to CC) CCASFLAGS assembler compiler flags (defaults to CFLAGS) PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path WINDMC Windows message compiler command WINDRES Windows resource compiler command MAKENSIS NSIS compiler command CXXCPP 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 ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested 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 else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF smartmontools configure 7.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 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 fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ------------------------------------------------------ ## ## Report this to smartmontools-support@listi.jpberlin.de ## ## ------------------------------------------------------ ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_mongrel # ac_fn_cxx_try_run LINENO # ------------------------ # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_cxx_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_run # ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_cxx_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_compile # ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES # --------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_cxx_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_type # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_cxx_check_func LINENO FUNC VAR # ------------------------------------ # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_cxx_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* 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_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_func cat >config.log <<_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 7.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { 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` /usr/bin/hostinfo = `(/usr/bin/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=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&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_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=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append 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 as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset 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: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > 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 cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } 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. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_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 $ac_precious_vars; 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,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_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 # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_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. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## 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 am__api_version='1.15' 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 as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # 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. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&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_fn_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 rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir 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. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$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' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # 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 ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file 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 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". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file 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 $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "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 # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi 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='7.0' 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"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi # Version of drive database branch smartmontools_drivedb_version=7.0 smartmontools_cvs_tag=`echo '$Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $'` smartmontools_release_date=2018-12-30 smartmontools_release_time="14:47:55 UTC" cat >>confdefs.h <<_ACEOF #define SMARTMONTOOLS_CONFIGURE_ARGS "$ac_configure_args" _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 "https://www.smartmontools.org/" _ACEOF ac_config_headers="$ac_config_headers config.h" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } # Check whether --enable-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 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 $as_echo "$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=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM 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. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 $as_echo_n "checking whether the C++ compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C++ compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 $as_echo_n "checking for C++ compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; 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 | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-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='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&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". rm -rf conftest.dir 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_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac 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 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # 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. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; 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 ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj 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_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= 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 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_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" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS 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 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* 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 -std 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 -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 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 for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&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". rm -rf conftest.dir 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 am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac 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 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # 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. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; 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 ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj 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 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$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 depcc="$CCAS" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CCAS_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&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". rm -rf conftest.dir 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_CCAS_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false 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 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # 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. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; 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 ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj 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_CCAS_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CCAS_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CCAS_dependencies_compiler_type" >&5 $as_echo "$am_cv_CCAS_dependencies_compiler_type" >&6; } CCASDEPMODE=depmode=$am_cv_CCAS_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CCAS_dependencies_compiler_type" = gcc3; then am__fastdepCCAS_TRUE= am__fastdepCCAS_FALSE='#' else am__fastdepCCAS_TRUE='#' am__fastdepCCAS_FALSE= fi if test "$cross_compiling" = "no"; then if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac case "${host}" in *-*-mingw*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windmc", so it can be a program name with args. set dummy ${ac_tool_prefix}windmc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_WINDMC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$WINDMC"; then ac_cv_prog_WINDMC="$WINDMC" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_WINDMC="${ac_tool_prefix}windmc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi WINDMC=$ac_cv_prog_WINDMC if test -n "$WINDMC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDMC" >&5 $as_echo "$WINDMC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_WINDMC"; then ac_ct_WINDMC=$WINDMC # Extract the first word of "windmc", so it can be a program name with args. set dummy windmc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_WINDMC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_WINDMC"; then ac_cv_prog_ac_ct_WINDMC="$ac_ct_WINDMC" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_WINDMC="windmc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_WINDMC=$ac_cv_prog_ac_ct_WINDMC if test -n "$ac_ct_WINDMC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDMC" >&5 $as_echo "$ac_ct_WINDMC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_WINDMC" = x; then WINDMC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac WINDMC=$ac_ct_WINDMC fi else WINDMC="$ac_cv_prog_WINDMC" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. set dummy ${ac_tool_prefix}windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_WINDRES+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$WINDRES"; then ac_cv_prog_WINDRES="$WINDRES" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_WINDRES="${ac_tool_prefix}windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi WINDRES=$ac_cv_prog_WINDRES if test -n "$WINDRES"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 $as_echo "$WINDRES" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_WINDRES"; then ac_ct_WINDRES=$WINDRES # Extract the first word of "windres", so it can be a program name with args. set dummy windres; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_WINDRES+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_WINDRES"; then ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # 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_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_WINDRES="windres" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES if test -n "$ac_ct_WINDRES"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 $as_echo "$ac_ct_WINDRES" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_WINDRES" = x; then WINDRES="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac WINDRES=$ac_ct_WINDRES fi else WINDRES="$ac_cv_prog_WINDRES" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for makensis" >&5 $as_echo_n "checking for makensis... " >&6; } if test -z "$MAKENSIS"; then if test -n "$PROGRAMFILES" && "$PROGRAMFILES/NSIS/makensis" -VERSION >/dev/null 2>&1; then MAKENSIS="$PROGRAMFILES/NSIS/makensis" elif makensis -VERSION >/dev/null 2>&1; then MAKENSIS=makensis fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${MAKENSIS:-no}" >&5 $as_echo "${MAKENSIS:-no}" >&6; } ;; esac # Check for SVN. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether this is a build from SVN" >&5 $as_echo_n "checking whether this is a build from SVN... " >&6; } is_svn_build=no svn_deps= if test -f "$srcdir/.svn/wc.db"; then # SVN 1.7, 1.8 working copy svn_deps='${srcdir}/.svn/wc.db' elif test -f "${srcdir}/.svn/entries"; then # SVN <= 1.6 working copy (SVN 1.7 has empty entries file) svn_deps='${srcdir}/.svn/entries' fi if test -n "$svn_deps"; then is_svn_build=unknown if (cd "$srcdir" && svn --version && svnversion && svn info) >/dev/null 2>&1; then is_svn_build=yes fi fi if test "$is_svn_build" = "yes"; then IS_SVN_BUILD_TRUE= IS_SVN_BUILD_FALSE='#' else IS_SVN_BUILD_TRUE='#' IS_SVN_BUILD_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $is_svn_build" >&5 $as_echo "$is_svn_build" >&6; } # Note: On Linux, clock_gettime() requires -lrt which implies -lpthreads # Check omitted for now, gettimeofday() provides reasonable precision # AC_SEARCH_LIBS(clock_gettime, rt) # Checks for header files. ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #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)) return 2; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h 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=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in locale.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default" if test "x$ac_cv_header_locale_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LOCALE_H 1 _ACEOF fi done for ac_header in byteswap.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "byteswap.h" "ac_cv_header_byteswap_h" "$ac_includes_default" if test "x$ac_cv_header_byteswap_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_BYTESWAP_H 1 _ACEOF fi done case "$host" in *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*) # Check for FreeBSD twe and twa include files for ac_header in sys/tweio.h sys/twereg.h sys/tw_osl_ioctl.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Check for the FreeBSD CCISS system header and use internal one if not found for ac_header in dev/ciss/cissio.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ciss/cissio.h" "ac_cv_header_dev_ciss_cissio_h" "$ac_includes_default" if test "x$ac_cv_header_dev_ciss_cissio_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DEV_CISS_CISSIO_H 1 _ACEOF $as_echo "#define CISS_LOCATION " >>confdefs.h else $as_echo "#define CISS_LOCATION \"cissio_freebsd.h\"" >>confdefs.h fi done ;; *-*-linux*) # is needed for cciss_ioctl.h at least on SuSE LINUX for ac_header in sys/sysmacros.h linux/compiler.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Check for Linux CCISS include file for ac_header in linux/cciss_ioctl.h do : ac_fn_cxx_check_header_compile "$LINENO" "linux/cciss_ioctl.h" "ac_cv_header_linux_cciss_ioctl_h" "$ac_includes_default #ifdef HAVE_LINUX_COMPILER_H # include #endif " if test "x$ac_cv_header_linux_cciss_ioctl_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_CCISS_IOCTL_H 1 _ACEOF fi done ;; *-*-netbsd*|*-*-openbsd*) for ac_header in dev/ata/atavar.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ata/atavar.h" "ac_cv_header_dev_ata_atavar_h" "$ac_includes_default" if test "x$ac_cv_header_dev_ata_atavar_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DEV_ATA_ATAVAR_H 1 _ACEOF fi done ;; *-*-cygwin*|*-*-mingw*) # Check for Windows DDK header files for ac_header in ntdddisk.h ddk/ntdddisk.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default #include " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ;; esac # Checks for typedefs, and compiler characteristics. ac_fn_cxx_check_type "$LINENO" "__int128" "ac_cv_type___int128" "$ac_includes_default" if test "x$ac_cv_type___int128" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE___INT128 1 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long double with more range or precision than double" >&5 $as_echo_n "checking for long double with more range or precision than double... " >&6; } if ${ac_cv_type_long_double_wider+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include long double const a[] = { 0.0L, DBL_MIN, DBL_MAX, DBL_EPSILON, LDBL_MIN, LDBL_MAX, LDBL_EPSILON }; long double f (long double x) { return ((x + (unsigned long int) 10) * (-1 / x) + a[0] + (x ? f (x) : 'c')); } int main () { static int test_array [1 - 2 * !((0 < ((DBL_MAX_EXP < LDBL_MAX_EXP) + (DBL_MANT_DIG < LDBL_MANT_DIG) - (LDBL_MAX_EXP < DBL_MAX_EXP) - (LDBL_MANT_DIG < DBL_MANT_DIG))) && (int) LDBL_EPSILON == 0 )]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_type_long_double_wider=yes else ac_cv_type_long_double_wider=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_double_wider" >&5 $as_echo "$ac_cv_type_long_double_wider" >&6; } if test $ac_cv_type_long_double_wider = yes; then $as_echo "#define HAVE_LONG_DOUBLE_WIDER 1" >>confdefs.h fi # Checks for library functions. for ac_func in getopt_long do : ac_fn_cxx_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" if test "x$ac_cv_func_getopt_long" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GETOPT_LONG 1 _ACEOF need_getopt_long=no else need_getopt_long=yes fi done if test "$need_getopt_long" = "yes"; then NEED_GETOPT_LONG_TRUE= NEED_GETOPT_LONG_FALSE='#' else NEED_GETOPT_LONG_TRUE='#' NEED_GETOPT_LONG_FALSE= fi for ac_func in clock_gettime ftime gettimeofday do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # Check byte ordering (defines WORDS_BIGENDIAN) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # check for __attribute__((packed)) # (sizeof() check is required to avoid false positives if other # __attribute__((x)) are supported) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports __attribute__((packed))" >&5 $as_echo_n "checking whether $CXX supports __attribute__((packed))... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { struct s { char a; short b; } __attribute__((packed)); typedef char t[sizeof(struct s) == 3 ? 1 : -1]; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : gcc_have_attr_packed=yes else gcc_have_attr_packed=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test "$gcc_have_attr_packed" = "yes"; then $as_echo "#define HAVE_ATTR_PACKED 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_have_attr_packed" >&5 $as_echo "$gcc_have_attr_packed" >&6; } initddir= # Check whether --with-initscriptdir was given. if test "${with_initscriptdir+set}" = set; then : withval=$with_initscriptdir; case "$withval" in auto|yes) as_fn_error $? "'--with-initscriptdir=$withval' is no longer supported" "$LINENO" 5 ;; no) ;; *) initddir="$withval" ;; esac fi if test -n "$initddir"; then INSTALL_INITSCRIPT_TRUE= INSTALL_INITSCRIPT_FALSE='#' else INSTALL_INITSCRIPT_TRUE='#' INSTALL_INITSCRIPT_FALSE= fi # use different init script templates for different OS case "${host}" in *-*-freebsd*) initdfile="smartd.freebsd.initd" ;; *-apple-darwin*) initdfile="com.smartmontools.smartd.plist" ;; *-*-cygwin*) initdfile="smartd.cygwin.initd" ;; *) initdfile="smartd.initd" ;; esac # Check whether --with-exampledir was given. if test "${with_exampledir+set}" = set; then : withval=$with_exampledir; exampledir="$withval" else exampledir='${docdir}/examplescripts' fi drivedbdir='${datadir}/${PACKAGE}' # Check whether --with-drivedbdir was given. if test "${with_drivedbdir+set}" = set; then : withval=$with_drivedbdir; case "$withval" in yes) ;; no) drivedbdir= ;; *) drivedbdir="$withval" ;; esac fi if test -n "$drivedbdir"; then ENABLE_DRIVEDB_TRUE= ENABLE_DRIVEDB_FALSE='#' else ENABLE_DRIVEDB_TRUE='#' ENABLE_DRIVEDB_FALSE= fi drivedb_version=$smartmontools_drivedb_version # Check whether --with-update-smart_drivedb was given. if test "${with_update_smart_drivedb+set}" = set; then : withval=$with_update_smart_drivedb; case "$withval" in yes|no) ;; 5.4[0-3]|6.[0-9]) drivedb_version=$withval; with_update_smart_drivedb=yes ;; *) as_fn_error $? "Invalid drivedb branch version: $withval" "$LINENO" 5 ;; esac else with_update_smart_drivedb=yes fi test -n "$drivedbdir" || with_update_smart_drivedb=no if test "$with_update_smart_drivedb" = "yes"; then ENABLE_UPDATE_SMART_DRIVEDB_TRUE= ENABLE_UPDATE_SMART_DRIVEDB_FALSE='#' else ENABLE_UPDATE_SMART_DRIVEDB_TRUE='#' ENABLE_UPDATE_SMART_DRIVEDB_FALSE= fi gnupg="gpg" # Also check for '--with-gnupg[=yes]' because 'yes' is a valid command with infinite output # Check whether --with-gnupg was given. if test "${with_gnupg+set}" = set; then : withval=$with_gnupg; case "$withval" in yes) ;; no) gnupg= ;; *) gnupg="$withval" ;; esac fi case "$with_update_smart_drivedb:$gnupg" in no:?*) as_fn_error $? " '--without-update-smart-drivedb' now requires '--without-gnupg'. NEWS: update-smart-drivedb now verifies the downloaded drivedb.h file with GnuPG." "$LINENO" 5 ;; esac # Check whether --with-smartdscriptdir was given. if test "${with_smartdscriptdir+set}" = set; then : withval=$with_smartdscriptdir; smartdscriptdir="$withval" else smartdscriptdir='${sysconfdir}' fi # Check whether --with-smartdplugindir was given. if test "${with_smartdplugindir+set}" = set; then : withval=$with_smartdplugindir; smartdplugindir=; test "$withval" != "no" && smartdplugindir="$withval" else smartdplugindir='${smartdscriptdir}/smartd_warning.d' fi # Check whether --with-scriptpath was given. if test "${with_scriptpath+set}" = set; then : withval=$with_scriptpath; scriptpath=; test "$withval" != "no" && scriptpath="$withval" else scriptpath="/usr/local/bin:/usr/bin:/bin" fi if test -n "$scriptpath"; then ENABLE_SCRIPTPATH_TRUE= ENABLE_SCRIPTPATH_FALSE='#' else ENABLE_SCRIPTPATH_TRUE='#' ENABLE_SCRIPTPATH_FALSE= fi savestates= # Check whether --with-savestates was given. if test "${with_savestates+set}" = set; then : withval=$with_savestates; case "$withval" in yes) savestates='${localstatedir}/lib/${PACKAGE}/smartd.' ;; no) ;; *) savestates="$withval" ;; esac fi savestatesdir="${savestates%/*}" if test -n "$savestates"; then ENABLE_SAVESTATES_TRUE= ENABLE_SAVESTATES_FALSE='#' else ENABLE_SAVESTATES_TRUE='#' ENABLE_SAVESTATES_FALSE= fi attributelog= # Check whether --with-attributelog was given. if test "${with_attributelog+set}" = set; then : withval=$with_attributelog; case "$withval" in yes) attributelog='${localstatedir}/lib/${PACKAGE}/attrlog.' ;; no) ;; *) attributelog="$withval" ;; esac fi attributelogdir="${attributelog%/*}" if test -n "$attributelog"; then ENABLE_ATTRIBUTELOG_TRUE= ENABLE_ATTRIBUTELOG_FALSE='#' else ENABLE_ATTRIBUTELOG_TRUE='#' ENABLE_ATTRIBUTELOG_FALSE= fi # Check whether --enable-sample was given. if test "${enable_sample+set}" = set; then : enableval=$enable_sample; smartd_suffix=; test "$enableval" = "yes" && smartd_suffix=".sample" else smartd_suffix=; fi # Check whether --enable-scsi-cdb-check was given. if test "${enable_scsi_cdb_check+set}" = set; then : enableval=$enable_scsi_cdb_check; if test "$enableval" = "yes"; then $as_echo "#define SCSI_CDB_CHECK 1" >>confdefs.h fi fi # Check whether --enable-fast-lebe was given. if test "${enable_fast_lebe+set}" = set; then : enableval=$enable_fast_lebe; if test "$enableval" = "no"; then $as_echo "#define IGNORE_FAST_LEBE 1" >>confdefs.h fi fi # Check whether --with-os-deps was given. if test "${with_os_deps+set}" = set; then : withval=$with_os_deps; for x in $with_os_deps; do case $x in *.o) ;; *) as_fn_error $? "non-object file specified by --with-os-deps" "$LINENO" 5 ;; esac done fi # Check whether --with-selinux was given. if test "${with_selinux+set}" = set; then : withval=$with_selinux; if test "$withval" = "yes"; then for ac_header in selinux/selinux.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default" if test "x$ac_cv_header_selinux_selinux_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SELINUX_SELINUX_H 1 _ACEOF else as_fn_error $? "Missing SELinux header files" "$LINENO" 5 fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for matchpathcon in -lselinux" >&5 $as_echo_n "checking for matchpathcon in -lselinux... " >&6; } if ${ac_cv_lib_selinux_matchpathcon+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lselinux $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char matchpathcon (); int main () { return matchpathcon (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_selinux_matchpathcon=yes else ac_cv_lib_selinux_matchpathcon=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_matchpathcon" >&5 $as_echo "$ac_cv_lib_selinux_matchpathcon" >&6; } if test "x$ac_cv_lib_selinux_matchpathcon" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSELINUX 1 _ACEOF LIBS="-lselinux $LIBS" else as_fn_error $? "Missing or incorrect SELinux library files" "$LINENO" 5 fi fi fi # Check whether --with-libcap-ng was given. if test "${with_libcap_ng+set}" = set; then : withval=$with_libcap_ng; else with_libcap_ng=auto fi use_libcap_ng=no case "$with_libcap_ng:$host_os" in auto:linux*|yes:*) for ac_header in cap-ng.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "cap-ng.h" "ac_cv_header_cap_ng_h" "$ac_includes_default" if test "x$ac_cv_header_cap_ng_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CAP_NG_H 1 _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking for capng_clear in -lcap-ng" >&5 $as_echo_n "checking for capng_clear in -lcap-ng... " >&6; } if ${ac_cv_lib_cap_ng_capng_clear+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcap-ng $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char capng_clear (); int main () { return capng_clear (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_cap_ng_capng_clear=yes else ac_cv_lib_cap_ng_capng_clear=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_ng_capng_clear" >&5 $as_echo "$ac_cv_lib_cap_ng_capng_clear" >&6; } if test "x$ac_cv_lib_cap_ng_capng_clear" = xyes; then : $as_echo "#define HAVE_LIBCAP_NG 1" >>confdefs.h CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes else as_fn_error $? "libcap-ng headers found but library is missing" "$LINENO" 5 fi else test "$with_libcap_ng" != "yes" || as_fn_error $? "Missing libcap-ng header files" "$LINENO" 5 fi done ;; esac # Check whether --with-libsystemd was given. if test "${with_libsystemd+set}" = set; then : withval=$with_libsystemd; else with_libsystemd=auto fi use_libsystemd=no case "$with_libsystemd:$host_os" in auto:linux*|yes:*) for ac_header in systemd/sd-daemon.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "systemd/sd-daemon.h" "ac_cv_header_systemd_sd_daemon_h" "$ac_includes_default" if test "x$ac_cv_header_systemd_sd_daemon_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYSTEMD_SD_DAEMON_H 1 _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sd_notify in -lsystemd" >&5 $as_echo_n "checking for sd_notify in -lsystemd... " >&6; } if ${ac_cv_lib_systemd_sd_notify+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsystemd $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sd_notify (); int main () { return sd_notify (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_systemd_sd_notify=yes else ac_cv_lib_systemd_sd_notify=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_systemd_sd_notify" >&5 $as_echo "$ac_cv_lib_systemd_sd_notify" >&6; } if test "x$ac_cv_lib_systemd_sd_notify" = xyes; then : $as_echo "#define HAVE_LIBSYSTEMD 1" >>confdefs.h SYSTEMD_LDADD="-lsystemd"; use_libsystemd=yes else as_fn_error $? "libsystemd headers found but library is missing" "$LINENO" 5 fi else test "$with_libsystemd" != "yes" || as_fn_error $? "Missing libsystemd header files" "$LINENO" 5 fi done ;; esac # Check whether --with-systemdsystemunitdir was given. if test "${with_systemdsystemunitdir+set}" = set; then : withval=$with_systemdsystemunitdir; else with_systemdsystemunitdir=auto fi systemdsystemunitdir= case "$with_systemdsystemunitdir:$use_libsystemd" in auto:yes|yes:yes) if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemdsystemunitdir" >&5 $as_echo_n "checking for systemdsystemunitdir... " >&6; } systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null` { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdsystemunitdir:-no}" >&5 $as_echo "${systemdsystemunitdir:-no}" >&6; } fi case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in yes:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;; yes:*:*|auto:*:|auto:/etc:*) ;; *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;; esac ;; auto:*|no:*) ;; *:yes) systemdsystemunitdir="$with_systemdsystemunitdir" ;; *) as_fn_error $? "'--with-systemdsystemunitdir=$with_systemdsystemunitdir' now requires '--with-libsystemd'" "$LINENO" 5 ;; esac if test -n "$systemdsystemunitdir"; then INSTALL_SYSTEMDUNIT_TRUE= INSTALL_SYSTEMDUNIT_FALSE='#' else INSTALL_SYSTEMDUNIT_TRUE='#' INSTALL_SYSTEMDUNIT_FALSE= fi # Check whether --with-systemdenvfile was given. if test "${with_systemdenvfile+set}" = set; then : withval=$with_systemdenvfile; else with_systemdenvfile=auto fi systemdenvfile= case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in auto:no:?*|yes:*:?*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for path of systemd EnvironmentFile" >&5 $as_echo_n "checking for path of systemd EnvironmentFile... " >&6; } for dir in sysconfig default; do if test -d /etc/$dir; then systemdenvfile='${sysconfdir}'/$dir/smartmontools break fi done { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdenvfile:-no}" >&5 $as_echo "${systemdenvfile:-no}" >&6; } case "$with_systemdenvfile:$systemdenvfile" in yes:) as_fn_error $? "Path of systemd EnvironmentFile not found" "$LINENO" 5 ;; esac ;; auto:*|no:*) ;; *:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;; *) systemdenvfile="$with_systemdenvfile" esac # TODO: Remove when NVMe support is no longer EXPERIMENTAL # Check whether --with-nvme-devicescan was given. if test "${with_nvme_devicescan+set}" = set; then : withval=$with_nvme_devicescan; fi # Check whether --with-solaris-sparc-ata was given. if test "${with_solaris_sparc_ata+set}" = set; then : withval=$with_solaris_sparc_ata; fi case "$host:$with_solaris_sparc_ata" in sparc-*-solaris*:yes) if test ! -f "$srcdir/os_solaris_ata.s"; then as_fn_error $? "Missing source file: $srcdir/os_solaris_ata.s This file is no longer included in the source tarball but still available in the SVN repository." "$LINENO" 5 fi $as_echo "#define WITH_SOLARIS_SPARC_ATA 1" >>confdefs.h ;; esac # Check whether --with-signal-func was given. if test "${with_signal_func+set}" = set; then : withval=$with_signal_func; else with_signal_func=sigaction fi case "$host:$with_signal_func" in *-*-mingw*:*) ;; *:sigaction) for ac_func in sigaction do : ac_fn_cxx_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction" if test "x$ac_cv_func_sigaction" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SIGACTION 1 _ACEOF else as_fn_error $? "Missing function 'sigaction()'. Try '--with-signal-func=sigset' or '--with-signal-func=signal'. Please send info about your system to $PACKAGE_BUGREPORT." "$LINENO" 5 fi done ;; *:sigset) for ac_func in sigset do : ac_fn_cxx_check_func "$LINENO" "sigset" "ac_cv_func_sigset" if test "x$ac_cv_func_sigset" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SIGSET 1 _ACEOF else as_fn_error $? "Missing function 'sigset()'" "$LINENO" 5 fi done ;; *:signal) ;; *) as_fn_error $? "Invalid option '--with-signal-func=$with_signal_func'" "$LINENO" 5 ;; esac # TODO: Remove after smartmontools 6.7 # Check whether --with-working-snprintf was given. if test "${with_working_snprintf+set}" = set; then : withval=$with_working_snprintf; else with_working_snprintf=yes fi if test "$with_working_snprintf" = "yes"; then $as_echo "#define HAVE_WORKING_SNPRINTF 1" >>confdefs.h fi case "$with_working_snprintf:$host_os: $CPPFLAGS $CXXFLAGS" in yes:mingw*:*\ -[DU]__USE_MINGW_ANSI_STDIO*) ;; yes:mingw*:*) # Older MinGW (4.6.3) do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set. # Newer MinGW (4.9.1) set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late. # Set __USE_MINGW_ANSI_STDIO always and fail if not fully supported. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports __USE_MINGW_ANSI_STDIO" >&5 $as_echo_n "checking whether $CXX supports __USE_MINGW_ANSI_STDIO... " >&6; } save_CXXFLAGS=$CXXFLAGS CXXFLAGS="-Wformat -Werror -D__USE_MINGW_ANSI_STDIO" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define __STDC_FORMAT_MACROS 1 #include #include void f(char * buf1, char * buf2, size_t size) { snprintf(buf1, size, "%lld", 42LL); snprintf(buf2, size, "%" PRId64, (int64_t)42); } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : result=yes else result=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $result" >&5 $as_echo "$result" >&6; } if test "$result" != "yes"; then as_fn_error $? " This version of $CXX does not support __USE_MINGW_ANSI_STDIO. Use option '--without-working-snprintf' to skip this check. Please send info about your system to $PACKAGE_BUGREPORT. " "$LINENO" 5 fi CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $save_CXXFLAGS" ;; esac # Check whether --with-mingw-aslr was given. if test "${with_mingw_aslr+set}" = set; then : withval=$with_mingw_aslr; else with_mingw_aslr=auto fi case "$host:${LDFLAGS+set}" in *-*-mingw*:) # MinGW defaults: link statically and indicate DEP and TS compatibility LDFLAGS="-static -Wl,--nxcompat,--tsaware" ;; esac case "$host:$with_mingw_aslr" in x86_64-*-mingw*:auto) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports --high-entropy-va" >&5 $as_echo_n "checking whether $CXX supports --high-entropy-va... " >&6; } save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup,--high-entropy-va,--image-base,0x140000000" # Link libstdc++ to detect MinGW 6.3.0 problems with high --image-base cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include std::string s(42, '.'); int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : with_mingw_aslr=yes else with_mingw_aslr=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_mingw_aslr" >&5 $as_echo "$with_mingw_aslr" >&6; } test "$with_mingw_aslr" = "yes" || with_mingw_aslr=low ;; esac case "$host:$with_mingw_aslr" in x86_64-*-mingw*:yes) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup,--high-entropy-va,--image-base,0x140000000" ;; x86_64-*-mingw*:low) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup" ;; *-*-mingw*:auto|*-*-mingw*:yes|*-*-mingw*:low) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-e_mainCRTStartup" ;; esac os_win32_manifest= case "$host" in *-*-mingw*) # Newer MinGW may add a default manifest { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX adds an application manifest" >&5 $as_echo_n "checking whether $CXX adds an application manifest... " >&6; } cc_adds_manifest=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '^1.*RT_MANIFEST' >/dev/null 2>&1; then cc_adds_manifest=incomplete # Manifest must provide a Win 10 compatibility ID if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' >/dev/null 2>&1; then cc_adds_manifest=yes fi fi else as_fn_error $? "test compile failed" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cc_adds_manifest" >&5 $as_echo "$cc_adds_manifest" >&6; } test "$cc_adds_manifest" = "yes" || os_win32_manifest='os_win32/default.manifest' ;; esac # Check whether --with-cxx11-option was given. if test "${with_cxx11_option+set}" = set; then : withval=$with_cxx11_option; else with_cxx11_option=auto fi check_cxx11_support() { save_CXXFLAGS=$CXXFLAGS CXXFLAGS=$1 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if __cplusplus < 201103L #error false #endif // use some C++11 features (and return v * 42 :-) auto cxx11(long v) noexcept -> decltype(v) { typedef decltype(v) t; t r = v; static const t a[] = { -7, -1, 1, 2, 3 }; static_assert(sizeof(r) == sizeof(a[0]), "fail"); auto f = [](t x, t y){ return x * y; }; for (const auto & e : a) r = f(r, e); return r; } int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : CXXFLAGS=$save_CXXFLAGS; return 0 else CXXFLAGS=$save_CXXFLAGS; return 1 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext } case "$with_cxx11_option" in no) ;; auto) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CXX option to accept C++11" >&5 $as_echo_n "checking for $CXX option to accept C++11... " >&6; } with_cxx11_option=unknown for option in "" "-std=gnu++11" "-std=gnu++0x" "-std=c++11" "-std=c++0x"; do if check_cxx11_support "$option"; then with_cxx11_option=$option; break; fi done { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_cxx11_option:-none needed}" >&5 $as_echo "${with_cxx11_option:-none needed}" >&6; } test "$with_cxx11_option" != "unknown" || as_fn_error $? " This version of smartmontools does not use C++11 features, but future versions possibly will. This script was unable to determine a compiler option to enable C++11. Use option '--with-cxx11-option=OPTION' to specify the compiler option (it will be used in the actual build only if '--with-cxx11-regex' is set). Use option '--without-cxx11-option' to suppress this error message if the compiler lacks C++11 support. In both cases, please send info about compiler and platform to $PACKAGE_BUGREPORT - Thanks!" "$LINENO" 5 ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX $with_cxx11_option accepts C++11" >&5 $as_echo_n "checking whether $CXX $with_cxx11_option accepts C++11... " >&6; } res=no; check_cxx11_support "$with_cxx11_option" && res=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $res" >&5 $as_echo "$res" >&6; } test "$res" = "yes" || as_fn_error $? "$CXX $with_cxx11_option does not accept C++11" "$LINENO" 5 ;; esac # Check whether --with-cxx11-regex was given. if test "${with_cxx11_regex+set}" = set; then : withval=$with_cxx11_regex; fi need_regex=no if test "$with_cxx11_regex" = "yes"; then $as_echo "#define WITH_CXX11_REGEX 1" >>confdefs.h case "$with_cxx11_option: $CXXFLAGS " in no:*) as_fn_error $? "'--with-cxx11-regex' requires C++11 support" "$LINENO" 5 ;; ?*:*\ $with_cxx11_option\ *) ;; ?*:*) CXXFLAGS="$CXXFLAGS $with_cxx11_option" ;; esac else for ac_func in regcomp do : ac_fn_cxx_check_func "$LINENO" "regcomp" "ac_cv_func_regcomp" if test "x$ac_cv_func_regcomp" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_REGCOMP 1 _ACEOF else need_regex=yes fi done fi if test "$need_regex" = "yes"; then NEED_REGEX_TRUE= NEED_REGEX_FALSE='#' else NEED_REGEX_TRUE='#' NEED_REGEX_FALSE= fi releaseversion='${PACKAGE}-${VERSION}' # Set platform-specific modules and symbols os_libs= os_dltools='curl wget lynx svn' os_mailer=mail os_hostname="'hostname'" os_dnsdomainname= os_nisdomainname="'domainname'" os_darwin=no os_solaris=no os_win32=no os_win32_mingw=no os_win64=no os_man_filter= os_nvme_devicescan= case "${host}" in *-*-linux*) os_deps='os_linux.o cciss.o dev_areca.o' os_dnsdomainname="'dnsdomainname' 'hostname -d'" os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'" os_man_filter=Linux os_nvme_devicescan=yes ;; *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*) os_deps='os_freebsd.o cciss.o dev_areca.o' os_libs='-lcam -lsbuf' os_dltools='curl wget lynx fetch svn' { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libusb20_dev_get_device_desc in -lusb" >&5 $as_echo_n "checking for libusb20_dev_get_device_desc in -lusb... " >&6; } if ${ac_cv_lib_usb_libusb20_dev_get_device_desc+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lusb $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char libusb20_dev_get_device_desc (); int main () { return libusb20_dev_get_device_desc (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_usb_libusb20_dev_get_device_desc=yes else ac_cv_lib_usb_libusb20_dev_get_device_desc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_usb_libusb20_dev_get_device_desc" >&5 $as_echo "$ac_cv_lib_usb_libusb20_dev_get_device_desc" >&6; } if test "x$ac_cv_lib_usb_libusb20_dev_get_device_desc" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBUSB 1 _ACEOF LIBS="-lusb $LIBS" fi os_man_filter=FreeBSD os_nvme_devicescan=no ;; sparc-*-solaris*) os_deps='os_solaris.o' test "$with_solaris_sparc_ata" = "yes" \ && os_deps="$os_deps os_solaris_ata.o" os_mailer='mailx' os_solaris=yes os_man_filter=Solaris ;; *-pc-solaris*) os_deps='os_solaris.o' os_mailer='mailx' os_solaris=yes os_man_filter=Solaris ;; *-*-netbsd*) os_deps='os_netbsd.o' os_libs='-lutil' os_man_filter=NetBSD os_nvme_devicescan=no ;; *-*-openbsd*) os_deps='os_openbsd.o' os_libs='-lutil' os_dltools='curl wget lynx ftp svn' os_man_filter=OpenBSD ;; *-*-cygwin*) os_deps='os_win32.o dev_areca.o' os_mailer='email' os_hostname="'hostname' 'echo "'"${HOSTNAME?unset}"'"'" os_dnsdomainname="'dnsdomainname' 'hostname -d' 'echo "'"${USERDNSDOMAIN?unset}"'"'" os_nisdomainname= os_win32=yes os_man_filter=Cygwin os_nvme_devicescan=yes ;; x86_64-*-mingw*) os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_win64=yes os_man_filter=Windows os_nvme_devicescan=yes ;; *-*-mingw*) os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_man_filter=Windows os_nvme_devicescan=yes ;; *-*-darwin*) os_deps='os_darwin.o' os_libs='-framework CoreFoundation -framework IOKit' os_darwin=yes os_man_filter=Darwin os_nvme_devicescan=no ;; *-*-nto-qnx*) os_deps='os_qnxnto.o' ;; *-*-os2-*) os_deps='os_os2.o' ;; *) os_deps='os_generic.o' ;; esac # Replace if '--with-os-deps' was specified test -z "$with_os_deps" || os_deps="$with_os_deps" # Check if we need adapter to old interface (dev_legacy.cpp) os_src=`echo "${os_deps}"|sed -n 's,^\([^ .]*\)\.o.*$,\1.cpp,p'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${os_src} uses new interface" >&5 $as_echo_n "checking whether ${os_src} uses new interface... " >&6; } if grep "smart_interface" "${srcdir}/${os_src}" >/dev/null 2>&1; then os_new_interface=yes else os_new_interface=no os_deps="${os_deps} dev_legacy.o" $as_echo "#define OLD_INTERFACE 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $os_new_interface" >&5 $as_echo "$os_new_interface" >&6; } # TODO: Remove when NVMe support is no longer EXPERIMENTAL case "$os_nvme_devicescan:${with_nvme_devicescan+set}" in no:|yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NVMe device scanning could be safely enabled" >&5 $as_echo_n "checking whether NVMe device scanning could be safely enabled... " >&6; } with_nvme_devicescan=$os_nvme_devicescan { $as_echo "$as_me:${as_lineno-$LINENO}: result: $os_nvme_devicescan" >&5 $as_echo "$os_nvme_devicescan" >&6; } os_nvme_devicescan=used ;; esac if test "$with_nvme_devicescan" = "yes"; then $as_echo "#define WITH_NVME_DEVICESCAN 1" >>confdefs.h fi # Create drivedb.h update branch name from version: 5.41[.X] -> RELEASE_5_41_DRIVEDB DRIVEDB_BRANCH=`echo "$drivedb_version" | sed 's,^\([0-9]*\.[0-9]*\)\..*$,\1,' \ | sed -n 's,^\([0-9][0-9]*\)\.\([0-9][0-9]*\)$,RELEASE_\1_\2_DRIVEDB,p'` if test -z "$DRIVEDB_BRANCH"; then as_fn_error $? "Unable to create DRIVEDB_BRANCH for version: $drivedb_version" "$LINENO" 5 fi # Enable platform-specific makefile sections if test "$os_darwin" = "yes"; then OS_DARWIN_TRUE= OS_DARWIN_FALSE='#' else OS_DARWIN_TRUE='#' OS_DARWIN_FALSE= fi if test "$os_solaris" = "yes"; then OS_SOLARIS_TRUE= OS_SOLARIS_FALSE='#' else OS_SOLARIS_TRUE='#' OS_SOLARIS_FALSE= fi if test "$os_win32" = "yes"; then OS_WIN32_TRUE= OS_WIN32_FALSE='#' else OS_WIN32_TRUE='#' OS_WIN32_FALSE= fi if test "$os_win32_mingw" = "yes"; then OS_WIN32_MINGW_TRUE= OS_WIN32_MINGW_FALSE='#' else OS_WIN32_MINGW_TRUE='#' OS_WIN32_MINGW_FALSE= fi if test -n "$MAKENSIS"; then OS_WIN32_NSIS_TRUE= OS_WIN32_NSIS_FALSE='#' else OS_WIN32_NSIS_TRUE='#' OS_WIN32_NSIS_FALSE= fi if test "$os_win64" = "yes"; then OS_WIN64_TRUE= OS_WIN64_FALSE='#' else OS_WIN64_TRUE='#' OS_WIN64_FALSE= fi if test "$GXX" = "yes"; then orig_CXXFLAGS=$CXXFLAGS # Add -Wall and -W[extra] if its not already specified case " $CXXFLAGS " in *\ -Wall\ *) ;; *) CXXFLAGS="$CXXFLAGS -Wall" ;; esac case " $CXXFLAGS " in *\ -W\ *|*\ -Wextra\ *) ;; *) CXXFLAGS="$CXXFLAGS -W" ;; esac # Add -Wformat=2 (GCC 3.0) -fstack-protector[-strong] (GCC 4.1[4.9]) if supported # and no -W or -f option was set in configure cmdline (TODO: -Wformat-signedness) for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do case " $orig_CXXFLAGS:$option" in *\ -W*:-W*|*\ -f*:-f*) continue ;; esac case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports $option" >&5 $as_echo_n "checking whether $CXX supports $option... " >&6; } save_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $option" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : res=yes else res=no; CXXFLAGS=$save_CXXFLAGS fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $res" >&5 $as_echo "$res" >&6; } done else # We are NOT using gcc, so enable host-specific compiler flags case "${host}" in sparc*-*-solaris*) # Tell the Solaris/SPARC C++ compiler about packed ATA structures case " $CXXFLAGS" in *\ -xmemalign*) ;; *) CXXFLAGS="-xmemalign=1i $CXXFLAGS" ;; esac ;; esac case "${host}" in *-*-solaris*) # Turn on optimization if user has not explicitly set its value case " $CXXFLAGS" in *\ -xO*) ;; *) CXXFLAGS="-xO2 $CXXFLAGS" ;; esac # Suppress trivial warnings case " $CXXFLAGS" in *\ -erroff*) ;; *) CXXFLAGS="-erroff=%none,wbadinitl,wbadasgl,badargtypel2w,badargtype2w $CXXFLAGS" ;; esac ;; esac fi cat >>confdefs.h <<_ACEOF #define SMARTMONTOOLS_BUILD_HOST "${host}" _ACEOF ac_config_files="$ac_config_files 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, we kill variables containing newlines. # 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. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} 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}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCCAS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_SVN_BUILD_TRUE}" && test -z "${IS_SVN_BUILD_FALSE}"; then as_fn_error $? "conditional \"IS_SVN_BUILD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NEED_GETOPT_LONG_TRUE}" && test -z "${NEED_GETOPT_LONG_FALSE}"; then as_fn_error $? "conditional \"NEED_GETOPT_LONG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INSTALL_INITSCRIPT_TRUE}" && test -z "${INSTALL_INITSCRIPT_FALSE}"; then as_fn_error $? "conditional \"INSTALL_INITSCRIPT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_DRIVEDB_TRUE}" && test -z "${ENABLE_DRIVEDB_FALSE}"; then as_fn_error $? "conditional \"ENABLE_DRIVEDB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_UPDATE_SMART_DRIVEDB_TRUE}" && test -z "${ENABLE_UPDATE_SMART_DRIVEDB_FALSE}"; then as_fn_error $? "conditional \"ENABLE_UPDATE_SMART_DRIVEDB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_SCRIPTPATH_TRUE}" && test -z "${ENABLE_SCRIPTPATH_FALSE}"; then as_fn_error $? "conditional \"ENABLE_SCRIPTPATH\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_SAVESTATES_TRUE}" && test -z "${ENABLE_SAVESTATES_FALSE}"; then as_fn_error $? "conditional \"ENABLE_SAVESTATES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ENABLE_ATTRIBUTELOG_TRUE}" && test -z "${ENABLE_ATTRIBUTELOG_FALSE}"; then as_fn_error $? "conditional \"ENABLE_ATTRIBUTELOG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INSTALL_SYSTEMDUNIT_TRUE}" && test -z "${INSTALL_SYSTEMDUNIT_FALSE}"; then as_fn_error $? "conditional \"INSTALL_SYSTEMDUNIT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NEED_REGEX_TRUE}" && test -z "${NEED_REGEX_FALSE}"; then as_fn_error $? "conditional \"NEED_REGEX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_DARWIN_TRUE}" && test -z "${OS_DARWIN_FALSE}"; then as_fn_error $? "conditional \"OS_DARWIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_SOLARIS_TRUE}" && test -z "${OS_SOLARIS_FALSE}"; then as_fn_error $? "conditional \"OS_SOLARIS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_WIN32_TRUE}" && test -z "${OS_WIN32_FALSE}"; then as_fn_error $? "conditional \"OS_WIN32\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_WIN32_MINGW_TRUE}" && test -z "${OS_WIN32_MINGW_FALSE}"; then as_fn_error $? "conditional \"OS_WIN32_MINGW\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_WIN32_NSIS_TRUE}" && test -z "${OS_WIN32_NSIS_FALSE}"; then as_fn_error $? "conditional \"OS_WIN32_NSIS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${OS_WIN64_TRUE}" && test -z "${OS_WIN64_FALSE}"; then as_fn_error $? "conditional \"OS_WIN64\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $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} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= 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 IFS=$as_save_IFS ;; 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 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; 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 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # 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 ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # 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'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by smartmontools $as_me 7.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent 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_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ smartmontools config.status 7.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. 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=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; 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 || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; 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 against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries 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[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #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. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # 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. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;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&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf 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. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_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"` # 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'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # 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 || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Note: Use `...` here as some shells do not properly parse '$(... case $x in X) ...)' info=` echo "-----------------------------------------------------------------------------" echo "${PACKAGE}-${VERSION} configuration:" echo "host operating system: $host" echo "C++ compiler: $CXX" echo "C compiler: $CC" echo "preprocessor flags: $CPPFLAGS" echo "C++ compiler flags: $CXXFLAGS" echo "C compiler flags: $CFLAGS" echo "linker flags: $LDFLAGS" echo "OS specific modules: $os_deps $os_libs $LIBS" case "$host_os" in mingw*) echo "application manifest: ${os_win32_manifest:-built-in}" echo "resource compiler: $WINDRES" echo "message compiler: $WINDMC" echo "NSIS compiler: $MAKENSIS" if test -n "$drivedbdir"; then echo "drive database file: EXEDIR/drivedb.h" if test -n "$MAKENSIS"; then echo "database update tool: EXEDIR/update-smart-drivedb.exe" fi else echo "drive database file: [disabled]" fi if test -n "$savestates"; then echo "smartd save files: \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state" fi if test -n "$attributelog"; then echo "smartd attribute logs: \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv" fi echo "NVMe DEVICESCAN: ${with_nvme_devicescan-no}" ;; *) echo "binary install path: \`eval eval eval echo $sbindir\`" echo "man page install path: \`eval eval eval echo $mandir\`" echo "doc file install path: \`eval eval eval echo $docdir\`" echo "examples install path: \`eval eval eval echo $exampledir\`" if test -n "$drivedbdir"; then echo "drive database file: \`eval eval eval echo $drivedbdir\`/drivedb.h" if test "$with_update_smart_drivedb" = "yes"; then echo "database update script: \`eval eval eval echo $sbindir\`/update-smart-drivedb" if test "$drivedb_version" = "$smartmontools_drivedb_version"; then echo "database update branch: branches/$DRIVEDB_BRANCH" else echo "... backported to: branches/$DRIVEDB_BRANCH" fi echo "download tools: \`eval eval eval echo $os_dltools\`" if test -n "$gnupg"; then echo "GnuPG for verification: \`eval eval eval echo $gnupg\`" else echo "GnuPG for verification: [disabled]" fi else echo "database update script: [disabled]" fi else echo "drive database file: [disabled]" fi echo "local drive database: \`eval eval eval echo $sysconfdir\`/smart_drivedb.h" echo "smartd config file: \`eval eval eval echo $sysconfdir\`/smartd.conf${smartd_suffix}" echo "smartd warning script: \`eval eval eval echo $smartdscriptdir\`/smartd_warning.sh" if test -n "$smartdplugindir"; then echo "smartd plugin path: \`eval eval eval echo $smartdplugindir\`" else echo "smartd plugin path: [disabled]" fi if test -n "$scriptpath"; then echo "PATH within scripts: \`eval eval eval echo $scriptpath\`" else echo "PATH within scripts: [inherited]" fi if test -n "$initddir"; then echo "smartd initd script: \`eval eval eval echo $initddir\`/smartd" elif test -z "$systemdsystemunitdir"; then echo "smartd initd script: [disabled]" fi if test -n "$systemdsystemunitdir"; then echo "smartd service file: \`eval eval eval echo $systemdsystemunitdir\`/smartd.service" if test -n "$systemdenvfile"; then echo "smartd environ file: \`eval eval eval echo $systemdenvfile\`" else echo "smartd environ file: [disabled]" fi fi if test -n "$savestates"; then echo "smartd save files: \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state" else echo "smartd save files: [disabled]" fi if test -n "$attributelog"; then echo "smartd attribute logs: \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv" else echo "smartd attribute logs: [disabled]" fi case "$host_os" in linux*) echo "SELinux support: ${with_selinux-no}" echo "libcap-ng support: $use_libcap_ng" echo "systemd notify support: $use_libsystemd" ;; esac echo "NVMe DEVICESCAN: ${with_nvme_devicescan-[not implemented]}" ;; esac echo "-----------------------------------------------------------------------------" ` { $as_echo "$as_me:${as_lineno-$LINENO}: $info " >&5 $as_echo "$as_me: $info " >&6;} # TODO: Remove when NVMe support is no longer EXPERIMENTAL case "$os_nvme_devicescan:$with_nvme_devicescan" in used:yes) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The default for the inclusion of NVME devices in smartd.conf 'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on this platform. If '--without-nvme-devicescan' is still needed, please inform $PACKAGE_BUGREPORT. Use option '--with-nvme-devicescan' to suppress this warning. " >&5 $as_echo "$as_me: WARNING: The default for the inclusion of NVME devices in smartd.conf 'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on this platform. If '--without-nvme-devicescan' is still needed, please inform $PACKAGE_BUGREPORT. Use option '--with-nvme-devicescan' to suppress this warning. " >&2;} ;; used:no) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: This version of smartmontools provides NVMe support which is still EXPERIMENTAL. NVMe devices are not yet included in smartd.conf 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified. Use option '--with-nvme-devicescan' to include NVMe devices. Use option '--without-nvme-devicescan' to suppress this warning. " >&5 $as_echo "$as_me: WARNING: This version of smartmontools provides NVMe support which is still EXPERIMENTAL. NVMe devices are not yet included in smartd.conf 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified. Use option '--with-nvme-devicescan' to include NVMe devices. Use option '--without-nvme-devicescan' to suppress this warning. " >&2;} ;; esac # TODO: Remove after smartmontools 6.7 if test "$with_working_snprintf" != "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The option '--without-working-snprintf' is deprecated and will be removed in a future version of smartmontools. If you still need support for pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT. " >&5 $as_echo "$as_me: WARNING: The option '--without-working-snprintf' is deprecated and will be removed in a future version of smartmontools. If you still need support for pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT. " >&2;} fi case "$host_os:$with_libsystemd:$use_libsystemd:$PKG_CONFIG" in linux*:auto:no:?*) if $PKG_CONFIG systemd >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: systemd(1) is used on this system but smartd systemd notify support will not be available because libsystemd-dev[el] package is not installed. Use option '--without-libsystemd' to suppress this warning. " >&5 $as_echo "$as_me: WARNING: systemd(1) is used on this system but smartd systemd notify support will not be available because libsystemd-dev[el] package is not installed. Use option '--without-libsystemd' to suppress this warning. " >&2;} fi ;; esac smartmontools-7.0/configure.ac0000644000175000010010000010406713412155326013507 00000000000000# # $Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $ # dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.60]) AC_INIT([smartmontools], [7.0], [smartmontools-support@listi.jpberlin.de]) AM_INIT_AUTOMAKE([1.10 foreign]) # Version of drive database branch smartmontools_drivedb_version=7.0 smartmontools_cvs_tag=`echo '$Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $'` smartmontools_release_date=2018-12-30 smartmontools_release_time="14:47:55 UTC" AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args", [smartmontools Configure Arguments]) 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, "https://www.smartmontools.org/", [smartmontools Home Page]) AC_CONFIG_SRCDIR([smartctl.cpp]) AC_CONFIG_HEADER([config.h]) AM_MAINTAINER_MODE AC_LANG([C++]) dnl Checks for programs. AC_PROG_CXX AM_PROG_AS AC_PROG_INSTALL m4_pattern_forbid([^PKG_PROG_]) if test "$cross_compiling" = "no"; then m4_ifdef([PKG_PROG_PKG_CONFIG], [PKG_PROG_PKG_CONFIG], [AC_MSG_WARN([m4/pkg.m4 missing, systemd detection disabled])]) fi AC_ARG_VAR(WINDMC, [Windows message compiler command]) AC_ARG_VAR(WINDRES, [Windows resource compiler command]) AC_ARG_VAR(MAKENSIS, [NSIS compiler command]) AC_CANONICAL_HOST case "${host}" in *-*-mingw*) AC_CHECK_TOOL(WINDMC, [windmc]) AC_CHECK_TOOL(WINDRES, [windres]) AC_MSG_CHECKING([for makensis]) if test -z "$MAKENSIS"; then if test -n "$PROGRAMFILES" && "$PROGRAMFILES/NSIS/makensis" -VERSION >/dev/null 2>&1; then MAKENSIS="$PROGRAMFILES/NSIS/makensis" elif makensis -VERSION >/dev/null 2>&1; then MAKENSIS=makensis fi fi AC_MSG_RESULT([${MAKENSIS:-no}]) ;; esac # Check for SVN. AC_MSG_CHECKING([whether this is a build from SVN]) is_svn_build=no svn_deps= if test -f "$srcdir/.svn/wc.db"; then # SVN 1.7, 1.8 working copy svn_deps='${srcdir}/.svn/wc.db' elif test -f "${srcdir}/.svn/entries"; then # SVN <= 1.6 working copy (SVN 1.7 has empty entries file) svn_deps='${srcdir}/.svn/entries' fi if test -n "$svn_deps"; then is_svn_build=unknown if (cd "$srcdir" && svn --version && svnversion && svn info) >/dev/null 2>&1; then is_svn_build=yes fi fi AC_SUBST([svn_deps]) AM_CONDITIONAL(IS_SVN_BUILD, [test "$is_svn_build" = "yes"]) AC_MSG_RESULT([$is_svn_build]) # Note: On Linux, clock_gettime() requires -lrt which implies -lpthreads # Check omitted for now, gettimeofday() provides reasonable precision # AC_SEARCH_LIBS(clock_gettime, rt) # Checks for header files. AC_CHECK_HEADERS([locale.h]) AC_CHECK_HEADERS([byteswap.h], [], [], []) case "$host" in *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*) # Check for FreeBSD twe and twa include files AC_CHECK_HEADERS([sys/tweio.h sys/twereg.h sys/tw_osl_ioctl.h]) # Check for the FreeBSD CCISS system header and use internal one if not found AC_CHECK_HEADERS([dev/ciss/cissio.h], [AC_DEFINE([CISS_LOCATION],[],[freebsd ciss header location])], [AC_DEFINE([CISS_LOCATION],["cissio_freebsd.h"],[freebsd ciss header location])]) ;; *-*-linux*) # is needed for cciss_ioctl.h at least on SuSE LINUX AC_CHECK_HEADERS([sys/sysmacros.h linux/compiler.h]) # Check for Linux CCISS include file AC_CHECK_HEADERS([linux/cciss_ioctl.h], [], [], [AC_INCLUDES_DEFAULT #ifdef HAVE_LINUX_COMPILER_H # include #endif ]) ;; *-*-netbsd*|*-*-openbsd*) AC_CHECK_HEADERS([dev/ata/atavar.h]) ;; *-*-cygwin*|*-*-mingw*) # Check for Windows DDK header files AC_CHECK_HEADERS([ntdddisk.h ddk/ntdddisk.h], [], [], [AC_INCLUDES_DEFAULT #include ]) ;; esac # Checks for typedefs, and compiler characteristics. AC_CHECK_TYPES([__int128]) AC_TYPE_LONG_DOUBLE_WIDER # Checks for library functions. AC_CHECK_FUNCS([getopt_long], [need_getopt_long=no], [need_getopt_long=yes]) AM_CONDITIONAL(NEED_GETOPT_LONG, [test "$need_getopt_long" = "yes"]) AC_CHECK_FUNCS([clock_gettime ftime gettimeofday]) # Check byte ordering (defines WORDS_BIGENDIAN) AC_C_BIGENDIAN # check for __attribute__((packed)) # (sizeof() check is required to avoid false positives if other # __attribute__((x)) are supported) AC_MSG_CHECKING([whether $CXX supports __attribute__((packed))]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[ struct s { char a; short b; } __attribute__((packed)); typedef char t[sizeof(struct s) == 3 ? 1 : -1];]])], [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, 1, [Define to 1 if C++ compiler supports __attribute__((packed))]) fi AC_MSG_RESULT([$gcc_have_attr_packed]) AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS) AC_SUBST(ASFLAGS) initddir= AC_ARG_WITH(initscriptdir, [AS_HELP_STRING([--with-initscriptdir=@<:@DIR|no@:>@], [Location of init scripts [no]])], [ case "$withval" in auto|yes) AC_MSG_ERROR(['--with-initscriptdir=$withval' is no longer supported]) ;; no) ;; *) initddir="$withval" ;; esac ]) AC_SUBST(initddir) AM_CONDITIONAL(INSTALL_INITSCRIPT, [test -n "$initddir"]) # use different init script templates for different OS case "${host}" in *-*-freebsd*) initdfile="smartd.freebsd.initd" ;; *-apple-darwin*) initdfile="com.smartmontools.smartd.plist" ;; *-*-cygwin*) initdfile="smartd.cygwin.initd" ;; *) initdfile="smartd.initd" ;; esac AC_SUBST(initdfile) AC_ARG_WITH(exampledir, [AS_HELP_STRING([--with-exampledir=DIR], [Location of example scripts [DOCDIR/examplescripts]])], [exampledir="$withval"], [exampledir='${docdir}/examplescripts']) AC_SUBST(exampledir) drivedbdir='${datadir}/${PACKAGE}' AC_ARG_WITH(drivedbdir, [AS_HELP_STRING([--with-drivedbdir@<:@=DIR|yes|no@:>@], [Location of drive database file [DATADIR/smartmontools]])], [case "$withval" in yes) ;; no) drivedbdir= ;; *) drivedbdir="$withval" ;; esac]) AC_SUBST(drivedbdir) AM_CONDITIONAL(ENABLE_DRIVEDB, [test -n "$drivedbdir"]) drivedb_version=$smartmontools_drivedb_version AC_ARG_WITH(update-smart_drivedb, [AS_HELP_STRING([--with-update-smart-drivedb@<:@=yes|no|X.Y@:>@], [Install update-smart-drivedb script (and backport it to branches/RELEASE_X_Y_DRIVEDB) [yes]])], [ case "$withval" in yes|no) ;; 5.4[[0-3]]|6.[[0-9]]) drivedb_version=$withval; with_update_smart_drivedb=yes ;; *) AC_MSG_ERROR([Invalid drivedb branch version: $withval]) ;; esac ], [with_update_smart_drivedb=yes]) test -n "$drivedbdir" || with_update_smart_drivedb=no AC_SUBST(with_update_smart_drivedb) AM_CONDITIONAL(ENABLE_UPDATE_SMART_DRIVEDB, [test "$with_update_smart_drivedb" = "yes"]) gnupg="gpg" # Also check for '--with-gnupg[=yes]' because 'yes' is a valid command with infinite output AC_ARG_WITH(gnupg, [AS_HELP_STRING([--with-gnupg@<:@=FILE|yes|no@:>@], [GnuPG used to verify drivedb.h [gpg]])], [case "$withval" in yes) ;; no) gnupg= ;; *) gnupg="$withval" ;; esac], []) AC_SUBST(gnupg) case "$with_update_smart_drivedb:$gnupg" in no:?*) AC_MSG_ERROR([ '--without-update-smart-drivedb' now requires '--without-gnupg'. NEWS: update-smart-drivedb now verifies the downloaded drivedb.h file with GnuPG.]) ;; esac AC_ARG_WITH(smartdscriptdir, [AS_HELP_STRING([--with-smartdscriptdir=DIR], [Location of smartd_warning.sh script [SYSCONFDIR]])], [smartdscriptdir="$withval"], [smartdscriptdir='${sysconfdir}']) AC_SUBST(smartdscriptdir) AC_ARG_WITH(smartdplugindir, [AS_HELP_STRING([--with-smartdplugindir=@<:@DIR|no@:>@], [Location of smartd_warning.sh plugin scripts [SMARTDSCRIPTDIR/smartd_warning.d]])], [smartdplugindir=; test "$withval" != "no" && smartdplugindir="$withval"], [smartdplugindir='${smartdscriptdir}/smartd_warning.d']) AC_SUBST(smartdplugindir) AC_ARG_WITH(scriptpath, [AS_HELP_STRING([--with-scriptpath=@<:@PATH|no@:>@], [PATH variable set within scripts [/usr/local/bin:/usr/bin:/bin]])], [scriptpath=; test "$withval" != "no" && scriptpath="$withval"], [scriptpath="/usr/local/bin:/usr/bin:/bin"]) AC_SUBST(scriptpath) AM_CONDITIONAL(ENABLE_SCRIPTPATH, [test -n "$scriptpath"]) savestates= AC_ARG_WITH(savestates, [AS_HELP_STRING([--with-savestates@<:@=PREFIX|yes|no@:>@], [Enable default smartd state files [no] (yes=LOCALSTATEDIR/lib/smartmontools/smartd.)])], [case "$withval" in yes) savestates='${localstatedir}/lib/${PACKAGE}/smartd.' ;; no) ;; *) savestates="$withval" ;; esac]) savestatesdir="${savestates%/*}" AC_SUBST(savestates) AC_SUBST(savestatesdir) AM_CONDITIONAL(ENABLE_SAVESTATES, [test -n "$savestates"]) attributelog= AC_ARG_WITH(attributelog, [AS_HELP_STRING([--with-attributelog@<:@=PREFIX|yes|no@:>@], [Enable default smartd attribute log files [no] (yes=LOCALSTATEDIR/lib/smartmontools/attrlog.)])], [case "$withval" in yes) attributelog='${localstatedir}/lib/${PACKAGE}/attrlog.' ;; no) ;; *) attributelog="$withval" ;; esac]) attributelogdir="${attributelog%/*}" AC_SUBST(attributelog) AC_SUBST(attributelogdir) AM_CONDITIONAL(ENABLE_ATTRIBUTELOG, [test -n "$attributelog"]) AC_ARG_ENABLE(sample, [AS_HELP_STRING([--enable-sample], [Enables appending .sample to the installed smartd rc script and configuration file])], [smartd_suffix=; test "$enableval" = "yes" && smartd_suffix=".sample"], [smartd_suffix=;]) AC_SUBST(smartd_suffix) AC_ARG_ENABLE([scsi-cdb-check], [AS_HELP_STRING([--enable-scsi-cdb-check], [do sanity check on each SCSI cdb])], [ if test "$enableval" = "yes"; then AC_DEFINE(SCSI_CDB_CHECK, 1, [Define to 1 to enable check on each SCSI cdb]) fi ],[]) AC_ARG_ENABLE([fast-lebe], [AS_HELP_STRING([--disable-fast-lebe], [use generic little-endian/big-endian code instead])], [ if test "$enableval" = "no"; then AC_DEFINE(IGNORE_FAST_LEBE, 1, [Define to 1 to use generic LE/BE code instead]) fi ],[]) AC_ARG_WITH(os-deps, [AS_HELP_STRING([--with-os-deps='os_module.o ...'], [Specify OS dependent module(s) [guessed]])], [ for x in $with_os_deps; do case $x in *.o) ;; *) AC_MSG_ERROR([non-object file specified by --with-os-deps]) ;; esac done ],[]) AC_ARG_WITH(selinux, [AS_HELP_STRING([--with-selinux@<:@=yes|no@:>@], [Enables SELinux support [no]])], [ if test "$withval" = "yes"; then AC_CHECK_HEADERS([selinux/selinux.h], [], [AC_MSG_ERROR([Missing SELinux header files])]) AC_CHECK_LIB(selinux, matchpathcon, [], [AC_MSG_ERROR([Missing or incorrect SELinux library files])]) fi ],[]) AC_ARG_WITH(libcap-ng, [AS_HELP_STRING([--with-libcap-ng@<:@=auto|yes|no@:>@], [Add Libcap-ng support to smartd [auto]])], [], [with_libcap_ng=auto]) use_libcap_ng=no case "$with_libcap_ng:$host_os" in auto:linux*|yes:*) AC_CHECK_HEADERS([cap-ng.h], [AC_CHECK_LIB([cap-ng], [capng_clear], [AC_DEFINE(HAVE_LIBCAP_NG, 1, [Define to 1 if you have the `cap-ng' library (-lcap-ng).]) dnl `vim syntax CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes], [AC_MSG_ERROR([libcap-ng headers found but library is missing])])], [test "$with_libcap_ng" != "yes" || AC_MSG_ERROR([Missing libcap-ng header files])]) ;; esac AC_SUBST(CAPNG_LDADD) AC_ARG_WITH(libsystemd, [AS_HELP_STRING([--with-libsystemd@<:@=auto|yes|no@:>@], [Add systemd 'Type=notify' support to smartd [auto]])], [], [with_libsystemd=auto]) use_libsystemd=no case "$with_libsystemd:$host_os" in auto:linux*|yes:*) AC_CHECK_HEADERS([systemd/sd-daemon.h], [AC_CHECK_LIB([systemd], [sd_notify], [AC_DEFINE(HAVE_LIBSYSTEMD, 1, [Define to 1 if you have the `systemd' library (-lsystemd).]) dnl `vim syntax SYSTEMD_LDADD="-lsystemd"; use_libsystemd=yes], [AC_MSG_ERROR([libsystemd headers found but library is missing])])], [test "$with_libsystemd" != "yes" || AC_MSG_ERROR([Missing libsystemd header files])]) ;; esac AC_SUBST(SYSTEMD_LDADD) AC_ARG_WITH(systemdsystemunitdir, [AS_HELP_STRING([--with-systemdsystemunitdir@<:@=DIR|auto|yes|no@:>@], [Location of systemd service files [auto]])], [], [with_systemdsystemunitdir=auto]) systemdsystemunitdir= case "$with_systemdsystemunitdir:$use_libsystemd" in auto:yes|yes:yes) if test -n "$PKG_CONFIG"; then AC_MSG_CHECKING([for systemdsystemunitdir]) systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null` AC_MSG_RESULT([${systemdsystemunitdir:-no}]) fi case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in yes:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;; yes:*:*|auto:*:|auto:/etc:*) ;; *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;; esac ;; auto:*|no:*) ;; *:yes) systemdsystemunitdir="$with_systemdsystemunitdir" ;; *) AC_MSG_ERROR(['--with-systemdsystemunitdir=$with_systemdsystemunitdir' now requires '--with-libsystemd']) ;; esac AC_SUBST(systemdsystemunitdir) AM_CONDITIONAL(INSTALL_SYSTEMDUNIT, [test -n "$systemdsystemunitdir"]) AC_ARG_WITH(systemdenvfile, [AS_HELP_STRING([--with-systemdenvfile@<:@=FILE|auto|yes|no@:>@], [Path of systemd EnvironmentFile [auto]])], [], [with_systemdenvfile=auto]) systemdenvfile= case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in auto:no:?*|yes:*:?*) AC_MSG_CHECKING([for path of systemd EnvironmentFile]) for dir in sysconfig default; do if test -d /etc/$dir; then systemdenvfile='${sysconfdir}'/$dir/smartmontools break fi done AC_MSG_RESULT([${systemdenvfile:-no}]) case "$with_systemdenvfile:$systemdenvfile" in yes:) AC_MSG_ERROR([Path of systemd EnvironmentFile not found]) ;; esac ;; auto:*|no:*) ;; *:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;; *) systemdenvfile="$with_systemdenvfile" esac AC_SUBST(systemdenvfile) # TODO: Remove when NVMe support is no longer EXPERIMENTAL AC_ARG_WITH(nvme-devicescan, [AS_HELP_STRING([--with-nvme-devicescan@<:@=yes|no@:>@], [Include NVMe devices in smartd DEVICESCAN [Linux,Windows:yes;Others:no]])]) AC_ARG_WITH(solaris-sparc-ata, [AS_HELP_STRING([--with-solaris-sparc-ata@<:@=yes|no@:>@], [Enable legacy ATA support on Solaris SPARC (requires os_solaris_ata.s from SVN repository) [no]])]) case "$host:$with_solaris_sparc_ata" in sparc-*-solaris*:yes) if test ! -f "$srcdir/os_solaris_ata.s"; then AC_MSG_ERROR([Missing source file: $srcdir/os_solaris_ata.s This file is no longer included in the source tarball but still available in the SVN repository.]) fi AC_DEFINE(WITH_SOLARIS_SPARC_ATA, 1, [Define to 1 to enable legacy ATA support on Solaris SPARC.]) ;; esac AC_ARG_WITH(signal-func, [AS_HELP_STRING([--with-signal-func=@<:@sigaction|sigset|signal@:>@], [Function to set signal(2) action [sigaction]])], [], [with_signal_func=sigaction]) case "$host:$with_signal_func" in *-*-mingw*:*) ;; *:sigaction) AC_CHECK_FUNCS([sigaction], [], AC_MSG_ERROR([Missing function 'sigaction()'. Try '--with-signal-func=sigset' or '--with-signal-func=signal'. Please send info about your system to $PACKAGE_BUGREPORT.])) ;; *:sigset) AC_CHECK_FUNCS([sigset], [], AC_MSG_ERROR([Missing function 'sigset()'])) ;; *:signal) ;; *) AC_MSG_ERROR([Invalid option '--with-signal-func=$with_signal_func']) ;; esac # TODO: Remove after smartmontools 6.7 AC_ARG_WITH(working-snprintf, [AS_HELP_STRING([--with-working-snprintf@<:@=yes|no@:>@], [Function snprintf() handles output truncation as specified by C99 [yes]])], [], [with_working_snprintf=yes]) if test "$with_working_snprintf" = "yes"; then AC_DEFINE(HAVE_WORKING_SNPRINTF, 1, [Define to 1 if the `snprintf' function is sane.]) dnl `vim syntax fi case "$with_working_snprintf:$host_os: $CPPFLAGS $CXXFLAGS" in yes:mingw*:*\ -[[DU]]__USE_MINGW_ANSI_STDIO*) ;; yes:mingw*:*) # Older MinGW (4.6.3) do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set. # Newer MinGW (4.9.1) set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late. # Set __USE_MINGW_ANSI_STDIO always and fail if not fully supported. AC_MSG_CHECKING([whether $CXX supports __USE_MINGW_ANSI_STDIO]) save_CXXFLAGS=$CXXFLAGS CXXFLAGS="-Wformat -Werror -D__USE_MINGW_ANSI_STDIO" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #define __STDC_FORMAT_MACROS 1 #include #include void f(char * buf1, char * buf2, size_t size) { snprintf(buf1, size, "%lld", 42LL); snprintf(buf2, size, "%" PRId64, (int64_t)42); }]])], [result=yes], [result=no]) AC_MSG_RESULT([$result]) if test "$result" != "yes"; then AC_MSG_ERROR([ This version of $CXX does not support __USE_MINGW_ANSI_STDIO. Use option '--without-working-snprintf' to skip this check. Please send info about your system to $PACKAGE_BUGREPORT. ]) fi CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $save_CXXFLAGS" ;; esac AC_ARG_WITH(mingw-aslr, [AS_HELP_STRING([--with-mingw-aslr@<:@=auto|yes|low|no@:>@], [Enable ASLR for MinGW executables [auto]])], [], [with_mingw_aslr=auto]) case "$host:${LDFLAGS+set}" in *-*-mingw*:) # MinGW defaults: link statically and indicate DEP and TS compatibility LDFLAGS="-static -Wl,--nxcompat,--tsaware" ;; esac case "$host:$with_mingw_aslr" in x86_64-*-mingw*:auto) AC_MSG_CHECKING([whether $CXX supports --high-entropy-va]) save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup,--high-entropy-va,--image-base,0x140000000" # Link libstdc++ to detect MinGW 6.3.0 problems with high --image-base AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include std::string s(42, '.');]])], [with_mingw_aslr=yes], [with_mingw_aslr=no]) LDFLAGS=$save_LDFLAGS AC_MSG_RESULT([$with_mingw_aslr]) test "$with_mingw_aslr" = "yes" || with_mingw_aslr=low ;; esac case "$host:$with_mingw_aslr" in x86_64-*-mingw*:yes) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup,--high-entropy-va,--image-base,0x140000000" ;; x86_64-*-mingw*:low) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-emainCRTStartup" ;; *-*-mingw*:auto|*-*-mingw*:yes|*-*-mingw*:low) LDFLAGS="$LDFLAGS -pie -Wl,--dynamicbase,-e_mainCRTStartup" ;; esac os_win32_manifest= case "$host" in *-*-mingw*) # Newer MinGW may add a default manifest AC_MSG_CHECKING([whether $CXX adds an application manifest]) cc_adds_manifest=no AC_LINK_IFELSE([AC_LANG_PROGRAM()], [ if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '^1.*RT_MANIFEST' >/dev/null 2>&1; then cc_adds_manifest=incomplete # Manifest must provide a Win 10 compatibility ID if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' >/dev/null 2>&1; then cc_adds_manifest=yes fi fi], [AC_MSG_ERROR([test compile failed])]) AC_MSG_RESULT([$cc_adds_manifest]) test "$cc_adds_manifest" = "yes" || os_win32_manifest='os_win32/default.manifest' ;; esac AC_ARG_WITH(cxx11-option, [AS_HELP_STRING([--with-cxx11-option=@<:@OPTION|auto|no@:>@], [Compiler option to enable C++11 support for future versions of smartmontools, 'no' if unsupported [auto]])], [], [with_cxx11_option=auto]) check_cxx11_support() { save_CXXFLAGS=$CXXFLAGS CXXFLAGS=$1 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __cplusplus < 201103L #error false #endif // use some C++11 features (and return v * 42 :-) auto cxx11(long v) noexcept -> decltype(v) { typedef decltype(v) t; t r = v; static const t a[] = { -7, -1, 1, 2, 3 }; static_assert(sizeof(r) == sizeof(a[0]), "fail"); auto f = [](t x, t y){ return x * y; }; for (const auto & e : a) r = f(r, e); return r; }]])], [CXXFLAGS=$save_CXXFLAGS; return 0], [CXXFLAGS=$save_CXXFLAGS; return 1]) } case "$with_cxx11_option" in no) ;; auto) AC_MSG_CHECKING([for $CXX option to accept C++11]) with_cxx11_option=unknown for option in "" "-std=gnu++11" "-std=gnu++0x" "-std=c++11" "-std=c++0x"; do if check_cxx11_support "$option"; then with_cxx11_option=$option; break; fi done AC_MSG_RESULT([${with_cxx11_option:-none needed}]) test "$with_cxx11_option" != "unknown" || AC_MSG_ERROR([ This version of smartmontools does not use C++11 features, but future versions possibly will. This script was unable to determine a compiler option to enable C++11. Use option '--with-cxx11-option=OPTION' to specify the compiler option (it will be used in the actual build only if '--with-cxx11-regex' is set). Use option '--without-cxx11-option' to suppress this error message if the compiler lacks C++11 support. In both cases, please send info about compiler and platform to $PACKAGE_BUGREPORT - Thanks!]) ;; *) AC_MSG_CHECKING([whether $CXX $with_cxx11_option accepts C++11]) res=no; check_cxx11_support "$with_cxx11_option" && res=yes AC_MSG_RESULT([$res]) test "$res" = "yes" || AC_MSG_ERROR([$CXX $with_cxx11_option does not accept C++11]) ;; esac AC_ARG_WITH(cxx11-regex, [AS_HELP_STRING([--with-cxx11-regex@<:@=yes|no@:>@], [Use C++11 std::regex instead of POSIX regex(3) [no]])]) need_regex=no if test "$with_cxx11_regex" = "yes"; then AC_DEFINE(WITH_CXX11_REGEX, 1, [Define to 1 to use C++11 std::regex instead of POSIX regex(3)]) case "$with_cxx11_option: $CXXFLAGS " in no:*) AC_MSG_ERROR(['--with-cxx11-regex' requires C++11 support]) ;; ?*:*\ $with_cxx11_option\ *) ;; ?*:*) CXXFLAGS="$CXXFLAGS $with_cxx11_option" ;; esac else AC_CHECK_FUNCS([regcomp], [], [need_regex=yes]) fi AM_CONDITIONAL(NEED_REGEX, [test "$need_regex" = "yes"]) AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}']) AC_SUBST(smartmontools_release_date) AC_SUBST(smartmontools_release_time) # Set platform-specific modules and symbols os_libs= os_dltools='curl wget lynx svn' os_mailer=mail os_hostname="'hostname'" os_dnsdomainname= os_nisdomainname="'domainname'" os_darwin=no os_solaris=no os_win32=no os_win32_mingw=no os_win64=no os_man_filter= os_nvme_devicescan= case "${host}" in *-*-linux*) os_deps='os_linux.o cciss.o dev_areca.o' os_dnsdomainname="'dnsdomainname' 'hostname -d'" os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'" os_man_filter=Linux os_nvme_devicescan=yes ;; *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*) os_deps='os_freebsd.o cciss.o dev_areca.o' os_libs='-lcam -lsbuf' os_dltools='curl wget lynx fetch svn' AC_CHECK_LIB(usb, libusb20_dev_get_device_desc) os_man_filter=FreeBSD os_nvme_devicescan=no ;; sparc-*-solaris*) os_deps='os_solaris.o' test "$with_solaris_sparc_ata" = "yes" \ && os_deps="$os_deps os_solaris_ata.o" os_mailer='mailx' os_solaris=yes os_man_filter=Solaris ;; *-pc-solaris*) os_deps='os_solaris.o' os_mailer='mailx' os_solaris=yes os_man_filter=Solaris ;; *-*-netbsd*) os_deps='os_netbsd.o' os_libs='-lutil' os_man_filter=NetBSD os_nvme_devicescan=no ;; *-*-openbsd*) os_deps='os_openbsd.o' os_libs='-lutil' os_dltools='curl wget lynx ftp svn' os_man_filter=OpenBSD ;; *-*-cygwin*) os_deps='os_win32.o dev_areca.o' os_mailer='email' os_hostname="'hostname' 'echo "'"${HOSTNAME?unset}"'"'" os_dnsdomainname="'dnsdomainname' 'hostname -d' 'echo "'"${USERDNSDOMAIN?unset}"'"'" os_nisdomainname= os_win32=yes os_man_filter=Cygwin os_nvme_devicescan=yes ;; x86_64-*-mingw*) os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_win64=yes os_man_filter=Windows os_nvme_devicescan=yes ;; *-*-mingw*) os_deps='os_win32.o dev_areca.o' os_win32=yes os_win32_mingw=yes os_man_filter=Windows os_nvme_devicescan=yes ;; *-*-darwin*) os_deps='os_darwin.o' os_libs='-framework CoreFoundation -framework IOKit' os_darwin=yes os_man_filter=Darwin os_nvme_devicescan=no ;; *-*-nto-qnx*) os_deps='os_qnxnto.o' ;; *-*-os2-*) os_deps='os_os2.o' ;; *) os_deps='os_generic.o' ;; esac # Replace if '--with-os-deps' was specified test -z "$with_os_deps" || os_deps="$with_os_deps" # Check if we need adapter to old interface (dev_legacy.cpp) os_src=`echo "${os_deps}"|sed -n 's,^\([[^ .]]*\)\.o.*$,\1.cpp,p'` AC_MSG_CHECKING([whether ${os_src} uses new interface]) if grep "smart_interface" "${srcdir}/${os_src}" >/dev/null 2>&1; then os_new_interface=yes else os_new_interface=no os_deps="${os_deps} dev_legacy.o" AC_DEFINE(OLD_INTERFACE, 1, [Define to 1 if os_*.cpp still uses the old interface]) fi AC_MSG_RESULT([$os_new_interface]) # TODO: Remove when NVMe support is no longer EXPERIMENTAL case "$os_nvme_devicescan:${with_nvme_devicescan+set}" in no:|yes:) AC_MSG_CHECKING([whether NVMe device scanning could be safely enabled]) with_nvme_devicescan=$os_nvme_devicescan AC_MSG_RESULT([$os_nvme_devicescan]) os_nvme_devicescan=used ;; esac AC_SUBST(with_nvme_devicescan) if test "$with_nvme_devicescan" = "yes"; then AC_DEFINE(WITH_NVME_DEVICESCAN, 1, [Define to 1 to include NVMe devices in smartd DEVICESCAN.]) fi AC_SUBST([os_deps]) AC_SUBST([os_libs]) AC_SUBST([os_dltools]) AC_SUBST([os_mailer]) AC_SUBST([os_hostname]) AC_SUBST([os_dnsdomainname]) AC_SUBST([os_nisdomainname]) AC_SUBST([os_man_filter]) AC_SUBST([os_win32_manifest]) # Create drivedb.h update branch name from version: 5.41[.X] -> RELEASE_5_41_DRIVEDB DRIVEDB_BRANCH=`echo "$drivedb_version" | sed 's,^\([[0-9]]*\.[[0-9]]*\)\..*$,\1,' \ | sed -n 's,^\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)$,RELEASE_\1_\2_DRIVEDB,p'` if test -z "$DRIVEDB_BRANCH"; then AC_MSG_ERROR([Unable to create DRIVEDB_BRANCH for version: $drivedb_version]) fi AC_SUBST([DRIVEDB_BRANCH]) # Enable platform-specific makefile sections AM_CONDITIONAL(OS_DARWIN, [test "$os_darwin" = "yes"]) AM_CONDITIONAL(OS_SOLARIS, [test "$os_solaris" = "yes"]) AM_CONDITIONAL(OS_WIN32, [test "$os_win32" = "yes"]) AM_CONDITIONAL(OS_WIN32_MINGW, [test "$os_win32_mingw" = "yes"]) AM_CONDITIONAL(OS_WIN32_NSIS, [test -n "$MAKENSIS"]) AM_CONDITIONAL(OS_WIN64, [test "$os_win64" = "yes"]) if test "$GXX" = "yes"; then orig_CXXFLAGS=$CXXFLAGS # Add -Wall and -W[extra] if its not already specified case " $CXXFLAGS " in *\ -Wall\ *) ;; *) CXXFLAGS="$CXXFLAGS -Wall" ;; esac case " $CXXFLAGS " in *\ -W\ *|*\ -Wextra\ *) ;; *) CXXFLAGS="$CXXFLAGS -W" ;; esac # Add -Wformat=2 (GCC 3.0) -fstack-protector[-strong] (GCC 4.1[4.9]) if supported # and no -W or -f option was set in configure cmdline (TODO: -Wformat-signedness) for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do case " $orig_CXXFLAGS:$option" in *\ -W*:-W*|*\ -f*:-f*) continue ;; esac case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac AC_MSG_CHECKING([whether $CXX supports $option]) save_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $option" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [res=yes], [res=no; CXXFLAGS=$save_CXXFLAGS]) AC_MSG_RESULT([$res]) done else # We are NOT using gcc, so enable host-specific compiler flags case "${host}" in sparc*-*-solaris*) # Tell the Solaris/SPARC C++ compiler about packed ATA structures case " $CXXFLAGS" in *\ -xmemalign*) ;; *) CXXFLAGS="-xmemalign=1i $CXXFLAGS" ;; esac ;; esac case "${host}" in *-*-solaris*) # Turn on optimization if user has not explicitly set its value case " $CXXFLAGS" in *\ -xO*) ;; *) CXXFLAGS="-xO2 $CXXFLAGS" ;; esac # Suppress trivial warnings case " $CXXFLAGS" in *\ -erroff*) ;; *) CXXFLAGS="-erroff=%none,wbadinitl,wbadasgl,badargtypel2w,badargtype2w $CXXFLAGS" ;; esac ;; esac fi AC_DEFINE_UNQUOTED(SMARTMONTOOLS_BUILD_HOST, "${host}", [smartmontools Build Host]) AC_SUBST(CXXFLAGS) AC_CONFIG_FILES(Makefile) AC_OUTPUT AC_PROG_MAKE_SET # Note: Use `...` here as some shells do not properly parse '$(... case $x in X) ...)' info=` echo "-----------------------------------------------------------------------------" echo "${PACKAGE}-${VERSION} configuration:" echo "host operating system: $host" echo "C++ compiler: $CXX" echo "C compiler: $CC" echo "preprocessor flags: $CPPFLAGS" echo "C++ compiler flags: $CXXFLAGS" echo "C compiler flags: $CFLAGS" echo "linker flags: $LDFLAGS" echo "OS specific modules: $os_deps $os_libs $LIBS" case "$host_os" in mingw*) echo "application manifest: ${os_win32_manifest:-built-in}" echo "resource compiler: $WINDRES" echo "message compiler: $WINDMC" echo "NSIS compiler: $MAKENSIS" if test -n "$drivedbdir"; then echo "drive database file: EXEDIR/drivedb.h" if test -n "$MAKENSIS"; then echo "database update tool: EXEDIR/update-smart-drivedb.exe" fi else echo "drive database file: [[disabled]]" fi if test -n "$savestates"; then echo "smartd save files: \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state" fi if test -n "$attributelog"; then echo "smartd attribute logs: \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv" fi echo "NVMe DEVICESCAN: ${with_nvme_devicescan-no}" ;; *) echo "binary install path: \`eval eval eval echo $sbindir\`" echo "man page install path: \`eval eval eval echo $mandir\`" echo "doc file install path: \`eval eval eval echo $docdir\`" echo "examples install path: \`eval eval eval echo $exampledir\`" if test -n "$drivedbdir"; then echo "drive database file: \`eval eval eval echo $drivedbdir\`/drivedb.h" if test "$with_update_smart_drivedb" = "yes"; then echo "database update script: \`eval eval eval echo $sbindir\`/update-smart-drivedb" if test "$drivedb_version" = "$smartmontools_drivedb_version"; then echo "database update branch: branches/$DRIVEDB_BRANCH" else echo "... backported to: branches/$DRIVEDB_BRANCH" fi echo "download tools: \`eval eval eval echo $os_dltools\`" if test -n "$gnupg"; then echo "GnuPG for verification: \`eval eval eval echo $gnupg\`" else echo "GnuPG for verification: [[disabled]]" fi else echo "database update script: [[disabled]]" fi else echo "drive database file: [[disabled]]" fi echo "local drive database: \`eval eval eval echo $sysconfdir\`/smart_drivedb.h" echo "smartd config file: \`eval eval eval echo $sysconfdir\`/smartd.conf${smartd_suffix}" echo "smartd warning script: \`eval eval eval echo $smartdscriptdir\`/smartd_warning.sh" if test -n "$smartdplugindir"; then echo "smartd plugin path: \`eval eval eval echo $smartdplugindir\`" else echo "smartd plugin path: [[disabled]]" fi if test -n "$scriptpath"; then echo "PATH within scripts: \`eval eval eval echo $scriptpath\`" else echo "PATH within scripts: [[inherited]]" fi if test -n "$initddir"; then echo "smartd initd script: \`eval eval eval echo $initddir\`/smartd" elif test -z "$systemdsystemunitdir"; then echo "smartd initd script: [[disabled]]" fi if test -n "$systemdsystemunitdir"; then echo "smartd service file: \`eval eval eval echo $systemdsystemunitdir\`/smartd.service" if test -n "$systemdenvfile"; then echo "smartd environ file: \`eval eval eval echo $systemdenvfile\`" else echo "smartd environ file: [[disabled]]" fi fi if test -n "$savestates"; then echo "smartd save files: \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state" else echo "smartd save files: [[disabled]]" fi if test -n "$attributelog"; then echo "smartd attribute logs: \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv" else echo "smartd attribute logs: [[disabled]]" fi case "$host_os" in linux*) echo "SELinux support: ${with_selinux-no}" echo "libcap-ng support: $use_libcap_ng" echo "systemd notify support: $use_libsystemd" ;; esac echo "NVMe DEVICESCAN: ${with_nvme_devicescan-[[not implemented]]}" ;; esac echo "-----------------------------------------------------------------------------" ` AC_MSG_NOTICE([ $info ]) # TODO: Remove when NVMe support is no longer EXPERIMENTAL case "$os_nvme_devicescan:$with_nvme_devicescan" in used:yes) AC_MSG_WARN([ The default for the inclusion of NVME devices in smartd.conf 'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on this platform. If '--without-nvme-devicescan' is still needed, please inform $PACKAGE_BUGREPORT. Use option '--with-nvme-devicescan' to suppress this warning. ]) ;; used:no) AC_MSG_WARN([ This version of smartmontools provides NVMe support which is still EXPERIMENTAL. NVMe devices are not yet included in smartd.conf 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified. Use option '--with-nvme-devicescan' to include NVMe devices. Use option '--without-nvme-devicescan' to suppress this warning. ]) ;; esac # TODO: Remove after smartmontools 6.7 if test "$with_working_snprintf" != "yes"; then AC_MSG_WARN([ The option '--without-working-snprintf' is deprecated and will be removed in a future version of smartmontools. If you still need support for pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT. ]) fi case "$host_os:$with_libsystemd:$use_libsystemd:$PKG_CONFIG" in linux*:auto:no:?*) if $PKG_CONFIG systemd >/dev/null 2>&1; then AC_MSG_WARN([ systemd(1) is used on this system but smartd systemd notify support will not be available because libsystemd-dev[[el]] package is not installed. Use option '--without-libsystemd' to suppress this warning. ]) fi ;; esac smartmontools-7.0/COPYING0000644000175000010010000004325411652065010012246 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General Public License instead of this License. smartmontools-7.0/csmisas.h0000644000175000010010000015556412123643645013050 00000000000000/************************************************************************** Module Name: CSMISAS.H Abstract: This file contains constants and data structure definitions used by drivers that support the Common Storage Management Interface specification for SAS or SATA in either the Windows or Linux. This should be considered as a reference implementation only. Changes may be necessary to accommodate a specific build environment or target OS. Revision History: 001 SEF 8/12/03 Initial release. 002 SEF 8/20/03 Cleanup to match documentation. 003 SEF 9/12/03 Additional cleanup, created combined header 004 SEF 9/23/03 Changed base types to match linux defaults Added RAID signature Added bControllerFlags to CSMI_SAS_CNTLR_CONFIG Changed CSMI_SAS_BEGIN_PACK to 8 for common structures Fixed other typos identified in first compilation test 005 SEF 10/03/03 Additions to match first version of CSMI document 006 SEF 10/14/03 Fixed typedef struct _CSMI_SAS_SMP_PASSTHRU_BUFFER Added defines for bConnectionRate 007 SEF 10/15/03 Added Firmware Download Control Code and support Added CSMI revision support 008 SEF 10/30/03 No functional change, just updated version to track spec changes 009 SEF 12/09/03 No functional change, just updated version to track spec changes 010 SEF 3/11/04 Fixed typedef struct CSMI_SAS_RAID_DRIVES to include the bFirmware member that is defined in the spec, but was missing in this file, added CC_CSMI_SAS_TASK_MANAGEMENT 011 SEF 4/02/04 No functional change, added comment line before CC_CSMI_SAS_TASK_MANAGEMENT 012 SEF 4/16/04 Added IOControllerNumber to linux header, Modified linux control codes to have upper word of 0xCC77.... to indicate CSMI version 77 Added bSignalClass to CC_CSMI_SET_PHY_INFO Added CC_CSMI_SAS_PHY_CONTROL support 013 SEF 5/14/04 Added CC_CSMI_SAS_GET_CONNECTOR_INFO support 014 SEF 5/24/04 No functional change, just updated version to track spec changes 015 SEF 6/16/04 changed bPinout to uPinout to reflect proper size, changed width of bLocation defines to reflect size 016 SEF 6/17/04 changed bLengthOfControls in CSMI_SAS_PHY_CONTROL to be proper size 017 SEF 9/17/04 added CSMI_SAS_SATA_PORT_SELECTOR, CSMI_SAS_LINK_VIRTUAL, CSMI_SAS_CON_NOT_PRESENT, and CSMI_SAS_CON_NOT_CONNECTED 018 SEF 9/20/04 added CSMI_SAS_PHY_USER_PATTERN, changed definition of CSMI_SAS_PHY_FIXED_PATTERN to not conflict with activate definition 019 SEF 12/06/04 added CSMI_SAS_GET_LOCATION added bSSPStatus to CSMI_SAS_SSP_PASSTHRU_STATUS structure 020 SEF 5/25/05 added CSMI_SAS_PHY_VIRTUAL_SMP, and changes to CSMI_SAS_GET_LOCATION 021 SEF 11/03/05 added new RAID creation functionality 022 SEF 2/01/06 corrected typo bNegotitiatedLInkRate Added two more RAID_TYPES, 7 and 8 023 SEF 4/04/06 added CSMI_RAID_TYPE_1E changed structures that contained surface scan to priority approach rather than time, causes 0.89 to incompatible with 0.87, so a version check is necessary when interpreting the raid structures Added netware section 024 DRG 5/22/06 Added uFailureCode to CSMI_SAS_RAID_CONFIG and CSMI_SAS_RAID_FEATURES Changed __u64 fields to high and low __u32 fields in order to avoid backward compatibility issues with packing and alignment. Fixed alignment problem in CSMI_SAS_RAID_DRIVES. Added CSMI_SAS_CNTLR_SMART_ARRAY to uControllerFlags Reassigned the value of CSMI_SAS_CNTLR_RAID_CFG_SUPPORT to avoid a conflict. **************************************************************************/ #ifndef _CSMI_SAS_H_ #define _CSMI_SAS_H_ // CSMI Specification Revision, the intent is that all versions of the // specification will be backward compatible after the 1.00 release. // Major revision number, corresponds to xxxx. of CSMI specification // Minor revision number, corresponds to .xxxx of CSMI specification #define CSMI_MAJOR_REVISION 0 #define CSMI_MINOR_REVISION 90 /*************************************************************************/ /* PATCHES FOR TYPOS */ /*************************************************************************/ #define bNegotitiatedLInkRate bNegotiatedLinkRate /*************************************************************************/ /* TARGET OS LINUX SPECIFIC CODE */ /*************************************************************************/ // EDM #ifdef _linux #ifdef __KERNEL__ // Linux base types #include #define __i8 char // pack definition // EDM #define CSMI_SAS_BEGIN_PACK(x) pack(x) // EDM #define CSMI_SAS_END_PACK pack() // IOCTL Control Codes // (IoctlHeader.ControlCode) // Control Codes prior to 0.77 // Control Codes requiring CSMI_ALL_SIGNATURE // #define CC_CSMI_SAS_GET_DRIVER_INFO 0x12345678 // #define CC_CSMI_SAS_GET_CNTLR_CONFIG 0x23456781 // #define CC_CSMI_SAS_GET_CNTLR_STATUS 0x34567812 // #define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0x92345678 // Control Codes requiring CSMI_RAID_SIGNATURE // #define CC_CSMI_SAS_GET_RAID_INFO 0x45678123 // #define CC_CSMI_SAS_GET_RAID_CONFIG 0x56781234 // Control Codes requiring CSMI_SAS_SIGNATURE // #define CC_CSMI_SAS_GET_PHY_INFO 0x67812345 // #define CC_CSMI_SAS_SET_PHY_INFO 0x78123456 // #define CC_CSMI_SAS_GET_LINK_ERRORS 0x81234567 // #define CC_CSMI_SAS_SMP_PASSTHRU 0xA1234567 // #define CC_CSMI_SAS_SSP_PASSTHRU 0xB1234567 // #define CC_CSMI_SAS_STP_PASSTHRU 0xC1234567 // #define CC_CSMI_SAS_GET_SATA_SIGNATURE 0xD1234567 // #define CC_CSMI_SAS_GET_SCSI_ADDRESS 0xE1234567 // #define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0xF1234567 // #define CC_CSMI_SAS_TASK_MANAGEMENT 0xA2345678 // Control Codes for 0.77 and later // Control Codes requiring CSMI_ALL_SIGNATURE #define CC_CSMI_SAS_GET_DRIVER_INFO 0xCC770001 #define CC_CSMI_SAS_GET_CNTLR_CONFIG 0xCC770002 #define CC_CSMI_SAS_GET_CNTLR_STATUS 0xCC770003 #define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0xCC770004 // Control Codes requiring CSMI_RAID_SIGNATURE #define CC_CSMI_SAS_GET_RAID_INFO 0xCC77000A #define CC_CSMI_SAS_GET_RAID_CONFIG 0xCC77000B #define CC_CSMI_SAS_GET_RAID_FEATURES 0xCC77000C #define CC_CSMI_SAS_SET_RAID_CONTROL 0xCC77000D #define CC_CSMI_SAS_GET_RAID_ELEMENT 0xCC77000E #define CC_CSMI_SAS_SET_RAID_OPERATION 0xCC77000F // Control Codes requiring CSMI_SAS_SIGNATURE #define CC_CSMI_SAS_GET_PHY_INFO 0xCC770014 #define CC_CSMI_SAS_SET_PHY_INFO 0xCC770015 #define CC_CSMI_SAS_GET_LINK_ERRORS 0xCC770016 #define CC_CSMI_SAS_SMP_PASSTHRU 0xCC770017 #define CC_CSMI_SAS_SSP_PASSTHRU 0xCC770018 #define CC_CSMI_SAS_STP_PASSTHRU 0xCC770019 #define CC_CSMI_SAS_GET_SATA_SIGNATURE 0xCC770020 #define CC_CSMI_SAS_GET_SCSI_ADDRESS 0xCC770021 #define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0xCC770022 #define CC_CSMI_SAS_TASK_MANAGEMENT 0xCC770023 #define CC_CSMI_SAS_GET_CONNECTOR_INFO 0xCC770024 #define CC_CSMI_SAS_GET_LOCATION 0xCC770025 // Control Codes requiring CSMI_PHY_SIGNATURE #define CC_CSMI_SAS_PHY_CONTROL 0xCC77003C // EDM #pragma CSMI_SAS_BEGIN_PACK(8) #pragma pack(8) // IOCTL_HEADER typedef struct _IOCTL_HEADER { __u32 IOControllerNumber; __u32 Length; __u32 ReturnCode; __u32 Timeout; __u16 Direction; } IOCTL_HEADER, *PIOCTL_HEADER; // EDM #pragma CSMI_SAS_END_PACK #pragma pack() #endif /*************************************************************************/ /* TARGET OS WINDOWS SPECIFIC CODE */ /*************************************************************************/ #ifdef _WIN32 // windows IOCTL definitions #if 0 // and CSMI_*_PACK are no longer needed #ifndef _NTDDSCSIH_ #include #endif // pack definition #if defined _MSC_VER #define CSMI_SAS_BEGIN_PACK(x) pack(push,x) #define CSMI_SAS_END_PACK pack(pop) #elif defined __BORLANDC__ #define CSMI_SAS_BEGIN_PACK(x) option -a##x #define CSMI_SAS_END_PACK option -a. #else #error "CSMISAS.H - Must externally define a pack compiler designator." #endif #endif // #if 0 // base types #define __u8 unsigned char #define __u16 unsigned short #ifndef __LP64__ // ILP32 (32-bit), LLP64 (64-bit MSVC, MinGW) #define __u32 unsigned long #else // LP64 (64-bit Cygwin) #define __u32 unsigned int #endif #define __u64 unsigned __int64 #define __i8 char // IOCTL Control Codes // (IoctlHeader.ControlCode) // Control Codes requiring CSMI_ALL_SIGNATURE #define CC_CSMI_SAS_GET_DRIVER_INFO 1 #define CC_CSMI_SAS_GET_CNTLR_CONFIG 2 #define CC_CSMI_SAS_GET_CNTLR_STATUS 3 #define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 4 // Control Codes requiring CSMI_RAID_SIGNATURE #define CC_CSMI_SAS_GET_RAID_INFO 10 #define CC_CSMI_SAS_GET_RAID_CONFIG 11 #define CC_CSMI_SAS_GET_RAID_FEATURES 12 #define CC_CSMI_SAS_SET_RAID_CONTROL 13 #define CC_CSMI_SAS_GET_RAID_ELEMENT 14 #define CC_CSMI_SAS_SET_RAID_OPERATION 15 // Control Codes requiring CSMI_SAS_SIGNATURE #define CC_CSMI_SAS_GET_PHY_INFO 20 #define CC_CSMI_SAS_SET_PHY_INFO 21 #define CC_CSMI_SAS_GET_LINK_ERRORS 22 #define CC_CSMI_SAS_SMP_PASSTHRU 23 #define CC_CSMI_SAS_SSP_PASSTHRU 24 #define CC_CSMI_SAS_STP_PASSTHRU 25 #define CC_CSMI_SAS_GET_SATA_SIGNATURE 26 #define CC_CSMI_SAS_GET_SCSI_ADDRESS 27 #define CC_CSMI_SAS_GET_DEVICE_ADDRESS 28 #define CC_CSMI_SAS_TASK_MANAGEMENT 29 #define CC_CSMI_SAS_GET_CONNECTOR_INFO 30 #define CC_CSMI_SAS_GET_LOCATION 31 // Control Codes requiring CSMI_PHY_SIGNATURE #define CC_CSMI_SAS_PHY_CONTROL 60 #define IOCTL_HEADER SRB_IO_CONTROL #define PIOCTL_HEADER PSRB_IO_CONTROL #endif /*************************************************************************/ /* TARGET OS NETWARE SPECIFIC CODE */ /*************************************************************************/ #ifdef _NETWARE // NetWare IOCTL definitions #define CSMI_SAS_BEGIN_PACK(x) pack(x) #define CSMI_SAS_END_PACK pack() #ifndef LONG typedef unsigned long LONG; #endif #ifndef WORD typedef unsigned short WORD; #endif #ifndef BYTE typedef unsigned char BYTE; #endif /* Need to have these definitions for Netware */ #define __u8 unsigned char #define __u16 unsigned short #define __u32 unsigned long #define __u64 unsigned __int64 #define __i8 char // EDM #pragma CSMI_SAS_BEGIN_PACK(8) #pragma pack(8) // IOCTL_HEADER typedef struct _IOCTL_HEADER { __u32 Length; __u32 ReturnCode; } IOCTL_HEADER, *PIOCTL_HEADER; // EDM #pragma CSMI_SAS_END_PACK #pragma pack() // IOCTL Control Codes // (IoctlHeader.ControlCode) // Control Codes requiring CSMI_ALL_SIGNATURE #define CC_CSMI_SAS_GET_DRIVER_INFO 0x01FF0001 #define CC_CSMI_SAS_GET_CNTLR_CONFIG 0x01FF0002 #define CC_CSMI_SAS_GET_CNTLR_STATUS 0x01FF0003 #define CC_CSMI_SAS_FIRMWARE_DOWNLOAD 0x01FF0004 // Control Codes requiring CSMI_RAID_SIGNATURE #define CC_CSMI_SAS_GET_RAID_INFO 0x01FF000A #define CC_CSMI_SAS_GET_RAID_CONFIG 0x01FF000B #define CC_CSMI_SAS_GET_RAID_FEATURES 0x01FF000C #define CC_CSMI_SAS_SET_RAID_CONTROL 0x01FF000D #define CC_CSMI_SAS_GET_RAID_ELEMENT 0x01FF000E #define CC_CSMI_SAS_SET_RAID_OPERATION 0x01FF000F // Control Codes requiring CSMI_SAS_SIGNATURE #define CC_CSMI_SAS_GET_PHY_INFO 0x01FF0014 #define CC_CSMI_SAS_SET_PHY_INFO 0x01FF0015 #define CC_CSMI_SAS_GET_LINK_ERRORS 0x01FF0016 #define CC_CSMI_SAS_SMP_PASSTHRU 0x01FF0017 #define CC_CSMI_SAS_SSP_PASSTHRU 0x01FF0018 #define CC_CSMI_SAS_STP_PASSTHRU 0x01FF0019 #define CC_CSMI_SAS_GET_SATA_SIGNATURE 0x01FF001A #define CC_CSMI_SAS_GET_SCSI_ADDRESS 0x01FF001B #define CC_CSMI_SAS_GET_DEVICE_ADDRESS 0x01FF001C #define CC_CSMI_SAS_TASK_MANAGEMENT 0x01FF001D #define CC_CSMI_SAS_GET_CONNECTOR_INFO 0x01FF001E #define CC_CSMI_SAS_GET_LOCATION 0x01FF001F // Control Codes requiring CSMI_PHY_SIGNATURE #define CC_CSMI_SAS_PHY_CONTROL 60 #endif /*************************************************************************/ /* TARGET OS NOT DEFINED ERROR */ /*************************************************************************/ // EDM //#if (!_WIN32 && !_linux && !_NETWARE) // #error "Unknown target OS." //#endif /*************************************************************************/ /* OS INDEPENDENT CODE */ /*************************************************************************/ /* * * * * * * * * * Class Independent IOCTL Constants * * * * * * * * * */ // Return codes for all IOCTL's regardless of class // (IoctlHeader.ReturnCode) #define CSMI_SAS_STATUS_SUCCESS 0 #define CSMI_SAS_STATUS_FAILED 1 #define CSMI_SAS_STATUS_BAD_CNTL_CODE 2 #define CSMI_SAS_STATUS_INVALID_PARAMETER 3 #define CSMI_SAS_STATUS_WRITE_ATTEMPTED 4 // Signature value // (IoctlHeader.Signature) #define CSMI_ALL_SIGNATURE "CSMIALL" // Timeout value default of 60 seconds // (IoctlHeader.Timeout) #define CSMI_ALL_TIMEOUT 60 // Direction values for data flow on this IOCTL // (IoctlHeader.Direction, Linux only) #define CSMI_SAS_DATA_READ 0 #define CSMI_SAS_DATA_WRITE 1 // I/O Bus Types // ISA and EISA bus types are not supported // (bIoBusType) #define CSMI_SAS_BUS_TYPE_PCI 3 #define CSMI_SAS_BUS_TYPE_PCMCIA 4 // Controller Status // (uStatus) #define CSMI_SAS_CNTLR_STATUS_GOOD 1 #define CSMI_SAS_CNTLR_STATUS_FAILED 2 #define CSMI_SAS_CNTLR_STATUS_OFFLINE 3 #define CSMI_SAS_CNTLR_STATUS_POWEROFF 4 // Offline Status Reason // (uOfflineReason) #define CSMI_SAS_OFFLINE_REASON_NO_REASON 0 #define CSMI_SAS_OFFLINE_REASON_INITIALIZING 1 #define CSMI_SAS_OFFLINE_REASON_BACKSIDE_BUS_DEGRADED 2 #define CSMI_SAS_OFFLINE_REASON_BACKSIDE_BUS_FAILURE 3 // Controller Class // (bControllerClass) #define CSMI_SAS_CNTLR_CLASS_HBA 5 // Controller Flag bits // (uControllerFlags) #define CSMI_SAS_CNTLR_SAS_HBA 0x00000001 #define CSMI_SAS_CNTLR_SAS_RAID 0x00000002 #define CSMI_SAS_CNTLR_SATA_HBA 0x00000004 #define CSMI_SAS_CNTLR_SATA_RAID 0x00000008 #define CSMI_SAS_CNTLR_SMART_ARRAY 0x00000010 // for firmware download #define CSMI_SAS_CNTLR_FWD_SUPPORT 0x00010000 #define CSMI_SAS_CNTLR_FWD_ONLINE 0x00020000 #define CSMI_SAS_CNTLR_FWD_SRESET 0x00040000 #define CSMI_SAS_CNTLR_FWD_HRESET 0x00080000 #define CSMI_SAS_CNTLR_FWD_RROM 0x00100000 // for RAID configuration supported #define CSMI_SAS_CNTLR_RAID_CFG_SUPPORT 0x01000000 // Download Flag bits // (uDownloadFlags) #define CSMI_SAS_FWD_VALIDATE 0x00000001 #define CSMI_SAS_FWD_SOFT_RESET 0x00000002 #define CSMI_SAS_FWD_HARD_RESET 0x00000004 // Firmware Download Status // (usStatus) #define CSMI_SAS_FWD_SUCCESS 0 #define CSMI_SAS_FWD_FAILED 1 #define CSMI_SAS_FWD_USING_RROM 2 #define CSMI_SAS_FWD_REJECT 3 #define CSMI_SAS_FWD_DOWNREV 4 // Firmware Download Severity // (usSeverity> #define CSMI_SAS_FWD_INFORMATION 0 #define CSMI_SAS_FWD_WARNING 1 #define CSMI_SAS_FWD_ERROR 2 #define CSMI_SAS_FWD_FATAL 3 /* * * * * * * * * * SAS RAID Class IOCTL Constants * * * * * * * * */ // Return codes for the RAID IOCTL's regardless of class // (IoctlHeader.ReturnCode) #define CSMI_SAS_RAID_SET_OUT_OF_RANGE 1000 #define CSMI_SAS_RAID_SET_BUFFER_TOO_SMALL 1001 #define CSMI_SAS_RAID_SET_DATA_CHANGED 1002 // Signature value // (IoctlHeader.Signature) #define CSMI_RAID_SIGNATURE "CSMIARY" // Timeout value default of 60 seconds // (IoctlHeader.Timeout) #define CSMI_RAID_TIMEOUT 60 // RAID Types // (bRaidType) #define CSMI_SAS_RAID_TYPE_NONE 0 #define CSMI_SAS_RAID_TYPE_0 1 #define CSMI_SAS_RAID_TYPE_1 2 #define CSMI_SAS_RAID_TYPE_10 3 #define CSMI_SAS_RAID_TYPE_5 4 #define CSMI_SAS_RAID_TYPE_15 5 #define CSMI_SAS_RAID_TYPE_6 6 #define CSMI_SAS_RAID_TYPE_50 7 #define CSMI_SAS_RAID_TYPE_VOLUME 8 #define CSMI_SAS_RAID_TYPE_1E 9 #define CSMI_SAS_RAID_TYPE_OTHER 255 // the last value 255 was already defined for other // so end is defined as 254 #define CSMI_SAS_RAID_TYPE_END 254 // RAID Status // (bStatus) #define CSMI_SAS_RAID_SET_STATUS_OK 0 #define CSMI_SAS_RAID_SET_STATUS_DEGRADED 1 #define CSMI_SAS_RAID_SET_STATUS_REBUILDING 2 #define CSMI_SAS_RAID_SET_STATUS_FAILED 3 #define CSMI_SAS_RAID_SET_STATUS_OFFLINE 4 #define CSMI_SAS_RAID_SET_STATUS_TRANSFORMING 5 #define CSMI_SAS_RAID_SET_STATUS_QUEUED_FOR_REBUILD 6 #define CSMI_SAS_RAID_SET_STATUS_QUEUED_FOR_TRANSFORMATION 7 // RAID Drive Count // (bDriveCount, 0xF1 to 0xFF are reserved) #define CSMI_SAS_RAID_DRIVE_COUNT_TOO_BIG 0xF1 #define CSMI_SAS_RAID_DRIVE_COUNT_SUPRESSED 0xF2 // RAID Data Type // (bDataType) #define CSMI_SAS_RAID_DATA_DRIVES 0 #define CSMI_SAS_RAID_DATA_DEVICE_ID 1 #define CSMI_SAS_RAID_DATA_ADDITIONAL_DATA 2 // RAID Drive Status // (bDriveStatus) #define CSMI_SAS_DRIVE_STATUS_OK 0 #define CSMI_SAS_DRIVE_STATUS_REBUILDING 1 #define CSMI_SAS_DRIVE_STATUS_FAILED 2 #define CSMI_SAS_DRIVE_STATUS_DEGRADED 3 #define CSMI_SAS_DRIVE_STATUS_OFFLINE 4 #define CSMI_SAS_DRIVE_STATUS_QUEUED_FOR_REBUILD 5 // RAID Drive Usage // (bDriveUsage) #define CSMI_SAS_DRIVE_CONFIG_NOT_USED 0 #define CSMI_SAS_DRIVE_CONFIG_MEMBER 1 #define CSMI_SAS_DRIVE_CONFIG_SPARE 2 #define CSMI_SAS_DRIVE_CONFIG_SPARE_ACTIVE 3 // RAID Drive Type // (bDriveType) #define CSMI_SAS_DRIVE_TYPE_UNKNOWN 0 #define CSMI_SAS_DRIVE_TYPE_SINGLE_PORT_SAS 1 #define CSMI_SAS_DRIVE_TYPE_DUAL_PORT_SAS 2 #define CSMI_SAS_DRIVE_TYPE_SATA 3 #define CSMI_SAS_DRIVE_TYPE_SATA_PS 4 #define CSMI_SAS_DRIVE_TYPE_OTHER 255 // RAID Write Protect // (bWriteProtect) #define CSMI_SAS_RAID_SET_WRITE_PROTECT_UNKNOWN 0 #define CSMI_SAS_RAID_SET_WRITE_PROTECT_UNCHANGED 0 #define CSMI_SAS_RAID_SET_WRITE_PROTECT_ENABLED 1 #define CSMI_SAS_RAID_SET_WRITE_PROTECT_DISABLED 2 // RAID Cache Setting // (bCacheSetting) #define CSMI_SAS_RAID_SET_CACHE_UNKNOWN 0 #define CSMI_SAS_RAID_SET_CACHE_UNCHANGED 0 #define CSMI_SAS_RAID_SET_CACHE_ENABLED 1 #define CSMI_SAS_RAID_SET_CACHE_DISABLED 2 #define CSMI_SAS_RAID_SET_CACHE_CORRUPT 3 // RAID Features // (uFeatures) #define CSMI_SAS_RAID_FEATURE_TRANSFORMATION 0x00000001 #define CSMI_SAS_RAID_FEATURE_REBUILD 0x00000002 #define CSMI_SAS_RAID_FEATURE_SPLIT_MIRROR 0x00000004 #define CSMI_SAS_RAID_FEATURE_MERGE_MIRROR 0x00000008 #define CSMI_SAS_RAID_FEATURE_LUN_RENUMBER 0x00000010 #define CSMI_SAS_RAID_FEATURE_SURFACE_SCAN 0x00000020 #define CSMI_SAS_RAID_FEATURE_SPARES_SHARED 0x00000040 // RAID Priority // (bDefaultTransformPriority, etc.) #define CSMI_SAS_PRIORITY_UNKNOWN 0 #define CSMI_SAS_PRIORITY_UNCHANGED 0 #define CSMI_SAS_PRIORITY_AUTO 1 #define CSMI_SAS_PRIORITY_OFF 2 #define CSMI_SAS_PRIORITY_LOW 3 #define CSMI_SAS_PRIORITY_MEDIUM 4 #define CSMI_SAS_PRIORITY_HIGH 5 // RAID Transformation Rules // (uRaidSetTransformationRules) #define CSMI_SAS_RAID_RULE_AVAILABLE_MEMORY 0x00000001 #define CSMI_SAS_RAID_RULE_OVERLAPPED_EXTENTS 0x00000002 // RAID Cache Ratios Supported // (bCacheRatiosSupported) // from 0 to 100 defines the write to read ratio, 0 is 100% write #define CSMI_SAS_RAID_CACHE_RATIO_RANGE 101 #define CSMI_SAS_RAID_CACHE_RATIO_FIXED 102 #define CSMI_SAS_RAID_CACHE_RATIO_AUTO 103 #define CSMI_SAS_RAID_CACHE_RATIO_END 255 // RAID Cache Ratio Flag // (bCacheRatioFlag) #define CSMI_SAS_RAID_CACHE_RATIO_DISABLE 0 #define CSMI_SAS_RAID_CACHE_RATIO_ENABLE 1 // RAID Clear Configuration Signature // (bClearConfiguration) #define CSMI_SAS_RAID_CLEAR_CONFIGURATION_SIGNATURE "RAIDCLR" // RAID Failure Codes // (uFailureCode) #define CSMI_SAS_FAIL_CODE_OK 0 #define CSMI_SAS_FAIL_CODE_PARAMETER_INVALID 1000 #define CSMI_SAS_FAIL_CODE_TRANSFORM_PRIORITY_INVALID 1001 #define CSMI_SAS_FAIL_CODE_REBUILD_PRIORITY_INVALID 1002 #define CSMI_SAS_FAIL_CODE_CACHE_RATIO_INVALID 1003 #define CSMI_SAS_FAIL_CODE_SURFACE_SCAN_INVALID 1004 #define CSMI_SAS_FAIL_CODE_CLEAR_CONFIGURATION_INVALID 1005 #define CSMI_SAS_FAIL_CODE_ELEMENT_INDEX_INVALID 1006 #define CSMI_SAS_FAIL_CODE_SUBELEMENT_INDEX_INVALID 1007 #define CSMI_SAS_FAIL_CODE_EXTENT_INVALID 1008 #define CSMI_SAS_FAIL_CODE_BLOCK_COUNT_INVALID 1009 #define CSMI_SAS_FAIL_CODE_DRIVE_INDEX_INVALID 1010 #define CSMI_SAS_FAIL_CODE_EXISTING_LUN_INVALID 1011 #define CSMI_SAS_FAIL_CODE_RAID_TYPE_INVALID 1012 #define CSMI_SAS_FAIL_CODE_STRIPE_SIZE_INVALID 1013 #define CSMI_SAS_FAIL_CODE_TRANSFORMATION_INVALID 1014 #define CSMI_SAS_FAIL_CODE_CHANGE_COUNT_INVALID 1015 #define CSMI_SAS_FAIL_CODE_ENUMERATION_TYPE_INVALID 1016 #define CSMI_SAS_FAIL_CODE_EXCEEDED_RAID_SET_COUNT 2000 #define CSMI_SAS_FAIL_CODE_DUPLICATE_LUN 2001 #define CSMI_SAS_FAIL_CODE_WAIT_FOR_OPERATION 3000 // RAID Enumeration Types // (uEnumerationType) #define CSMI_SAS_RAID_ELEMENT_TYPE_DRIVE 0 #define CSMI_SAS_RAID_ELEMENT_TYPE_MODULE 1 #define CSMI_SAS_RAID_ELEMENT_TYPE_DRIVE_RAID_SET 2 #define CSMI_SAS_RAID_ELEMENT_TYPE_EXTENT_DRIVE 3 // RAID Extent Types // (bExtentType) #define CSMI_SAS_RAID_EXTENT_RESERVED 0 #define CSMI_SAS_RAID_EXTENT_METADATA 1 #define CSMI_SAS_RAID_EXTENT_ALLOCATED 2 #define CSMI_SAS_RAID_EXTENT_UNALLOCATED 3 // RAID Operation Types // (uOperationType) #define CSMI_SAS_RAID_SET_CREATE 0 #define CSMI_SAS_RAID_SET_LABEL 1 #define CSMI_SAS_RAID_SET_TRANSFORM 2 #define CSMI_SAS_RAID_SET_DELETE 3 #define CSMI_SAS_RAID_SET_WRITE_PROTECT 4 #define CSMI_SAS_RAID_SET_CACHE 5 #define CSMI_SAS_RAID_SET_ONLINE_STATE 6 #define CSMI_SAS_RAID_SET_SPARE 7 // RAID Transform Types // (bTransformType) #define CSMI_SAS_RAID_SET_TRANSFORM_SPLIT_MIRROR 0 #define CSMI_SAS_RAID_SET_TRANSFORM_MERGE_RAID_0 1 #define CSMI_SAS_RAID_SET_TRANSFORM_LUN_RENUMBER 2 #define CSMI_SAS_RAID_SET_TRANSFORM_RAID_SET 3 // RAID Online State // (bOnlineState) #define CSMI_SAS_RAID_SET_STATE_UNKNOWN 0 #define CSMI_SAS_RAID_SET_STATE_ONLINE 1 #define CSMI_SAS_RAID_SET_STATE_OFFLINE 2 /* * * * * * * * * * SAS HBA Class IOCTL Constants * * * * * * * * * */ // Return codes for SAS IOCTL's // (IoctlHeader.ReturnCode) #define CSMI_SAS_PHY_INFO_CHANGED CSMI_SAS_STATUS_SUCCESS #define CSMI_SAS_PHY_INFO_NOT_CHANGEABLE 2000 #define CSMI_SAS_LINK_RATE_OUT_OF_RANGE 2001 #define CSMI_SAS_PHY_DOES_NOT_EXIST 2002 #define CSMI_SAS_PHY_DOES_NOT_MATCH_PORT 2003 #define CSMI_SAS_PHY_CANNOT_BE_SELECTED 2004 #define CSMI_SAS_SELECT_PHY_OR_PORT 2005 #define CSMI_SAS_PORT_DOES_NOT_EXIST 2006 #define CSMI_SAS_PORT_CANNOT_BE_SELECTED 2007 #define CSMI_SAS_CONNECTION_FAILED 2008 #define CSMI_SAS_NO_SATA_DEVICE 2009 #define CSMI_SAS_NO_SATA_SIGNATURE 2010 #define CSMI_SAS_SCSI_EMULATION 2011 #define CSMI_SAS_NOT_AN_END_DEVICE 2012 #define CSMI_SAS_NO_SCSI_ADDRESS 2013 #define CSMI_SAS_NO_DEVICE_ADDRESS 2014 // Signature value // (IoctlHeader.Signature) #define CSMI_SAS_SIGNATURE "CSMISAS" // Timeout value default of 60 seconds // (IoctlHeader.Timeout) #define CSMI_SAS_TIMEOUT 60 // Device types // (bDeviceType) #define CSMI_SAS_PHY_UNUSED 0x00 #define CSMI_SAS_NO_DEVICE_ATTACHED 0x00 #define CSMI_SAS_END_DEVICE 0x10 #define CSMI_SAS_EDGE_EXPANDER_DEVICE 0x20 #define CSMI_SAS_FANOUT_EXPANDER_DEVICE 0x30 // Protocol options // (bInitiatorPortProtocol, bTargetPortProtocol) #define CSMI_SAS_PROTOCOL_SATA 0x01 #define CSMI_SAS_PROTOCOL_SMP 0x02 #define CSMI_SAS_PROTOCOL_STP 0x04 #define CSMI_SAS_PROTOCOL_SSP 0x08 // Negotiated and hardware link rates // (bNegotiatedLinkRate, bMinimumLinkRate, bMaximumLinkRate) #define CSMI_SAS_LINK_RATE_UNKNOWN 0x00 #define CSMI_SAS_PHY_DISABLED 0x01 #define CSMI_SAS_LINK_RATE_FAILED 0x02 #define CSMI_SAS_SATA_SPINUP_HOLD 0x03 #define CSMI_SAS_SATA_PORT_SELECTOR 0x04 #define CSMI_SAS_LINK_RATE_1_5_GBPS 0x08 #define CSMI_SAS_LINK_RATE_3_0_GBPS 0x09 #define CSMI_SAS_LINK_VIRTUAL 0x10 // Discover state // (bAutoDiscover) #define CSMI_SAS_DISCOVER_NOT_SUPPORTED 0x00 #define CSMI_SAS_DISCOVER_NOT_STARTED 0x01 #define CSMI_SAS_DISCOVER_IN_PROGRESS 0x02 #define CSMI_SAS_DISCOVER_COMPLETE 0x03 #define CSMI_SAS_DISCOVER_ERROR 0x04 // Phy features #define CSMI_SAS_PHY_VIRTUAL_SMP 0x01 // Programmed link rates // (bMinimumLinkRate, bMaximumLinkRate) // (bProgrammedMinimumLinkRate, bProgrammedMaximumLinkRate) #define CSMI_SAS_PROGRAMMED_LINK_RATE_UNCHANGED 0x00 #define CSMI_SAS_PROGRAMMED_LINK_RATE_1_5_GBPS 0x08 #define CSMI_SAS_PROGRAMMED_LINK_RATE_3_0_GBPS 0x09 // Link rate // (bNegotiatedLinkRate in CSMI_SAS_SET_PHY_INFO) #define CSMI_SAS_LINK_RATE_NEGOTIATE 0x00 #define CSMI_SAS_LINK_RATE_PHY_DISABLED 0x01 // Signal class // (bSignalClass in CSMI_SAS_SET_PHY_INFO) #define CSMI_SAS_SIGNAL_CLASS_UNKNOWN 0x00 #define CSMI_SAS_SIGNAL_CLASS_DIRECT 0x01 #define CSMI_SAS_SIGNAL_CLASS_SERVER 0x02 #define CSMI_SAS_SIGNAL_CLASS_ENCLOSURE 0x03 // Link error reset // (bResetCounts) #define CSMI_SAS_LINK_ERROR_DONT_RESET_COUNTS 0x00 #define CSMI_SAS_LINK_ERROR_RESET_COUNTS 0x01 // Phy identifier // (bPhyIdentifier) #define CSMI_SAS_USE_PORT_IDENTIFIER 0xFF // Port identifier // (bPortIdentifier) #define CSMI_SAS_IGNORE_PORT 0xFF // Programmed link rates // (bConnectionRate) #define CSMI_SAS_LINK_RATE_NEGOTIATED 0x00 #define CSMI_SAS_LINK_RATE_1_5_GBPS 0x08 #define CSMI_SAS_LINK_RATE_3_0_GBPS 0x09 // Connection status // (bConnectionStatus) #define CSMI_SAS_OPEN_ACCEPT 0 #define CSMI_SAS_OPEN_REJECT_BAD_DESTINATION 1 #define CSMI_SAS_OPEN_REJECT_RATE_NOT_SUPPORTED 2 #define CSMI_SAS_OPEN_REJECT_NO_DESTINATION 3 #define CSMI_SAS_OPEN_REJECT_PATHWAY_BLOCKED 4 #define CSMI_SAS_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED 5 #define CSMI_SAS_OPEN_REJECT_RESERVE_ABANDON 6 #define CSMI_SAS_OPEN_REJECT_RESERVE_CONTINUE 7 #define CSMI_SAS_OPEN_REJECT_RESERVE_INITIALIZE 8 #define CSMI_SAS_OPEN_REJECT_RESERVE_STOP 9 #define CSMI_SAS_OPEN_REJECT_RETRY 10 #define CSMI_SAS_OPEN_REJECT_STP_RESOURCES_BUSY 11 #define CSMI_SAS_OPEN_REJECT_WRONG_DESTINATION 12 // SSP Status // (bSSPStatus) #define CSMI_SAS_SSP_STATUS_UNKNOWN 0x00 #define CSMI_SAS_SSP_STATUS_WAITING 0x01 #define CSMI_SAS_SSP_STATUS_COMPLETED 0x02 #define CSMI_SAS_SSP_STATUS_FATAL_ERROR 0x03 #define CSMI_SAS_SSP_STATUS_RETRY 0x04 #define CSMI_SAS_SSP_STATUS_NO_TAG 0x05 // SSP Flags // (uFlags) #define CSMI_SAS_SSP_READ 0x00000001 #define CSMI_SAS_SSP_WRITE 0x00000002 #define CSMI_SAS_SSP_UNSPECIFIED 0x00000004 #define CSMI_SAS_SSP_TASK_ATTRIBUTE_SIMPLE 0x00000000 #define CSMI_SAS_SSP_TASK_ATTRIBUTE_HEAD_OF_QUEUE 0x00000010 #define CSMI_SAS_SSP_TASK_ATTRIBUTE_ORDERED 0x00000020 #define CSMI_SAS_SSP_TASK_ATTRIBUTE_ACA 0x00000040 // SSP Data present // (bDataPresent) #define CSMI_SAS_SSP_NO_DATA_PRESENT 0x00 #define CSMI_SAS_SSP_RESPONSE_DATA_PRESENT 0x01 #define CSMI_SAS_SSP_SENSE_DATA_PRESENT 0x02 // STP Flags // (uFlags) #define CSMI_SAS_STP_READ 0x00000001 #define CSMI_SAS_STP_WRITE 0x00000002 #define CSMI_SAS_STP_UNSPECIFIED 0x00000004 #define CSMI_SAS_STP_PIO 0x00000010 #define CSMI_SAS_STP_DMA 0x00000020 #define CSMI_SAS_STP_PACKET 0x00000040 #define CSMI_SAS_STP_DMA_QUEUED 0x00000080 #define CSMI_SAS_STP_EXECUTE_DIAG 0x00000100 #define CSMI_SAS_STP_RESET_DEVICE 0x00000200 // Task Management Flags // (uFlags) #define CSMI_SAS_TASK_IU 0x00000001 #define CSMI_SAS_HARD_RESET_SEQUENCE 0x00000002 #define CSMI_SAS_SUPPRESS_RESULT 0x00000004 // Task Management Functions // (bTaskManagement) #define CSMI_SAS_SSP_ABORT_TASK 0x01 #define CSMI_SAS_SSP_ABORT_TASK_SET 0x02 #define CSMI_SAS_SSP_CLEAR_TASK_SET 0x04 #define CSMI_SAS_SSP_LOGICAL_UNIT_RESET 0x08 #define CSMI_SAS_SSP_CLEAR_ACA 0x40 #define CSMI_SAS_SSP_QUERY_TASK 0x80 // Task Management Information // (uInformation) #define CSMI_SAS_SSP_TEST 1 #define CSMI_SAS_SSP_EXCEEDED 2 #define CSMI_SAS_SSP_DEMAND 3 #define CSMI_SAS_SSP_TRIGGER 4 // Connector Pinout Information // (uPinout) #define CSMI_SAS_CON_UNKNOWN 0x00000001 #define CSMI_SAS_CON_SFF_8482 0x00000002 #define CSMI_SAS_CON_SFF_8470_LANE_1 0x00000100 #define CSMI_SAS_CON_SFF_8470_LANE_2 0x00000200 #define CSMI_SAS_CON_SFF_8470_LANE_3 0x00000400 #define CSMI_SAS_CON_SFF_8470_LANE_4 0x00000800 #define CSMI_SAS_CON_SFF_8484_LANE_1 0x00010000 #define CSMI_SAS_CON_SFF_8484_LANE_2 0x00020000 #define CSMI_SAS_CON_SFF_8484_LANE_3 0x00040000 #define CSMI_SAS_CON_SFF_8484_LANE_4 0x00080000 // Connector Location Information // (bLocation) // same as uPinout above... // #define CSMI_SAS_CON_UNKNOWN 0x01 #define CSMI_SAS_CON_INTERNAL 0x02 #define CSMI_SAS_CON_EXTERNAL 0x04 #define CSMI_SAS_CON_SWITCHABLE 0x08 #define CSMI_SAS_CON_AUTO 0x10 #define CSMI_SAS_CON_NOT_PRESENT 0x20 #define CSMI_SAS_CON_NOT_CONNECTED 0x80 // Device location identification // (bIdentify) #define CSMI_SAS_LOCATE_UNKNOWN 0x00 #define CSMI_SAS_LOCATE_FORCE_OFF 0x01 #define CSMI_SAS_LOCATE_FORCE_ON 0x02 // Location Valid flags // (uLocationFlags) #define CSMI_SAS_LOCATE_SAS_ADDRESS_VALID 0x00000001 #define CSMI_SAS_LOCATE_SAS_LUN_VALID 0x00000002 #define CSMI_SAS_LOCATE_ENCLOSURE_IDENTIFIER_VALID 0x00000004 #define CSMI_SAS_LOCATE_ENCLOSURE_NAME_VALID 0x00000008 #define CSMI_SAS_LOCATE_BAY_PREFIX_VALID 0x00000010 #define CSMI_SAS_LOCATE_BAY_IDENTIFIER_VALID 0x00000020 #define CSMI_SAS_LOCATE_LOCATION_STATE_VALID 0x00000040 /* * * * * * * * SAS Phy Control Class IOCTL Constants * * * * * * * * */ // Return codes for SAS Phy Control IOCTL's // (IoctlHeader.ReturnCode) // Signature value // (IoctlHeader.Signature) #define CSMI_PHY_SIGNATURE "CSMIPHY" // Phy Control Functions // (bFunction) // values 0x00 to 0xFF are consistent in definition with the SMP PHY CONTROL // function defined in the SAS spec #define CSMI_SAS_PC_NOP 0x00000000 #define CSMI_SAS_PC_LINK_RESET 0x00000001 #define CSMI_SAS_PC_HARD_RESET 0x00000002 #define CSMI_SAS_PC_PHY_DISABLE 0x00000003 // 0x04 to 0xFF reserved... #define CSMI_SAS_PC_GET_PHY_SETTINGS 0x00000100 // Link Flags #define CSMI_SAS_PHY_ACTIVATE_CONTROL 0x00000001 #define CSMI_SAS_PHY_UPDATE_SPINUP_RATE 0x00000002 #define CSMI_SAS_PHY_AUTO_COMWAKE 0x00000004 // Device Types for Phy Settings // (bType) #define CSMI_SAS_UNDEFINED 0x00 #define CSMI_SAS_SATA 0x01 #define CSMI_SAS_SAS 0x02 // Transmitter Flags // (uTransmitterFlags) #define CSMI_SAS_PHY_PREEMPHASIS_DISABLED 0x00000001 // Receiver Flags // (uReceiverFlags) #define CSMI_SAS_PHY_EQUALIZATION_DISABLED 0x00000001 // Pattern Flags // (uPatternFlags) // #define CSMI_SAS_PHY_ACTIVATE_CONTROL 0x00000001 #define CSMI_SAS_PHY_DISABLE_SCRAMBLING 0x00000002 #define CSMI_SAS_PHY_DISABLE_ALIGN 0x00000004 #define CSMI_SAS_PHY_DISABLE_SSC 0x00000008 #define CSMI_SAS_PHY_FIXED_PATTERN 0x00000010 #define CSMI_SAS_PHY_USER_PATTERN 0x00000020 // Fixed Patterns // (bFixedPattern) #define CSMI_SAS_PHY_CJPAT 0x00000001 #define CSMI_SAS_PHY_ALIGN 0x00000002 // Type Flags // (bTypeFlags) #define CSMI_SAS_PHY_POSITIVE_DISPARITY 0x01 #define CSMI_SAS_PHY_NEGATIVE_DISPARITY 0x02 #define CSMI_SAS_PHY_CONTROL_CHARACTER 0x04 // Miscellaneous #define SLOT_NUMBER_UNKNOWN 0xFFFF /*************************************************************************/ /* DATA STRUCTURES */ /*************************************************************************/ /* * * * * * * * * * Class Independent Structures * * * * * * * * * */ // EDM #pragma CSMI_SAS_BEGIN_PACK(8) #pragma pack(8) // CC_CSMI_SAS_DRIVER_INFO typedef struct _CSMI_SAS_DRIVER_INFO { __u8 szName[81]; __u8 szDescription[81]; __u16 usMajorRevision; __u16 usMinorRevision; __u16 usBuildRevision; __u16 usReleaseRevision; __u16 usCSMIMajorRevision; __u16 usCSMIMinorRevision; } CSMI_SAS_DRIVER_INFO, *PCSMI_SAS_DRIVER_INFO; typedef struct _CSMI_SAS_DRIVER_INFO_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_DRIVER_INFO Information; } CSMI_SAS_DRIVER_INFO_BUFFER, *PCSMI_SAS_DRIVER_INFO_BUFFER; // CC_CSMI_SAS_CNTLR_CONFIGURATION typedef struct _CSMI_SAS_PCI_BUS_ADDRESS { __u8 bBusNumber; __u8 bDeviceNumber; __u8 bFunctionNumber; __u8 bReserved; } CSMI_SAS_PCI_BUS_ADDRESS, *PCSMI_SAS_PCI_BUS_ADDRESS; typedef union _CSMI_SAS_IO_BUS_ADDRESS { CSMI_SAS_PCI_BUS_ADDRESS PciAddress; __u8 bReserved[32]; } CSMI_SAS_IO_BUS_ADDRESS, *PCSMI_SAS_IO_BUS_ADDRESS; typedef struct _CSMI_SAS_CNTLR_CONFIG { __u32 uBaseIoAddress; struct { __u32 uLowPart; __u32 uHighPart; } BaseMemoryAddress; __u32 uBoardID; __u16 usSlotNumber; __u8 bControllerClass; __u8 bIoBusType; CSMI_SAS_IO_BUS_ADDRESS BusAddress; __u8 szSerialNumber[81]; __u16 usMajorRevision; __u16 usMinorRevision; __u16 usBuildRevision; __u16 usReleaseRevision; __u16 usBIOSMajorRevision; __u16 usBIOSMinorRevision; __u16 usBIOSBuildRevision; __u16 usBIOSReleaseRevision; __u32 uControllerFlags; __u16 usRromMajorRevision; __u16 usRromMinorRevision; __u16 usRromBuildRevision; __u16 usRromReleaseRevision; __u16 usRromBIOSMajorRevision; __u16 usRromBIOSMinorRevision; __u16 usRromBIOSBuildRevision; __u16 usRromBIOSReleaseRevision; __u8 bReserved[7]; } CSMI_SAS_CNTLR_CONFIG, *PCSMI_SAS_CNTLR_CONFIG; typedef struct _CSMI_SAS_CNTLR_CONFIG_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_CNTLR_CONFIG Configuration; } CSMI_SAS_CNTLR_CONFIG_BUFFER, *PCSMI_SAS_CNTLR_CONFIG_BUFFER; // CC_CSMI_SAS_CNTLR_STATUS typedef struct _CSMI_SAS_CNTLR_STATUS { __u32 uStatus; __u32 uOfflineReason; __u8 bReserved[28]; } CSMI_SAS_CNTLR_STATUS, *PCSMI_SAS_CNTLR_STATUS; typedef struct _CSMI_SAS_CNTLR_STATUS_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_CNTLR_STATUS Status; } CSMI_SAS_CNTLR_STATUS_BUFFER, *PCSMI_SAS_CNTLR_STATUS_BUFFER; // CC_CSMI_SAS_FIRMWARE_DOWNLOAD typedef struct _CSMI_SAS_FIRMWARE_DOWNLOAD { __u32 uBufferLength; __u32 uDownloadFlags; __u8 bReserved[32]; __u16 usStatus; __u16 usSeverity; } CSMI_SAS_FIRMWARE_DOWNLOAD, *PCSMI_SAS_FIRMWARE_DOWNLOAD; typedef struct _CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_FIRMWARE_DOWNLOAD Information; __u8 bDataBuffer[1]; } CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER, *PCSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER; // CC_CSMI_SAS_RAID_INFO typedef struct _CSMI_SAS_RAID_INFO { __u32 uNumRaidSets; __u32 uMaxDrivesPerSet; __u32 uMaxRaidSets; __u8 bMaxRaidTypes; __u8 bReservedByteFields[7]; struct { __u32 uLowPart; __u32 uHighPart; } ulMinRaidSetBlocks; struct { __u32 uLowPart; __u32 uHighPart; } ulMaxRaidSetBlocks; __u32 uMaxPhysicalDrives; __u32 uMaxExtents; __u32 uMaxModules; __u32 uMaxTransformationMemory; __u32 uChangeCount; __u8 bReserved[44]; } CSMI_SAS_RAID_INFO, *PCSMI_SAS_RAID_INFO; typedef struct _CSMI_SAS_RAID_INFO_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_INFO Information; } CSMI_SAS_RAID_INFO_BUFFER, *PCSMI_SAS_RAID_INFO_BUFFER; // CC_CSMI_SAS_GET_RAID_CONFIG typedef struct _CSMI_SAS_RAID_DRIVES { __u8 bModel[40]; __u8 bFirmware[8]; __u8 bSerialNumber[40]; __u8 bSASAddress[8]; __u8 bSASLun[8]; __u8 bDriveStatus; __u8 bDriveUsage; __u16 usBlockSize; __u8 bDriveType; __u8 bReserved[15]; __u32 uDriveIndex; struct { __u32 uLowPart; __u32 uHighPart; } ulTotalUserBlocks; } CSMI_SAS_RAID_DRIVES, *PCSMI_SAS_RAID_DRIVES; typedef struct _CSMI_SAS_RAID_DEVICE_ID { __u8 bDeviceIdentificationVPDPage[1]; } CSMI_SAS_RAID_DEVICE_ID, *PCSMI_SAS_RAID_DEVICE_ID; typedef struct _CSMI_SAS_RAID_SET_ADDITIONAL_DATA { __u8 bLabel[16]; __u8 bRaidSetLun[8]; __u8 bWriteProtection; __u8 bCacheSetting; __u8 bCacheRatio; __u16 usBlockSize; __u8 bReservedBytes[11]; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetExtentOffset; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetBlocks; __u32 uStripeSizeInBlocks; __u32 uSectorsPerTrack; __u8 bApplicationScratchPad[16]; __u32 uNumberOfHeads; __u32 uNumberOfTracks; __u8 bReserved[24]; } CSMI_SAS_RAID_SET_ADDITIONAL_DATA, *PCSMI_SAS_RAID_SET_ADDITIONAL_DATA; typedef struct _CSMI_SAS_RAID_CONFIG { __u32 uRaidSetIndex; __u32 uCapacity; __u32 uStripeSize; __u8 bRaidType; __u8 bStatus; __u8 bInformation; __u8 bDriveCount; __u8 bDataType; __u8 bReserved[11]; __u32 uFailureCode; __u32 uChangeCount; union { CSMI_SAS_RAID_DRIVES Drives[1]; CSMI_SAS_RAID_DEVICE_ID DeviceId[1]; CSMI_SAS_RAID_SET_ADDITIONAL_DATA Data[1]; }; } CSMI_SAS_RAID_CONFIG, *PCSMI_SAS_RAID_CONFIG; typedef struct _CSMI_SAS_RAID_CONFIG_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_CONFIG Configuration; } CSMI_SAS_RAID_CONFIG_BUFFER, *PCSMI_SAS_RAID_CONFIG_BUFFER; // CC_CSMI_SAS_GET_RAID_FEATURES typedef struct _CSMI_SAS_RAID_TYPE_DESCRIPTION { __u8 bRaidType; __u8 bReservedBytes[7]; __u32 uSupportedStripeSizeMap; __u8 bReserved[24]; } CSMI_SAS_RAID_TYPE_DESCRIPTION, *PCSMI_SAS_RAID_TYPE_DESCRIPTION; typedef struct _CSMI_SAS_RAID_FEATURES { __u32 uFeatures; __u8 bReservedFeatures[32]; __u8 bDefaultTransformPriority; __u8 bTransformPriority; __u8 bDefaultRebuildPriority; __u8 bRebuildPriority; __u8 bDefaultSurfaceScanPriority; __u8 bSurfaceScanPriority; __u16 usReserved; __u32 uRaidSetTransformationRules; __u32 uReserved[11]; CSMI_SAS_RAID_TYPE_DESCRIPTION RaidType[24]; __u8 bCacheRatiosSupported[104]; __u32 uChangeCount; __u32 uFailureCode; __u8 bReserved[120]; } CSMI_SAS_RAID_FEATURES, *PCSMI_SAS_RAID_FEATURES; typedef struct _CSMI_SAS_RAID_FEATURES_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_FEATURES Information; } CSMI_SAS_RAID_FEATURES_BUFFER, *PCSMI_SAS_RAID_FEATURES_BUFFER; // CC_CSMI_SAS_SET_RAID_CONTROL typedef struct _CSMI_SAS_RAID_CONTROL { __u8 bTransformPriority; __u8 bRebuildPriority; __u8 bCacheRatioFlag; __u8 bCacheRatio; __u8 bSurfaceScanPriority; __u8 bReservedBytes[15]; __u8 bClearConfiguration[8]; __u32 uChangeCount; __u8 bReserved[88]; __u32 uFailureCode; __u8 bFailureDescription[80]; } CSMI_SAS_RAID_CONTROL, *PCSMI_SAS_RAID_CONTROL; typedef struct _CSMI_SAS_RAID_CONTROL_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_CONTROL Information; } CSMI_SAS_RAID_CONTROL_BUFFER, *PCSMI_SAS_RAID_CONTROL_BUFFER; // CC_CSMI_SAS_GET_RAID_ELEMENT typedef struct _CSMI_SAS_DRIVE_EXTENT_INFO { __u32 uDriveIndex; __u8 bExtentType; __u8 bReservedBytes[7]; struct { __u32 uLowPart; __u32 uHighPart; } ulExtentOffset; struct { __u32 uLowPart; __u32 uHighPart; } ulExtentBlocks; __u32 uRaidSetIndex; __u8 bReserved[96]; } CSMI_SAS_DRIVE_EXTENT_INFO, *PCSMI_SAS_DRIVE_EXTENT_INFO; typedef struct _CSMI_SAS_RAID_MODULE_INFO { __u8 bReserved[128]; } CSMI_SAS_RAID_MODULE_INFO, *PCSMI_SAS_RAID_MODULE_INFO; typedef struct _CSMI_SAS_DRIVE_LOCATION { __u8 bConnector[16]; __u8 bBoxName[16]; __u32 uBay; __u8 bReservedBytes[4]; __u8 bAttachedSASAddress[8]; __u8 bAttachedPhyIdentifier; __u8 bReserved[79]; } CSMI_SAS_DRIVE_LOCATION, *PCSMI_SAS_DRIVE_LOCATION; typedef struct _CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA { __u8 bNegotiatedLinkRate[2]; __u8 bReserved[126]; } CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA, *PCSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA; typedef struct _CSMI_SAS_DRIVE_INFO { CSMI_SAS_RAID_DRIVES Device; CSMI_SAS_RAID_DRIVES_ADDITIONAL_DATA Data; CSMI_SAS_DRIVE_LOCATION Location; __u8 bReserved[16]; } CSMI_SAS_DRIVE_INFO, *PCSMI_SAS_DRIVE_INFO; typedef struct _CSMI_SAS_RAID_ELEMENT { __u32 uEnumerationType; __u32 uElementIndex; __u32 uNumElements; __u32 uChangeCount; __u32 uSubElementIndex; __u8 bReserved[32]; __u32 uFailureCode; __u8 bFailureDescription[80]; union { CSMI_SAS_DRIVE_INFO Drive; CSMI_SAS_RAID_MODULE_INFO Module; CSMI_SAS_DRIVE_EXTENT_INFO Extent; } Element; } CSMI_SAS_RAID_ELEMENT, *PCSMI_SAS_RAID_ELEMENT; typedef struct _CSMI_SAS_RAID_ELEMENT_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_ELEMENT Information; } CSMI_SAS_RAID_ELEMENT_BUFFER, *PCSMI_SAS_RAID_ELEMENT_BUFFER; // CC_CSMI_SAS_SET_RAID_OPERATION typedef struct _CSMI_SAS_RAID_SET_LIST { __u32 uRaidSetIndex; __u8 bExistingLun[8]; __u8 bNewLun[8]; __u8 bReserved[12]; } CSMI_SAS_RAID_SET_LIST, *PCSMI_SAS_RAID_SET_LIST; typedef struct _CSMI_SAS_RAID_SET_DRIVE_LIST { __u32 uDriveIndex; __u8 bDriveUsage; __u8 bReserved[27]; } CSMI_SAS_RAID_SET_DRIVE_LIST, *PCSMI_SAS_RAID_SET_DRIVE_LIST; typedef struct _CSMI_SAS_RAID_SET_SPARE_INFO { __u32 uRaidSetIndex; __u32 uDriveCount; __u8 bApplicationScratchPad[16]; __u8 bReserved[104]; } CSMI_SAS_RAID_SET_SPARE_INFO, *PCSMI_SAS_RAID_SET_SPARE_INFO; typedef struct _CSMI_SAS_RAID_SET_ONLINE_STATE_INFO { __u32 uRaidSetIndex; __u8 bOnlineState; __u8 bReserved[123]; } CSMI_SAS_RAID_SET_ONLINE_STATE_INFO, *PCSMI_SAS_RAID_SET_ONLINE_STATE_INFO; typedef struct _CSMI_SAS_RAID_SET_CACHE_INFO { __u32 uRaidSetIndex; __u8 bCacheSetting; __u8 bCacheRatioFlag; __u8 bCacheRatio; __u8 bReserved[121]; } CSMI_SAS_RAID_SET_CACHE_INFO, *PCSMI_SAS_RAID_SET_CACHE_INFO; typedef struct _CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO { __u32 uRaidSetIndex; __u8 bWriteProtectSetting; __u8 bReserved[123]; } CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO, *PCSMI_SAS_RAID_SET_WRITE_PROTECT_INFO; typedef struct _CSMI_SAS_RAID_SET_DELETE_INFO { __u32 uRaidSetIndex; __u8 bReserved[124]; } CSMI_SAS_RAID_SET_DELETE_INFO, *PCSMI_SAS_RAID_SET_DELETE_INFO; typedef struct _CSMI_SAS_RAID_SET_MODIFY_INFO { __u8 bRaidType; __u8 bReservedBytes[7]; __u32 uStripeSize; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetBlocks; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetExtentOffset; __u32 uDriveCount; __u8 bReserved[96]; } CSMI_SAS_RAID_SET_MODIFY_INFO, *PCSMI_SAS_RAID_SET_MODIFY_INFO; typedef struct _CSMI_SAS_RAID_SET_TRANSFORM_INFO { __u8 bTransformType; __u8 bReservedBytes[3]; __u32 uRaidSetIndex; __u8 bRaidType; __u8 bReservedBytes2[11]; __u32 uAdditionalRaidSetIndex; __u32 uRaidSetCount; __u8 bApplicationScratchPad[16]; CSMI_SAS_RAID_SET_MODIFY_INFO Modify; __u8 bReserved[80]; } CSMI_SAS_RAID_SET_TRANSFORM_INFO, *PCSMI_SAS_RAID_SET_TRANSFORM_INFO; typedef struct _CSMI_SAS_RAID_SET_LABEL_INFO { __u32 uRaidSetIndex; __u8 bLabel[16]; __u8 bReserved[108]; } CSMI_SAS_RAID_SET_LABEL_INFO, *PCSMI_SAS_RAID_SET_LABEL_INFO; typedef struct _CSMI_SAS_RAID_SET_CREATE_INFO { __u8 bRaidType; __u8 bReservedBytes[7]; __u32 uStripeSize; __u32 uTrackSectorCount; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetBlocks; struct { __u32 uLowPart; __u32 uHighPart; } ulRaidSetExtentOffset; __u32 uDriveCount; __u8 bLabel[16]; __u32 uRaidSetIndex; __u8 bApplicationScratchPad[16]; __u32 uNumberOfHeads; __u32 uNumberOfTracks; __u8 bReserved[48]; } CSMI_SAS_RAID_SET_CREATE_INFO, *PCSMI_SAS_RAID_SET_CREATE_INFO; typedef struct _CSMI_SAS_RAID_SET_OPERATION { __u32 uOperationType; __u32 uChangeCount; __u32 uFailureCode; __u8 bFailureDescription[80]; __u8 bReserved[28]; union { CSMI_SAS_RAID_SET_CREATE_INFO Create; CSMI_SAS_RAID_SET_LABEL_INFO Label; CSMI_SAS_RAID_SET_TRANSFORM_INFO Transform; CSMI_SAS_RAID_SET_DELETE_INFO Delete; CSMI_SAS_RAID_SET_WRITE_PROTECT_INFO Protect; CSMI_SAS_RAID_SET_CACHE_INFO Cache; CSMI_SAS_RAID_SET_ONLINE_STATE_INFO State; CSMI_SAS_RAID_SET_SPARE_INFO Spare; } Operation; union { CSMI_SAS_RAID_SET_DRIVE_LIST DriveList[1]; CSMI_SAS_RAID_SET_LIST RaidSetList[1]; } Parameters; } CSMI_SAS_RAID_SET_OPERATION, *PCSMI_SAS_RAID_SET_OPERATION; typedef struct _CSMI_SAS_RAID_SET_OPERATION_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_RAID_SET_OPERATION Information; } CSMI_SAS_RAID_SET_OPERATION_BUFFER, *PCSMI_SAS_RAID_SET_OPERATION_BUFFER; /* * * * * * * * * * SAS HBA Class Structures * * * * * * * * * */ // CC_CSMI_SAS_GET_PHY_INFO typedef struct _CSMI_SAS_IDENTIFY { __u8 bDeviceType; __u8 bRestricted; __u8 bInitiatorPortProtocol; __u8 bTargetPortProtocol; __u8 bRestricted2[8]; __u8 bSASAddress[8]; __u8 bPhyIdentifier; __u8 bSignalClass; __u8 bReserved[6]; } CSMI_SAS_IDENTIFY, *PCSMI_SAS_IDENTIFY; typedef struct _CSMI_SAS_PHY_ENTITY { CSMI_SAS_IDENTIFY Identify; __u8 bPortIdentifier; __u8 bNegotiatedLinkRate; __u8 bMinimumLinkRate; __u8 bMaximumLinkRate; __u8 bPhyChangeCount; __u8 bAutoDiscover; __u8 bPhyFeatures; __u8 bReserved; CSMI_SAS_IDENTIFY Attached; } CSMI_SAS_PHY_ENTITY, *PCSMI_SAS_PHY_ENTITY; typedef struct _CSMI_SAS_PHY_INFO { __u8 bNumberOfPhys; __u8 bReserved[3]; CSMI_SAS_PHY_ENTITY Phy[32]; } CSMI_SAS_PHY_INFO, *PCSMI_SAS_PHY_INFO; typedef struct _CSMI_SAS_PHY_INFO_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_PHY_INFO Information; } CSMI_SAS_PHY_INFO_BUFFER, *PCSMI_SAS_PHY_INFO_BUFFER; // CC_CSMI_SAS_SET_PHY_INFO typedef struct _CSMI_SAS_SET_PHY_INFO { __u8 bPhyIdentifier; __u8 bNegotiatedLinkRate; __u8 bProgrammedMinimumLinkRate; __u8 bProgrammedMaximumLinkRate; __u8 bSignalClass; __u8 bReserved[3]; } CSMI_SAS_SET_PHY_INFO, *PCSMI_SAS_SET_PHY_INFO; typedef struct _CSMI_SAS_SET_PHY_INFO_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_SET_PHY_INFO Information; } CSMI_SAS_SET_PHY_INFO_BUFFER, *PCSMI_SAS_SET_PHY_INFO_BUFFER; // CC_CSMI_SAS_GET_LINK_ERRORS typedef struct _CSMI_SAS_LINK_ERRORS { __u8 bPhyIdentifier; __u8 bResetCounts; __u8 bReserved[2]; __u32 uInvalidDwordCount; __u32 uRunningDisparityErrorCount; __u32 uLossOfDwordSyncCount; __u32 uPhyResetProblemCount; } CSMI_SAS_LINK_ERRORS, *PCSMI_SAS_LINK_ERRORS; typedef struct _CSMI_SAS_LINK_ERRORS_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_LINK_ERRORS Information; } CSMI_SAS_LINK_ERRORS_BUFFER, *PCSMI_SAS_LINK_ERRORS_BUFFER; // CC_CSMI_SAS_SMP_PASSTHRU typedef struct _CSMI_SAS_SMP_REQUEST { __u8 bFrameType; __u8 bFunction; __u8 bReserved[2]; __u8 bAdditionalRequestBytes[1016]; } CSMI_SAS_SMP_REQUEST, *PCSMI_SAS_SMP_REQUEST; typedef struct _CSMI_SAS_SMP_RESPONSE { __u8 bFrameType; __u8 bFunction; __u8 bFunctionResult; __u8 bReserved; __u8 bAdditionalResponseBytes[1016]; } CSMI_SAS_SMP_RESPONSE, *PCSMI_SAS_SMP_RESPONSE; typedef struct _CSMI_SAS_SMP_PASSTHRU { __u8 bPhyIdentifier; __u8 bPortIdentifier; __u8 bConnectionRate; __u8 bReserved; __u8 bDestinationSASAddress[8]; __u32 uRequestLength; CSMI_SAS_SMP_REQUEST Request; __u8 bConnectionStatus; __u8 bReserved2[3]; __u32 uResponseBytes; CSMI_SAS_SMP_RESPONSE Response; } CSMI_SAS_SMP_PASSTHRU, *PCSMI_SAS_SMP_PASSTHRU; typedef struct _CSMI_SAS_SMP_PASSTHRU_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_SMP_PASSTHRU Parameters; } CSMI_SAS_SMP_PASSTHRU_BUFFER, *PCSMI_SAS_SMP_PASSTHRU_BUFFER; // CC_CSMI_SAS_SSP_PASSTHRU typedef struct _CSMI_SAS_SSP_PASSTHRU { __u8 bPhyIdentifier; __u8 bPortIdentifier; __u8 bConnectionRate; __u8 bReserved; __u8 bDestinationSASAddress[8]; __u8 bLun[8]; __u8 bCDBLength; __u8 bAdditionalCDBLength; __u8 bReserved2[2]; __u8 bCDB[16]; __u32 uFlags; __u8 bAdditionalCDB[24]; __u32 uDataLength; } CSMI_SAS_SSP_PASSTHRU, *PCSMI_SAS_SSP_PASSTHRU; typedef struct _CSMI_SAS_SSP_PASSTHRU_STATUS { __u8 bConnectionStatus; __u8 bSSPStatus; __u8 bReserved[2]; __u8 bDataPresent; __u8 bStatus; __u8 bResponseLength[2]; __u8 bResponse[256]; __u32 uDataBytes; } CSMI_SAS_SSP_PASSTHRU_STATUS, *PCSMI_SAS_SSP_PASSTHRU_STATUS; typedef struct _CSMI_SAS_SSP_PASSTHRU_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_SSP_PASSTHRU Parameters; CSMI_SAS_SSP_PASSTHRU_STATUS Status; __u8 bDataBuffer[1]; } CSMI_SAS_SSP_PASSTHRU_BUFFER, *PCSMI_SAS_SSP_PASSTHRU_BUFFER; // CC_CSMI_SAS_STP_PASSTHRU typedef struct _CSMI_SAS_STP_PASSTHRU { __u8 bPhyIdentifier; __u8 bPortIdentifier; __u8 bConnectionRate; __u8 bReserved; __u8 bDestinationSASAddress[8]; __u8 bReserved2[4]; __u8 bCommandFIS[20]; __u32 uFlags; __u32 uDataLength; } CSMI_SAS_STP_PASSTHRU, *PCSMI_SAS_STP_PASSTHRU; typedef struct _CSMI_SAS_STP_PASSTHRU_STATUS { __u8 bConnectionStatus; __u8 bReserved[3]; __u8 bStatusFIS[20]; __u32 uSCR[16]; __u32 uDataBytes; } CSMI_SAS_STP_PASSTHRU_STATUS, *PCSMI_SAS_STP_PASSTHRU_STATUS; typedef struct _CSMI_SAS_STP_PASSTHRU_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_STP_PASSTHRU Parameters; CSMI_SAS_STP_PASSTHRU_STATUS Status; __u8 bDataBuffer[1]; } CSMI_SAS_STP_PASSTHRU_BUFFER, *PCSMI_SAS_STP_PASSTHRU_BUFFER; // CC_CSMI_SAS_GET_SATA_SIGNATURE typedef struct _CSMI_SAS_SATA_SIGNATURE { __u8 bPhyIdentifier; __u8 bReserved[3]; __u8 bSignatureFIS[20]; } CSMI_SAS_SATA_SIGNATURE, *PCSMI_SAS_SATA_SIGNATURE; typedef struct _CSMI_SAS_SATA_SIGNATURE_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_SATA_SIGNATURE Signature; } CSMI_SAS_SATA_SIGNATURE_BUFFER, *PCSMI_SAS_SATA_SIGNATURE_BUFFER; // CC_CSMI_SAS_GET_SCSI_ADDRESS typedef struct _CSMI_SAS_GET_SCSI_ADDRESS_BUFFER { IOCTL_HEADER IoctlHeader; __u8 bSASAddress[8]; __u8 bSASLun[8]; __u8 bHostIndex; __u8 bPathId; __u8 bTargetId; __u8 bLun; } CSMI_SAS_GET_SCSI_ADDRESS_BUFFER, *PCSMI_SAS_GET_SCSI_ADDRESS_BUFFER; // CC_CSMI_SAS_GET_DEVICE_ADDRESS typedef struct _CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER { IOCTL_HEADER IoctlHeader; __u8 bHostIndex; __u8 bPathId; __u8 bTargetId; __u8 bLun; __u8 bSASAddress[8]; __u8 bSASLun[8]; } CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER, *PCSMI_SAS_GET_DEVICE_ADDRESS_BUFFER; // CC_CSMI_SAS_TASK_MANAGEMENT typedef struct _CSMI_SAS_SSP_TASK_IU { __u8 bHostIndex; __u8 bPathId; __u8 bTargetId; __u8 bLun; __u32 uFlags; __u32 uQueueTag; __u32 uReserved; __u8 bTaskManagementFunction; __u8 bReserved[7]; __u32 uInformation; } CSMI_SAS_SSP_TASK_IU, *PCSMI_SAS_SSP_TASK_IU; typedef struct _CSMI_SAS_SSP_TASK_IU_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_SSP_TASK_IU Parameters; CSMI_SAS_SSP_PASSTHRU_STATUS Status; } CSMI_SAS_SSP_TASK_IU_BUFFER, *PCSMI_SAS_SSP_TASK_IU_BUFFER; // CC_CSMI_SAS_GET_CONNECTOR_INFO typedef struct _CSMI_SAS_GET_CONNECTOR_INFO { __u32 uPinout; __u8 bConnector[16]; __u8 bLocation; __u8 bReserved[15]; } CSMI_SAS_CONNECTOR_INFO, *PCSMI_SAS_CONNECTOR_INFO; typedef struct _CSMI_SAS_CONNECTOR_INFO_BUFFER { IOCTL_HEADER IoctlHeader; CSMI_SAS_CONNECTOR_INFO Reference[32]; } CSMI_SAS_CONNECTOR_INFO_BUFFER, *PCSMI_SAS_CONNECTOR_INFO_BUFFER; // CC_CSMI_SAS_GET_LOCATION typedef struct _CSMI_SAS_LOCATION_IDENTIFIER { __u32 bLocationFlags; __u8 bSASAddress[8]; __u8 bSASLun[8]; __u8 bEnclosureIdentifier[8]; __u8 bEnclosureName[32]; __u8 bBayPrefix[32]; __u8 bBayIdentifier; __u8 bLocationState; __u8 bReserved[2]; } CSMI_SAS_LOCATION_IDENTIFIER, *PCSMI_SAS_LOCATION_IDENTIFIER; typedef struct _CSMI_SAS_GET_LOCATION_BUFFER { IOCTL_HEADER IoctlHeader; __u8 bHostIndex; __u8 bPathId; __u8 bTargetId; __u8 bLun; __u8 bIdentify; __u8 bNumberOfLocationIdentifiers; __u8 bLengthOfLocationIdentifier; CSMI_SAS_LOCATION_IDENTIFIER Location[1]; } CSMI_SAS_GET_LOCATION_BUFFER, *PCSMI_SAS_GET_LOCATION_BUFFER; // CC_CSMI_SAS_PHY_CONTROL typedef struct _CSMI_SAS_CHARACTER { __u8 bTypeFlags; __u8 bValue; } CSMI_SAS_CHARACTER, *PCSMI_SAS_CHARACTER; typedef struct _CSMI_SAS_PHY_CONTROL { __u8 bType; __u8 bRate; __u8 bReserved[6]; __u32 uVendorUnique[8]; __u32 uTransmitterFlags; __i8 bTransmitAmplitude; __i8 bTransmitterPreemphasis; __i8 bTransmitterSlewRate; __i8 bTransmitterReserved[13]; __u8 bTransmitterVendorUnique[64]; __u32 uReceiverFlags; __i8 bReceiverThreshold; __i8 bReceiverEqualizationGain; __i8 bReceiverReserved[14]; __u8 bReceiverVendorUnique[64]; __u32 uPatternFlags; __u8 bFixedPattern; __u8 bUserPatternLength; __u8 bPatternReserved[6]; CSMI_SAS_CHARACTER UserPatternBuffer[16]; } CSMI_SAS_PHY_CONTROL, *PCSMI_SAS_PHY_CONTROL; typedef struct _CSMI_SAS_PHY_CONTROL_BUFFER { IOCTL_HEADER IoctlHeader; __u32 uFunction; __u8 bPhyIdentifier; __u16 usLengthOfControl; __u8 bNumberOfControls; __u8 bReserved[4]; __u32 uLinkFlags; __u8 bSpinupRate; __u8 bLinkReserved[7]; __u32 uVendorUnique[8]; CSMI_SAS_PHY_CONTROL Control[1]; } CSMI_SAS_PHY_CONTROL_BUFFER, *PCSMI_SAS_PHY_CONTROL_BUFFER; //EDM #pragma CSMI_SAS_END_PACK #pragma pack() #endif // _CSMI_SAS_H_ smartmontools-7.0/depcomp0000755000175000010010000005601713412155343012576 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2016-01-11.22; # UTC # Copyright (C) 1999-2017 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, see . # 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 outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} 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" # Avoid interferences from the environment. gccflag= dashmflag= # 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 cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc 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. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## 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). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - 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 -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # 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. ## 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. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -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 -ne 0; then 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 ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # 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 ;; 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. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then 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" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; 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. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # 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 ;; #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 "X$1" != 'X--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|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | 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 "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi 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. -arch) eat=yes ;; -*|$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" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | 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 "X$1" != 'X--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 -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [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. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # 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 ;; 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-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: smartmontools-7.0/dev_areca.cpp0000644000175000010010000004717313336335341013644 00000000000000/* * dev_areca.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2012 Hank Wu * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "dev_interface.h" #include "dev_areca.h" const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4760 2018-08-19 18:45:53Z chrfranke $" DEV_ARECA_H_CVSID; #include "atacmds.h" #include "scsicmds.h" #include #if 0 // For debugging areca code static void dumpdata(unsigned char *block, int len) { int ln = (len / 16) + 1; // total line# unsigned char c; int pos = 0; printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); printf("=====================================================================\n"); for ( int l = 0; l < ln && len; l++ ) { // printf the line# and the HEX data // if a line data length < 16 then append the space to the tail of line to reach 16 chars printf("%02X | ", l); for ( pos = 0; pos < 16 && len; pos++, len-- ) { c = block[l*16+pos]; printf("%02X ", c); } if ( pos < 16 ) { for ( int loop = pos; loop < 16; loop++ ) { printf(" "); } } // print ASCII char for ( int loop = 0; loop < pos; loop++ ) { c = block[l*16+loop]; if ( c >= 0x20 && c <= 0x7F ) { printf("%c", c); } else { printf("."); } } printf("\n"); } printf("=====================================================================\n"); } #endif generic_areca_device::generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), m_disknum(disknum), m_encnum(encnum) { set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } generic_areca_device::~generic_areca_device() throw() { } // PURPOSE // This is an interface routine meant to isolate the OS dependent // parts of the code, and to provide a debugging interface. Each // different port and OS needs to provide it's own interface. This // is the Windows interface to the Areca "arcmsr" driver. It allows ATA // commands to be passed through the SCSI driver. // DETAILED DESCRIPTION OF ARGUMENTS // fd: is the file descriptor provided by open() // disknum is the disk number (0 to 127) in the RAID array // command: defines the different operations. // select: additional input data if needed (which log, which type of // self-test). // data: location to write output data, if needed (512 bytes). // Note: not all commands use all arguments. // RETURN VALUES // -1 if the command failed // 0 if the command succeeded, // STATUS_CHECK routine: // -1 if the command failed // 0 if the command succeeded and disk SMART status is "OK" // 1 if the command succeeded and disk SMART status is "FAILING" int generic_areca_device::arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len) { if (arcmsr_cmd >= ARCMSR_CMD_TOTAL) return -1; static const unsigned int cmds[ARCMSR_CMD_TOTAL] = { ARCMSR_IOCTL_READ_RQBUFFER, ARCMSR_IOCTL_WRITE_WQBUFFER, ARCMSR_IOCTL_CLEAR_RQBUFFER, ARCMSR_IOCTL_CLEAR_WQBUFFER, ARCMSR_IOCTL_RETURN_CODE_3F }; int ioctlreturn = 0; sSRB_BUFFER sBuf; struct scsi_cmnd_io iop; int dir = DXFER_TO_DEVICE; uint8_t cdb[10]={0}; uint8_t sense[32]={0}; unsigned char *areca_return_packet; int total = 0; int expected = -1; unsigned char return_buff[2048]={0}; unsigned char *ptr = &return_buff[0]; memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); memset(&iop, 0, sizeof(iop)); sBuf.srbioctl.HeaderLength = sizeof(sARCMSR_IO_HDR); memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); sBuf.srbioctl.Timeout = 10000; sBuf.srbioctl.ControlCode = cmds[arcmsr_cmd]; switch ( arcmsr_cmd ) { // command for writing data to driver case ARCMSR_WRITE_WQBUFFER: if ( data && data_len ) { sBuf.srbioctl.Length = data_len; memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); } /* FALLTHRU */ // commands for clearing related buffer of driver case ARCMSR_CLEAR_RQBUFFER: case ARCMSR_CLEAR_WQBUFFER: cdb[0] = 0x3B; //SCSI_WRITE_BUF command; break; // command for reading data from driver case ARCMSR_READ_RQBUFFER: // command for identifying driver case ARCMSR_RETURN_CODE_3F: cdb[0] = 0x3C; //SCSI_READ_BUF command; dir = DXFER_FROM_DEVICE; break; default: // unknown arcmsr commands return -1; } cdb[1] = 0x01; cdb[2] = 0xf0; cdb[5] = cmds[arcmsr_cmd] >> 24; cdb[6] = cmds[arcmsr_cmd] >> 16; cdb[7] = cmds[arcmsr_cmd] >> 8; cdb[8] = cmds[arcmsr_cmd] & 0x0F; iop.dxfer_dir = dir; iop.dxfer_len = sizeof(sBuf); iop.dxferp = (unsigned char *)&sBuf; iop.cmnd = cdb; iop.cmnd_len = sizeof(cdb); iop.sensep = sense; iop.max_sense_len = sizeof(sense); iop.timeout = SCSI_TIMEOUT_DEFAULT; while ( 1 ) { ioctlreturn = arcmsr_do_scsi_io(&iop); if(ioctlreturn || iop.scsi_status) { break; } if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER ) { // if succeeded, just returns the length of outgoing data return data_len; } if ( sBuf.srbioctl.Length ) { memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); ptr += sBuf.srbioctl.Length; total += sBuf.srbioctl.Length; // the returned bytes enough to compute payload length ? if ( expected < 0 && total >= 5 ) { areca_return_packet = (unsigned char *)&return_buff[0]; if ( areca_return_packet[0] == 0x5E && areca_return_packet[1] == 0x01 && areca_return_packet[2] == 0x61 ) { // valid header, let's compute the returned payload length, // we expected the total length is // payload + 3 bytes header + 2 bytes length + 1 byte checksum expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; } } if ( total >= 7 && total >= expected ) { //printf("total bytes received = %d, expected length = %d\n", total, expected); // ------ Okay! we received enough -------- break; } } } // Deal with the different error cases if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F ) { // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...) return -4; } if ( ioctlreturn ) { pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); return -2; } if ( iop.scsi_status ) { pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status); return -3; } if ( data ) { memcpy(data, return_buff, total); } return total; } bool generic_areca_device::arcmsr_probe() { if(!is_open()) { open(); } if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0) { return false; } return true; } int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result) { int expected = 0; unsigned char return_buff[2048]; unsigned char cs = 0; int cs_pos = 0; // ----- ADD CHECKSUM ----- cs_pos = areca_packet_len - 1; for(int i = 3; i < cs_pos; i++) { areca_packet[cs_pos] += areca_packet[i]; } if(!arcmsr_lock()) { return -1; } expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0); if (expected==-3) { return set_err(EIO); } arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0); expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len); if ( expected > 0 ) { expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff)); } if ( expected < 3 + 1 ) // Prefix + Checksum { return -1; } if(!arcmsr_unlock()) { return -1; } // ----- VERIFY THE CHECKSUM ----- cs = 0; for ( int loop = 3; loop < expected - 1; loop++ ) { cs += return_buff[loop]; } if ( return_buff[expected - 1] != cs ) { return -1; } memcpy(result, return_buff, expected); return expected; } int generic_areca_device::arcmsr_get_controller_type() { int expected = 0; unsigned char return_buff[2048]; unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00}; memset(return_buff, 0, sizeof(return_buff)); expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); if ( expected < 0 ) { return -1; } return return_buff[0xc2]; } int generic_areca_device::arcmsr_get_dev_type() { int expected = 0; unsigned char return_buff[2048]; int ctlr_type = -1; int encnum = get_encnum(); int disknum = get_disknum(); unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22, (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00}; memset(return_buff, 0, sizeof(return_buff)); expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff); if ( expected < 0 ) { return -1; } ctlr_type = arcmsr_get_controller_type(); if( ctlr_type < 0 ) { return ctlr_type; } if( ctlr_type == 0x02/* SATA Controllers */ || (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) ) { // SATA device return 1; } // SAS device return 0; } bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { // ATA input registers typedef struct _ATA_INPUT_REGISTERS { unsigned char features; unsigned char sector_count; unsigned char sector_number; unsigned char cylinder_low; unsigned char cylinder_high; unsigned char device_head; unsigned char command; unsigned char reserved[8]; unsigned char data[512]; // [in/out] buffer for outgoing/incoming data } sATA_INPUT_REGISTERS; // ATA output registers // Note: The output registers is re-sorted for areca internal use only typedef struct _ATA_OUTPUT_REGISTERS { unsigned char error; unsigned char status; unsigned char sector_count; unsigned char sector_number; unsigned char cylinder_low; unsigned char cylinder_high; } sATA_OUTPUT_REGISTERS; // Areca packet format for outgoing: // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 // B[3~4] : 2 bytes command length + variant data length, little endian // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c // B[6~last-1] : variant bytes payload data // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) // // // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte // +--------------------------------------------------------------------------------+ // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | // +--------------------------------------------------------------------------------+ // //Areca packet format for incoming: // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 // B[3~4] : 2 bytes payload length, little endian // B[5~last-1] : variant bytes returned payload data // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) // // // header 3 bytes length 2 bytes payload data x bytes cs 1 byte // +-------------------------------------------------------------------+ // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | // +-------------------------------------------------------------------+ unsigned char areca_packet[640]; int areca_packet_len = sizeof(areca_packet); unsigned char return_buff[2048]; int expected = 0; sATA_INPUT_REGISTERS *ata_cmd; // For debugging #if 0 memset(sInq, 0, sizeof(sInq)); scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); dumpdata((unsigned char *)sInq, sizeof(sInq)); #endif memset(areca_packet, 0, areca_packet_len); // ----- BEGIN TO SETUP HEADERS ------- areca_packet[0] = 0x5E; areca_packet[1] = 0x01; areca_packet[2] = 0x61; areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command // ----- BEGIN TO SETUP PAYLOAD DATA ----- memcpy(&areca_packet[7], "SmrT", 4); // areca defined password ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; // Set registers { const ata_in_regs & r = in.in_regs; ata_cmd->features = r.features; ata_cmd->sector_count = r.sector_count; ata_cmd->sector_number = r.lba_low; ata_cmd->cylinder_low = r.lba_mid; ata_cmd->cylinder_high = r.lba_high; ata_cmd->device_head = r.device; ata_cmd->command = r.command; } bool readdata = false; if (in.direction == ata_cmd_in::data_in) { readdata = true; // the command will read data areca_packet[6] = 0x13; } else if ( in.direction == ata_cmd_in::no_data ) { // the commands will return no data areca_packet[6] = 0x15; } else if (in.direction == ata_cmd_in::data_out) { // the commands will write data memcpy(ata_cmd->data, in.buffer, in.size); areca_packet[6] = 0x14; } else { // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE return set_err(ENOSYS); } areca_packet[11] = get_disknum() - 1; // disk# areca_packet[19] = get_encnum() - 1; // enc# // ----- BEGIN TO SEND TO ARECA DRIVER ------ expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); if ( expected < 0 ) { return set_err(EIO); } sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; if ( ata_out->status ) { if ( in.in_regs.command == ATA_IDENTIFY_DEVICE && !nonempty((unsigned char *)in.buffer, in.size)) { return set_err(ENODEV, "No drive on port %d", get_disknum()); } } // returns with data if (readdata) { memcpy(in.buffer, &return_buff[7], in.size); } // Return register values { ata_out_regs & r = out.out_regs; r.error = ata_out->error; r.sector_count = ata_out->sector_count; r.lba_low = ata_out->sector_number; r.lba_mid = ata_out->cylinder_low; r.lba_high = ata_out->cylinder_high; r.status = ata_out->status; } return true; } bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop) { // Areca packet format for outgoing: // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 // B[3~4] : 2 bytes command length + variant data length, little endian // B[5] : 1 bytes areca defined command code // B[6~last-1] : variant bytes payload data // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) // // // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte // +--------------------------------------------------------------------------------+ // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | // +--------------------------------------------------------------------------------+ // //Areca packet format for incoming: // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 // B[3~4] : 2 bytes payload length, little endian // B[5~last-1] : variant bytes returned payload data // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) // // // header 3 bytes length 2 bytes payload data x bytes cs 1 byte // +-------------------------------------------------------------------+ // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | // +-------------------------------------------------------------------+ unsigned char areca_packet[640]; int areca_packet_len = sizeof(areca_packet); unsigned char return_buff[2048]; int expected = 0; if (iop->cmnd_len > 16) { set_err(EINVAL, "cmnd_len too large"); return false; } memset(areca_packet, 0, areca_packet_len); // ----- BEGIN TO SETUP HEADERS ------- areca_packet[0] = 0x5E; areca_packet[1] = 0x01; areca_packet[2] = 0x61; areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); areca_packet[5] = 0x1c; // ----- BEGIN TO SETUP PAYLOAD DATA ----- areca_packet[6] = 0x16; // scsi pass through memcpy(&areca_packet[7], "SmrT", 4); // areca defined password areca_packet[12] = iop->cmnd_len; // cdb length memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes) areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8); areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16); areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24); if(iop->dxfer_dir == DXFER_TO_DEVICE) { areca_packet[13] |= 0x01; memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len); } else if (iop->dxfer_dir == DXFER_FROM_DEVICE) { } else if( iop->dxfer_dir == DXFER_NONE) { } else { // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE return set_err(ENOSYS); } areca_packet[11] = get_disknum() - 1; // disk# areca_packet[19] = get_encnum() - 1; // enc# // ----- BEGIN TO SEND TO ARECA DRIVER ------ expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff); if (expected < 0) return set_err(EIO, "arcmsr_scsi_pass_through: I/O error"); if (expected < 15) // 7 bytes if port is empty return set_err(EIO, "arcmsr_scsi_pass_through: missing data (%d bytes, expected %d)", expected, 15); int scsi_status = return_buff[5]; int in_data_len = return_buff[11] | return_buff[12] << 8 | return_buff[13] << 16 | return_buff[14] << 24; if (iop->dxfer_dir == DXFER_FROM_DEVICE) { memset(iop->dxferp, 0, iop->dxfer_len); // need? memcpy(iop->dxferp, &return_buff[15], in_data_len); } if(scsi_status == 0xE1 /* Underrun, actual data length < requested data length */) { // don't care, just ignore scsi_status = 0x0; } if(scsi_status != 0x00 && scsi_status != SCSI_STATUS_CHECK_CONDITION) { return set_err(EIO); } if(scsi_status == SCSI_STATUS_CHECK_CONDITION) { // check condition iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; iop->resp_sense_len = 4; iop->sensep[0] = return_buff[7]; iop->sensep[1] = return_buff[8]; iop->sensep[2] = return_buff[9]; iop->sensep[3] = return_buff[10]; } return true; } ///////////////////////////////////////////////////////////// areca_ata_device::areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca") { set_encnum(encnum); set_disknum(disknum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } areca_ata_device::~areca_ata_device() throw() { } bool areca_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_output_regs | //ata_device::supports_multi_sector | // TODO ata_device::supports_48bit_hi_null, "Areca") ) return false; return arcmsr_ata_pass_through(in, out); } ///////////////////////////////////////////////////////////// areca_scsi_device::areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca") { set_encnum(encnum); set_disknum(disknum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } areca_scsi_device::~areca_scsi_device() throw() { } bool areca_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) { return arcmsr_scsi_pass_through(iop); } smartmontools-7.0/dev_areca.h0000644000175000010010000001307013336335341013276 00000000000000/* * dev_areca.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2012 Hank Wu * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEV_ARECA_H #define DEV_ARECA_H #define DEV_ARECA_H_CVSID "$Id: dev_areca.h 4760 2018-08-19 18:45:53Z chrfranke $" ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support /* GENERIC ARECA IO CONTROL CODE*/ enum _GENERIC_ARCMSR_CMDS { ARCMSR_READ_RQBUFFER = 0, ARCMSR_WRITE_WQBUFFER, ARCMSR_CLEAR_RQBUFFER, ARCMSR_CLEAR_WQBUFFER, ARCMSR_RETURN_CODE_3F, ARCMSR_CMD_TOTAL }; #define ARECA_SIG_STR "ARCMSR" #if defined(_WIN32) || defined(__CYGWIN__) #define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004 #define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008 #define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C #define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010 #define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018 #elif defined(__linux__) /*DeviceType*/ #define ARECA_SATA_RAID 0x90000000 /*FunctionCode*/ #define FUNCTION_READ_RQBUFFER 0x0801 #define FUNCTION_WRITE_WQBUFFER 0x0802 #define FUNCTION_CLEAR_RQBUFFER 0x0803 #define FUNCTION_CLEAR_WQBUFFER 0x0804 #define FUNCTION_RETURN_CODE_3F 0x0806 /* ARECA IO CONTROL CODE*/ #define ARCMSR_IOCTL_READ_RQBUFFER (ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER) #define ARCMSR_IOCTL_WRITE_WQBUFFER (ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER) #define ARCMSR_IOCTL_CLEAR_RQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER) #define ARCMSR_IOCTL_CLEAR_WQBUFFER (ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER) #define ARCMSR_IOCTL_RETURN_CODE_3F (ARECA_SATA_RAID | FUNCTION_RETURN_CODE_3F) #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include // _IOWR /*FunctionCode*/ #define FUNCTION_READ_RQBUFFER 0x0801 #define FUNCTION_WRITE_WQBUFFER 0x0802 #define FUNCTION_CLEAR_RQBUFFER 0x0803 #define FUNCTION_CLEAR_WQBUFFER 0x0804 #define FUNCTION_RETURN_CODE_3F 0x0806 /* ARECA IO CONTROL CODE*/ #define ARCMSR_IOCTL_READ_RQBUFFER _IOWR('F', FUNCTION_READ_RQBUFFER, sSRB_BUFFER) #define ARCMSR_IOCTL_WRITE_WQBUFFER _IOWR('F', FUNCTION_WRITE_WQBUFFER, sSRB_BUFFER) #define ARCMSR_IOCTL_CLEAR_RQBUFFER _IOWR('F', FUNCTION_CLEAR_RQBUFFER, sSRB_BUFFER) #define ARCMSR_IOCTL_CLEAR_WQBUFFER _IOWR('F', FUNCTION_CLEAR_WQBUFFER, sSRB_BUFFER) #define ARCMSR_IOCTL_RETURN_CODE_3F _IOWR('F', FUNCTION_RETURN_CODE_3F, sSRB_BUFFER) #endif // The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver typedef struct _ARCMSR_IO_HDR { unsigned int HeaderLength; unsigned char Signature[8]; unsigned int Timeout; unsigned int ControlCode; unsigned int ReturnCode; unsigned int Length; } sARCMSR_IO_HDR; typedef struct _SRB_BUFFER { sARCMSR_IO_HDR srbioctl; unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware } sSRB_BUFFER; class generic_areca_device : virtual public smart_device { public: generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); ~generic_areca_device() throw(); ///////////////////////////////////////////////////////////////////// // OS-dependent functions virtual bool arcmsr_lock() = 0; virtual bool arcmsr_unlock() = 0; virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) = 0; ///////////////////////////////////////////////////////////////////// // OS-independent functions virtual int arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len); virtual int arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result); virtual bool arcmsr_probe(); virtual int arcmsr_get_dev_type(); virtual int arcmsr_get_controller_type(); virtual bool arcmsr_scsi_pass_through(scsi_cmnd_io * iop); virtual bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: generic_areca_device() : smart_device(never_called), m_disknum(-1), m_encnum(-1) { } void set_disknum(int disknum) {m_disknum = disknum;} void set_encnum(int encnum) {m_encnum = encnum;} int get_disknum() {return m_disknum;} int get_encnum() {return m_encnum;} private: int m_disknum; ///< Disk number. int m_encnum; ///< Enclosure number. }; // SATA(ATA) device behind Areca RAID Controller class areca_ata_device : public ata_device, public generic_areca_device { public: areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); ~areca_ata_device() throw(); bool arcmsr_lock() { return true; } bool arcmsr_unlock() { return true; } int arcmsr_do_scsi_io(struct scsi_cmnd_io * /* iop */) { return -1; } protected: areca_ata_device(): smart_device(never_called) { } virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); }; // SAS(SCSI) device behind Areca RAID Controller class areca_scsi_device : public scsi_device, public generic_areca_device { public: areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); ~areca_scsi_device() throw(); bool arcmsr_lock() { return true; } bool arcmsr_unlock() { return true; } int arcmsr_do_scsi_io(struct scsi_cmnd_io * /* iop */) { return -1; } protected: areca_scsi_device(): smart_device(never_called) { } virtual bool scsi_pass_through(scsi_cmnd_io * iop); }; #endif smartmontools-7.0/dev_ata_cmd_set.cpp0000644000175000010010000000610713336335341015024 00000000000000/* * dev_ata_cmd_set.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "atacmds.h" #include "dev_ata_cmd_set.h" #include const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp 4760 2018-08-19 18:45:53Z chrfranke $" DEV_ATA_CMD_SET_H_CVSID; ///////////////////////////////////////////////////////////////////////////// // ata_device_with_command_set // Adapter routine to implement new ATA pass through with old interface bool ata_device_with_command_set::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_ok(in, true)) // data_out_support return false; smart_command_set command = (smart_command_set)-1; int select = 0; char * data = (char *)in.buffer; char buffer[512]; switch (in.in_regs.command) { case ATA_IDENTIFY_DEVICE: command = IDENTIFY; break; case ATA_IDENTIFY_PACKET_DEVICE: command = PIDENTIFY; break; case ATA_CHECK_POWER_MODE: command = CHECK_POWER_MODE; data = buffer; data[0] = 0; break; case ATA_SMART_CMD: switch (in.in_regs.features) { case ATA_SMART_ENABLE: command = ENABLE; break; case ATA_SMART_READ_VALUES: command = READ_VALUES; break; case ATA_SMART_READ_THRESHOLDS: command = READ_THRESHOLDS; break; case ATA_SMART_READ_LOG_SECTOR: command = READ_LOG; select = in.in_regs.lba_low; break; case ATA_SMART_WRITE_LOG_SECTOR: command = WRITE_LOG; select = in.in_regs.lba_low; break; case ATA_SMART_DISABLE: command = DISABLE; break; case ATA_SMART_STATUS: command = (in.out_needed.lba_high ? STATUS_CHECK : STATUS); break; case ATA_SMART_AUTO_OFFLINE: command = AUTO_OFFLINE; select = in.in_regs.sector_count; break; case ATA_SMART_AUTOSAVE: command = AUTOSAVE; select = in.in_regs.sector_count; break; case ATA_SMART_IMMEDIATE_OFFLINE: command = IMMEDIATE_OFFLINE; select = in.in_regs.lba_low; break; default: return set_err(ENOSYS, "Unknown SMART command"); } break; default: return set_err(ENOSYS, "Non-SMART commands not implemented"); } clear_err(); errno = 0; int rc = ata_command_interface(command, select, data); if (rc < 0) { if (!get_errno()) set_err(errno); return false; } switch (command) { case CHECK_POWER_MODE: out.out_regs.sector_count = data[0]; break; case STATUS_CHECK: switch (rc) { case 0: // Good SMART status out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f; break; case 1: // Bad SMART status out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4; break; } break; default: break; } return true; } smartmontools-7.0/dev_ata_cmd_set.h0000644000175000010010000000202513336335341014464 00000000000000/* * dev_ata_cmd_set.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEV_ATA_CMD_SET_H #define DEV_ATA_CMD_SET_H #define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h 4760 2018-08-19 18:45:53Z chrfranke $" #include "atacmds.h" // smart_command_set #include "dev_interface.h" ///////////////////////////////////////////////////////////////////////////// // ata_device_with_command_set /// Adapter class to implement new ATA pass through old interface. class ata_device_with_command_set : public /*implements*/ ata_device { public: /// ATA pass through mapped to ata_command_interface(). virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: /// Old ATA interface called by ata_pass_through() virtual int ata_command_interface(smart_command_set command, int select, char * data) = 0; ata_device_with_command_set() : smart_device(never_called) { } }; #endif // DEV_ATA_CMD_SET_H smartmontools-7.0/dev_intelliprop.cpp0000644000175000010010000002503713336335341015125 00000000000000/* * dev_intelliprop.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016 Casey Biemiller * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "atacmds.h" //ATTR_PACKED and ASSERT_SIZEOF_STRUCT #include "dev_interface.h" #include "dev_intelliprop.h" #include "dev_tunnelled.h" #include const char * dev_intelliprop_cpp_cvsid = "$Id: dev_intelliprop.cpp 4760 2018-08-19 18:45:53Z chrfranke $" DEV_INTELLIPROP_H_CVSID; //Vendor Specific log addresses #define LOG_C0 0xc0 // VS LOG MODE CONTROL BITS enum { IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED = (0 << 0), // NOTE: Not supported IPROP_VS_LOG_MODE_CTL_MANUAL_SUPPORTED = (1 << 1), IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED = (0 << 2), // NOTE: Not supported IPROP_VS_LOG_MODE_CTL_MANUAL_ENABLED = (1 << 3), }; // VS LOG PORT SETTING BITS enum { IPROP_VS_LOG_PORT_WRITE_ENABLE_MASK = 0xC000, IPROP_VS_LOG_PORT_WRITE_ENABLE_VALID = 0x8000, IPROP_VS_LOG_PORT_RX_DC_GAIN_MASK = 0x3000, IPROP_VS_LOG_PORT_RX_DC_GAIN_SHIFT = 12, IPROP_VS_LOG_PORT_RX_EQ_MASK = 0x0F00, IPROP_VS_LOG_PORT_RX_EQ_SHIFT = 8, IPROP_VS_LOG_PORT_TX_PREEMP_MASK = 0x00F8, IPROP_VS_LOG_PORT_TX_PREEMP_SHIFT = 3, IPROP_VS_LOG_PORT_TX_VOD_MASK = 0x0007, IPROP_VS_LOG_PORT_TX_VOD_SHIFT = 0, }; //This struct is used for the Vendor Specific log C0 on devices that support it. #pragma pack(1) struct iprop_internal_log { uint32_t drive_select; // Bytes - [ 3: 0] of Log C0 uint32_t obsolete; // Bytes - [ 7: 4] of Log C0 uint8_t mode_control; // Byte - [ 8] of Log C0 uint8_t log_passthrough; // Byte - [ 9] of Log C0 uint16_t tier_id; // Bytes - [ 11: 10] of Log C0 uint32_t hw_version; // Bytes - [ 15: 12] of Log C0 uint32_t fw_version; // Bytes - [ 19: 16] of Log C0 uint8_t variant[8]; // Bytes - [ 27: 20] of Log C0 uint8_t reserved[228]; // Bytes - [255: 28] of Log C0 uint16_t port_0_settings[3]; // Bytes - [263:256] of Log C0 uint16_t port_0_reserved; uint16_t port_1_settings[3]; // Bytes - [271:264] of Log C0 uint16_t port_1_reserved; uint16_t port_2_settings[3]; // Bytes - [279:272] of Log C0 uint16_t port_2_reserved; uint16_t port_3_settings[3]; // Bytes - [287:280] of Log C0 uint16_t port_3_reserved; uint16_t port_4_settings[3]; // Bytes - [295:288] of Log C0 uint16_t port_4_reserved; uint8_t reserved2[214]; // Bytes - [509:296] of Log C0 uint16_t crc; // Bytes - [511:510] of Log C0 } ATTR_PACKED; #pragma pack() ASSERT_SIZEOF_STRUCT(iprop_internal_log, 512); /** * buffer is a pointer to a buffer of bytes, which should include data and * also CRC if the function is being used to check CRC * len is the number of bytes in the buffer (including CRC if it is present) * check_crc is a boolean value, set true to check an existing CRC, false * to calculate a new CRC */ static uint16_t iprop_crc16_1(uint8_t * buffer, uint32_t len, bool check_crc) { uint8_t crc[16]; uint16_t crc_final = 0; uint8_t crc_msb; uint8_t data_msb; uint32_t total_len; // Initialize CRC array for (uint32_t ii = 0; ii < 16; ii++) { crc[ii] = 0; //crc[ii] = (crc_in >> ii) & 1; } // If calculating a new CRC, we need to pad the data with extra zeroes total_len = check_crc ? len : len + 2; // Loop for each byte, plus extra for the CRC itself for (uint32_t ii = 0; ii < total_len; ii++) { uint8_t data = (ii < len) ? buffer[ii] : 0; // Loop for each bit for (uint32_t jj = 0; jj < 8; jj++) { crc_msb = crc[15]; data_msb = (data >> (8 - jj - 1)) & 1; crc[15] = crc[14] ^ crc_msb; crc[14] = crc[13]; crc[13] = crc[12]; crc[12] = crc[11]; crc[11] = crc[10] ^ crc_msb; crc[10] = crc[9]; crc[9] = crc[8] ^ crc_msb; crc[8] = crc[7] ^ crc_msb; crc[7] = crc[6] ^ crc_msb; crc[6] = crc[5]; crc[5] = crc[4] ^ crc_msb; crc[4] = crc[3] ^ crc_msb; crc[3] = crc[2]; crc[2] = crc[1] ^ crc_msb; crc[1] = crc[0] ^ crc_msb; crc[0] = data_msb ^ crc_msb; } } // Convert CRC array to final value for (uint32_t ii = 0; ii < 16; ii++) { if (crc[ii] == 1) { crc_final |= (1 << ii); } else { crc_final &= ~(1 << ii); } } return crc_final; } static void iprop_dump_log_structure(struct iprop_internal_log const * const log) { pout("Dumping LOG Structure:\n"); pout(" drive_select: 0x%08x\n", log->drive_select); pout(" obsolete: 0x%08x\n", log->obsolete); pout(" mode_control: 0x%02x\n", log->mode_control); pout(" log_passthrough: 0x%02x\n", log->log_passthrough); pout(" tier_id: 0x%04x\n", log->tier_id); pout(" hw_version: 0x%08x\n", log->hw_version); pout(" fw_version: 0x%08x\n", log->fw_version); pout(" variant: \""); for (int ii = 0; ii < 8; ii++) { pout("%c", (char)log->variant[ii]); } pout("\"\n"); pout(" port_0_settings(Gen 1): 0x%08x\n", log->port_0_settings[0]); pout(" port_0_settings(Gen 2): 0x%08x\n", log->port_0_settings[1]); pout(" port_0_settings(Gen 3): 0x%08x\n", log->port_0_settings[2]); pout(" port_1_settings(Gen 1): 0x%08x\n", log->port_1_settings[0]); pout(" port_1_settings(Gen 2): 0x%08x\n", log->port_1_settings[1]); pout(" port_1_settings(Gen 3): 0x%08x\n", log->port_1_settings[2]); pout(" port_2_settings(Gen 1): 0x%08x\n", log->port_2_settings[0]); pout(" port_2_settings(Gen 2): 0x%08x\n", log->port_2_settings[1]); pout(" port_2_settings(Gen 3): 0x%08x\n", log->port_2_settings[2]); pout(" port_3_settings(Gen 1): 0x%08x\n", log->port_3_settings[0]); pout(" port_3_settings(Gen 2): 0x%08x\n", log->port_3_settings[1]); pout(" port_3_settings(Gen 3): 0x%08x\n", log->port_3_settings[2]); pout(" port_4_settings(Gen 1): 0x%08x\n", log->port_4_settings[0]); pout(" port_4_settings(Gen 2): 0x%08x\n", log->port_4_settings[1]); pout(" port_4_settings(Gen 3): 0x%08x\n", log->port_4_settings[2]); pout(" crc: 0x%04x\n", log->crc); pout("\n"); } static bool iprop_switch_routed_drive(ata_device * device, int drive_select) { // Declare a log page buffer and initialize it with what is on the drive currently iprop_internal_log write_payload; if (!ataReadLogExt(device, LOG_C0, 0, 0, &write_payload, 1)) return device->set_err(EIO, "intelliprop: Initial Read Log failed: %s", device->get_errmsg()); // Check the returned data is good uint16_t const crc_check = iprop_crc16_1((uint8_t *)&write_payload, sizeof(struct iprop_internal_log), false); //If this first read fails the crc check, the log can be still sent with routing information //as long as everything else in the log is zeroed. So there is no need to return false. if (crc_check != 0) { if (ata_debugmode) pout("Intelliprop WARNING: Received log crc(0x%04X) is invalid!\n", crc_check); iprop_dump_log_structure(&write_payload); memset(&write_payload, 0, sizeof(struct iprop_internal_log)); } //The option to read the log, even if successful, could be useful if (ata_debugmode) iprop_dump_log_structure(&write_payload); // Modify the current drive select to what we were given write_payload.drive_select = (uint32_t)drive_select; if (ata_debugmode) pout("Intelliprop - Change to port 0x%08X.\n", write_payload.drive_select); write_payload.log_passthrough = 0; // TEST (Set to 1, non hydra member drive will abort --> test error handling) write_payload.tier_id = 0; // TEST (Set to non-zero, non hydra member drive will abort --> test error handling) // Update the CRC area uint16_t const crc_new = iprop_crc16_1((uint8_t *)&write_payload, sizeof(struct iprop_internal_log) - sizeof(uint16_t), false); write_payload.crc = (crc_new >> 8) | (crc_new << 8); // Check our CRC work uint16_t const crc_check2 = iprop_crc16_1((uint8_t *)&write_payload, sizeof(struct iprop_internal_log), false); if (crc_check2 != 0) return device->set_err(EIO, "intelliprop: Re-calculated log crc(0x%04X) is invalid!", crc_check2); // Apply the Write LOG if (!ataWriteLogExt(device, LOG_C0, 0, &write_payload, 1)) return device->set_err(EIO, "intelliprop: Write Log failed: %s", device->get_errmsg()); // Check that the Write LOG was applied iprop_internal_log check_payload; if (!ataReadLogExt(device, LOG_C0, 0, 0, &check_payload, 1)) return device->set_err(EIO, "intelliprop: Secondary Read Log failed: %s", device->get_errmsg()); if (check_payload.drive_select != write_payload.drive_select) { if (ata_debugmode > 1) iprop_dump_log_structure(&check_payload); return device->set_err(EIO, "intelliprop: Current drive select val(0x%08X) is not expected(0x%08X)", check_payload.drive_select, write_payload.drive_select); } return true; } namespace intelliprop { class intelliprop_device : public tunnelled_device< /*implements*/ ata_device, /*by using an*/ ata_device > { public: intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev); virtual ~intelliprop_device() throw(); virtual bool open(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); private: unsigned m_phydrive; }; intelliprop_device::intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev) : smart_device(intf, atadev->get_dev_name(), "intelliprop", "intelliprop"), tunnelled_device(atadev), m_phydrive(phydrive) { set_info().info_name = strprintf("%s [intelliprop_disk_%u]", atadev->get_info_name(), phydrive); } intelliprop_device::~intelliprop_device() throw() { } bool intelliprop_device::open() { if (!tunnelled_device::open()) return false; ata_device * atadev = get_tunnel_dev(); if (!iprop_switch_routed_drive(atadev, m_phydrive)) { close(); return set_err(atadev->get_err()); } return true; } bool intelliprop_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { return get_tunnel_dev()->ata_pass_through(in, out); } }//namespace ata_device * get_intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev) { return new intelliprop::intelliprop_device(intf, phydrive, atadev); } smartmontools-7.0/dev_intelliprop.h0000644000175000010010000000071613342760624014572 00000000000000/* * dev_intelliprop.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016 Casey Biemiller * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEV_INTELLIPROP_H #define DEV_INTELLIPROP_H #define DEV_INTELLIPROP_H_CVSID "$Id: dev_intelliprop.h 4763 2018-09-02 13:11:48Z chrfranke $" ata_device * get_intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev); #endif smartmontools-7.0/dev_interface.cpp0000644000175000010010000003434313402014526014515 00000000000000/* * dev_interface.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "dev_interface.h" #include "dev_intelliprop.h" #include "dev_tunnelled.h" #include "atacmds.h" // ATA_SMART_CMD/STATUS #include "scsicmds.h" // scsi_cmnd_io #include "utility.h" #include #include #include #if defined(HAVE_GETTIMEOFDAY) #include #elif defined(HAVE_FTIME) #include #endif const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4848 2018-12-05 18:30:46Z chrfranke $" DEV_INTERFACE_H_CVSID; ///////////////////////////////////////////////////////////////////////////// // smart_device int smart_device::s_num_objects = 0; smart_device::smart_device(smart_interface * intf, const char * dev_name, const char * dev_type, const char * req_type) : m_intf(intf), m_info(dev_name, dev_type, req_type), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0) { s_num_objects++; } smart_device::smart_device(do_not_use_in_implementation_classes) : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0) { throw std::logic_error("smart_device: wrong constructor called in implementation class"); } smart_device::~smart_device() throw() { s_num_objects--; } bool smart_device::is_syscall_unsup() const { if (get_errno() == ENOSYS) return true; #ifdef ENOTSUP if (get_errno() == ENOTSUP) return true; #endif return false; } bool smart_device::set_err(int no, const char * msg, ...) { if (!msg) return set_err(no); m_err.no = no; va_list ap; va_start(ap, msg); m_err.msg = vstrprintf(msg, ap); va_end(ap); return false; } bool smart_device::set_err(int no) { return smi()->set_err_var(&m_err, no); } smart_device * smart_device::autodetect_open() { open(); return this; } bool smart_device::is_powered_down() { return false; } bool smart_device::owns(const smart_device * /*dev*/) const { return false; } void smart_device::release(const smart_device * /*dev*/) { } ///////////////////////////////////////////////////////////////////////////// // ata_device ata_in_regs_48bit::ata_in_regs_48bit() : features_16(features, prev.features), sector_count_16(sector_count, prev.sector_count), lba_low_16(lba_low, prev.lba_low), lba_mid_16(lba_mid, prev.lba_mid), lba_high_16(lba_high, prev.lba_high), lba_48( lba_low, lba_mid, lba_high, prev.lba_low, prev.lba_mid, prev.lba_high) { } ata_out_regs_48bit::ata_out_regs_48bit() : sector_count_16(sector_count, prev.sector_count), lba_low_16(lba_low, prev.lba_low), lba_mid_16(lba_mid, prev.lba_mid), lba_high_16(lba_high, prev.lba_high), lba_48( lba_low, lba_mid, lba_high, prev.lba_low, prev.lba_mid, prev.lba_high) { } ata_cmd_in::ata_cmd_in() : direction(no_data), buffer(0), size(0) { } ata_cmd_out::ata_cmd_out() { } bool ata_device::ata_pass_through(const ata_cmd_in & in) { ata_cmd_out dummy; return ata_pass_through(in, dummy); } bool ata_device::ata_cmd_is_supported(const ata_cmd_in & in, unsigned flags, const char * type /* = 0 */) { // Check DATA IN/OUT switch (in.direction) { case ata_cmd_in::no_data: break; case ata_cmd_in::data_in: break; case ata_cmd_in::data_out: break; default: return set_err(EINVAL, "Invalid data direction %d", (int)in.direction); } // Check buffer size if (in.direction == ata_cmd_in::no_data) { if (in.size) return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size); } else { if (!in.buffer) return set_err(EINVAL, "Buffer not set for DATA IN/OUT command"); unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count; // TODO: Add check for sector count == 0 if (count * 512 != in.size) return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size); } // Check features const char * errmsg = 0; if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out)) errmsg = "DATA OUT ATA commands not implemented"; else if ( in.out_needed.is_set() && !(flags & supports_output_regs) && !( in.in_regs.command == ATA_SMART_CMD && in.in_regs.features == ATA_SMART_STATUS && (flags & supports_smart_status))) errmsg = "Read of ATA output registers not implemented"; else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector)) errmsg = "Multi-sector ATA commands not implemented"; else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit))) errmsg = "48-bit ATA commands not implemented"; else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit)) errmsg = "48-bit ATA commands not fully implemented"; if (errmsg) return set_err(ENOSYS, "%s%s%s%s", errmsg, (type ? " [" : ""), (type ? type : ""), (type ? "]" : "")); return true; } bool ata_device::ata_identify_is_cached() const { return false; } ///////////////////////////////////////////////////////////////////////////// // scsi_device bool scsi_device::scsi_pass_through_and_check(scsi_cmnd_io * iop, const char * msg) { // Provide sense buffer unsigned char sense[32] = {0, }; iop->sensep = sense; iop->max_sense_len = sizeof(sense); iop->timeout = SCSI_TIMEOUT_DEFAULT; // Run cmd if (!scsi_pass_through(iop)) { if (scsi_debugmode > 0) pout("%sscsi_pass_through() failed, errno=%d [%s]\n", msg, get_errno(), get_errmsg()); return false; } // Check sense scsi_sense_disect sinfo; scsi_do_sense_disect(iop, &sinfo); int err = scsiSimpleSenseFilter(&sinfo); if (err) { if (scsi_debugmode > 0) pout("%sscsi error: %s\n", msg, scsiErrString(err)); return set_err(EIO, "scsi error %s", scsiErrString(err)); } return true; } ///////////////////////////////////////////////////////////////////////////// // nvme_device bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */) { if (!status) throw std::logic_error("nvme_device: set_nvme_err() called with status=0"); out.status = status; out.status_valid = true; return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status); } ///////////////////////////////////////////////////////////////////////////// // tunnelled_device_base tunnelled_device_base::tunnelled_device_base(smart_device * tunnel_dev) : smart_device(never_called), m_tunnel_base_dev(tunnel_dev) { } tunnelled_device_base::~tunnelled_device_base() throw() { delete m_tunnel_base_dev; } bool tunnelled_device_base::is_open() const { return (m_tunnel_base_dev && m_tunnel_base_dev->is_open()); } bool tunnelled_device_base::open() { if (!m_tunnel_base_dev) return set_err(ENOSYS); if (!m_tunnel_base_dev->open()) return set_err(m_tunnel_base_dev->get_err()); return true; } bool tunnelled_device_base::close() { if (!m_tunnel_base_dev) return true; if (!m_tunnel_base_dev->close()) return set_err(m_tunnel_base_dev->get_err()); return true; } bool tunnelled_device_base::owns(const smart_device * dev) const { return (m_tunnel_base_dev && (m_tunnel_base_dev == dev)); } void tunnelled_device_base::release(const smart_device * dev) { if (m_tunnel_base_dev == dev) m_tunnel_base_dev = 0; } ///////////////////////////////////////////////////////////////////////////// // smart_interface // Pointer to (usually singleton) interface object returned by ::smi() smart_interface * smart_interface::s_instance; std::string smart_interface::get_os_version_str() { return SMARTMONTOOLS_BUILD_HOST; } std::string smart_interface::get_valid_dev_types_str() { // default std::string s = "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], " "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntjmicron[,NSID], " "intelliprop,N[+TYPE]"; // append custom std::string s2 = get_valid_custom_dev_types_str(); if (!s2.empty()) { s += ", "; s += s2; } return s; } std::string smart_interface::get_app_examples(const char * /*appname*/) { return ""; } int64_t smart_interface::get_timer_usec() { #if defined(HAVE_GETTIMEOFDAY) #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) { static bool have_clock_monotonic = true; if (have_clock_monotonic) { struct timespec ts; if (!clock_gettime(CLOCK_MONOTONIC, &ts)) return ts.tv_sec * 1000000LL + ts.tv_nsec/1000; have_clock_monotonic = false; } } #endif { struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec * 1000000LL + tv.tv_usec; } #elif defined(HAVE_FTIME) { struct timeb tb; ftime(&tb); return tb.time * 1000000LL + tb.millitm * 1000; } #else return -1; #endif } bool smart_interface::disable_system_auto_standby(bool /*disable*/) { return set_err(ENOSYS); } bool smart_interface::set_err(int no, const char * msg, ...) { if (!msg) return set_err(no); m_err.no = no; va_list ap; va_start(ap, msg); m_err.msg = vstrprintf(msg, ap); va_end(ap); return false; } bool smart_interface::set_err(int no) { return set_err_var(&m_err, no); } bool smart_interface::set_err_var(smart_device::error_info * err, int no) { err->no = no; err->msg = get_msg_for_errno(no); if (err->msg.empty() && no != 0) err->msg = strprintf("Unknown error %d", no); return false; } const char * smart_interface::get_msg_for_errno(int no) { return strerror(no); } ///////////////////////////////////////////////////////////////////////////// // Default device factory smart_device * smart_interface::get_smart_device(const char * name, const char * type) { clear_err(); // Call platform specific autodetection if no device type specified smart_device * dev; if (!type || !*type) { dev = autodetect_smart_device(name); if (!dev && !get_errno()) set_err(EINVAL, "Unable to detect device type"); return dev; } // First check for platform specific device types dev = get_custom_smart_device(name, type); if (dev || get_errno()) return dev; if (!strcmp(type, "ata")) dev = get_ata_device(name, type); else if (!strcmp(type, "scsi")) dev = get_scsi_device(name, type); else if (str_starts_with(type, "nvme")) { int n1 = -1, n2 = -1, len = strlen(type); unsigned nsid = 0; // invalid namespace id -> use default sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2); if (!(n1 == len || n2 == len)) { set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type); return 0; } dev = get_nvme_device(name, type, nsid); } else if ( (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3]))) || str_starts_with(type, "scsi+") || str_starts_with(type, "usb") ) { // Split "sat...+base..." -> ("sat...", "base...") unsigned satlen = strcspn(type, "+"); std::string sattype(type, satlen); const char * basetype = (type[satlen] ? type+satlen+1 : ""); // Recurse to allocate base device, default is standard SCSI if (!*basetype) basetype = "scsi"; smart_device_auto_ptr basedev( get_smart_device(name, basetype) ); if (!basedev) { set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg()); return 0; } // Result must be SCSI if (!basedev->is_scsi()) { set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype); return 0; } // Attach SAT tunnel return get_sat_device(sattype.c_str(), basedev.release()->to_scsi()); } else if (str_starts_with(type, "snt")) { smart_device_auto_ptr basedev( get_smart_device(name, "scsi") ); if (!basedev) { set_err(EINVAL, "Type '%s': %s", type, get_errmsg()); return 0; } return get_snt_device(type, basedev.release()->to_scsi()); } else if (str_starts_with(type, "intelliprop")) { // Parse "intelliprop,N[+base...]" unsigned phydrive = ~0; int n = -1; char c = 0; sscanf(type, "intelliprop,%u%n%c", &phydrive, &n, &c); if (!((n == (int)strlen(type) || c == '+') && phydrive <= 3)) { set_err(EINVAL, "Option '-d intelliprop,N' requires N between 0 and 3"); return 0; } const char * basetype = (type[n] ? type + n + 1 : ""); // Recurse to allocate base device, default is standard ATA if (!*basetype) basetype = "ata"; smart_device_auto_ptr basedev( get_smart_device(name, basetype) ); if (!basedev) { set_err(EINVAL, "Type '%s': %s", type, get_errmsg()); return 0; } // Result must be ATA if (!basedev->is_ata()) { set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype); return 0; } return get_intelliprop_device(this, phydrive, basedev.release()->to_ata()); } else { set_err(EINVAL, "Unknown device type '%s'", type); return 0; } if (!dev && !get_errno()) set_err(EINVAL, "Not a device of type '%s'", type); return dev; } bool smart_interface::scan_smart_devices(smart_device_list & /*devlist*/, const char * /*type*/, const char * /*pattern*/ /* = 0 */) { return set_err(ENOSYS); } bool smart_interface::scan_smart_devices(smart_device_list & devlist, const smart_devtype_list & types, const char * pattern /* = 0 */) { unsigned n = types.size(); if (n == 0) return scan_smart_devices(devlist, (const char *)0, pattern); if (n == 1) return scan_smart_devices(devlist, types.front().c_str(), pattern); for (unsigned i = 0; i < n; i++) { smart_device_list tmplist; if (!scan_smart_devices(tmplist, types[i].c_str(), pattern)) return false; devlist.append(tmplist); } return true; } nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/) { set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools"); return 0; } smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/) { return 0; } std::string smart_interface::get_valid_custom_dev_types_str() { return ""; } smart_device * smart_interface::get_scsi_passthrough_device(const char * type, scsi_device * scsidev) { if (!strncmp(type, "snt", 3)) { return get_snt_device(type, scsidev); } return get_sat_device(type, scsidev); } smartmontools-7.0/dev_interface.h0000644000175000010010000007362113402014526014164 00000000000000/* * dev_interface.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEV_INTERFACE_H #define DEV_INTERFACE_H #define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4848 2018-12-05 18:30:46Z chrfranke $\n" #include "utility.h" #include #include #include ///////////////////////////////////////////////////////////////////////////// // Common functionality for all device types // Forward declarations class smart_interface; class ata_device; class scsi_device; class nvme_device; /// Base class for all devices class smart_device { // Types public: /// Device info strings struct device_info { device_info() { } device_info(const char * d_name, const char * d_type, const char * r_type) : dev_name(d_name), info_name(d_name), dev_type(d_type), req_type(r_type) { } std::string dev_name; ///< Device (path)name std::string info_name; ///< Informal name std::string dev_type; ///< Actual device type std::string req_type; ///< Device type requested by user, empty if none }; /// Error (number,message) pair struct error_info { explicit error_info(int n = 0) : no(n) { } error_info(int n, const char * m) : no(n), msg(m) { } void clear() { no = 0; msg.erase(); } int no; ///< Error number std::string msg; ///< Error message }; // Construction protected: /// Constructor to init interface and device info. /// Must be called in implementation classes. smart_device(smart_interface * intf, const char * dev_name, const char * dev_type, const char * req_type); /// Dummy enum for dummy constructor. enum do_not_use_in_implementation_classes { never_called }; /// Dummy constructor for abstract classes. /// Must never be called in implementation classes. explicit smart_device(do_not_use_in_implementation_classes); public: virtual ~smart_device() throw(); // Attributes public: /////////////////////////////////////////////// // Dynamic downcasts to actual device flavor /// Return true if ATA device bool is_ata() const { return !!m_ata_ptr; } /// Return true if SCSI device bool is_scsi() const { return !!m_scsi_ptr; } /// Return true if NVMe device bool is_nvme() const { return !!m_nvme_ptr; } /// Downcast to ATA device. ata_device * to_ata() { return m_ata_ptr; } /// Downcast to ATA device (const). const ata_device * to_ata() const { return m_ata_ptr; } /// Downcast to SCSI device. scsi_device * to_scsi() { return m_scsi_ptr; } /// Downcast to SCSI device (const). const scsi_device * to_scsi() const { return m_scsi_ptr; } /// Downcast to NVMe device. nvme_device * to_nvme() { return m_nvme_ptr; } /// Downcast to NVMe device (const). const nvme_device * to_nvme() const { return m_nvme_ptr; } /////////////////////////////////////////////// // Device information /// Get device info struct. const device_info & get_info() const { return m_info; } /// Get device (path)name. const char * get_dev_name() const { return m_info.dev_name.c_str(); } /// Get informal name. const char * get_info_name() const { return m_info.info_name.c_str(); } /// Get device type. const char * get_dev_type() const { return m_info.dev_type.c_str(); } /// Get type requested by user, empty if none. const char * get_req_type() const { return m_info.req_type.c_str(); } protected: /// R/W access to device info struct. device_info & set_info() { return m_info; } public: /////////////////////////////////////////////// // Last error information /// Get last error info struct. const error_info & get_err() const { return m_err; } /// Get last error number. int get_errno() const { return m_err.no; } /// Get last error message. const char * get_errmsg() const { return m_err.msg.c_str(); } /// Return true if last error indicates an unsupported system call. /// Default implementation returns true on ENOSYS and ENOTSUP. virtual bool is_syscall_unsup() const; /// Set last error number and message. /// Printf()-like formatting is supported. /// Returns false always to allow use as a return expression. bool set_err(int no, const char * msg, ...) __attribute_format_printf(3, 4); /// Set last error info struct. bool set_err(const error_info & err) { m_err = err; return false; } /// Clear last error info. void clear_err() { m_err.clear(); } /// Set last error number and default message. /// Message is retrieved from interface's get_msg_for_errno(no). bool set_err(int no); /// Get current number of allocated 'smart_device' objects. static int get_num_objects() { return s_num_objects; } // Operations public: /////////////////////////////////////////////// // Device open/close // Must be implemented in derived class /// Return true if device is open. virtual bool is_open() const = 0; /// Open device, return false on error. virtual bool open() = 0; /// Close device, return false on error. virtual bool close() = 0; /// Open device with autodetection support. /// May return another device for further access. /// In this case, the original pointer is no longer valid. /// Default implementation calls 'open()' and returns 'this'. virtual smart_device * autodetect_open(); /////////////////////////////////////////////// // Support for checking power mode reported by operating system /// Early test if device is powered up or down. /// Can be used without calling 'open()' first! /// Return true when device is powered down, false when /// powered up. If this function is not implemented or /// the mode cannot be determined, return false. /// Default implementation returns false. virtual bool is_powered_down(); /////////////////////////////////////////////// // Support for tunnelled devices /// Return true if other device is owned by this device. /// Default implementation returns false. virtual bool owns(const smart_device * dev) const; /// Release ownership of other device. /// Default implementation does nothing. virtual void release(const smart_device * dev); protected: /// Get interface which produced this object. smart_interface * smi() { return m_intf; } /// Get interface which produced this object (const). const smart_interface * smi() const { return m_intf; } // Implementation private: smart_interface * m_intf; device_info m_info; error_info m_err; // Pointers for to_ata(), to_scsi(), to_nvme() // set by ATA/SCSI/NVMe interface classes. friend class ata_device; ata_device * m_ata_ptr; friend class scsi_device; scsi_device * m_scsi_ptr; friend class nvme_device; nvme_device * m_nvme_ptr; // Number of objects. static int s_num_objects; // Prevent copy/assignment smart_device(const smart_device &); void operator=(const smart_device &); }; ///////////////////////////////////////////////////////////////////////////// // ATA specific interface /// ATA register value and info whether it has ever been set // (Automatically set by first assignment) class ata_register { public: ata_register() : m_val(0x00), m_is_set(false) { } ata_register & operator=(unsigned char x) { m_val = x; m_is_set = true; return * this; } unsigned char val() const { return m_val; } operator unsigned char() const { return m_val; } bool is_set() const { return m_is_set; } private: unsigned char m_val; ///< Register value bool m_is_set; ///< true if set }; /// ATA Input registers (for 28-bit commands) struct ata_in_regs { // ATA-6/7 register names // ATA-3/4/5 // ATA-8 ata_register features; // features // features ata_register sector_count; // sector count // count ata_register lba_low; // sector number // ] ata_register lba_mid; // cylinder low // ] lba ata_register lba_high; // cylinder high // ] ata_register device; // device/head // device ata_register command; // command // command /// Return true if any register is set bool is_set() const { return (features.is_set() || sector_count.is_set() || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set() || device.is_set() || command.is_set()); } }; /// ATA Output registers (for 28-bit commands) struct ata_out_regs { ata_register error; ata_register sector_count; ata_register lba_low; ata_register lba_mid; ata_register lba_high; ata_register device; ata_register status; /// Return true if any register is set bool is_set() const { return (error.is_set() || sector_count.is_set() || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set() || device.is_set() || status.is_set()); } }; /// 16-bit alias to a 8-bit ATA register pair. class ata_reg_alias_16 { public: ata_reg_alias_16(ata_register & lo, ata_register & hi) : m_lo(lo), m_hi(hi) { } ata_reg_alias_16 & operator=(unsigned short x) { m_lo = (unsigned char) x; m_hi = (unsigned char)(x >> 8); return * this; } unsigned short val() const { return m_lo | (m_hi << 8); } operator unsigned short() const { return m_lo | (m_hi << 8); } private: ata_register & m_lo, & m_hi; // References must not be copied. ata_reg_alias_16(const ata_reg_alias_16 &); void operator=(const ata_reg_alias_16 &); }; /// 48-bit alias to six 8-bit ATA registers (for LBA). class ata_reg_alias_48 { public: ata_reg_alias_48(ata_register & ll, ata_register & lm, ata_register & lh, ata_register & hl, ata_register & hm, ata_register & hh) : m_ll(ll), m_lm(lm), m_lh(lh), m_hl(hl), m_hm(hm), m_hh(hh) { } ata_reg_alias_48 & operator=(uint64_t x) { m_ll = (unsigned char) x; m_lm = (unsigned char)(x >> 8); m_lh = (unsigned char)(x >> 16); m_hl = (unsigned char)(x >> 24); m_hm = (unsigned char)(x >> 32); m_hh = (unsigned char)(x >> 40); return * this; } uint64_t val() const { return ( (unsigned)m_ll | ((unsigned)m_lm << 8) | ((unsigned)m_lh << 16) | ((unsigned)m_hl << 24) | ((uint64_t)m_hm << 32) | ((uint64_t)m_hh << 40)); } operator uint64_t() const { return val(); } private: ata_register & m_ll, & m_lm, & m_lh, & m_hl, & m_hm, & m_hh; // References must not be copied. ata_reg_alias_48(const ata_reg_alias_48 &); void operator=(const ata_reg_alias_48 &); }; /// ATA Input registers for 48-bit commands // See section 4.14 of T13/1532D Volume 1 Revision 4b // // Uses ATA-6/7 method to specify 16-bit registers as // recent (low byte) and previous (high byte) content of // 8-bit registers. // // (ATA-8 ACS does not longer follow this scheme, it uses // abstract registers with sufficient size and leaves the // actual mapping to the transport layer.) // struct ata_in_regs_48bit : public ata_in_regs // "most recently written" registers { ata_in_regs prev; ///< "previous content" // 16-bit aliases for above pair. ata_reg_alias_16 features_16; ata_reg_alias_16 sector_count_16; ata_reg_alias_16 lba_low_16; ata_reg_alias_16 lba_mid_16; ata_reg_alias_16 lba_high_16; // 48-bit alias to all 8-bit LBA registers. ata_reg_alias_48 lba_48; /// Return true if 48-bit command bool is_48bit_cmd() const { return prev.is_set(); } /// Return true if 48-bit command with any nonzero high byte bool is_real_48bit_cmd() const { return ( prev.features || prev.sector_count || prev.lba_low || prev.lba_mid || prev.lba_high); } ata_in_regs_48bit(); }; /// ATA Output registers for 48-bit commands struct ata_out_regs_48bit : public ata_out_regs // read with HOB=0 { ata_out_regs prev; ///< read with HOB=1 // 16-bit aliases for above pair. ata_reg_alias_16 sector_count_16; ata_reg_alias_16 lba_low_16; ata_reg_alias_16 lba_mid_16; ata_reg_alias_16 lba_high_16; // 48-bit alias to all 8-bit LBA registers. ata_reg_alias_48 lba_48; ata_out_regs_48bit(); }; /// Flags for each ATA output register struct ata_out_regs_flags { bool error, sector_count, lba_low, lba_mid, lba_high, device, status; /// Return true if any flag is set. bool is_set() const { return ( error || sector_count || lba_low || lba_mid || lba_high || device || status); } /// Default constructor clears all flags. ata_out_regs_flags() : error(false), sector_count(false), lba_low(false), lba_mid(false), lba_high(false), device(false), status(false) { } }; /// ATA pass through input parameters struct ata_cmd_in { ata_in_regs_48bit in_regs; ///< Input registers ata_out_regs_flags out_needed; ///< True if output register value needed enum { no_data = 0, data_in, data_out } direction; ///< I/O direction void * buffer; ///< Pointer to data buffer unsigned size; ///< Size of buffer /// Prepare for 28-bit DATA IN command void set_data_in(void * buf, unsigned nsectors) { buffer = buf; in_regs.sector_count = nsectors; direction = data_in; size = nsectors * 512; } /// Prepare for 28-bit DATA OUT command void set_data_out(const void * buf, unsigned nsectors) { buffer = const_cast(buf); in_regs.sector_count = nsectors; direction = data_out; size = nsectors * 512; } /// Prepare for 48-bit DATA IN command void set_data_in_48bit(void * buf, unsigned nsectors) { buffer = buf; // Note: This also sets 'in_regs.is_48bit_cmd()' in_regs.sector_count_16 = nsectors; direction = data_in; size = nsectors * 512; } ata_cmd_in(); }; /// ATA pass through output parameters struct ata_cmd_out { ata_out_regs_48bit out_regs; ///< Output registers ata_cmd_out(); }; /// ATA device access class ata_device : virtual public /*extends*/ smart_device { public: /// ATA pass through. /// Return false on error. /// Must be implemented in derived class. virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) = 0; /// ATA pass through without output registers. /// Return false on error. /// Calls ata_pass_through(in, dummy), cannot be reimplemented. bool ata_pass_through(const ata_cmd_in & in); /// Return true if OS caches ATA identify sector. /// Default implementation returns false. virtual bool ata_identify_is_cached() const; protected: /// Flags for ata_cmd_is_supported(). enum { supports_data_out = 0x01, // PIO DATA OUT supports_smart_status = 0x02, // read output registers for SMART STATUS only supports_output_regs = 0x04, // read output registers for all commands supports_multi_sector = 0x08, // more than one sector (1 DRQ/sector variant) supports_48bit_hi_null = 0x10, // 48-bit commands with null high bytes only supports_48bit = 0x20, // all 48-bit commands }; /// Check command input parameters. /// Return false if required features are not implemented. /// Calls set_err(...) accordingly. bool ata_cmd_is_supported(const ata_cmd_in & in, unsigned flags, const char * type = 0); /// Check command input parameters (old version). // TODO: Remove if no longer used. bool ata_cmd_is_ok(const ata_cmd_in & in, bool data_out_support = false, bool multi_sector_support = false, bool ata_48bit_support = false) { return ata_cmd_is_supported(in, (data_out_support ? supports_data_out : 0) | supports_output_regs | (multi_sector_support ? supports_multi_sector : 0) | (ata_48bit_support ? supports_48bit : 0)); } /// Hide/unhide ATA interface. void hide_ata(bool hide = true) { m_ata_ptr = (!hide ? this : 0); } /// Default constructor, registers device as ATA. ata_device() : smart_device(never_called) { hide_ata(false); } }; ///////////////////////////////////////////////////////////////////////////// // SCSI specific interface struct scsi_cmnd_io; /// SCSI device access class scsi_device : virtual public /*extends*/ smart_device { public: /// SCSI pass through. /// Returns false on error. virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0; // Call scsi_pass_through and check sense. bool scsi_pass_through_and_check(scsi_cmnd_io * iop, const char * msg = ""); /// Always try READ CAPACITY(10) (rcap10) first but once we know /// rcap16 is needed, use it instead. void set_rcap16_first() { rcap16_first = true; } bool use_rcap16() const { return rcap16_first; } protected: /// Hide/unhide SCSI interface. void hide_scsi(bool hide = true) { m_scsi_ptr = (!hide ? this : 0); } /// Default constructor, registers device as SCSI. scsi_device() : smart_device(never_called), rcap16_first(false) { hide_scsi(false); } private: bool rcap16_first; }; ///////////////////////////////////////////////////////////////////////////// // NVMe specific interface /// NVMe pass through input parameters struct nvme_cmd_in { unsigned char opcode; ///< Opcode (CDW0 07:00) unsigned nsid; ///< Namespace ID unsigned cdw10, cdw11, cdw12, cdw13, cdw14, cdw15; ///< Cmd specific void * buffer; ///< Pointer to data buffer unsigned size; ///< Size of buffer enum { no_data = 0x0, data_out = 0x1, data_in = 0x2, data_io = 0x3 }; /// Get I/O direction from opcode unsigned char direction() const { return (opcode & 0x3); } // Prepare for DATA IN command void set_data_in(unsigned char op, void * buf, unsigned sz) { opcode = op; if (direction() != data_in) throw std::logic_error("invalid opcode for DATA IN"); buffer = buf; size = sz; } nvme_cmd_in() : opcode(0), nsid(0), cdw10(0), cdw11(0), cdw12(0), cdw13(0), cdw14(0), cdw15(0), buffer(0), size(0) { } }; /// NVMe pass through output parameters struct nvme_cmd_out { unsigned result; ///< Command specific result (DW0) unsigned short status; ///< Status Field (DW3 31:17) bool status_valid; ///< true if status is valid nvme_cmd_out() : result(0), status(0), status_valid(false) { } }; /// NVMe device access class nvme_device : virtual public /*extends*/ smart_device { public: /// NVMe pass through. /// Return false on error. virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) = 0; /// Get namespace id. unsigned get_nsid() const { return m_nsid; } protected: /// Hide/unhide NVMe interface. void hide_nvme(bool hide = true) { m_nvme_ptr = (!hide ? this : 0); } /// Constructor requires namespace ID, registers device as NVMe. explicit nvme_device(unsigned nsid) : smart_device(never_called), m_nsid(nsid) { hide_nvme(false); } /// Set namespace id. /// Should be called in open() function if get_nsid() returns 0. void set_nsid(unsigned nsid) { m_nsid = nsid; } /// Set last error number and message if pass-through returns NVMe error status. /// Returns false always to allow use as a return expression. bool set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg = 0); private: unsigned m_nsid; }; ///////////////////////////////////////////////////////////////////////////// /// Smart pointer class for device pointers template class any_device_auto_ptr { public: typedef Dev device_type; /// Construct from optional pointer to device /// and optional pointer to base device. explicit any_device_auto_ptr(device_type * dev = 0, smart_device * base_dev = 0) : m_dev(dev), m_base_dev(base_dev) { } /// Destructor deletes device object. ~any_device_auto_ptr() throw() { reset(); } /// Assign a new pointer. /// Throws if a pointer is already assigned. void operator=(device_type * dev) { if (m_dev) fail(); m_dev = dev; } /// Delete device object and clear the pointer. void reset() { if (m_dev) { if (m_base_dev && m_dev->owns(m_base_dev)) m_dev->release(m_base_dev); delete m_dev; m_dev = 0; } } /// Return the pointer and release ownership. device_type * release() { device_type * dev = m_dev; m_dev = 0; return dev; } /// Replace the pointer. /// Used to call dev->autodetect_open(). void replace(device_type * dev) { m_dev = dev; } /// Return the pointer. device_type * get() const { return m_dev; } /// Pointer dereferencing. device_type & operator*() const { return *m_dev; } /// Pointer dereferencing. device_type * operator->() const { return m_dev; } /// For (ptr != 0) check. operator bool() const { return !!m_dev; } /// For (ptr == 0) check. bool operator !() const { return !m_dev; } private: device_type * m_dev; smart_device * m_base_dev; void fail() const { throw std::logic_error("any_device_auto_ptr: wrong usage"); } // Prevent copy/assignment any_device_auto_ptr(const any_device_auto_ptr &); void operator=(const any_device_auto_ptr &); }; typedef any_device_auto_ptr smart_device_auto_ptr; typedef any_device_auto_ptr ata_device_auto_ptr; typedef any_device_auto_ptr scsi_device_auto_ptr; typedef any_device_auto_ptr nvme_device_auto_ptr; ///////////////////////////////////////////////////////////////////////////// // smart_device_list /// List of devices for DEVICESCAN class smart_device_list { // Construction public: smart_device_list() { } ~smart_device_list() throw() { for (unsigned i = 0; i < m_list.size(); i++) delete m_list[i]; } // Attributes unsigned size() const { return m_list.size(); } // Operations void clear() { for (unsigned i = 0; i < m_list.size(); i++) delete m_list[i]; m_list.clear(); } void push_back(smart_device * dev) { m_list.push_back(dev); } void push_back(smart_device_auto_ptr & dev) { m_list.push_back(dev.get()); dev.release(); } smart_device * at(unsigned i) { return m_list.at(i); } const smart_device * at(unsigned i) const { return m_list.at(i); } smart_device * release(unsigned i) { smart_device * dev = m_list.at(i); m_list[i] = 0; return dev; } void append(smart_device_list & devlist) { for (unsigned i = 0; i < devlist.size(); i++) { smart_device * dev = devlist.at(i); if (!dev) continue; push_back(dev); devlist.m_list.at(i) = 0; } } // Implementation private: std::vector m_list; // Prevent copy/assignment smart_device_list(const smart_device_list &); void operator=(const smart_device_list &); }; /// List of types for DEVICESCAN typedef std::vector smart_devtype_list; ///////////////////////////////////////////////////////////////////////////// // smart_interface /// The platform interface abstraction class smart_interface { public: /// Initialize platform interface and register with smi(). /// Must be implemented by platform module and register interface with set() static void init(); smart_interface() { } virtual ~smart_interface() throw() { } /// Return info string about build host and/or OS version. /// Default implementation returns SMARTMONTOOLS_BUILD_HOST. virtual std::string get_os_version_str(); /// Return valid args for device type option/directive. /// Default implementation returns "ata, scsi, sat, usb*..." /// concatenated with result from get_valid_custom_dev_types_str(). virtual std::string get_valid_dev_types_str(); /// Return example string for program 'appname'. /// Default implementation returns empty string. /// For the migration of print_smartctl_examples(), /// function is allowed to print examples to stdout. /// TODO: Remove this hack. virtual std::string get_app_examples(const char * appname); /// Get microseconds since some unspecified starting point. /// Used only for command duration measurements in debug outputs. /// Returns -1 if unsupported. /// Default implementation uses clock_gettime(), gettimeofday() or ftime(). virtual int64_t get_timer_usec(); /// Disable/Enable system auto standby/sleep mode. /// Return false if unsupported or if system is running /// on battery. /// Default implementation returns false. virtual bool disable_system_auto_standby(bool disable); /////////////////////////////////////////////// // Last error information /// Get last error info struct. const smart_device::error_info & get_err() const { return m_err; } /// Get last error number. int get_errno() const { return m_err.no; } /// Get last error message. const char * get_errmsg() const { return m_err.msg.c_str(); } /// Set last error number and message. /// Printf()-like formatting is supported. /// Returns false always to allow use as a return expression. bool set_err(int no, const char * msg, ...) __attribute_format_printf(3, 4); /// Set last error info struct. bool set_err(const smart_device::error_info & err) { m_err = err; return false; } /// Clear last error info. void clear_err() { m_err.clear(); } /// Set last error number and default message. /// Message is retrieved from get_msg_for_errno(no). bool set_err(int no); /// Set last error number and default message to any error_info. /// Used by set_err(no). bool set_err_var(smart_device::error_info * err, int no); /// Convert error number into message, used by set_err(no). /// Default implementation returns strerror(no). virtual const char * get_msg_for_errno(int no); /////////////////////////////////////////////////////////////////////////// // Device factory: /// Return device object for device 'name' with some 'type'. /// 'type' is 0 if not specified by user. /// Return 0 on error. /// Default implementation selects between ata, scsi and custom device. virtual smart_device * get_smart_device(const char * name, const char * type); /// Fill 'devlist' with devices of some 'type' with device names /// specified by some optional 'pattern'. /// Use platform specific default if 'type' is empty or 0. /// Return false on error. /// Default implementation returns false; virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); /// Fill 'devlist' with devices of all 'types' with device names /// specified by some optional 'pattern'. /// Use platform specific default if 'types' is empty. /// Return false on error. /// Default implementation calls above function for all types /// and concatenates the results. virtual bool scan_smart_devices(smart_device_list & devlist, const smart_devtype_list & types, const char * pattern = 0); protected: /// Return standard ATA device. virtual ata_device * get_ata_device(const char * name, const char * type) = 0; /// Return standard SCSI device. virtual scsi_device * get_scsi_device(const char * name, const char * type) = 0; /// Return standard NVMe device. /// Default implementation returns 0. virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); /// Autodetect device if no device type specified. virtual smart_device * autodetect_smart_device(const char * name) = 0; /// Return device for platform specific 'type'. /// Default implementation returns 0. virtual smart_device * get_custom_smart_device(const char * name, const char * type); /// Return valid 'type' args accepted by above. /// This is called in get_valid_dev_types_str(). /// Default implementation returns empty string. virtual std::string get_valid_custom_dev_types_str(); /// Return ATA->SCSI of NVMe->SCSI filter for a SAT, SNT or USB 'type'. /// Uses get_sat_device and get_snt_device. /// Return 0 and delete 'scsidev' on error. virtual smart_device * get_scsi_passthrough_device(const char * type, scsi_device * scsidev); /// Return ATA->SCSI filter for a SAT or USB 'type'. /// Device 'scsidev' is used for SCSI access. /// Return 0 and delete 'scsidev' on error. /// Override only if platform needs special handling. virtual ata_device * get_sat_device(const char * type, scsi_device * scsidev); //{ implemented in scsiata.cpp } /// Return NVMe->SCSI filter for a SNT or USB 'type'. /// Device 'scsidev' is used for SCSI access. /// Return 0 and delete 'scsidev' on error. /// Override only if platform needs special handling. virtual nvme_device * get_snt_device(const char * type, scsi_device * scsidev); //{ implemented in scsinvme.cpp } public: /// Try to detect a SAT device behind a SCSI interface. /// Inquiry data can be passed if available. /// Return appropriate device if yes, otherwise 0. /// Override only if platform needs special handling. virtual ata_device * autodetect_sat_device(scsi_device * scsidev, const unsigned char * inqdata, unsigned inqsize); //{ implemented in scsiata.cpp } /// Get type name for USB device with known VENDOR:PRODUCT ID. /// Return name if device known and supported, otherwise 0. virtual const char * get_usb_dev_type_by_id(int vendor_id, int product_id, int version = -1); //{ implemented in scsiata.cpp } protected: /// Set interface to use, must be called from init(). static void set(smart_interface * intf) { s_instance = intf; } // Implementation private: smart_device::error_info m_err; friend smart_interface * smi(); // below static smart_interface * s_instance; ///< Pointer to the interface object. // Prevent copy/assignment smart_interface(const smart_interface &); void operator=(const smart_interface &); }; ///////////////////////////////////////////////////////////////////////////// // smi() /// Global access to the (usually singleton) smart_interface inline smart_interface * smi() { return smart_interface::s_instance; } ///////////////////////////////////////////////////////////////////////////// #endif // DEV_INTERFACE_H smartmontools-7.0/dev_legacy.cpp0000644000175000010010000002052613336335341014026 00000000000000/* * dev_legacy.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "utility.h" #include "atacmds.h" #include "scsicmds.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" #include const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 4760 2018-08-19 18:45:53Z chrfranke $" DEV_INTERFACE_H_CVSID; ///////////////////////////////////////////////////////////////////////////// // Legacy interface declarations (now commented out globally): // from utility.h: int guess_device_type(const char * dev_name); int make_device_names (char ***devlist, const char* name); int deviceopen(const char *pathname, char *type); int deviceclose(int fd); // from atacmds.h: int ata_command_interface(int device, smart_command_set command, int select, char *data); // from scsicmds.h: int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); // from smartctl.h: void print_smartctl_examples(); ///////////////////////////////////////////////////////////////////////////// namespace os { // No need to publish anything, name provided for Doxygen ///////////////////////////////////////////////////////////////////////////// /// Implement shared open/close routines with old functions. class legacy_smart_device : virtual public /*implements*/ smart_device { public: explicit legacy_smart_device(const char * mode) : smart_device(never_called), m_fd(-1), m_mode(mode) { } virtual ~legacy_smart_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: /// Return filedesc for derived classes. int get_fd() const { return m_fd; } private: int m_fd; ///< filedesc, -1 if not open. const char * m_mode; ///< Mode string for deviceopen(). }; legacy_smart_device::~legacy_smart_device() throw() { if (m_fd >= 0) ::deviceclose(m_fd); } bool legacy_smart_device::is_open() const { return (m_fd >= 0); } bool legacy_smart_device::open() { m_fd = ::deviceopen(get_dev_name(), const_cast(m_mode)); if (m_fd < 0) { set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); return false; } return true; } bool legacy_smart_device::close() { int fd = m_fd; m_fd = -1; if (::deviceclose(fd) < 0) { set_err(errno); return false; } return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement standard ATA support with old functions class legacy_ata_device : public /*implements*/ ata_device_with_command_set, public /*extends*/ legacy_smart_device { public: legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); }; legacy_ata_device::legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), legacy_smart_device("ATA") { } int legacy_ata_device::ata_command_interface(smart_command_set command, int select, char * data) { return ::ata_command_interface(get_fd(), command, select, data); } ///////////////////////////////////////////////////////////////////////////// /// Implement standard SCSI support with old functions class legacy_scsi_device : public /*implements*/ scsi_device, public /*extends*/ legacy_smart_device { public: legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual smart_device * autodetect_open(); virtual bool scsi_pass_through(scsi_cmnd_io * iop); }; legacy_scsi_device::legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "scsi", req_type), legacy_smart_device("SCSI") { } bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = ::do_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); if (status < 0) { set_err(-status); return false; } return true; } ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support smart_device * legacy_scsi_device::autodetect_open() { // Open device if (!open()) return this; // No Autodetection if device type was specified by user if (*get_req_type()) return this; // The code below is based on smartd.cpp:SCSIFilterKnown() // Get INQUIRY unsigned char req_buff[64] = {0, }; int req_len = 36; if (scsiStdInquiry(this, 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(this, req_buff, req_len)) { // device doesn't like INQUIRY commands close(); set_err(EIO, "INQUIRY failed"); return this; } } int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) return this; // Use INQUIRY to detect type // SAT or USB ? { smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); if (newdev) // NOTE: 'this' is now owned by '*newdev' return newdev; } // Nothing special found return this; } ///////////////////////////////////////////////////////////////////////////// /// Implement platform interface with old functions. class legacy_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual smart_device * autodetect_smart_device(const char * name); }; ////////////////////////////////////////////////////////////////////// std::string legacy_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) ::print_smartctl_examples(); // this prints to stdout ... return ""; // ... so don't print again. } ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type) { return new legacy_ata_device(this, name, type); } scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type) { return new legacy_scsi_device(this, name, type); } smart_device * legacy_smart_interface::autodetect_smart_device(const char * name) { switch (::guess_device_type(name)) { case CONTROLLER_ATA : return new legacy_ata_device(this, name, ""); case CONTROLLER_SCSI: return new legacy_scsi_device(this, name, ""); } // TODO: Test autodetect device here return 0; } static void free_devnames(char * * devnames, int numdevs) { if (!devnames) return; for (int i = 0; i < numdevs; i++) { if (devnames[i]) free(devnames[i]); } free(devnames); } bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /*= 0*/) { if (pattern) { set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); return false; } // Make namelists char * * atanames = 0; int numata = 0; if (!type || !strcmp(type, "ata")) { numata = ::make_device_names(&atanames, "ATA"); if (numata < 0) { set_err(ENOMEM); return false; } } char * * scsinames = 0; int numscsi = 0; if (!type || !strcmp(type, "scsi")) { numscsi = ::make_device_names(&scsinames, "SCSI"); if (numscsi < 0) { free_devnames(atanames, numata); set_err(ENOMEM); return false; } } // Add to devlist int i; if (!type) type=""; for (i = 0; i < numata; i++) { ata_device * atadev = get_ata_device(atanames[i], type); if (atadev) devlist.push_back(atadev); } free_devnames(atanames, numata); for (i = 0; i < numscsi; i++) { scsi_device * scsidev = get_scsi_device(scsinames[i], type); if (scsidev) devlist.push_back(scsidev); } free_devnames(scsinames, numscsi); return true; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static os::legacy_smart_interface the_interface; smart_interface::set(&the_interface); } smartmontools-7.0/dev_tunnelled.h0000644000175000010010000000410313402014526014203 00000000000000/* * dev_tunnelled.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEV_TUNNELLED_H #define DEV_TUNNELLED_H #define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h 4848 2018-12-05 18:30:46Z chrfranke $" #include "dev_interface.h" ///////////////////////////////////////////////////////////////////////////// // tunnelled_device_base /// Common functionality for all tunnelled_device classes. class tunnelled_device_base : virtual public /*implements*/ smart_device { protected: explicit tunnelled_device_base(smart_device * tunnel_dev); public: virtual ~tunnelled_device_base() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); virtual bool owns(const smart_device * dev) const; virtual void release(const smart_device * dev); private: smart_device * m_tunnel_base_dev; }; ///////////////////////////////////////////////////////////////////////////// // tunnelled_device /// Implement a device by tunneling through another device template class tunnelled_device : public BaseDev, public tunnelled_device_base { public: typedef TunnelDev tunnel_device_type; protected: explicit tunnelled_device(tunnel_device_type * tunnel_dev) : smart_device(smart_device::never_called), tunnelled_device_base(tunnel_dev), m_tunnel_dev(tunnel_dev) { } // For nvme_device explicit tunnelled_device(tunnel_device_type * tunnel_dev, unsigned nsid) : smart_device(smart_device::never_called), BaseDev(nsid), tunnelled_device_base(tunnel_dev), m_tunnel_dev(tunnel_dev) { } public: virtual void release(const smart_device * dev) { if (m_tunnel_dev == dev) m_tunnel_dev = 0; tunnelled_device_base::release(dev); } tunnel_device_type * get_tunnel_dev() { return m_tunnel_dev; } const tunnel_device_type * get_tunnel_dev() const { return m_tunnel_dev; } private: tunnel_device_type * m_tunnel_dev; }; #endif // DEV_TUNNELLED_H smartmontools-7.0/drivedb.h0000644000175000010010000056603013411173217013011 00000000000000/* * drivedb.h - smartmontools drive database file * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-11 Philip Williams, Bruce Allen * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Structure used to store drive database entries: * * struct drive_settings { * const char * modelfamily; * const char * modelregexp; * const char * firmwareregexp; * const char * warningmsg; * const char * presets; * }; * * The elements are used in the following ways: * * modelfamily Informal string about the model family/series of a * device. Set to "" if no info (apart from device id) * known. The entry is ignored if this string starts with * a dollar sign. Must not start with "USB:", see below. * modelregexp POSIX extended regular expression to match the model of * a device. This should never be "". * firmwareregexp POSIX extended regular expression to match a devices's * firmware. This is optional and should be "" if it is not * to be used. If it is nonempty 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. * presets String with vendor-specific attribute ('-v') and firmware * bug fix ('-F') options. Same syntax as in smartctl command * line. The user's own settings override these. * * The regular expressions for drive model and firmware must match the full * string. The effect of "^FULLSTRING$" is identical to "FULLSTRING". * The form ".*SUBSTRING.*" can be used if substring match is desired. * * The table will be searched from the start to end or until the first match, * so the order in the table is important for distinct entries that could match * the same drive. * * * Format for USB ID entries: * * modelfamily String with format "USB: DEVICE; BRIDGE" where * DEVICE is the name of the device and BRIDGE is * the name of the USB bridge. Both may be empty * if no info known. * modelregexp POSIX extended regular expression to match the USB * vendor:product ID in hex notation ("0x1234:0xabcd"). * This should never be "". * firmwareregexp POSIX extended regular expression to match the USB * bcdDevice info. Only compared during search if other * entries with same USB vendor:product ID exist. * warningmsg Not used yet. * presets String with one device type ('-d') option. * */ /* const drive_settings builtin_knowndrives[] = { */ { "$Id: drivedb.h 4868 2018-12-27 15:58:07Z chrfranke $", "-", "-", "This is a dummy entry to hold the SVN-Id of drivedb.h", "" }, { "DEFAULT", "-", "-", "Default settings", "-v 1,raw48,Raw_Read_Error_Rate " "-v 2,raw48,Throughput_Performance " "-v 3,raw16(avg16),Spin_Up_Time " "-v 4,raw48,Start_Stop_Count " "-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 6,raw48,Read_Channel_Margin,HDD " "-v 7,raw48,Seek_Error_Rate,HDD " "-v 8,raw48,Seek_Time_Performance,HDD " "-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Spin_Retry_Count,HDD " "-v 11,raw48,Calibration_Retry_Count,HDD " "-v 12,raw48,Power_Cycle_Count " "-v 13,raw48,Read_Soft_Error_Rate " // 14-174 Unknown_Attribute "-v 175,raw48,Program_Fail_Count_Chip,SSD " "-v 176,raw48,Erase_Fail_Count_Chip,SSD " "-v 177,raw48,Wear_Leveling_Count,SSD " "-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip,SSD " "-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot,SSD " "-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot,SSD " "-v 181,raw48,Program_Fail_Cnt_Total " "-v 182,raw48,Erase_Fail_Count_Total,SSD " "-v 183,raw48,Runtime_Bad_Block " "-v 184,raw48,End-to-End_Error " // 185-186 Unknown_Attribute "-v 187,raw48,Reported_Uncorrect " "-v 188,raw48,Command_Timeout " "-v 189,raw48,High_Fly_Writes,HDD " "-v 190,tempminmax,Airflow_Temperature_Cel " "-v 191,raw48,G-Sense_Error_Rate,HDD " "-v 192,raw48,Power-Off_Retract_Count " "-v 193,raw48,Load_Cycle_Count,HDD " "-v 194,tempminmax,Temperature_Celsius " "-v 195,raw48,Hardware_ECC_Recovered " "-v 196,raw16(raw16),Reallocated_Event_Count " "-v 197,raw48,Current_Pending_Sector " "-v 198,raw48,Offline_Uncorrectable " "-v 199,raw48,UDMA_CRC_Error_Count " "-v 200,raw48,Multi_Zone_Error_Rate,HDD " "-v 201,raw48,Soft_Read_Error_Rate,HDD " "-v 202,raw48,Data_Address_Mark_Errs,HDD " "-v 203,raw48,Run_Out_Cancel " "-v 204,raw48,Soft_ECC_Correction " "-v 205,raw48,Thermal_Asperity_Rate " "-v 206,raw48,Flying_Height,HDD " "-v 207,raw48,Spin_High_Current,HDD " "-v 208,raw48,Spin_Buzz,HDD " "-v 209,raw48,Offline_Seek_Performnce,HDD " // 210-219 Unknown_Attribute "-v 220,raw48,Disk_Shift,HDD " "-v 221,raw48,G-Sense_Error_Rate,HDD " "-v 222,raw48,Loaded_Hours,HDD " "-v 223,raw48,Load_Retry_Count,HDD " "-v 224,raw48,Load_Friction,HDD " "-v 225,raw48,Load_Cycle_Count,HDD " "-v 226,raw48,Load-in_Time,HDD " "-v 227,raw48,Torq-amp_Count,HDD " "-v 228,raw48,Power-off_Retract_Count " // 229 Unknown_Attribute "-v 230,raw48,Head_Amplitude,HDD " "-v 231,raw48,Temperature_Celsius " "-v 232,raw48,Available_Reservd_Space " "-v 233,raw48,Media_Wearout_Indicator,SSD " // 234-239 Unknown_Attribute "-v 240,raw24(raw8),Head_Flying_Hours,HDD " "-v 241,raw48,Total_LBAs_Written " "-v 242,raw48,Total_LBAs_Read " // 243-249 Unknown_Attribute "-v 250,raw48,Read_Error_Retry_Rate " // 251-253 Unknown_Attribute "-v 254,raw48,Free_Fall_Sensor,HDD" }, { "Swissbit C440 Industrial CompactFlash Card", // spec v1.23 found at http://www.farnell.com/datasheets/1821167.pdf // tested with SFCF4096H2BU4TO-I-MS-527-STD "SFCF(2048|4096|8192|16GB|32GB|64GB)H[0-9]BU[24]TO-(C|I)-(MS|QT|NU)-5[0-9]7-STD", "", "", "-v 196,raw24/raw24,Spare_Blocks " "-v 213,raw24/raw24,Spare_Blocks_Worst_Chip " "-v 229,raw48,Erase_Count " "-v 203,raw48,Total_ECC_Errors " "-v 232,raw48,Total_Number_of_Reads " "-v 214,raw48,Reserved_Attribute " // Spec says "to be determined" "-v 215,raw48,Current_TRIM_Percent " }, { "Apacer SSD", "(2|4|8|16|32)GB SATA Flash Drive", // tested with APSDM002G15AN-CT/SFDDA01C and SFI2101D, APSDM004G13AN-AT/SFDE001A "SF(DDA01C|I2101D|DE001A)", "", // spec found at http://wfcache.advantech.com/www/certified-peripherals/documents/96fmcff-04g-cs-ap_Datasheet.pdf "-v 160,raw48,Initial_Bad_Block_Count " "-v 161,raw48,Bad_Block_Count " "-v 162,raw48,Spare_Block_Count " "-v 163,raw48,Max_Erase_Count " "-v 164,raw48,Average_Erase_Count " "-v 165,raw48,Average_Erase_Count " // could be wrong "-v 166,raw48,Later_Bad_Block_Count " "-v 167,raw48,SSD_Protect_Mode " "-v 168,raw48,SATA_PHY_Err_Ct " }, { "Apple MacBook Air SSD", // probably Toshiba "APPLE SSD TS(064|128)E", // tested with APPLE SSD TS064E/TQAABBF0 "", "", "-v 173,raw48,Wear_Leveling_Count " // ] "-v 241,raw48,Host_Writes_GiB " // ] guessed (ticket #655) "-v 242,raw48,Host_Reades_GiB " // ] }, { "Apple SD/SM/TS...E/F/G SSDs", // SanDisk/Samsung/Toshiba? "APPLE SSD (S[DM]|TS)0?(128|256|512|768|1024)[EFG]", // tested with APPLE SSD SD256E/1021AP, SD0128F/A223321 // APPLE SSD SM768E/CXM90A1Q, SM0512F/UXM2JA1Q, TS0256F/109L0704, SM0512G/BXW1SA0Q, SM1024G/BXW1SA0Q "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 169,raw48,Unknown_Apple_Attrib " "-v 173,raw48,Wear_Leveling_Count " // ] "-v 174,raw48,Host_Reads_MiB " // ] guessed (ticket #342), S[DM]*F only "-v 175,raw48,Host_Writes_MiB " // ] //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 197,raw48,Current_Pending_Sector " //"-v 199,raw48,UDMA_CRC_Error_Count " //"-v 240,raw48,Unknown_SSD_Attribute " }, { "Crucial/Micron RealSSD C300/P300", // Marvell 88SS9174 "C300-CTFDDA[AC](064|128|256)MAG|" // tested with C300-CTFDDAC128MAG/0002, // C300-CTFDDAC064MAG/0006 "P300-MTFDDAC(050|100|200)SAL", // tested with P300-MTFDDAC100SAL/0003 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Grown_Failing_Block_Ct " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 181,raw16,Non4k_Aligned_Access " "-v 183,raw48,SATA_Iface_Downshift " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " //"-v 188,raw48,Command_Timeout " "-v 189,raw48,Factory_Bad_Block_Ct " //"-v 194,tempminmax,Temperature_Celsius " //"-v 195,raw48,Hardware_ECC_Recovered " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 202,raw48,Percent_Lifetime_Used " "-v 206,raw48,Write_Error_Rate " }, { "Crucial/Micron RealSSD m4/C400/P400", // Marvell 9176, fixed firmware "C400-MTFDDA[ACK](064|128|256|512)MAM|" // M4-CT032M4SSD3/04MH "M4-CT(032|064|128|256|512)M4SSD[123]|" // tested with M4-CT512M4SSD2/0309 "MTFDDA[AK](064|128|256|512|050|100|200|400)MA[MNR]-1[JKS]1.*", // tested with // MTFDDAK256MAR-1K1AA/MA52, MTFDDAK256MAM-1K12/08TH, // MTFDDAA064MAR-1J1AB 49Y5835 49Y5838IBM/MA49 (P400e) "030[9-Z]|03[1-Z].|0[4-Z]..|[1-Z]....*", // >= "0309" "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Grown_Failing_Block_Ct " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 181,raw16,Non4k_Aligned_Access " "-v 183,raw48,SATA_Iface_Downshift " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " //"-v 188,raw48,Command_Timeout " "-v 189,raw48,Factory_Bad_Block_Ct " //"-v 194,tempminmax,Temperature_Celsius " //"-v 195,raw48,Hardware_ECC_Recovered " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 202,raw48,Perc_Rated_Life_Used " "-v 206,raw48,Write_Error_Rate " "-v 225,raw48,Unknown_Marvell_Attr " // P400e "-v 231,raw48,Unknown_Marvell_Attr " // P400e "-v 242,raw48,Host_Reads" // P400e: 2MiB? }, { "Crucial/Micron RealSSD m4/C400", // Marvell 9176, buggy or unknown firmware "C400-MTFDDA[ACK](064|128|256|512)MAM|" // tested with C400-MTFDDAC256MAM/0002 "M4-CT(032|064|128|256|512)M4SSD[123]", // tested with M4-CT064M4SSD2/0002, // M4-CT064M4SSD2/0009, M4-CT256M4SSD3/000F "", "This drive may hang after 5184 hours of power-on time:\n" "https://www.tomshardware.com/news/Crucial-m4-Firmware-BSOD,14544.html\n" "See the following web page for firmware updates:\n" "http://www.crucial.com/usa/en/support-ssd", "-v 170,raw48,Grown_Failing_Block_Ct " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 181,raw16,Non4k_Aligned_Access " "-v 183,raw48,SATA_Iface_Downshift " "-v 189,raw48,Factory_Bad_Block_Ct " "-v 202,raw48,Perc_Rated_Life_Used " "-v 206,raw48,Write_Error_Rate" }, { "Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs", "Crucial_CT(128|256|512)MX100SSD1|"// Marvell 88SS9189, tested with Crucial_CT256MX100SSD1/MU01 "Crucial_CT(200|250|256|500|512|1000|1024)MX200SSD[1346]|" // Marvell 88SS9189, tested with // Crucial_CT500MX200SSD1/MU01, Crucial_CT1024MX200SSD1/MU01, Crucial_CT250MX200SSD3/MU01, // Crucial_CT250MX200SSD1/MU03 "Crucial_CT(275|525|750|1050|2050)MX300SSD[14]|" // Marvell 88SS1074, tested with // Crucial_CT275MX300SSD1/M0CR040, Crucial_CT525MX300SSD1/M0CR021, Crucial_CT750MX300SSD1/M0CR011, // Crucial_CT2050MX300SSD1/M0CR031 "Crucial_CT(120|240|480|960)M500SSD[134]|" // Marvell 88SS9187, tested with // Crucial_CT120M500SSD1/MU02, Crucial_CT120M500SSD3/MU02, Crucial_CT240M500SSD1/MU03, // Crucial_CT480M500SSD1/MU03, Crucial_CT960M500SSD1/MU03, Crucial_CT240M500SSD4/MU05 "Crucial_CT(128|256|512|1024)M550SSD[134]|" // tested with Crucial_CT512M550SSD3/MU01, // Crucial_CT1024M550SSD1/MU01, Crucial_CT128M550SSD4/MU02 "CT(120|240|480)BX300SSD1|" // Silicon Motion SM2258, same attributes as Marvell-based Crucial SSDs, // tested with CT240BX300SSD1/M2CR010 "CT(120|240|480|960)BX500SSD1|" // Silicon Motion SM2258XT, tested with CT120BX500SSD1/M6CR013 "CT(250|500|1000|2000)MX500SSD[14]|" // Silicon Motion SM2258, tested with CT250MX500SSD1/M3CR010 // CT500MX500SSD1/M3CR010, CT1000MX500SSD1/M3CR010, CT2000MX500SSD1/M3CR010, CT250MX500SSD4/M3CR022 "Micron_M500_MTFDDA[KTV](120|240|480|960)MAV|"// tested with Micron_M500_MTFDDAK960MAV/MU05 "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB|" // tested with Micron_M500DC_EEFDDAA120MBB/129, // Micron_M500DC_MTFDDAK800MBB/0129 "(Micron[_ ])?M500IT[_ ]MTFDDA[KTY](032|050|060|064|120|128|240|256)[MS]BD|" // tested with M500IT_MTFDDAK240MBD/MG02 "(Micron_)?M510[_-]MTFDDA[KTV](128|256)MAZ|" // tested with M510-MTFDDAK256MAZ/MU01 "MICRON_M510DC_(EE|MT)FDDAK(120|240|480|800|960)MBP|" // tested with Micron_M510DC_MTFDDAK240MBP/0005 "(Micron_)?M550[_-]MTFDDA[KTV](064|128|256|512|1T0)MAY|" // tested with M550-MTFDDAK256MAY/MU01 "Micron_M600_(EE|MT)FDDA[KTV](128|256|512|1T0)MBF[25Z]?|" // tested with Micron_M600_MTFDDAK1T0MBF/MU01 "(Micron_1100_)?MTFDDA[KV](256|512|1T0|2T0)TBN|" // Marvell 88SS1074, tested with // Micron_1100_MTFDDAK256TBN/M0MU020, MTFDDAK256TBN/M0MA020 (OEM) "Micron 1100 SATA (256G|512G|1T|2T)B", // tested with Micron 1100 SATA 256GB/M0DL022 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " "-v 5,raw48,Reallocate_NAND_Blk_Cnt " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserved_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Ave_Block-Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 180,raw48,Unused_Reserve_NAND_Blk " "-v 183,raw48,SATA_Interfac_Downshift " "-v 184,raw48,Error_Correction_Count " //"-v 187,raw48,Reported_Uncorrect " //"-v 194,tempminmax,Temperature_Celsius " "-v 195,raw48,Cumulativ_Corrected_ECC " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 202,raw48,Percent_Lifetime_Remain " "-v 206,raw48,Write_Error_Rate " "-v 210,raw48,Success_RAIN_Recov_Cnt " "-v 246,raw48,Total_Host_Sector_Write " "-v 247,raw48,Host_Program_Page_Count " "-v 248,raw48,FTL_Program_Page_Count" }, // Reference: https://www.micron.com/resource-details/feec878a-265e-49a7-8086-15137c5f9011 // TN-FD-34: 5100 SSD SMART Implementation { "Micron 5100 Pro / 5200 SSDs", "(Micron_5100_)?(EE|MT)FDDA[KV](240|480|960|1T9|3T8|7T6)T(BY|CB|CC)|" // Matches both stock and Dell OEM // tested with Micron_5100_MTFDDAK3T8TCB/D0MU410, MTFDDAK3T8TCB/D0MU410 "(Micron_5200_)?MTFDDAK(480|960|1T9|3T8|7T6)TD(C|D|N)", // tested with Micron_5200_MTFDDAK3T8TDD/D1MU505 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw48,Reallocated_Block_Count " //"-v 9,raw24(raw8),Power_On_Hours " // raw24(raw8)?? //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserved_Block_Pct " // Percentage of remaining reserved blocks available "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Avg_Block-Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " //"-v 180,raw48,Reserved_Block_Count " // absolute count of remaining reserved blocks available "-v 183,raw48,SATA_Int_Downshift_Ct " // SATA speed downshift count //"-v 184,raw48,Error_Correction_Count " //"-v 187,raw48,Reported_Uncorrect " // Number of UECC correction failures //"-v 188,raw48,Command_Timeouts " //"-v 194,tempminmax,Temperature_Celsius " // 100 - degrees C, wraps: 101 reported as 255 //"-v 195,raw48,Cumulativ_Corrected_ECC " //"-v 196,raw48,Reallocation_Event_Ct " //"-v 197,raw48,Current_Pending_Sector " // Use the raw value //"-v 198,raw48,Offline_Uncorrectable " // Use the raw value //"-v 199,raw48,UDMA_CRC_Error_Count " // Use the raw value "-v 202,raw48,Percent_Lifetime_Remain " // Remaining endurance, trips at 10% "-v 206,raw48,Write_Error_Rate " "-v 210,raw48,RAIN_Success_Recovered " // Total number of NAND pages recovered by RAIN "-v 247,raw48,Host_Program_Page_Count " "-v 248,raw48,Bckgnd_Program_Page_Cnt" }, { "Micron M500DC/M510DC Enterprise SSDs", "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB|" // tested with // Micron_M500DC_EEFDDAA120MBB/129, Micron_M500DC_MTFDDAK800MBB/0129 "MICRON_M510DC_(EE|MT)FDDAK(120|240|480|800|960)MBP", // tested with // Micron_M510DC_MTFDDAK240MBP/0005 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " "-v 5,raw48,Reallocated_Block_Count " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserved_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Ave_Block-Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 184,raw48,Error_Correction_Count " //"-v 187,raw48,Reported_Uncorrect " "-v 188,raw48,Command_Timeouts " //"-v 194,tempminmax,Temperature_Celsius " "-v 195,raw48,Cumulativ_Corrected_ECC " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 202,raw48,Percent_Lifetime_Remain " "-v 206,raw48,Write_Error_Rate " "-v 247,raw48,Host_Program_Page_Count " "-v 248,raw48,Bckgnd_Program_Page_Cnt" }, { "SandForce Driven SSDs", // Corsair Force LS with buggy firmware only "Corsair Force LS SSD", // tested with Corsair Force LS SSD/S9FM01.8 "S9FM01\\.8", "A firmware update is available for this drive.\n" "It is HIGHLY RECOMMENDED for drives with specific serial numbers.\n" "See the following web pages for details:\n" "https://www.corsair.com/en-us/force-series-ls-60gb-sata-3-6gb-s-ssd\n" "https://www.smartmontools.org/ticket/628", "-v 1,raw24/raw32,Raw_Read_Error_Rate " "-v 5,raw48,Retired_Block_Count " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " "-v 162,raw48,Unknown_SandForce_Attr " "-v 170,raw48,Reserve_Block_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Unknown_SandForce_Attr " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 181,raw48,Program_Fail_Count " //"-v 187,raw48,Reported_Uncorrect " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 196,raw16(raw16),Reallocated_Event_Count " "-v 218,raw48,Unknown_SandForce_Attr " "-v 231,raw48,SSD_Life_Left " "-v 241,raw48,Lifetime_Writes_GiB " "-v 242,raw48,Lifetime_Reads_GiB" }, { "SandForce Driven SSDs", "SandForce 1st Ed\\.|" // Demo Drive, tested with firmware 320A13F0 "ADATA SSD S(396|510|599) .?..GB|" // tested with ADATA SSD S510 60GB/320ABBF0, // ADATA SSD S599 256GB/3.1.0, 64GB/3.4.6 "ADATA SP[389]00|" // tested with ADATA SP300/5.0.2d, SP800/5.0.6c, // ADATA SP900/5.0.6 (Premier Pro, SF-2281) "ADATA SSD S[PX]900 (64|128|256|512)GB-DL2|" // tested with ADATA SSD SP900 256GB-DL2/5.0.6, // ADATA SSD SX900 512GB-DL2/5.8.2 "ADATA XM11 (128|256)GB|" // tested with ADATA XM11 128GB/5.0.1 "ATP Velocity MIV (60|120|240|480)GB|" // tested with ATP Velocity MIV 480GB/110719 "Comay BladeDrive E28 (800|1600|3200)GB|" // LSI SF-2581, tested with Comay BladeDrive E28 800GB/2.71 "Corsair CSSD-F(40|60|80|115|120|160|240)GBP?2.*|" // Corsair Force, tested with // Corsair CSSD-F40GB2/1.1, Corsair CSSD-F115GB2-A/2.1a "Corsair Voyager GTX|" // Corsair Voyager GTX/S9FM02J6 "Corsair Force ((3 |LS )?SSD|GS|GT)|" // SF-2281, tested with // Corsair Force SSD/5.05, 3 SSD/1.3.2, GT/1.3.3, GS/5.03, // Corsair Force LS SSD/S8FM06.5, S9FM01.8, S9FM02.0 "FM-25S2S-(60|120|240)GBP2|" // G.SKILL Phoenix Pro, SF-1200, tested with // FM-25S2S-240GBP2/4.2 "FTM(06|12|24|48)CT25H|" // Supertalent TeraDrive CT, tested with // FTM24CT25H/STTMP2P1 "KINGSTON SE50S37?(100|240|480)G|" // tested with KINGSTON SE50S3100G/KE1ABBF0, // KINGSTON SE50S37100G/61AABBF0 (E50) "KINGSTON SH10[03]S3(90|120|240|480)G|" // HyperX (3K), SF-2281, tested with // SH100S3240G/320ABBF0, SH103S3120G/505ABBF0 "KINGSTON SKC(300S37A|380S3)(60|120|180|240|480)G|" // KC300, SF-2281, tested with // SKC300S37A120G/KC4ABBF0, SKC380S3120G/507ABBF0 "KINGSTON SVP200S3(7A)?(60|90|120|240|480)G|" // V+ 200, SF-2281, tested with // SVP200S37A480G/502ABBF0, SVP200S390G/332ABBF0 "KINGSTON SMS200S3(30|60|120)G|" // mSATA, SF-2241, tested with SMS200S3120G/KC3ABBF0 "KINGSTON SMS450S3(32|64|128)G|" // mSATA, SF-2281, tested with SMS450S3128G/503ABBF0 "KINGSTON (SV300|SKC100|SE100)S3.*G|" // other SF-2281 "KINGSTON SHFS37A(120|240|480)G|" // HyperX Fury, SF-2281, tested with KINGSTON SHFS37A240G/608ABBF0 "KINGSTON SNS4151S316GD|" // KINGSTON SNS4151S316GD/S9FM01.6 "MKNSSDCR(45|60|90|120|180|240|360|480)GB(-(7|DX7?|MX|G2))?|" // Mushkin Chronos (7mm/Deluxe/MX/G2), // SF-2281, tested with MKNSSDCR120GB, MKNSSDCR120GB-MX/560ABBF0, MKNSSDCR480GB-DX7/603ABBF0 "MKNSSDEC(60|120|240|480|512)GB|" // Mushkin Enhanced ECO2, tested with MKNSSDEC120GB/604ABBF0 "MKNSSDAT(30|40|60|120|180|240|480)GB(-(DX|V))?|" // Mushkin Atlas (Deluxe/Value), mSATA, SF-2281, // tested with MKNSSDAT120GB-V/540ABBF0 "Mushkin MKNSSDCL(40|60|80|90|115|120|180|240|480)GB-DX2?|" // Mushkin Callisto deluxe, // SF-1200/1222, Mushkin MKNSSDCL60GB-DX/361A13F0 "MXSSD3MDSF-(60|120)G|" // MX-DS FUSION, tested with MXSSD3MDSF-60G/2.32 "OCZ[ -](AGILITY2([ -]EX)?|COLOSSUS2|ONYX2|VERTEX(2|-LE))( [123]\\..*)?|" // SF-1200, // tested with OCZ-VERTEX2/1.11, OCZ-VERTEX2 3.5/1.11 "OCZ-NOCTI|" // mSATA, SF-2100, tested with OCZ-NOCTI/2.15 "OCZ-REVODRIVE3?( X2)?|" // PCIe, SF-1200/2281, tested with // OCZ-REVODRIVE( X2)?/1.20, OCZ-REVODRIVE3 X2/2.11 "OCZ-REVODRIVE350|" "OCZ[ -](VELO|VERTEX2[ -](EX|PRO))( [123]\\..*)?|" // SF-1500, tested with // OCZ VERTEX2-PRO/1.10 (Bogus thresholds for attribute 232 and 235) "D2[CR]STK251...-....(\\.C)?|" // OCZ Deneva 2 C/R, SF-22xx/25xx, // tested with D2CSTK251M11-0240/2.08, D2CSTK251A10-0240/2.15, D2RSTK251M11-0100.C/3.22 "OCZ-(AGILITY3|SOLID3|VERTEX3( LT| MI)?)|" // SF-2200, tested with OCZ-VERTEX3/2.02, // OCZ-AGILITY3/2.11, OCZ-SOLID3/2.15, OCZ-VERTEX3 MI/2.15, OCZ-VERTEX3 LT/2.22 "OCZ Z-DRIVE R4 [CR]M8[48]|" // PCIe, SF-2282/2582, tested with OCZ Z-DRIVE R4 CM84/2.13 // (Bogus attributes under Linux) "OCZ Z-DRIVE 4500|" "OCZ-VELO DRIVE|" // VeloDrive R, PCIe, tested with OCZ-VELO DRIVE/1.33 "TALOS2|" // OCZ Talos 2 C/R, SAS (works with -d sat), 2*SF-2282, tested with TALOS2/3.20E "(APOC|DENC|DENEVA|FTNC|GFGC|MANG|MMOC|NIMC|TMSC).*|" // other OCZ SF-1200, // tested with DENCSTE251M11-0120/1.33, DENEVA PCI-E/1.33 "(DENR|DRSAK|EC188|NIMR|PSIR|TRSAK).*|" // other OCZ SF-1500 "OWC Aura Pro( 6G SSD)?|" // tested with OWC Aura Pro 6G SSD/507ABBF0, OWC Aura Pro/603ABBF0 "OWC Mercury Electra (Pro )?[36]G SSD|" // tested with // OWC Mercury Electra 6G SSD/502ABBF0, OWC Mercury Electra Pro 3G SSD/541ABBF0 "OWC Mercury E(xtreme|XTREME) Pro (6G |RE )?SSD|" // tested with // OWC Mercury Extreme Pro SSD/360A13F0, OWC Mercury EXTREME Pro 6G SSD/507ABBF0 "Patriot Pyro|" // tested with Patriot Pyro/332ABBF0 "SanDisk SDSSDX(60|120|240|480)GG25|" // SanDisk Extreme, SF-2281, tested with // SDSSDX240GG25/R201 "SanDisk SDSSDA(120|240|480)G|" // SanDisk SSD Plus, tested with SanDisk SDSSDA240G/U21010RL "SuperSSpeed S301 [0-9]*GB|" // SF-2281, tested with SuperSSpeed S301 128GB/503 "SG9XCS2D(0?50|100|200|400)GESLT|" // Smart Storage Systems XceedIOPS2, tested with // SG9XCS2D200GESLT/SA03L370 "SSD9SC(120|240|480)GED[EA]|" // PNY Prevail Elite, tested with SSD9SC120GEDA/334ABBF0 "(TX32|TX31C1|VN0.?..GCNMK).*|" // Smart Storage Systems XceedSTOR "(TX22D1|TX21B1).*|" // Smart Storage Systems XceedIOPS2 "TX52D1.*|" // Smart Storage Systems Xcel-200 "TS(64|128|256|512)GSSD[37]20|" // Transcend SSD320/720, SF-2281, tested with // TS128GSSD320, TS256GSSD720/5.2.0 "UGB(88P|99S)GC...H[BF].|" // Unigen, tested with // UGB88PGC100HF2/MP Rev2, UGB99SGC100HB3/RC Rev3 "SG9XCS(1F|2D)(50|100|200|400)GE01|" // XceedIOPS, tested with SG9XCS2D50GE01/SA03F34V "VisionTek GoDrive (60|120|240|480)GB", // tested with VisionTek GoDrive 480GB/506ABBF0 "", "", "-v 1,raw24/raw32,Raw_Read_Error_Rate " "-v 5,raw48,Retired_Block_Count " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " "-v 13,raw24/raw32,Soft_Read_Error_Rate " "-v 100,raw48,Gigabytes_Erased " "-v 162,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0 "-v 170,raw48,Reserve_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0 "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 177,raw48,Wear_Range_Delta " "-v 181,raw48,Program_Fail_Count " "-v 182,raw48,Erase_Fail_Count " "-v 184,raw48,IO_Error_Detect_Code_Ct " //"-v 187,raw48,Reported_Uncorrect " "-v 189,tempminmax,Airflow_Temperature_Cel " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " "-v 195,raw24/raw32,ECC_Uncorr_Error_Count " //"-v 196,raw16(raw16),Reallocated_Event_Count " "-v 198,raw24/raw32:210zr54,Uncorrectable_Sector_Ct " // KINGSTON SE100S3100G/510ABBF0 "-v 199,raw48,SATA_CRC_Error_Count " "-v 201,raw24/raw32,Unc_Soft_Read_Err_Rate " "-v 204,raw24/raw32,Soft_ECC_Correct_Rate " "-v 218,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0 "-v 230,raw48,Life_Curve_Status " "-v 231,raw48,SSD_Life_Left " //"-v 232,raw48,Available_Reservd_Space " "-v 233,raw48,SandForce_Internal " "-v 234,raw48,SandForce_Internal " "-v 235,raw48,SuperCap_Health " "-v 241,raw48,Lifetime_Writes_GiB " "-v 242,raw48,Lifetime_Reads_GiB" }, { "StorFly CFast SATA 6Gbps SSDs", // http://datasheet.octopart.com/VSFCS2CC060G-100-Virtium-datasheet-82287733.pdf // tested with StorFly VSFCS2CC060G-100/0409-000 "StorFly VSFCS2C[CI](016|030|060|120|240)G-...", // C - commercial, I industrial "", "", "-v 192,raw48,Unsafe_Shutdown_Count " "-v 160,raw48,Uncorrectable_Error_Cnt " // 0729 - remaining in block life. In 0828 remaining is normalized to 100% then decreases "-v 161,raw48,Spares_Remaining " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 169,raw48,Lifetime_Remaining% " "-v 248,raw48,Lifetime_Remaining% " // later then 0409 FW. "-v 249,raw48,Spares_Remaining_Perc " // later then 0409 FW. }, { "Phison Driven SSDs", // see MKP_521_Phison_SMART_attribute.pdf "KINGSTON SEDC400S37(400|480|800|960|1600|1800)G|" // DC400, tested with // KINGSTON SEDC400S37480G/SAFM02.[GH], KINGSTON SEDC400S37960G/SAFM32.I "KINGSTON SUV300S37A(120|240|480)G|" // UV300 SSD, tested with KINGSTON SUV300S37A120G/SAFM11.K "KINGSTON SKC310S3B?7A960G|" // SSDNow KC310, KINGSTON SKC310S37A960G/SAFM00.r "KINGSTON SKC400S37(128G|256G|512G|1T)|" // SSDNow KC400, KINGSTON SKC400S37128G "KINGSTON SV310S3(7A|D7|N7A|B7A)960G|" // SSDNow V310 "PNY CS(13|22)11 (120|240|480|960)GB SSD|" // tested with PNY CS1311 120GB SSD/CS131122, // PNY CS2211 240GB SSD/CS221016 "SSD Smartbuy (60|120|240)GB|" // SSD Smartbuy 240GB/SBFM91.1 "KINGSTON SHSS3B?7A(120|240|480|960)G|" // HyperX Savage "KINGSTON ?SA400S37(120|240|480|960)G", // Kingston A400 SSD, Phison S11 or // Silicon Motion controller (see ticket #801), tested with // KINGSTON SA400S37240G/SBFK10D7, KINGSTON SA400S37120G/SBFK71E0, */SBFKB1D1 // KINGSTON SA400S37480G/SBFK10D7 (two spaces) "", "", //"-v 1,raw48,Raw_Read_Error_Rate " "-v 2,raw48,Not_In_Use " "-v 3,raw48,Not_In_Use " "-v 5,raw48,Not_In_Use " "-v 7,raw48,Not_In_Use " "-v 8,raw48,Not_In_Use " //"-v 9,raw24(raw8),Power_On_Hours " "-v 5,raw48,Retired_Block_Count " //"-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Not_In_Use " //"-v 12,raw48,Power_Cycle_Count " "-v 168,raw48,SATA_Phy_Error_Count " "-v 170,raw24/raw24:z54z10,Bad_Blk_Ct_Erl/Lat " // Early bad block/Later bad block "-v 173,raw16(avg16),MaxAvgErase_Ct " "-v 175,raw48,Not_In_Use " "-v 183,raw48,Unknown_Attribute " //"-v 187,raw48,Reported_Uncorrect " "-v 192,raw48,Unsafe_Shutdown_Count " //"-v 194,tempminmax,Temperature_Celsius " "-v 196,raw48,Not_In_Use " "-v 197,raw48,Not_In_Use " "-v 199,raw48,CRC_Error_Count " "-v 218,raw48,CRC_Error_Count " "-v 231,raw48,SSD_Life_Left " "-v 233,raw48,Flash_Writes_GiB " "-v 240,raw48,Not_In_Use " "-v 241,raw48,Lifetime_Writes_GiB " "-v 242,raw48,Lifetime_Reads_GiB " "-v 244,raw48,Average_Erase_Count " "-v 245,raw48,Max_Erase_Count " "-v 246,raw48,Total_Erase_Count " }, // this is a copy of the Phison bases record for the OEM drives with a very // weak information in the model. Detection is based on Firmware. { "Phison Driven OEM SSDs", // see MKP_521_Phison_SMART_attribute.pdf "GOODRAM|" // tested with GOODRAM CX200 (GOODRAM/SAFM12.2) "INTENSO|" // tested with Intenso SSD SATA III Top (INTENSO/S9FM02.6, .../SAFM01.6) "SATA SSD", // tested with Supermicro SSD-DM032-PHI (SATA SSD/S9FM02.1), // PC Engines msata16d (SATA SSD/S9FM02.3), FoxLine flssd240x4s(SATA SSD/SBFM10.5) "S9FM02\\.[136]|SAFM01\\.6|SAFM12\\.2|SBFM10\\.5|SBFM91\\.2", "", //"-v 1,raw48,Raw_Read_Error_Rate " "-v 2,raw48,Not_In_Use " "-v 3,raw48,Not_In_Use " "-v 5,raw48,Not_In_Use " "-v 7,raw48,Not_In_Use " "-v 8,raw48,Not_In_Use " //"-v 9,raw24(raw8),Power_On_Hours " "-v 5,raw48,Retired_Block_Count " //"-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Not_In_Use " //"-v 12,raw48,Power_Cycle_Count " "-v 168,raw48,SATA_Phy_Error_Count " "-v 170,raw24/raw24:z54z10,Bad_Blk_Ct_Erl/Lat " // Early bad block/Later bad block "-v 173,raw16(avg16),MaxAvgErase_Ct " "-v 175,raw48,Not_In_Use " "-v 183,raw48,Unknown_Attribute " //"-v 187,raw48,Reported_Uncorrect " "-v 192,raw48,Unsafe_Shutdown_Count " //"-v 194,tempminmax,Temperature_Celsius " "-v 196,raw48,Not_In_Use " "-v 197,raw48,Not_In_Use " "-v 199,raw48,CRC_Error_Count " "-v 218,raw48,CRC_Error_Count " "-v 231,raw48,SSD_Life_Left " "-v 233,raw48,Flash_Writes_GiB " "-v 240,raw48,Not_In_Use " "-v 241,raw48,Lifetime_Writes_GiB " "-v 242,raw48,Lifetime_Reads_GiB " "-v 244,raw48,Average_Erase_Count " "-v 245,raw48,Max_Erase_Count " "-v 246,raw48,Total_Erase_Count " }, { "Indilinx Barefoot based SSDs", "Corsair CSSD-V(32|60|64|128|256)GB2|" // Corsair Nova, tested with Corsair CSSD-V32GB2/2.2 "Corsair CMFSSD-(32|64|128|256)D1|" // Corsair Extreme, tested with Corsair CMFSSD-128D1/1.0 "CRUCIAL_CT(64|128|256)M225|" // tested with CRUCIAL_CT64M225/1571 "G.SKILL FALCON (64|128|256)GB SSD|" // tested with G.SKILL FALCON 128GB SSD/2030 "OCZ[ -](AGILITY|ONYX|VERTEX( 1199|-TURBO| v1\\.10)?)|" // tested with // OCZ-ONYX/1.6, OCZ-VERTEX 1199/00.P97, OCZ-VERTEX/1.30, OCZ VERTEX-TURBO/1.5, OCZ-VERTEX v1.10/1370 "Patriot[ -]Torqx.*|" "RENICE Z2|" // tested with RENICE Z2/2030 "STT_FT[MD](28|32|56|64)GX25H|" // Super Talent Ultradrive GX, tested with STT_FTM64GX25H/1916 "TS(18|25)M(64|128)MLC(16|32|64|128|256|512)GSSD|" // ASAX Leopard Hunt II, tested with TS25M64MLC64GSSD/0.1 "FM-25S2I-(64|128)GBFII|" // G.Skill FALCON II, tested with FM-25S2I-64GBFII "TS(60|120)GSSD25D-M", // Transcend Ultra SSD (SATA II), see also Ticket #80 "", "", "-v 1,raw64 " // Raw_Read_Error_Rate "-v 9,raw64 " // Power_On_Hours "-v 12,raw64 " // Power_Cycle_Count "-v 184,raw64,Initial_Bad_Block_Count " "-v 195,raw64,Program_Failure_Blk_Ct " "-v 196,raw64,Erase_Failure_Blk_Ct " "-v 197,raw64,Read_Failure_Blk_Ct " "-v 198,raw64,Read_Sectors_Tot_Ct " "-v 199,raw64,Write_Sectors_Tot_Ct " "-v 200,raw64,Read_Commands_Tot_Ct " "-v 201,raw64,Write_Commands_Tot_Ct " "-v 202,raw64,Error_Bits_Flash_Tot_Ct " "-v 203,raw64,Corr_Read_Errors_Tot_Ct " "-v 204,raw64,Bad_Block_Full_Flag " "-v 205,raw64,Max_PE_Count_Spec " "-v 206,raw64,Min_Erase_Count " "-v 207,raw64,Max_Erase_Count " "-v 208,raw64,Average_Erase_Count " "-v 209,raw64,Remaining_Lifetime_Perc " "-v 210,raw64,Indilinx_Internal " "-v 211,raw64,SATA_Error_Ct_CRC " "-v 212,raw64,SATA_Error_Ct_Handshake " "-v 213,raw64,Indilinx_Internal" }, { "Indilinx Barefoot_2/Everest/Martini based SSDs", "OCZ VERTEX[ -]PLUS|" // tested with OCZ VERTEX-PLUS/3.55, OCZ VERTEX PLUS/3.55 "OCZ-VERTEX PLUS R2|" // Barefoot 2, tested with OCZ-VERTEX PLUS R2/1.2 "OCZ-PETROL|" // Everest 1, tested with OCZ-PETROL/3.12 "OCZ-AGILITY4|" // Everest 2, tested with OCZ-AGILITY4/1.5.2 "OCZ-VERTEX4", // Everest 2, tested with OCZ-VERTEX4/1.5 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 232,raw48,Lifetime_Writes " // LBA? //"-v 233,raw48,Media_Wearout_Indicator" }, { "Indilinx Barefoot 3 based SSDs", "OCZ-VECTOR(1[58]0)?|" // tested with OCZ-VECTOR/1.03, OCZ-VECTOR150/1.2, OCZ-VECTOR180 "OCZ-VERTEX4[56]0A?|" // Barefoot 3 M10, tested with OCZ-VERTEX450/1.0, OCZ-VERTEX460/1.0, VERTEX460A "OCZ-SABER1000|" "OCZ-ARC100|" "Radeon R7", // Barefoot 3 M00, tested with Radeon R7/1.00 "", "", "-v 5,raw48,Runtime_Bad_Block " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 171,raw48,Avail_OP_Block_Count " "-v 174,raw48,Pwr_Cycle_Ct_Unplanned " "-v 187,raw48,Total_Unc_NAND_Reads " "-v 195,raw48,Total_Prog_Failures " "-v 196,raw48,Total_Erase_Failures " "-v 197,raw48,Total_Unc_Read_Failures " "-v 198,raw48,Host_Reads_GiB " "-v 199,raw48,Host_Writes_GiB " "-v 205,raw48,Max_Rated_PE_Count " "-v 206,raw48,Min_Erase_Count " "-v 207,raw48,Max_Erase_Count " "-v 208,raw48,Average_Erase_Count " "-v 210,raw48,SATA_CRC_Error_Count " "-v 212,raw48,Pages_Requiring_Rd_Rtry " "-v 213,raw48,Snmple_Retry_Attempts " "-v 214,raw48,Adaptive_Retry_Attempts " "-v 222,raw48,RAID_Recovery_Count " "-v 224,raw48,In_Warranty " "-v 225,raw48,DAS_Polarity " "-v 226,raw48,Partial_Pfail " "-v 230,raw48,Write_Throttling " "-v 233,raw48,Remaining_Lifetime_Perc " "-v 241,raw48,Host_Writes_GiB " // M00/M10 "-v 242,raw48,Host_Reads_GiB " // M00/M10 "-v 249,raw48,Total_NAND_Prog_Ct_GiB " "-v 251,raw48,Total_NAND_Read_Ct_GiB" }, { "OCZ Intrepid 3000 SSDs", // tested with OCZ INTREPID 3600/1.4.3.6, 3800/1.4.3.0, 3700/1.5.0.4 "OCZ INTREPID 3[678]00", "", "", "-v 5,raw48,Runtime_Bad_Block " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 100,raw48,Total_Blocks_Erased " "-v 171,raw48,Avail_OP_Block_Count " "-v 174,raw48,Pwr_Cycle_Ct_Unplanned " "-v 184,raw48,Factory_Bad_Block_Count " "-v 187,raw48,Total_Unc_NAND_Reads " "-v 190,tempminmax,Temperature_Celsius " "-v 195,raw48,Total_Prog_Failures " "-v 196,raw48,Total_Erase_Failures " "-v 197,raw48,Total_Unc_Read_Failures " "-v 198,raw48,Host_Reads_GiB " "-v 199,raw48,Host_Writes_GiB " "-v 202,raw48,Total_Read_Bits_Corr_Ct " "-v 205,raw48,Max_Rated_PE_Count " "-v 206,raw48,Min_Erase_Count " "-v 207,raw48,Max_Erase_Count " "-v 208,raw48,Average_Erase_Count " "-v 210,raw48,SATA_CRC_Error_Count " "-v 211,raw48,SATA_UNC_Count " "-v 212,raw48,NAND_Reads_with_Retry " "-v 213,raw48,Simple_Rd_Rtry_Attempts " "-v 214,raw48,Adaptv_Rd_Rtry_Attempts " "-v 221,raw48,Int_Data_Path_Prot_Unc " "-v 222,raw48,RAID_Recovery_Count " "-v 230,raw48,SuperCap_Charge_Status " // 0=not charged, 1=fully charged, 2=unknown "-v 233,raw48,Remaining_Lifetime_Perc " "-v 249,raw48,Total_NAND_Prog_Ct_GiB " "-v 251,raw48,Total_NAND_Read_Ct_GiB" }, { "OCZ/Toshiba Trion SSDs", "OCZ-TRION1[05]0|" // tested with OCZ-TRION100/SAFM11.2A, TRION150/SAFZ72.2 "TOSHIBA-TR150|" // tested with TOSHIBA-TR150/SAFZ12.3 "TOSHIBA Q300( Pro\\.)?", // tested with TOSHIBA Q300 Pro./JYRA0101 "", "", //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 167,raw48,SSD_Protect_Mode " "-v 168,raw48,SATA_PHY_Error_Count " "-v 169,raw48,Bad_Block_Count " "-v 173,raw48,Erase_Count " "-v 192,raw48,Unexpect_Power_Loss_Ct " //"-v 194,tempminmax,Temperature_Celsius " "-v 241,raw48,Host_Writes" }, { "InnoDisk InnoLite SATADOM D150QV SSDs", // tested with InnoLite SATADOM D150QV-L/120319 // InnoLite SATADOM D150QV/120319 "InnoLite SATADOM D150QV.*", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 7,raw48,Seek_Error_Rate " // from InnoDisk iSMART Linux tool, useless for SSD //"-v 8,raw48,Seek_Time_Performance " //"-v 9,raw48,Power_On_Hours " //"-v 10,raw48,Spin_Retry_Count " //"-v 12,raw48,Power_Cycle_Count " "-v 168,raw48,SATA_PHY_Error_Count " "-v 170,raw16,Bad_Block_Count_New/Tot " "-v 173,raw16,Erase_Count_Max/Avg " "-v 175,raw48,Bad_Cluster_Table_Count " "-v 192,raw48,Unexpect_Power_Loss_Ct " //"-v 194,tempminmax,Temperature_Celsius " //"-v 197,raw48,Current_Pending_Sector " "-v 229,hex48,Flash_ID " "-v 235,raw16,Lat_Bad_Blk_Era/Wri/Rea " "-v 236,raw48,Unstable_Power_Count " "-v 240,raw48,Write_Head" }, { "Innodisk 1ME3/3ME/3SE SSDs", // tested with 2.5" SATA SSD 3ME/S140714, // Mini PCIeDOM 1ME3/S15604, InnoDisk Corp. - mSATA 3SE/S130710 "((1\\.8|2\\.5)\"? SATA SSD|InnoDisk Corp\\. - mSATA|Mini PCIeDOM|SATA Slim) (1ME3|3[MS]E)", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 5,raw48,Reallocated_Sector_Count " "-v 7,raw48,Seek_Error_Rate " // ? "-v 8,raw48,Seek_Time_Performance " // ? //"-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Spin_Retry_Count " // ? //"-v 12,raw48,Power_Cycle_Count " "-v 168,raw48,SATA_PHY_Error_Count " "-v 169,hex48,Unknown_Innodisk_Attr " "-v 170,raw16,Bad_Block_Count " "-v 173,raw16,Erase_Count " "-v 175,raw48,Bad_Cluster_Table_Count " "-v 176,raw48,Uncorr_RECORD_Count " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " // ] only in spec //"-v 197,raw48,Current_Pending_Sector " "-v 225,raw48,Unknown_Innodisk_Attr " "-v 229,hex48,Flash_ID " "-v 235,raw48,Later_Bad_Block " "-v 236,raw48,Unstable_Power_Count " "-v 240,raw48,Write_Head" }, { "Innodisk 3IE2/3ME2/3MG2/3SE2 SSDs", // tested with 2.5" SATA SSD 3MG2-P/M140402, // 1.8 SATA SSD 3IE2-P/M150821, 2.5" SATA SSD 3IE2-P/M150821, // SATA Slim 3MG2-P/M141114, M.2 (S80) 3MG2-P/M141114, M.2 (S42) 3SE2-P/M150821, // M.2 (S42) 3ME2/M151013 "((1\\.8|2\\.5)\"? SATA SSD|SATA Slim|M\\.2 \\(S(42|80)\\)) 3(IE|ME|MG|SE)2(-P)?", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 160,raw48,Uncorrectable_Error_Cnt " "-v 161,raw48,Number_of_Pure_Spare " "-v 163,raw48,Initial_Bad_Block_Count " "-v 164,raw48,Total_Erase_Count " "-v 165,raw48,Max_Erase_Count " "-v 166,raw48,Min_Erase_Count " "-v 167,raw48,Average_Erase_Count " "-v 168,raw48,Max_Erase_Count_of_Spec " "-v 169,raw48,Remaining_Lifetime_Perc " //"-v 175,raw48,Program_Fail_Count_Chip " //"-v 176,raw48,Erase_Fail_Count_Chip " //"-v 177,raw48,Wear_Leveling_Count " "-v 178,raw48,Runtime_Invalid_Blk_Cnt " //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 182,raw48,Erase_Fail_Count_Total " //"-v 187,raw48,Reported_Uncorrect " // ] only in spec //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 195,raw48,Hardware_ECC_Recovered " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " // ] //"-v 232,raw48,Available_Reservd_Space " "-v 233,raw48,Flash_Writes_32MiB " // ] "-v 234,raw48,Flash_Reads_32MiB " // ] "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 245,raw48,Flash_Writes_32MiB" }, { "Innodisk 3IE3/3ME3/3ME4 SSDs", // tested with 2.5" SATA SSD 3ME3/S15A19, CFast 3ME3/S15A19 // InnoDisk Corp. - mSATA 3ME3/S15A19, mSATA mini 3ME3/S15A19, M.2 (S42) 3ME3, // SATA Slim 3ME3/S15A19, SATADOM-MH 3ME3/S15A19, SATADOM-ML 3ME3/S15A19, // SATADOM-MV 3ME3/S15A19, SATADOM-SL 3ME3/S15A19, SATADOM-SV 3ME3/S15A19, // SATADOM-SL 3IE3/S151019N, 2.5" SATA SSD 3IE3/S15C14i, CFast 3IE3/S15C14i, // InnoDisk Corp. - mSATA 3IE3/S15C14i, Mini PCIeDOM 1IE3/S15C14i, // mSATA mini 3IE3/S15C14i, M.2 (S42) 3IE3/S15C14i, SATA Slim 3IE3/S15C14i, // SATADOM-SH 3IE3 V2/S15C14i, SATADOM-SL 3IE3 V2/S15A19i, SATADOM-SV 3IE3 V2/S15C14i // mSATA 3ME4/L16711, M.2 (S42) 3ME4/L16711, SATADOM-MH 3ME4/L16B01, // SATADOM-SH 3ME4/L16B01, SATADOM-SH Type C 3ME4/L16B01, SATADOM-SH Type D 3ME4/L16B01 "(2.5\" SATA SSD|CFast|InnoDisk Corp\\. - mSATA|Mini PCIeDOM|mSATA( mini)?|" "M\\.2 \\(S42\\)|SATA Slim|SATADOM-[MS][HLV]( Type [CD])?) 3([IM]E3|ME4)( V2)?", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " //"-v 3,raw16(avg16),Spin_Up_Time " "-v 5,raw48,Later_Bad_Block " "-v 7,raw48,Seek_Error_Rate " // ? "-v 8,raw48,Seek_Time_Performance " // ? //"-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Spin_Retry_Count " // ? //"-v 12,raw48,Power_Cycle_Count " "-v 163,raw48,Total_Bad_Block_Count " "-v 165,raw48,Max_Erase_Count " "-v 167,raw48,Average_Erase_Count " "-v 168,raw48,SATA_PHY_Error_Count " "-v 169,raw48,Remaining_Lifetime_Perc " "-v 170,raw48,Spare_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 175,raw48,Bad_Cluster_Table_Count " "-v 176,raw48,RANGE_RECORD_Count " //"-v 187,raw48,Reported_Uncorrect " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 197,raw48,Current_Pending_Sector " "-v 225,raw48,Data_Log_Write_Count " "-v 229,hex48,Flash_ID " "-v 232,raw48,Spares_Remaining_Perc " "-v 235,raw16,Later_Bad_Blk_Inf_R/W/E " // Read/Write/Erase "-v 240,raw48,Write_Head " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "InnoDisk iCF 9000 CompactFlash Cards", // tested with InnoDisk Corp. - iCF9000 1GB/140808, // ..., InnoDisk Corp. - iCF9000 64GB/140808 "InnoDisk Corp\\. - iCF9000 (1|2|4|8|16|32|64)GB", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 12,raw48,Power_Cycle_Count " "-v 160,raw48,Uncorrectable_Error_Cnt " "-v 161,raw48,Valid_Spare_Block_Cnt " "-v 162,raw48,Child_Pair_Count " "-v 163,raw48,Initial_Bad_Block_Count " "-v 164,raw48,Total_Erase_Count " "-v 165,raw48,Max_Erase_Count " "-v 166,raw48,Min_Erase_Count " "-v 167,raw48,Average_Erase_Count " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 195,raw48,Hardware_ECC_Recovered " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " //"-v 229,raw48,Flash_ID " // only in spec "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "Intel X25-E SSDs", "SSDSA2SH(032|064)G1.* INTEL", // G1 = first generation "", "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Intel_Internal " "-v 227,raw48,Intel_Internal " "-v 228,raw48,Intel_Internal " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator" }, { "Intel X18-M/X25-M G1 SSDs", "INTEL SSDSA[12]MH(080|160)G1.*", // G1 = first generation, 50nm "", "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Intel_Internal " "-v 227,raw48,Intel_Internal " "-v 228,raw48,Intel_Internal " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator" }, { "Intel X18-M/X25-M/X25-V G2 SSDs", // fixed firmware // tested with INTEL SSDSA2M(080|160)G2GC/2CV102J8 (X25-M) "INTEL SSDSA[12]M(040|080|120|160)G2.*", // G2 = second generation, 34nm "2CV102(J[89A-Z]|[K-Z].)", // >= "2CV102J8" "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " //"-v 184,raw48,End-to-End_Error " // G2 only "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator" }, { "Intel X18-M/X25-M/X25-V G2 SSDs", // buggy or unknown firmware // tested with INTEL SSDSA2M040G2GC/2CV102HD (X25-V) "INTEL SSDSA[12]M(040|080|120|160)G2.*", "", "This drive may require a firmware update to\n" "fix possible drive hangs when reading SMART self-test log:\n" "https://downloadcenter.intel.com/download/26491", "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes" }, { "Intel 311/313 Series SSDs", // tested with INTEL SSDSA2VP020G2/2CV102M5, // INTEL SSDSA2VP020G3/9CV10379, INTEL SSDMAEXC024G3H/9CV10379 "INTEL SSD(SA2VP|MAEXC)(020|024)G[23]H?", // SA2VP = 2.5", MAEXC = mSATA, G2 = 311, G3 = 313 "", "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserve_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "Intel 320 Series SSDs", // tested with INTEL SSDSA2CT040G3/4PC10362, // INTEL SSDSA2CW160G3/4PC10362, SSDSA2BT040G3/4PC10362, SSDSA2BW120G3A/4PC10362, // INTEL SSDSA2BW300G3D/4PC10362, SSDSA2BW160G3L/4PC1LE04, SSDSA1NW160G3/4PC10362 "INTEL SSDSA[12][BCN][WT](040|080|120|160|300|600)G3[ADL]?", // 2B = 2.5" 7mm, 2C = 2.5" 9.5mm, 1N = 1.8" microSATA "", "", "-F nologdir " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserve_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 183,raw48,SATA_Downshift_Count " // FW >= 4Px10362 //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " "-v 199,raw48,CRC_Error_Count " // FW >= 4Px10362 "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "Intel 710 Series SSDs", // tested with INTEL SSDSA2BZ[12]00G3/6PB10362 "INTEL SSDSA2BZ(100|200|300)G3", "", "", "-F nologdir " //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Reserve_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " // Missing in 710 specification from September 2011 "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " //"-v 190,tempminmax,Airflow_Temperature_Cel " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "Intel 510 Series SSDs", "INTEL SSDSC2MH(120|250)A2", "", "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator" }, { "Intel 520 Series SSDs", // tested with INTEL SSDSC2CW120A3/400i, SSDSC2BW480A3F/400i, // INTEL SSDSC2BW180A3L/LB3i "INTEL SSDSC2[BC]W(060|120|180|240|480)A3[FL]?", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " //"-v 184,raw48,End-to-End_Error " "-v 187,raw48,Uncorrectable_Error_Cnt " //"-v 192,raw48,Power-Off_Retract_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, { "Intel 525 Series SSDs", // mSATA, tested with SSDMCEAC120B3/LLLi "INTEL SSDMCEAC(030|060|090|120|180|240)B3", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " "-v 187,raw48,Uncorrectable_Error_Cnt " //"-v 190,tempminmax,Airflow_Temperature_Cel " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, { "Intel 53x and Pro 2500 Series SSDs", // SandForce SF-2281, tested with // INTEL SSDSC2BW180A4/DC12, SSDSC2BW240A4/DC12, SSDMCEAW120A4/DC33 // INTEL SSDMCEAW240A4/DC33, SSDSC2BF480A5/TG26, SSDSC2BW240H6/RG21 "INTEL SSD(MCEA|SC2B|SCKJ)[WF](056|080|120|180|240|360|480)(A4|A5|H6)", // SC2B = 2.5", MCEA = mSATA, SCKJ = M.2; A4 = 530, A5 = Pro 2500, H6 = 535 "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " "-v 187,raw48,Uncorrectable_Error_Cnt " //"-v 190,tempminmax,Airflow_Temperature_Cel " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, { "Intel 330/335 Series SSDs", // tested with INTEL SSDSC2CT180A3/300i, SSDSC2CT240A3/300i, // INTEL SSDSC2CT240A4/335t "INTEL SSDSC2CT(060|120|180|240)A[34]", // A4 = 335 Series "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,msec24hour32,Power_On_Hours_and_Msec " //"-v 12,raw48,Power_Cycle_Count " //"-v 181,raw48,Program_Fail_Cnt_Total " // ] Missing in 330 specification from April 2012 //"-v 182,raw48,Erase_Fail_Count_Total " // ] //"-v 192,raw48,Power-Off_Retract_Count " "-v 225,raw48,Host_Writes_32MiB " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 249,raw48,NAND_Writes_1GiB" }, // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-540s-series-spec.html // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-540s-series-m2-spec.html { "Intel 540 Series SSDs", // INTEL SSDSC2KW120H6/LSF036C, INTEL SSDSC2KW480H6/LSF036C "INTEL SSDSC[K2]KW(120H|180H|240H|360H|480H|010X)6", "", "", "-v 9,msec24hour32,Power_On_Hours_and_Msec " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 183,raw48,SATA_Downshift_Count " "-v 187,raw48,Uncorrectable_Error_Cnt " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " "-v 249,raw48,NAND_Writes_1GiB" }, { "Intel 730 and DC S35x0/3610/3700 Series SSDs", // tested with INTEL SSDSC2BP480G4, SSDSC2BB120G4/D2010355, // INTEL SSDSC2BB800G4T, SSDSC2BA200G3/5DV10250, SSDSC2BB080G6/G2010130, SSDSC2BX200G4/G2010110, // INTEL SSDSC2BB016T6/G2010140, SSDSC2BX016T4/G2010140, SSDSC2BB150G7/N2010101 "INTEL SSDSC(1N|2B)[ABPX]((080|100|120|150|160|200|240|300|400|480|600|800)G[3467][RT]?|(012|016)T[46])", // A = S3700, B*4 = S3500, B*6 = S3510, P = 730, X = S3610 // Dell ships drives with model of the form SSDSC2BB120G4R "", "", //"-v 3,raw16(avg16),Spin_Up_Time " //"-v 4,raw48,Start_Stop_Count " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unsafe_Shutdown_Count " "-v 175,raw16(raw16),Power_Loss_Cap_Test " "-v 183,raw48,SATA_Downshift_Count " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " "-v 190,tempminmax,Temperature_Case " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 194,tempminmax,Temperature_Internal " //"-v 197,raw48,Current_Pending_Sector " "-v 199,raw48,CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 234,raw24/raw32:04321,Thermal_Throttle " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 243,raw48,NAND_Writes_32MiB " // S3510/3610 "-F xerrorlba" // tested with SSDSC2BB600G4/D2010355 }, // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-pro-5400s-series-spec.html // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-pro-5400s-series-m2-spec.html { "Intel SSD Pro 5400s Series", // Tested with SSDSC2KF480H6/LSF036P "INTEL SSDSC[2K]KF(120H|180H|240H|360H|480H|010X)6", "", "", "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 183,raw48,SATA_Downshift_Count " "-v 187,raw48,Uncorrectable_Error_Cnt " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " "-v 249,raw48,NAND_Writes_1GiB " }, { "Intel 3710 Series SSDs", // INTEL SSDSC2BA200G4R/G201DL2B (dell) "INTEL SSDSC2BA(200G|400G|800G|012T)4.?", "", "", "-v 9,msec24hour32,Power_On_Hours_and_Msec " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 183,raw48,SATA_Downshift_Count " "-v 187,raw48,Uncorrectable_Error_Cnt " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes " "-v 234,raw24/raw32:04321,Thermal_Throttle " "-v 243,raw48,NAND_Writes_32MiB " }, { "Intel S3520 Series SSDs", // INTEL SSDSC2BB960G7/N2010112, INTEL SSDSC2BB016T7/N2010112 "INTEL SSDSC(2|K)(J|B)B(240G|480G|960G|150G|760G|800G|012T|016T)7.?", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unsafe_Shutdown_Count " "-v 175,raw16(raw16),Power_Loss_Cap_Test " "-v 183,raw48,SATA_Downshift_Count " "-v 184,raw48,End-to-End_Error_Count " "-v 187,raw48,Uncorrectable_Error_Cnt " "-v 190,tempminmax,Case_Temperature " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 194,tempminmax,Drive_Temperature " "-v 197,raw48,Pending_Sector_Count " "-v 199,raw48,CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 234,raw24/raw32:04321,Thermal_Throttle_Status " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 243,raw48,NAND_Writes_32MiB" }, { "Dell Certified Intel S3520 Series SSDs", "SSDSC(2|K)(J|B)B(240G|480G|960G|120G|760G|800G|012T|016T)7R.?", "", "", "-v 170,raw48,Available_Reservd_Space " "-v 174,raw48,Unsafe_Shutdown_Count " "-v 195,raw48,Uncorrectable_Error_Cnt " "-v 199,raw48,CRC_Error_Count " "-v 201,raw16(raw16),Power_Loss_Cap_Test " "-v 202,raw48,End_of_Life " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' "-v 233,raw48,Total_LBAs_Written " "-v 234,raw24/raw32:04321,Thermal_Throttle_Status " "-v 245,raw48,Percent_Life_Remaining" }, { "Intel S4510/S4610/S4500/S4600 Series SSDs", // INTEL SSDSC2KB480G7/SCV10100, // INTEL SSDSC2KB960G7/SCV10100, INTEL SSDSC2KB038T7/SCV10100, // INTEL SSDSC2KB038T7/SCV10121, INTEL SSDSC2KG240G7/SCV10100 "INTEL SSDSC(2K|KK)(B|G)(240G|480G|960G|019T|038T)(7|8).?", "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Available_Reservd_Space " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 174,raw48,Unsafe_Shutdown_Count " "-v 175,raw16(raw16),Power_Loss_Cap_Test " "-v 183,raw48,SATA_Downshift_Count " "-v 184,raw48,End-to-End_Error_Count " "-v 187,raw48,Uncorrectable_Error_Cnt " "-v 190,tempminmax,Drive_Temperature " "-v 192,raw48,Unsafe_Shutdown_Count " "-v 197,raw48,Pending_Sector_Count " "-v 199,raw48,CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " "-v 234,raw24/raw32:04321,Thermal_Throttle_Status " "-v 235,raw16(raw16),Power_Loss_Cap_Test " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 243,raw48,NAND_Writes_32MiB" }, { "Dell Certified Intel S4x00/D3-S4x10 Series SSDs", // INTEL SSDSC2KB480G7R/SCV1DL58, // INTEL SSDSC2KB960G7R/SCV1DL58, INTEL SSDSC2KB038T7R/SCV1DL58, // INTEL SSDSC2KB038T7R/SCV1DL58, INTEL SSDSC2KG240G7R/SCV1DL58 "SSDSC(2K|KK)(B|G)(240G|480G|960G|019T|038T)(7R|8R).?", "", "", "-v 170,raw48,Available_Reservd_Space " "-v 174,raw48,Unsafe_Shutdown_Count " "-v 195,raw48,Uncorrectable_Error_Cnt " "-v 199,raw48,CRC_Error_Count " "-v 201,raw16(raw16),Power_Loss_Cap_Test " "-v 202,raw48,End_of_Life " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024) "-v 227,raw48,Workld_Host_Reads_Perc " // Timed Workload Host Reads Percentage "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40' "-v 233,raw48,Total_LBAs_Written " "-v 234,raw24/raw32:04321,Thermal_Throttle_Status " "-v 245,raw48,Percent_Life_Remaining" }, { "Kingston branded X25-V SSDs", // fixed firmware "KINGSTON SSDNow 40GB", "2CV102(J[89A-Z]|[K-Z].)", // >= "2CV102J8" "", "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes" }, { "Kingston branded X25-V SSDs", // buggy or unknown firmware "KINGSTON SSDNow 40GB", "", "This drive may require a firmware update to\n" "fix possible drive hangs when reading SMART self-test log.\n" "To update Kingston branded drives, a modified Intel update\n" "tool must be used. Search for \"kingston 40gb firmware\".", "-v 192,raw48,Unsafe_Shutdown_Count " "-v 225,raw48,Host_Writes_32MiB " "-v 226,raw48,Workld_Media_Wear_Indic " "-v 227,raw48,Workld_Host_Reads_Perc " "-v 228,raw48,Workload_Minutes" }, { "JMicron based SSDs", // JMicron JMF60x "Kingston SSDNow V Series [0-9]*GB|" // tested with Kingston SSDNow V Series 64GB/B090522a "TS(2|4|8|16|32|64|128|192)GSSD(18|25)[MS]?-[MS]", // Transcend IDE and SATA, tested with // TS32GSSD25-M/V090331, TS32GSSD18M-M/v090331 "[BVv].*", // other Transcend SSD versions will be caught by subsequent entry "", //"-v 9,raw24(raw8),Power_On_Hours " // raw value always 0? //"-v 12,raw48,Power_Cycle_Count " //"-v 194,tempminmax,Temperature_Celsius " // raw value always 0? "-v 229,hex64:w012345r,Halt_System/Flash_ID " // Halt, Flash[7] "-v 232,hex64:w012345r,Firmware_Version_Info " // "YYMMDD", #Channels, #Banks "-v 233,hex48:w01234,ECC_Fail_Record " // Fail number, Row[3], Channel, Bank "-v 234,raw24/raw24:w01234,Avg/Max_Erase_Count " "-v 235,raw24/raw24:w01z23,Good/Sys_Block_Count" }, { "JMicron based SSDs", // JMicron JMF61x, JMF66x, JMF670 "ADATA S596 Turbo|" // tested with ADATA S596 Turbo 256GB SATA SSD (JMicron JMF616) "ADATA SP600|" // tested with ADATA SP600/2.4 (JMicron JMF661) "ADATA SP310|" // Premier Pro SP310 mSATA, JMF667, tested with ADATA SP310/3.04 "ADATA SX930|" // tested with ADATA SX930/6.8SE "APPLE SSD TS(064|128|256|512)C|" // Toshiba?, tested with APPLE SSD TS064C/CJAA0201 "KingSpec KDM-SA\\.51-008GMJ|" // tested with KingSpec KDM-SA.51-008GMJ/1.092.37 (JMF605?) "KINGSTON SNV425S2(64|128)GB|" // SSDNow V Series (2. Generation, JMF618), // tested with KINGSTON SNV425S264GB/C091126a "KINGSTON SSDNOW 30GB|" // tested with KINGSTON SSDNOW 30GB/AJXA0202 "KINGSTON SS100S2(8|16)G|" // SSDNow S100 Series, tested with KINGSTON SS100S28G/D100309a "KINGSTON SNVP325S2(64|128|256|512)GB|" // SSDNow V+ Series, tested with KINGSTON SNVP325S2128GB/AGYA0201 "KINGSTON SVP?100S2B?(64|96|128|256|512)G|" // SSDNow V100/V+100 Series, // tested with KINGSTON SVP100S296G/CJR10202, KINGSTON SV100S2256G/D110225a "KINGSTON SV200S3(64|128|256)G|" // SSDNow V200 Series, tested with KINGSTON SV200S3128G/E120506a "TOSHIBA THNS128GG4BBAA|" // Toshiba / Super Talent UltraDrive DX, // tested with Toshiba 128GB 2.5" SSD (built in MacBooks) "TOSHIBA THNSNC128GMLJ|" // tested with THNSNC128GMLJ/CJTA0202 (built in Toshiba Protege/Dynabook) "TS(8|16|32|64|128|192|256|512)GSSD25S?-(MD?|S)|" // Transcend IDE and SATA, JMF612, tested with // TS256GSSD25S-M/101028, TS32GSSD25-M/20101227 "TS(32|64|128|256)G(SSD|MSA)340", // Transcend SSD340 SATA/mSATA, JMF667/670, tested with // TS256GSSD340/SVN263, TS256GSSD340/SVN423b, TS256GMSA340/SVN263 "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " "-v 3,raw48,Unknown_JMF_Attribute " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 7,raw48,Unknown_JMF_Attribute " "-v 8,raw48,Unknown_JMF_Attribute " //"-v 9,raw24(raw8),Power_On_Hours " "-v 10,raw48,Unknown_JMF_Attribute " //"-v 12,raw48,Power_Cycle_Count " "-v 167,raw48,Unknown_JMF_Attribute " "-v 168,raw48,SATA_Phy_Error_Count " "-v 169,raw48,Unknown_JMF_Attribute " "-v 170,raw16,Bad_Block_Count " "-v 173,raw16,Erase_Count " // JMF661: different? "-v 175,raw48,Bad_Cluster_Table_Count " "-v 192,raw48,Unexpect_Power_Loss_Ct " //"-v 194,tempminmax,Temperature_Celsius " //"-v 197,raw48,Current_Pending_Sector " "-v 233,raw48,Unknown_JMF_Attribute " // FW SVN423b "-v 234,raw48,Unknown_JMF_Attribute " // FW SVN423b "-v 240,raw48,Unknown_JMF_Attribute " //"-v 241,raw48,Total_LBAs_Written " // FW SVN423b //"-v 242,raw48,Total_LBAs_Read " // FW SVN423b }, { "Plextor M3/M5/M6 Series SSDs", // Marvell 88SS9174 (M3, M5S), 88SS9187 (M5P, M5Pro), 88SS9188 (M6M/S), // tested with PLEXTOR PX-128M3/1.01, PX-128M3P/1.04, PX-256M3/1.05, PX-128M5S/1.02, PX-256M5S/1.03, // PX-128M5M/1.05, PX-128M5S/1.05, PX-128M5Pro/1.05, PX-512M5Pro/1.06, PX-256M5P/1.01, PX-128M6S/1.03 // (1.04/5 Firmware self-test log lifetime unit is bogus, possibly 1/256 hours) // PLEXTOR PX-256M6S+/1.00 "PLEXTOR PX-(64|128|256|512|768)M(3P?|5[MPS]|5Pro|6[MS])\\+?", "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Unknown_Plextor_Attrib " // M6S/1.03 "-v 171,raw48,Unknown_Plextor_Attrib " // M6S/1.03 "-v 172,raw48,Unknown_Plextor_Attrib " // M6S/1.03 "-v 173,raw48,Unknown_Plextor_Attrib " // M6S/1.03 "-v 174,raw48,Unknown_Plextor_Attrib " // M6S/1.03 //"-v 175,raw48,Program_Fail_Count_Chip " // M6S/1.03 //"-v 176,raw48,Erase_Fail_Count_Chip " // M6S/1.03 //"-v 177,raw48,Wear_Leveling_Count " //"-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip " //"-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot " // M6S/1.03 //"-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot " // M6S/1.03 //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 182,raw48,Erase_Fail_Count_Total " //"-v 183,raw48,Runtime_Bad_Block " // M6S/1.03 //"-v 184,raw48,End-to-End_Error " // M6S/1.03 //"-v 187,raw48,Reported_Uncorrect " //"-v 188,raw48,Command_Timeout " // M6S/1.03 //"-v 192,raw48,Power-Off_Retract_Count " //"-v 195,raw48,Hardware_ECC_Recovered " // MS6/1.03 //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " //"-v 232,raw48,Available_Reservd_Space " //"-v 233,raw48,Media_Wearout_Indicator " // MS6/1.03 "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB" }, { "Samsung based SSDs", "SAMSUNG SSD PM800 .*GB|" // SAMSUNG PM800 SSDs, tested with SAMSUNG SSD PM800 TH 64GB/VBM25D1Q "SAMSUNG SSD PM810 .*GB|" // SAMSUNG PM810 (470 series) SSDs, tested with // SAMSUNG SSD PM810 2.5" 128GB/AXM06D1Q "SAMSUNG SSD SM841N? (2\\.5\"? 7mm |mSATA )?(128|256|512)GB( SED)?|" // tested with // SAMSUNG SSD SM841 2.5" 7mm 256GB/DXM02D0Q, SAMSUNG SSD SM841 mSATA 512GB/DXM44D0Q, // SAMSUNG SSD SM841N 2.5 7mm 128GB/DXM03D0Q, SAMSUNG SSD SM841N mSATA 256GB SED/DXM45D6Q "SAMSUNG SSD PM851 (mSATA |M\\.2 )?(2280 )?(128|256|512)GB|" // tested with SAMSUNG SSD PM851 mSATA 128GB, // SAMSUNG SSD PM851 M.2 2280 256GB/EXT25D0Q "SAMSUNG 470 Series SSD|" // tested with SAMSUNG 470 Series SSD 64GB/AXM09B1Q "Samsung SSD 750 EVO (120|250|500)GB|" // tested with Samsung SSD 750 EVO 250GB/MAT01B6Q "SAMSUNG SSD 830 Series|" // tested with SAMSUNG SSD 830 Series 64GB/CXM03B1Q "SAMSUNG SSD PM830 .*|" // SAMSUNG SSD PM830 2.5" 7mm 128GB/CXM03D1Q "MZ7PC(512|256|128|064)HA(GH|FU|DR)-000.*|" // probably PM830, tested with SAMSUNG MZ7PC128HAFU-000L1/CXM04L1Q "Samsung SSD 840 (PRO )?Series|" // tested with Samsung SSD 840 PRO Series 128GB/DXM04B0Q, // Samsung SSD 840 Series/DXT06B0Q "Samsung SSD 8[456]0 EVO (mSATA |M\\.2 )?((120|250|500|750)G|[12]T)B|" // tested with // Samsung SSD 840 EVO (120|250|500|750)GB/EXT0AB0Q, // Samsung SSD 840 EVO (120|250)GB/EXT0BB6Q, 1TB/EXT0BB0Q, 120GB mSATA/EXT41B6Q, // Samsung SSD 850 EVO 250GB/EMT01B6Q, Samsung SSD 850 EVO M.2 250GB/EMT21B6Q, // Samsung SSD 850 EVO mSATA 120GB/EMT41B6Q, Samsung SSD 850 EVO 2TB/EMT02B6Q, // Samsung SSD 860 EVO 250GB/RVT01B6Q, Samsung SSD 860 EVO mSATA 250GB/RVT41B6Q, // Samsung SSD 860 EVO 500GB/RVT01B6Q, Samsung SSD 860 EVO mSATA 500GB/RVT41B6Q, // Samsung SSD 860 EVO mSATA 1TB/RVT41B6Q, Samsung SSD 860 EVO 2TB/RVT01B6Q "Samsung SSD 8[56]0 PRO ((128|256|512)G|1T)B|" // tested with Samsung SSD 850 PRO 128GB/EXM01B6Q, // Samsung SSD 850 PRO 1TB/EXM01B6Q, Samsung SSD 860 PRO 256GB/RVM01B6Q, // Samsung SSD 860 PRO 512GB/RVM01B6Q, Samsung SSD 860 PRO 1TB/RVM01B6Q "SAMSUNG MZ7PA256HMDR-.*|" // PM810 (470 Series), tested with SAMSUNG MZ7PA256HMDR-010H1/AXM07H1Q "Samsung SSD 845DC EVO .*|" // Samsung SSD 845DC EVO 960GB/EXT03X3Q "SAMSUNG MZ[7M]PC(032|064|128|256|512)HBCD-.*|" // PM830, tested with SAMSUNG MZMPC032HBCD-000L1/CXM12L1Q "SAMSUNG MZ7TD(128|256)HAFV-.*|" // 840 Series, tested with SAMSUNG MZ7TD256HAFV-000L7/DXT06L6Q "SAMSUNG MZMTD(128|256|512)HAGL-.*|" // PM841, tested with SAMSUNG MZMTD512HAGL-00000/DXT4200Q "SAMSUNG MZ7WD((120|240)H[AC]FV|480HAGM|960HAGP)-00003|" // SM843T Series, tested with // SAMSUNG MZ7WD120HAFV-00003/DXM85W3Q, SAMSUNG MZ7WD120HCFV-00003/DXM9203Q "SAMSUNG MZ[7N]TE(128|256|512)HMHP-.*|" // PM851, tested with SAMSUNG MZ7TE256HMHP-000L7/EXT09L6Q, // SAMSUNG MZNTE256HMHP-000H1/EXT22H0Q "SAMSUNG MZMPF(032|064)HCFV-.*|" // CM851 mSATA, tested with SAMSUNG MZMPF032HCFV-000H1/FXM42H2Q "SAMSUNG MZ7GE(240HMGR|(480|960)HMHP)-00003|" // SM853T Series, tested with // SAMSUNG MZ7GE240HMGR-00003/EXT0303Q "SAMSUNG MZ7LM(120|240|480|960|1T9|3T8)HC(JM|HP|GR|FD)-.*|" // PM863 Series, tested with // SAMSUNG MZ7LM960HCHP-0E003/GXT3003Q "SAMSUNG MZ7LM(240|480|960|1T9|3T8)HM(JP|HQ|LP)-.*|" // PM863a Series, tested with // SAMSUNG MZ7LM3T8HMLP-00005/GXT5104Q "SAMSUNG MZ7KM(120|240|480|960|1T9)H[AM](FD|GR|HP|HQ|JM)-.*|" // SM863(a), tested with // SAMSUNG MZ7KM480HAHP-0E005/GXM1003Q, SAMSUNG MZ7KM480HMHQ-00005/GXM5104Q "SAMSUNG MZ7LH(240|480|960|1T9|3T8|7T6)H[AM](HQ|JR|LT|LA)-.*|" //PM883, tested with SAMSUNG MZ7LH960HAJR-00005 "SAMSUNG MZ7KH(240|480|960|1T9|3T8)HA(HQ|JR|LS)-.*|" //SM883 "SAMSUNG MZN(LF|TY)(128|256)H[CD]HP-.*|" // CM871/871a, tested with SAMSUNG MZNLF128HCHP-000H1/FXT21H1Q, // SAMSUNG MZNTY256HDHP-000/MAT21K0Q "SAMSUNG MZ[7N]LN(128|256|512|1T0)H[ACM](GR|HP|HQ|J[HPQ]|LR)-.*|" // PM871/871a/b, tested with // SAMSUNG MZ7LN128HCHP-00000/EMT0100Q, SAMSUNG MZ7LN256HAHQ-000H1/MVT03H6Q, // SAMSUNG MZNLN256HMHQ-000H1/MAV21H3Q "SAMSUNG SSD PM871 .*|" // SAMSUNG SSD PM871 2.5 7mm 256GB/EMT02D0Q // SAMSUNG MZ7LN256HMJP-00000/MAV0100Q, SAMSUNG MZ7LN512HMJP-00000/MAV0100Q "SAMSUNG MZHPV(128|256|512)HDG(L|M)-.*|" // SM951, tested with SAMSUNG MZHPV512HDGL-00000/BXW2500Q, // SAMSUNG MZHPV128HDGM-00000 (BXW2500Q) "Samsung Portable SSD T5", // tested with Samsung Portable SSD T5 (0x04e8:0x61f5) "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 170,raw48,Unused_Rsvd_Blk_Ct_Chip " // CM871 "-v 171,raw48,Program_Fail_Count_Chip " // CM871 "-v 172,raw48,Erase_Fail_Count_Chip " // CM871 "-v 173,raw48,Wear_Leveling_Count " // CM871 "-v 174,raw48,Unexpect_Power_Loss_Ct " // CM871 //"-v 175,raw48,Program_Fail_Count_Chip " //"-v 176,raw48,Erase_Fail_Count_Chip " //"-v 177,raw48,Wear_Leveling_Count " //"-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip " //"-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot " //"-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot " //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 182,raw48,Erase_Fail_Count_Total " //"-v 183,raw48,Runtime_Bad_Block " //"-v 184,raw48,End-to-End_Error " // SM843T Series "-v 187,raw48,Uncorrectable_Error_Cnt " //"-v 190,tempminmax,Airflow_Temperature_Cel " // seems to be some sort of temperature value for 470 Series? "-v 191,raw48,Unknown_Samsung_Attr " // PM810 //"-v 194,tempminmax,Temperature_Celsius " "-v 195,raw48,ECC_Error_Rate " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 198,raw48,Offline_Uncorrectable " "-v 199,raw48,CRC_Error_Count " "-v 201,raw48,Supercap_Status " "-v 202,raw48,Exception_Mode_Status " //"-v 233,raw48,Media_Wearout_Indicator // PM851, 840 "-v 234,raw48,Unknown_Samsung_Attr " // PM851, 840 "-v 235,raw48,POR_Recovery_Count " // PM851, 830/840/850 "-v 236,raw48,Unknown_Samsung_Attr " // PM851, 840 "-v 237,raw48,Unknown_Samsung_Attr " // PM851, 840 "-v 238,raw48,Unknown_Samsung_Attr " // PM851, 840 //"-v 241,raw48,Total_LBAs_Written " //"-v 242,raw48,Total_LBAs_Read " // PM851, SM841N "-v 243,raw48,SATA_Downshift_Ct " // PM863 "-v 244,raw48,Thermal_Throttle_St " // PM863 "-v 245,raw48,Timed_Workld_Media_Wear " // PM863 "-v 246,raw48,Timed_Workld_RdWr_Ratio " // PM863 "-v 247,raw48,Timed_Workld_Timer " // PM863 "-v 249,raw48,Unknown_Samsung_Attr " // CM871a "-v 250,raw48,SATA_Iface_Downshift " // from the spec "-v 251,raw48,NAND_Writes" // PM863 }, { "Marvell based SanDisk SSDs", "SanDisk SD5SG2[0-9]*G1052E|" // X100 (88SS9174), tested with SanDisk SD5SG2256G1052E/10.04.01 "SanDisk SD6S[BF][12]M[0-9]*G(1022I?)?|" // X110/X210 (88SS9175/187?), tested with SanDisk SD6SB1M064G1022I/X231600, // SanDisk SD6SB1M256G1022I/X231600, SanDisk SD6SF1M128G1022/X231200, SanDisk SD6SB2M512G1022I/X210400 "SanDisk SD7S[BN]6S-?(128|256|512)G(1122|-1006)|" // X300 (88SS9189?), tested with // SanDisk SD7SB6S128G1122/X3310000, SanDisk SD7SN6S-512G-1006/X3511006 "SanDisk SD8S[BN]8U-?((128|256|512)G|1T00)(1122|-1006)|" // X400 (88SS1074), tested with SanDisk SD8SB8U128G1122/X4120000 "SanDisk SDSSDHP[0-9]*G|" // Ultra Plus (88SS9175), tested with SanDisk SDSSDHP128G/X23[01]6RL "SanDisk (SDSSDHII|Ultra II )[0-9]*GB?|" // Ultra II (88SS9190/88SS9189), tested with // SanDisk SDSSDHII120G/X31200RL, SanDisk Ultra II 960GB/X41100RL "SanDisk SDSSDH2(128|256)G|" // SanDisk SDSSDH2128G/X211200 "SanDisk SDSSDXPS?[0-9]*G|" // Extreme II/Pro (88SS9187), tested with SanDisk SDSSDXP480G/R1311, // SanDisk SDSSDXPS480G/X21200RL "SSD SATAIII 16GB", // SSD SATAIII 16GB/i221100 (see #923) "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 165,raw48,Total_Write/Erase_Count " "-v 166,raw48,Min_W/E_Cycle " "-v 167,raw48,Min_Bad_Block/Die " "-v 168,raw48,Maximum_Erase_Cycle " "-v 169,raw48,Total_Bad_Block " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Avg_Write/Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " //"-v 184,raw48,End-to-End_Error " //"-v 187,raw48,Reported_Uncorrect " //"-v 188,raw48,Command_Timeout " //"-v 194,tempminmax,Temperature_Celsius " "-v 199,raw48,SATA_CRC_Error " "-v 201,raw48,Lifetime_Remaining% " "-v 212,raw48,SATA_PHY_Error " "-v 230,raw48,Perc_Write/Erase_Count " "-v 232,raw48,Perc_Avail_Resrvd_Space " "-v 233,raw48,Total_NAND_Writes_GiB " "-v 234,raw48,Perc_Write/Erase_Ct_BC " "-v 241,raw48,Total_Writes_GiB " "-v 242,raw48,Total_Reads_GiB " //"-v 243,raw48,Unknown_Attribute " "-v 244,raw48,Thermal_Throttle " "-v 249,raw48,TLC_NAND_GB_Writes" }, { "SanDisk based SSDs", // see also #463 for the vendor attribute description "SanDisk iSSD P4 [0-9]*GB|" // tested with SanDisk iSSD P4 16GB/SSD 9.14 "SanDisk pSSD|" // tested with SandDisk pSSD/3 (62.7 GB, SanDisk Extreme USB3.0 SDCZ80-064G-J57, 0x0781:0x5580) "SanDisk SDSSDP[0-9]*G|" // tested with SanDisk SDSSDP064G/1.0.0, SDSSDP128G/2.0.0 "SanDisk SDSSDRC032G|" // tested with SanDisk SanDisk SDSSDRC032G/3.1.0 "SanDisk SSD i100 [0-9]*GB|" // tested with SanDisk SSD i100 8GB/11.56.04, 24GB/11.56.04 "SanDisk SSD U100 ([0-9]*GB|SMG2)|" // tested with SanDisk SSD U100 8GB/10.56.00, 256GB/10.01.02, SMG2/10.56.04 "SanDisk SSD U110 (8|16|24|32|64|128)GB|" // tested with SanDisk SSD U110 32GB/U221000 "SanDisk SDSA6MM-.*|" // tested with SanDisk SDSA6MM-016G-1006/U221006 "SanDisk SD7[SU]B[23]Q(064|128|256|512)G.*", // tested with SD7SB3Q064G1122/SD7UB3Q256G1122/SD7SB3Q128G/SD7UB2Q512G1122 "", "", //"-v 5,raw16(raw16),Reallocated_Sector_Ct " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 165,raw48,Total_Write/Erase_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Avg_Write/Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " //"-v 187,raw48,Reported_Uncorrect " "-v 212,raw48,SATA_PHY_Error " "-v 230,raw48,Perc_Write/Erase_Count " "-v 232,raw48,Perc_Avail_Resrvd_Space " "-v 234,raw48,Perc_Write/Erase_Ct_BC " //"-v 241,raw48,Total_LBAs_Written " //"-v 242,raw48,Total_LBAs_Read " "-v 244,raw48,Thermal_Throttle " }, // SDLF1DAR-480G-1HAW/ZR07RE41 // SDLF1DAR-480G-1JA1/RP41ZH06 { "Sandisk SATA Cloudspeed Max and GEN2 ESS SSDs", "SD[A-Z0-9]{2}[1-3][A-Z]{3}-?[0-9]{3}[GT]-?1[A-Z0-9]{3}", "","", "-v 13,raw48,Lifetime_UECC_Ct " "-v 32,raw48,Lifetime_Write_AmpFctr " "-v 33,raw48,Write_AmpFctr " "-v 170,raw48,Reserve_Erase_BlkCt " "-v 171,raw48,Program_Fail_Ct " "-v 172,raw48,Erase_Fail_Ct " "-v 173,raw48,Percent_Life_Used " "-v 174,raw48,Unexpect_Power_Loss " "-v 175,raw48,Lifetime_Die_Failure_Ct " "-v 177,raw48,Lifetime_Remaining% " "-v 178,raw48,SSD_LifeLeft(0.01%) " "-v 180,raw48,Undetected_Data_Err_Ct " "-v 183,raw48,LT_Link_Rate_DwnGrd_Ct " "-v 191,raw48,Clean_Shutdown_Ct " "-v 192,raw48,Unclean_Shutdown_Ct " "-v 196,raw48,Lifetime_Retried_Blk_Ct " "-v 204,raw48,Average_Block-Erase_Ct " "-v 205,raw48,Read_Retry_Enable_Ct " "-v 206,raw48,Successful_RaidRecov_Ct " "-v 207,raw48,Trimmed_Sector_Ct " "-v 211,raw48,Read_Disturb_ReallocEvt " "-v 233,raw48,Lifetime_Nand_Writes " "-v 235,raw48,Capacitor_Health " "-v 244,raw48,Therm_Throt_Activation " "-v 245,raw48,Drive_Life_Remaining% " "-v 253,raw48,SPI_Test_Remaining " }, { "Sandisk SATA CS1K GEN1 ESS SSDs", "SD[A-Z0-9]{2}[NO][A-Z0-9]{3}-?[0-9]{3}[GT]-?1[A-Z0-9]{3}", "","", "-v 1,raw48,UECC_Ct " "-v 2,raw48,Internal_File_Check " "-v 5,raw16(raw16),Retried_Blk_Ct " "-v 32,raw48,Write_Ampflication " "-v 170,raw48,Reserve_Blk_Remaining " "-v 171,raw48,Program_Fail_Ct " "-v 172,raw48,Erase_Fail_Ct " "-v 173,raw48,Drive_Life_Used% " "-v 174,raw48,Unexpect_PwrLoss_Ct " "-v 175,raw48,PwrLoss_ProtectionFail " "-v 177,raw48,DriveLife_Remaining% " "-v 178,raw48,SSD_Life_Left " "-v 180,raw48,End_to_End_Err_Detect " "-v 190,raw48,Drive_Temp_Warning " "-v 195,raw48,Uncorrectable_Err_Ct " "-v 202,raw48,Exception_Mode_Status " "-v 233,raw48,Number_Of_Write_Ct " "-v 245,raw48,DriveLife_Used% " }, { "SiliconMotion based SSDs", // SM2246EN (Transcend TS6500) "R3SL(120|240|480|960)G|" // AMD Radeon Solid State Drives, "CT(120|250|500|1000)BX100SSD1|" // Crucial BX100, tested with CT250BX100SSD1/MU02, // CT500BX100SSD1/MU02, CT1000BX100SSD1/MU02 "CT(240|480|960)BX200SSD1|" // Crucial BX200 Solid State Drive, tested with CT480BX200SSD1/MU02.6 "KingDian S400 (120|240|480)GB|" // SM2256EN, tested with KingDian S400 120GB/Q0607A "KingSpec KSD-[PS]A25\\.[1-9]-(016|032|064|128)(MS|SJ)|" // tested with KingSpec KSD-PA25.6-064MS/20140803 "T60|" // KingSpec T60, tested with T60/20151120 "TEAML5Lite3D(120G|240G|480G|1T)|" // Team Group L5Lite 3D, tested with TEAML5Lite3D240G/R0302A0 "TS((16|32|64|128|256|512)G|1T)(SSD|MSA)(370S?|420[IK]?)|" // Transcend SSD370/420 SATA/mSATA, TS6500, // tested with TS32GMSA370/20140402, TS16GMSA370/20140516, TS64GSSD370/20140516, // TS256GSSD370/N0815B, TS256GSSD370S/N1114H, TS512GSSD370S/N1114H, TS32GSSD420I/N1114H, // TS32GSSD420K/P1225CE "TS(16|32|64|128|512|256)GMTS400S?|" // TS256GMTS400 "TS(120|240)GMTS420|" // Transcend MTS420 "TS(128G|256G|512G|1T)SSD230S|" // TS128GSSD230S/P1025F8 "TS(120|240|480|960)GSSD220S|" // TS480GSSD220S/P0520AA "TS(16G|32G|64G|128G|256G|512G|1T)MTS800S?|" // MTS800, tested with TS1TMTS800/O1225H1 "TS(16|32|64)GMSA630|" // MSA630 mSATA SSD, tested with TS32GMSA630/N0113E1 "TS(32|64|128)GPSD330|" // Transcend PSD SSD, tested with TS64GPSD330/20140121 "TS(16|32|64|96|128|256)GSSD630|" // Transcend 630, tested with TS16GSSD630/N0113E1 "TS(128G|256G|512G|1T)ESD400K|" // Transcend ESD400 Portable, tested with // TS256GESD400K/R0605AS (0x2174:0x2000) "MKNSSDRE(1TB|2TB|512GB|500GB|256GB|250GB)|" // MKNSSDRE256GB/N1007C "MKNSSDTR(240|500|250|120|480|240)GB(-LT)?|" // MKNSSDTR500GB/O1126A "LITEON LMH-(128|256|512)V2M-.*|" // LITEON LMH-256V2M-11 MSATA 256GB/FM8110C "ADATA SP550", // ADATA SP550/O0803B5a "", "", //"-v 1,raw48,Raw_Read_Error_Rate " //"-v 2,raw48,Throughput_Performance " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 148,raw48,Total_SLC_Erase_Ct " "-v 149,raw48,Max_SLC_Erase_Ct " "-v 150,raw48,Min_SLC_Erase_Ct " "-v 151,raw48,Average_SLC_Erase_Ct " "-v 160,raw48,Uncorrectable_Error_Cnt " "-v 161,raw48,Valid_Spare_Block_Cnt " "-v 163,raw48,Initial_Bad_Block_Count " "-v 164,raw48,Total_Erase_Count " "-v 165,raw48,Max_Erase_Count " "-v 166,raw48,Min_Erase_Count " "-v 167,raw48,Average_Erase_Count " "-v 168,raw48,Max_Erase_Count_of_Spec " "-v 169,raw48,Remaining_Lifetime_Perc " //"-v 175,raw48,Program_Fail_Count_Chip " //"-v 176,raw48,Erase_Fail_Count_Chip " //"-v 177,raw48,Wear_Leveling_Count " "-v 178,raw48,Runtime_Invalid_Blk_Cnt " //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 182,raw48,Erase_Fail_Count_Total " //"-v 187,raw48,Reported_Uncorrect " //"-v 192,raw48,Power-Off_Retract_Count " //"-v 194,tempminmax,Temperature_Celsius " //"-v 195,raw48,Hardware_ECC_Recovered " //"-v 196,raw16(raw16),Reallocated_Event_Count " //"-v 197,raw48,Current_Pending_Sector " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 225,raw48,Host_Writes_32MiB " // FW 20140402 //"-v 232,raw48,Available_Reservd_Space " "-v 241,raw48,Host_Writes_32MiB " "-v 242,raw48,Host_Reads_32MiB " "-v 245,raw48,TLC_Writes_32MiB " // FW N0815B, N1114H "-v 246,raw48,SLC_Writes_32MiB " "-v 247,raw48,Raid_Recoverty_Ct" }, { "SMART Modular Technologies mSATA XL+ SLC SSDs", // tested with SH9MST6D16GJSI01 "SH9MST6D[0-9]*GJSI?[0-9]*", // based on http://www.smartm.com/salesLiterature/embedded/mSATA_overview.pdf "", "", // attributes info from http://www.mouser.com/ds/2/723/smartmodular_09302015_SH9MST6DxxxGJSxxx_rA[1]-770719.pdf "-v 1,raw48,Uncorrectable_ECC_Cnt " //"-v 5,raw16(raw16),Reallocated_Sector_Ct " "-v 9,raw48,Power_On_Hours " // override default raw24(raw8) format //"-v 12,raw48,Power_Cycle_Count " "-v 14,raw48,Device_Capacity_LBAs " "-v 15,raw48,User_Capacity_LBAs " // spec DecID is wrong, HexID is right "-v 16,raw48,Init_Spare_Blocks_Avail " // spec DecID is wrong, HexID is right "-v 17,raw48,Spare_Blocks_Remaining " // spec DecID is wrong, HexID is right "-v 100,raw48,Total_Erase_Count " "-v 168,raw48,SATA_PHY_Err_Ct " "-v 170,raw48,Initial_Bad_Block_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Max_Erase_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 175,raw48,Average_Erase_Count " //"-v 181,raw48,Program_Fail_Cnt_Total " //"-v 187,raw48,Reported_Uncorrect " //"-v 194,tempminmax,Temperature_Celsius " "-v 197,raw48,Not_In_Use " "-v 198,raw48,Not_In_Use " "-v 199,raw48,SATA_CRC_Error_Count " "-v 202,raw48,Perc_Rated_Life_Used " "-v 231,raw48,Perc_Rated_Life_Remain " "-v 232,raw48,Read_Fail_Count " "-v 234,raw48,Flash_Reads_LBAs " "-v 235,raw48,Flash_Writes_LBAs " "-v 241,raw48,Host_Writes_LBAs " "-v 242,raw48,Host_Reads_LBAs" // 247-248 Missing in specification from April 2015 }, { "Smart Storage Systems Xcel-10 SSDs", // based on http://www.smartm.com/files/salesLiterature/storage/xcel10.pdf "SMART A25FD-(32|64|128)GI32N", // tested with SMART A25FD-128GI32N/B9F23D4K "", "", // attributes info from http://www.adtron.com/pdf/SMART_Attributes_Xcel-10_810800014_RevB.pdf "-v 1,raw48,Not_Supported " "-v 2,raw48,Not_Supported " //"-v 9,raw24(raw8),Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 191,raw48,Not_Supported " //"-v 192,raw48,Power-Off_Retract_Count " "-v 197,raw48,ECC_Error_Count " //"-v 198,raw48,Offline_Uncorrectable " //"-v 199,raw48,UDMA_CRC_Error_Count " "-v 251,raw48,Min_Spares_Remain_Perc " // percentage of the total number of spare blocks available "-v 252,raw48,Added_Bad_Flash_Blk_Ct " // number of bad flash blocks "-v 254,raw48,Total_Erase_Blocks_Ct" // number of times the drive has erased any erase block }, { "Smart Storage Systems XceedSecure2 SSDs", "(SMART|Adtron) ([AIS]25FBS|S35FCS).*", "", "", "-v 9,sec2hour,Power_On_Hours " "-v 194,hex64,Proprietary_194" }, { "Smart Storage Systems XceedUltraX/Adtron A25FBX SSDs", "(SMART|Adtron) (A|I)25FBX.*", "", "", "-v 9,hex64,Proprietary_9 " "-v 194,hex48,Proprietary_194" }, { "Smart Storage Systems Adtron A25FB 2xN SSDs", "(SMART|Adtron) A25FB.*2.N", "", "", "-v 110,hex64,Proprietary_HWC " "-v 111,hex64,Proprietary_MP " "-v 112,hex64,Proprietary_RtR " "-v 113,hex64,Proprietary_RR " "-v 120,hex64,Proprietary_HFAll " "-v 121,hex64,Proprietary_HF1st " "-v 122,hex64,Proprietary_HF2nd " "-v 123,hex64,Proprietary_HF3rd " "-v 125,hex64,Proprietary_SFAll " "-v 126,hex64,Proprietary_SF1st " "-v 127,hex64,Proprietary_SF2nd " "-v 128,hex64,Proprietary_SF3rd " "-v 194,raw24/raw32:zvzzzw,Fractional_Temperature" }, { "Smart Storage Systems Adtron A25FB 3xN SSDs", "(SMART|Adtron) A25FB-.*3.N", "", "", "-v 9,sec2hour,Power_On_Hours " "-v 113,hex48,Proprietary_RR " "-v 130,raw48:54321,Minimum_Spares_All_Zs" //"-v 194,tempminmax,Temperature_Celsius" }, { "STEC Mach2 CompactFlash Cards", // tested with STEC M2P CF 1.0.0/K1385MS "STEC M2P CF 1.0.0", "", "", "-v 100,raw48,Erase_Program_Cycles " "-v 103,raw48,Remaining_Energy_Storg " "-v 170,raw48,Reserved_Block_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 211,raw48,Unknown_Attribute " // ] Missing in specification "-v 212,raw48,Unknown_Attribute" // ] from September 2012 }, { "Transcend CompactFlash Cards", // tested with TRANSCEND/20080820, // TS4GCF133/20100709, TS16GCF133/20100709, TS16GCF150/20110407 "TRANSCEND|TS(4|8|16)GCF(133|150)", "", "", "-v 7,raw48,Unknown_Attribute " "-v 8,raw48,Unknown_Attribute" }, { "Marvell SSD SD88SA024BA0 (SUN branded)", "MARVELL SD88SA024BA0 SUN24G 0902M0054V", "", "", "" }, { "HP 1TB SATA disk GB1000EAFJL", "GB1000EAFJL", "", "", "" }, { "HP 500GB SATA disk MM0500EANCR", "MM0500EANCR", "", "", "" }, { "HP 250GB SATA disk VB0250EAVER", "VB0250EAVER", "", "", "" }, { "IBM Deskstar 60GXP", // ER60A46A firmware "(IBM-|Hitachi )?IC35L0[12346]0AVER07.*", "ER60A46A", "", "" }, { "IBM Deskstar 60GXP", // All other firmware "(IBM-|Hitachi )?IC35L0[12346]0AVER07.*", "", "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n" "Please see http://haque.net/dtla_update/", "" }, { "IBM Deskstar 40GV & 75GXP (A5AA/A6AA firmware)", "(IBM-)?DTLA-30[57]0[123467][05].*", "T[WX][123468AG][OF]A[56]AA", "", "" }, { "IBM Deskstar 40GV & 75GXP (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://haque.net/dtla_update/", "" }, { "", // ExcelStor J240, J340, J360, J680, J880 and J8160 "ExcelStor Technology J(24|34|36|68|88|816)0", "", "", "" }, { "", // Fujitsu M1623TAU "FUJITSU M1623TAU", "", "", "-v 9,seconds" }, { "Fujitsu MHG", "FUJITSU MHG2...ATU?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHH", "FUJITSU MHH2...ATU?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHJ", "FUJITSU MHJ2...ATU?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHK", "FUJITSU MHK2...ATU?.*", "", "", "-v 9,seconds" }, { "", // Fujitsu MHL2300AT "FUJITSU MHL2300AT", "", "This drive's firmware has a harmless Drive Identity Structure\n" "checksum error bug.", "-v 9,seconds" }, { "", // MHM2200AT, MHM2150AT, MHM2100AT, MHM2060AT "FUJITSU MHM2(20|15|10|06)0AT", "", "This drive's firmware has a harmless Drive Identity Structure\n" "checksum error bug.", "-v 9,seconds" }, { "Fujitsu MHN", "FUJITSU MHN2...AT", "", "", "-v 9,seconds" }, { "", // Fujitsu MHR2020AT "FUJITSU MHR2020AT", "", "", "-v 9,seconds" }, { "", // Fujitsu MHR2040AT "FUJITSU MHR2040AT", "", // Tested on 40BA "", "-v 9,seconds -v 192,emergencyretractcyclect " "-v 198,offlinescanuncsectorct -v 200,writeerrorcount" }, { "Fujitsu MHS AT", "FUJITSU MHS20[6432]0AT( .)?", "", "", "-v 9,seconds -v 192,emergencyretractcyclect " "-v 198,offlinescanuncsectorct -v 200,writeerrorcount " "-v 201,detectedtacount" }, { "Fujitsu MHT", // tested with FUJITSU MHT2030AC/909B "FUJITSU MHT2...(AC|AH|AS|AT|BH)U?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHU", "FUJITSU MHU2...ATU?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHV", "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*", "", "", "-v 9,seconds" }, { "Fujitsu MPA..MPG", "FUJITSU MP[A-G]3...A[HTEV]U?.*", "", "", "-v 9,seconds" }, { "Fujitsu MHY BH", "FUJITSU MHY2(04|06|08|10|12|16|20|25)0BH.*", "", "", "-v 240,raw48,Transfer_Error_Rate" }, { "Fujitsu MHW AC", // tested with FUJITSU MHW2060AC/00900004 "FUJITSU MHW20(40|60)AC", "", "", "" }, { "Fujitsu MHW BH", "FUJITSU MHW2(04|06|08|10|12|16)0BH.*", "", "", "" }, { "Fujitsu MHW BJ", "FUJITSU MHW2(08|12|16)0BJ.*", "", "", "" }, { "Fujitsu MHZ BH", "FUJITSU MHZ2(04|08|12|16|20|25|32)0BH.*", "", "", "" }, { "Fujitsu MHZ BJ", "FUJITSU MHZ2(08|12|16|20|25|32)0BJ.*", "", "", "-v 9,minutes" }, { "Fujitsu MHZ BS", "FUJITSU MHZ2(12|25)0BS.*", "", "", "" }, { "Fujitsu MHZ BK", "FUJITSU MHZ2(08|12|16|25)0BK.*", "", "", "" }, { "Fujitsu MJA BH", "FUJITSU MJA2(08|12|16|25|32|40|50)0BH.*", "", "", "" }, { "", // Samsung SV4012H (known firmware) "SAMSUNG SV4012H", "RM100-08", "", "-v 9,halfminutes -F samsung" }, { "", // Samsung SV4012H (all other firmware) "SAMSUNG SV4012H", "", "May need -F samsung disabled; see manual for details.", "-v 9,halfminutes -F samsung" }, { "", // Samsung SV0412H (known firmware) "SAMSUNG SV0412H", "SK100-01", "", "-v 9,halfminutes -v 194,10xCelsius -F samsung" }, { "", // Samsung SV0412H (all other firmware) "SAMSUNG SV0412H", "", "May need -F samsung disabled; see manual for details.", "-v 9,halfminutes -v 194,10xCelsius -F samsung" }, { "", // Samsung SV1204H (known firmware) "SAMSUNG SV1204H", "RK100-1[3-5]", "", "-v 9,halfminutes -v 194,10xCelsius -F samsung" }, { "", // Samsung SV1204H (all other firmware) "SAMSUNG SV1204H", "", "May need -F samsung disabled; see manual for details.", "-v 9,halfminutes -v 194,10xCelsius -F samsung" }, { "", // SAMSUNG SV0322A tested with FW JK200-35 "SAMSUNG SV0322A", "", "", "" }, { "SAMSUNG SpinPoint V80", // tested with SV1604N/TR100-23 "SAMSUNG SV(0211|0401|0612|0802|1203|1604)N", "", "", "-v 9,halfminutes -F samsung2" }, { "", // SAMSUNG SP40A2H with RR100-07 firmware "SAMSUNG SP40A2H", "RR100-07", "", "-v 9,halfminutes -F samsung" }, { "", // SAMSUNG SP80A4H with RT100-06 firmware "SAMSUNG SP80A4H", "RT100-06", "", "-v 9,halfminutes -F samsung" }, { "", // SAMSUNG SP8004H with QW100-61 firmware "SAMSUNG SP8004H", "QW100-61", "", "-v 9,halfminutes -F samsung" }, { "SAMSUNG SpinPoint F1 DT", // tested with HD103UJ/1AA01113 "SAMSUNG HD(083G|16[12]G|25[12]H|32[12]H|50[12]I|642J|75[23]L|10[23]U)J", "", "", "" }, { "SAMSUNG SpinPoint F1 EG", // tested with HD103UI/1AA01113 "SAMSUNG HD(252H|322H|502I|642J|753L|103U)I", "", "", "" }, { "SAMSUNG SpinPoint F1 RE", // tested with HE103UJ/1AA01113 "SAMSUNG HE(252H|322H|502I|642J|753L|103U)J", "", "", "" }, { "SAMSUNG SpinPoint F2 EG", // tested with HD154UI/1AG01118 "SAMSUNG HD(502H|10[23]S|15[34]U)I", "", "", "" }, { "SAMSUNG SpinPoint F3", // tested with HD502HJ/1AJ100E4 "SAMSUNG HD(502H|754J|103S)J", "", "", "" }, { "Seagate Barracuda SpinPoint F3", // tested with ST1000DM005 HD103SJ/1AJ100E5 "ST[0-9DM]* HD(502H|754J|103S)J", "", "", "" }, { "SAMSUNG SpinPoint F3 EG", // tested with HD503HI/1AJ100E4, HD153WI/1AN10002 "SAMSUNG HD(253G|(324|503)H|754J|105S|(153|203)W)I", "", "", "" }, { "SAMSUNG SpinPoint F3 RE", // tested with HE103SJ/1AJ30001 "SAMSUNG HE(502H|754J|103S)J", "", "", "" }, { "Seagate Samsung Spinpoint F4", // tested with ST250DM001 HD256GJ/1AR10001 "ST(250|320)DM001 HD(256G|322G|323H)J", "", "", "" }, { "SAMSUNG SpinPoint F4 EG (AF)",// tested with HD204UI/1AQ10001(buggy|fixed) "SAMSUNG HD(155|204)UI", "", // 1AQ10001 "Using smartmontools or hdparm with this\n" "drive may result in data loss due to a firmware bug.\n" "****** THIS DRIVE MAY OR MAY NOT BE AFFECTED! ******\n" "Buggy and fixed firmware report same version number!\n" "See the following web pages for details:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/223571en\n" "https://www.smartmontools.org/wiki/SamsungF4EGBadBlocks", "" }, { "Seagate Samsung SpinPoint F4 EG (AF)", // later sold as Barracuda Green, // tested with ST2000DL004 HD204UI/1AQ10001 "ST2000DL004 HD204UI", "", "", "" }, { "SAMSUNG SpinPoint S250", // tested with HD200HJ/KF100-06 "SAMSUNG HD(162|200|250)HJ", "", "", "" }, { "SAMSUNG SpinPoint T133", // tested with HD300LJ/ZT100-12, HD400LJ/ZZ100-14, HD401LJ/ZZ100-15 "SAMSUNG HD(250KD|(30[01]|320|40[01])L[DJ])", "", "", "" }, { "SAMSUNG SpinPoint T166", // tested with HD252KJ/CM100-11, HD501LJ/CR100-1[01] "SAMSUNG HD(080G|160H|252K|32[01]K|403L|50[01]L)J", "", "", "-v 197,increasing" // at least HD501LJ/CR100-11 }, { "SAMSUNG SpinPoint P120", // VF100-37 firmware, tested with SP2514N/VF100-37 "SAMSUNG SP(16[01]3|2[05][01]4)[CN]", "VF100-37", "", "-F samsung3" }, { "SAMSUNG SpinPoint P120", // other firmware, tested with SP2504C/VT100-33 "SAMSUNG SP(16[01]3|2[05][01]4)[CN]", "", "May need -F samsung3 enabled; see manual for details.", "" }, { "SAMSUNG SpinPoint P80 SD", // tested with HD160JJ/ZM100-33, SAMSUNG HD080HJ/P/ZH100-34 "SAMSUNG HD(080H|120I|160J)J(/P)?", "", "", "" }, { "SAMSUNG SpinPoint P80", // BH100-35 firmware, tested with SP0842N/BH100-35 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]", "BH100-35", "", "-F samsung3" }, { "SAMSUNG SpinPoint P80", // firmware *-35 or later "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]", ".*-3[5-9]", "May need -F samsung3 enabled; see manual for details.", "" }, { "SAMSUNG SpinPoint P80", // firmware *-25...34, tested with // SP0401N/TJ100-30, SP1614C/SW100-25 and -34 "SAMSUNG SP(04[05]1|08[0124]2|12[0145]3|16[0145]4)[CN]", ".*-(2[5-9]|3[0-4])", "", "-v 9,halfminutes -v 198,increasing" }, { "SAMSUNG SpinPoint P80", // firmware *-23...24, tested with // SP0802N/TK100-23, // SP1213N/TL100-23, // SP1604N/TM100-23 and -24 "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]", ".*-2[34]", "", "-v 9,halfminutes -F samsung2" }, { "SAMSUNG SpinPoint P80", // unknown firmware "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]", "", "May need -F samsung2 or -F samsung3 enabled; see manual for details.", "" }, { "SAMSUNG SpinPoint M40/60/80", // tested with HM120IC/AN100-16, HM160JI/AD100-16 "SAMSUNG HM(0[468]0H|120I|1[026]0J)[CI]", "", "", "-v 9,halfminutes" }, { "SAMSUNG SpinPoint M5", // tested with HM160HI/HH100-12 "SAMSUNG HM(((061|080)G|(121|160)H|250J)I|160HC)", "", "", "" }, { "SAMSUNG SpinPoint M6", // tested with HM320JI/2SS00_01 M6 "SAMSUNG HM(251J|320[HJ]|[45]00L)I", "", "", "" }, { "SAMSUNG SpinPoint M7", // tested with HM500JI/2AC101C4 "SAMSUNG HM(250H|320I|[45]00J)I", "", "", "" }, { "SAMSUNG SpinPoint M7E (AF)", // tested with HM321HI/2AJ10001, HM641JI/2AJ10001 "SAMSUNG HM(161G|(251|321)H|501I|641J)I", "", "", "" }, { "Seagate Samsung SpinPoint M7E", // tested with ST640LM000 HM641JI/2AJ10001 "ST(160|250|320|500|640)LM00[01] HM[0-9]*[GHIJ]I", "", "", "" }, { "SAMSUNG SpinPoint M7U (USB)", // tested with HM252HX/2AC101C4 "SAMSUNG HM(162H|252H|322I|502J)X", "", "", "" }, { "SAMSUNG SpinPoint M8 (AF)", // tested with HN-M101MBB/2AR10001 "SAMSUNG HN-M(250|320|500|640|750|101)MBB", "", "", "" }, { "Seagate Samsung SpinPoint M8 (AF)", // tested with // ST750LM022 HN-M750MBB/2AR10001, ST320LM001 HN-M320MBB/2AR10002, // APPLE HDD ST500LM012/2BA30003 "ST(250|320|500|640|750|1000)LM0[012][124] HN-M[0-9]*MBB|" "APPLE HDD ST500LM012", "", "", "" }, { "SAMSUNG SpinPoint M8U (USB)", // tested with HN-M500XBB/2AR10001 "SAMSUNG HN-M(320|500|750|101)XBB", "", "", "" }, { "Seagate Samsung SpinPoint M8U (USB)", // tested with ST1000LM025 HN-M101ABB/2AR10001, // ST1000LM025 HN-M101ABB/2BA30003 (0x04e8:0x61b6) "ST(250|320|500|640|750|1000)LM0[012][3459] HN-M[0-9]*ABB", "", "", "" }, { "Seagate Samsung SpinPoint M9T", // tested with ST2000LM003 HN-M201RAD/2BC10003 // (Seagate Expansion Portable) "ST(1500|2000)LM0(03|04|06|07|10) HN-M[0-9]*RAD", "", "", "" }, // Flash accelerated, no SMART info in the specs // ST1000LX015-1U7172/SDM1 { "Seagate FireCuda 2.5", // "ST(500|1000|2000)LX0(01|15|25)-.*", "", "", "-v 240,msec24hour32 " }, // ST1000DX002/CC41 { "Seagate FireCuda 3.5", // ST2000DX002-2DV164/CC41 "ST[12]000DX002-.*", "", "", "-v 240,msec24hour32 " }, { "Seagate Samsung SpinPoint M9TU (USB)", // tested with ST1500LM008 HN-M151AAD/2BC10001 // (0x04e8:0x61b5), ST2000LM005 HN-M201AAD2BC10001 (0x04e8:0x61b4) "ST(1500|2000)LM00[58] HN-M[0-9]*AAD", "", "", "" }, { "SAMSUNG SpinPoint MP5", // tested with HM250HJ/2AK10001 "SAMSUNG HM(250H|320H|500J|640J)J", "", "", "" }, { "SAMSUNG SpinPoint MT2", // tested with HM100UI/2AM10001 "SAMSUNG HM100UI", "", "", "" }, { "SAMSUNG HM100UX (S2 Portable)", // tested with HM100UX/2AM10001 "SAMSUNG HM100UX", "", "", "" }, { "SAMSUNG SpinPoint M", // tested with MP0402H/UC100-11 "SAMSUNG MP0(302|402|603|804)H", "", "", "-v 9,halfminutes" }, { "SAMSUNG SpinPoint N3U-3 (USB, 4KiB LLS)", // tested with HS25YJZ/3AU10-01 "SAMSUNG HS(122H|2[05]YJ)Z", "", "", "" }, { "SK hynix SATA SSDs", "SK ?hynix SC(210|300|308|313).*|" // tested with // SK hynix SC210 mSATA 256GB/20002L00, // SKhynix SC300 HFS256G32MND-3210A/20131P00, // SK hynix SC308 SATA 128GB/30001P10, // SK hynix SC313 HFS256G32TNF-N3A0A/70000P10 "HFS(128|256|512)G32MND-(2200|3210)A|" // HFS128G32MND-2200A/20200L00, // HFS512G32MND-3210A/20100P00 "HFS(120|250|500)G32TND-N1A2A", // HFS500G32TND-N1A2A/30000P10 "", "", "-v 5,raw48,Retired_Block_Count " "-v 100,raw48,Total_Erase_Count " "-v 168,raw48,Min_Erase_Count " "-v 169,raw48,Max_Erase_Count " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Wear_Leveling_Count " "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 176,raw48,Unused_Rsvd_Blk_Cnt_Tot " "-v 180,raw48,Erase_Fail_Count " "-v 181,raw48,Non4k_Aligned_Access " "-v 183,raw48,SATA_Downshift_Count " "-v 201,raw48,Percent_Lifetime_Remain " "-v 212,raw48,Phy_Error_Count " "-v 231,raw48,SSD_Life_Left " "-v 241,raw48,Total_Writes_GiB " "-v 242,raw48,Total_Reads_GiB " "-v 243,raw48,Total_Media_Writes " "-v 250,raw48,Read_Retry_Count " }, { "Maxtor Fireball 541DX", "Maxtor 2B0(0[468]|1[05]|20)H1", "", "", "-v 9,minutes -v 194,unknown" }, { "Maxtor Fireball 3", "Maxtor 2F0[234]0[JL]0", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 1280 ATA", // no self-test log, ATA2-Fast "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 2160 Ultra ATA", "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 2880 Ultra ATA", "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 3400 Ultra ATA", "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax D540X-4G", "Maxtor 4G(120J6|160J[68])", "", "", "-v 9,minutes -v 194,unknown" }, { "Maxtor DiamondMax D540X-4K", "MAXTOR 4K(020H1|040H2|060H3|080H4)", "", "", "" }, { "Maxtor DiamondMax Plus D740X", "MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)", "", "", "" }, { "Maxtor DiamondMax Plus 5120 Ultra ATA 33", "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax Plus 6800 Ultra ATA 66", "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax D540X-4D", "Maxtor 4D0(20H1|40H2|60H3|80H4)", "", "", "-v 9,minutes -v 194,unknown" }, { "Maxtor DiamondMax 16", "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 4320 Ultra ATA", "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 17 VL", "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 20 VL", "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax VL 30", // U: ATA66, H: ATA100 "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 36", "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 40 ATA 66", "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax Plus 40 (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)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 40 VL Ultra ATA 100", "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax Plus 45 Ulta ATA 100", "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 60 ATA 66", "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 60 ATA 100", "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax Plus 60", "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 80", "Maxtor (98196H8|96147H6)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 536DX", "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax Plus 8", "Maxtor 6(E0[234]|K04)0L0", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 10 (ATA/133 and SATA/150)", "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 10 (SATA/300)", "Maxtor 6V(080E|160E|200E|250F|300F|320F)0", "", "", "" }, { "Maxtor DiamondMax Plus 9", "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)", "", "", "-v 9,minutes" }, { "Maxtor DiamondMax 11", "Maxtor 6H[45]00[FR]0", "", "", "" }, { "Maxtor DiamondMax 17", "Maxtor 6G(080L|160[PE])0", "", "", "" }, { "Seagate Maxtor DiamondMax 20", "MAXTOR STM3(40|80|160)[28]1[12]0?AS?", "", "", "" }, { "Seagate Maxtor DiamondMax 21", // tested with MAXTOR STM3250310AS/3.AAF "MAXTOR STM3(80[28]15|160215|250310|(250|320)820|320620|500630)AS?", "", "", "" }, { "Seagate Maxtor DiamondMax 22", // fixed firmware "(MAXTOR )?STM3(500320|750330|1000340)AS?", "MX1A", // http://knowledge.seagate.com/articles/en_US/FAQ/207969en "", "" }, { "Seagate Maxtor DiamondMax 22", // fixed firmware "(MAXTOR )?STM3(160813|320614|640323|1000334)AS?", "MX1B", // http://knowledge.seagate.com/articles/en_US/FAQ/207975en "", "" }, { "Seagate Maxtor DiamondMax 22", // buggy firmware "(MAXTOR )?STM3(500320|750330|1000340)AS?", "MX15", "There are known problems with these drives,\n" "AND THIS FIRMWARE VERSION IS AFFECTED,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207969en", "" }, { "Seagate Maxtor DiamondMax 22", // unknown firmware "(MAXTOR )?STM3(160813|32061[34]|500320|640323|750330|10003(34|40))AS?", "", "There are known problems with these drives,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207969en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207975en", "" }, { "Seagate Maxtor DiamondMax 23", // new firmware "STM3((160|250)31|(320|500)41|(750|1000)52)8AS?", "CC3[D-Z]", "", "" }, { "Seagate Maxtor DiamondMax 23", // unknown firmware "STM3((160|250)31|(320|500)41|(750|1000)52)8AS?", "", "A firmware update for this drive may be available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/213911en", "" }, { "Maxtor MaXLine Plus II", "Maxtor 7Y250[PM]0", "", "", "-v 9,minutes" }, { "Maxtor MaXLine II", "Maxtor [45]A(25|30|32)0[JN]0", "", "", "-v 9,minutes" }, { "Maxtor MaXLine III (ATA/133 and SATA/150)", "Maxtor 7L(25|30)0[SR]0", "", "", "-v 9,minutes" }, { "Maxtor MaXLine III (SATA/300)", "Maxtor 7V(25|30)0F0", "", "", "" }, { "Maxtor MaXLine Pro 500", // There is also a 7H500R0 model, but I "Maxtor 7H500F0", // haven't added it because I suspect "", // it might need vendoropts_9_minutes "", "" // and nobody has submitted a report yet }, { "", // HITACHI_DK14FA-20B "HITACHI_DK14FA-20B", "", "", "-v 9,minutes -v 193,loadunload" }, { "HITACHI Travelstar DK23XX/DK23XXB", "HITACHI_DK23..-..B?", "", "", "-v 9,minutes -v 193,loadunload" }, { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)", "(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)", "", "", "-v 9,minutes -v 193,loadunload" }, { "Hitachi Endurastar J4K30/N4K30", "HE[JN]4230[23]0F9AT00", "", "", "-v 9,minutes -v 193,loadunload" }, { "Hitachi Travelstar C4K60", // 1.8" slim drive "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00", "", "", "-v 9,minutes -v 193,loadunload" }, { "IBM Travelstar 4GT", "IBM-DTCA-2(324|409)0", "", "", "" }, { "IBM Travelstar 6GN", "IBM-DBCA-20(324|486|648)0", "", "", "" }, { "IBM Travelstar 25GS, 18GT, and 12GN", "IBM-DARA-2(25|18|15|12|09|06)000", "", "", "" }, { "IBM Travelstar 14GS", "IBM-DCYA-214000", "", "", "" }, { "IBM Travelstar 4LP", "IBM-DTNA-2(180|216)0", "", "", "" }, { "IBM Travelstar 48GH, 30GN, and 15GN", "(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.", "", "", "" }, { "IBM Travelstar 32GH, 30GT, and 20GN", "IBM-DJSA-2(32|30|20|10|05)", "", "", "" }, { "IBM Travelstar 4GN", "IBM-DKLA-2(216|324|432)0", "", "", "" }, { "IBM/Hitachi Travelstar 60GH and 40GN", "(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.", "", "", "" }, { "IBM/Hitachi Travelstar 40GNX", "(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.", "", "", "" }, { "Hitachi Travelstar 80GN", "(Hitachi )?IC25N0[23468]0ATMR04-.", "", "", "" }, { "Hitachi Travelstar 4K40", "(Hitachi )?HTS4240[234]0M9AT00", "", "", "" }, { "Hitachi Travelstar 4K120", "(Hitachi )?(HTS4212(60|80|10|12)H9AT00|HTS421260G9AT00)", "", "", "" }, { "Hitachi Travelstar 5K80", "(Hitachi )?HTS5480[8642]0M9AT00", "", "", "" }, { "Hitachi Travelstar 5K100", "(Hitachi )?HTS5410[1864]0G9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar E5K100", "(Hitachi )?HTE541040G9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar 5K120", "(Hitachi )?HTS5412(60|80|10|12)H9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar 5K160", "(Hitachi |HITACHI )?HTS5416([468]0|1[26])J9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar E5K160", "(Hitachi )?HTE5416(12|16|60|80)J9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar 5K250", "(Hitachi |HITACHI )?HTS5425(80|12|16|20|25)K9(A3|SA)00", "", "", "" }, { "Hitachi Travelstar 5K320", // tested with HITACHI HTS543232L9SA00/FB4ZC4EC, // Hitachi HTS543212L9SA02/FBBAC52F "(Hitachi |HITACHI )?HT(S|E)5432(80|12|16|25|32)L9(A3(00)?|SA0[012])", "", "", "" }, { "Hitachi/HGST Travelstar Z5K320", // tested with Hitachi HTS543232A7A384/ES2OA70K "(Hitachi|HGST) HT[ES]5432(16|25|32)A7A38[145]", "", "", "" }, { "Hitachi Travelstar 5K500.B", // tested with Hitachi HTS545050B9SA00/PB4OC60X "(Hitachi )?HT[ES]5450(12|16|25|32|40|50)B9(A30[01]|SA00)", "", "", "" }, { "Hitachi/HGST Travelstar Z5K500", // tested with HGST HTS545050A7E380/GG2OAC90, // Hitachi HTS545032A7E380/GGBOA7A0, HGST HTS545050A7E680/GR2OA230, // APPLE HDD HTS545050A7E362/GG2AB990 "(Hitachi|HGST|APPLE HDD) HT[ES]5450(25|32|50)A7E(362|38[01]|680)", "", "", "" }, { "Hitachi/HGST Travelstar 5K750", // tested with Hitachi HTS547575A9E384/JE4OA60A, // APPLE HDD HTS547550A9E384/JE3AD70F "(Hitachi|APPLE HDD) HT[ES]5475(50|64|75)A9E38[14]", "", "", "" }, { "HGST Travelstar 5K1000", // tested with HGST HTS541010A9E680/JA0OA560, // HGST HTS541075A9E680/JA2OA560 "HGST HT[ES]5410(64|75|10)A9E68[01]", "", "", "" }, { "HGST Travelstar Z5K1000", // tested with HGST HTS541010A7E630/SE0OA4A0 "HGST HTS5410(75|10)A7E63[015]", "", "", "" }, { "HGST Travelstar 5K1500", // tested with HGST HTS541515A9E630/KA0OA500 "HGST HT[ES]541515A9E63[015]", "", "", "" }, { "Hitachi Travelstar 7K60", "(Hitachi )?HTS726060M9AT00", "", "", "" }, { "Hitachi Travelstar E7K60", "(Hitachi )?HTE7260[46]0M9AT00", "", "", "" }, { "Hitachi Travelstar 7K100", "(Hitachi )?HTS7210[168]0G9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar E7K100", "(Hitachi )?HTE7210[168]0G9(AT|SA)00", "", "", "" }, { "Hitachi Travelstar 7K200", // tested with HITACHI HTS722016K9SA00/DCDZC75A "(Hitachi |HITACHI )?HTS7220(80|10|12|16|20)K9(A3|SA)00", "", "", "" }, { "Hitachi Travelstar 7K320", // tested with // HTS723225L9A360/FCDOC30F, HTS723216L9A362/FC2OC39F "(Hitachi )?HT[ES]7232(80|12|16|25|32)L9(A300|A36[02]|SA61)", "", "", "" }, { "Hitachi Travelstar Z7K320", // tested with HITACHI HTS723232A7A364/EC2ZB70B "(HITACHI )?HT[ES]7232(16|25|32)A7A36[145]", "", "", "" }, { "Hitachi Travelstar 7K500", // tested with Hitachi HTS725050A9A360/PC4OC70D, // HITACHI HTS725032A9A364/PC3ZC70F "(Hitachi |HITACHI )?HT[ES]7250(12|16|25|32|50)A9A36[02-5]", "", "", "" }, { "Hitachi/HGST Travelstar Z7K500", // tested with HITACHI HTS725050A7E630/GH2ZB390, // HGST HTS725050A7E630/GH2OA420, HGST HTS725050A7E630/GH2OA530 "(HITACHI|HGST) HT[ES]7250(25|32|50)A7E63[015]", "", "", "" }, { "Hitachi/HGST Travelstar 7K750", // tested with Hitachi HTS727550A9E364/JF3OA0E0, // Hitachi HTS727575A9E364/JF4OA0D0 "(Hitachi|HGST) HT[ES]7275(50|64|75)A9E36[14]", "", "", "" }, { "HGST Travelstar 7K1000", // tested with HGST HTS721010A9E630/JB0OA3B0 // HGST HTS721075A9E630/JB2OA3J0 "HGST HT[ES]7210(10|75)A9E63[01]", "", "", "" }, { "IBM Deskstar 14GXP and 16GP", "IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0", "", "", "" }, { "IBM Deskstar 25GP and 22GXP", "IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0", "", "", "" }, { "IBM Deskstar 37GP and 34GXP", "IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0", "", "", "" }, { "IBM/Hitachi Deskstar 120GXP", "(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]", "", "", "" }, { "IBM/Hitachi Deskstar GXP-180", "(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]", "", "", "" }, { "Hitachi CinemaStar 5K320", // tested with Hitachi HCS5C3225SLA380/STBOA37H "Hitachi HCS5C32(25|32)SLA380", "", "", "" }, { "Hitachi CinemaStar 5K1000", // Hitachi HCS5C1010CLA382/JC4OA3EA "Hitachi HCS5C10(10|75|50|32|25|16)CLA382", "", "", "" }, { "Hitachi Deskstar 5K3000", // tested with HDS5C3030ALA630/MEAOA5C0, // Hitachi HDS5C3020BLE630/MZ4OAAB0 (OEM, Toshiba Canvio Desktop) "(Hitachi )?HDS5C30(15|20|30)(ALA|BLE)63[02].*", "", "", "" }, { "Hitachi Deskstar 5K4000", // tested with HDS5C4040ALE630/MPAOA250 "(Hitachi )?HDS5C40(30|40)ALE63[01].*", "", "", "" }, { "Hitachi Deskstar 7K80", "(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320|80PLA380).*", "", "", "" }, { "Hitachi Deskstar 7K160", "(Hitachi )?HDS7216(80|16)PLA[3T]80.*", "", "", "" }, { "Hitachi Deskstar 7K250", "(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)", "", "", "" }, { "Hitachi Deskstar 7K250 (SUN branded)", "HITACHI HDS7225SBSUN250G.*", "", "", "" }, { "Hitachi Deskstar T7K250", "(Hitachi )?HDT7225((25|20|16)DLA(T80|380))", "", "", "" }, { "Hitachi Deskstar 7K400", "(Hitachi )?HDS724040KL(AT|SA)80", "", "", "" }, { "Hitachi Deskstar 7K500", "(Hitachi )?HDS725050KLA(360|T80)", "", "", "" }, { "Hitachi Deskstar P7K500", "(Hitachi )?HDP7250(16|25|32|40|50)GLA(36|38|T8)0", "", "", "" }, { "Hitachi Deskstar T7K500", "(Hitachi )?HDT7250(25|32|40|50)VLA(360|380|T80)", "", "", "" }, { "Hitachi Deskstar 7K1000", "(Hitachi )?HDS7210(50|75|10)KLA330", "", "", "" }, { "Hitachi Deskstar 7K1000.B", "(Hitachi )?HDT7210((16|25)SLA380|(32|50|64|75|10)SLA360)", "", "", "" }, { "Hitachi Deskstar 7K1000.C", // tested with Hitachi HDS721010CLA330/JP4OA3MA, // Hitachi HDS721025CLA682/JP1OA41A "(Hitachi )?HDS7210((16|25)CLA[36]82|(32|50)CLA[36]62|(64|75|10)CLA[36]3[02])", "", "", "" }, { "Hitachi Deskstar 7K1000.D", // tested with HDS721010DLE630/MS2OA5Q0 "Hitachi HDS7210(25|32|50|75|10)DLE630", "", "", "" }, { "Hitachi Deskstar E7K1000", // tested with HDE721010SLA330/ST6OA31B "Hitachi HDE7210(50|75|10)SLA330", "", "", "" }, { "Hitachi Deskstar 7K2000", "Hitachi HDS722020ALA330", "", "", "" }, { "Hitachi Deskstar 7K3000", // tested with Hitachi HDS723030ALA640/MKAOA3B0, // Hitachi HDS723030BLE640/MX6OAAB0 "Hitachi HDS7230((15|20)BLA642|30ALA640|30BLE640)", "", "", "" }, { "Hitachi/HGST Deskstar 7K4000", // tested with Hitachi HDS724040ALE640/MJAOA250, // HGST HDS724040ALE640/MJAOA580 "(Hitachi|HGST) HDS724040ALE640", "", "", "" }, { "HGST Deskstar NAS", // tested with HGST HDN724040ALE640/MJAOA5E0, // HGST HDN726050ALE610/APGNT517, HGST HDN726060ALE610/APGNT517 // HGST HDN726040ALE614/APGNW7JH, HGST HDN726060ALE614/K1HE594D "HGST HDN72(4030|4040|6040|6050|6060)ALE6(10|14|40|04)", "", "", "" }, { "Hitachi Ultrastar A7K1000", // tested with // HUA721010KLA330 44X2459 42C0424IBM/GKAOAB4A "(Hitachi )?HUA7210(50|75|10)KLA330.*", "", "", "" }, { "Hitachi Ultrastar A7K2000", // tested with // HUA722010CLA330 43W7629 42C0401IBM "(Hitachi )?HUA7220(50|10|20)[AC]LA33[01].*", "", "", "" }, { "Hitachi Ultrastar 7K3000", // tested with Hitachi HUA723030ALA640/MKAOA580, // Hitachi HUA723020ALA641/MK7OA840 "Hitachi HUA7230(20|30)ALA64[01]", "", "", "" }, { "Hitachi/HGST Ultrastar 7K4000", // tested with Hitachi HUS724040ALE640/MJAOA3B0, // HGST HUS724040ALE640/MJAOA580, HGST HUS724020ALA640/MF6OAA70 "(Hitachi|HGST) HUS7240(20|30|40)AL[AE]64[01]", "", "", "" }, { "Hitachi/HGST Ultrastar 7K2", // "(Hitachi|HGST) HUS722T[12]TALA604", "", "", "-v 16,raw48,Gas_Gauge" }, { "HGST Ultrastar 7K6000", // tested with HGST HUS726060ALE614/APGNW517 "HGST HUS7260[2456]0AL[AEN]61[014]", "", "", "" }, { "HGST Ultrastar He6", // tested with HGST HUS726060ALA640/AHGNT1E2 "HGST HUS726060ALA64[01]", "", "", "-v 22,raw48,Helium_Level" }, { "HGST Ultrastar He8", // tested with HGST HUH728060ALE600/GR2OA230 "HGST HUH7280(60|80)AL[EN]60[014]", "", "", "-v 22,raw48,Helium_Level" }, { "HGST Ultrastar He10", // tested with HGST HUH7210100ALE600/0F27452 "HGST HUH7210(08|10)AL[EN]60[014]", "", "", "-v 22,raw48,Helium_Level" }, { "WDC HGST Ultrastar He10", // WD white label, tested with // WDC WD80EMAZ-00WJTA0/83.H0A83, WDC WD80EZAZ-11TDBA0/83.H0A83, // WDC WD100EZAZ-11TDBA0/83.H0A83 "WDC WD(80E[MZ]|100EZ)AZ-.*", "", "", "-v 22,raw48,Helium_Level" }, { "HGST Ultrastar DC HC520 (He12)", // tested with HGST HUH721212ALE600/LEGNT3D0 "HGST HUH721212AL[EN]60[014]", "", "", "-v 22,raw48,Helium_Level" }, { "HGST MegaScale 4000", // tested with HGST HMS5C4040ALE640/MPAOA580 "HGST HMS5C4040[AB]LE64[01]", // B = DC 4000.B "", "", "" }, { "Toshiba 2.5\" HDD (10-20 GB)", "TOSHIBA MK(101[67]GAP|15[67]GAP|20(1[678]GAP|(18|23)GAS))", "", "", "" }, { "Toshiba 2.5\" HDD (30-60 GB)", "TOSHIBA MK((6034|4032)GSX|(6034|4032)GAX|(6026|4026|4019|3019)GAXB?|(6025|6021|4025|4021|4018|3025|3021|3018)GAS|(4036|3029)GACE?|(4018|3017)GAP)", "", "", "" }, { "Toshiba 2.5\" HDD (80 GB and above)", "TOSHIBA MK(80(25GAS|26GAX|32GAX|32GSX)|10(31GAS|32GAX)|12(33GAS|34G[AS]X)|2035GSS)", "", "", "" }, { "Toshiba 2.5\" HDD MK..37GSX", // tested with TOSHIBA MK1637GSX/DL032C "TOSHIBA MK(12|16)37GSX", "", "", "" }, { "Toshiba 2.5\" HDD MK..46GSX", // tested with TOSHIBA MK1246GSX/LB213M "TOSHIBA MK(80|12|16|25)46GSX", "", "", "" }, { "Toshiba 2.5\" HDD MK..50GACY", // tested with TOSHIBA MK8050GACY/TF105A "TOSHIBA MK8050GACY", "", "", "" }, { "Toshiba 2.5\" HDD MK..34GSX", // tested with TOSHIBA MK8034GSX/AH301E "TOSHIBA MK(80|12|10)34GSX", "", "", "" }, // { "Toshiba 2.5\" HDD MK..32GSX", // tested with TOSHIBA MK1032GSX/AS021G "TOSHIBA MK(10|80|60|40)32GSX", "", "", "" }, { "Toshiba 2.5\" HDD MK..51GSY", // tested with TOSHIBA MK1251GSY/LD101D "TOSHIBA MK(80|12|16|25)51GSY", "", "", "-v 9,minutes" }, { "Toshiba 2.5\" HDD MK..52GSX", "TOSHIBA MK(80|12|16|25|32)52GSX", "", "", "" }, { "Toshiba 2.5\" HDD MK..55GSX", // tested with TOSHIBA MK5055GSX/FG001A, MK3255GSXF/FH115B "TOSHIBA MK(12|16|25|32|40|50)55GSXF?", "", "", "" }, { "Toshiba 2.5\" HDD MK..56GSY", // tested with TOSHIBA MK2556GSYF/LJ001D "TOSHIBA MK(16|25|32|50)56GSYF?", "", "", "-v 9,minutes" }, { "Toshiba 2.5\" HDD MK..59GSXP (AF)", "TOSHIBA MK(32|50|64|75)59GSXP?", "", "", "" }, { "Toshiba 2.5\" HDD MK..59GSM (AF)", "TOSHIBA MK(75|10)59GSM", "", "", "" }, { "Toshiba 2.5\" HDD MK..61GSY[N]", // tested with TOSHIBA MK5061GSY/MC102E, MK5061GSYN/MH000A, // TOSHIBA MK2561GSYN/MH000D "TOSHIBA MK(16|25|32|50|64)61GSYN?", "", "", "-v 9,minutes" // TOSHIBA MK2561GSYN/MH000D }, { "Toshiba 2.5\" HDD MK..61GSYB", // tested with TOSHIBA MK5061GSYB/ME0A "TOSHIBA MK(16|25|32|50|64)61GSYB", "", "", "" }, { "Toshiba 2.5\" HDD MK..65GSX", // tested with TOSHIBA MK5065GSX/GJ003A, MK3265GSXN/GH012H, // MK5065GSXF/GP006B, MK2565GSX H/GJ003A "TOSHIBA MK(16|25|32|50|64)65GSX[FN]?( H)?", // "... H" = USB ? "", "", "" }, { "Toshiba 2.5\" HDD MK..75GSX", // tested with TOSHIBA MK7575GSX/GT001C "TOSHIBA MK(32|50|64|75)75GSX", "", "", "" }, { "Toshiba 2.5\" HDD MK..76GSX", // tested with TOSHIBA MK3276GSX/GS002D "TOSHIBA MK(16|25|32|50|64)76GSX", "", "", "-v 9,minutes" }, { "Toshiba 2.5\" HDD MQ01ABB...", // tested with TOSHIBA MQ01ABB200/AY000U "TOSHIBA MQ01ABB(100|150|200)", "", "", "" }, { "Toshiba 2.5\" HDD MQ01ABC...", // tested with TOSHIBA MQ01ABC150/AQ001U "TOSHIBA MQ01ABC(100|150|200)", "", "", "" }, { "Toshiba 2.5\" HDD MQ01ABD...", // tested with TOSHIBA MQ01ABD100/AX001U, // TOSHIBA MQ01ABD100V/AX001Q "TOSHIBA MQ01ABD(025|032|050|064|075|100)V?", "", "", "" }, { "Toshiba 2.5\" HDD MQ01ABF...", // tested with TOSHIBA MQ01ABF050/AM001J "TOSHIBA MQ01ABF(050|075|100)", "", "", "" }, { "Toshiba 2.5\" HDD MQ01UBB... (USB 3.0)", // tested with TOSHIBA MQ01UBB200/AY000U (0x0480:0xa100), // TOSHIBA MQ01UBB200/34MATMZ5T (0x05ac:0x8406) "TOSHIBA MQ01UBB200", "", "", "" }, { "Toshiba 2.5\" HDD MQ01UBD... (USB 3.0)", // tested with TOSHIBA MQ01UBD050/AX001U (0x0480:0xa007), // TOSHIBA MQ01UBD100/AX001U (0x0480:0x0201, 0x0480:0xa200), // TOSHIBA MQ01UBD050/AX101U (0x0480:0xa202) "TOSHIBA MQ01UBD(050|075|100)", "", "", "" }, { "Toshiba 2.5\" HDD MQ04UBF... (USB 3.0)", // tested with TOSHIBA MQ04UBF100/JU000U (0x0480:0xa202) "TOSHIBA MQ04UBF100", "", "", "" }, { "Toshiba 2.5\" HDD MQ03ABB...", // tested with TOSHIBA MQ03ABB300 "TOSHIBA MQ03ABB[23]00", "", "", "" }, { "Toshiba 2.5\" HDD MQ03UBB...", // tested with TOSHIBA MQ03UBB200/37I7T0NJT "TOSHIBA MQ03UBB(300|200|250)", "", "", "" }, { "Toshiba 3.5\" HDD MK.002TSKB", // tested with TOSHIBA MK1002TSKB/MT1A "TOSHIBA MK(10|20)02TSKB", "", "", "" }, { "Toshiba 3.5\" MG03ACAxxx(Y) Enterprise HDD", // tested with TOSHIBA MG03ACA100/FL1A "TOSHIBA MG03ACA[1234]00Y?", "", "", "" }, { "Toshiba 3.5\" MD04ACA... Enterprise HDD", // tested with TOSHIBA MD04ACA500/FP1A "TOSHIBA MD04ACA[2345]00", "", "", "" }, { "Toshiba 3.5\" MG04ACA... Enterprise HDD", // tested with TOSHIBA MG04ACA600A/FS2B "TOSHIBA MG04ACA[23456]00[AE].?", "", "", "" }, { "Toshiba 3.5\" DT01ABA... Desktop HDD", // tested with TOSHIBA DT01ABA300/MZ6OABB0 "TOSHIBA DT01ABA(100|150|200|300)", "", "", "" }, { "Toshiba 3.5\" DT01ACA... Desktop HDD", // tested with TOSHIBA DT01ACA100/MS2OA750, // TOSHIBA DT01ACA200/MX4OABB0, TOSHIBA DT01ACA300/MX6OABB0 "TOSHIBA DT01ACA(025|032|050|075|100|150|200|300)", "", "", "" }, { "Toshiba X300", // tested with TOSHIBA HDWE160/FS2A "TOSHIBA HDWE1[456]0", "", "", "" }, { "Toshiba P300", // tested with TOSHIBA HDWD120/MX4OACF0 "TOSHIBA HDWD1(30|20|10|05)", "", "", "" }, { "Toshiba 1.8\" HDD", "TOSHIBA MK[23468]00[4-9]GA[HL]", "", "", "" }, { "Toshiba 1.8\" HDD MK..29GSG", "TOSHIBA MK(12|16|25)29GSG", "", "", "" }, { "", // TOSHIBA MK6022GAX "TOSHIBA MK6022GAX", "", "", "" }, { "Toshiba HK4R Series SSD", // TOSHIBA THNSN8960PCSE/8EET6101 "TOSHIBA THNSN8(120P|240P|480P|960P|1Q92)CSE", "", "", "-v 167,raw48,SSD_Protect_Mode " "-v 168,raw48,SATA_PHY_Error_Count " "-v 169,raw48,Bad_Block_Count " "-v 173,raw48,Erase_Count " }, { "Toshiba HG6 Series SSD", // TOSHIBA THNSNJ512GCST/JTRA0102 // http://www.farnell.com/datasheets/1852757.pdf // TOSHIBA THNSFJ256GCSU/JULA1102 // TOSHIBA THNSFJ256GDNU A/JYLA1102 "TOSHIBA THNS[NF]J(060|128|256|512)G[BCAM8VD][SCN][TU].*", "", "", "-v 167,raw48,SSD_Protect_Mode " "-v 168,raw48,SATA_PHY_Error_Count " "-v 169,raw48,Bad_Block_Count " "-v 173,raw48,Erase_Count " }, { "", // TOSHIBA MK6409MAV "TOSHIBA MK6409MAV", "", "", "" }, { "Toshiba MKx019GAXB (SUN branded)", "TOS MK[34]019GAXB SUN[34]0G", "", "", "" }, { "Seagate Momentus", "ST9(20|28|40|48)11A", "", "", "" }, { "Seagate Momentus 42", "ST9(2014|3015|4019)A", "", "", "" }, { "Seagate Momentus 4200.2", // tested with ST960812A/3.05 "ST9(100822|808210|60812|50212|402113|30219)A", "", "", "" }, { "Seagate Momentus 5400.2", "ST9(808211|6082[12]|408114|308110|120821|10082[34]|8823|6812|4813|3811)AS?", "", "", "" }, { "Seagate Momentus 5400.3", "ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AS?", "", "", "" }, { "Seagate Momentus 5400.3 ED", "ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AB", "", "", "" }, { "Seagate Momentus 5400.4", "ST9(120817|(160|200|250)827)AS", "", "", "" }, { "Seagate Momentus 5400.5", "ST9((80|120|160)310|(250|320)320)AS", "", "", "" }, { "Seagate Momentus 5400.6", "ST9(80313|160(301|314)|(12|25)0315|250317|(320|500)325|500327|640320)ASG?", "", "", "-F xerrorlba" // ST9500325AS/0002SDM1 (ticket #1094) }, { "Seagate Momentus 5400.7", "ST9(160316|(250|320)310|(500|640)320)AS", "", "", "" }, { "Seagate Momentus 5400.7 (AF)", // tested with ST9640322AS/0001BSM2 // (device reports 4KiB LPS with 1 sector offset) "ST9(320312|400321|640322|750423)AS", "", "", "" }, { "Seagate Momentus 5400 PSD", // Hybrid drives "ST9(808212|(120|160)8220)AS", "", "", "" }, { "Seagate Momentus 7200.1", "ST9(10021|80825|6023|4015)AS?", "", "", "" }, { "Seagate Momentus 7200.2", "ST9(80813|100821|120823|160823|200420)ASG?", "", "", "" }, { "Seagate Momentus 7200.3", "ST9((80|120|160)411|(250|320)421)ASG?", "", "", "" }, { "Seagate Momentus 7200.4", "ST9(160412|250410|320423|500420)ASG?", "", "", "" }, { "Seagate Momentus 7200 FDE.2", "ST9((160413|25041[12]|320426|50042[12])AS|(16041[489]|2504[16]4|32042[67]|500426)ASG)", "", "", "" }, { "Seagate Momentus 7200.5", // tested with ST9750420AS/0001SDM5, ST9750420AS/0002SDM1 "ST9(50042[34]|64042[012]|75042[02])ASG?", "", "", "" }, { "Seagate Momentus XT", // fixed firmware "ST9(2505610|3205620|5005620)AS", "SD2[68]", // http://knowledge.seagate.com/articles/en_US/FAQ/215451en "", "" }, { "Seagate Momentus XT", // buggy firmware, tested with ST92505610AS/SD24 "ST9(2505610|3205620|5005620)AS", "SD2[45]", "These drives may corrupt large files,\n" "AND THIS FIRMWARE VERSION IS AFFECTED,\n" "see the following web pages for details:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/215451en\n" "https://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac", "" }, { "Seagate Momentus XT", // unknown firmware "ST9(2505610|3205620|5005620)AS", "", "These drives may corrupt large files,\n" "see the following web pages for details:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/215451en\n" "https://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac", "" }, { "Seagate Momentus XT (AF)", // tested with ST750LX003-1AC154/SM12 "ST750LX003-.*", "", "", "" }, { "Seagate Momentus Thin", // tested with ST320LT007-9ZV142/0004LVM1 "ST(160|250|320)LT0(07|09|11|14)-.*", "", "", "" }, { "Seagate Laptop HDD", // tested with ST500LT012-9WS142/0001SDM1, // ST500LM021-1KJ152/0002LIM1, ST4000LM016-1N2170/0003 "ST((25|32|50)0LT0(12|15|25)|(32|50)0LM0(10|21)|[34]000LM016)-.*", "", "", "" }, { "Seagate Laptop SSHD", // tested with ST500LM000-1EJ162/SM11 "ST(500|1000)LM0(00|14)-.*", "", "", "" }, { "Seagate Medalist 1010, 1720, 1721, 2120, 3230 and 4340", // ATA2, with -t permissive "ST3(1010|1720|1721|2120|3230|4340)A", "", "", "" }, { "Seagate Medalist 2110, 3221, 4321, 6531, and 8641", "ST3(2110|3221|4321|6531|8641)A", "", "", "" }, { "Seagate U4", "ST3(2112|4311|6421|8421)A", "", "", "" }, { "Seagate U5", "ST3(40823|30621|20413|15311|10211)A", "", "", "" }, { "Seagate U6", "ST3(8002|6002|4081|3061|2041)0A", "", "", "" }, { "Seagate U7", "ST3(30012|40012|60012|80022|120020)A", "", "", "" }, { "Seagate U8", "ST3(4313|6811|8410|4313|13021|17221)A", "", "", "" }, { "Seagate U9", // tested with ST3160022ACE/9.51 "ST3(80012|120025|160022)A(CE)?", "", "", "" }, { "Seagate U10", "ST3(20423|15323|10212)A", "", "", "" }, { "Seagate UX", "ST3(10014A(CE)?|20014A)", "", "", "" }, { "Seagate Barracuda ATA", "ST3(2804|2724|2043|1362|1022|681)0A", "", "", "" }, { "Seagate Barracuda ATA II", "ST3(3063|2042|1532|1021)0A", "", "", "" }, { "Seagate Barracuda ATA III", "ST3(40824|30620|20414|15310|10215)A", "", "", "" }, { "Seagate Barracuda ATA IV", "ST3(20011|30011|40016|60021|80021)A", "", "", "" }, { "Seagate Barracuda ATA V", "ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)", "", "", "" }, { "Seagate Barracuda 5400.1", "ST340015A", "", "", "" }, { "Seagate Barracuda 7200.7 and 7200.7 Plus", // tested with "ST380819AS 39M3701 39M0171 IBM"/3.03 "ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]082[78]AS|8001[13]AS?|8081[79]AS|60014A|40111AS|40014AS?)( .* IBM)?", "", "", "" }, { "Seagate Barracuda 7200.8", "ST3(400[68]32|300[68]31|250[68]23|200826)AS?", "", "", "" }, { "Seagate Barracuda 7200.9", "ST3(402111?|80[28]110?|120[28]1[0134]|160[28]1[012]|200827|250[68]24|300[68]22|(320|400)[68]33|500[68](32|41))AS?.*", "", "", "" }, { "Seagate Barracuda 7200.10", // tested with GB0160EAFJE/HPG0 "ST3((80|160)[28]15|200820|250[34]10|(250|300|320|400)[68]20|360320|500[68]30|750[68]40)AS?|" "GB0160EAFJE", // HP OEM "", "", "" }, { "Seagate Barracuda 7200.11", // unaffected firmware "ST3(160813|320[68]13|500[368]20|640[36]23|640[35]30|750[36]30|1000(333|[36]40)|1500341)AS?", "CC.?.?", // http://knowledge.seagate.com/articles/en_US/FAQ/207957en "", "" }, { "Seagate Barracuda 7200.11", // fixed firmware "ST3(500[368]20|750[36]30|1000340)AS?", "SD1A", // http://knowledge.seagate.com/articles/en_US/FAQ/207951en "", "" }, { "Seagate Barracuda 7200.11", // fixed firmware "ST3(160813|320[68]13|640[36]23|1000333|1500341)AS?", "SD[12]B", // http://knowledge.seagate.com/articles/en_US/FAQ/207957en "", "" }, { "Seagate Barracuda 7200.11", // buggy or fixed firmware "ST3(500[368]20|640[35]30|750[36]30|1000340)AS?", "(AD14|SD1[5-9]|SD81)", "There are known problems with these drives,\n" "THIS DRIVE MAY OR MAY NOT BE AFFECTED,\n" "see the following web pages for details:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207951en\n" "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=632758", "" }, { "Seagate Barracuda 7200.11", // unknown firmware "ST3(160813|320[68]13|500[368]20|640[36]23|640[35]30|750[36]30|1000(333|[36]40)|1500341)AS?", "", "There are known problems with these drives,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207951en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207957en", "" }, { "Seagate Barracuda 7200.12", // new firmware "ST3(160318|250318|320418|50041[08]|750528|1000528)AS", "CC4[9A-Z]", "", "" }, { "Seagate Barracuda 7200.12", // unknown firmware "ST3(160318|250318|320418|50041[08]|750528|1000528)AS", "", "A firmware update for this drive may be available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/213891en", "" }, { "Seagate Barracuda 7200.12", // tested with ST3250312AS/JC45, ST31000524AS/JC45, // ST3500413AS/JC4B, ST3750525AS/JC4B // ST3160316AS/JC45 // Possible options: ST31000524AS, ST3500413AS, ST3250312AS , // ST3750525AS, ST3320413AS, ST3160316AS "ST3(160318|25031[128]|320418|50041[038]|750(518|52[358])|100052[348]|320413|160316)AS", "", "", "" }, { "Seagate Barracuda XT", // tested with ST32000641AS/CC13, // ST4000DX000-1C5160/CC42 "ST(3(2000641|3000651)AS|4000DX000-.*)", "", "", "" }, { "Seagate Barracuda 7200.14 (AF)", // new firmware, tested with // ST3000DM001-9YN166/CC4H, ST3000DM001-9YN166/CC9E "ST(1000|1500|2000|2500|3000)DM00[1-3]-9YN16.", "CC(4[H-Z]|[5-9A-Z]..*)", // >= "CC4H" "", "-v 188,raw16 -v 240,msec24hour32" // tested with ST3000DM001-9YN166/CC4H }, { "Seagate Barracuda 7200.14 (AF)", // old firmware, tested with // ST1000DM003-9YN162/CC46 "ST(1000|1500|2000|2500|3000)DM00[1-3]-9YN16.", "CC4[679CG]", "A firmware update for this drive is available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/223651en", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Barracuda 7200.14 (AF)", // unknown firmware "ST(1000|1500|2000|2500|3000)DM00[1-3]-9YN16.", "", "A firmware update for this drive may be available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/223651en", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Barracuda 7200.14 (AF)", // different part number, tested with // ST1000DM003-1CH162/CC47, ST1000DM003-1CH162/CC49, ST2000DM001-1CH164/CC24, // ST1000DM000-9TS15E/CC92, APPLE HDD ST3000DM001/AP15 (no attr 240) "ST(1000|1500|2000|2500|3000)DM00[0-3]-.*|" "APPLE HDD ST3000DM001", "", "", "-v 188,raw16 -v 240,msec24hour32" }, // should be ST4000DM005, ST3000DM008,ST3000DM009,ST2000DM006,ST2000DM007 // ST1000DM010, ST500DM009 // tested: ST3000DM008-2DM166/CC26 { "Seagate Barracuda 3.5", // tested on ST1000DM010-2EP102/Z9ACZM97 "ST(4000DM00[45]|3000DM008|3000DM009|2000DM006|2000DM007|1000DM010|500DM009)-.*", "", "", "-v 188,raw16 -v 240,msec24hour32" }, // ST8000DM004, ST6000DM003, ST4000DM004, ST3000DM007, ST2000DM005 { "Seagate Barracuda Compute", // tested on ST8000DM004-2CX188/0001 "ST(8000DM004|6000DM003|4000DM004|3000DM007|2000DM005)-.*", "", "", "" }, { "Seagate Barracuda Pro", // tested on ST8000DM004-2CX188/0001 "ST(8000DM005|6000DM004|4000DM006|2000DM009)-.*", "", "", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Barracuda 7200.14 (AF)", // < 1TB, tested with ST250DM000-1BC141 "ST(250|320|500|750)DM00[0-3]-.*", "", "", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Desktop HDD.15", // tested with ST4000DM000-1CD168/CC43, ST5000DM000-1FK178/CC44, // ST6000DM001-1XY17Z/CC48 "ST[4568]000DM00[012]-.*", "", "", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Desktop SSHD", // tested with ST2000DX001-1CM164/CC43 "ST[124]000DX001-.*", "", "", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Barracuda LP", // new firmware "ST3(500412|1000520|1500541|2000542)AS", "CC3[5-9A-Z]", "", "" // -F xerrorlba ? }, { "Seagate Barracuda LP", // unknown firmware "ST3(500412|1000520|1500541|2000542)AS", "", "A firmware update for this drive may be available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/213915en", "-F xerrorlba" // tested with ST31000520AS/CC32 }, { "Seagate Barracuda Green (AF)", // new firmware "ST((10|15|20)00DL00[123])-.*", "CC(3[2-9A-Z]|[4-9A-Z]..*)", // >= "CC32" "", "" }, { "Seagate Barracuda Green (AF)", // unknown firmware "ST((10|15|20)00DL00[123])-.*", "", "A firmware update for this drive may be available,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/218171en", "" }, { "Seagate Barracuda ES", "ST3(250[68]2|32062|40062|50063|75064)0NS", "", "", "" }, // ST5000LM000, ST4000LM024, ST3000LM024, ST2000LM015, ST1000LM048, ST500LM030 { "Seagate Barracuda 2.5 5400", // ST2000LM015-2E8174/SDM1, ST4000LM024-2AN17V/0001 "ST(5000LM000|[34]000LM024|2000LM015|1000LM048|500LM030)-.*", "", "", "-v 183,raw48,SATA_Downshift_Count " }, { "Seagate Barracuda ES.2", // fixed firmware "ST3(25031|50032|75033|100034)0NS", "SN[01]6|" // http://knowledge.seagate.com/articles/en_US/FAQ/207963en "MA(0[^7]|[^0].)", // http://dellfirmware.seagate.com/dell_firmware/DellFirmwareRequest.jsp "", // ^^^^^^^^^^^^ down (no DNS A record) "-F xerrorlba" // tested with ST31000340NS/SN06 }, { "Seagate Barracuda ES.2", // buggy firmware (Dell) "ST3(25031|50032|75033|100034)0NS", "MA07", "There are known problems with these drives,\n" "AND THIS FIRMWARE VERSION IS AFFECTED,\n" "contact Dell support for a firmware update.", "" }, { "Seagate Barracuda ES.2", // unknown firmware "ST3(25031|50032|75033|100034)0NS", "", "There are known problems with these drives,\n" "see the following Seagate web pages:\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n" "http://knowledge.seagate.com/articles/en_US/FAQ/207963en", "" }, { "Seagate Constellation (SATA)", // tested with ST9500530NS/SN03 "ST9(160511|500530)NS", "", "", "" }, { "Seagate Constellation ES (SATA)", // tested with ST31000524NS/SN11, // MB0500EAMZD/HPG1 "ST3(50051|100052|200064)4NS|" "MB0500EAMZD", // HP OEM "", "", "" }, { "Seagate Constellation ES (SATA 6Gb/s)", // tested with ST1000NM0011/SN02, // MB1000GCEEK/HPG1 "ST(5|10|20)00NM0011|" "MB1000GCEEK", // HP OEM "", "", "" }, { "Seagate Constellation ES.2 (SATA 6Gb/s)", // tested with ST32000645NS/0004, ST33000650NS, // MB3000EBKAB/HPG6 "ST3(2000645|300065[012])NS|" "MB3000EBKAB", // HP OEM "", "", "" }, { "Seagate Constellation ES.3", // tested with ST1000NM0033-9ZM173/0001, // ST4000NM0033-9ZM170/SN03, MB1000GCWCV/HPGC, MB4000GCWDC/HPGE "ST[1234]000NM00[35]3-.*|" "MB[14]000GCW(CV|DC)", // HP OEM "", "", "" }, { "Seagate Constellation CS", // tested with ST3000NC000/CE02, ST3000NC002-1DY166/CN02 "ST(1000|2000|3000)NC00[0-3](-.*)?", "", "", "" }, { "Seagate Constellation.2 (SATA)", // 2.5", tested with ST91000640NS/SN02, MM1000GBKAL/HPGB "ST9(25061|50062|100064)[012]NS|" // *SS = SAS "MM1000GBKAL", // HP OEM "", "", "" }, // ST6000NM0004, ST6000NM0024, ST6000NM0044, ST6000NM0084, ST5000NM0024, // ST5000NM0044, ST4000NM0024, ST4000NM0044, ST2000NM0024, ST2000NM0044 // ST4000NM0035, ST3000NM0005, ST2000NM0055, ST1000NM0055, ST4000NM0045, // ST3000NM0015, ST2000NM0065, ST1000NM0065, ST4000NM0105, ST3000NM0055 { "Seagate Enterprise Capacity 3.5 HDD", // tested with ST6000NM0024-1HT17Z/SN02, // ST10000NM0016-1TT101/SNB0 // ST4000NM0085-1YY107/ZC11SXPH // ST8000NM0045-1RL112/NN02 // ST6000NM0004-1FT17Z/NN01 // ST4000NM0035-1V4107/TNC3 // ST1000NM0055-1V410C/TN02 // ST8000NM0055-1RM112/SN04 "ST([1234568]|10)000NM0[01][0-68][456]-.*", // *[069]4 = 4Kn "", "", "-v 188,raw16 -v 240,msec24hour32" }, { "Seagate Enterprise Capacity 3.5 HDD", // V5.1, ms in attribute 9 "ST[12]000NM0008-.*", // tested with ST1000NM0008-2F2100/SN01 "", "", "-v 9,msec24hour32 -v 188,raw16 -v 240,msec24hour32" }, { "Seagate Exos 5E8", // tested with ST8000AS0003-2HH188/0003 "ST8000AS0003-.*", "", "", "-v 9,msec24hour32 -v 240,msec24hour32" }, { "Seagate Exos X12", // tested with ST12000NM0007-2A1101/SN02 "ST12000NM00[01]7-.*", // *17 = SED "", "", "-v 240,msec24hour32" }, // new models: ST8000VN0002, ST6000VN0021, ST4000VN000 // ST8000VN0012, ST6000VN0031, ST4000VN003 // tested with ST8000VN0002-1Z8112/ZA13YGNF { "Seagate NAS HDD", // tested with ST2000VN000-1H3164/SC42, ST3000VN000-1H4167/SC43 "ST([234]000VN000|[468]000VN00(02|21|12|31|3))-.*", "", "", "" }, // ST8000NE0001, ST8000NE0011, ST6000VN0001, ST6000VN0011, ST5000VN0001, // ST5000VN0011, ST4000VN0001, ST4000VN0011, ST3000VN0001, ST3000VN0011, // ST2000VN0001, ST2000VN0011 // tested with ST8000NE0001-1WN112/PNA2 { "Seagate Enterprise NAS HDD", "ST(8000NE|[65432]000VN)00[01]1-.*", "", "", "" }, // ST10000VN0004, ST8000VN0022, ST6000VN0041, ST4000VN008, ST3000VN007, // ST2000VN004, ST1000VN002 { "Seagate IronWolf", // tested with ST6000VN0041-2EL11C/SC61, // ST12000VN0007-2GS116/SC60 "ST(12|10|8|6|4|3|2|1)000VN00(04|07|22|41|8|7|2|4)-.*", "", "", "" }, { "Seagate IronWolf Pro", // tested with ST4000NE0025-2EW107/EN02, // ST8000NE0004-1ZF11G/EN01, ST8000NE0021-2EN112/EN02 "ST([24]000NE0025|6000NE0023|8000NE00(04|08|21)|(10|12|14)000NE000[478])-.*", "", "", "" }, { "Seagate Archive HDD", // tested with ST8000AS0002-1NA17Z/AR13 "ST[568]000AS00[01][12]-.*", "", "", "" }, { "Seagate Pipeline HD 5900.1", "ST3(160310|320[34]10|500(321|422))CS", "", "", "" }, { "Seagate Pipeline HD 5900.2", // tested with ST31000322CS/SC13 "ST3(160316|250[34]12|320(311|413)|500(312|414)|1000(322|424))CS", "", "", "" }, { "Seagate Video 3.5 HDD", // tested with ST4000VM000-1F3168/SC23, SC25 "ST(10|15|20|30|40)00VM00[023]-.*", "", "", "" }, { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310", "ST3(17240|13030|10231|8420|4310)A", "", "", "" }, { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312", "ST3(1724|1303|1023|842|431)2A", "", "", "" }, { "Seagate NL35", "ST3(250623|250823|400632|400832|250824|250624|400633|400833|500641|500841)NS", "", "", "" }, { "Seagate SV35.2", "ST3(160815|250820|320620|500630|750640)[AS]V", "", "", "" }, { "Seagate SV35.3", // tested with ST3500320SV/SV16 "ST3(500320|750330|1000340)SV", "", "", "" }, { "Seagate SV35.5", // tested with ST31000525SV/CV12 "ST3(250311|500410|1000525)SV", "", "", "" }, // ST6000VX0001,ST6000VX0011,ST5000VX0001,ST5000VX0011,ST4000VX000 // ST4000VX002, ST3000VX002, ST2000VX003, ST1000VX001, ST1000VX002 // ST3000VX000, ST3000VX004, ST2000VX000, ST2000VX004, ST1000VX000 { "Seagate Surveillance", // tested with ST1000VX001-1HH162/CV11, ST2000VX000-9YW164/CV12, // ST4000VX000-1F4168/CV14, ST2000VX003-1HH164/CV12 "ST([1-6]000VX00[01234]1?|31000526SV|3500411SV)(-.*)?", "", "", "" }, { "Seagate DB35", // tested with ST3250823ACE/3.03, ST3300831SCE/3.03 "ST3(200826|250823|300831|400832)[AS]CE", "", "", "" }, { "Seagate DB35.2", // tested with ST3160212SCE/3.ACB "ST3(802110|120213|160212|200827|250824|300822|400833|500841)[AS]CE", "", "", "" }, { "Seagate DB35.3", "ST3(750640SCE|((80|160)215|(250|320|400)820|500830|750840)[AS]CE)", "", "", "" }, { "Seagate LD25.2", // tested with ST940210AS/3.ALC "ST9(40|80)210AS?", "", "", "" }, { "Seagate ST1.2 CompactFlash", // tested with ST68022CF/3.01 "ST6[468]022CF", "", "", "" }, { "Seagate Nytro XF1230 SATA SSD", // tested with XF1230-1A0480/ST200354 "XF1230-1A(0240|0480|0960|1920)", "", "", "-v 174,raw48,Unexpect_Power_Loss_Ct " "-v 180,raw48,End_to_End_Err_Detect " "-v 183,raw48,SATA_Downshift_Count " "-v 189,raw48,SSD_Health_Flags " "-v 190,raw48,SATA_Error_Ct " "-v 201,raw48,Read_Error_Rate " "-v 231,raw48,SSD_Life_Left_Perc " "-v 234,raw48,Lifetime_Nand_Gb " "-v 241,raw48,Total_Writes_GiB " "-v 242,raw48,Total_Reads_GiB " "-v 245,raw48,Read_Error_Rate " }, { "WD Blue and Green SSDs", // tested with WDC WDS250G1B0A-00H9H0/X41000WD, // WDC WDS250G1B0A-00H9H0/X41100WD, WDC WDS100T1B0A-00H9H0, // WDC WDS120G2G0A-00JH30/UE360000, WDC WDS240G2G0A-00JH30/UF300000 "WDC WDS((120|240|250|480|500)G|100T)(1B|2G)0[AB]-.*", // *1B* = Blue, *2G* = Green "", "", //"-v 5,raw48,Reallocated_Sector_Ct " // Reassigned Block Count //"-v 9,raw48,Power_On_Hours " //"-v 12,raw48,Power_Cycle_Count " "-v 165,raw48,Block_Erase_Count " "-v 166,raw48,Minimum_PE_Cycles_TLC " "-v 167,raw48,Max_Bad_Blocks_per_Die " "-v 168,raw48,Maximum_PE_Cycles_TLC " "-v 169,raw48,Total_Bad_Blocks " "-v 170,raw48,Grown_Bad_Blocks " "-v 171,raw48,Program_Fail_Count " "-v 172,raw48,Erase_Fail_Count " "-v 173,raw48,Average_PE_Cycles_TLC " "-v 174,raw48,Unexpected_Power_Loss " //"-v 184,raw48,End-to-end_Error " // Detection/Correction Count //"-v 187,raw48,Reported_Uncorrect " // Uncorrectable Errors //"-v 188,raw48,Command_Timeout //"-v 194,tempminmax,Temperature_Celsius " //"-v 199,raw48,UDMA_CRC_Error_Count // SATA CRC Errors "-v 230,hex48,Media_Wearout_Indicator " // Maybe hex16 //"-v 232,raw48,Available_Reserve_Space" "-v 233,raw48,NAND_GB_Written_TLC " "-v 234,raw48,NAND_GB_Written_SLC " "-v 241,raw48,Total_Host_GB_Written " "-v 242,raw48,Total_Host_GB_Read " "-v 244,raw48,Temp_Throttle_Status " }, { "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-.*", "", "", "" }, { "Western Digital Caviar", /* 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-.*", "", "", "" }, { "Western Digital Caviar WDxxxAB", /* 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|8|25)00AB-.*", "", "", "" }, { "Western Digital Caviar WDxxxAA", /* 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(-.*)?", "", "", "" }, { "Western Digital Caviar WDxxxBA", /* 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", "", "", "" }, { "Western Digital Caviar AC", // add only 5400rpm/7200rpm (ata33 and faster) "WDC AC((116|121|125|225|132|232)|([1-4][4-9][0-9])|([1-4][0-9][0-9][0-9]))00[A-Z]?.*", "", "", "" }, { "Western Digital Caviar SE", /* 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 * UPDATE 2: this does not apply to more recent models, at least WD3200AAJB */ "WDC WD(4|6|8|10|12|16|18|20|25|30|32|40|50)00(JB|PB)-.*", "", "", "" }, { "Western Digital Caviar Blue EIDE", // WD Caviar SE EIDE /* not completely accurate: at least also WD800JB, WD(4|8|20|25)00BB sold as Caviar Blue */ "WDC WD(16|25|32|40|50)00AAJB-.*", "", "", "" }, { "Western Digital Caviar Blue EIDE", // WD Caviar SE16 EIDE "WDC WD(25|32|40|50)00AAKB-.*", "", "", "" }, { "Western Digital RE EIDE", "WDC WD(12|16|25|32)00SB-.*", "", "", "" }, { "Western Digital Caviar Serial ATA", "WDC WD(4|8|20|32)00BD-.*", "", "", "" }, { "Western Digital Caviar SE Serial ATA", // tested with WDC WD3000JD-98KLB0/08.05J08 "WDC WD(4|8|12|16|20|25|30|32|40)00(JD|KD|PD)-.*", "", "", "" }, { "Western Digital Caviar SE Serial ATA", "WDC WD(8|12|16|20|25|30|32|40|50)00JS-.*", "", "", "" }, { "Western Digital Caviar SE16 Serial ATA", "WDC WD(16|20|25|32|40|50|75)00KS-.*", "", "", "" }, { "Western Digital Caviar Blue Serial ATA", // WD Caviar SE Serial ATA /* not completely accurate: at least also WD800BD, (4|8)00JD sold as Caviar Blue */ "WDC WD((8|12|16|25|32)00AABS|(8|12|16|25|32|40|50)00AAJS)-.*", "", "", "" }, { "Western Digital Caviar Blue (SATA)", // WD Caviar SE16 Serial ATA // tested with WD1602ABKS-18N8A0/DELL/02.03B04 "WDC WD((16|20|25|32|40|50|64|75)00AAKS|1602ABKS|10EALS)-.*", "", "", "" }, { "Western Digital Blue", // tested with WDC WD5000AZLX-00K4KA0/80.00A80, // WDC WD10EZEX-00RKKA0/80.00A80, WDC WD10EZEX-75M2NA0/01.01A01, WDC WD40EZRZ-00WN9B0/80.00A80 "WDC WD((25|32|50)00AAKX|5000AZ(LX|RZ)|7500A(AL|ZE)X|10E(AL|ZE)X|[1-6]0EZRZ)-.*", "", "", "" }, { "Western Digital RE Serial ATA", "WDC WD(12|16|25|32)00(SD|YD|YS)-.*", "", "", "" }, { "Western Digital RE2 Serial ATA", "WDC WD((40|50|75)00(YR|YS|AYYS)|(16|32|40|50)0[01]ABYS)-.*", "", "", "" }, { "Western Digital RE2-GP", "WDC WD(5000AB|7500AY|1000FY)PS-.*", "", "", "" }, { "Western Digital RE3 Serial ATA", // tested with WDC WD7502ABYS-02A6B0/03.00C06 "WDC WD((25|32|50|75)02A|(75|10)02F)BYS-.*", "", "", "" }, { "Western Digital RE4", // tested with WDC WD2003FYYS-18W0B0/01.01D02, // WDC WD1003FBYZ-010FB0/01.01V03 // WDC WD5003ABYZ-011FA0/01.01S03 "WDC WD((25|50)03ABY[XZ]|1003FBY[XZ]|(15|20)03FYYS)-.*", "", "", "" }, { "Western Digital RE4-GP", // tested with WDC WD2002FYPS-02W3B0/04.01G01, // WD2003FYPS-27W9B0/01.01D02 "(WDC )?WD200[23]FYPS-.*", "", "", "" }, { "Western Digital Re", // tested with WDC WD1004FBYZ-01YCBB0/RR02, // WDC WD2000FYYZ-01UL1B0/01.01K01, WDC WD2000FYYZ-01UL1B1/01.01K02, // WDC WD4000FYYZ-01UL1B2/01.01K03, WD2000FYYX/00.0D1K2, // WDC WD1004FBYZ-01YCBB1/RR04 // WD4000FYYZ, WD4000FDYZ, WD3000FYYZ, WD3000FDYZ, WD2000FYYZ, WD2000FDYZ // WD2004FBYZ, WD1004FBYZ "WDC WD((1004|2004)FBYZ|([234]000)FDYZ|[234]000FYYZ|2000FYYX)-.*", "", "", "-v 16,raw48,Total_LBAs_Read" // WDC WD1004FBYZ-01YCBB1/RR04 }, { "Western Digital Se", // tested with WDC WD2000F9YZ-09N20L0/01.01A01 // WD6001F9YZ, WD5001F9YZ, WD4000F9YZ, WD3000F9YZ, WD2000F9YZ, WD1002F9YZ "WDC WD(1002|2000|3000|4000|5001|6001)F9YZ-.*", "", "", "" }, { "Western Digital Caviar Green", // tested with WDC WD7500AADS-00M2B0/01.00A01, // WDC WD10EADX/77.04D77 "WDC WD((50|64|75)00AA[CV]S|(50|64|75)00AADS|10EA[CV]S|(10|15|20)EAD[SX])-.*", "", "", "-F xerrorlba" // tested with WDC WD7500AADS-00M2B0/01.00A01 }, { "Western Digital Caviar Green (AF)", "WDC WD(((64|75|80)00AA|(10|15|20)EA|(25|30)EZ)R|20EAC)S-.*", "", "", "" }, { "Western Digital Green", // tested with // WDC WD10EZRX-00A8LB0/01.01A01, WDC WD20EZRX-00DC0B0/80.00A80, // WDC WD30EZRX-00MMMB0/80.00A80, WDC WD40EZRX-00SPEB0/80.00A80, // WDC WD60EZRX-00MVLB1/80.00A80 "WDC WD(7500AA|(10|15|20)EA|(10|20|25|30|40|50|60)EZ)RX-.*", "", "", "" }, { "Western Digital Caviar Black", // tested with WDC WD7501AAES/06.01D06 "WDC WD((500|640)1AAL|7501AA[EL]|1001FA[EL]|2001FAS)S-.*|" "WDC WD(2002|7502|1502|5003|1002|5002)(FAE|AAE|AZE|AAL)X-.*", // could be // WD2002FAEX, WD7502AAEX, WD1502FAEX, WD5003AZEX, WD1002FAEX, WD5002AALX "", "", "" }, { "Western Digital Black", // tested with // WDC WD1003FZEX-00MK2A0/01.01A01, WDC WD3001FAEX-00MJRA0/01.01L01, // WDC WD3003FZEX-00Z4SA0/01.01A01, WDC WD4001FAEX-00MJRA0/01.01L01 // WDC WD4003FZEX-00Z4SA0/01.01A01, WDC WD5003AZEX-00RKKA0/80.00A80, // WDC WD4004FZWX-00GBGB0/81.H0A81 "WDC WD(6001|2003|5001|1003|4003|4004|5003|3003|3001)(FZW|FZE|AZE)X-.*|" // could be // new series WD6001FZWX WD2003FZEX WD5001FZWX WD1003FZEX // WD4003FZEX WD5003AZEX WD3003FZEX WD4004FZWX "WDC WD(4001|3001|2002|1002|5003|7500|5000|3200|2500|1600)(FAE|AZE)X-.*", // old series: WD4001FAEX WD3001FAEX WD2002FAEX WD1002FAEX WD5003AZEX "", "", "" }, { "Western Digital AV ATA", // tested with WDC WD3200AVJB-63J5A0/01.03E01 "WDC WD(8|16|25|32|50)00AV[BJ]B-.*", "", "", "" }, { "Western Digital AV SATA", "WDC WD(16|25|32)00AVJS-.*", "", "", "" }, { "Western Digital AV-GP", "WDC WD((16|25|32|50|64|75)00AV[CDV]S|(10|15|20)EV[CDV]S)-.*", "", "", "" }, { "Western Digital AV-GP (AF)", // tested with WDC WD10EURS-630AB1/80.00A80, // WDC WD10EUCX-63YZ1Y0/51.0AB52, WDC WD20EURX-64HYZY0/80.00A80 "WDC WD(5000AUDX|7500AURS|10EUCX|(10|15|20|25|30)EUR[SX])-.*", "", "", "" }, { "Western Digital AV", // tested with DC WD10JUCT-63CYNY0/01.01A01 "WDC WD((16|25|32|50)00BU[CD]|5000LUC|10JUC)T-.*", "", "", "" }, { "Western Digital Raptor", "WDC WD((360|740|800)GD|(360|740|800|1500)ADF[DS])-.*", "", "", "" }, { "Western Digital Raptor X", "WDC WD1500AHFD-.*", "", "", "" }, { "Western Digital VelociRaptor", // tested with WDC WD1500HLHX-01JJPV0/04.05G04 "WDC WD(((800H|(1500|3000)[BH]|1600H|3000G)LFS)|((1500|3000|4500|6000)[BH]LHX))-.*", "", "", "" }, { "Western Digital VelociRaptor (AF)", // tested with WDC WD1000DHTZ-04N21V0/04.06A00 "WDC WD(2500H|5000B|5000H|1000D)HTZ-.*", "", "", "" }, { "Western Digital Scorpio EIDE", "WDC WD(4|6|8|10|12|16)00(UE|VE)-.*", "", "", "" }, { "Western Digital Scorpio Blue EIDE", // tested with WDC WD3200BEVE-00A0HT0/11.01A11 "WDC WD(4|6|8|10|12|16|25|32)00BEVE-.*", "", "", "" }, { "Western Digital Scorpio Serial ATA", "WDC WD(4|6|8|10|12|16|25)00BEAS-.*", "", "", "" }, { "Western Digital Scorpio Blue Serial ATA", "WDC WD((4|6|8|10|12|16|25)00BEVS|(8|12|16|25|32|40|50|64)00BEVT|7500KEVT|10TEVT)-.*", "", "", "" }, { "Western Digital Scorpio Blue Serial ATA (AF)", // tested with // WDC WD10JPVT-00A1YT0/01.01A01 "WDC WD((16|25|32|50|64|75)00BPVT|10[JT]PVT)-.*", "", "", "" }, { "Western Digital Scorpio Black", // tested with WDC WD5000BEKT-00KA9T0/01.01A01 "WDC WD(8|12|16|25|32|50)00B[EJ]KT-.*", "", "", "" }, { "Western Digital Scorpio Black (AF)", "WDC WD(50|75)00BPKT-.*", "", "", "" }, { "Western Digital Red", // tested with WDC WD10EFRX-68JCSN0/01.01A01, // WDC WD10JFCX-68N6GN0/01.01A01, WDC WD30EFRX-68EUZN0/82.00A82, // WDC WD40EFRX-68WT0N0/80.00A80, WDC WD60EFRX-68MYMN1/82.00A82, // WDC WD80EFAX-68LHPN0/83.H0A83, WDC WD80EFZX-68UW8N0/83.H0A83, // WDC WD80EZZX-11CSGA0/83.H0A03 (My Book 0x1058:0x25ee) "WDC WD(7500BFC|10JFC|[1-6]0EFR|80E[FZ][AZ])X-.*", "", "", "-v 22,raw48,Helium_Level" // WD80EFZX }, { "Western Digital Red Pro", // tested with WDC WD2001FFSX-68JNUN0/81.00A81, // WDC WD6002FFWX-68TZ4N0/83.H0A83 "WDC WD([2-68]00[12])FF[SW]X-.*", "", "", "" }, { "Western Digital Purple", // tested with WDC WD40PURX-64GVNY0/80.00A80 "WDC WD[123456]0PURX-.*", "", "", "" }, { "Western Digital Gold", // tested with WDC WD1005FBYZ-01YCBB2/RR07, // WDC WD2005FBYZ-01YCBB2/RR07, WDC WD4002FYYZ-01B7CB0/01.01M02, // WDC WD8003FRYZ-01JPDB1/01.01H02, WDC WD121KRYZ-01W0RB0/01.01H01 "WDC WD([12]005FB|4002FY|6002FR|800[23]FR|1[02]1KR)YZ-.*", "", "", "-v 22,raw48,Helium_Level" // WD121KRYZ }, { "Western Digital Blue Mobile", // tested with WDC WD5000LPVX-08V0TT2/03.01A03, // WDC WD20NPVZ-00WFZT0/01.01A01 "WDC WD((25|32|50|75)00[BLM]|10[JS]|20N)P[CV][TXZ]-.*", "", "", "" }, { "Western Digital Green Mobile", // tested with WDC WD20NPVX-00EA4T0/01.01A01 "WDC WD(15|20)NPV[TX]-.*", "", "", "" }, { "Western Digital Black Mobile", // tested with WDC WD7500BPKX-22HPJT0/01.01A01, // WDC WD10JPLX-00MBPT0/01.01H01 "WDC WD((16|25|32)00BEK[TX]|(25|32|50|75)00(BPK|LPL)X|10JPLX)-.*", "", "", "" }, { "Western Digital Elements / My Passport (USB)", // tested with WDC WD5000BMVW-11AMCS0/01.01A01 "WDC WD(25|32|40|50)00BMV[UVW]-.*", // *W-* = USB 3.0 "", "", "" }, { "Western Digital Elements / My Passport (USB, AF)", // tested with // WDC WD5000KMVV-11TK7S1/01.01A01, // WDC WD5000LMVW-11CKRS0/01.01A01 (0x1058:0x07ae), // WDC WD5000LMVW-11VEDS0/01.01A01 (0x1058:0x0816), // WDC WD7500BMVW-11AJGS2/01.01A01, // WDC WD10JMVW-11AJGS2/01.01A01 (0x1058:0x10b8), // WDC WD10JMVW-11AJGS4/01.01A01 (0x1058:0x25a0/25a2), // WDC WD10JMVW-11S5XS1/01.01A01, // WDC WD10TMVW-11ZSMS5/01.01A01, // WDC WD20NMVW-11AV3S2/01.01A01 (0x1058:0x0822), // WDC WD20NMVW-11AV3S3/01.01A01 (0x1058:0x0837), // WDC WD20NMVW-11EDZS6/01.01A01 (0x1058-0x259f), // WDC WD20NMVW-11EDZS7/01.01A01 (0x1058:0x259d/25a1), // WDC WD20NMVW-11W68S0/01.01A01, // WDC WD20NMVW-59AV3S3/01.01A01 (0x1058:0x107d), // WDC WD30NMVW-11C3NS4/01.01A01, // WDC WD40NMZW-11GX6S1/01.01A01 (0x1058:0x2599/25e2/25fa) "WDC WD(5000[LK]|7500[BK]|10[JT]|[234]0N)M[VZ][VW]-.*", // *W-* = USB 3.0 "", "", "" }, { "Quantum Bigfoot", // tested with TS10.0A/A21.0G00, TS12.7A/A21.0F00 "QUANTUM BIGFOOT TS(10\\.0|12\\.7)A", "", "", "" }, { "Quantum Fireball lct15", "QUANTUM FIREBALLlct15 ([123]0|22)", "", "", "" }, { "Quantum Fireball lct20", "QUANTUM FIREBALLlct20 [1234]0", "", "", "" }, { "Quantum Fireball CX", "QUANTUM FIREBALL CX10.2A", "", "", "" }, { "Quantum Fireball CR", "QUANTUM FIREBALL CR(4.3|6.4|8.4|13.0)A", "", "", "" }, { "Quantum Fireball EX", // tested with QUANTUM FIREBALL EX10.2A/A0A.0D00 "QUANTUM FIREBALL EX(3\\.2|6\\.4|10\\.2)A", "", "", "" }, { "Quantum Fireball ST", "QUANTUM FIREBALL ST(3.2|4.3|4300)A", "", "", "" }, { "Quantum Fireball SE", "QUANTUM FIREBALL SE4.3A", "", "", "" }, { "Quantum Fireball Plus LM", "QUANTUM FIREBALLP LM(10.2|15|20.[45]|30)", "", "", "" }, { "Quantum Fireball Plus AS", "QUANTUM FIREBALLP AS(10.2|20.5|30.0|40.0|60.0)", "", "", "" }, { "Quantum Fireball Plus KX", "QUANTUM FIREBALLP KX27.3", "", "", "" }, { "Quantum Fireball Plus KA", "QUANTUM FIREBALLP KA(9|10).1", "", "", "" }, //////////////////////////////////////////////////// // USB ID entries //////////////////////////////////////////////////// // 0x0350 (?) { "USB: ViPowER USB3.0 Storage; ", "0x0350:0x0038", "", // 0x1905 "", "-d sat,12" // ATA output registers missing }, // Hewlett-Packard { "USB: HP Desktop HD BD07; ", // 2TB "0x03f0:0xbd07", "", "", "-d sat" }, // ALi { "USB: ; ALi M5621", // USB->PATA "0x0402:0x5621", "", "", "" // unsupported }, // VIA { "USB: Connectland BE-USB2-35BP-LCM; VIA VT6204", "0x040d:0x6204", "", "", "" // unsupported }, // Buffalo / Melco { "USB: Buffalo JustStore Portable HD-PVU2; ", "0x0411:0x0181", "", "", "-d sat" }, { "USB: Buffalo Drivestation Duo; ", "0x0411:0x01ce", "", "", "-d sat" }, { "USB: Buffalo DriveStation HD-LBU2 ; Medialogic MLDU11", "0x0411:0x01ea", "", "", "-d sat" }, { "USB: Buffalo; ", "0x0411:0x0(1[df]9|1e7|240|251|27e)", // 0x01d9: HD-PCTU2 (0x0108), 0x01e7: HD-PNTU3, // 0x01f9: HD-PZU3 (0x0100), 0x0240: HD-PCFU3, 0x0251: HD-PNFU3, 0x027e: HD-LC3 "", "", "-d sat" }, // LG Electronics { "USB: LG Mini HXD5; JMicron", "0x043e:0x70f1", "", // 0x0100 "", "-d usbjmicron" }, // Hitachi (?) { "USB: ; Renesas uPD720231A", // USB2/3->SATA // 0x0229: Pi-102 Raspberry Pi USB to mSATA Converter Board // 0x022a: DeLock 62652 converter SATA 6GB/s > USB 3.0 "0x045b:0x022[9a]", "", "", "-d sat" }, // Philips { "USB: Philips; ", // SDE3273FC/97 2.5" SATA HDD enclosure "0x0471:0x2021", "", // 0x0103 "", "-d sat" }, // Toshiba { "USB: Toshiba Canvio 500GB; SunPlus", "0x0480:0xa004", "", "", "-d usbsunplus" }, { "USB: Toshiba; ", "0x0480:0x....", "", "", "-d sat" }, // Cypress { "USB: ; Cypress CY7C68300A (AT2)", "0x04b4:0x6830", "0x0001", "", "" // unsupported }, { "USB: ; Cypress CY7C68300B/C (AT2LP)", "0x04b4:0x6830", "0x0240", "", "-d usbcypress" }, // Fujitsu { "USB: Fujitsu/Zalman ZM-VE300; ", // USB 3.0 "0x04c5:0x2028", "", // 0x0001 "", "-d sat" }, { "USB: ; Fujitsu", // DeLock 42475, USB 3.0 "0x04c5:0x201d", "", // 0x0001 "", "-d sat" }, // Myson Century { "USB: ; Myson Century CS8818", "0x04cf:0x8818", "", // 0xb007 "", "" // unsupported }, // Samsung { "USB: Samsung S2 Portable; JMicron", "0x04e8:0x1f0[568a]", // 0x1f0a: SAMSUNG HN-M101XBB "", "", "-d usbjmicron" // 0x1f0a: works also with "-d sat" }, { "USB: Samsung S1 Portable; JMicron", "0x04e8:0x2f03", "", "", "-d usbjmicron" }, { "USB: Samsung Story Station; ", "0x04e8:0x5f0[56]", "", "", "-d sat" }, { "USB: Samsung G2 Portable; JMicron", "0x04e8:0x6032", "0x0000", "", "-d usbjmicron" // ticket #132 }, { "USB: Samsung G2 Portable; ", "0x04e8:0x6032", "0x...[1-9]", // >= 0x0001 "", "-d sat" }, { "USB: Samsung Story Station 3.0; ", "0x04e8:0x6052", "", "", "-d sat" }, { "USB: Samsung Story Station 3.0; ", "0x04e8:0x6054", "", "", "-d sat" }, { "USB: Samsung M2 Portable 3.0; ", "0x04e8:0x60c5", "", "", "-d sat" }, { "USB: Samsung D3 Station; ", "0x04e8:0x612[45]", // 3TB, 4TB "", // 0x200, 0x202 "", "-d sat" }, { "USB: Samsung M3 Portable USB 3.0; ", // 1.5/2TB: SpinPoint M9TU "0x04e8:0x61b[3456]", // 500MB, 2TB, 1.5TB, 1TB "", // 0x0e00 "", "-d sat" }, { "USB: Samsung S3 Portable; ", "0x04e8:0x61c8", // ST1000LM025 HN-M101ABB "", // 0x1301 "", "-d sat" }, { "USB: Samsung Portable SSD T5; ", "0x04e8:0x61f5", "", // 0x0100 "", "-d sat" }, // Sunplus { "USB: ; SunPlus", "0x04fc:0x0c05", "", "", "-d usbsunplus" }, { "USB: ; SunPlus SPDIF215", "0x04fc:0x0c15", "", // 0xf615 "", "-d usbsunplus" }, { "USB: ; SunPlus SPDIF225", // USB+SATA->SATA "0x04fc:0x0c25", "", // 0x0103 "", "-d usbsunplus" }, // Iomega { "USB: Iomega Prestige Desktop USB 3.0; ", "0x059b:0x0070", "", // 0x0004 "", "-d sat" // ATA output registers missing }, { "USB: Iomega LPHD080-0; ", "0x059b:0x0272", "", "", "-d usbcypress" }, { "USB: Iomega MDHD500-U; JMicron", "0x059b:0x0274", "", // 0x0000 "", "-d usbjmicron,0" }, { "USB: Iomega MDHD500-U; ", "0x059b:0x0275", "", // 0x0001 "", "" // unsupported }, { "USB: Iomega; JMicron", "0x059b:0x027[78]", // 0x0277: MDHD-UE, 0x0278: LDHD-UPS "", // 0x0000 "", "-d usbjmicron" }, { "USB: Iomega LDHD-UP; Sunplus", "0x059b:0x0370", "", "", "-d usbsunplus" }, { "USB: Iomega; JMicron", "0x059b:0x0(47[05]|57[15])", // 0x0470: LPHD-UP, 0x0475: GDHDU2 (0x0100), // 0x0575: LDHD-UP "", "", "-d usbjmicron" }, { "USB: Iomega; JMicron", "0x059b:0x047a", "", // 0x0100 "", "-d sat" // works also with "-d usbjmicron" }, // LaCie { "USB: LaCie hard disk (FA Porsche design);", "0x059f:0x0651", "", "", "" // unsupported }, { "USB: LaCie d2 Quadra; Oxford OXUF934SSA-LQAG ", // USB+IEEE1394+eSATA->SATA "0x059f:0x0828", "", "", "-d sat" }, { "USB: LaCie hard disk; JMicron", "0x059f:0x0951", "", "", "-d usbjmicron" }, { "USB: LaCie Rugged Triple Interface; ", "0x059f:0x100c", "", // 0x0001 "", "-d sat" }, { "USB: LaCie Desktop Hard Drive;", "0x059f:0x1010", "", "", "-d usbsunplus" }, { "USB: LaCie Desktop Hard Drive; ", "0x059f:0x101[68]", // 0x1016: SAMSUNG HD103UJ "", // 0x0001 "", "-d sat" }, { "USB: LaCie Desktop Hard Drive; JMicron", "0x059f:0x1019", "", "", "-d usbjmicron" }, { "USB: LaCie Rugged Hard Drive; JMicron", "0x059f:0x101d", "", // 0x0001 "", "-d usbjmicron,x" }, { "USB: LaCie Little Disk USB2; JMicron", "0x059f:0x1021", "", "", "-d usbjmicron" }, { "USB: LaCie hard disk; ", "0x059f:0x1029", "", // 0x0100 "", "-d sat" }, { "USB: Lacie rikiki; JMicron", "0x059f:0x102a", "", "", "-d usbjmicron,x" }, { "USB: LaCie D2 USB3; LucidPort USB300 ", "0x059f:0x103d", "", "", "-d sat" }, { "USB: LaCie rikiki USB 3.0; ", "0x059f:0x10(49|57)", "", "", "-d sat" }, { "USB: LaCie minimus USB 3.0; ", "0x059f:0x104a", "", "", "-d sat" }, { "USB: LaCie Rugged Mini USB 3.0; ", "0x059f:0x1051", "", // 0x0000 "", "-d sat" }, { "USB: LaCie Rugged Mini HDD; ", "0x059f:0x106b", "", "", "-d sat" }, { "USB: LaCie; ", // 0x1070: ASMedia 1053 ? "0x059f:0x10(6f|7[05])", "", // 6f/70=0x0001, 75=0x0000 "", "-d sat" }, // In-System Design { "USB: ; In-System/Cypress ISD-300A1", "0x05ab:0x0060", "", // 0x1101 "", "-d usbcypress" }, // Apple { "USB: Apple; ", "0x05ac:0x8406", // TOSHIBA MQ01UBB200 "", "", "-d sat" }, // Genesys Logic { "USB: ; Genesys Logic GL881E", "0x05e3:0x0702", "", "", "" // unsupported }, { "USB: ; Genesys Logic", // TODO: requires '-T permissive' "0x05e3:0x0718", "", // 0x0041 "", "-d sat" }, { "USB: ; Genesys Logic GL3310", "0x05e3:0x0731", // Chieftec USB 3.0 2.5" case "", "", "-d sat" }, { "USB: ; Genesys Logic", "0x05e3:0x0735", "", // 0x1003 "", "-d sat" }, // Micron { "USB: Micron USB SSD; ", "0x0634:0x0655", "", "", "" // unsupported }, // Prolific { "USB: ; Prolific PL2507", // USB->PATA "0x067b:0x2507", "", "", "-d usbjmicron,0" // Port number is required }, { "USB: ; Prolific PL2571/2771/2773/2775", // USB->SATA, USB3->SATA, "0x067b:0x(2571|277[135])", // USB3+eSATA->SATA, USB3->2xSATA "", "", "-d usbprolific" }, { "USB: ; Prolific PL3507", // USB+IEEE1394->PATA "0x067b:0x3507", "", // 0x0001 "", "-d usbjmicron,p" }, // Imation { "USB: Imation ; ", // Imation Odyssey external USB dock "0x0718:0x1000", "", // 0x5104 "", "-d sat" }, // SanDisk { "USB: SanDisk SDCZ80 Flash Drive; Fujitsu", // ATA ID: SanDisk pSSD "0x0781:0x558[08]", "", "", "-d sat" }, // Freecom { "USB: ; Innostor IS631", // No Name USB3->SATA Enclosure "0x07ab:0x0621", "", "", "-d sat" }, { "USB: Freecom; ", "0x07ab:0xfc17", "", // 0x0100 "", "-d sat" }, { "USB: Freecom Quattro 3.0; ", // USB3.0+IEEE1394+eSATA->SATA "0x07ab:0xfc77", "", "", "-d sat" }, { "USB: Freecom Mobile Drive XXS; JMicron", "0x07ab:0xfc88", "", // 0x0101 "", "-d usbjmicron,x" }, { "USB: Freecom Hard Drive XS; Sunplus", "0x07ab:0xfc8e", "", // 0x010f "", "-d usbsunplus" }, { "USB: Freecom; ", // Intel labeled "0x07ab:0xfc8f", "", // 0x0000 "", "-d sat" }, { "USB: Freecom Classic HD 120GB; ", "0x07ab:0xfccd", "", "", "" // unsupported }, { "USB: Freecom HD; JMicron", // 500GB "0x07ab:0xfcd[6a]", "", "", "-d usbjmicron" }, // Oxford Semiconductor, Ltd { "USB: ; Oxford", "0x0928:0x0000", "", "", "" // unsupported }, { "USB: ; Oxford OXU921DS", "0x0928:0x0002", "", "", "" // unsupported }, { "USB: ; Oxford", // Zalman ZM-VE200 "0x0928:0x0010", "", // 0x0304 "", "-d sat" }, // Toshiba { "USB: Toshiba PX1270E-1G16; Sunplus", "0x0930:0x0b03", "", "", "-d usbsunplus" }, { "USB: Toshiba PX1396E-3T01; Sunplus", // similar to Dura Micro 501 "0x0930:0x0b09", "", "", "-d usbsunplus" }, { "USB: Toshiba Stor.E Steel; Sunplus", "0x0930:0x0b11", "", "", "-d usbsunplus" }, { "USB: Toshiba Stor.E; ", "0x0930:0x0b1[9ab]", "", // 0x0001 "", "-d sat" }, // Lumberg, Inc. { "USB: Toshiba Stor.E; Sunplus", "0x0939:0x0b1[56]", "", "", "-d usbsunplus" }, // Apricorn { "USB: Apricorn SATA Wire; ", "0x0984:0x0040", "", "", "-d sat" }, // Neodio Technologies { "USB: Neodio; Initio INIC-1810PL", "0x0aec:0x3050", "", // 0x0100 "", "-d sat" }, // Seagate { "USB: Seagate External Drive; Cypress", "0x0bc2:0x0503", "", // 0x0240 "", "-d usbcypress" }, { "USB: Seagate FreeAgent; ", "0x0bc2:0x(3008|50(31|a1))", "", "", "-d sat,12" // 0x50a1: "-d sat" does not work (ticket #151) }, { "USB: Seagate; ", "0x0bc2:0x....", "", "", "-d sat" }, // Addonics { "USB: Addonics HDMU3; ", // (ticket #609) "0x0bf6:0x1001", "", // 0x0100 "", "" }, // Dura Micro { "USB: Dura Micro; Cypress", "0x0c0b:0xb001", "", // 0x1110 "", "-d usbcypress" }, { "USB: Dura Micro; Initio", "0x0c0b:0xb136", "", // 0x0108 "", "-d sat" }, { "USB: Dura Micro 509; Sunplus", "0x0c0b:0xb159", "", // 0x0103 "", "-d usbsunplus" }, // Maxtor { "USB: Maxtor OneTouch 200GB; ", "0x0d49:0x7010", "", "", "" // unsupported }, { "USB: Maxtor OneTouch; ", "0x0d49:0x7300", "", // 0x0121 "", "-d sat" }, { "USB: Maxtor OneTouch 4; ", "0x0d49:0x7310", "", // 0x0125 "", "-d sat" }, { "USB: Maxtor OneTouch 4 Mini; ", "0x0d49:0x7350", "", // 0x0125 "", "-d sat" }, { "USB: Maxtor BlackArmor Portable; ", "0x0d49:0x7550", "", "", "-d sat" }, { "USB: Maxtor Basics Desktop; ", "0x0d49:0x7410", "", // 0x0122 "", "-d sat" }, { "USB: Maxtor Basics Portable; ", "0x0d49:0x7450", "", // 0x0122 "", "-d sat" }, // Jess-Link International { "USB: ; Cypress", // Medion HDDrive2Go "0x0dbf:0x9001", "", // 0x0240 "", "-d usbcypress" }, // Oyen Digital { "USB: Oyen Digital MiniPro USB 3.0; ", "0x0dc4:0x020a", "", "", "-d sat" }, // Cowon Systems, Inc. { "USB: Cowon iAudio X5; ", "0x0e21:0x0510", "", "", "-d usbcypress" }, // iRiver { "USB: iRiver iHP-120/140 MP3 Player; Cypress", "0x1006:0x3002", "", // 0x0100 "", "-d usbcypress" }, // Western Digital { "USB: WD My Passport (IDE); Cypress", "0x1058:0x0701", "", // 0x0240 "", "-d usbcypress" }, { "USB: Western Digital; ", "0x1058:0x....", "", "", "-d sat" }, // Atech Flash Technology { "USB: ; Atech", // Enclosure from Kingston SSDNow notebook upgrade kit "0x11b0:0x6298", "", // 0x0108 "", "-d sat" }, // ADATA { "USB: ADATA; ", "0x125f:0xa(11|13|15|31|35|75)a", // 0xa11a: Classic CH11 1TB, 0xa13a: NH13 1TB, "", // 0xa15a: HD710 1TB, 0xa31a: HV620 2TB (0x0100), 0xa35a: HD650 2TB (0x6503), "", // 0xa75a: HD710P 4TB "-d sat" }, { "USB: ADATA; Cypress", "0x125f:0xa9[34]a", // 0xa93a: SH93 (0x0150) "", "", "-d usbcypress" }, // Initio { "USB: ; Initio", "0x13fd:0x(054|1(04|15))0", // 0x0540: Initio 316000 "", // 0x1040 (0x0106): USB->SATA+PATA, Chieftec CEB-25I "", // 0x1150: Initio 6Y120L0, CoolerMaster XCraft RX-3HU "" // unsupported }, { "USB: ; Initio", "0x13fd:0x16[45]0", "", // 0x1640: 0x0864, 0x1650: 0x0436 "", "-d sat,12" // some SMART commands fail, see ticket #295 }, { "USB: ; Initio", "0x13fd:0x....", "", "", "-d sat" }, // Super Top { "USB: Super Top generic enclosure; ", "0x14cd:0x6116", "", // 0x0150, older report suggests -d usbcypress "", // 0x0160 also reported as unsupported "-d sat" }, // JMicron { "USB: ; JMicron JMS539", // USB2/3->SATA (old firmware) "0x152d:0x0539", "0x0100", // 1.00, various devices support -d usbjmicron "", // 1.00, SSI SI-1359RUS3 supports -d sat, "" // -d usbjmicron may disconnect drive (ticket #552) }, { "USB: ; JMicron JMS539", // USB2/3->SATA (new firmware) "0x152d:0x0539", "0x020[56]|" // 2.05, ZTC USB 3.0 enclosure (ticket #338) "0x28(01|03|12)", // 28.01, DATOptic U3eSATA (USB3.0 bridge with port multiplier) "", // 28.03, Mediasonic ProBox HF2-SU3S2 Rev 2 (port multiplier, ticket #504) "-d sat" // 28.12, Mediasonic ProBox H82-SU3S2 (port multiplier) }, { "USB: ; JMicron ", // USB->SATA->4xSATA (port multiplier) "0x152d:0x0551", // JMS539? (old firmware may use 0x152d:0x0539, ticket #552) "", // 0x0100 "", "-d usbjmicron,x" }, { "USB: ; JMicron", "0x152d:0x0561", "", // 0x0003, ODROID CloudShell 2 "", "-d sat" }, { "USB: ; JMicron JM562", // USB2/3+eSATA->2xSATA, USB2/3->3xSATA (RAID0/1) "0x152d:0x0562", "", // 0x0106, Fantec QB-X2US3R (ticket #966) "", // only ATA IDENTIFY works, SMART commands don't work "-d sat" }, { "USB: ; JMicron", // USB2/3->2xSATA "0x152d:0x0565", "", // 0x9114, Akasa DuoDock X (ticket #607) "", "-d sat" }, { "USB: ; JMicron JMS567", // USB2/3->SATA "0x152d:0x0567", "", // 0x0114 "", // 0x0205, 2.05, Mediasonic ProBox HF2-SU3S2 Rev 3 (port multiplier, ticket #504) "-d sat" }, { "USB: ; JMicron JMS578", // USB->SATA "0x152d:0x0578", "", // 0x0100 "", "-d sat" }, { "USB: ; JMicron", "0x152d:0x0579", // Intenso External "", // 0x0100 "", "-d sat" }, { "USB: ; JMicron JMS583", // USB->PCIe (NVMe) "0x152d:0x0583", "", "", "-d sntjmicron#please_try" // TODO: Remove '#please_try' when no longer EXPERIMENTAL }, { "USB: OCZ THROTTLE OCZESATATHR8G; JMicron JMF601", "0x152d:0x0602", "", "", "" // unsupported }, { "USB: ; JMicron JMS561", // USB2/3->2xSATA "0x152d:0x[19]561", // 0x1561(0x0106), Sabrent USB 3.0 Dual Bay SATA Dock "", // 0x9561(0x0105), Orico 6629US3-C USB 3.0 Dual Bay SATA Dock "", "-d sat" }, { "USB: ; JMicron JM20329", // USB->SATA "0x152d:0x2329", "", // 0x0100 "", "-d usbjmicron" }, { "USB: ; JMicron JM20336", // USB+SATA->SATA, USB->2xSATA "0x152d:0x2336", "", // 0x0100 "", "-d usbjmicron,x" }, { "USB: Generic JMicron adapter; JMicron", "0x152d:0x2337", "", "", "-d usbjmicron" }, { "USB: ; JMicron JM20337/8", // USB->SATA+PATA, USB+SATA->PATA "0x152d:0x2338", "", // 0x0100 "", "-d usbjmicron" }, { "USB: ; JMicron JM20339", // USB->SATA "0x152d:0x2339", "", // 0x0100 "", "-d usbjmicron,x" }, { "USB: ; JMicron", // USB+SATA->SATA "0x152d:0x2351", // e.g. Verbatim Portable Hard Drive 500Gb "", // 0x0100 "", "-d sat" }, { "USB: ; JMicron", // USB->SATA "0x152d:0x2352", "", // 0x0100 "", "-d usbjmicron,x" }, { "USB: ; JMicron", // USB->SATA "0x152d:0x2509", "", // 0x0100 "", "-d usbjmicron,x" }, { "USB: ; JMicron JMS566", // USB3->SATA "0x152d:0x2566", // e.g. Chieftec CEB-7035S "", // 0x0114 "", "-d usbjmicron,x" }, { "USB: ; JMicron JMS567", // USB3->SATA "0x152d:0x2567", "", // 0x0117, Chieftec CEB-7053S "", "-d sat" }, { "USB: ; JMicron", "0x152d:0x2590", "", // 0x0x8105 (ticket #550) "", "-d sat" }, { "USB: ; JMicron JMS567", // USB2/3->SATA "0x152d:0x3562", "", // 0x0310, StarTech S358BU33ERM (port multiplier, ticket #508) "", "-d sat" }, { "USB: ; JMicron", // USB3->SATA "0x152d:0x3569", "", // 0x0203 "", "-d sat" }, { "USB: ; JMicron", "0x152d:0x578e", "", // 0x1402, Intenso Memory Center "", "-d sat" }, { "USB: ; JMicron JMS561U", // USB3->2xSATA "0x152d:0x8561", "", // 0x0107 "", "-d sat" }, // ASMedia { "USB: ; ASMedia", "0x174c:0x....", "", "", "-d sat" }, // LucidPort { "USB: ; LucidPORT USB300", // RaidSonic ICY BOX IB-110StU3-B, Sharkoon SATA QuickPort H3 "0x1759:0x500[02]", // 0x5000: USB 2.0, 0x5002: USB 3.0 "", "", "-d sat" }, { "USB: ; LucidPort", // Fuj:tech SATA-USB3 dock "0x1759:0x5100", "", // 0x2580 "", "-d sat" }, // Verbatim { "USB: Verbatim Portable Hard Drive; Sunplus", "0x18a5:0x0214", "", // 0x0112 "", "-d usbsunplus" }, { "USB: Verbatim FW/USB160; Oxford OXUF934SSA-LQAG", // USB+IEEE1394->SATA "0x18a5:0x0215", "", // 0x0001 "", "-d sat" }, { "USB: Verbatim External Hard Drive 47519; Sunplus", // USB->SATA "0x18a5:0x0216", "", "", "-d usbsunplus" }, { "USB: Verbatim Pocket Hard Drive; JMicron", // SAMSUNG SpinPoint N3U-3 (USB, 4KiB LLS) "0x18a5:0x0227", "", "", "-d usbjmicron" // "-d usbjmicron,x" does not work }, { "USB: Verbatim External Hard Drive; JMicron", // 2TB "0x18a5:0x022a", "", "", "-d usbjmicron" }, { "USB: Verbatim Store'n'Go; JMicron", // USB->SATA "0x18a5:0x022b", "", // 0x0100 "", "-d usbjmicron" }, { "USB: Verbatim Pocket Hard Drive; ", // 1TB USB 3.0 "0x18a5:0x0237", "", "", "-d sat,12" }, { "USB: Verbatim External Hard Drive; ", // USB 3.0 "0x18a5:0x040[08]", // 0=3TB, 8=1TB "", "", "-d sat" }, // Silicon Image { "USB: Vantec NST-400MX-SR; Silicon Image 5744", "0x1a4a:0x1670", "", "", "" // unsupported }, // Corsair { "USB: Voyager GTX; ", "0x1b1c:0x1a0e", "", "", "-d sat" }, // SunplusIT { "USB: ; SunplusIT", "0x1bcf:0x0c31", "", "", "-d usbsunplus" }, // TrekStor { "USB: TrekStor DataStation; ", // DataStation maxi light (USB 3.0) "0x1e68:0x0050", "", // 0x0100 "", "-d sat" }, // Innostor { "USB: ; Innostor IS611", // USB3->SATA+PATA "0x1f75:0x0611", // SMART access via PATA does not work "", "", "-d sat" }, { "USB: ; Innostor IS621", // USB3->SATA "0x1f75:0x0621", // Dynex 2.5" USB 3.0 Exclosure DX-HD302513 "", "", "-d sat" }, { "USB: ; Innostor IS888", // USB3->SATA "0x1f75:0x0888", "", // 0x0034, Sharkoon SATA QuickDeck Pro USB 3.0 (unsupported) "", // 0x0036, works with -d sat (ticket #827) "-d sat" }, // VIA Labs { "USB: ; VIA VL701", // USB2/3->SATA "0x2109:0x0701", // Intenso 2,5" 1TB USB3 "", // 0x0107 "", "-d sat" // ATA output registers missing }, { "USB: ; VIA VL711", // USB2/3->SATA "0x2109:0x0711", "", // 0x0114, Mediasonic ProBox K32-SU3 (ticket #594) "", // 0x0507, Intenso 2,5" Memory Case 2TB USB3 "-d sat" }, { "USB: ; VIA VL715", // USB2/3->SATA "0x2109:0x0715", "", // 0x0336 "", "-d sat" }, // Transcend (?) { "USB: Transcend ESD400; ", "0x2174:0x2000", // TS256GESD400K "", // 0x1000 "", "-d sat" }, // 0x2537 (?) { "USB: ; ", // USB 3.0 "0x2537:0x106[68]", // 0x1066: Orico 2599US3, 0x1068: Fantec ER-35U3 "", // 0x0100 "", "-d sat" }, // Power Quotient International { "USB: PQI H560; ", "0x3538:0x0902", "", // 0x0000 "", "-d sat" }, // Power Quotient International { "USB: PQI bridge; ", "0x3538:0x0064", "", "", "-d usbsunplus" }, // Sharkoon { "USB: Sharkoon QuickPort XT USB 3.0; ", "0x357d:0x7788", "", "", "-d sat" }, // Hitachi/SimpleTech { "USB: Hitachi Touro Desk; JMicron", // 3TB "0x4971:0x1011", "", "", "-d usbjmicron" }, { "USB: Hitachi Touro; ", "0x4971:0x101[45]", // 14=1TB, 15=2TB "", // 0x0000 "", "-d sat" // ATA output registers missing }, { "USB: Hitachi Touro Mobile; ", // 1TB "0x4971:0x102[034]", "", // 0x0100 "", "-d sat" }, { "USB: SimpleTech;", // USB 3.0 HDD BOX Agestar, Rock External HDD 3,5" UASP "0x4971:0x8017", "", "", "-d sat" }, { "USB: Hitachi/SimpleTech; JMicron", // 1TB "0x4971:0xce17", "", "", "-d usbjmicron,x" }, // OnSpec { "USB: ; OnSpec", // USB->PATA "0x55aa:0x2b00", "", // 0x0100 "", "" // unsupported }, // 0x6795 (?) { "USB: Sharkoon 2-Bay RAID Box; ", // USB 3.0 "0x6795:0x2756", "", // 0x0100 "", "-d sat" }, // JMicron II { "USB: ; JMicron JMS566", "0xa152:0xb566", "", // 0x0223 "", "-d sat" }, // 0xabcd (?) { "USB: ; ", "0xabcd:0x610[34]", // 0x6103: LogiLink AU0028A V1.0 USB 3.0 to IDE & SATA Adapter // 0x6104: LogiLink PCCloneEX Lite "", "", "-d sat" }, /* }; // builtin_knowndrives[] */ smartmontools-7.0/examplescripts/0000755000175000010010000000000013412155414014332 500000000000000smartmontools-7.0/examplescripts/Example10000755000175000010010000000300512362270734015660 00000000000000#! /bin/sh # # 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 3958 2014-07-18 19:13:32Z chrfranke $ # 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 /usr/bin/mailx below. /usr/bin/mail -s "SMART errors detected on host: `hostname`" $SMARTD_ADDRESS < /root/tempfile # And exit exit 0 smartmontools-7.0/examplescripts/Example20000755000175000010010000000130412362270734015661 00000000000000#! /bin/sh # # 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 3958 2014-07-18 19:13:32Z chrfranke $ # 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. Solaris and # other OSes may need to use /usr/bin/mailx below. /usr/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg smartmontools-7.0/examplescripts/Example30000755000175000010010000000126312362270734015666 00000000000000#! /bin/sh # # 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 3958 2014-07-18 19:13:32Z chrfranke $ # Warn all users of a problem wall <Your hard disk drive is failing! S.M.A.R.T. message: $SMARTD_MESSAGE" smartmontools-7.0/examplescripts/Example50000755000175000010010000000024612203764655015674 00000000000000#!/bin/bash -e tmp=$(tempfile) cat >$tmp run-parts --report --lsbsysinit --arg=$tmp --arg="$1" \ --arg="$2" --arg="$3" -- /etc/smartmontools/run.d rm -f $tmp smartmontools-7.0/examplescripts/Example60000755000175000010010000000161413203116467015666 00000000000000#! /bin/sh # Send mail if which mail >/dev/null 2>&1 then echo "$SMARTD_MESSAGE" | mail -s "$SMARTD_FAILTYPE" "$SMARTD_ADDRESS" fi # Notify desktop user MESSAGE="SMART Disk monitor:" case "$SMARTD_FAILTYPE" in "EmailTest"|"Health"|"Temperature"|"Usage") ;; *) # "CurrentPendingSector", // 10 # "OfflineUncorrectableSector", // 11 # "FailedReadSmartErrorLog", // 7 # "ErrorCount", // 4 # "FailedReadSmartData", // 6 # "FailedHealthCheck", // 5 # "FailedOpenDevice", // 9 # "SelfTest", // 3 # "FailedReadSmartSelfTestLog", // 8 exit 0 esac # direct write to terminals, do not use 'wall', because we don't want its ugly header for t in $(who | awk '{ print $2; }' | grep -e '^tty' -e '^pts/') do echo "$MESSAGE $SMARTD_MESSAGE" >/dev/$t 2>/dev/null ||: done smartmontools-7.0/examplescripts/README0000644000175000010010000000332513336335341015141 00000000000000# Home page: http://www.smartmontools.org # # $Id: README 4760 2018-08-19 18:45:53Z chrfranke $ # # Copyright (C) 2003-08 Bruce Allen # Copyright (C) 2009-18 Christian Franke # # SPDX-License-Identifier: GPL-2.0-or-later # This directory contains executable shell 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. Example4: Uses powersave-notify to issue a desktop neutral warning. (/etc/smartmontools/run.d/10powersave-notify from Debian package) Example5: Uses run-parts(8) to run scripts from /etc/smartmontools/run.d/. (/usr/share/smartmontools/smartd-runner from Debian package) Example6: Sends a warning mail and then notifies the users by direct write to terminals. (/usr/libexec/smartmontools/smartdnotify from Fedora package) smartmontools-7.0/freebsd_nvme_ioctl.h0000644000175000010010000001033613246034412015213 00000000000000/*- * Copyright (C) 2012-2013 Intel Corporation * 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. * * $FreeBSD$ */ #include #define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command) #if __FreeBSD_version < 1100110 struct nvme_command { /* dword 0 */ uint16_t opc : 8; /* opcode */ uint16_t fuse : 2; /* fused operation */ uint16_t rsvd1 : 6; uint16_t cid; /* command identifier */ /* dword 1 */ uint32_t nsid; /* namespace identifier */ /* dword 2-3 */ uint32_t rsvd2; uint32_t rsvd3; /* dword 4-5 */ uint64_t mptr; /* metadata pointer */ /* dword 6-7 */ uint64_t prp1; /* prp entry 1 */ /* dword 8-9 */ uint64_t prp2; /* prp entry 2 */ /* dword 10-15 */ uint32_t cdw10; /* command-specific */ uint32_t cdw11; /* command-specific */ uint32_t cdw12; /* command-specific */ uint32_t cdw13; /* command-specific */ uint32_t cdw14; /* command-specific */ uint32_t cdw15; /* command-specific */ } __packed; struct nvme_status { uint16_t p : 1; /* phase tag */ uint16_t sc : 8; /* status code */ uint16_t sct : 3; /* status code type */ uint16_t rsvd2 : 2; uint16_t m : 1; /* more */ uint16_t dnr : 1; /* do not retry */ } __packed; struct nvme_completion { /* dword 0 */ uint32_t cdw0; /* command-specific */ /* dword 1 */ uint32_t rsvd1; /* dword 2 */ uint16_t sqhd; /* submission queue head pointer */ uint16_t sqid; /* submission queue identifier */ /* dword 3 */ uint16_t cid; /* command identifier */ struct nvme_status status; } __packed; struct nvme_pt_command { /* * cmd is used to specify a passthrough command to a controller or * namespace. * * The following fields from cmd may be specified by the caller: * * opc (opcode) * * nsid (namespace id) - for admin commands only * * cdw10-cdw15 * * Remaining fields must be set to 0 by the caller. */ struct nvme_command cmd; /* * cpl returns completion status for the passthrough command * specified by cmd. * * The following fields will be filled out by the driver, for * consumption by the caller: * * cdw0 * * status (except for phase) * * Remaining fields will be set to 0 by the driver. */ struct nvme_completion cpl; /* buf is the data buffer associated with this passthrough command. */ void * buf; /* * len is the length of the data buffer associated with this * passthrough command. */ uint32_t len; /* * is_read = 1 if the passthrough command will read data into the * supplied buffer from the controller. * * is_read = 0 if the passthrough command will write data from the * supplied buffer to the controller. */ uint32_t is_read; /* * driver_lock is used by the driver only. It must be set to 0 * by the caller. */ struct mtx * driver_lock; }; #else #include #endif #if __FreeBSD_version < 1200058 #define nvme_completion_is_error(cpl) \ ((cpl)->status.sc != 0 || (cpl)->status.sct != 0) #endif #define NVME_CTRLR_PREFIX "/dev/nvme" #define NVME_NS_PREFIX "ns" smartmontools-7.0/getopt/0000755000175000010010000000000013412155411012566 500000000000000smartmontools-7.0/getopt/bits/0000755000175000010010000000000013412155411013527 500000000000000smartmontools-7.0/getopt/bits/getopt_core.h0000644000175000010010000000712213336323570016144 00000000000000/* Declarations for getopt (basic, portable features only). Copyright (C) 1989-2018 Free Software Foundation, Inc. This file is part of the GNU C Library and is also part of gnulib. Patches to this file should be submitted to both projects. 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, see . */ #ifndef _GETOPT_CORE_H #define _GETOPT_CORE_H 1 /* This header should not be used directly; include getopt.h or unistd.h instead. Unlike most bits headers, it does not have a protective #error, because the guard macro for getopt.h in gnulib is not fixed. */ __BEGIN_DECLS /* For communication from 'getopt' to the caller. When 'getopt' finds an option that takes an argument, the argument value is returned here. Also, when 'ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to 'getopt'. On entry to 'getopt', zero means this is the first call; initialize. When 'getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, 'optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message 'getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, 'optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in 'optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU 'getopt'. The argument '--' causes premature termination of argument scanning, explicitly telling 'getopt' that there are no more options. If OPTS begins with '-', then non-option arguments are treated as arguments to the option '\1'. This behavior is specific to the GNU 'getopt'. If OPTS begins with '+', or POSIXLY_CORRECT is set in the environment, then do not permute arguments. For standards compliance, the 'argv' argument has the type char *const *, but this is inaccurate; if argument permutation is enabled, the argv array (not the strings it points to) must be writable. */ extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) __THROW __nonnull ((2, 3)); __END_DECLS #endif /* getopt_core.h */ smartmontools-7.0/getopt/bits/getopt_ext.h0000644000175000010010000000573513336323570016024 00000000000000/* Declarations for getopt (GNU extensions). Copyright (C) 1989-2018 Free Software Foundation, Inc. This file is part of the GNU C Library and is also part of gnulib. Patches to this file should be submitted to both projects. 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, see . */ #ifndef _GETOPT_EXT_H #define _GETOPT_EXT_H 1 /* This header should not be used directly; include getopt.h instead. Unlike most bits headers, it does not have a protective #error, because the guard macro for getopt.h in gnulib is not fixed. */ __BEGIN_DECLS /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of 'struct option' terminated by an element containing a name which is zero. The field 'has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field 'flag' is not NULL, it points to a variable that is set to the value given in the field 'val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an 'int' to a compiled-in constant, such as set a value from 'optarg', set the option's 'flag' field to zero and its 'val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero 'flag' field, 'getopt' returns the contents of the 'val' field. */ struct option { const char *name; /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the 'has_arg' field of 'struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind) __THROW __nonnull ((2, 3)); extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind) __THROW __nonnull ((2, 3)); __END_DECLS #endif /* getopt_ext.h */ smartmontools-7.0/getopt/getopt.c0000644000175000010010000005743013336325511014172 00000000000000/* Getopt for GNU. Copyright (C) 1987-2018 Free Software Foundation, Inc. This file is part of the GNU C Library and is also part of gnulib. Patches to this file should be submitted to both projects. 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, see . */ #if !defined(_LIBC) && !defined(_GETOPT_STANDALONE) # include #endif #include "getopt.h" #include #include #include #ifndef _GETOPT_STANDALONE #include #endif #ifdef _LIBC /* When used as part of glibc, error printing must be done differently for standards compliance. getopt is not a cancellation point, so it must not call functions that are, and it is specified by an older standard than stdio locking, so it must not refer to functions in the "user namespace" related to stdio locking. Finally, it must use glibc's internal message translation so that the messages are looked up in the proper text domain. */ # include # define fprintf __fxprintf_nocancel # define flockfile(fp) _IO_flockfile (fp) # define funlockfile(fp) _IO_funlockfile (fp) #else #ifndef _GETOPT_STANDALONE # include "gettext.h" # define _(msgid) gettext (msgid) #else # define _(msgid) (msgid) #endif /* When used standalone, flockfile and funlockfile might not be available. */ # ifndef _POSIX_THREAD_SAFE_FUNCTIONS # define flockfile(fp) /* nop */ # define funlockfile(fp) /* nop */ # endif /* When used standalone, do not attempt to use alloca. */ # define __libc_use_alloca(size) 0 # undef alloca # define alloca(size) (abort (), (void *)0) #endif /* This implementation of 'getopt' has three modes for handling options interspersed with non-option arguments. It can stop scanning for options at the first non-option argument encountered, as POSIX specifies. It can continue scanning for options after the first non-option argument, but permute 'argv' as it goes so that, after 'getopt' is done, all the options precede all the non-option arguments and 'optind' points to the first non-option argument. Or, it can report non-option arguments as if they were arguments to the option character '\x01'. The default behavior of 'getopt_long' is to permute the argument list. When this implementation is used standalone, the default behavior of 'getopt' is to stop at the first non-option argument, but when it is used as part of GNU libc it also permutes the argument list. In both cases, setting the environment variable POSIXLY_CORRECT to any value disables permutation. If the first character of the OPTSTRING argument to 'getopt' or 'getopt_long' is '+', both functions will stop at the first non-option argument. If it is '-', both functions will report non-option arguments as arguments to the option character '\x01'. */ #include "getopt_int.h" /* For communication from 'getopt' to the caller. When 'getopt' finds an option that takes an argument, the argument value is returned here. Also, when 'ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to 'getopt'. On entry to 'getopt', zero means this is the first call; initialize. When 'getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, 'optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Keep a global copy of all internal members of getopt_data. */ static struct _getopt_data getopt_data; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. 'first_nonopt' and 'last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (char **argv, struct _getopt_data *d) { int bottom = d->__first_nonopt; int middle = d->__last_nonopt; int top = d->optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ d->__first_nonopt += (d->optind - d->__last_nonopt); d->__last_nonopt = d->optind; } /* Process the argument starting with d->__nextchar as a long option. d->optind should *not* have been advanced over this argument. If the value returned is -1, it was not actually a long option, the state is unchanged, and the argument should be processed as a set of short options (this can only happen when long_only is true). Otherwise, the option (and its argument, if any) have been consumed and the return value is the value to return from _getopt_internal_r. */ static int process_long_option (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int print_errors, const char *prefix) { char *nameend; size_t namelen; const struct option *p; const struct option *pfound = NULL; int n_options; int option_index; for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; namelen = nameend - d->__nextchar; /* First look for an exact match, counting the options as a side effect. */ for (p = longopts, n_options = 0; p->name; p++, n_options++) if (!strncmp (p->name, d->__nextchar, namelen) && namelen == strlen (p->name)) { /* Exact match found. */ pfound = p; option_index = n_options; break; } if (pfound == NULL) { /* Didn't find an exact match, so look for abbreviations. */ unsigned char *ambig_set = NULL; int ambig_malloced = 0; int ambig_fallback = 0; int indfound = -1; for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, d->__nextchar, namelen)) { if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) { /* Second or later nonexact match found. */ if (!ambig_fallback) { if (!print_errors) /* Don't waste effort tracking the ambig set if we're not going to print it anyway. */ ambig_fallback = 1; else if (!ambig_set) { if (__libc_use_alloca (n_options)) ambig_set = alloca (n_options); else if ((ambig_set = malloc (n_options)) == NULL) /* Fall back to simpler error message. */ ambig_fallback = 1; else ambig_malloced = 1; if (ambig_set) { memset (ambig_set, 0, n_options); ambig_set[indfound] = 1; } } if (ambig_set) ambig_set[option_index] = 1; } } } if (ambig_set || ambig_fallback) { if (print_errors) { if (ambig_fallback) fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"), argv[0], prefix, d->__nextchar); else { flockfile (stderr); fprintf (stderr, _("%s: option '%s%s' is ambiguous; possibilities:"), argv[0], prefix, d->__nextchar); for (option_index = 0; option_index < n_options; option_index++) if (ambig_set[option_index]) fprintf (stderr, " '%s%s'", prefix, longopts[option_index].name); /* This must use 'fprintf' even though it's only printing a single character, so that it goes through __fxprintf_nocancel when compiled as part of glibc. */ fprintf (stderr, "\n"); funlockfile (stderr); } } if (ambig_malloced) free (ambig_set); d->__nextchar += strlen (d->__nextchar); d->optind++; d->optopt = 0; return '?'; } option_index = indfound; } if (pfound == NULL) { /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. */ if (!long_only || argv[d->optind][1] == '-' || strchr (optstring, *d->__nextchar) == NULL) { if (print_errors) fprintf (stderr, _("%s: unrecognized option '%s%s'\n"), argv[0], prefix, d->__nextchar); d->__nextchar = NULL; d->optind++; d->optopt = 0; return '?'; } /* Otherwise interpret it as a short option. */ return -1; } /* We have found a matching long option. Consume it. */ d->optind++; d->__nextchar = NULL; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) fprintf (stderr, _("%s: option '%s%s' doesn't allow an argument\n"), argv[0], prefix, pfound->name); d->optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) fprintf (stderr, _("%s: option '%s%s' requires an argument\n"), argv[0], prefix, pfound->name); d->optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } #ifndef _GL_UNUSED # ifdef __GNUC__ # define _GL_UNUSED __attribute__((__unused__)) # else # define _GL_UNUSED # endif #endif /* Initialize internal data upon the first call to getopt. */ static const char * _getopt_initialize (int argc _GL_UNUSED, char **argv _GL_UNUSED, const char *optstring, struct _getopt_data *d, int posixly_correct) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ if (d->optind == 0) d->optind = 1; d->__first_nonopt = d->__last_nonopt = d->optind; d->__nextchar = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { d->__ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { d->__ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct || !!getenv ("POSIXLY_CORRECT")) d->__ordering = REQUIRE_ORDER; else d->__ordering = PERMUTE; d->__initialized = 1; return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If 'getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If 'getopt' finds another option character, it returns that character, updating 'optind' and 'nextchar' so that the next call to 'getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, 'getopt' returns -1. Then 'optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set 'opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in 'optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in 'optarg', otherwise 'optarg' is set to zero. If OPTSTRING starts with '-' or '+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with '--' instead of '-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a '=', or else the in next ARGV-element. When 'getopt' finds a long-named option, it returns 0 if that option's 'flag' field is nonzero, the value of the option's 'val' field if the 'flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of 'struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal_r (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int posixly_correct) { int print_errors = d->opterr; if (argc < 1) return -1; d->optarg = NULL; if (d->optind == 0 || !d->__initialized) optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct); else if (optstring[0] == '-' || optstring[0] == '+') optstring++; if (optstring[0] == ':') print_errors = 0; /* Test whether ARGV[optind] points to a non-option argument. */ #define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') if (d->__nextchar == NULL || *d->__nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (d->__last_nonopt > d->optind) d->__last_nonopt = d->optind; if (d->__first_nonopt > d->optind) d->__first_nonopt = d->optind; if (d->__ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange (argv, d); else if (d->__last_nonopt != d->optind) d->__first_nonopt = d->optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (d->optind < argc && NONOPTION_P) d->optind++; d->__last_nonopt = d->optind; } /* The special ARGV-element '--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (d->optind != argc && !strcmp (argv[d->optind], "--")) { d->optind++; if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange (argv, d); else if (d->__first_nonopt == d->__last_nonopt) d->__first_nonopt = d->optind; d->__last_nonopt = argc; d->optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (d->optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (d->__first_nonopt != d->__last_nonopt) d->optind = d->__first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (d->__ordering == REQUIRE_ORDER) return -1; d->optarg = argv[d->optind++]; return 1; } /* We have found another option-ARGV-element. Check whether it might be a long option. */ if (longopts) { if (argv[d->optind][1] == '-') { /* "--foo" is always a long option. The special option "--" was handled above. */ d->__nextchar = argv[d->optind] + 2; return process_long_option (argc, argv, optstring, longopts, longind, long_only, d, print_errors, "--"); } /* If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (long_only && (argv[d->optind][2] || !strchr (optstring, argv[d->optind][1]))) { int code; d->__nextchar = argv[d->optind] + 1; code = process_long_option (argc, argv, optstring, longopts, longind, long_only, d, print_errors, "-"); if (code != -1) return code; } } /* It is not a long option. Skip the initial punctuation. */ d->__nextchar = argv[d->optind] + 1; } /* Look at and handle the next short option-character. */ { char c = *d->__nextchar++; const char *temp = strchr (optstring, c); /* Increment 'optind' when we start to process its last character. */ if (*d->__nextchar == '\0') ++d->optind; if (temp == NULL || c == ':' || c == ';') { if (print_errors) fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); d->optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL) { /* This is an option that requires an argument. */ if (*d->__nextchar != '\0') d->optarg = d->__nextchar; else if (d->optind == argc) { if (print_errors) fprintf (stderr, _("%s: option requires an argument -- '%c'\n"), argv[0], c); d->optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else d->optarg = argv[d->optind]; d->__nextchar = d->optarg; d->optarg = NULL; return process_long_option (argc, argv, optstring, longopts, longind, 0 /* long_only */, d, print_errors, "-W "); } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*d->__nextchar != '\0') { d->optarg = d->__nextchar; d->optind++; } else d->optarg = NULL; d->__nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*d->__nextchar != '\0') { d->optarg = d->__nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ d->optind++; } else if (d->optind == argc) { if (print_errors) fprintf (stderr, _("%s: option requires an argument -- '%c'\n"), argv[0], c); d->optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented 'optind' once; increment it again when taking next ARGV-elt as argument. */ d->optarg = argv[d->optind++]; d->__nextchar = NULL; } } return c; } } int _getopt_internal (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, int posixly_correct) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; result = _getopt_internal_r (argc, argv, optstring, longopts, longind, long_only, &getopt_data, posixly_correct); optind = getopt_data.optind; optarg = getopt_data.optarg; optopt = getopt_data.optopt; return result; } /* glibc gets a LSB-compliant getopt and a POSIX-complaint __posix_getopt. Standalone applications just get a POSIX-compliant getopt. POSIX and LSB both require these functions to take 'char *const *argv' even though this is incorrect (because of the permutation). */ #define GETOPT_ENTRY(NAME, POSIXLY_CORRECT) \ int \ NAME (int argc, char *const *argv, const char *optstring) \ { \ return _getopt_internal (argc, (char **)argv, optstring, \ 0, 0, 0, POSIXLY_CORRECT); \ } #ifdef _LIBC GETOPT_ENTRY(getopt, 0) GETOPT_ENTRY(__posix_getopt, 1) #else GETOPT_ENTRY(getopt, 1) #endif #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of 'getopt'. */ int main (int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value '%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ smartmontools-7.0/getopt/getopt.h0000644000175000010010000000373113336325511014172 00000000000000/* Declarations for getopt. Copyright (C) 1989-2018 Free Software Foundation, Inc. This file is part of the GNU C Library. Unlike the bulk of the getopt implementation, this file is NOT part of gnulib; gnulib also has a getopt.h but it is different. 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, see . */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifndef _GETOPT_STANDALONE #include #endif // From : #ifndef __BEGIN_DECLS # ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } # else # define __BEGIN_DECLS # define __END_DECLS # endif #endif #ifndef __THROW # ifdef __cplusplus # define __THROW throw() # elif defined(__GNUC__) # define __THROW __attribute__ ((__nothrow__)) # else # define __THROW # endif #endif #ifndef __nonnull # ifdef __GNUC__ # define __nonnull(x) __attribute__ ((__nonnull__ x)) # else # define __nonnull(x) # endif #endif /* The type of the 'argv' argument to getopt_long and getopt_long_only is properly 'char **', since both functions may write to the array (in order to move all the options to the beginning). However, for compatibility with old versions of LSB, glibc has to use 'char *const *' instead. */ #ifndef __getopt_argv_const # define __getopt_argv_const const #endif #include #include #endif /* getopt.h */ smartmontools-7.0/getopt/getopt1.c0000644000175000010010000000742513336325511014252 00000000000000/* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987-2018 Free Software Foundation, Inc. This file is part of the GNU C Library and is also part of gnulib. Patches to this file should be submitted to both projects. 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, see . */ #if !defined(_LIBC) && !defined(_GETOPT_STANDALONE) # include #endif #include "getopt.h" #include "getopt_int.h" int getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, opt_index, 0, 0); } int _getopt_long_r (int argc, char **argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, 0, d, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, opt_index, 1, 0); } int _getopt_long_only_r (int argc, char **argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, 1, d, 0); } #ifdef TEST #include #include int main (int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static const struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value '%s'\n", optarg); break; case 'd': printf ("option d with value '%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ smartmontools-7.0/getopt/getopt_int.h0000644000175000010010000001011113336323570015035 00000000000000/* Internal declarations for getopt. Copyright (C) 1989-2018 Free Software Foundation, Inc. This file is part of the GNU C Library and is also part of gnulib. Patches to this file should be submitted to both projects. 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, see . */ #ifndef _GETOPT_INT_H #define _GETOPT_INT_H 1 #include extern int _getopt_internal (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, int __posixly_correct); /* Reentrant versions which can handle parsing multiple argument vectors at the same time. */ /* Describe how to deal with options that follow non-option ARGV-elements. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what POSIX specifies should happen. PERMUTE means permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. The special argument '--' forces an end of option-scanning regardless of the value of 'ordering'. In the case of RETURN_IN_ORDER, only '--' can cause 'getopt' to return -1 with 'optind' != ARGC. */ enum __ord { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; /* Data type for reentrant functions. */ struct _getopt_data { /* These have exactly the same meaning as the corresponding global variables, except that they are used for the reentrant versions of getopt. */ int optind; int opterr; int optopt; char *optarg; /* Internal members. */ /* True if the internal members have been initialized. */ int __initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ char *__nextchar; /* See __ord above. */ enum __ord __ordering; /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. 'first_nonopt' is the index in ARGV of the first of them; 'last_nonopt' is the index after the last of them. */ int __first_nonopt; int __last_nonopt; }; /* The initializer is necessary to set OPTIND and OPTERR to their default values and to clear the initialization flag. */ #define _GETOPT_DATA_INITIALIZER { 1, 1 } extern int _getopt_internal_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, struct _getopt_data *__data, int __posixly_correct); extern int _getopt_long_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, struct _getopt_data *__data); extern int _getopt_long_only_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, struct _getopt_data *__data); #endif /* getopt_int.h */ smartmontools-7.0/INSTALL0000644000175000010010000004435113411754225012253 00000000000000Smartmontools installation instructions ======================================= $Id: INSTALL 4880 2018-12-29 20:27:01Z chrfranke $ Please also see the smartmontools home page: https://www.smartmontools.org/ Table of contents: [1] System requirements [2] Installing from SVN [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 ======================= Note: Some of this info is outdated as it refers to very old OS versions. 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. 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 or later 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. E) Cygwin The code was tested on Cygwin 2.11.* x86 and x86_64. It should also work on other recent releases. Both Cygwin and Windows versions of smartmontools share the same code to access the raw devices. The information in the "Windows" section below also applies to the Cygwin version. F) Windows The code was tested on Windows XP SP3, 2003, Vista, Windows 7, 8, 8.1 and Windows 10 up to 1809. Support von Windows 9x/ME and NT4 was removed after smartmontools 5.43. ATA or SATA devices are supported if the device driver implements the SMART IOCTLs or IOCTL_IDE_PASS_THROUGH or IOCTL_ATA_PASS_THROUGH. Only the latter provides full pass-through support which is needed for all smartmontools features. SATA devices behind a Intel RST driver are accessed through CSMI. SCSI and USB devices are accessed through SPTI. Special driver support is not required. NVMe devices are supported with the Windows 10 NVMe driver or with vendor specific drivers supporting NVME_PASS_THROUGH. 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. Only basic SMART commands are supported for ATA devices. It's important to know that on 10.3.x, some things don't work 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. The OS X SAT SMART Driver provides access to SMART data for SAT capable USB and Firewire devices: https://github.com/kasbert/OS-X-SAT-SMART-Driver https://github.com/RJVB/OS-X-SAT-SMART-Driver This does not require any smartctl -d TYPE option and should work also with older smartmontools releases. 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. Only ATA disks are supported. [2] Installing from SVN ======================= Get the sources from the SVN repository: svn co https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools smartmontools Then type: cd smartmontools ./autogen.sh and continue with step [3] below, skipping the "unpack the tarball" step. The autogen.sh command is ONLY required when installing from SVN. You need GNU Autoconf (version 2.60 or greater), GNU Automake (version 1.10 or greater) and their dependencies installed in order to run it. [3] Installing from the source tarball ====================================== If you are NOT installing from SVN, then unpack the tarball: tar xvf smartmontools-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 and other settings: --prefix=/usr/local --exec-prefix='${prefix}' --sbindir='${exec_prefix}/sbin' --sysconfdir='${prefix}/etc' --localstatedir='${prefix}/var' --datarootdir='${prefix}/share' --datadir='${datarootdir}' --mandir='${datarootdir}/man' --docdir='${datarootdir}/doc/smartmontools' --disable-sample --disable-scsi-cdb-check --enable-fast-lebe --without-initscriptdir --with-exampledir='${docdir}/examplescripts' --with-drivedbdir='${datadir}/smartmontools' --with-update-smart-drivedb --with-gnupg --with-smartdscriptdir='${sysconfdir}' --with-smartdplugindir='${smartdscriptdir}/smartd_warning.d' --with-scriptpath='/usr/local/bin:/bin:/usr/bin' --without-savestates --without-attributelog --with-os-deps='os_linux.o dev_areca.o' (platform specific) --without-selinux --with-libcap-ng=auto --with-libsystemd=auto --with-systemdsystemunitdir=auto --with-systemdenvfile=auto --with-nvme-devicescan (Linux, Windows: yes; Others: no) --without-solaris-sparc-ata (Solaris SPARC only) --with-signal-func=sigaction --with-working-snprintf --with-mingw-aslr=auto (Windows only) --with-cxx11-option=auto --without-cxx11-regex 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. The first output line of smartctl and smartd provides information about release number, last SVN checkin date and revision, platform, and package. The latter defaults to "(local build)" and can be changed by the variable BUILD_INFO, for example: make BUILD_INFO='"(Debian 5.39-2)"' [4] Guidelines for different Linux distributions ================================================ Note: Please send corrections/additions to: smartmontools-support@listi.jpberlin.de Red Hat: ./configure --sbindir=/usr/sbin \ --sysconfdir=/etc \ --mandir=/usr/share/man \ --docdir=/usr/share/doc/smartmontools \ --with-initscriptdir=/etc/rc.d/init.d 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 \ --docdir=/usr/share/doc/smartmontools \ --with-initscriptdir=/etc/rc.d 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 \ --docdir=/usr/share/doc/packages/smartmontools-VERSION \ --with-initscriptdir=/etc/init.d \ [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 \ --docdir=/usr/local/share/doc/smartmontools-VERSION \ --with-initscriptdir=/usr/local/etc/rc.d/ \ --enable-sample 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 If you'd like to build the i386 version on a powerpc machine, you can use CXX='g++ -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386' \ ./configure --host=i386-apple-darwin \ --with-initscriptdir=/Library/StartupItems [7] Guidelines for NetBSD ========================= ./configure --prefix=/usr/pkg \ --docdir=/usr/pkg/share/doc/smartmontools [8] Guidelines for Solaris ========================== smartmontools has been partially but not completely ported to Solaris. It includes complete SCSI support but no ATA or NVMe support. It can be compiled with either CC (Sun's C++ compiler) or GNU g++. To compile with g++: ./configure [args] make To compile with Sun CC: env CC=cc CXX=CC ./configure [args] make The correct arguments [args] to configure are: --sbindir=/usr/sbin \ --sysconfdir=/etc \ --mandir=/usr/share/man \ --docdir=/usr/share/doc/smartmontools-VERSION \ --with-initscriptdir=/etc/init.d [9] Guidelines for Cygwin ========================= ./configure --prefix=/usr \ --sysconfdir=/etc \ --with-initscriptdir=/etc/rc.d/init.d [10] Guidelines for Windows =========================== To compile statically linked Windows release with MinGW gcc on MSYS, use: ./configure make Instead of using "make install", copy the .exe files into some directory in the PATH. Cross-compile statically linked 32-bit and 64-bit versions with MinGW-w64: ./configure --build=$(./config.guess) \ --host=i686-w64-mingw32 ./configure --build=$(./config.guess) \ --host=x86_64-w64-mingw32 Tested on Cygwin, Debian, Fedora and Ubuntu. To create the Windows installer, use: make installer-win32 This builds the distribution directory and packs it into the self-extracting install program ./smartmontools-VERSION.win32-setup.exe The installer is build using the command "makensis" from the NSIS package (https://nsis.sourceforge.net/). To create a combined 32-/64-bit installer, use this in 32-bit build directory if 64-build directory is at ../build64: make builddir_win64=../build64 installer_win32 To both create and run the (interactive) installer, use: make install-win32 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 *.pdf preformatted man pages. To prepare os_win32/vc14 directory for MS Visual Studio C++ 2015 builds, use the following on MSYS or Cygwin: mkdir vctmp && cd vctmp ../configure [... any MinGW option set from above ...] make config-vc14 The MSVC project files (os_win32/vc14/*) are included in SVN (but not in source tarball). The target config-vc14 from a Makefile configured for MinGW creates os_win32/vc14/{config.h,smart*.rc,svnversion.h}. The configure script must be run outside of the source directory to avoid inclusion of the original config.h. Additional MSVC related make targets are clean-vc14, distclean-vc14 and maintainer-clean-vc14. [11] Guidelines for OS/2, eComStation ===================================== To compile the OS/2 code, please run ./configure 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 \ --docdir=/usr/local/share/doc/smartmontools-VERSION \ --enable-sample 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: /etc/smartd.conf.sample [13] Comments ============ To compile from another directory, you can replace the step ./configure [options] by the following: mkdir objdir cd objdir ../configure [options] Man pages contents is platform-specific by default. Info specific to other platforms may be not visible. To generate man pages with full contents use: make os_man_filter= 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 sudo /usr/sbin/smartctl -x /dev/sda Source and binary packages for Windows are available at https://sourceforge.net/projects/smartmontools/files/ Refer to https://www.smartmontools.org/wiki/Download for any additional download and installation instructions. The following files are installed if ./configure has no options: /usr/local/sbin/smartctl [Executable command-line utility] /usr/local/sbin/smartd [Executable daemon] /usr/local/sbin/update-smart-drivedb [Drive database update script] /usr/local/etc/smartd.conf [Configuration file for smartd daemon] /usr/local/etc/smartd_warning.sh [Warning script for smartd daemon] /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/man/man8/update-smart-drivedb.8 [Manual page] /usr/local/share/doc/smartmontools/AUTHORS [Information about the authors and developers] /usr/local/share/doc/smartmontools/ChangeLog [A log of changes. Also see SVN] /usr/local/share/doc/smartmontools/COPYING [GNU General Public License Version 2] /usr/local/share/doc/smartmontools/INSTALL [Installation instructions: what you're reading!] /usr/local/share/doc/smartmontools/NEWS [Significant enhancements and fixes] /usr/local/share/doc/smartmontools/README [Overview] /usr/local/share/doc/smartmontools/TODO [No longer maintained] /usr/local/share/doc/smartmontools/smartd.conf [Example configuration file for smartd] /usr/local/share/doc/smartmontools/examplescripts/ [Executable scripts for -M exec of smartd.conf (4 files)] /usr/local/share/smartmontools/drivedb.h [Drive database] Due to checks done by '--with-systemdsystemunitdir=auto', the following file may also be installed: /usr/local/lib/systemd/system/smartd.service [Systemd service file for smartd] If /usr/local/etc/smartd.conf exists and differs from the default then the default configuration file is installed as /usr/local/etc/smartd.conf.sample instead. The commands: make htmlman make pdfman make txtman may be used to build .html, .pdf 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 man, man2html and groff 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 --help a description of available configure options is printed [with defaults in square brackets]. See also section [3] above. The following old configure options are no longer supported: Old option Replacement --with-docdir=DIR --docdir=DIR (autoconf >= 2.60) --enable-drivedb [no option needed] --disable-drivedb --without-drivedbdir --enable-savestates --with-savestates[=yes] --disable-savestates [no option needed] --enable-attrbutelog --with-attributelog[=yes] --disable-savestates [no option needed] --with-initscriptdir[=yes] --with-initscriptdir=DIR --with-initscriptdir=auto --with-initscriptdir=DIR 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 --docdir /home/joe/share/doc/smartmontools --mandir /home/joe/share/man --sysconfdir /home/joe/etc --with-exampledir /home/joe/share/doc/smartmontools/examplescripts --with-drivedbdir /home/joe/share/smartmontools --with-initscriptdir [disabled] --with-systemdsystemunitdir [see below] If systemd is present (and pkg-config reports /lib/systemd/system as the systemdsystemunitdir): --with-systemdsystemunitdir /home/joe/lib/systemd/system else --with-systemdsystemunitdir [disabled] Additional information about using configure can be found here: https://www.gnu.org/software/autoconf/manual/autoconf.html#Running-configure-Scripts smartmontools-7.0/install-sh0000755000175000010010000003452413412155343013224 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2016-01-11.22; # UTC # 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. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly 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: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -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. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; 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 if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? 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 "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # 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 "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # 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 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $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 $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 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. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: smartmontools-7.0/json.cpp0000644000175000010010000003012013367137754012700 00000000000000/* * json.cpp * * Home page of code is: https://www.smartmontools.org * * Copyright (C) 2017-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include "json.h" const char * json_cvsid = "$Id: json.cpp 4830 2018-11-02 21:21:48Z chrfranke $" JSON_H_CVSID; #include "sg_unaligned.h" #include "utility.h" // uint128_*() #include #include static void jassert_failed(int line, const char * expr) { char msg[128]; // Avoid __FILE__ as it may break reproducible builds snprintf(msg, sizeof(msg), "json.cpp(%d): Assertion failed: %s", line, expr); throw std::logic_error(msg); } #define jassert(expr) (!(expr) ? jassert_failed(__LINE__, #expr) : (void)0) static void check_key(const char * key) { // Limit: object keys should be valid identifiers (lowercase only) char c = key[0]; jassert('a' <= c && c <= 'z'); for (int i = 1; (c = key[i]); i++) jassert(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || (c == '_')); } json::ref::ref(json & js, const char * key) : m_js(js) { check_key(key); m_path.push_back(node_info(key)); } json::ref::ref(const ref & base, const char * key) : m_js(base.m_js), m_path(base.m_path) { check_key(key); m_path.push_back(node_info(key)); } json::ref::ref(const ref & base, int index) : m_js(base.m_js), m_path(base.m_path) { jassert(0 <= index && index < 10000); // Limit: large arrays not supported m_path.push_back(node_info(index)); } json::ref::ref(const ref & base, const char * /*dummy*/, const char * key_suffix) : m_js(base.m_js), m_path(base.m_path) { int n = (int)m_path.size(), i; for (i = n; --i >= 0; ) { std::string & base_key = m_path[i].key; if (base_key.empty()) continue; // skip array base_key += key_suffix; break; } jassert(i >= 0); // Limit: top level element must be an object } void json::ref::operator=(bool value) { m_js.set_bool(m_path, value); } void json::ref::operator=(long long value) { m_js.set_int64(m_path, (int64_t)value); } void json::ref::operator=(unsigned long long value) { m_js.set_uint64(m_path, (uint64_t)value); } void json::ref::operator=(int value) { operator=((long long)value); } void json::ref::operator=(unsigned value) { operator=((unsigned long long)value); } void json::ref::operator=(long value) { operator=((long long)value); } void json::ref::operator=(unsigned long value) { operator=((unsigned long long)value); } void json::ref::operator=(const std::string & value) { m_js.set_string(m_path, value); } void json::ref::operator=(const char * value) { jassert(value); // Limit: null not supported operator=(std::string(value)); } void json::ref::set_uint128(uint64_t value_hi, uint64_t value_lo) { if (!value_hi) operator=((unsigned long long)value_lo); else m_js.set_uint128(m_path, value_hi, value_lo); } bool json::ref::set_if_safe_uint64(uint64_t value) { if (!is_safe_uint(value)) return false; operator=((unsigned long long)value); return true; } bool json::ref::set_if_safe_uint128(uint64_t value_hi, uint64_t value_lo) { if (value_hi) return false; return set_if_safe_uint64(value_lo); } bool json::ref::set_if_safe_le128(const void * pvalue) { return set_if_safe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8), sg_get_unaligned_le64( pvalue )); } void json::ref::set_unsafe_uint64(uint64_t value) { // Output as number "KEY" operator=((unsigned long long)value); if (!m_js.m_verbose && is_safe_uint(value)) return; // Output as string "KEY_s" char s[32]; snprintf(s, sizeof(s), "%" PRIu64, value); with_suffix("_s") = s; } void json::ref::set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo) { if (!m_js.m_verbose && !value_hi) set_unsafe_uint64(value_lo); else { // Output as number "KEY", string "KEY_s" and LE byte array "KEY_le[]" m_js.m_uint128_output = true; set_uint128(value_hi, value_lo); char s[64]; with_suffix("_s") = uint128_hilo_to_str(s, value_hi, value_lo); ref le = with_suffix("_le"); for (int i = 0; i < 8; i++) { uint64_t v = (value_lo >> (i << 3)); if (!v && !value_hi) break; le[i] = v & 0xff; } for (int i = 0; i < 8; i++) { uint64_t v = value_hi >> (i << 3); if (!v) break; le[8 + i] = v & 0xff; } } } void json::ref::set_unsafe_le128(const void * pvalue) { set_unsafe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8), sg_get_unaligned_le64( pvalue )); } json::node::node() : type(nt_unset), intval(0), intval_hi(0) { } json::node::node(const std::string & key_) : type(nt_unset), intval(0), intval_hi(0), key(key_) { } json::node::~node() { for (size_t i = 0; i < childs.size(); i++) delete childs[i]; } json::node::const_iterator::const_iterator(const json::node * node_p, bool sorted) : m_node_p(node_p), m_use_map(sorted && node_p->type == nt_object), m_child_idx(0) { if (m_use_map) m_key_iter = node_p->key2index.begin(); } bool json::node::const_iterator::at_end() const { if (m_use_map) return (m_key_iter == m_node_p->key2index.end()); else return (m_child_idx >= m_node_p->childs.size()); } unsigned json::node::const_iterator::array_index() const { jassert(m_node_p->type == nt_array); return m_child_idx; } void json::node::const_iterator::operator++() { if (m_use_map) ++m_key_iter; else ++m_child_idx; } const json::node * json::node::const_iterator::operator*() const { if (m_use_map) return m_node_p->childs[m_key_iter->second]; else return m_node_p->childs[m_child_idx]; } json::node * json::find_or_create_node(const json::node_path & path, node_type type) { node * p = &m_root_node; for (unsigned i = 0; i < path.size(); i++) { const node_info & pi = path[i]; if (!pi.key.empty()) { // Object if (p->type == nt_unset) p->type = nt_object; else jassert(p->type == nt_object); // Limit: type change not supported // Existing or new object element? node::keymap::iterator ni = p->key2index.find(pi.key); node * p2; if (ni != p->key2index.end()) { // Object element exists p2 = p->childs[ni->second]; } else { // Create new object element p->key2index[pi.key] = (unsigned)p->childs.size(); p2 = new node(pi.key); p->childs.push_back(p2); } jassert(p2 && p2->key == pi.key); p = p2; } else { // Array if (p->type == nt_unset) p->type = nt_array; else jassert(p->type == nt_array); // Limit: type change not supported node * p2; // Existing or new array element? if (pi.index < (int)p->childs.size()) { // Array index exists p2 = p->childs[pi.index]; if (!p2) // Already created ? p->childs[pi.index] = p2 = new node; } else { // Grow array, fill gap, create new element p->childs.resize(pi.index + 1); p->childs[pi.index] = p2 = new node; } jassert(p2 && p2->key.empty()); p = p2; } } if ( p->type == nt_unset || ( nt_int <= p->type && p->type <= nt_uint128 && nt_int <= type && type <= nt_uint128)) p->type = type; else jassert(p->type == type); // Limit: type change not supported return p; } json::json() : m_enabled(false), m_verbose(false), m_uint128_output(false) { } void json::set_bool(const node_path & path, bool value) { if (!m_enabled) return; find_or_create_node(path, nt_bool)->intval = (value ? 1 : 0); } void json::set_int64(const node_path & path, int64_t value) { if (!m_enabled) return; find_or_create_node(path, nt_int)->intval = (uint64_t)value; } void json::set_uint64(const node_path & path, uint64_t value) { if (!m_enabled) return; find_or_create_node(path, nt_uint)->intval = value; } void json::set_uint128(const node_path & path, uint64_t value_hi, uint64_t value_lo) { if (!m_enabled) return; node * p = find_or_create_node(path, nt_uint128); p->intval_hi = value_hi; p->intval = value_lo; } void json::set_string(const node_path & path, const std::string & value) { if (!m_enabled) return; find_or_create_node(path, nt_string)->strval = value; } static void print_string(FILE * f, const char * s) { putc('"', f); for (int i = 0; s[i]; i++) { char c = s[i]; if (c == '"' || c == '\\') putc('\\', f); else if (c == '\t') { putc('\\', f); c = 't'; } else if ((unsigned char)c < ' ') c = '?'; // Not ' '-'~', '\t' or UTF-8 putc(c, f); } putc('"', f); } void json::print_json(FILE * f, bool pretty, bool sorted, const node * p, int level) { if (!p->key.empty()) fprintf(f, "\"%s\":%s", p->key.c_str(), (pretty ? " " : "")); switch (p->type) { case nt_object: case nt_array: putc((p->type == nt_object ? '{' : '['), f); if (!p->childs.empty()) { bool first = true; for (node::const_iterator it(p, sorted); !it.at_end(); ++it) { if (!first) putc(',', f); if (pretty) fprintf(f, "\n%*s", (level + 1) * 2, ""); const node * p2 = *it; if (!p2) { // Unset element of sparse array jassert(p->type == nt_array); fputs("null", f); } else { // Recurse print_json(f, pretty, sorted, p2, level + 1); } first = false; } if (pretty) fprintf(f, "\n%*s", level * 2, ""); } putc((p->type == nt_object ? '}' : ']'), f); break; case nt_bool: fputs((p->intval ? "true" : "false"), f); break; case nt_int: fprintf(f, "%" PRId64, (int64_t)p->intval); break; case nt_uint: fprintf(f, "%" PRIu64, p->intval); break; case nt_uint128: { char buf[64]; fputs(uint128_hilo_to_str(buf, p->intval_hi, p->intval), f); } break; case nt_string: print_string(f, p->strval.c_str()); break; default: jassert(false); } } void json::print_flat(FILE * f, bool sorted, const node * p, std::string & path) { switch (p->type) { case nt_object: case nt_array: fprintf(f, "%s = %s;\n", path.c_str(), (p->type == nt_object ? "{}" : "[]")); if (!p->childs.empty()) { unsigned len = path.size(); for (node::const_iterator it(p, sorted); !it.at_end(); ++it) { const node * p2 = *it; if (p->type == nt_array) { char buf[10]; snprintf(buf, sizeof(buf), "[%u]", it.array_index()); path += buf; } else { path += '.'; path += p2->key; } if (!p2) { // Unset element of sparse array jassert(p->type == nt_array); fprintf(f, "%s = null;\n", path.c_str()); } else { // Recurse print_flat(f, sorted, p2, path); } path.erase(len); } } break; case nt_bool: fprintf(f, "%s = %s;\n", path.c_str(), (p->intval ? "true" : "false")); break; case nt_int: fprintf(f, "%s = %" PRId64 ";\n", path.c_str(), (int64_t)p->intval); break; case nt_uint: fprintf(f, "%s = %" PRIu64 ";\n", path.c_str(), p->intval); break; case nt_uint128: { char buf[64]; fprintf(f, "%s = %s;\n", path.c_str(), uint128_hilo_to_str(buf, p->intval_hi, p->intval)); } break; case nt_string: fprintf(f, "%s = ", path.c_str()); print_string(f, p->strval.c_str()); fputs(";\n", f); break; default: jassert(false); } } void json::print(FILE * f, const print_options & options) const { if (m_root_node.type == nt_unset) return; jassert(m_root_node.type == nt_object); if (!options.flat) { print_json(f, options.pretty, options.sorted, &m_root_node, 0); if (options.pretty) putc('\n', f); } else { std::string path("json"); print_flat(f, options.sorted, &m_root_node, path); } } smartmontools-7.0/json.h0000644000175000010010000001174413364416727012355 00000000000000/* * json.h * * Home page of code is: https://www.smartmontools.org * * Copyright (C) 2017-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef JSON_H_CVSID #define JSON_H_CVSID "$Id: json.h 4825 2018-10-25 19:47:35Z chrfranke $" #include #include #include #include #include /// Create and print JSON output. class json { private: struct node_info { std::string key; int index; node_info() : index(0) { } explicit node_info(const char * key_) : key(key_), index(0) { } explicit node_info(int index_) : index(index_) { } }; typedef std::vector node_path; public: /// Return true if value is a safe JSON integer. /// Same as Number.isSafeInteger(value) in JavaScript. static bool is_safe_uint(unsigned long long value) { return (value < (1ULL << 53)); } json(); /// Reference to a JSON element. class ref { public: /// Return reference to object element. ref operator[](const char * key) const { return ref(*this, key); }; /// Return reference to array element. ref operator[](int index) const { return ref(*this, index); }; // Assignment operators create or change element. void operator=(bool value); void operator=(int value); void operator=(unsigned value); void operator=(long value); void operator=(unsigned long value); void operator=(long long value); void operator=(unsigned long long value); void operator=(const char * value); void operator=(const std::string & value); /// Return reference to element with KEY_SUFFIX appended to last key. ref with_suffix(const char * key_suffix) const { return ref(*this, "", key_suffix); } void set_uint128(uint64_t value_hi, uint64_t value_lo); // Output only if safe integer. bool set_if_safe_uint64(uint64_t value); bool set_if_safe_uint128(uint64_t value_hi, uint64_t value_lo); bool set_if_safe_le128(const void * pvalue); // If unsafe integer, output also as string with key "NUMBER_s". void set_unsafe_uint64(uint64_t value); void set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo); void set_unsafe_le128(const void * pvalue); private: friend class json; ref(json & js, const char * key); ref(const ref & base, const char * key); ref(const ref & base, int index); ref(const ref & base, const char * /*dummy*/, const char * key_suffix); json & m_js; node_path m_path; }; /// Return reference to element of top level object. ref operator[](const char * key) { return ref(*this, key); }; /// Enable/disable JSON output. void enable(bool yes = true) { m_enabled = yes; } /// Return true if enabled. bool is_enabled() const { return m_enabled; } /// Enable/disable extra string output for safe integers also. void set_verbose(bool yes = true) { m_verbose = yes; } /// Return true if any 128-bit value has been output. bool has_uint128_output() const { return m_uint128_output; } /// Options for print(). struct print_options { bool pretty; //< Pretty-print output. bool sorted; //< Sort object keys. bool flat; //< Print flat format. print_options() : pretty(false), sorted(false), flat(false) { } }; /// Print JSON tree to a file. void print(FILE * f, const print_options & options) const; private: enum node_type { nt_unset, nt_object, nt_array, nt_bool, nt_int, nt_uint, nt_uint128, nt_string }; struct node { node(); explicit node(const std::string & key_); ~node(); node_type type; uint64_t intval, intval_hi; std::string strval; std::string key; std::vector childs; typedef std::map keymap; keymap key2index; class const_iterator { public: const_iterator(const node * node_p, bool sorted); bool at_end() const; unsigned array_index() const; void operator++(); const node * operator*() const; private: const node * m_node_p; bool m_use_map; unsigned m_child_idx; keymap::const_iterator m_key_iter; }; #if __cplusplus >= 201103 node(const node &) = delete; void operator=(const node &) = delete; #else private: node(const node &); void operator=(const node &); #endif }; bool m_enabled; bool m_verbose; bool m_uint128_output; node m_root_node; node * find_or_create_node(const node_path & path, node_type type); void set_bool(const node_path & path, bool value); void set_int64(const node_path & path, int64_t value); void set_uint64(const node_path & path, uint64_t value); void set_uint128(const node_path & path, uint64_t value_hi, uint64_t value_lo); void set_string(const node_path & path, const std::string & value); static void print_json(FILE * f, bool pretty, bool sorted, const node * p, int level); static void print_flat(FILE * f, bool sorted, const node * p, std::string & path); }; #endif // JSON_H_CVSID smartmontools-7.0/knowndrives.cpp0000644000175000010010000006717513401001476014300 00000000000000/* * knowndrives.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-11 Philip Williams, Bruce Allen * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include "atacmds.h" #include "knowndrives.h" #include "utility.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include // access() #endif #include const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 4842 2018-12-02 16:07:26Z chrfranke $" KNOWNDRIVES_H_CVSID; #define MODEL_STRING_LENGTH 40 #define FIRMWARE_STRING_LENGTH 8 #define TABLEPRINTWIDTH 19 // Builtin table of known drives. // Used as a default if not read from // "/usr/{,/local}share/smartmontools/drivedb.h" // or any other file specified by '-B' option, // see read_default_drive_databases() below. // The drive_settings structure is described in drivedb.h. const drive_settings builtin_knowndrives[] = { #include "drivedb.h" }; const unsigned builtin_knowndrives_size = sizeof(builtin_knowndrives) / sizeof(builtin_knowndrives[0]); /// Drive database class. Stores custom entries read from file. /// Provides transparent access to concatenation of custom and /// default table. class drive_database { public: drive_database(); ~drive_database(); /// Get total number of entries. unsigned size() const { return m_custom_tab.size() + m_builtin_size; } /// Get number of custom entries. unsigned custom_size() const { return m_custom_tab.size(); } /// Array access. const drive_settings & operator[](unsigned i); /// Append new custom entry. void push_back(const drive_settings & src); /// Append builtin table. void append(const drive_settings * builtin_tab, unsigned builtin_size) { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; } private: const drive_settings * m_builtin_tab; unsigned m_builtin_size; std::vector m_custom_tab; std::vector m_custom_strings; const char * copy_string(const char * str); drive_database(const drive_database &); void operator=(const drive_database &); }; drive_database::drive_database() : m_builtin_tab(0), m_builtin_size(0) { } drive_database::~drive_database() { for (unsigned i = 0; i < m_custom_strings.size(); i++) delete [] m_custom_strings[i]; } const drive_settings & drive_database::operator[](unsigned i) { return (i < m_custom_tab.size() ? m_custom_tab[i] : m_builtin_tab[i - m_custom_tab.size()] ); } void drive_database::push_back(const drive_settings & src) { drive_settings dest; dest.modelfamily = copy_string(src.modelfamily); dest.modelregexp = copy_string(src.modelregexp); dest.firmwareregexp = copy_string(src.firmwareregexp); dest.warningmsg = copy_string(src.warningmsg); dest.presets = copy_string(src.presets); m_custom_tab.push_back(dest); } const char * drive_database::copy_string(const char * src) { size_t len = strlen(src); char * dest = new char[len+1]; memcpy(dest, src, len+1); try { m_custom_strings.push_back(dest); } catch (...) { delete [] dest; throw; } return dest; } /// The drive database. static drive_database knowndrives; enum dbentry_type { DBENTRY_ATA_DEFAULT, DBENTRY_ATA, DBENTRY_USB }; // Return type of entry static dbentry_type get_modelfamily_type(const char * modelfamily) { if (modelfamily[0] == 'D' && !strcmp(modelfamily, "DEFAULT")) return DBENTRY_ATA_DEFAULT; else if(modelfamily[0] == 'U' && str_starts_with(modelfamily, "USB:")) return DBENTRY_USB; else return DBENTRY_ATA; } static inline dbentry_type get_dbentry_type(const drive_settings * dbentry) { return get_modelfamily_type(dbentry->modelfamily); } // Compile regular expression, print message on failure. static bool compile(regular_expression & regex, const char *pattern) { if (!regex.compile(pattern)) { pout("Internal error: unable to compile regular expression \"%s\": %s\n" "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", pattern, regex.get_errmsg()); return false; } return true; } // Compile & match a regular expression. static bool match(const char * pattern, const char * str) { regular_expression regex; if (!compile(regex, pattern)) return false; return regex.full_match(str); } // 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 entry of the // first match in knowndrives[] or 0 if no match if found. static const drive_settings * lookup_drive(const char * model, const char * firmware) { if (!model) model = ""; if (!firmware) firmware = ""; for (unsigned i = 0; i < knowndrives.size(); i++) { // Skip DEFAULT and USB entries if (get_dbentry_type(&knowndrives[i]) != DBENTRY_ATA) continue; // Check whether model matches the regular expression in knowndrives[i]. if (!match(knowndrives[i].modelregexp, model)) continue; // Model matches, now check firmware. "" matches always. if (!( !*knowndrives[i].firmwareregexp || match(knowndrives[i].firmwareregexp, firmware))) continue; // Found return &knowndrives[i]; } // Not found return 0; } // Parse drive or USB options in preset string, return false on error. static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs, firmwarebug_defs * firmwarebugs, std::string * type) { for (int i = 0; ; ) { i += strspn(presets+i, " \t"); if (!presets[i]) break; char opt, arg[80+1+13]; int len = -1; if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0)) return false; if (opt == 'v' && defs) { // Parse "-v N,format[,name[,HDD|SSD]]" if (!parse_attribute_def(arg, *defs, (firmwarebugs ? PRIOR_DATABASE : PRIOR_DEFAULT))) return false; } else if (opt == 'F' && firmwarebugs) { firmwarebug_defs bug; if (!parse_firmwarebug_def(arg, bug)) return false; // Don't set if user specified '-F none'. if (!firmwarebugs->is_set(BUG_NONE)) firmwarebugs->set(bug); } else if (opt == 'd' && type) { // TODO: Check valid types *type = arg; } else return false; i += len; } return true; } // Parse '-v' options in default preset string, return false on error. static inline bool parse_default_presets(const char * presets, ata_vendor_attr_defs & defs) { return parse_db_presets(presets, &defs, 0, 0); } // Parse '-v' and '-F' options in preset string, return false on error. static inline bool parse_presets(const char * presets, ata_vendor_attr_defs & defs, firmwarebug_defs & firmwarebugs) { return parse_db_presets(presets, &defs, &firmwarebugs, 0); } // Parse '-d' option in preset string, return false on error. static inline bool parse_usb_type(const char * presets, std::string & type) { return parse_db_presets(presets, 0, 0, &type); } // Parse "USB: [DEVICE] ; [BRIDGE]" string static void parse_usb_names(const char * names, usb_dev_info & info) { int n1 = -1, n2 = -1, n3 = -1; sscanf(names, "USB: %n%*[^;]%n; %n", &n1, &n2, &n3); if (0 < n1 && n1 < n2) info.usb_device.assign(names+n1, n2-n1); else sscanf(names, "USB: ; %n", &n3); if (0 < n3) info.usb_bridge = names+n3; } // Search drivedb for USB device with vendor:product ID. int lookup_usb_device(int vendor_id, int product_id, int bcd_device, usb_dev_info & info, usb_dev_info & info2) { // Format strings to match char usb_id_str[16], bcd_dev_str[16]; snprintf(usb_id_str, sizeof(usb_id_str), "0x%04x:0x%04x", vendor_id, product_id); if (bcd_device >= 0) snprintf(bcd_dev_str, sizeof(bcd_dev_str), "0x%04x", bcd_device); else bcd_dev_str[0] = 0; int found = 0; for (unsigned i = 0; i < knowndrives.size(); i++) { const drive_settings & dbentry = knowndrives[i]; // Skip drive entries if (get_dbentry_type(&dbentry) != DBENTRY_USB) continue; // Check whether USB vendor:product ID matches if (!match(dbentry.modelregexp, usb_id_str)) continue; // Parse '-d type' usb_dev_info d; if (!parse_usb_type(dbentry.presets, d.usb_type)) return 0; // Syntax error parse_usb_names(dbentry.modelfamily, d); // If two entries with same vendor:product ID have different // types, use bcd_device (if provided by OS) to select entry. if ( *dbentry.firmwareregexp && *bcd_dev_str && match(dbentry.firmwareregexp, bcd_dev_str)) { // Exact match including bcd_device info = d; found = 1; break; } else if (!found) { // First match without bcd_device info = d; found = 1; } else if (info.usb_type != d.usb_type) { // Another possible match with different type info2 = d; found = 2; break; } // Stop search at first matching entry with empty bcd_device if (!*dbentry.firmwareregexp) break; } return found; } // Shows one entry of knowndrives[], returns #errors. static int showonepreset(const drive_settings * dbentry) { // Basic error check if (!( dbentry && dbentry->modelfamily && dbentry->modelregexp && *dbentry->modelregexp && dbentry->firmwareregexp && dbentry->warningmsg && dbentry->presets )) { pout("Invalid drive database entry. Please report\n" "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n"); return 1; } dbentry_type type = get_dbentry_type(dbentry); bool usb = (type == DBENTRY_USB); // print and check model and firmware regular expressions int errcnt = 0; regular_expression regex; pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"), dbentry->modelregexp); if (!compile(regex, dbentry->modelregexp)) errcnt++; pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"), *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change) if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp)) errcnt++; if (!usb) { pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily); // if there are any presets, then show them firmwarebug_defs firmwarebugs; bool first_preset = true; if (*dbentry->presets) { ata_vendor_attr_defs defs; if (type == DBENTRY_ATA_DEFAULT) { if (!parse_default_presets(dbentry->presets, defs)) { pout("Syntax error in DEFAULT option string \"%s\"\n", dbentry->presets); errcnt++; } } else { if (!parse_presets(dbentry->presets, defs, firmwarebugs)) { pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); errcnt++; } } for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) { if (defs[i].priority != PRIOR_DEFAULT || !defs[i].name.empty()) { std::string name = ata_get_smart_attr_name(i, defs); // Use leading zeros instead of spaces so that everything lines up. pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", i, name.c_str()); // Check max name length suitable for smartctl -A output const unsigned maxlen = 23; if (name.size() > maxlen) { pout("%*s\n", TABLEPRINTWIDTH+6+maxlen, "Error: Attribute name too long ------^"); errcnt++; } first_preset = false; } } } if (first_preset) pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required."); // describe firmwarefix for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) { if (!firmwarebugs.is_set((firmwarebug_t)b)) continue; const char * fixdesc; switch ((firmwarebug_t)b) { case BUG_NOLOGDIR: fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)"; break; case BUG_SAMSUNG: fixdesc = "Fixes byte order in some SMART data (same as -F samsung)"; break; case BUG_SAMSUNG2: fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)"; break; case BUG_SAMSUNG3: fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)"; break; case BUG_XERRORLBA: fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)"; break; default: fixdesc = "UNKNOWN"; errcnt++; break; } pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc); } } else { // Print USB info usb_dev_info info; parse_usb_names(dbentry->modelfamily, info); pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Device:", (!info.usb_device.empty() ? info.usb_device.c_str() : "[unknown]")); pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Bridge:", (!info.usb_bridge.empty() ? info.usb_bridge.c_str() : "[unknown]")); if (*dbentry->presets && !parse_usb_type(dbentry->presets, info.usb_type)) { pout("Syntax error in USB type string \"%s\"\n", dbentry->presets); errcnt++; } pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type", (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]")); } // Print any special warnings if (*dbentry->warningmsg) pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg); return errcnt; } // Shows all presets for drives in knowndrives[]. // Returns #syntax errors. int showallpresets() { // loop over all entries in the knowndrives[] table, printing them // out in a nice format int errcnt = 0; for (unsigned i = 0; i < knowndrives.size(); i++) { errcnt += showonepreset(&knowndrives[i]); pout("\n"); } pout("Total number of entries :%5u\n" "Entries read from file(s):%5u\n\n", knowndrives.size(), knowndrives.custom_size()); pout("For information about adding a drive to the database see the FAQ on the\n"); pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n"); if (errcnt > 0) pout("\nFound %d syntax error(s) in database.\n" "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt); return errcnt; } // Shows all matching presets for a drive in knowndrives[]. // Returns # matching entries. int showmatchingpresets(const char *model, const char *firmware) { int cnt = 0; const char * firmwaremsg = (firmware ? firmware : "(any)"); for (unsigned i = 0; i < knowndrives.size(); i++) { if (!match(knowndrives[i].modelregexp, model)) continue; if ( firmware && *knowndrives[i].firmwareregexp && !match(knowndrives[i].firmwareregexp, firmware)) continue; // Found 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"); } 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 show_presets(const ata_identify_device * drive) { char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; // get the drive's model/firmware strings ata_format_id_string(model, drive->model, sizeof(model)-1); ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); // and search to see if they match values in the table const drive_settings * dbentry = lookup_drive(model, firmware); if (!dbentry) { // 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(dbentry); } // Searches drive database and sets preset vendor attribute // options in defs and firmwarebugs. // Values that have already been set will not be changed. // Returns pointer to database entry or nullptr if none found const drive_settings * lookup_drive_apply_presets( const ata_identify_device * drive, ata_vendor_attr_defs & defs, firmwarebug_defs & firmwarebugs) { // get the drive's model/firmware strings char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1]; ata_format_id_string(model, drive->model, sizeof(model)-1); ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1); // Look up the drive in knowndrives[]. const drive_settings * dbentry = lookup_drive(model, firmware); if (!dbentry) return 0; if (*dbentry->presets) { // Apply presets if (!parse_presets(dbentry->presets, defs, firmwarebugs)) pout("Syntax error in preset option string \"%s\"\n", dbentry->presets); } return dbentry; } ///////////////////////////////////////////////////////////////////////////// // Parser for drive database files // Abstract pointer to read file input. // Operations supported: c = *p; c = p[1]; ++p; class stdin_iterator { public: explicit stdin_iterator(FILE * f) : m_f(f), m_next(0) { get(); get(); } stdin_iterator & operator++() { get(); return *this; } char operator*() const { return m_c; } char operator[](int i) const { if (i != 1) fail(); return m_next; } private: FILE * m_f; char m_c, m_next; void get(); void fail() const; }; void stdin_iterator::get() { m_c = m_next; int ch = getc(m_f); m_next = (ch != EOF ? ch : 0); } void stdin_iterator::fail() const { throw std::runtime_error("stdin_iterator: wrong usage"); } // Use above as parser input 'pointer'. Can easily be changed later // to e.g. 'const char *' if above is too slow. typedef stdin_iterator parse_ptr; // Skip whitespace and comments. static parse_ptr skip_white(parse_ptr src, const char * path, int & line) { for ( ; ; ++src) switch (*src) { case ' ': case '\t': continue; case '\n': ++line; continue; case '/': switch (src[1]) { case '/': // skip '// comment' ++src; ++src; while (*src && *src != '\n') ++src; if (*src) ++line; break; case '*': // skip '/* comment */' ++src; ++src; for (;;) { if (!*src) { pout("%s(%d): Missing '*/'\n", path, line); return src; } char c = *src; ++src; if (c == '\n') ++line; else if (c == '*' && *src == '/') break; } break; default: return src; } continue; default: return src; } } // Info about a token. struct token_info { char type; int line; std::string value; token_info() : type(0), line(0) { } }; // Get next token. static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line) { src = skip_white(src, path, line); switch (*src) { case '{': case '}': case ',': // Simple token token.type = *src; token.line = line; ++src; break; case '"': // String constant token.type = '"'; token.line = line; token.value = ""; do { for (++src; *src != '"'; ++src) { char c = *src; if (!c || c == '\n' || (c == '\\' && !src[1])) { pout("%s(%d): Missing terminating '\"'\n", path, line); token.type = '?'; token.line = line; return src; } if (c == '\\') { c = *++src; switch (c) { case 'n' : c = '\n'; break; case '\n': ++line; break; case '\\': case '"': break; default: pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c); token.type = '?'; token.line = line; continue; } } token.value += c; } // Lookahead to detect string constant concatenation src = skip_white(++src, path, line); } while (*src == '"'); break; case 0: // EOF token.type = 0; token.line = line; break; default: pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src); token.type = '?'; token.line = line; while (*src && *src != '\n') ++src; break; } return src; } // Parse drive database from abstract input pointer. static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path) { int state = 0, field = 0; std::string values[5]; bool ok = true; token_info token; int line = 1; src = get_token(src, token, path, line); for (;;) { // EOF is ok after '}', trailing ',' is also allowed. if (!token.type && (state == 0 || state == 4)) break; // Check expected token const char expect[] = "{\",},"; if (token.type != expect[state]) { if (token.type != '?') pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]); ok = false; // Skip to next entry while (token.type && token.type != '{') src = get_token(src, token, path, line); state = 0; if (token.type) continue; break; } // Interpret parser state switch (state) { case 0: // ... ^{...} state = 1; field = 0; break; case 1: // {... ^"..." ...} switch (field) { case 1: case 2: if (!token.value.empty()) { regular_expression regex; if (!regex.compile(token.value.c_str())) { pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg()); ok = false; } } else if (field == 1) { pout("%s(%d): Missing regular expression for drive model\n", path, token.line); ok = false; } break; case 4: if (!token.value.empty()) { // Syntax check switch (get_modelfamily_type(values[0].c_str())) { case DBENTRY_ATA_DEFAULT: { ata_vendor_attr_defs defs; if (!parse_default_presets(token.value.c_str(), defs)) { pout("%s(%d): Syntax error in DEFAULT option string\n", path, token.line); ok = false; } } break; default: { // DBENTRY_ATA ata_vendor_attr_defs defs; firmwarebug_defs fix; if (!parse_presets(token.value.c_str(), defs, fix)) { pout("%s(%d): Syntax error in preset option string\n", path, token.line); ok = false; } } break; case DBENTRY_USB: { std::string type; if (!parse_usb_type(token.value.c_str(), type)) { pout("%s(%d): Syntax error in USB type string\n", path, token.line); ok = false; } } break; } } break; } values[field] = token.value; state = (++field < 5 ? 2 : 3); break; case 2: // {... "..."^, ...} state = 1; break; case 3: // {...^}, ... { drive_settings entry; entry.modelfamily = values[0].c_str(); entry.modelregexp = values[1].c_str(); entry.firmwareregexp = values[2].c_str(); entry.warningmsg = values[3].c_str(); entry.presets = values[4].c_str(); db.push_back(entry); } state = 4; break; case 4: // {...}^, ... state = 0; break; default: pout("Bad state %d\n", state); return false; } src = get_token(src, token, path, line); } return ok; } // Read drive database from file. bool read_drive_database(const char * path) { stdio_file f(path, "r" #ifdef __CYGWIN__ // Allow files with '\r\n'. "t" #endif ); if (!f) { pout("%s: cannot open drive database file\n", path); return false; } return parse_drive_database(parse_ptr(f), knowndrives, path); } // Get path for additional database file const char * get_drivedb_path_add() { #ifndef _WIN32 return SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h"; #else static std::string path = get_exe_dir() + "/drivedb-add.h"; return path.c_str(); #endif } #ifdef SMARTMONTOOLS_DRIVEDBDIR // Get path for default database file const char * get_drivedb_path_default() { #ifndef _WIN32 return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h"; #else static std::string path = get_exe_dir() + "/drivedb.h"; return path.c_str(); #endif } #endif // Read drive databases from standard places. static bool read_default_drive_databases() { // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h const char * db1 = get_drivedb_path_add(); if (!access(db1, 0)) { if (!read_drive_database(db1)) return false; } #ifdef SMARTMONTOOLS_DRIVEDBDIR // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h const char * db2 = get_drivedb_path_default(); if (!access(db2, 0)) { if (!read_drive_database(db2)) return false; } else #endif { // Append builtin table. knowndrives.append(builtin_knowndrives, builtin_knowndrives_size); } return true; } static ata_vendor_attr_defs default_attr_defs; // Initialize default_attr_defs. static bool init_default_attr_defs() { // Lookup default entry const drive_settings * entry = 0; for (unsigned i = 0; i < knowndrives.size(); i++) { if (get_dbentry_type(&knowndrives[i]) != DBENTRY_ATA_DEFAULT) continue; entry = &knowndrives[i]; break; } if (!entry) { // Fall back to builtin database for (unsigned i = 0; i < builtin_knowndrives_size; i++) { if (get_dbentry_type(&builtin_knowndrives[i]) != DBENTRY_ATA_DEFAULT) continue; entry = &builtin_knowndrives[i]; break; } if (!entry) throw std::logic_error("DEFAULT entry missing in builtin drive database"); pout("Warning: DEFAULT entry missing in drive database file(s)\n"); } if (!parse_default_presets(entry->presets, default_attr_defs)) { pout("Syntax error in DEFAULT drive database entry\n"); return false; } return true; } // Init default db entry and optionally read drive databases from standard places. bool init_drive_database(bool use_default_db) { if (use_default_db && !read_default_drive_databases()) return false; return init_default_attr_defs(); } // Get vendor attribute options from default db entry. const ata_vendor_attr_defs & get_default_attr_defs() { return default_attr_defs; } smartmontools-7.0/knowndrives.h0000644000175000010010000000443113336335341013737 00000000000000/* * knowndrives.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-11 Philip Williams, Bruce Allen * Copyright (C) 2008-15 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef KNOWNDRIVES_H_ #define KNOWNDRIVES_H_ #define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 4760 2018-08-19 18:45:53Z chrfranke $\n" // Structure to store drive database entries, see drivedb.h for a description. struct drive_settings { const char * modelfamily; const char * modelregexp; const char * firmwareregexp; const char * warningmsg; const char * presets; }; // info returned by lookup_usb_device() struct usb_dev_info { std::string usb_device; // Device name, empty if unknown std::string usb_bridge; // USB bridge name, empty if unknown std::string usb_type; // Type string ('-d' option). }; // Search drivedb for USB device with vendor:product ID. int lookup_usb_device(int vendor_id, int product_id, int bcd_device, usb_dev_info & info, usb_dev_info & info2); // Shows the presets (if any) that are available for the given drive. void show_presets(const ata_identify_device * drive); // Shows all presets for drives in knowndrives[]. // Returns #syntax errors. int showallpresets(); // Shows all matching presets for a drive in knowndrives[]. // Returns # matching entries. int showmatchingpresets(const char *model, const char *firmware); // Searches drive database and sets preset vendor attribute // options in defs and firmwarebugs. // Values that have already been set will not be changed. // Returns pointer to database entry or nullptr if none found. const drive_settings * lookup_drive_apply_presets( const ata_identify_device * drive, ata_vendor_attr_defs & defs, firmwarebug_defs & firmwarebugs); // Get path for additional database file const char * get_drivedb_path_add(); #ifdef SMARTMONTOOLS_DRIVEDBDIR // Get path for default database file const char * get_drivedb_path_default(); #endif // Read drive database from file. bool read_drive_database(const char * path); // Init default db entry and optionally read drive databases from standard places. bool init_drive_database(bool use_default_db); // Get vendor attribute options from default db entry. const ata_vendor_attr_defs & get_default_attr_defs(); #endif smartmontools-7.0/linux_nvme_ioctl.h0000644000175000010010000000302712673070121014737 00000000000000/* * Definitions for the NVM Express ioctl interface * Copyright (c) 2011-2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef _UAPI_LINUX_NVME_IOCTL_H #define _UAPI_LINUX_NVME_IOCTL_H #include struct nvme_user_io { __u8 opcode; __u8 flags; __u16 control; __u16 nblocks; __u16 rsvd; __u64 metadata; __u64 addr; __u64 slba; __u32 dsmgmt; __u32 reftag; __u16 apptag; __u16 appmask; }; struct nvme_passthru_cmd { __u8 opcode; __u8 flags; __u16 rsvd1; __u32 nsid; __u32 cdw2; __u32 cdw3; __u64 metadata; __u64 addr; __u32 metadata_len; __u32 data_len; __u32 cdw10; __u32 cdw11; __u32 cdw12; __u32 cdw13; __u32 cdw14; __u32 cdw15; __u32 timeout_ms; __u32 result; }; #define nvme_admin_cmd nvme_passthru_cmd #define NVME_IOCTL_ID _IO('N', 0x40) #define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd) #define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io) #define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd) #define NVME_IOCTL_RESET _IO('N', 0x44) #define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) #endif /* _UAPI_LINUX_NVME_IOCTL_H */ smartmontools-7.0/m4/0000755000175000010010000000000013412155406011610 500000000000000smartmontools-7.0/m4/pkg.m40000644000175000010010000002401113412155331012546 00000000000000dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- dnl serial 11 (pkg-config-0.29.1) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR smartmontools-7.0/Makefile.am0000644000175000010010000010214113402014526013237 00000000000000## Process this file with automake to produce Makefile.in # # $Id: Makefile.am 4848 2018-12-05 18:30:46Z chrfranke $ # @SET_MAKE@ ACLOCAL_AMFLAGS = -I m4 # Make sure .cpp takes precedence to avoid compiling old .c file SUFFIXES = .cpp .c .s .o # BUILD_INFO can be provided by package maintainers (see INSTALL file) BUILD_INFO= "(local build)" AM_CPPFLAGS = \ -DBUILD_INFO='$(BUILD_INFO)' \ -DSMARTMONTOOLS_SYSCONFDIR='"$(sysconfdir)"' \ -DSMARTMONTOOLS_SMARTDSCRIPTDIR='"$(smartdscriptdir)"' if ENABLE_DRIVEDB AM_CPPFLAGS += -DSMARTMONTOOLS_DRIVEDBDIR='"$(drivedbdir)"' endif if ENABLE_SAVESTATES AM_CPPFLAGS += -DSMARTMONTOOLS_SAVESTATES='"$(savestates)"' endif if ENABLE_ATTRIBUTELOG AM_CPPFLAGS += -DSMARTMONTOOLS_ATTRIBUTELOG='"$(attributelog)"' endif if OS_WIN32_MINGW AM_CPPFLAGS += -I$(srcdir)/os_win32 endif if NEED_GETOPT_LONG AM_CPPFLAGS += -I$(srcdir)/getopt -D_GETOPT_STANDALONE endif if NEED_REGEX AM_CPPFLAGS += -I$(srcdir)/regex -D_REGEX_STANDALONE endif sbin_PROGRAMS = \ smartctl \ smartd if ENABLE_UPDATE_SMART_DRIVEDB if OS_WIN32_MINGW else sbin_SCRIPTS = update-smart-drivedb endif endif smartctl_SOURCES = \ smartctl.cpp \ smartctl.h \ atacmdnames.cpp \ atacmdnames.h \ atacmds.cpp \ atacmds.h \ ataidentify.cpp \ ataidentify.h \ ataprint.cpp \ ataprint.h \ dev_ata_cmd_set.cpp \ dev_ata_cmd_set.h \ dev_intelliprop.cpp \ dev_intelliprop.h \ dev_interface.cpp \ dev_interface.h \ dev_tunnelled.h \ drivedb.h \ json.cpp \ json.h \ knowndrives.cpp \ knowndrives.h \ nvmecmds.cpp \ nvmecmds.h \ nvmeprint.cpp \ nvmeprint.h \ scsicmds.cpp \ scsicmds.h \ scsiata.cpp \ scsinvme.cpp \ scsiprint.cpp \ scsiprint.h \ utility.cpp \ utility.h \ sg_unaligned.h smartctl_LDADD = $(os_deps) $(os_libs) smartctl_DEPENDENCIES = $(os_deps) EXTRA_smartctl_SOURCES = \ os_darwin.cpp \ os_darwin.h \ os_linux.cpp \ os_linux.h \ os_freebsd.cpp \ os_freebsd.h \ os_netbsd.cpp \ os_netbsd.h \ os_openbsd.cpp \ os_openbsd.h \ os_os2.cpp \ os_os2.h \ os_qnxnto.cpp \ os_qnxnto.h \ os_solaris.cpp \ os_solaris.h \ os_win32.cpp \ os_generic.cpp \ os_generic.h \ aacraid.h \ cciss.cpp \ cciss.h \ cissio_freebsd.h \ dev_areca.cpp \ dev_areca.h \ dev_legacy.cpp \ linux_nvme_ioctl.h \ megaraid.h if OS_WIN32_MINGW smartctl_SOURCES += \ os_win32/popen_win32.cpp \ os_win32/popen.h smartctl_LDADD += smartctl_res.o smartctl_DEPENDENCIES += smartctl_res.o endif smartd_SOURCES = \ smartd.cpp \ atacmdnames.cpp \ atacmdnames.h \ atacmds.cpp \ atacmds.h \ dev_ata_cmd_set.cpp \ dev_ata_cmd_set.h \ dev_intelliprop.cpp \ dev_intelliprop.h \ dev_interface.cpp \ dev_interface.h \ dev_tunnelled.h \ drivedb.h \ knowndrives.cpp \ knowndrives.h \ nvmecmds.cpp \ nvmecmds.h \ scsicmds.cpp \ scsicmds.h \ scsiata.cpp \ scsinvme.cpp \ utility.cpp \ utility.h \ sg_unaligned.h smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD) $(SYSTEMD_LDADD) smartd_DEPENDENCIES = $(os_deps) EXTRA_smartd_SOURCES = \ os_darwin.cpp \ os_darwin.h \ os_linux.cpp \ os_linux.h \ os_freebsd.cpp \ os_freebsd.h \ os_netbsd.cpp \ os_netbsd.h \ os_openbsd.cpp \ os_openbsd.h \ os_os2.cpp \ os_os2.h \ os_qnxnto.cpp \ os_qnxnto.h \ os_solaris.cpp \ os_solaris.h \ os_win32.cpp \ os_generic.cpp \ os_generic.h \ aacraid.h \ cciss.cpp \ cciss.h \ cissio_freebsd.h \ dev_areca.cpp \ dev_areca.h \ dev_legacy.cpp \ linux_nvme_ioctl.h \ freebsd_nvme_ioctl.h \ netbsd_nvme_ioctl.h \ megaraid.h if OS_WIN32_MINGW smartd_SOURCES += \ os_win32/daemon_win32.cpp \ os_win32/daemon_win32.h \ os_win32/popen_win32.cpp \ os_win32/popen.h \ os_win32/syslog_win32.cpp \ os_win32/syslog.h smartd_LDADD += smartd_res.o smartd_DEPENDENCIES += smartd_res.o endif # Exclude from source tarball nodist_EXTRA_smartctl_SOURCES = os_solaris_ata.s nodist_EXTRA_smartd_SOURCES = os_solaris_ata.s if NEED_GETOPT_LONG smartctl_SOURCES += \ getopt/getopt.c \ getopt/getopt.h \ getopt/getopt1.c \ getopt/getopt_int.h \ getopt/bits/getopt_core.h \ getopt/bits/getopt_ext.h smartd_SOURCES += \ getopt/getopt.c \ getopt/getopt.h \ getopt/getopt1.c \ getopt/getopt_int.h \ getopt/bits/getopt_core.h \ getopt/bits/getopt_ext.h endif if NEED_REGEX smartctl_SOURCES += \ regex/regex.c \ regex/regex.h \ regex/regex_internal.h smartd_SOURCES += \ regex/regex.c \ regex/regex.h \ regex/regex_internal.h # Included by regex.c: EXTRA_smartctl_SOURCES += \ regex/regcomp.c \ regex/regexec.c \ regex/regex_internal.c EXTRA_smartd_SOURCES += \ regex/regcomp.c \ regex/regexec.c \ regex/regex_internal.c endif if OS_WIN32 smartctl_SOURCES += \ csmisas.h \ os_win32/wmiquery.cpp \ os_win32/wmiquery.h smartd_SOURCES += \ csmisas.h \ os_win32/wmiquery.cpp \ os_win32/wmiquery.h smartctl_LDADD += -lole32 -loleaut32 smartd_LDADD += -lole32 -loleaut32 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 if ENABLE_UPDATE_SMART_DRIVEDB extra_MANS += update-smart-drivedb.1m endif all-local: $(extra_MANS) install-man: $(extra_MANS) @$(NORMAL_INSTALL) $(MKDIR_P) '$(DESTDIR)$(mandir)/man4' $(MKDIR_P) '$(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 else # For systems that adopts traditional manner man_MANS = smartd.conf.5 \ smartctl.8 \ smartd.8 if ENABLE_UPDATE_SMART_DRIVEDB man_MANS += update-smart-drivedb.8 endif endif docsdir=$(docdir) docs_DATA = \ AUTHORS \ ChangeLog \ ChangeLog-5.0-6.0 \ COPYING \ INSTALL \ NEWS \ README \ TODO \ smartd.conf examplesdir=$(exampledir) examples_DATA = \ examplescripts/README examples_SCRIPTS = \ examplescripts/Example1 \ examplescripts/Example2 \ examplescripts/Example3 \ examplescripts/Example4 \ examplescripts/Example5 \ examplescripts/Example6 sysconf_DATA = smartd.conf # If modified smartd.conf exists install smartd.conf.sample instead install-sysconfDATA: $(sysconf_DATA) $(MKDIR_P) '$(DESTDIR)$(sysconfdir)' @s="$(srcdir)/smartd.conf"; \ f="$(DESTDIR)$(sysconfdir)/smartd.conf$(smartd_suffix)"; \ if test -z "$(smartd_suffix)" && test -f "$$f"; then \ if cmp "$$s" "$$f" >/dev/null 2>/dev/null; then :; else \ echo "************************************************************"; \ echo "*** $$f preserved"; \ echo "*** installing smartd.conf.sample instead"; \ echo "************************************************************"; \ f="$$f".sample; \ fi; \ fi; \ echo " $(INSTALL_DATA) '$$s' '$$f'"; \ $(INSTALL_DATA) "$$s" "$$f" # If smartd.conf.sample exists preserve smartd.conf uninstall-sysconfDATA: @f="$(DESTDIR)$(sysconfdir)/smartd.conf$(smartd_suffix)"; \ if test -z "$(smartd_suffix)" && test -f "$$f".sample; then \ echo "************************************************************"; \ echo "*** $$f preserved"; \ echo "*** removing smartd.conf.sample instead"; \ echo "************************************************************"; \ f="$$f".sample; \ fi; \ echo " rm -f '$$f'"; \ rm -f "$$f" smartdscript_SCRIPTS = smartd_warning.sh EXTRA_DIST = \ .editorconfig \ autogen.sh \ smartd.initd.in \ smartd.cygwin.initd.in \ smartd.freebsd.initd.in \ smartd.8.in \ smartctl.8.in \ smartd.conf.5.in \ smartd.conf \ smartd.service.in \ smartd_warning.sh.in \ update-smart-drivedb.in \ update-smart-drivedb.8.in \ m4/pkg.m4 \ os_darwin/com.smartmontools.smartd.plist.in \ os_darwin/pkg/PackageInfo.in \ os_darwin/pkg/Distribution.in \ os_darwin/pkg/installer/README.html \ os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall \ os_win32/default.manifest \ os_win32/installer.nsi \ os_win32/runcmd.c \ os_win32/smartd_mailer.ps1 \ os_win32/smartd_mailer.conf.sample.ps1 \ os_win32/smartd_warning.cmd \ os_win32/syslogevt.mc \ os_win32/update-smart-drivedb.nsi \ os_win32/versioninfo.rc.in \ os_win32/wtssendmsg.c \ $(docs_DATA) \ $(examples_DATA) \ $(examples_SCRIPTS) CLEANFILES = \ smartd.8 \ smartd.1m \ smartd.8.html \ smartd.8.html.tmp \ smartd.8.pdf \ smartd.8.txt \ smartctl.8 \ smartctl.1m \ smartctl.8.html \ smartctl.8.html.tmp \ smartctl.8.pdf \ smartctl.8.txt \ smartd.conf.5 \ smartd.conf.4 \ smartd.conf.5.html \ smartd.conf.5.html.tmp \ smartd.conf.5.pdf \ smartd.conf.5.txt \ smartd.initd \ smartd.cygwin.initd \ smartd.freebsd.initd \ smartd.service \ smartd_warning.sh \ svnversion.h \ update-smart-drivedb \ update-smart-drivedb.8 \ update-smart-drivedb.1m \ update-smart-drivedb.8.html \ update-smart-drivedb.8.html.tmp \ update-smart-drivedb.8.pdf \ update-smart-drivedb.8.txt \ SMART # 'make maintainer-clean' also removes files generated by './autogen.sh' MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in \ $(srcdir)/aclocal.m4 \ $(srcdir)/compile \ $(srcdir)/configure \ $(srcdir)/config.guess \ $(srcdir)/config.h.in \ $(srcdir)/config.h.in~ \ $(srcdir)/config.sub \ $(srcdir)/depcomp \ $(srcdir)/install-sh \ $(srcdir)/missing \ $(srcdir)/m4/pkg.m4 smartctl.o utility.o: svnversion.h if IS_SVN_BUILD # Get version info from SVN svnversion.h: ChangeLog Makefile $(svn_deps) @echo ' svn info | $$(VERSION_FROM_SVN_INFO) > $@' @echo '/* svnversion.h. Generated by Makefile from svn info. */' > $@ @(cd $(srcdir) \ && svnversion 2>/dev/null | sed -n 's,^\([0-9].*\),REV "\1",p' \ && TZ= LC_ALL=C svn info 2>/dev/null \ | sed -n 'h;s,^.* Date: *\([^ ]*\) .*$$,DATE "\1",p;g;s,^.* Date: *[^ ]* *\([^ ]*\) .*$$,TIME "\1",p') \ | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@ else # SVN not available, guess version info from Id strings svnversion.h: ChangeLog Makefile NEWS @echo ' cat ChangeLog NEWS $$(SOURCES) | $$(VERSION_FROM_SVN_IDS) > $@' @echo '/* svnversion.h. Generated by Makefile from Id strings. */' > $@ @(cd $(srcdir) && cat ChangeLog NEWS Makefile.am configure.ac smart*.in *.cpp *.h) \ | sed -n 's,^.*\$$[I][d]: [^ ]* \([0-9][0-9]* [0-9][-0-9]* [0-9][:0-9]*\)[^:0-9][^$$]*\$$.*$$,\1,p' \ | sort -n -r \ | sed -n 'h;s,^\([^ ]*\) .*$$,REV "\1",p;g;s,^[^ ]* \([^ ]*\) .*$$,DATE "\1",p;g;s,^[^ ]* [^ ]* \([^ ]*\)$$,TIME "\1",p;q' \ | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@ endif if ENABLE_DRIVEDB drivedb_DATA = drivedb.h endif update-smart-drivedb: update-smart-drivedb.in config.status $(SHELL) ./config.status --file=$@ chmod +x $@ smartd_warning.sh: smartd_warning.sh.in config.status $(SHELL) ./config.status --file=$@ chmod +x $@ if INSTALL_INITSCRIPT if OS_DARWIN initd_DATA = com.smartmontools.smartd.plist initd_DATA_install = install-initdDATA-darwin initd_DATA_uninstall = uninstall-initdDATA-darwin com.smartmontools.smartd.plist : os_darwin/com.smartmontools.smartd.plist.in sed "s|/usr/sbin/|$(sbindir)/|" $< > $@ install-initdDATA-darwin: $(initd_DATA) $(MKDIR_P) '$(DESTDIR)$(initddir)' $(INSTALL_DATA) $(top_builddir)/$(initd_DATA) $(DESTDIR)$(initddir)/$(initd_DATA) uninstall-initdDATA-darwin: rm -f $(DESTDIR)$(initddir)/$(initd_DATA) else initd_DATA = $(initdfile) $(initdfile): $(srcdir)/$(initdfile).in Makefile sed 's|/usr/local/sbin/|$(sbindir)/|g' $(srcdir)/$(initdfile).in > $@ initd_install_name = smartd$(smartd_suffix) initd_DATA_install = install-initdDATA-generic initd_DATA_uninstall = uninstall-initdDATA-generic install-initdDATA-generic: $(initd_DATA) $(MKDIR_P) '$(DESTDIR)$(initddir)' $(INSTALL_SCRIPT) '$(top_builddir)/$(initdfile)' '$(DESTDIR)$(initddir)/smartd$(smartd_suffix)' uninstall-initdDATA-generic: rm -f '$(DESTDIR)$(initddir)/$(initd_install_name)' endif else initd_DATA_install = install-initdDATA-null initd_DATA_uninstall = uninstall-initdDATA-null install-initdDATA-null: uninstall-initdDATA-null: endif install-initdDATA : $(initd_DATA_install) uninstall-initdDATA: $(initd_DATA_uninstall) if INSTALL_SYSTEMDUNIT systemdsystemunit_DATA = smartd.service endif smartd.service: smartd.service.in Makefile @echo ' $$(SMARTD_SERVICE_FILTER) < $(srcdir)/smartd.service.in > $@' @{ \ sed 's|/usr/local/sbin/smartd|$(sbindir)/smartd|' | \ if test -n '$(systemdenvfile)'; then \ sed 's|/usr/local/etc/sysconfig/smartmontools|$(systemdenvfile)|'; \ else \ sed -e '/^EnvironmentFile=/d' -e 's| *\$$smartd[_a-z]* *||g'; \ fi; } < $(srcdir)/smartd.service.in > $@ # Create empty directories if configured. # Default install rules no longer create empty directories since automake 1.11. installdirs-local: @for d in '$(smartdplugindir)' '$(savestatesdir)' '$(attributelogdir)'; do \ test -n "$$d" || continue; \ echo " $(MKDIR_P) '$(DESTDIR)$$d'"; \ $(MKDIR_P) "$(DESTDIR)$$d" || exit 1; \ done install-data-local: installdirs-local # # Build man pages # MAN_FILTER = { \ sed -e 's|CURRENT_SVN_VERSION|$(releaseversion)|g' \ -e "s|CURRENT_SVN_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g" \ -e "s|CURRENT_SVN_REV|`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`|g" \ -e 's|/usr/local/share/man/|$(mandir)/|g' \ -e 's|/usr/local/sbin/|$(sbindir)/|g' \ -e 's|/usr/local/share/doc/smartmontools/examplescripts/|!exampledir!|g' \ -e 's|/usr/local/share/doc/smartmontools/|$(docsdir)/|g' \ -e 's|!exampledir!|$(exampledir)/|g' \ -e 's|/usr/local/etc/smartd\.conf|$(sysconfdir)/smartd.conf|g' \ -e 's|/usr/local/etc/smart_drivedb\.h|$(sysconfdir)/smart_drivedb.h|g' \ -e 's|/usr/local/etc/smartd_warning\.sh|$(smartdscriptdir)/smartd_warning.sh|g' \ -e 's|\\fBmail\\fP|\\fB$(os_mailer)\\fP|g' \ -e 's|\\'\''mail\\'\''|\\'\''$(os_mailer)\\'\''|g' \ -e 's|/usr/bin/mail|/usr/bin/$(os_mailer)|g' \ -e 's|RELEASE_6_0_DRIVEDB|$(DRIVEDB_BRANCH)|g' | \ if test -n '$(drivedbdir)'; then \ sed 's|/usr/local/share/smartmontools/drivedb\.h|$(drivedbdir)/drivedb.h|g' ; \ else \ sed '/^\.\\" %IF ENABLE_DRIVEDB/,/^\.\\" %ENDIF ENABLE_DRIVEDB/ s,^,.\\"\# ,' ; \ fi | \ if test '$(with_update_smart_drivedb)' = 'yes'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_UPDATE_SMART_DRIVEDB/,/^\.\\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(initddir)'; then \ sed 's|/usr/local/etc/rc\.d/init\.d/|$(initddir)/|g' ; \ else \ sed '/^\.\\" %IF ENABLE_INITSCRIPT/,/^\.\\" %ENDIF ENABLE_INITSCRIPT/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(savestates)'; then \ sed 's|/usr/local/var/lib/smartmontools/smartd\.|$(savestates)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_SAVESTATES/,/^\.\\" %ENDIF ENABLE_SAVESTATES/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(attributelog)'; then \ sed 's|/usr/local/var/lib/smartmontools/attrlog\.|$(attributelog)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_ATTRIBUTELOG/,/^\.\\" %ENDIF ENABLE_ATTRIBUTELOG/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(smartdplugindir)'; then \ sed 's|/usr/local/etc/smartd_warning\.d|$(smartdplugindir)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_SMARTDPLUGINDIR/,/^\.\\" %ENDIF ENABLE_SMARTDPLUGINDIR/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(CAPNG_LDADD)'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(SYSTEMD_LDADD)'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_SYSTEMD_NOTIFY/,/^\.\\" %ENDIF ENABLE_SYSTEMD_NOTIFY/ s,^,.\\"\# ,' ; \ fi | \ if test '$(with_nvme_devicescan)' = 'yes'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_NVME_DEVICESCAN/,/^\.\\" %ENDIF ENABLE_NVME_DEVICESCAN/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(os_man_filter)'; then \ sed -e 's,OS_MAN_FILTER,$(os_man_filter),g' \ -e '/^\.\\" %IF NOT OS .*$(os_man_filter)/,/^.\\" %ENDIF NOT OS .*$(os_man_filter)/ s,^,.\\"\# ,' \ -e '/^\.\\" %IF OS .*$(os_man_filter)/,/^\.\\" %ENDIF OS .*$(os_man_filter)/ s,^,!!,' \ -e '/^\.\\" %IF OS ./,/^\.\\" %ENDIF OS ./ s,^,.\\"\# ,' \ -e '/^!*\.\\" %IF NOT OS ./,/^!*\.\\" %ENDIF NOT OS ./ s,^,!!,' \ -e 's,^!!!*\.\\"! \(.*\)$$,\1 \\"\#,' \ -e 's,^!!!*,,' ; \ else \ cat; \ fi; } # Implicit rule 'smart%: smart%.in ...' does not work with BSD make smartctl.8: smartctl.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@' @$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@ smartd.8: smartd.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@' @$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@ smartd.conf.5: smartd.conf.5.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@' @$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@ update-smart-drivedb.8: update-smart-drivedb.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@' @$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@ # Build Solaris specific man pages SOLARIS_MAN_FILTER = \ sed -e '/^\.TH/s, \([58]\) , !!\1!! ,' \ -e '/^\.BR/s, (\([578]\)), (!!\1!!),' \ -e 's,\\fP(\([578]\)),\\fP(!!\1!!),g' \ -e 's,!!5!!,4,g' -e 's,!!7!!,5,g' -e 's,!!8!!,1m,g' \ -e 's,/var/log/messages,/var/adm/messages,g' smartctl.1m: smartctl.8 @echo ' $$(SOLARIS_MAN_FILTER) < smartctl.8 > $@' @$(SOLARIS_MAN_FILTER) < smartctl.8 > $@ smartd.1m: smartd.8 @echo ' $$(SOLARIS_MAN_FILTER) < smartd.8 > $@' @$(SOLARIS_MAN_FILTER) < smartd.8 > $@ smartd.conf.4: smartd.conf.5 @echo ' $$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@' @$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@ update-smart-drivedb.1m: update-smart-drivedb.8 @echo ' $$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@' @$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@ # Convert man pages into .html, .pdf and .txt # TODO: configure MAN2HTML = man2html #MAN2HTML = groff -man -Thtml MAN2PDF = man -Tpdf -l #MAN2PDF = groff -man -Tpdf MAN2TXT = groff -man -Tascii -P'-bcou' # Remove HTTP header and fix links in man2html output FIXHTML = sed -e '1s,^Content-type.*,,' \ -e 's,,,g' \ -e 's,,,g' \ -e 's,\([^<]*\),\1,g' \ -e 's,\([^<]*\),\1,g' htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html update-smart-drivedb.8.html pdfman: smartctl.8.pdf smartd.8.pdf smartd.conf.5.pdf update-smart-drivedb.8.pdf txtman: smartctl.8.txt smartd.8.txt smartd.conf.5.txt update-smart-drivedb.8.txt %.5.html: %.5 $(MAN2HTML) $< > $@.tmp @echo ' $$(FIXHTML) < $@.tmp > $@' @$(FIXHTML) < $@.tmp > $@ %.8.html: %.8 $(MAN2HTML) $< > $@.tmp @echo ' $$(FIXHTML) < $@.tmp > $@' @$(FIXHTML) < $@.tmp > $@ %.5.pdf: %.5 $(MAN2PDF) $< > $@ %.8.pdf: %.8 $(MAN2PDF) $< > $@ %.5.txt: %.5 $(MAN2TXT) $< > $@ %.8.txt: %.8 $(MAN2TXT) $< > $@ # Check drive database syntax check: @if ./smartctl -B $(srcdir)/drivedb.h -P showall >/dev/null; then \ echo "$(srcdir)/drivedb.h: OK"; \ else \ echo "$(srcdir)/drivedb.h: Syntax check failed"; exit 1; \ fi if OS_WIN32_MINGW # Windows resources smartctl_res.o: smartctl_res.rc $(os_win32_manifest) $(WINDRES) $< $@ smartd_res.o: smartd_res.rc syslogevt.rc $(os_win32_manifest) $(WINDRES) $< $@ runcmda_res.o: runcmda_res.rc defadmin.manifest $(WINDRES) $< $@ runcmdu_res.o: runcmdu_res.rc $(os_win32_manifest) $(WINDRES) $< $@ wtssendmsg_res.o: wtssendmsg_res.rc $(os_win32_manifest) $(WINDRES) $< $@ # Convert version for VERSIONINFO resource: 6.6 r4519 -> 6.6.0.4519, # set description, name, version and year WIN_RC_FILTER = { \ ver=`echo '$(PACKAGE_VERSION).0' | sed -n 's,^\([0-9]*\.[0-9]*\.[0-9]*\).*$$,\1,p'` && \ rev=`sed -n 's,^.*REV[^"]*"\([0-9]*\).*$$,\1,p' svnversion.h` && \ txtver="$${ver:-0.0.0}.$${rev:-0}" && binver=`echo "$$txtver" | sed 's|\.|,|g'` && \ yy=`sed -n 's,^.*DATE[^"]*"20\([0-9][0-9]\).*$$,\1,p' svnversion.h` && yy="$${yy:-XX}" && \ sed -e "s|@DESC@|$$d|" -e "s|@NAME@|$$n|" -e "s|@BINARY_VERSION@|$$binver|g" \ -e "s|@TEXT_VERSION@|$$txtver|g" -e "s|@YY@|$$yy|g"; } WIN_MAKE_RES = \ echo "n=$$n d=\"$$d"'"; $$(WIN_RC_FILTER) < $< > $@'; \ $(WIN_RC_FILTER) < $< > $@ WIN_APP_MANIFEST = \ if test -n '$(os_win32_manifest)'; then \ echo "echo '1 24 \"$(srcdir)/$(os_win32_manifest)\"' >> $@"; \ echo '1 24 "$(srcdir)/$(os_win32_manifest)"' >> $@; \ fi smartctl_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @n=smartctl d="Control and Monitor Utility for SMART Disks"; $(WIN_MAKE_RES) @$(WIN_APP_MANIFEST) smartd_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @n=smartd d="SMART Disk Monitoring Daemon"; $(WIN_MAKE_RES) echo '#include "./syslogevt.rc"' >> $@ @$(WIN_APP_MANIFEST) runcmdu_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @n=runcmdu d="Run console command"; $(WIN_MAKE_RES) @$(WIN_APP_MANIFEST) runcmda_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @n=runcmda d="Run console command as admin"; $(WIN_MAKE_RES) echo '1 24 "./defadmin.manifest"' >> $@ wtssendmsg_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @n=wtssendmsg d="Send console messages"; $(WIN_MAKE_RES) @$(WIN_APP_MANIFEST) syslogevt.rc: os_win32/syslogevt.mc $(WINDMC) -b $< defadmin.manifest: os_win32/default.manifest sed 's,"asInvoker","requireAdministrator",' $< > $@ # Definitions for Windows distribution if OS_WIN64 win_bits = 64 else win_bits = 32 endif distdir_win32 = $(PACKAGE)-$(VERSION).win$(win_bits) distzip_win32 = $(PACKAGE)-$(VERSION).win$(win_bits).zip distinst_win32 = $(PACKAGE)-$(VERSION).win$(win_bits)-setup.exe exedir_win32 = $(distdir_win32)/bin docdir_win32 = $(distdir_win32)/doc EXEFILES_WIN32 = \ $(exedir_win32)/smartctl.exe \ $(exedir_win32)/smartctl-nc.exe \ $(exedir_win32)/smartd.exe \ $(exedir_win32)/smartd_mailer.ps1 \ $(exedir_win32)/smartd_mailer.conf.sample.ps1 \ $(exedir_win32)/smartd_warning.cmd \ $(exedir_win32)/runcmda.exe \ $(exedir_win32)/runcmdu.exe \ $(exedir_win32)/wtssendmsg.exe if ENABLE_DRIVEDB if OS_WIN32_NSIS EXEFILES_WIN32 += \ $(exedir_win32)/update-smart-drivedb.exe endif endif FILES_WIN32 = \ $(EXEFILES_WIN32) \ $(docdir_win32)/AUTHORS.txt \ $(docdir_win32)/ChangeLog.txt \ $(docdir_win32)/ChangeLog-5.0-6.0.txt \ $(docdir_win32)/COPYING.txt \ $(docdir_win32)/INSTALL.txt \ $(docdir_win32)/NEWS.txt \ $(docdir_win32)/README.txt \ $(docdir_win32)/TODO.txt \ $(docdir_win32)/checksums$(win_bits).txt \ $(docdir_win32)/smartd.conf \ $(docdir_win32)/smartctl.8.html \ $(docdir_win32)/smartctl.8.pdf \ $(docdir_win32)/smartd.8.html \ $(docdir_win32)/smartd.8.pdf \ $(docdir_win32)/smartd.conf.5.html \ $(docdir_win32)/smartd.conf.5.pdf if ENABLE_DRIVEDB FILES_WIN32 += \ $(exedir_win32)/drivedb.h endif CLEANFILES += \ $(FILES_WIN32) \ defadmin.manifest \ distdir.mkdir \ runcmda.exe runcmda_res.rc \ runcmdu.exe runcmdu_res.rc \ smartctl-nc.exe smartctl-nc.exe.tmp \ smartctl_res.rc smartd_res.rc \ syslogevt.h \ syslogevt.rc syslogevt_*.bin \ update-smart-drivedb.exe \ wtssendmsg.exe wtssendmsg_res.rc # Note: Only use without options to be compatible with all variants UNIX2DOS = unix2dos # Build Windows distribution dist-win32: $(distzip_win32) install-win32: $(distinst_win32) ./$(distinst_win32) installer-win32: $(distinst_win32) distdir-win32: distdir.mkdir $(FILES_WIN32) $(distzip_win32): distdir.mkdir $(FILES_WIN32) @rm -fv $(distzip_win32) cd $(distdir_win32) && zip -9 ../$(distzip_win32) bin/* doc/* md5sum $@ > $@.md5 sha1sum $@ > $@.sha1 sha256sum $@ > $@.sha256 if OS_WIN32_NSIS # Build NSIS installer # Note: Only option character '-' is also compatible with Linux version of makensis $(distinst_win32): os_win32/installer.nsi smartctl_res.rc distdir.mkdir $(FILES_WIN32) test -z '$(builddir_win64)' || ( cd $(builddir_win64) && make distdir-win32 ) @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h` && \ rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h` && \ version=`sed -n 's|^ *VALUE "FileVersion", "\([0-9.]*\)".*$$|\1|p' smartctl_res.rc` && \ yy=`echo "$$date" | sed -n 's,^20\([0-9][0-9]\).*$$,\1,p'`; yy="$${yy:-XX}" && \ verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO) && \ d64= && if [ -n '$(builddir_win64)' ]; then d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; fi && \ echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR='$$verstr' $<" && \ '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR="$$verstr" $< md5sum $@ > $@.md5 sha1sum $@ > $@.sha1 sha256sum $@ > $@.sha256 # Build drivedb.h update tool update-smart-drivedb.exe: os_win32/update-smart-drivedb.nsi "$(MAKENSIS)" -V2 -NOCD -DBRANCH=$(DRIVEDB_BRANCH) $< else $(distinst_win32): @echo "makensis: command not found. Please install NSIS from http://nsis.sourceforge.net/" 1>&2 @exit 1 endif cleandist-win32: rm -rf $(distdir_win32) distdir.mkdir distdir.mkdir: @test -d $(exedir_win32) || mkdir -pv $(exedir_win32) @test -d $(docdir_win32) || mkdir -pv $(docdir_win32) touch $@ $(exedir_win32)/%.exe: %.exe cp -p $< $@ if test -n '$(STRIP)'; then $(STRIP) -s $@; else strip -s $@; fi touch -r $< $@ # strip would break NSIS integrity check $(exedir_win32)/update-smart-drivedb.exe: update-smart-drivedb.exe cp -p $< $@ $(exedir_win32)/%.h: $(srcdir)/%.h $(UNIX2DOS) < $< > $@ touch -r $< $@ $(exedir_win32)/%.cmd: $(srcdir)/os_win32/%.cmd $(UNIX2DOS) < $< > $@ touch -r $< $@ $(exedir_win32)/%.ps1: $(srcdir)/os_win32/%.ps1 $(UNIX2DOS) < $< > $@ touch -r $< $@ $(docdir_win32)/%.html: %.html $(UNIX2DOS) < $< > $@ touch -r $< $@ $(docdir_win32)/%.pdf: %.pdf cp -p $< $@ $(docdir_win32)/%.txt: $(srcdir)/% $(UNIX2DOS) < $< > $@ touch -r $< $@ $(docdir_win32)/%.conf: $(srcdir)/%.conf $(UNIX2DOS) < $< > $@ touch -r $< $@ $(docdir_win32)/checksums$(win_bits).txt: $(EXEFILES_WIN32) (cd $(exedir_win32) && md5sum *.exe && sha1sum *.exe && sha256sum *.exe) \ | $(UNIX2DOS) > $@ # Build non-console version of smartctl for GSmartControl. # The script below changes the word at offset 220 (Subsystem) from 3 # (Console) to 2 (GUI) in a copy of smartctl.exe. # This will be changed when a tool (like 'editbin') is available in # the Cygwin distribution smartctl-nc.exe: smartctl.exe @rm -f $@ cp -p smartctl.exe $@.tmp @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 3; then :; \ else echo "invalid EXE header"; exit 1; fi @echo "editbin /subsystem:windows $@.tmp" @echo -ne '\002' | dd bs=1 seek=220 count=1 conv=notrunc of=$@.tmp 2>/dev/null @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 2; then :; \ else echo "EXE patch failed"; exit 1; fi mv -f $@.tmp $@ # Build runcmd?.exe and wtssendmsg.exe runcmd.o: os_win32/runcmd.c $(CC) -c -Os $< runcmdu.exe: runcmd.o runcmdu_res.o $(CC) -o $@ $^ runcmda.exe: runcmd.o runcmda_res.o $(CC) -o $@ $^ wtssendmsg.exe: os_win32/wtssendmsg.c wtssendmsg_res.o $(CC) -Os -o $@ $^ -lwtsapi32 # Build os_win32/vc14/{config.h,smart*.rc,svnversion.h} for MSVC14 from MinGW files vcver = 14 CONFIG_VC_FILES = \ $(srcdir)/os_win32/vc$(vcver)/config.h \ $(srcdir)/os_win32/vc$(vcver)/smartctl_res.rc \ $(srcdir)/os_win32/vc$(vcver)/smartd_res.rc \ $(srcdir)/os_win32/vc$(vcver)/svnversion.h config-vc$(vcver): $(CONFIG_VC_FILES) $(srcdir)/os_win32/vc$(vcver)/config.h: config.h Makefile sed -e '1i/* os_win32/vc$(vcver)/config.h. Generated from config.h by Makefile. */' \ -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|LONG_DOUBLE_WIDER\|STRINGS_H\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \ -e 's,^\(#define SMARTMONTOOLS_BUILD_HOST "[^-]*\)[^"]*,\1-pc-w32vc$(vcver),' $< > $@ $(srcdir)/os_win32/vc$(vcver)/svnversion.h: svnversion.h cp $< $@ $(srcdir)/os_win32/vc$(vcver)/smartctl_res.rc: smartctl_res.rc sed '/^1 24 /d' $< > $@ $(srcdir)/os_win32/vc$(vcver)/smartd_res.rc: smartd_res.rc sed '/^1 24 /d' $< > $@ clean-vc$(vcver): rm -f $(srcdir)/os_win32/vc$(vcver)/smartmontools.VC.VC.opendb rm -f $(srcdir)/os_win32/vc$(vcver)/smartmontools.VC.db rm -f $(srcdir)/os_win32/vc$(vcver)/syslogevt.h rm -rf $(srcdir)/os_win32/vc$(vcver)/Debug rm -rf $(srcdir)/os_win32/vc$(vcver)/Release rm -rf $(srcdir)/os_win32/vc$(vcver)/x64 distclean-vc$(vcver): clean-vc$(vcver) rm -f $(CONFIG_VC_FILES) maintainer-clean-vc$(vcver): distclean-vc$(vcver) rm -rf $(srcdir)/os_win32/vc$(vcver)/.vs endif if OS_DARWIN # Definitions for OSX distribution distdir_darwin = $(PACKAGE)-$(VERSION).darwin dmg_darwin = $(PACKAGE)-$(VERSION).dmg pkg_darwin = $(PACKAGE)-$(VERSION).pkg # build darwin installer $(pkg_darwin): ${MAKE} install DESTDIR=$(distdir_darwin)/root @cp $(srcdir)/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall $(distdir_darwin)/root$(sbindir) @mkdir -p $(distdir_darwin)/pkg @( cd $(distdir_darwin)/root && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > $(distdir_darwin)/pkg/Payload PAYLOAD_FILES=`find $(distdir_darwin)/root | wc -l` &&\ PAYLOAD_SIZEKB=`du -BK -s $(distdir_darwin)/root|${AWK} '{print $$1}'|tr -d 'K'` &&\ sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" \ -e "s|@size@|$${PAYLOAD_SIZEKB}|" $(srcdir)/os_darwin/pkg/PackageInfo.in \ > $(distdir_darwin)/pkg/PackageInfo &&\ sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" -e "s|@size@|$${PAYLOAD_SIZEKB}|" \ -e "s|@pkgname@|$(pkg_darwin)|" \ $(srcdir)/os_darwin/pkg/Distribution.in > $(distdir_darwin)/pkg/Distribution @mkdir -p $(distdir_darwin)/pkg/Resources/English.lproj @cp $(srcdir)/COPYING $(distdir_darwin)/pkg/Resources/English.lproj/license.txt @mkbom -u 0 -g 80 $(distdir_darwin)/root $(distdir_darwin)/pkg/Bom @mkdir -p $(distdir_darwin)/dmg @( cd $(distdir_darwin)/pkg && xar --compression none -cf "../dmg/$(pkg_darwin)" * ) # build darwon dmg image $(dmg_darwin): @cp $(srcdir)/os_darwin/pkg/installer/README.html $(distdir_darwin)/dmg @mkisofs -V 'smartmontools' -no-pad -r -apple -o $(distdir_darwin)/smartmontools-$(VERSION).iso \ -hfs-bless "$(distdir_darwin)/dmg/" "$(distdir_darwin)/dmg/" @dmg dmg $(distdir_darwin)/smartmontools-$(VERSION).iso $(dmg_darwin) md5sum $@ > $@.md5 sha1sum $@ > $@.sha1 sha256sum $@ > $@.sha256 install-darwin: install-darwin-cleanup $(pkg_darwin) $(dmg_darwin) install-darwin-cleanup: @rm -rf $(distdir_darwin) endif smartmontools-7.0/Makefile.in0000644000175000010010000032172413412155343013266 00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 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 4848 2018-12-05 18:30:46Z chrfranke $ # VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd 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@ @ENABLE_DRIVEDB_TRUE@am__append_1 = -DSMARTMONTOOLS_DRIVEDBDIR='"$(drivedbdir)"' @ENABLE_SAVESTATES_TRUE@am__append_2 = -DSMARTMONTOOLS_SAVESTATES='"$(savestates)"' @ENABLE_ATTRIBUTELOG_TRUE@am__append_3 = -DSMARTMONTOOLS_ATTRIBUTELOG='"$(attributelog)"' @OS_WIN32_MINGW_TRUE@am__append_4 = -I$(srcdir)/os_win32 @NEED_GETOPT_LONG_TRUE@am__append_5 = -I$(srcdir)/getopt -D_GETOPT_STANDALONE @NEED_REGEX_TRUE@am__append_6 = -I$(srcdir)/regex -D_REGEX_STANDALONE sbin_PROGRAMS = smartctl$(EXEEXT) smartd$(EXEEXT) @OS_WIN32_MINGW_TRUE@am__append_7 = \ @OS_WIN32_MINGW_TRUE@ os_win32/popen_win32.cpp \ @OS_WIN32_MINGW_TRUE@ os_win32/popen.h @OS_WIN32_MINGW_TRUE@am__append_8 = smartctl_res.o @OS_WIN32_MINGW_TRUE@am__append_9 = smartctl_res.o @OS_WIN32_MINGW_TRUE@am__append_10 = \ @OS_WIN32_MINGW_TRUE@ os_win32/daemon_win32.cpp \ @OS_WIN32_MINGW_TRUE@ os_win32/daemon_win32.h \ @OS_WIN32_MINGW_TRUE@ os_win32/popen_win32.cpp \ @OS_WIN32_MINGW_TRUE@ os_win32/popen.h \ @OS_WIN32_MINGW_TRUE@ os_win32/syslog_win32.cpp \ @OS_WIN32_MINGW_TRUE@ os_win32/syslog.h @OS_WIN32_MINGW_TRUE@am__append_11 = smartd_res.o @OS_WIN32_MINGW_TRUE@am__append_12 = smartd_res.o @NEED_GETOPT_LONG_TRUE@am__append_13 = \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt.c \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt.h \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt1.c \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt_int.h \ @NEED_GETOPT_LONG_TRUE@ getopt/bits/getopt_core.h \ @NEED_GETOPT_LONG_TRUE@ getopt/bits/getopt_ext.h @NEED_GETOPT_LONG_TRUE@am__append_14 = \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt.c \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt.h \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt1.c \ @NEED_GETOPT_LONG_TRUE@ getopt/getopt_int.h \ @NEED_GETOPT_LONG_TRUE@ getopt/bits/getopt_core.h \ @NEED_GETOPT_LONG_TRUE@ getopt/bits/getopt_ext.h @NEED_REGEX_TRUE@am__append_15 = \ @NEED_REGEX_TRUE@ regex/regex.c \ @NEED_REGEX_TRUE@ regex/regex.h \ @NEED_REGEX_TRUE@ regex/regex_internal.h @NEED_REGEX_TRUE@am__append_16 = \ @NEED_REGEX_TRUE@ regex/regex.c \ @NEED_REGEX_TRUE@ regex/regex.h \ @NEED_REGEX_TRUE@ regex/regex_internal.h # Included by regex.c: @NEED_REGEX_TRUE@am__append_17 = \ @NEED_REGEX_TRUE@ regex/regcomp.c \ @NEED_REGEX_TRUE@ regex/regexec.c \ @NEED_REGEX_TRUE@ regex/regex_internal.c @NEED_REGEX_TRUE@am__append_18 = \ @NEED_REGEX_TRUE@ regex/regcomp.c \ @NEED_REGEX_TRUE@ regex/regexec.c \ @NEED_REGEX_TRUE@ regex/regex_internal.c @OS_WIN32_TRUE@am__append_19 = \ @OS_WIN32_TRUE@ csmisas.h \ @OS_WIN32_TRUE@ os_win32/wmiquery.cpp \ @OS_WIN32_TRUE@ os_win32/wmiquery.h @OS_WIN32_TRUE@am__append_20 = \ @OS_WIN32_TRUE@ csmisas.h \ @OS_WIN32_TRUE@ os_win32/wmiquery.cpp \ @OS_WIN32_TRUE@ os_win32/wmiquery.h @OS_WIN32_TRUE@am__append_21 = -lole32 -loleaut32 @OS_WIN32_TRUE@am__append_22 = -lole32 -loleaut32 @ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_TRUE@am__append_23 = update-smart-drivedb.1m @ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_FALSE@am__append_24 = update-smart-drivedb.8 @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@am__append_25 = \ @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ $(exedir_win32)/update-smart-drivedb.exe @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@am__append_26 = \ @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@ $(exedir_win32)/drivedb.h @OS_WIN32_MINGW_TRUE@am__append_27 = \ @OS_WIN32_MINGW_TRUE@ $(FILES_WIN32) \ @OS_WIN32_MINGW_TRUE@ defadmin.manifest \ @OS_WIN32_MINGW_TRUE@ distdir.mkdir \ @OS_WIN32_MINGW_TRUE@ runcmda.exe runcmda_res.rc \ @OS_WIN32_MINGW_TRUE@ runcmdu.exe runcmdu_res.rc \ @OS_WIN32_MINGW_TRUE@ smartctl-nc.exe smartctl-nc.exe.tmp \ @OS_WIN32_MINGW_TRUE@ smartctl_res.rc smartd_res.rc \ @OS_WIN32_MINGW_TRUE@ syslogevt.h \ @OS_WIN32_MINGW_TRUE@ syslogevt.rc syslogevt_*.bin \ @OS_WIN32_MINGW_TRUE@ update-smart-drivedb.exe \ @OS_WIN32_MINGW_TRUE@ wtssendmsg.exe wtssendmsg_res.rc subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/pkg.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(examplesdir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(smartdscriptdir)" \ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" \ "$(DESTDIR)$(docsdir)" "$(DESTDIR)$(drivedbdir)" \ "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(initddir)" \ "$(DESTDIR)$(sysconfdir)" "$(DESTDIR)$(systemdsystemunitdir)" PROGRAMS = $(sbin_PROGRAMS) am__smartctl_SOURCES_DIST = smartctl.cpp smartctl.h atacmdnames.cpp \ atacmdnames.h atacmds.cpp atacmds.h ataidentify.cpp \ ataidentify.h ataprint.cpp ataprint.h dev_ata_cmd_set.cpp \ dev_ata_cmd_set.h dev_intelliprop.cpp dev_intelliprop.h \ dev_interface.cpp dev_interface.h dev_tunnelled.h drivedb.h \ json.cpp json.h knowndrives.cpp knowndrives.h nvmecmds.cpp \ nvmecmds.h nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h \ scsiata.cpp scsinvme.cpp scsiprint.cpp scsiprint.h utility.cpp \ utility.h sg_unaligned.h os_win32/popen_win32.cpp \ os_win32/popen.h getopt/getopt.c getopt/getopt.h \ getopt/getopt1.c getopt/getopt_int.h getopt/bits/getopt_core.h \ getopt/bits/getopt_ext.h regex/regex.c regex/regex.h \ regex/regex_internal.h csmisas.h os_win32/wmiquery.cpp \ os_win32/wmiquery.h @OS_WIN32_MINGW_TRUE@am__objects_1 = popen_win32.$(OBJEXT) @NEED_GETOPT_LONG_TRUE@am__objects_2 = getopt.$(OBJEXT) \ @NEED_GETOPT_LONG_TRUE@ getopt1.$(OBJEXT) @NEED_REGEX_TRUE@am__objects_3 = regex.$(OBJEXT) @OS_WIN32_TRUE@am__objects_4 = wmiquery.$(OBJEXT) am_smartctl_OBJECTS = smartctl.$(OBJEXT) atacmdnames.$(OBJEXT) \ atacmds.$(OBJEXT) ataidentify.$(OBJEXT) ataprint.$(OBJEXT) \ dev_ata_cmd_set.$(OBJEXT) dev_intelliprop.$(OBJEXT) \ dev_interface.$(OBJEXT) json.$(OBJEXT) knowndrives.$(OBJEXT) \ nvmecmds.$(OBJEXT) nvmeprint.$(OBJEXT) scsicmds.$(OBJEXT) \ scsiata.$(OBJEXT) scsinvme.$(OBJEXT) scsiprint.$(OBJEXT) \ utility.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ $(am__objects_3) $(am__objects_4) am__EXTRA_smartctl_SOURCES_DIST = os_darwin.cpp os_darwin.h \ os_linux.cpp os_linux.h os_freebsd.cpp os_freebsd.h \ os_netbsd.cpp os_netbsd.h os_openbsd.cpp os_openbsd.h \ os_os2.cpp os_os2.h os_qnxnto.cpp os_qnxnto.h os_solaris.cpp \ os_solaris.h os_win32.cpp os_generic.cpp os_generic.h \ aacraid.h cciss.cpp cciss.h cissio_freebsd.h dev_areca.cpp \ dev_areca.h dev_legacy.cpp linux_nvme_ioctl.h megaraid.h \ regex/regcomp.c regex/regexec.c regex/regex_internal.c smartctl_OBJECTS = $(am_smartctl_OBJECTS) am__DEPENDENCIES_1 = am__smartd_SOURCES_DIST = smartd.cpp atacmdnames.cpp atacmdnames.h \ atacmds.cpp atacmds.h dev_ata_cmd_set.cpp dev_ata_cmd_set.h \ dev_intelliprop.cpp dev_intelliprop.h dev_interface.cpp \ dev_interface.h dev_tunnelled.h drivedb.h knowndrives.cpp \ knowndrives.h nvmecmds.cpp nvmecmds.h scsicmds.cpp scsicmds.h \ scsiata.cpp scsinvme.cpp utility.cpp utility.h sg_unaligned.h \ os_win32/daemon_win32.cpp os_win32/daemon_win32.h \ os_win32/popen_win32.cpp os_win32/popen.h \ os_win32/syslog_win32.cpp os_win32/syslog.h getopt/getopt.c \ getopt/getopt.h getopt/getopt1.c getopt/getopt_int.h \ getopt/bits/getopt_core.h getopt/bits/getopt_ext.h \ regex/regex.c regex/regex.h regex/regex_internal.h csmisas.h \ os_win32/wmiquery.cpp os_win32/wmiquery.h @OS_WIN32_MINGW_TRUE@am__objects_5 = daemon_win32.$(OBJEXT) \ @OS_WIN32_MINGW_TRUE@ popen_win32.$(OBJEXT) \ @OS_WIN32_MINGW_TRUE@ syslog_win32.$(OBJEXT) am_smartd_OBJECTS = smartd.$(OBJEXT) atacmdnames.$(OBJEXT) \ atacmds.$(OBJEXT) dev_ata_cmd_set.$(OBJEXT) \ dev_intelliprop.$(OBJEXT) dev_interface.$(OBJEXT) \ knowndrives.$(OBJEXT) nvmecmds.$(OBJEXT) scsicmds.$(OBJEXT) \ scsiata.$(OBJEXT) scsinvme.$(OBJEXT) utility.$(OBJEXT) \ $(am__objects_5) $(am__objects_2) $(am__objects_3) \ $(am__objects_4) am__EXTRA_smartd_SOURCES_DIST = os_darwin.cpp os_darwin.h os_linux.cpp \ os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \ os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \ os_qnxnto.cpp os_qnxnto.h os_solaris.cpp os_solaris.h \ os_win32.cpp os_generic.cpp os_generic.h aacraid.h cciss.cpp \ cciss.h cissio_freebsd.h dev_areca.cpp dev_areca.h \ dev_legacy.cpp linux_nvme_ioctl.h freebsd_nvme_ioctl.h \ netbsd_nvme_ioctl.h megaraid.h regex/regcomp.c regex/regexec.c \ regex/regex_internal.c smartd_OBJECTS = $(am_smartd_OBJECTS) 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 = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } SCRIPTS = $(examples_SCRIPTS) $(sbin_SCRIPTS) $(smartdscript_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = CCASCOMPILE = $(CCAS) $(AM_CCASFLAGS) $(CCASFLAGS) AM_V_CCAS = $(am__v_CCAS_@AM_V@) am__v_CCAS_ = $(am__v_CCAS_@AM_DEFAULT_V@) am__v_CCAS_0 = @echo " CCAS " $@; am__v_CCAS_1 = SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) \ $(nodist_EXTRA_smartctl_SOURCES) $(smartd_SOURCES) \ $(EXTRA_smartd_SOURCES) $(nodist_EXTRA_smartd_SOURCES) DIST_SOURCES = $(am__smartctl_SOURCES_DIST) \ $(am__EXTRA_smartctl_SOURCES_DIST) $(am__smartd_SOURCES_DIST) \ $(am__EXTRA_smartd_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 NROFF = nroff MANS = $(man_MANS) DATA = $(docs_DATA) $(drivedb_DATA) $(examples_DATA) $(initd_DATA) \ $(sysconf_DATA) $(systemdsystemunit_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in AUTHORS \ COPYING ChangeLog INSTALL NEWS README TODO compile \ config.guess config.sub depcomp install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ASFLAGS = @ASFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CAPNG_LDADD = @CAPNG_LDADD@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ CCASFLAGS = @CCASFLAGS@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DRIVEDB_BRANCH = @DRIVEDB_BRANCH@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL = @INSTALL@ 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@ MAKEINFO = @MAKEINFO@ MAKENSIS = @MAKENSIS@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSTEMD_LDADD = @SYSTEMD_LDADD@ VERSION = @VERSION@ WINDMC = @WINDMC@ WINDRES = @WINDRES@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ attributelog = @attributelog@ attributelogdir = @attributelogdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ drivedbdir = @drivedbdir@ dvidir = @dvidir@ exampledir = @exampledir@ exec_prefix = @exec_prefix@ gcc_have_attr_packed = @gcc_have_attr_packed@ gnupg = @gnupg@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ initddir = @initddir@ initdfile = @initdfile@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ os_deps = @os_deps@ os_dltools = @os_dltools@ os_dnsdomainname = @os_dnsdomainname@ os_hostname = @os_hostname@ os_libs = @os_libs@ os_mailer = @os_mailer@ os_man_filter = @os_man_filter@ os_nisdomainname = @os_nisdomainname@ os_win32_manifest = @os_win32_manifest@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ releaseversion = @releaseversion@ savestates = @savestates@ savestatesdir = @savestatesdir@ sbindir = @sbindir@ scriptpath = @scriptpath@ sharedstatedir = @sharedstatedir@ smartd_suffix = @smartd_suffix@ smartdplugindir = @smartdplugindir@ smartdscriptdir = @smartdscriptdir@ smartmontools_release_date = @smartmontools_release_date@ smartmontools_release_time = @smartmontools_release_time@ srcdir = @srcdir@ svn_deps = @svn_deps@ sysconfdir = @sysconfdir@ systemdenvfile = @systemdenvfile@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ with_nvme_devicescan = @with_nvme_devicescan@ with_update_smart_drivedb = @with_update_smart_drivedb@ ACLOCAL_AMFLAGS = -I m4 # Make sure .cpp takes precedence to avoid compiling old .c file SUFFIXES = .cpp .c .s .o # BUILD_INFO can be provided by package maintainers (see INSTALL file) BUILD_INFO = "(local build)" AM_CPPFLAGS = -DBUILD_INFO='$(BUILD_INFO)' \ -DSMARTMONTOOLS_SYSCONFDIR='"$(sysconfdir)"' \ -DSMARTMONTOOLS_SMARTDSCRIPTDIR='"$(smartdscriptdir)"' \ $(am__append_1) $(am__append_2) $(am__append_3) \ $(am__append_4) $(am__append_5) $(am__append_6) @ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_WIN32_MINGW_FALSE@sbin_SCRIPTS = update-smart-drivedb smartctl_SOURCES = smartctl.cpp smartctl.h atacmdnames.cpp \ atacmdnames.h atacmds.cpp atacmds.h ataidentify.cpp \ ataidentify.h ataprint.cpp ataprint.h dev_ata_cmd_set.cpp \ dev_ata_cmd_set.h dev_intelliprop.cpp dev_intelliprop.h \ dev_interface.cpp dev_interface.h dev_tunnelled.h drivedb.h \ json.cpp json.h knowndrives.cpp knowndrives.h nvmecmds.cpp \ nvmecmds.h nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h \ scsiata.cpp scsinvme.cpp scsiprint.cpp scsiprint.h utility.cpp \ utility.h sg_unaligned.h $(am__append_7) $(am__append_13) \ $(am__append_15) $(am__append_19) smartctl_LDADD = $(os_deps) $(os_libs) $(am__append_8) \ $(am__append_21) smartctl_DEPENDENCIES = $(os_deps) $(am__append_9) EXTRA_smartctl_SOURCES = os_darwin.cpp os_darwin.h os_linux.cpp \ os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \ os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \ os_qnxnto.cpp os_qnxnto.h os_solaris.cpp os_solaris.h \ os_win32.cpp os_generic.cpp os_generic.h aacraid.h cciss.cpp \ cciss.h cissio_freebsd.h dev_areca.cpp dev_areca.h \ dev_legacy.cpp linux_nvme_ioctl.h megaraid.h $(am__append_17) smartd_SOURCES = smartd.cpp atacmdnames.cpp atacmdnames.h atacmds.cpp \ atacmds.h dev_ata_cmd_set.cpp dev_ata_cmd_set.h \ dev_intelliprop.cpp dev_intelliprop.h dev_interface.cpp \ dev_interface.h dev_tunnelled.h drivedb.h knowndrives.cpp \ knowndrives.h nvmecmds.cpp nvmecmds.h scsicmds.cpp scsicmds.h \ scsiata.cpp scsinvme.cpp utility.cpp utility.h sg_unaligned.h \ $(am__append_10) $(am__append_14) $(am__append_16) \ $(am__append_20) smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD) $(SYSTEMD_LDADD) \ $(am__append_11) $(am__append_22) smartd_DEPENDENCIES = $(os_deps) $(am__append_12) EXTRA_smartd_SOURCES = os_darwin.cpp os_darwin.h os_linux.cpp \ os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \ os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \ os_qnxnto.cpp os_qnxnto.h os_solaris.cpp os_solaris.h \ os_win32.cpp os_generic.cpp os_generic.h aacraid.h cciss.cpp \ cciss.h cissio_freebsd.h dev_areca.cpp dev_areca.h \ dev_legacy.cpp linux_nvme_ioctl.h freebsd_nvme_ioctl.h \ netbsd_nvme_ioctl.h megaraid.h $(am__append_18) # Exclude from source tarball nodist_EXTRA_smartctl_SOURCES = os_solaris_ata.s nodist_EXTRA_smartd_SOURCES = os_solaris_ata.s # 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 smartctl.1m smartd.1m \ @OS_SOLARIS_TRUE@ $(am__append_23) # For systems that adopts traditional manner @OS_SOLARIS_FALSE@man_MANS = smartd.conf.5 smartctl.8 smartd.8 \ @OS_SOLARIS_FALSE@ $(am__append_24) docsdir = $(docdir) docs_DATA = \ AUTHORS \ ChangeLog \ ChangeLog-5.0-6.0 \ COPYING \ INSTALL \ NEWS \ README \ TODO \ smartd.conf examplesdir = $(exampledir) examples_DATA = \ examplescripts/README examples_SCRIPTS = \ examplescripts/Example1 \ examplescripts/Example2 \ examplescripts/Example3 \ examplescripts/Example4 \ examplescripts/Example5 \ examplescripts/Example6 sysconf_DATA = smartd.conf smartdscript_SCRIPTS = smartd_warning.sh EXTRA_DIST = \ .editorconfig \ autogen.sh \ smartd.initd.in \ smartd.cygwin.initd.in \ smartd.freebsd.initd.in \ smartd.8.in \ smartctl.8.in \ smartd.conf.5.in \ smartd.conf \ smartd.service.in \ smartd_warning.sh.in \ update-smart-drivedb.in \ update-smart-drivedb.8.in \ m4/pkg.m4 \ os_darwin/com.smartmontools.smartd.plist.in \ os_darwin/pkg/PackageInfo.in \ os_darwin/pkg/Distribution.in \ os_darwin/pkg/installer/README.html \ os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall \ os_win32/default.manifest \ os_win32/installer.nsi \ os_win32/runcmd.c \ os_win32/smartd_mailer.ps1 \ os_win32/smartd_mailer.conf.sample.ps1 \ os_win32/smartd_warning.cmd \ os_win32/syslogevt.mc \ os_win32/update-smart-drivedb.nsi \ os_win32/versioninfo.rc.in \ os_win32/wtssendmsg.c \ $(docs_DATA) \ $(examples_DATA) \ $(examples_SCRIPTS) CLEANFILES = smartd.8 smartd.1m smartd.8.html smartd.8.html.tmp \ smartd.8.pdf smartd.8.txt smartctl.8 smartctl.1m \ smartctl.8.html smartctl.8.html.tmp smartctl.8.pdf \ smartctl.8.txt smartd.conf.5 smartd.conf.4 smartd.conf.5.html \ smartd.conf.5.html.tmp smartd.conf.5.pdf smartd.conf.5.txt \ smartd.initd smartd.cygwin.initd smartd.freebsd.initd \ smartd.service smartd_warning.sh svnversion.h \ update-smart-drivedb update-smart-drivedb.8 \ update-smart-drivedb.1m update-smart-drivedb.8.html \ update-smart-drivedb.8.html.tmp update-smart-drivedb.8.pdf \ update-smart-drivedb.8.txt SMART $(am__append_27) # 'make maintainer-clean' also removes files generated by './autogen.sh' MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in \ $(srcdir)/aclocal.m4 \ $(srcdir)/compile \ $(srcdir)/configure \ $(srcdir)/config.guess \ $(srcdir)/config.h.in \ $(srcdir)/config.h.in~ \ $(srcdir)/config.sub \ $(srcdir)/depcomp \ $(srcdir)/install-sh \ $(srcdir)/missing \ $(srcdir)/m4/pkg.m4 @ENABLE_DRIVEDB_TRUE@drivedb_DATA = drivedb.h @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@initd_DATA = $(initdfile) @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@initd_DATA = com.smartmontools.smartd.plist @INSTALL_INITSCRIPT_FALSE@initd_DATA_install = install-initdDATA-null @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@initd_DATA_install = install-initdDATA-generic @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@initd_DATA_install = install-initdDATA-darwin @INSTALL_INITSCRIPT_FALSE@initd_DATA_uninstall = uninstall-initdDATA-null @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@initd_DATA_uninstall = uninstall-initdDATA-generic @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@initd_DATA_uninstall = uninstall-initdDATA-darwin @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@initd_install_name = smartd$(smartd_suffix) @INSTALL_SYSTEMDUNIT_TRUE@systemdsystemunit_DATA = smartd.service # # Build man pages # MAN_FILTER = { \ sed -e 's|CURRENT_SVN_VERSION|$(releaseversion)|g' \ -e "s|CURRENT_SVN_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g" \ -e "s|CURRENT_SVN_REV|`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`|g" \ -e 's|/usr/local/share/man/|$(mandir)/|g' \ -e 's|/usr/local/sbin/|$(sbindir)/|g' \ -e 's|/usr/local/share/doc/smartmontools/examplescripts/|!exampledir!|g' \ -e 's|/usr/local/share/doc/smartmontools/|$(docsdir)/|g' \ -e 's|!exampledir!|$(exampledir)/|g' \ -e 's|/usr/local/etc/smartd\.conf|$(sysconfdir)/smartd.conf|g' \ -e 's|/usr/local/etc/smart_drivedb\.h|$(sysconfdir)/smart_drivedb.h|g' \ -e 's|/usr/local/etc/smartd_warning\.sh|$(smartdscriptdir)/smartd_warning.sh|g' \ -e 's|\\fBmail\\fP|\\fB$(os_mailer)\\fP|g' \ -e 's|\\'\''mail\\'\''|\\'\''$(os_mailer)\\'\''|g' \ -e 's|/usr/bin/mail|/usr/bin/$(os_mailer)|g' \ -e 's|RELEASE_6_0_DRIVEDB|$(DRIVEDB_BRANCH)|g' | \ if test -n '$(drivedbdir)'; then \ sed 's|/usr/local/share/smartmontools/drivedb\.h|$(drivedbdir)/drivedb.h|g' ; \ else \ sed '/^\.\\" %IF ENABLE_DRIVEDB/,/^\.\\" %ENDIF ENABLE_DRIVEDB/ s,^,.\\"\# ,' ; \ fi | \ if test '$(with_update_smart_drivedb)' = 'yes'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_UPDATE_SMART_DRIVEDB/,/^\.\\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(initddir)'; then \ sed 's|/usr/local/etc/rc\.d/init\.d/|$(initddir)/|g' ; \ else \ sed '/^\.\\" %IF ENABLE_INITSCRIPT/,/^\.\\" %ENDIF ENABLE_INITSCRIPT/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(savestates)'; then \ sed 's|/usr/local/var/lib/smartmontools/smartd\.|$(savestates)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_SAVESTATES/,/^\.\\" %ENDIF ENABLE_SAVESTATES/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(attributelog)'; then \ sed 's|/usr/local/var/lib/smartmontools/attrlog\.|$(attributelog)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_ATTRIBUTELOG/,/^\.\\" %ENDIF ENABLE_ATTRIBUTELOG/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(smartdplugindir)'; then \ sed 's|/usr/local/etc/smartd_warning\.d|$(smartdplugindir)|g' ; \ else \ sed '/^\.\\" %IF ENABLE_SMARTDPLUGINDIR/,/^\.\\" %ENDIF ENABLE_SMARTDPLUGINDIR/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(CAPNG_LDADD)'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(SYSTEMD_LDADD)'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_SYSTEMD_NOTIFY/,/^\.\\" %ENDIF ENABLE_SYSTEMD_NOTIFY/ s,^,.\\"\# ,' ; \ fi | \ if test '$(with_nvme_devicescan)' = 'yes'; then \ cat; \ else \ sed '/^\.\\" %IF ENABLE_NVME_DEVICESCAN/,/^\.\\" %ENDIF ENABLE_NVME_DEVICESCAN/ s,^,.\\"\# ,' ; \ fi | \ if test -n '$(os_man_filter)'; then \ sed -e 's,OS_MAN_FILTER,$(os_man_filter),g' \ -e '/^\.\\" %IF NOT OS .*$(os_man_filter)/,/^.\\" %ENDIF NOT OS .*$(os_man_filter)/ s,^,.\\"\# ,' \ -e '/^\.\\" %IF OS .*$(os_man_filter)/,/^\.\\" %ENDIF OS .*$(os_man_filter)/ s,^,!!,' \ -e '/^\.\\" %IF OS ./,/^\.\\" %ENDIF OS ./ s,^,.\\"\# ,' \ -e '/^!*\.\\" %IF NOT OS ./,/^!*\.\\" %ENDIF NOT OS ./ s,^,!!,' \ -e 's,^!!!*\.\\"! \(.*\)$$,\1 \\"\#,' \ -e 's,^!!!*,,' ; \ else \ cat; \ fi; } # Build Solaris specific man pages SOLARIS_MAN_FILTER = \ sed -e '/^\.TH/s, \([58]\) , !!\1!! ,' \ -e '/^\.BR/s, (\([578]\)), (!!\1!!),' \ -e 's,\\fP(\([578]\)),\\fP(!!\1!!),g' \ -e 's,!!5!!,4,g' -e 's,!!7!!,5,g' -e 's,!!8!!,1m,g' \ -e 's,/var/log/messages,/var/adm/messages,g' # Convert man pages into .html, .pdf and .txt # TODO: configure MAN2HTML = man2html #MAN2HTML = groff -man -Thtml MAN2PDF = man -Tpdf -l #MAN2PDF = groff -man -Tpdf MAN2TXT = groff -man -Tascii -P'-bcou' # Remove HTTP header and fix links in man2html output FIXHTML = sed -e '1s,^Content-type.*,,' \ -e 's,,,g' \ -e 's,,,g' \ -e 's,\([^<]*\),\1,g' \ -e 's,\([^<]*\),\1,g' # Convert version for VERSIONINFO resource: 6.6 r4519 -> 6.6.0.4519, # set description, name, version and year @OS_WIN32_MINGW_TRUE@WIN_RC_FILTER = { \ @OS_WIN32_MINGW_TRUE@ ver=`echo '$(PACKAGE_VERSION).0' | sed -n 's,^\([0-9]*\.[0-9]*\.[0-9]*\).*$$,\1,p'` && \ @OS_WIN32_MINGW_TRUE@ rev=`sed -n 's,^.*REV[^"]*"\([0-9]*\).*$$,\1,p' svnversion.h` && \ @OS_WIN32_MINGW_TRUE@ txtver="$${ver:-0.0.0}.$${rev:-0}" && binver=`echo "$$txtver" | sed 's|\.|,|g'` && \ @OS_WIN32_MINGW_TRUE@ yy=`sed -n 's,^.*DATE[^"]*"20\([0-9][0-9]\).*$$,\1,p' svnversion.h` && yy="$${yy:-XX}" && \ @OS_WIN32_MINGW_TRUE@ sed -e "s|@DESC@|$$d|" -e "s|@NAME@|$$n|" -e "s|@BINARY_VERSION@|$$binver|g" \ @OS_WIN32_MINGW_TRUE@ -e "s|@TEXT_VERSION@|$$txtver|g" -e "s|@YY@|$$yy|g"; } @OS_WIN32_MINGW_TRUE@WIN_MAKE_RES = \ @OS_WIN32_MINGW_TRUE@ echo "n=$$n d=\"$$d"'"; $$(WIN_RC_FILTER) < $< > $@'; \ @OS_WIN32_MINGW_TRUE@ $(WIN_RC_FILTER) < $< > $@ @OS_WIN32_MINGW_TRUE@WIN_APP_MANIFEST = \ @OS_WIN32_MINGW_TRUE@ if test -n '$(os_win32_manifest)'; then \ @OS_WIN32_MINGW_TRUE@ echo "echo '1 24 \"$(srcdir)/$(os_win32_manifest)\"' >> $@"; \ @OS_WIN32_MINGW_TRUE@ echo '1 24 "$(srcdir)/$(os_win32_manifest)"' >> $@; \ @OS_WIN32_MINGW_TRUE@ fi @OS_WIN32_MINGW_TRUE@@OS_WIN64_FALSE@win_bits = 32 # Definitions for Windows distribution @OS_WIN32_MINGW_TRUE@@OS_WIN64_TRUE@win_bits = 64 @OS_WIN32_MINGW_TRUE@distdir_win32 = $(PACKAGE)-$(VERSION).win$(win_bits) @OS_WIN32_MINGW_TRUE@distzip_win32 = $(PACKAGE)-$(VERSION).win$(win_bits).zip @OS_WIN32_MINGW_TRUE@distinst_win32 = $(PACKAGE)-$(VERSION).win$(win_bits)-setup.exe @OS_WIN32_MINGW_TRUE@exedir_win32 = $(distdir_win32)/bin @OS_WIN32_MINGW_TRUE@docdir_win32 = $(distdir_win32)/doc @OS_WIN32_MINGW_TRUE@EXEFILES_WIN32 = $(exedir_win32)/smartctl.exe \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartctl-nc.exe \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartd.exe \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartd_mailer.ps1 \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartd_mailer.conf.sample.ps1 \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/smartd_warning.cmd \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/runcmda.exe \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/runcmdu.exe \ @OS_WIN32_MINGW_TRUE@ $(exedir_win32)/wtssendmsg.exe \ @OS_WIN32_MINGW_TRUE@ $(am__append_25) @OS_WIN32_MINGW_TRUE@FILES_WIN32 = $(EXEFILES_WIN32) \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/AUTHORS.txt \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/ChangeLog.txt \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/ChangeLog-5.0-6.0.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)/checksums$(win_bits).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.pdf \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.8.html \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.8.pdf \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.conf.5.html \ @OS_WIN32_MINGW_TRUE@ $(docdir_win32)/smartd.conf.5.pdf \ @OS_WIN32_MINGW_TRUE@ $(am__append_26) # Note: Only use without options to be compatible with all variants @OS_WIN32_MINGW_TRUE@UNIX2DOS = unix2dos # Build os_win32/vc14/{config.h,smart*.rc,svnversion.h} for MSVC14 from MinGW files @OS_WIN32_MINGW_TRUE@vcver = 14 @OS_WIN32_MINGW_TRUE@CONFIG_VC_FILES = \ @OS_WIN32_MINGW_TRUE@ $(srcdir)/os_win32/vc$(vcver)/config.h \ @OS_WIN32_MINGW_TRUE@ $(srcdir)/os_win32/vc$(vcver)/smartctl_res.rc \ @OS_WIN32_MINGW_TRUE@ $(srcdir)/os_win32/vc$(vcver)/smartd_res.rc \ @OS_WIN32_MINGW_TRUE@ $(srcdir)/os_win32/vc$(vcver)/svnversion.h # Definitions for OSX distribution @OS_DARWIN_TRUE@distdir_darwin = $(PACKAGE)-$(VERSION).darwin @OS_DARWIN_TRUE@dmg_darwin = $(PACKAGE)-$(VERSION).dmg @OS_DARWIN_TRUE@pkg_darwin = $(PACKAGE)-$(VERSION).pkg all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .cpp .c .s .o .obj am--refresh: Makefile @: $(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'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign 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) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 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) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) smartctl$(EXEEXT): $(smartctl_OBJECTS) $(smartctl_DEPENDENCIES) $(EXTRA_smartctl_DEPENDENCIES) @rm -f smartctl$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(smartctl_OBJECTS) $(smartctl_LDADD) $(LIBS) smartd$(EXEEXT): $(smartd_OBJECTS) $(smartd_DEPENDENCIES) $(EXTRA_smartd_DEPENDENCIES) @rm -f smartd$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(smartd_OBJECTS) $(smartd_LDADD) $(LIBS) install-examplesSCRIPTS: $(examples_SCRIPTS) @$(NORMAL_INSTALL) @list='$(examples_SCRIPTS)'; test -n "$(examplesdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(examplesdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(examplesdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(examplesdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(examplesdir)$$dir" || exit $$?; \ } \ ; done uninstall-examplesSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(examples_SCRIPTS)'; test -n "$(examplesdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(examplesdir)'; $(am__uninstall_files_from_dir) install-sbinSCRIPTS: $(sbin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) install-smartdscriptSCRIPTS: $(smartdscript_SCRIPTS) @$(NORMAL_INSTALL) @list='$(smartdscript_SCRIPTS)'; test -n "$(smartdscriptdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(smartdscriptdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(smartdscriptdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(smartdscriptdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(smartdscriptdir)$$dir" || exit $$?; \ } \ ; done uninstall-smartdscriptSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(smartdscript_SCRIPTS)'; test -n "$(smartdscriptdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(smartdscriptdir)'; $(am__uninstall_files_from_dir) 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)/ataidentify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ataprint.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cciss.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon_win32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_areca.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_ata_cmd_set.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_intelliprop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_interface.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_legacy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knowndrives.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nvmecmds.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nvmeprint.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_os2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_qnxnto.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)/popen_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)/scsiata.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsicmds.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsinvme.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@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wmiquery.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` getopt.o: getopt/getopt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getopt.o -MD -MP -MF $(DEPDIR)/getopt.Tpo -c -o getopt.o `test -f 'getopt/getopt.c' || echo '$(srcdir)/'`getopt/getopt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getopt.Tpo $(DEPDIR)/getopt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getopt/getopt.c' object='getopt.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o getopt.o `test -f 'getopt/getopt.c' || echo '$(srcdir)/'`getopt/getopt.c getopt.obj: getopt/getopt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getopt.obj -MD -MP -MF $(DEPDIR)/getopt.Tpo -c -o getopt.obj `if test -f 'getopt/getopt.c'; then $(CYGPATH_W) 'getopt/getopt.c'; else $(CYGPATH_W) '$(srcdir)/getopt/getopt.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getopt.Tpo $(DEPDIR)/getopt.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getopt/getopt.c' object='getopt.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o getopt.obj `if test -f 'getopt/getopt.c'; then $(CYGPATH_W) 'getopt/getopt.c'; else $(CYGPATH_W) '$(srcdir)/getopt/getopt.c'; fi` getopt1.o: getopt/getopt1.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getopt1.o -MD -MP -MF $(DEPDIR)/getopt1.Tpo -c -o getopt1.o `test -f 'getopt/getopt1.c' || echo '$(srcdir)/'`getopt/getopt1.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getopt1.Tpo $(DEPDIR)/getopt1.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getopt/getopt1.c' object='getopt1.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o getopt1.o `test -f 'getopt/getopt1.c' || echo '$(srcdir)/'`getopt/getopt1.c getopt1.obj: getopt/getopt1.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getopt1.obj -MD -MP -MF $(DEPDIR)/getopt1.Tpo -c -o getopt1.obj `if test -f 'getopt/getopt1.c'; then $(CYGPATH_W) 'getopt/getopt1.c'; else $(CYGPATH_W) '$(srcdir)/getopt/getopt1.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getopt1.Tpo $(DEPDIR)/getopt1.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getopt/getopt1.c' object='getopt1.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o getopt1.obj `if test -f 'getopt/getopt1.c'; then $(CYGPATH_W) 'getopt/getopt1.c'; else $(CYGPATH_W) '$(srcdir)/getopt/getopt1.c'; fi` regex.o: regex/regex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regex.c' || echo '$(srcdir)/'`regex/regex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regex.Tpo $(DEPDIR)/regex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regex.c' object='regex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.o `test -f 'regex/regex.c' || echo '$(srcdir)/'`regex/regex.c regex.obj: regex/regex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regex.c'; then $(CYGPATH_W) 'regex/regex.c'; else $(CYGPATH_W) '$(srcdir)/regex/regex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regex.Tpo $(DEPDIR)/regex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regex.c' object='regex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.obj `if test -f 'regex/regex.c'; then $(CYGPATH_W) 'regex/regex.c'; else $(CYGPATH_W) '$(srcdir)/regex/regex.c'; fi` regcomp.o: regex/regcomp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regcomp.c' || echo '$(srcdir)/'`regex/regcomp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regcomp.Tpo $(DEPDIR)/regcomp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regcomp.c' object='regcomp.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.o `test -f 'regex/regcomp.c' || echo '$(srcdir)/'`regex/regcomp.c regcomp.obj: regex/regcomp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regcomp.c'; then $(CYGPATH_W) 'regex/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/regex/regcomp.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regcomp.Tpo $(DEPDIR)/regcomp.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regcomp.c' object='regcomp.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.obj `if test -f 'regex/regcomp.c'; then $(CYGPATH_W) 'regex/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/regex/regcomp.c'; fi` regexec.o: regex/regexec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regexec.c' || echo '$(srcdir)/'`regex/regexec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regexec.Tpo $(DEPDIR)/regexec.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regexec.c' object='regexec.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.o `test -f 'regex/regexec.c' || echo '$(srcdir)/'`regex/regexec.c regexec.obj: regex/regexec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regexec.c'; then $(CYGPATH_W) 'regex/regexec.c'; else $(CYGPATH_W) '$(srcdir)/regex/regexec.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regexec.Tpo $(DEPDIR)/regexec.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regexec.c' object='regexec.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.obj `if test -f 'regex/regexec.c'; then $(CYGPATH_W) 'regex/regexec.c'; else $(CYGPATH_W) '$(srcdir)/regex/regexec.c'; fi` regex_internal.o: regex/regex_internal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regex_internal.c' || echo '$(srcdir)/'`regex/regex_internal.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regex_internal.Tpo $(DEPDIR)/regex_internal.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regex_internal.c' object='regex_internal.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.o `test -f 'regex/regex_internal.c' || echo '$(srcdir)/'`regex/regex_internal.c regex_internal.obj: regex/regex_internal.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(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 'regex/regex_internal.c'; then $(CYGPATH_W) 'regex/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/regex/regex_internal.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/regex_internal.Tpo $(DEPDIR)/regex_internal.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex/regex_internal.c' object='regex_internal.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.obj `if test -f 'regex/regex_internal.c'; then $(CYGPATH_W) 'regex/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/regex/regex_internal.c'; fi` .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` popen_win32.o: os_win32/popen_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT popen_win32.o -MD -MP -MF $(DEPDIR)/popen_win32.Tpo -c -o popen_win32.o `test -f 'os_win32/popen_win32.cpp' || echo '$(srcdir)/'`os_win32/popen_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/popen_win32.Tpo $(DEPDIR)/popen_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/popen_win32.cpp' object='popen_win32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o popen_win32.o `test -f 'os_win32/popen_win32.cpp' || echo '$(srcdir)/'`os_win32/popen_win32.cpp popen_win32.obj: os_win32/popen_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT popen_win32.obj -MD -MP -MF $(DEPDIR)/popen_win32.Tpo -c -o popen_win32.obj `if test -f 'os_win32/popen_win32.cpp'; then $(CYGPATH_W) 'os_win32/popen_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/popen_win32.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/popen_win32.Tpo $(DEPDIR)/popen_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/popen_win32.cpp' object='popen_win32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o popen_win32.obj `if test -f 'os_win32/popen_win32.cpp'; then $(CYGPATH_W) 'os_win32/popen_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/popen_win32.cpp'; fi` wmiquery.o: os_win32/wmiquery.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT wmiquery.o -MD -MP -MF $(DEPDIR)/wmiquery.Tpo -c -o wmiquery.o `test -f 'os_win32/wmiquery.cpp' || echo '$(srcdir)/'`os_win32/wmiquery.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wmiquery.Tpo $(DEPDIR)/wmiquery.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/wmiquery.cpp' object='wmiquery.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o wmiquery.o `test -f 'os_win32/wmiquery.cpp' || echo '$(srcdir)/'`os_win32/wmiquery.cpp wmiquery.obj: os_win32/wmiquery.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT wmiquery.obj -MD -MP -MF $(DEPDIR)/wmiquery.Tpo -c -o wmiquery.obj `if test -f 'os_win32/wmiquery.cpp'; then $(CYGPATH_W) 'os_win32/wmiquery.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/wmiquery.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wmiquery.Tpo $(DEPDIR)/wmiquery.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/wmiquery.cpp' object='wmiquery.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o wmiquery.obj `if test -f 'os_win32/wmiquery.cpp'; then $(CYGPATH_W) 'os_win32/wmiquery.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/wmiquery.cpp'; fi` daemon_win32.o: os_win32/daemon_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT daemon_win32.o -MD -MP -MF $(DEPDIR)/daemon_win32.Tpo -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.cpp' || echo '$(srcdir)/'`os_win32/daemon_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/daemon_win32.Tpo $(DEPDIR)/daemon_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/daemon_win32.cpp' object='daemon_win32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.cpp' || echo '$(srcdir)/'`os_win32/daemon_win32.cpp daemon_win32.obj: os_win32/daemon_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT daemon_win32.obj -MD -MP -MF $(DEPDIR)/daemon_win32.Tpo -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.cpp'; then $(CYGPATH_W) 'os_win32/daemon_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/daemon_win32.Tpo $(DEPDIR)/daemon_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/daemon_win32.cpp' object='daemon_win32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.cpp'; then $(CYGPATH_W) 'os_win32/daemon_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.cpp'; fi` syslog_win32.o: os_win32/syslog_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT syslog_win32.o -MD -MP -MF $(DEPDIR)/syslog_win32.Tpo -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.cpp' || echo '$(srcdir)/'`os_win32/syslog_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/syslog_win32.Tpo $(DEPDIR)/syslog_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/syslog_win32.cpp' object='syslog_win32.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.cpp' || echo '$(srcdir)/'`os_win32/syslog_win32.cpp syslog_win32.obj: os_win32/syslog_win32.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT syslog_win32.obj -MD -MP -MF $(DEPDIR)/syslog_win32.Tpo -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.cpp'; then $(CYGPATH_W) 'os_win32/syslog_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/syslog_win32.Tpo $(DEPDIR)/syslog_win32.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='os_win32/syslog_win32.cpp' object='syslog_win32.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.cpp'; then $(CYGPATH_W) 'os_win32/syslog_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.cpp'; fi` .s.o: $(AM_V_CCAS)$(CCASCOMPILE) -c -o $@ $< .s.obj: $(AM_V_CCAS)$(CCASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` install-man5: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.5[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) install-man8: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.8[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) install-docsDATA: $(docs_DATA) @$(NORMAL_INSTALL) @list='$(docs_DATA)'; test -n "$(docsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docsdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docsdir)" || exit $$?; \ done uninstall-docsDATA: @$(NORMAL_UNINSTALL) @list='$(docs_DATA)'; test -n "$(docsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docsdir)'; $(am__uninstall_files_from_dir) install-drivedbDATA: $(drivedb_DATA) @$(NORMAL_INSTALL) @list='$(drivedb_DATA)'; test -n "$(drivedbdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(drivedbdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(drivedbdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(drivedbdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(drivedbdir)" || exit $$?; \ done uninstall-drivedbDATA: @$(NORMAL_UNINSTALL) @list='$(drivedb_DATA)'; test -n "$(drivedbdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(drivedbdir)'; $(am__uninstall_files_from_dir) install-examplesDATA: $(examples_DATA) @$(NORMAL_INSTALL) @list='$(examples_DATA)'; test -n "$(examplesdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(examplesdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(examplesdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(examplesdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(examplesdir)" || exit $$?; \ done uninstall-examplesDATA: @$(NORMAL_UNINSTALL) @list='$(examples_DATA)'; test -n "$(examplesdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(examplesdir)'; $(am__uninstall_files_from_dir) install-systemdsystemunitDATA: $(systemdsystemunit_DATA) @$(NORMAL_INSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \ done uninstall-systemdsystemunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -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 $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_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*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(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 \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { 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-am @OS_SOLARIS_FALSE@all-local: all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) config.h \ all-local installdirs: installdirs-local for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(smartdscriptdir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" "$(DESTDIR)$(drivedbdir)" "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)" "$(DESTDIR)$(systemdsystemunitdir)"; 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: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am distclean: distclean-am -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-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-data-local install-docsDATA \ install-drivedbDATA install-examplesDATA \ install-examplesSCRIPTS install-initdDATA install-man \ install-smartdscriptSCRIPTS install-systemdsystemunitDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-sbinPROGRAMS install-sbinSCRIPTS \ install-sysconfDATA install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: @OS_SOLARIS_FALSE@install-man: install-man5 install-man8 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -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-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-docsDATA uninstall-drivedbDATA \ uninstall-examplesDATA uninstall-examplesSCRIPTS \ uninstall-initdDATA uninstall-man uninstall-sbinPROGRAMS \ uninstall-sbinSCRIPTS uninstall-smartdscriptSCRIPTS \ uninstall-sysconfDATA uninstall-systemdsystemunitDATA @OS_SOLARIS_FALSE@uninstall-man: uninstall-man5 uninstall-man8 .MAKE: all install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local am--refresh check \ check-am clean clean-cscope clean-generic clean-sbinPROGRAMS \ cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-compile distclean-generic \ distclean-hdr distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-data-local install-docsDATA install-drivedbDATA \ install-dvi install-dvi-am install-examplesDATA \ install-examplesSCRIPTS install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-initdDATA install-man install-man5 install-man8 \ install-pdf install-pdf-am install-ps install-ps-am \ install-sbinPROGRAMS install-sbinSCRIPTS \ install-smartdscriptSCRIPTS install-strip install-sysconfDATA \ install-systemdsystemunitDATA installcheck installcheck-am \ installdirs installdirs-local maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-docsDATA uninstall-drivedbDATA \ uninstall-examplesDATA uninstall-examplesSCRIPTS \ uninstall-initdDATA uninstall-man uninstall-man5 \ uninstall-man8 uninstall-sbinPROGRAMS uninstall-sbinSCRIPTS \ uninstall-smartdscriptSCRIPTS uninstall-sysconfDATA \ uninstall-systemdsystemunitDATA .PRECIOUS: Makefile @SET_MAKE@ @OS_SOLARIS_TRUE@all-local: $(extra_MANS) @OS_SOLARIS_TRUE@install-man: $(extra_MANS) @OS_SOLARIS_TRUE@ @$(NORMAL_INSTALL) @OS_SOLARIS_TRUE@ $(MKDIR_P) '$(DESTDIR)$(mandir)/man4' @OS_SOLARIS_TRUE@ $(MKDIR_P) '$(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 # If modified smartd.conf exists install smartd.conf.sample instead install-sysconfDATA: $(sysconf_DATA) $(MKDIR_P) '$(DESTDIR)$(sysconfdir)' @s="$(srcdir)/smartd.conf"; \ f="$(DESTDIR)$(sysconfdir)/smartd.conf$(smartd_suffix)"; \ if test -z "$(smartd_suffix)" && test -f "$$f"; then \ if cmp "$$s" "$$f" >/dev/null 2>/dev/null; then :; else \ echo "************************************************************"; \ echo "*** $$f preserved"; \ echo "*** installing smartd.conf.sample instead"; \ echo "************************************************************"; \ f="$$f".sample; \ fi; \ fi; \ echo " $(INSTALL_DATA) '$$s' '$$f'"; \ $(INSTALL_DATA) "$$s" "$$f" # If smartd.conf.sample exists preserve smartd.conf uninstall-sysconfDATA: @f="$(DESTDIR)$(sysconfdir)/smartd.conf$(smartd_suffix)"; \ if test -z "$(smartd_suffix)" && test -f "$$f".sample; then \ echo "************************************************************"; \ echo "*** $$f preserved"; \ echo "*** removing smartd.conf.sample instead"; \ echo "************************************************************"; \ f="$$f".sample; \ fi; \ echo " rm -f '$$f'"; \ rm -f "$$f" smartctl.o utility.o: svnversion.h # Get version info from SVN @IS_SVN_BUILD_TRUE@svnversion.h: ChangeLog Makefile $(svn_deps) @IS_SVN_BUILD_TRUE@ @echo ' svn info | $$(VERSION_FROM_SVN_INFO) > $@' @IS_SVN_BUILD_TRUE@ @echo '/* svnversion.h. Generated by Makefile from svn info. */' > $@ @IS_SVN_BUILD_TRUE@ @(cd $(srcdir) \ @IS_SVN_BUILD_TRUE@ && svnversion 2>/dev/null | sed -n 's,^\([0-9].*\),REV "\1",p' \ @IS_SVN_BUILD_TRUE@ && TZ= LC_ALL=C svn info 2>/dev/null \ @IS_SVN_BUILD_TRUE@ | sed -n 'h;s,^.* Date: *\([^ ]*\) .*$$,DATE "\1",p;g;s,^.* Date: *[^ ]* *\([^ ]*\) .*$$,TIME "\1",p') \ @IS_SVN_BUILD_TRUE@ | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@ # SVN not available, guess version info from Id strings @IS_SVN_BUILD_FALSE@svnversion.h: ChangeLog Makefile NEWS @IS_SVN_BUILD_FALSE@ @echo ' cat ChangeLog NEWS $$(SOURCES) | $$(VERSION_FROM_SVN_IDS) > $@' @IS_SVN_BUILD_FALSE@ @echo '/* svnversion.h. Generated by Makefile from Id strings. */' > $@ @IS_SVN_BUILD_FALSE@ @(cd $(srcdir) && cat ChangeLog NEWS Makefile.am configure.ac smart*.in *.cpp *.h) \ @IS_SVN_BUILD_FALSE@ | sed -n 's,^.*\$$[I][d]: [^ ]* \([0-9][0-9]* [0-9][-0-9]* [0-9][:0-9]*\)[^:0-9][^$$]*\$$.*$$,\1,p' \ @IS_SVN_BUILD_FALSE@ | sort -n -r \ @IS_SVN_BUILD_FALSE@ | sed -n 'h;s,^\([^ ]*\) .*$$,REV "\1",p;g;s,^[^ ]* \([^ ]*\) .*$$,DATE "\1",p;g;s,^[^ ]* [^ ]* \([^ ]*\)$$,TIME "\1",p;q' \ @IS_SVN_BUILD_FALSE@ | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@ update-smart-drivedb: update-smart-drivedb.in config.status $(SHELL) ./config.status --file=$@ chmod +x $@ smartd_warning.sh: smartd_warning.sh.in config.status $(SHELL) ./config.status --file=$@ chmod +x $@ @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@com.smartmontools.smartd.plist : os_darwin/com.smartmontools.smartd.plist.in @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@ sed "s|/usr/sbin/|$(sbindir)/|" $< > $@ @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@install-initdDATA-darwin: $(initd_DATA) @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@ $(MKDIR_P) '$(DESTDIR)$(initddir)' @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@ $(INSTALL_DATA) $(top_builddir)/$(initd_DATA) $(DESTDIR)$(initddir)/$(initd_DATA) @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@uninstall-initdDATA-darwin: @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_TRUE@ rm -f $(DESTDIR)$(initddir)/$(initd_DATA) @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@$(initdfile): $(srcdir)/$(initdfile).in Makefile @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@ sed 's|/usr/local/sbin/|$(sbindir)/|g' $(srcdir)/$(initdfile).in > $@ @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@install-initdDATA-generic: $(initd_DATA) @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@ $(MKDIR_P) '$(DESTDIR)$(initddir)' @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@ $(INSTALL_SCRIPT) '$(top_builddir)/$(initdfile)' '$(DESTDIR)$(initddir)/smartd$(smartd_suffix)' @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@uninstall-initdDATA-generic: @INSTALL_INITSCRIPT_TRUE@@OS_DARWIN_FALSE@ rm -f '$(DESTDIR)$(initddir)/$(initd_install_name)' @INSTALL_INITSCRIPT_FALSE@install-initdDATA-null: @INSTALL_INITSCRIPT_FALSE@uninstall-initdDATA-null: install-initdDATA : $(initd_DATA_install) uninstall-initdDATA: $(initd_DATA_uninstall) smartd.service: smartd.service.in Makefile @echo ' $$(SMARTD_SERVICE_FILTER) < $(srcdir)/smartd.service.in > $@' @{ \ sed 's|/usr/local/sbin/smartd|$(sbindir)/smartd|' | \ if test -n '$(systemdenvfile)'; then \ sed 's|/usr/local/etc/sysconfig/smartmontools|$(systemdenvfile)|'; \ else \ sed -e '/^EnvironmentFile=/d' -e 's| *\$$smartd[_a-z]* *||g'; \ fi; } < $(srcdir)/smartd.service.in > $@ # Create empty directories if configured. # Default install rules no longer create empty directories since automake 1.11. installdirs-local: @for d in '$(smartdplugindir)' '$(savestatesdir)' '$(attributelogdir)'; do \ test -n "$$d" || continue; \ echo " $(MKDIR_P) '$(DESTDIR)$$d'"; \ $(MKDIR_P) "$(DESTDIR)$$d" || exit 1; \ done install-data-local: installdirs-local # Implicit rule 'smart%: smart%.in ...' does not work with BSD make smartctl.8: smartctl.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@' @$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@ smartd.8: smartd.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@' @$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@ smartd.conf.5: smartd.conf.5.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@' @$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@ update-smart-drivedb.8: update-smart-drivedb.8.in Makefile svnversion.h @echo ' $$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@' @$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@ smartctl.1m: smartctl.8 @echo ' $$(SOLARIS_MAN_FILTER) < smartctl.8 > $@' @$(SOLARIS_MAN_FILTER) < smartctl.8 > $@ smartd.1m: smartd.8 @echo ' $$(SOLARIS_MAN_FILTER) < smartd.8 > $@' @$(SOLARIS_MAN_FILTER) < smartd.8 > $@ smartd.conf.4: smartd.conf.5 @echo ' $$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@' @$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@ update-smart-drivedb.1m: update-smart-drivedb.8 @echo ' $$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@' @$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@ htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html update-smart-drivedb.8.html pdfman: smartctl.8.pdf smartd.8.pdf smartd.conf.5.pdf update-smart-drivedb.8.pdf txtman: smartctl.8.txt smartd.8.txt smartd.conf.5.txt update-smart-drivedb.8.txt %.5.html: %.5 $(MAN2HTML) $< > $@.tmp @echo ' $$(FIXHTML) < $@.tmp > $@' @$(FIXHTML) < $@.tmp > $@ %.8.html: %.8 $(MAN2HTML) $< > $@.tmp @echo ' $$(FIXHTML) < $@.tmp > $@' @$(FIXHTML) < $@.tmp > $@ %.5.pdf: %.5 $(MAN2PDF) $< > $@ %.8.pdf: %.8 $(MAN2PDF) $< > $@ %.5.txt: %.5 $(MAN2TXT) $< > $@ %.8.txt: %.8 $(MAN2TXT) $< > $@ # Check drive database syntax check: @if ./smartctl -B $(srcdir)/drivedb.h -P showall >/dev/null; then \ echo "$(srcdir)/drivedb.h: OK"; \ else \ echo "$(srcdir)/drivedb.h: Syntax check failed"; exit 1; \ fi # Windows resources @OS_WIN32_MINGW_TRUE@smartctl_res.o: smartctl_res.rc $(os_win32_manifest) @OS_WIN32_MINGW_TRUE@ $(WINDRES) $< $@ @OS_WIN32_MINGW_TRUE@smartd_res.o: smartd_res.rc syslogevt.rc $(os_win32_manifest) @OS_WIN32_MINGW_TRUE@ $(WINDRES) $< $@ @OS_WIN32_MINGW_TRUE@runcmda_res.o: runcmda_res.rc defadmin.manifest @OS_WIN32_MINGW_TRUE@ $(WINDRES) $< $@ @OS_WIN32_MINGW_TRUE@runcmdu_res.o: runcmdu_res.rc $(os_win32_manifest) @OS_WIN32_MINGW_TRUE@ $(WINDRES) $< $@ @OS_WIN32_MINGW_TRUE@wtssendmsg_res.o: wtssendmsg_res.rc $(os_win32_manifest) @OS_WIN32_MINGW_TRUE@ $(WINDRES) $< $@ @OS_WIN32_MINGW_TRUE@smartctl_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @OS_WIN32_MINGW_TRUE@ @n=smartctl d="Control and Monitor Utility for SMART Disks"; $(WIN_MAKE_RES) @OS_WIN32_MINGW_TRUE@ @$(WIN_APP_MANIFEST) @OS_WIN32_MINGW_TRUE@smartd_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @OS_WIN32_MINGW_TRUE@ @n=smartd d="SMART Disk Monitoring Daemon"; $(WIN_MAKE_RES) @OS_WIN32_MINGW_TRUE@ echo '#include "./syslogevt.rc"' >> $@ @OS_WIN32_MINGW_TRUE@ @$(WIN_APP_MANIFEST) @OS_WIN32_MINGW_TRUE@runcmdu_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @OS_WIN32_MINGW_TRUE@ @n=runcmdu d="Run console command"; $(WIN_MAKE_RES) @OS_WIN32_MINGW_TRUE@ @$(WIN_APP_MANIFEST) @OS_WIN32_MINGW_TRUE@runcmda_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @OS_WIN32_MINGW_TRUE@ @n=runcmda d="Run console command as admin"; $(WIN_MAKE_RES) @OS_WIN32_MINGW_TRUE@ echo '1 24 "./defadmin.manifest"' >> $@ @OS_WIN32_MINGW_TRUE@wtssendmsg_res.rc: os_win32/versioninfo.rc.in Makefile svnversion.h @OS_WIN32_MINGW_TRUE@ @n=wtssendmsg d="Send console messages"; $(WIN_MAKE_RES) @OS_WIN32_MINGW_TRUE@ @$(WIN_APP_MANIFEST) @OS_WIN32_MINGW_TRUE@syslogevt.rc: os_win32/syslogevt.mc @OS_WIN32_MINGW_TRUE@ $(WINDMC) -b $< @OS_WIN32_MINGW_TRUE@defadmin.manifest: os_win32/default.manifest @OS_WIN32_MINGW_TRUE@ sed 's,"asInvoker","requireAdministrator",' $< > $@ # Build Windows distribution @OS_WIN32_MINGW_TRUE@dist-win32: $(distzip_win32) @OS_WIN32_MINGW_TRUE@install-win32: $(distinst_win32) @OS_WIN32_MINGW_TRUE@ ./$(distinst_win32) @OS_WIN32_MINGW_TRUE@installer-win32: $(distinst_win32) @OS_WIN32_MINGW_TRUE@distdir-win32: distdir.mkdir $(FILES_WIN32) @OS_WIN32_MINGW_TRUE@$(distzip_win32): distdir.mkdir $(FILES_WIN32) @OS_WIN32_MINGW_TRUE@ @rm -fv $(distzip_win32) @OS_WIN32_MINGW_TRUE@ cd $(distdir_win32) && zip -9 ../$(distzip_win32) bin/* doc/* @OS_WIN32_MINGW_TRUE@ md5sum $@ > $@.md5 @OS_WIN32_MINGW_TRUE@ sha1sum $@ > $@.sha1 @OS_WIN32_MINGW_TRUE@ sha256sum $@ > $@.sha256 # Build NSIS installer # Note: Only option character '-' is also compatible with Linux version of makensis @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@$(distinst_win32): os_win32/installer.nsi smartctl_res.rc distdir.mkdir $(FILES_WIN32) @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ test -z '$(builddir_win64)' || ( cd $(builddir_win64) && make distdir-win32 ) @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h` && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h` && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ version=`sed -n 's|^ *VALUE "FileVersion", "\([0-9.]*\)".*$$|\1|p' smartctl_res.rc` && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ yy=`echo "$$date" | sed -n 's,^20\([0-9][0-9]\).*$$,\1,p'`; yy="$${yy:-XX}" && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO) && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ d64= && if [ -n '$(builddir_win64)' ]; then d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; fi && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR='$$verstr' $<" && \ @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR="$$verstr" $< @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ md5sum $@ > $@.md5 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ sha1sum $@ > $@.sha1 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ sha256sum $@ > $@.sha256 # Build drivedb.h update tool @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@update-smart-drivedb.exe: os_win32/update-smart-drivedb.nsi @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@ "$(MAKENSIS)" -V2 -NOCD -DBRANCH=$(DRIVEDB_BRANCH) $< @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_FALSE@$(distinst_win32): @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_FALSE@ @echo "makensis: command not found. Please install NSIS from http://nsis.sourceforge.net/" 1>&2 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_FALSE@ @exit 1 @OS_WIN32_MINGW_TRUE@cleandist-win32: @OS_WIN32_MINGW_TRUE@ rm -rf $(distdir_win32) distdir.mkdir @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@$(exedir_win32)/%.exe: %.exe @OS_WIN32_MINGW_TRUE@ cp -p $< $@ @OS_WIN32_MINGW_TRUE@ if test -n '$(STRIP)'; then $(STRIP) -s $@; else strip -s $@; fi @OS_WIN32_MINGW_TRUE@ touch -r $< $@ # strip would break NSIS integrity check @OS_WIN32_MINGW_TRUE@$(exedir_win32)/update-smart-drivedb.exe: update-smart-drivedb.exe @OS_WIN32_MINGW_TRUE@ cp -p $< $@ @OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.h: $(srcdir)/%.h @OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ @OS_WIN32_MINGW_TRUE@ touch -r $< $@ @OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.cmd: $(srcdir)/os_win32/%.cmd @OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ @OS_WIN32_MINGW_TRUE@ touch -r $< $@ @OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.ps1: $(srcdir)/os_win32/%.ps1 @OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ @OS_WIN32_MINGW_TRUE@ touch -r $< $@ @OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.html: %.html @OS_WIN32_MINGW_TRUE@ $(UNIX2DOS) < $< > $@ @OS_WIN32_MINGW_TRUE@ touch -r $< $@ @OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.pdf: %.pdf @OS_WIN32_MINGW_TRUE@ cp -p $< $@ @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 $< $@ @OS_WIN32_MINGW_TRUE@$(docdir_win32)/checksums$(win_bits).txt: $(EXEFILES_WIN32) @OS_WIN32_MINGW_TRUE@ (cd $(exedir_win32) && md5sum *.exe && sha1sum *.exe && sha256sum *.exe) \ @OS_WIN32_MINGW_TRUE@ | $(UNIX2DOS) > $@ # Build non-console version of smartctl for GSmartControl. # The script below changes the word at offset 220 (Subsystem) from 3 # (Console) to 2 (GUI) in a copy of smartctl.exe. # This will be changed when a tool (like 'editbin') is available in # the Cygwin distribution @OS_WIN32_MINGW_TRUE@smartctl-nc.exe: smartctl.exe @OS_WIN32_MINGW_TRUE@ @rm -f $@ @OS_WIN32_MINGW_TRUE@ cp -p smartctl.exe $@.tmp @OS_WIN32_MINGW_TRUE@ @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 3; then :; \ @OS_WIN32_MINGW_TRUE@ else echo "invalid EXE header"; exit 1; fi @OS_WIN32_MINGW_TRUE@ @echo "editbin /subsystem:windows $@.tmp" @OS_WIN32_MINGW_TRUE@ @echo -ne '\002' | dd bs=1 seek=220 count=1 conv=notrunc of=$@.tmp 2>/dev/null @OS_WIN32_MINGW_TRUE@ @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 2; then :; \ @OS_WIN32_MINGW_TRUE@ else echo "EXE patch failed"; exit 1; fi @OS_WIN32_MINGW_TRUE@ mv -f $@.tmp $@ # Build runcmd?.exe and wtssendmsg.exe @OS_WIN32_MINGW_TRUE@runcmd.o: os_win32/runcmd.c @OS_WIN32_MINGW_TRUE@ $(CC) -c -Os $< @OS_WIN32_MINGW_TRUE@runcmdu.exe: runcmd.o runcmdu_res.o @OS_WIN32_MINGW_TRUE@ $(CC) -o $@ $^ @OS_WIN32_MINGW_TRUE@runcmda.exe: runcmd.o runcmda_res.o @OS_WIN32_MINGW_TRUE@ $(CC) -o $@ $^ @OS_WIN32_MINGW_TRUE@wtssendmsg.exe: os_win32/wtssendmsg.c wtssendmsg_res.o @OS_WIN32_MINGW_TRUE@ $(CC) -Os -o $@ $^ -lwtsapi32 @OS_WIN32_MINGW_TRUE@config-vc$(vcver): $(CONFIG_VC_FILES) @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/config.h: config.h Makefile @OS_WIN32_MINGW_TRUE@ sed -e '1i/* os_win32/vc$(vcver)/config.h. Generated from config.h by Makefile. */' \ @OS_WIN32_MINGW_TRUE@ -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|LONG_DOUBLE_WIDER\|STRINGS_H\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \ @OS_WIN32_MINGW_TRUE@ -e 's,^\(#define SMARTMONTOOLS_BUILD_HOST "[^-]*\)[^"]*,\1-pc-w32vc$(vcver),' $< > $@ @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/svnversion.h: svnversion.h @OS_WIN32_MINGW_TRUE@ cp $< $@ @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/smartctl_res.rc: smartctl_res.rc @OS_WIN32_MINGW_TRUE@ sed '/^1 24 /d' $< > $@ @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/smartd_res.rc: smartd_res.rc @OS_WIN32_MINGW_TRUE@ sed '/^1 24 /d' $< > $@ @OS_WIN32_MINGW_TRUE@clean-vc$(vcver): @OS_WIN32_MINGW_TRUE@ rm -f $(srcdir)/os_win32/vc$(vcver)/smartmontools.VC.VC.opendb @OS_WIN32_MINGW_TRUE@ rm -f $(srcdir)/os_win32/vc$(vcver)/smartmontools.VC.db @OS_WIN32_MINGW_TRUE@ rm -f $(srcdir)/os_win32/vc$(vcver)/syslogevt.h @OS_WIN32_MINGW_TRUE@ rm -rf $(srcdir)/os_win32/vc$(vcver)/Debug @OS_WIN32_MINGW_TRUE@ rm -rf $(srcdir)/os_win32/vc$(vcver)/Release @OS_WIN32_MINGW_TRUE@ rm -rf $(srcdir)/os_win32/vc$(vcver)/x64 @OS_WIN32_MINGW_TRUE@distclean-vc$(vcver): clean-vc$(vcver) @OS_WIN32_MINGW_TRUE@ rm -f $(CONFIG_VC_FILES) @OS_WIN32_MINGW_TRUE@maintainer-clean-vc$(vcver): distclean-vc$(vcver) @OS_WIN32_MINGW_TRUE@ rm -rf $(srcdir)/os_win32/vc$(vcver)/.vs # build darwin installer @OS_DARWIN_TRUE@$(pkg_darwin): @OS_DARWIN_TRUE@ ${MAKE} install DESTDIR=$(distdir_darwin)/root @OS_DARWIN_TRUE@ @cp $(srcdir)/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall $(distdir_darwin)/root$(sbindir) @OS_DARWIN_TRUE@ @mkdir -p $(distdir_darwin)/pkg @OS_DARWIN_TRUE@ @( cd $(distdir_darwin)/root && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > $(distdir_darwin)/pkg/Payload @OS_DARWIN_TRUE@ PAYLOAD_FILES=`find $(distdir_darwin)/root | wc -l` &&\ @OS_DARWIN_TRUE@ PAYLOAD_SIZEKB=`du -BK -s $(distdir_darwin)/root|${AWK} '{print $$1}'|tr -d 'K'` &&\ @OS_DARWIN_TRUE@ sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" \ @OS_DARWIN_TRUE@ -e "s|@size@|$${PAYLOAD_SIZEKB}|" $(srcdir)/os_darwin/pkg/PackageInfo.in \ @OS_DARWIN_TRUE@ > $(distdir_darwin)/pkg/PackageInfo &&\ @OS_DARWIN_TRUE@ sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" -e "s|@size@|$${PAYLOAD_SIZEKB}|" \ @OS_DARWIN_TRUE@ -e "s|@pkgname@|$(pkg_darwin)|" \ @OS_DARWIN_TRUE@ $(srcdir)/os_darwin/pkg/Distribution.in > $(distdir_darwin)/pkg/Distribution @OS_DARWIN_TRUE@ @mkdir -p $(distdir_darwin)/pkg/Resources/English.lproj @OS_DARWIN_TRUE@ @cp $(srcdir)/COPYING $(distdir_darwin)/pkg/Resources/English.lproj/license.txt @OS_DARWIN_TRUE@ @mkbom -u 0 -g 80 $(distdir_darwin)/root $(distdir_darwin)/pkg/Bom @OS_DARWIN_TRUE@ @mkdir -p $(distdir_darwin)/dmg @OS_DARWIN_TRUE@ @( cd $(distdir_darwin)/pkg && xar --compression none -cf "../dmg/$(pkg_darwin)" * ) # build darwon dmg image @OS_DARWIN_TRUE@$(dmg_darwin): @OS_DARWIN_TRUE@ @cp $(srcdir)/os_darwin/pkg/installer/README.html $(distdir_darwin)/dmg @OS_DARWIN_TRUE@ @mkisofs -V 'smartmontools' -no-pad -r -apple -o $(distdir_darwin)/smartmontools-$(VERSION).iso \ @OS_DARWIN_TRUE@ -hfs-bless "$(distdir_darwin)/dmg/" "$(distdir_darwin)/dmg/" @OS_DARWIN_TRUE@ @dmg dmg $(distdir_darwin)/smartmontools-$(VERSION).iso $(dmg_darwin) @OS_DARWIN_TRUE@ md5sum $@ > $@.md5 @OS_DARWIN_TRUE@ sha1sum $@ > $@.sha1 @OS_DARWIN_TRUE@ sha256sum $@ > $@.sha256 @OS_DARWIN_TRUE@install-darwin: install-darwin-cleanup $(pkg_darwin) $(dmg_darwin) @OS_DARWIN_TRUE@install-darwin-cleanup: @OS_DARWIN_TRUE@ @rm -rf $(distdir_darwin) # 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: smartmontools-7.0/megaraid.h0000644000175000010010000001376713336613560013155 00000000000000/* * megaraid.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2008 Jordan Hargrave * * SPDX-License-Identifier: GPL-2.0-or-later */ int megaraid_io_interface(int device, int target, struct scsi_cmnd_io *, int); #undef u32 #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t #define u64 uint64_t /*====================================================== * PERC2/3/4 Passthrough SCSI Command Interface * * Contents from: * drivers/scsi/megaraid/megaraid_ioctl.h * drivers/scsi/megaraid/mbox_defs.h *======================================================*/ #define MEGAIOC_MAGIC 'm' #define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t) /* Following subopcode work for opcode == 0x82 */ #define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | adapno) #define MEGAIOC_QNADAP 'm' #define MEGAIOC_QDRVRVER 'e' #define MEGAIOC_QADAPINFO 'g' #define MEGA_MBOXCMD_PASSTHRU 0x03 #define MAX_REQ_SENSE_LEN 0x20 #define MAX_CDB_LEN 10 typedef struct { uint8_t timeout : 3; uint8_t ars : 1; uint8_t reserved : 3; uint8_t islogical : 1; uint8_t logdrv; uint8_t channel; uint8_t target; uint8_t queuetag; uint8_t queueaction; uint8_t cdb[MAX_CDB_LEN]; uint8_t cdblen; uint8_t reqsenselen; uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; uint8_t numsgelements; uint8_t scsistatus; uint32_t dataxferaddr; uint32_t dataxferlen; } __attribute__((packed)) mega_passthru; typedef struct { uint8_t cmd; uint8_t cmdid; uint8_t opcode; uint8_t subopcode; uint32_t lba; uint32_t xferaddr; uint8_t logdrv; uint8_t resvd[3]; uint8_t numstatus; uint8_t status; } __attribute__((packed)) megacmd_t; typedef union { uint8_t *pointer; uint8_t pad[8]; } ptr_t; // The above definition assumes sizeof(void*) <= 8. // This assumption also exists in the linux megaraid device driver. // So define a macro to check expected size of ptr_t 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 // you have an unexpected pointer size on your platform and can not // use megaraid support in smartmontools. typedef char assert_sizeof_ptr_t[sizeof(ptr_t) == 8 ? 1 : -1]; struct uioctl_t { uint32_t inlen; uint32_t outlen; union { uint8_t fca[16]; struct { uint8_t opcode; uint8_t subopcode; uint16_t adapno; ptr_t buffer; uint32_t length; } __attribute__((packed)) fcs; } __attribute__((packed)) ui; megacmd_t mbox; mega_passthru pthru; ptr_t data; } __attribute__((packed)); /*=================================================== * PERC5/6 Passthrough SCSI Command Interface * * Contents from: * drivers/scsi/megaraid/megaraid_sas.h *===================================================*/ #define MEGASAS_MAGIC 'M' #define MEGASAS_IOC_FIRMWARE _IOWR(MEGASAS_MAGIC, 1, struct megasas_iocpacket) #define MFI_CMD_PD_SCSI_IO 0x04 #define MFI_CMD_DCMD 0x05 #define MFI_FRAME_SGL64 0x02 #define MFI_STAT_OK 0x00 #define MFI_DCMD_PD_GET_LIST 0x02010000 /* * Number of mailbox bytes in DCMD message frame */ #define MFI_MBOX_SIZE 12 #define MAX_IOCTL_SGE 16 #define MFI_FRAME_DIR_NONE 0x0000 #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 #define MAX_SYS_PDS 240 struct megasas_sge32 { u32 phys_addr; u32 length; } __attribute__ ((packed)); struct megasas_sge64 { u64 phys_addr; u32 length; } __attribute__ ((packed)); union megasas_sgl { struct megasas_sge32 sge32[1]; struct megasas_sge64 sge64[1]; } __attribute__ ((packed)); struct megasas_header { u8 cmd; /*00h */ u8 sense_len; /*01h */ u8 cmd_status; /*02h */ u8 scsi_status; /*03h */ u8 target_id; /*04h */ u8 lun; /*05h */ u8 cdb_len; /*06h */ u8 sge_count; /*07h */ u32 context; /*08h */ u32 pad_0; /*0Ch */ u16 flags; /*10h */ u16 timeout; /*12h */ u32 data_xferlen; /*14h */ } __attribute__ ((packed)); struct megasas_pthru_frame { u8 cmd; /*00h */ u8 sense_len; /*01h */ u8 cmd_status; /*02h */ u8 scsi_status; /*03h */ u8 target_id; /*04h */ u8 lun; /*05h */ u8 cdb_len; /*06h */ u8 sge_count; /*07h */ u32 context; /*08h */ u32 pad_0; /*0Ch */ u16 flags; /*10h */ u16 timeout; /*12h */ u32 data_xfer_len; /*14h */ u32 sense_buf_phys_addr_lo; /*18h */ u32 sense_buf_phys_addr_hi; /*1Ch */ u8 cdb[16]; /*20h */ union megasas_sgl sgl; /*30h */ } __attribute__ ((packed)); struct megasas_dcmd_frame { u8 cmd; /*00h */ u8 reserved_0; /*01h */ u8 cmd_status; /*02h */ u8 reserved_1[4]; /*03h */ u8 sge_count; /*07h */ u32 context; /*08h */ u32 pad_0; /*0Ch */ u16 flags; /*10h */ u16 timeout; /*12h */ u32 data_xfer_len; /*14h */ u32 opcode; /*18h */ union { /*1Ch */ u8 b[12]; u16 s[6]; u32 w[3]; } mbox; union megasas_sgl sgl; /*28h */ } __attribute__ ((packed)); struct megasas_iocpacket { u16 host_no; u16 __pad1; u32 sgl_off; u32 sge_count; u32 sense_off; u32 sense_len; union { u8 raw[128]; struct megasas_header hdr; struct megasas_pthru_frame pthru; struct megasas_dcmd_frame dcmd; } frame; struct iovec sgl[MAX_IOCTL_SGE]; } __attribute__ ((packed)); struct megasas_pd_address { u16 device_id; u16 encl_device_id; u8 encl_index; u8 slot_number; u8 scsi_dev_type; /* 0 = disk */ u8 connect_port_bitmap; u64 sas_addr[2]; } __attribute__ ((packed)); struct megasas_pd_list { u32 size; u32 count; struct megasas_pd_address addr[MAX_SYS_PDS]; } __attribute__ ((packed)); #undef u8 #undef u16 #undef u32 #undef u64 smartmontools-7.0/missing0000755000175000010010000001533113412155343012612 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1996-2017 Free Software Foundation, Inc. # Originally written 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, see . # 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 case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: smartmontools-7.0/netbsd_nvme_ioctl.h0000644000175000010010000001333512771777571015110 00000000000000/* $NetBSD: nvmereg.h,v 1.1 2016/05/01 10:21:02 nonaka Exp $ */ /* $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */ /* * Copyright (c) 2014 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include struct nvme_sge { uint8_t id; uint8_t _reserved[15]; } __packed __aligned(8); struct nvme_sqe { uint8_t opcode; uint8_t flags; uint16_t cid; uint32_t nsid; uint8_t _reserved[8]; uint64_t mptr; union { uint64_t prp[2]; struct nvme_sge sge; } __packed entry; uint32_t cdw10; uint32_t cdw11; uint32_t cdw12; uint32_t cdw13; uint32_t cdw14; uint32_t cdw15; } __packed __aligned(8); struct nvme_cqe { uint32_t cdw0; uint32_t _reserved; uint16_t sqhd; /* SQ Head Pointer */ uint16_t sqid; /* SQ Identifier */ uint16_t cid; /* Command Identifier */ uint16_t flags; #define NVME_CQE_DNR __BIT(15) #define NVME_CQE_M __BIT(14) #define NVME_CQE_SCT(_f) ((_f) & (0x07 << 8)) #define NVME_CQE_SCT_GENERIC (0x00 << 8) #define NVME_CQE_SCT_COMMAND (0x01 << 8) #define NVME_CQE_SCT_MEDIAERR (0x02 << 8) #define NVME_CQE_SCT_VENDOR (0x07 << 8) #define NVME_CQE_SC(_f) ((_f) & (0x7f << 1)) #define NVME_CQE_SC_SUCCESS (0x00 << 1) #define NVME_CQE_SC_INVALID_OPCODE (0x01 << 1) #define NVME_CQE_SC_INVALID_FIELD (0x02 << 1) #define NVME_CQE_SC_CID_CONFLICT (0x03 << 1) #define NVME_CQE_SC_DATA_XFER_ERR (0x04 << 1) #define NVME_CQE_SC_ABRT_BY_NO_PWR (0x05 << 1) #define NVME_CQE_SC_INTERNAL_DEV_ERR (0x06 << 1) #define NVME_CQE_SC_CMD_ABRT_REQD (0x07 << 1) #define NVME_CQE_SC_CMD_ABDR_SQ_DEL (0x08 << 1) #define NVME_CQE_SC_CMD_ABDR_FUSE_ERR (0x09 << 1) #define NVME_CQE_SC_CMD_ABDR_FUSE_MISS (0x0a << 1) #define NVME_CQE_SC_INVALID_NS (0x0b << 1) #define NVME_CQE_SC_CMD_SEQ_ERR (0x0c << 1) #define NVME_CQE_SC_INVALID_LAST_SGL (0x0d << 1) #define NVME_CQE_SC_INVALID_NUM_SGL (0x0e << 1) #define NVME_CQE_SC_DATA_SGL_LEN (0x0f << 1) #define NVME_CQE_SC_MDATA_SGL_LEN (0x10 << 1) #define NVME_CQE_SC_SGL_TYPE_INVALID (0x11 << 1) #define NVME_CQE_SC_LBA_RANGE (0x80 << 1) #define NVME_CQE_SC_CAP_EXCEEDED (0x81 << 1) #define NVME_CQE_NS_NOT_RDY (0x82 << 1) #define NVME_CQE_RSV_CONFLICT (0x83 << 1) #define NVME_CQE_PHASE __BIT(0) } __packed __aligned(8); /*- * Copyright (C) 2012-2013 Intel Corporation * 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. * * $FreeBSD$ */ #define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command) #define nvme_completion_is_error(cpl) \ ((NVME_CQE_SC((cpl)->flags) != NVME_CQE_SC_SUCCESS) \ || (NVME_CQE_SCT((cpl)->flags) != NVME_CQE_SCT_GENERIC)) struct nvme_pt_command { /* * cmd is used to specify a passthrough command to a controller or * namespace. * * The following fields from cmd may be specified by the caller: * * opcode * * nsid (namespace id) - for admin commands only * * cdw10-cdw15 * * Remaining fields must be set to 0 by the caller. */ struct nvme_sqe cmd; /* * cpl returns completion status for the passthrough command * specified by cmd. * * The following fields will be filled out by the driver, for * consumption by the caller: * * cdw0 * * flags (except for phase) * * Remaining fields will be set to 0 by the driver. */ struct nvme_cqe cpl; /* buf is the data buffer associated with this passthrough command. */ void *buf; /* * len is the length of the data buffer associated with this * passthrough command. */ uint32_t len; /* * is_read = 1 if the passthrough command will read data into the * supplied buffer from the controller. * * is_read = 0 if the passthrough command will write data from the * supplied buffer to the controller. */ uint32_t is_read; /* * timeout (unit: ms) * * 0: use default timeout value */ uint32_t timeout; }; #define NVME_PREFIX "/dev/nvme" #define NVME_NS_PREFIX "ns" smartmontools-7.0/NEWS0000644000175000010010000010231713412155326011714 00000000000000smartmontools NEWS ------------------ $Id: NEWS 4883 2018-12-30 14:48:54Z chrfranke $ The most up-to-date version of this file is: https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/NEWS Date 2018-12-30 Summary: smartmontools release 7.0 ----------------------------------------------------------- - smartctl: New options '-j' and '--json[=giosu]' to enable experimental JSON output mode. Also supports an alternative flat format suitable for grep ('--json=g'). - smartctl '-l devstat': Fix for logs with 256 sectors. - smartctl '-l error': Prints SCSI Pending Defects log page if supported. - smartctl '-H': Prints SCSI Format Status log page if supported. - smartctl '-x': Now includes '-l defects'. - NVMe/USB: Device type '-d sntjmicron' for NVMe drives behind JMicron USB to NVMe bridges. - NVMe: SMART/Health Information and Error Information are always read with broadcast NSID. - SCSI: Various improvements for the decoding of log pages. - SCSI: Improved handling of READ CAPACITY command. - SCSI: Support for SAS host managed drives. - New option '-d scsi+TYPE' to disable SAT auto detection for 'aacraid' and 'cciss'. - update-smart-drivedb: New option '-u github'. - update-smart-drivedb: New signing key. - configure: New options '--with-cxx11-regex' and '--with-libsystemd'. - configure: Default for '--with-nvme-devicescan' is now 'yes' on Linux and Windows. - configure: Default for '--with-scriptpath' now includes '/usr/local/bin'. - configure: Options '-with-initscriptdir=[auto|yes]' are no longer supported. - Reworked handling of unaligned LE and BE integers. - Dropped various backward compatibility fixes for very old compilers. - Replaced GPL header with 'SPDX-License-Identifier' in all source files. - HDD, SSD and USB additions to drive database. - Always use '-d sat' for USB vendor IDs with known SAT support. - Linux: Device scan with '-d by-id' scans '/dev/disk/by-id/*' symlinks. - Linux: Dropped device scan support for devfs. - Linux: Fixed device scan with multiple '-d TYPE' options. - Linux: Fixed device scan crash on missing '/proc/devices'. - Linux: USB ID detection also for '/dev/sgN'. - Linux: Compile fix for systems with musl libc. - Linux smartd: Could now run as systemd service with 'Type=notify' set. - FreeBSD: NVMe device scanning. - FreeBSD: Various compile fixes. - NetBSD: Fixed regression in name based device type detection. - NetBSD big endian: Fixed regression in ATA code. - NetBSD big endian: Fixed byte ordering of ATA IDENTIFY data from USB/SAT drives. - Windows: Improved search for actual CSMI port number. - Windows: Installer includes VERSIONINFO resource. Date 2017-11-05 Summary: smartmontools release 6.6 ----------------------------------------------------------- - smartctl '-i' and '--identify': ATA ACS-4 and SATA 3.3 enhancements. - smartctl: Control ATA write cache through SCT Feature Control with '-s wcache-sct,ata|on|off[,p]' and '-g wcache-sct'. - smartctl: Print ATA Pending Defects log with '-l defects'. - smartctl '-s wcreorder,on|off': New persistent flag ',p'. - smartctl '-s standby': Prevent temporary drive spinup. - smartctl '-n POWERMODE': New parameter to set exit status. - smartctl '-g security': ATA Security Level check fixed. - smartctl '-l scttemp*': Print minimum supported ERC Time Limit. - smartctl '-q noserial': Now also suppresses "SAS address" output. - smartctl '-i': Print IEEE EUI-64 of NVMe namespace. - smartctl '-c': Print NVMe 1.3 feature flags. - smartctl '-A': Print NVMe 1.3 thermal temperature transition statistic. - smartctl '-g/s dsn': Get/set ATA DSN. - smartd: Uses also device identify information to detect for duplicate devices. - smartd '-e dsn' directive: Set ATA DSN. - smartd: Improved SCSI/SAS temperature logging. - smartd: Silence emails and log messages on open errors of '-d removable' devices. - smartd: Exit on device open error unless '-q never' or '-d removable' is specified (regression). - update-smart-drivedb: Now authenticates downloaded file with GnuPG. - update-smart-drivedb: New options '--trunk', '--no-verify' and '--export-key'. - Device type '-d intelliprop,N' for IntelliProp controllers. - SCSI: Default timeout increased to 1 minute. - configure: New options '--with-gnupg', '--with-scriptpath' and '--with-update-smart-drivedb=X.Y' - configure: Checks for C++11 support option and requires '--with[out]-cxx11-option' if option unknown or no C++11 support. - HDD, SSD and USB additions to drive database. - New smartmontools-* mailing list addresses. - Man page formatting reworked. - Linux: Uses SG_IO V4 API if supported. - Linux: Devices behind hpsa driver are no longer detected as regular SCSI devices. - Darwin: Initial NVMe support based on undocumented API. - FreeBSD: Fix panic on INVARIANTS enabled kernel. - FreeBSD: Improve ATA SMART STATUS check for legacy controllers. - FreeBSD: Compile fix for FreeBSD-11 and newer. - NetBSD: NVMe support. - NetBSD: Full 28-bit ATA support. - NetBSD: Compile fix. - NetBSD: Use a raw disk device file. - OpenBSD: Compile fix. - OS/2: Support for the OS2AHCI driver, updating source code, adding autoscan support, adding self-test support. - Windows: Support for Windows 10 NVMe driver (stornvme.sys). - Windows: Fix CSMI access for IRST driver 15.2. - Windows smartd: Ability to run PowerShell scripts with '-M exec'. - Windows smartd: New PowerShell script to send smartd warning emails without external tools. - Windows package: Now provides PDF man pages. Date 2016-05-07 Summary: smartmontools release 6.5 ----------------------------------------------------------- - Experimental support for NVMe devices on FreeBSD, Linux and Windows. - smartctl '-i', '-c', '-H' and '-l error': NVMe support. - smartctl '-l nvmelog': New option for NVMe. - smartd.conf '-H', '-l error' and '-W': NVMe support. - Optional NVMe device scanning support on Linux and Windows. - configure option '--with-nvme-devicescan' to include NVMe in default device scanning result. - Device scanning now allows to specify multiple '-d TYPE' options. - ATA: Added new POWER MODE values introduced in ATA ACS-2. - ATA: SCT commands are no longer issued if ATA Security is locked. - SCSI: LB provisioning improvements. - SCSI: Fixed GLTSD bit set/cleared info messages. - SCSI: Solid State media log page is no longer checked for tapes. - SCSI: Improved handling when no tape cartridge in drive. - SCSI: Workaround for buggy Seagate firmware. - SAT: Improved heuristics to detect bogus sense data from SAT layer. - smartd: Fixed crash on missing argument to '-s' directive. - update-smart-drivedb: Now uses HTTPS for download by default. - update-smart-drivedb: New options to select URL and download tool. - update-smart-drivedb: New download tool 'svn'. - configure option '--without-update-smart-drivedb' to disable update-smart-drivedb script. - configure options '--disable-drivedb', '--enable-savestates', '--enable-attributelog' and '--with-docdir' are no longer supported. - autoconf < 2.60 and automake < 1.10 are no longer supported. - Drive database file now also includes the DEFAULT setting for each attribute. - HDD, SSD and USB additions to drive database. - Darwin: New support files for package installer. New makefile target 'install-darwin' builds DMG image. - Solaris: Auto detection of SATA devices behind SAT layer. - Solaris SPARC: Legacy ATA support disabled by default. New configure option '--with-solaris-sparc-ata' enables it. File os_solaris_ata.s is no longer included in source tarball. - Windows: Auto detection of USB devices specified by drive letter. - Windows: Device scanning does no longer ignore unknown USB devices. - Windows: Prevent drive spin up by '-n standby' check. - Windows: New application manifests indicating Win 10 support. - Windows smartd: '-m [sys]msgbox' is no longer supported. - Windows installer: Defaults to 64-bit version on 64-bit Windows. - Various code changes suggested by Clang Static Analyser and Cppcheck. Date 2015-06-04 Summary: smartmontools release 6.4 ----------------------------------------------------------- - Device type '-d usbprolific' for Prolific PL2571/277x USB bridges. - SAT: Support for ATA registers returned in fixed format sense data. - smartctl '-i' and '--identify': ATA ACS-4 and SATA 3.2 enhancements. - smartctl '-l xerror': Support for logs with more than 255 pages. - smartctl '-l devstat': Prints ACS-3 DSN flags. - smartctl '-l devstat': Read via SMART command if GP log is not available. - smartctl '-l scttempsts': Prints SCT SMART STATUS (ACS-4) and vendor specific SCT bytes. - configure option '--with-systemdenvfile=auto' as new default. - configure options '--disable-drivedb', '--enable-savestates' and '--enable-attributelog' are deprecated. - Corresponding '--with-*' options are enhanced accordingly. - Configure option '--with-docdir' is deprecated. - autoconf < 2.60 and automake < 1.10 are deprecated. (all of the above still work but a warning is printed if used) - HDD, SSD and USB additions to drive database. - Linux: AACRAID fixes, SMART STATUS should work now. - Linux: '/dev/megaraid_sas_ioctl_node' fd leak fix. - Darwin: '-S' command implemented, '-l devstat' should work now. - Cygwin: Compile fix. - Windows: Device type '-d aacraid' for AACRAID controllers. - Windows: SAT autodetection based on IOCTL_STORAGE_QUERY_PROPERTY. - Windows installer: Fix possible loss of user PATH environment variable. Date 2014-07-26 Summary: smartmontools release 6.3 ----------------------------------------------------------- - smartctl: Fixed bogus error messages from '-g/-s wcreorder'. - smartctl prints ATA form factor. - SCSI: Improved support of modern disks (SAS SSDs). - SCSI: Fixed sense data noise from old disks. - update-smart-drivedb man page. - configure option '--with-smartdscriptdir'. - configure option '--with-smartdplugindir'. - configure option '--with-systemdenvfile'. - configure option '--with-working-snprintf'. - Removed build time stamps to support reproducible builds. - Compile fixes for C++11. - HDD, SSD and USB additions to drive database. - Linux: Support for controllers behind AACRAID driver. - Linux: Fixed DEVICESCAN max path count. - FreeBSD: Fixed possible crash caused by wrong SCSI error handling. - FreeBSD: Compile fix for kFreeBSD. - Windows: Reworked CSMI port scanning. - QNX: Compile fix. Date 2013-07-26 Summary: smartmontools release 6.2 ----------------------------------------------------------- - smartctl: Added ATA write cache reordering control using '-g wcreorder' and '-s wcreorder,[on|off]' options. - update-smart-drivedb: Updated for new SVN repository. - HDD, SSD and USB additions to drive database. - Areca RAID support: Fixed possible segfault on empty port. - HPT RAID support: Maximum disk number increased to 128. - Linux: RHEL 9 compile fixes. - FreeBSD: Device scanning now skips port multipliers. - Cygwin: 64-bit compile fixes. - Windows: Fixed bogus temp file name in smartd_warning.cmd (already included in smartmontools-6.1-2.win32-setup.exe). - Windows: smartd service is no longer installed as interactive. Date 2013-03-16 Summary: smartmontools release 6.1 ----------------------------------------------------------- - smartctl '-l directory': improved output format. - smartctl: Fix parsing of '-l select,cont+SIZE' option. - smartctl prints ATA Additional Product Id (OEM Id). - smartctl '-s/-g wcache' for SCSI devices to control write cache. - smartctl '-s/-g rcache' for SCSI devices to control read cache. - smartctl prints more info for SCSI devices: media rotation rate, form factor, physical block size, lowest LBA alignement, logical block provisioning, disk protection type and selftest progress status. - smartctl '--identify' updated for latest ATA ACS-3 spec. - smartd runs /etc/smartd_warning.sh to generate warning emails (Windows: smartd_warning.cmd). - smartd '-w PATH' option to specify this executable. - smartd '-d ignore' directive. - smartd DEVICESCAN ignores devices already specified. - smartd: added support for state persistence ('-s') and attribute logging ('-A') for SCSI devices. - smartd '-W' directive uses ATA attribute 190 if 194 is missing. - Support of larger SCSI defect lists via READ DEFECT(12). - Device type '-d usbjmicron,p' for Prolific USB bridges. - Many HDD, SSD and USB additions to drive database. - Linux: Support for SAS disks behind Areca controllers. - Linux: Improved support for SATA disks on LSI/Megaraid controllers - Linux: disks on MegaRaid controllers are automatically scanned - FreeBSD: Support for SAS disks behind Areca controllers. - FreeBSD: Enhanced ATA command support for 3ware. - FreeBSD: Support for 3ware 9750 (/dev/twsX). - FreeBSD: Fixed support for 48-bit ATA commands on legacy controllers with ATACAM driver. - FreeBSD: Improved support for SAS/SCSI disks on LSI/Megaraid controllers. - Windows: smartd.conf '-M exec' supports path names with spaces. - Windows: Tool wtssendmsg.exe to handle smartd.conf '-m console'. - Windows: DEVICESCAN now supports up to 128 drives. - Windows: smartctl.exe and smartd.exe include VERSIONINFO resource. - Windows: smartd.exe includes MESSAGETABLE resource. - Windows: syslogevt.exe is no longer provided. Date 2012-10-10 Summary: smartmontools release 6.0 ----------------------------------------------------------- - option/directive '-F nologdir' and '-F xerrorlba'. - smartctl '--identify' option. - smartctl prints nominal media rotation rate (ATA). - smartctl prints SATA version and speed. - smartctl '-l sataphy' works for CD/DVD drives also. - smartctl '-x' includes ATA Device Statistics. - smartd warning emails include device identify info. - smartd '-d' output is flushed to support redirection. - HDD, SSD and USB additions to drive database. - Windows smartd: smartd.conf directives '-m console', '-m active', '-m connected'. - Windows: Support for SAS disks behind Areca controllers. - Windows: Win9x/ME and NT4 are no longer supported. Date 2012-06-30 Summary: smartmontools release 5.43 ----------------------------------------------------------- - smartctl options '-g, --get' and '-s, --set' to get/set various ATA settings: AAM, APM, Read look-ahead, Write cache, Security (freeze), Standby mode/timer. - smartd directive '-e' to set (most of) the above settings on startup. - smartctl options '-f hex' and '-f hex,[id|val]'. - smartctl does not start ATA self-test if another test is already running. Override with new option '-t force'. - smartctl supports extended self-test polling times greater than 255 minutes. - Controller-independent SAT detection: '-d sat,auto[+TYPE]'. - smartd.conf DEFAULT directive. - Many HDD, SSD and USB additions to drive database. - Linux and FreeBSD: Support for SATA disks behind Areca SAS RAID controllers and HP Smart Array controllers. - Windows: Support for SATA disks behind Areca controllers. - Windows smartd: directives '-l offlinests,ns' and '-l selfteststs,ns'. - Windows installer: Combined 32-/64-bit support. - FreeBSD: fixed crash on SCSI devices with FreeBSD9-RC1 Date 2011-10-20 Summary: smartmontools release 5.42 ----------------------------------------------------------- - smartctl option '-l devstat' (Device Statistics). - smartctl option '-l ssd' (SSD endurance indicator). - smartd logs identify information of each SCSI/SAS device. - smartd resends warning emails if problem reappears. - smartd directives '-l offlinests' and '-l selfteststs'. - Many HDD, SSD and USB additions to drive database. - Platform-specific man pages. - smartd.8 man page no longer includes smartd.conf.5. - FreeBSD: Compilation fixes. - FreeBSD: Support for Areca controllers. - FreeBSD: Fix '-l scterc' support. - FreeBSD: Support for 48-bit ATA commands. - Linux: Support for Areca controllers enhanced. - Windows installer: UAC support. - Windows: update-smart-drivedb /S(ilent) option. - Windows: improved USB ID detection. Date 2011-06-09 Summary: smartmontools release 5.41 ----------------------------------------------------------- - Failed self-tests outdated by a newer successful extended self-test are no longer reported as errors. - Support for ATA Long Logical/Physical Sectors (LLS/LPS). - 'smartctl --scan-open' can create a draft smartd.conf. - smartctl prints World Wide Name (WWN) of ATA device. - smartctl option '-f brief' to select new attribute output format which includes more flags and fits in 80 columns. - smartd logs identify information and WWN of each ATA device. - smartd logs warning from drive database if present. - smartd logs changes of offline data collection status. - smartd directive '-l scterc,READTIME,WRITETIME'. - smartd preserves last scheduled selective self-tests span. - 'smartd.service' file for systemd. - configure option '--with-systemdsystemunitdir' - configure option '--with-exampledir'. - configure searches for init.d or rc.d directory. - 'make install' does no longer overwrite an existing smartd.conf file. - 'update-smart-drivedb' does no longer require GNU sed. - Many HDD, SSD and USB additions to drive database. - Linux USB autodetection: Enable '-d sat,16' for newer kernels. - Linux megaraid: Fix segfault on non-data SCSI commands. - Linux megaraid: Fix pass-through of non-data ATA commands. - FreeBSD: Use 'fetch' in 'update-smart-drivedb'. - OpenBSD: Use 'ftp' in 'update-smart-drivedb'. - OpenBSD: Workaround for shell bug. - OpenBSD: Fix DEVICESCAN for OpenBSD >= 4.8. - Windows: Experimental support for Intel ICHxR RAID. - Windows: DEVICESCAN includes USB devices. - Windows: Faster USB ID detection. - Windows: update-smart-drivedb tool. - Windows installer: Option '/SO component,...'. - Windows: Fix smartd warning email truncation on Win7. - Windows installer: Fix shortcut removal on Vista/Win7. - Windows: Add missing quotes in smartctl-run.bat and smartd-run.bat Date 2010-10-16 Summary: smartmontools release 5.40 ----------------------------------------------------------- - Other config entries may precede smartd DEVICESCAN. - Option '-v' allows to specify byte order of attribute raw value - configure: New default value for '--with-docdir'. - configure: '--enable-drivedb' is now the default. - Improved support for Intel SSDs. - Improved support for SandForce based SSDs. - Drive database is in a separate source file 'drivedb.h' which can be downloaded from SVN. - USB ID info is now included in 'drivedb.h'. - Many additions to drive database. - New script 'update-smart-drivedb'. - smartd libcap-ng support, option '-C, --capabilities'. - smartd directive '-l xerror' to check Extended Comprehensive SMART Error Log. - smartctl option '-l scterc[,...]' to get/set the SCT Error Recovery Control time limit. - smartctl option '-t vendor,N'. - smartctl options '--scan, --scan-open'. - Linux: Add '/dev/sd[a-c][a-z]' to smartd DEVICESCAN. - Linux: Support SATA drives on LSI 3ware 9750 controllers. - Windows: Read 'drivedb.h' and 'smartd.conf' from exe directory. - Windows: Support for 64-bit executables. - Windows: Support for cross compilation on Linux. - Fix regression in smartctl option '-t select,M-N'. - Fix SCT temperature table commands on big endian CPUs. - Fix regression in smartd SMARTD_DEVICE and SMARTD_DEVICETYPE environment variables. Date 2010-01-28 Summary: smartmontools release 5.39.1 ----------------------------------------------------------- - Fix crash on kFreeBSD. - Fix regression in smartctl option '-q, --quietmode'. - Fix regression in smartd directive '-l selftest'. - Linux: Allow smartd 'DEVICESCAN -d sat'. - Linux: Fix spin-up of SATA drive if '-n standby' is used. - Windows: Fix parsing of 3ware 'tw_cli' output. Date 2009-12-09 Summary: smartmontools release 5.39 (UNSTABLE/EXPERIMENTAL) ----------------------------------------------------------- - Sourcecode repository moved from CVS to SVN - Support for USB devices with Cypress, JMicron and Sunplus USB bridges - USB device type autodetection for some devices on Linux, Windows and FreeBSD (http://www.smartmontools.org/wiki/Supported_USB-Devices) - Support for Areca controllers on Linux - Support for MegaRAID controllers on Linux - Support for HighPoint RocketRAID controllers on FreeBSD - Support RAID controllers using /dev/pass devices on FreeBSD - Support CHECK_POWER_MODE and WRITE_LOG on FreeBSD - Support for up to 128 devices on 3ware controllers - smartctl option '-l xerror' to print ATA SMART Extended Comprehensive Error Log - smartctl option '-l xselftest' to print ATA SMART Extended Self-test Log - smartctl option '-l sataphy' to print SATA Phy Event Counters - smartctl option '-l sasphy' to print SAS device phy information - smartctl options '-l gplog,...' and '-l smartlog,...' to print any log page - smartctl option '-x' to print all extended info if available - smartctl prints SCSI load/unload cycle counts - Improve display of huge raw values of some SMART attributes - Option '-d sat+TYPE' to use SAT with controllers which require '-d TYPE' - Option '-v ID,RAW_FORMAT,ATTR_NAME' to add new vendor specific attributes - Support for SSD drives using 64-bit raw attributes - Many additions to drive database - New simplified syntax for drive database - Option '-B FILE' to read drive database from a file - Configure option to add drive database file to distribution - smartd can now handle attributes 197 and 198 with increasing raw values - smartd logs changes of self-test execution status - smartd directive '-n powermode,N' to limit the number of skipped checks - smartd flag '!' for '-r' and '-R' directives to log changes as critical - smartd supports scheduled Selective Self-Tests - Self-tests scheduled during system downtime or disk standby are run after next startup - smartd option '-s PREFIX' to store smartd internal state until next startup - smartd option '-A PREFIX' to log attributes at each check cycle - Configure options to enable the above by default - Change to an object oriented interface to access ATA and SCSI devices - Linux, Win32 and FreeBSD modules migrated to new interface - Rework of smartd data structures - Checkin date and SVN revision and optional BUILD_INFO printed in version info - Better support for GSmartControl on Windows - SELinux fixes to 3ware device node creation - Fix CCISS file descriptor leak on FreeBSD - Compile fixes for Solaris and FreeBSD - Use getaddrinfo() instead of gethostbyname() to support IPv6 - C++ Support for QNX Target, already tested for QNX 6.3.2 on x86 and armle target - Additional support for Samsung MLC flash drives - New device type detection algorithm on FreeBSD, not based on filename - Support for the adaX (ATA on CAM bus) devices on FreeBSD 8.0 and higher Date 2008-03-10 Summary: smartmontools release 5.38 (STABLE) -------------------------------------------- This is a stable release of smartmontools. In addition to changes below, it includes: - Libata/Marvell driver devices no longer need explicit '-d' switch - DEVICESCAN automatically detects libata/marvell driver SATA devices - Fixed auto-offline/autosave support in FreeBSD - SAT device type + SCSI generic devices work properly with smartd under Linux - Many additions to drive database - More portable autogen/autoconf/automake script set - Additional Windows IOCTL route to access SMART data - Some ATA-8 updates - Smoother CCISS compilation support in Linux - Dragonfly support - Fixed some ATA identity byte swap problems on big endian CPUs - Added support for the QNX operating system - No-fork option added to smartd - Improved device scanning and drive type recognition in Windows - 3ware support for controllers with more disks (32 max) - Improved Windows installer - Improved SMART Attribute list and descriptions - Fix to smartctl return codes - Fix to scheduled tests on Highpoint RAID controllers - New samsung firmware bug workaround option - Auto-offline and Auto-save fixed in Linux + libata - Solaris: better SCSI support and support for Sun compilers AND gcc - Fixed and improvements to CCISS support - More options for SCSI temperature monitoring and history - Additional command line options for selective self-tests - Compilation fixes for various platforms. See CHANGELOG for more details, or smartmontools SVN for still further details. Date 2006-12-20 Summary: smartmontools release 5.37 (UNSTABLE/EXPERIMENTAL) ----------------------------------------------------------- This is an unstable/experimental release of smartmontools. It includes: - Many additions to the table of known drives - SAT (SCSI/ATA Translation) support - SCSI background scan results log - smartd -W directive for temperature tracking and warning - smartctl -n option to check power state - improved smartd power state logging - CCISS support under Linux - HighPoint RocketRAID support under Linux - 3ware RAID support under Windows - SPT interface for SCSI devices under Windows - ATA selective self test under Windows XP/2003 - NSIS installer support for Windows version - Started move from C to C++ - Various other improvements 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://smartmontools.cvs.sourceforge.net/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. smartmontools-7.0/nvmecmds.cpp0000644000175000010010000001502213336335341013533 00000000000000/* * nvmecmds.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "nvmecmds.h" const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 4760 2018-08-19 18:45:53Z chrfranke $" NVMECMDS_H_CVSID; #include "dev_interface.h" #include "atacmds.h" // swapx(), ASSERT_*(), dont_print_serial_number #include "scsicmds.h" // dStrHex() #include "utility.h" using namespace smartmontools; // Check nvme_* struct sizes ASSERT_SIZEOF_STRUCT(nvme_id_ctrl, 4096); ASSERT_SIZEOF_STRUCT(nvme_id_ns, 4096); ASSERT_SIZEOF_STRUCT(nvme_error_log_page, 64); ASSERT_SIZEOF_STRUCT(nvme_smart_log, 512); // Print NVMe debug messages? unsigned char nvme_debugmode = 0; // Dump up to 4096 bytes, do not dump trailing zero bytes. // TODO: Handle this by new unified function in utility.cpp static void debug_hex_dump(const void * data, unsigned size) { const unsigned char * p = (const unsigned char *)data; const unsigned limit = 4096; // sizeof(nvme_id_ctrl) unsigned sz = (size <= limit ? size : limit); while (sz > 0x10 && !p[sz-1]) sz--; if (sz < size) { if (sz & 0x0f) sz = (sz & ~0x0f) + 0x10; sz += 0x10; if (sz > size) sz = size; } dStrHex((const uint8_t *)p, sz, 0); if (sz < size) pout(" ...\n"); } // Call NVMe pass-through and print debug info if requested. static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in, nvme_cmd_out & out) { int64_t start_usec = -1; if (nvme_debugmode) { pout(" [NVMe call: opcode=0x%02x, size=0x%04x, nsid=0x%08x, cdw10=0x%08x", in.opcode, in.size, in.nsid, in.cdw10); if (in.cdw11 || in.cdw12 || in.cdw13 || in.cdw14 || in.cdw15) pout(",\n cdw1x=0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x", in.cdw11, in.cdw12, in.cdw13, in.cdw14, in.cdw15); pout("]\n"); start_usec = smi()->get_timer_usec(); } bool ok = device->nvme_pass_through(in, out); if ( dont_print_serial_number && ok && in.opcode == nvme_admin_identify && in.cdw10 == 0x01) { // Invalidate serial number nvme_id_ctrl & id_ctrl = *reinterpret_cast(in.buffer); memset(id_ctrl.sn, 'X', sizeof(id_ctrl.sn)); } if (nvme_debugmode) { if (start_usec >= 0) { int64_t duration_usec = smi()->get_timer_usec() - start_usec; if (duration_usec >= 500) pout(" [Duration: %.3fs]\n", duration_usec / 1000000.0); } if (!ok) { pout(" [NVMe call failed: "); if (out.status_valid) pout("NVMe Status=0x%04x", out.status); else pout("%s", device->get_errmsg()); } else { pout(" [NVMe call succeeded: result=0x%08x", out.result); if (nvme_debugmode > 1 && in.direction() == nvme_cmd_in::data_in) { pout("\n"); debug_hex_dump(in.buffer, in.size); pout(" "); } } pout("]\n"); } return ok; } // Call NVMe pass-through and print debug info if requested. // Version without output parameters. static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in) { nvme_cmd_out out; return nvme_pass_through(device, in, out); } // Read NVMe identify info with controller/namespace field CNS. static bool nvme_read_identify(nvme_device * device, unsigned nsid, unsigned char cns, void * data, unsigned size) { memset(data, 0, size); nvme_cmd_in in; in.set_data_in(nvme_admin_identify, data, size); in.nsid = nsid; in.cdw10 = cns; return nvme_pass_through(device, in); } // Read NVMe Identify Controller data structure. bool nvme_read_id_ctrl(nvme_device * device, nvme_id_ctrl & id_ctrl) { if (!nvme_read_identify(device, 0, 0x01, &id_ctrl, sizeof(id_ctrl))) return false; if (isbigendian()) { swapx(&id_ctrl.vid); swapx(&id_ctrl.ssvid); swapx(&id_ctrl.cntlid); swapx(&id_ctrl.oacs); swapx(&id_ctrl.wctemp); swapx(&id_ctrl.cctemp); swapx(&id_ctrl.mtfa); swapx(&id_ctrl.hmpre); swapx(&id_ctrl.hmmin); swapx(&id_ctrl.rpmbs); swapx(&id_ctrl.nn); swapx(&id_ctrl.oncs); swapx(&id_ctrl.fuses); swapx(&id_ctrl.awun); swapx(&id_ctrl.awupf); swapx(&id_ctrl.acwu); swapx(&id_ctrl.sgls); for (int i = 0; i < 32; i++) { swapx(&id_ctrl.psd[i].max_power); swapx(&id_ctrl.psd[i].entry_lat); swapx(&id_ctrl.psd[i].exit_lat); swapx(&id_ctrl.psd[i].idle_power); swapx(&id_ctrl.psd[i].active_power); } } return true; } // Read NVMe Identify Namespace data structure for namespace NSID. bool nvme_read_id_ns(nvme_device * device, unsigned nsid, nvme_id_ns & id_ns) { if (!nvme_read_identify(device, nsid, 0x00, &id_ns, sizeof(id_ns))) return false; if (isbigendian()) { swapx(&id_ns.nsze); swapx(&id_ns.ncap); swapx(&id_ns.nuse); swapx(&id_ns.nawun); swapx(&id_ns.nawupf); swapx(&id_ns.nacwu); swapx(&id_ns.nabsn); swapx(&id_ns.nabo); swapx(&id_ns.nabspf); for (int i = 0; i < 16; i++) swapx(&id_ns.lbaf[i].ms); } return true; } // Read NVMe log page with identifier LID. bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size, bool broadcast_nsid) { if (!(4 <= size && size <= 0x4000 && (size % 4) == 0)) throw std::logic_error("nvme_read_log_page(): invalid size"); memset(data, 0, size); nvme_cmd_in in; in.set_data_in(nvme_admin_get_log_page, data, size); in.nsid = broadcast_nsid ? 0xffffffff : device->get_nsid(); in.cdw10 = lid | (((size / 4) - 1) << 16); return nvme_pass_through(device, in); } // Read NVMe Error Information Log. bool nvme_read_error_log(nvme_device * device, nvme_error_log_page * error_log, unsigned num_entries) { if (!nvme_read_log_page(device, 0x01, error_log, num_entries * sizeof(*error_log), true)) return false; if (isbigendian()) { for (unsigned i = 0; i < num_entries; i++) { swapx(&error_log[i].error_count); swapx(&error_log[i].sqid); swapx(&error_log[i].cmdid); swapx(&error_log[i].status_field); swapx(&error_log[i].parm_error_location); swapx(&error_log[i].lba); swapx(&error_log[i].nsid); } } return true; } // Read NVMe SMART/Health Information log. bool nvme_read_smart_log(nvme_device * device, nvme_smart_log & smart_log) { if (!nvme_read_log_page(device, 0x02, &smart_log, sizeof(smart_log), true)) return false; if (isbigendian()) { swapx(&smart_log.warning_temp_time); swapx(&smart_log.critical_comp_time); for (int i = 0; i < 8; i++) swapx(&smart_log.temp_sensor[i]); } return true; } smartmontools-7.0/nvmecmds.h0000644000175000010010000001505213401001476013173 00000000000000/* * nvmecmds.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016-18 Christian Franke * * Original code from : * Copyright (C) 2011-2014 Intel Corporation * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef NVMECMDS_H #define NVMECMDS_H #define NVMECMDS_H_CVSID "$Id: nvmecmds.h 4842 2018-12-02 16:07:26Z chrfranke $" #include // The code below was originally imported from include file from // Linux kernel sources. Types from were replaced. // Symbol names are unchanged but placed in a namespace to allow inclusion // of the original . namespace smartmontools { //////////////////////////////////////////////////////////////////////////// // BEGIN: From struct nvme_error_log_page { uint64_t error_count; unsigned short sqid; unsigned short cmdid; unsigned short status_field; unsigned short parm_error_location; uint64_t lba; unsigned int nsid; unsigned char vs; unsigned char resv[35]; }; struct nvme_id_power_state { unsigned short max_power; // centiwatts unsigned char rsvd2; unsigned char flags; unsigned int entry_lat; // microseconds unsigned int exit_lat; // microseconds unsigned char read_tput; unsigned char read_lat; unsigned char write_tput; unsigned char write_lat; unsigned short idle_power; unsigned char idle_scale; unsigned char rsvd19; unsigned short active_power; unsigned char active_work_scale; unsigned char rsvd23[9]; }; struct nvme_id_ctrl { unsigned short vid; unsigned short ssvid; char sn[20]; char mn[40]; char fr[8]; unsigned char rab; unsigned char ieee[3]; unsigned char cmic; unsigned char mdts; unsigned short cntlid; unsigned int ver; unsigned int rtd3r; unsigned int rtd3e; unsigned int oaes; unsigned int ctratt; unsigned char rsvd100[156]; unsigned short oacs; unsigned char acl; unsigned char aerl; unsigned char frmw; unsigned char lpa; unsigned char elpe; unsigned char npss; unsigned char avscc; unsigned char apsta; unsigned short wctemp; unsigned short cctemp; unsigned short mtfa; unsigned int hmpre; unsigned int hmmin; unsigned char tnvmcap[16]; unsigned char unvmcap[16]; unsigned int rpmbs; unsigned short edstt; unsigned char dsto; unsigned char fwug; unsigned short kas; unsigned short hctma; unsigned short mntmt; unsigned short mxtmt; unsigned int sanicap; unsigned char rsvd332[180]; unsigned char sqes; unsigned char cqes; unsigned short maxcmd; unsigned int nn; unsigned short oncs; unsigned short fuses; unsigned char fna; unsigned char vwc; unsigned short awun; unsigned short awupf; unsigned char nvscc; unsigned char rsvd531; unsigned short acwu; unsigned char rsvd534[2]; unsigned int sgls; unsigned char rsvd540[228]; char subnqn[256]; unsigned char rsvd1024[768]; unsigned int ioccsz; unsigned int iorcsz; unsigned short icdoff; unsigned char ctrattr; unsigned char msdbd; unsigned char rsvd1804[244]; struct nvme_id_power_state psd[32]; unsigned char vs[1024]; }; struct nvme_lbaf { unsigned short ms; unsigned char ds; unsigned char rp; }; struct nvme_id_ns { uint64_t nsze; uint64_t ncap; uint64_t nuse; unsigned char nsfeat; unsigned char nlbaf; unsigned char flbas; unsigned char mc; unsigned char dpc; unsigned char dps; unsigned char nmic; unsigned char rescap; unsigned char fpi; unsigned char rsvd33; unsigned short nawun; unsigned short nawupf; unsigned short nacwu; unsigned short nabsn; unsigned short nabo; unsigned short nabspf; unsigned char rsvd46[2]; unsigned char nvmcap[16]; unsigned char rsvd64[40]; unsigned char nguid[16]; unsigned char eui64[8]; struct nvme_lbaf lbaf[16]; unsigned char rsvd192[192]; unsigned char vs[3712]; }; struct nvme_smart_log { unsigned char critical_warning; unsigned char temperature[2]; unsigned char avail_spare; unsigned char spare_thresh; unsigned char percent_used; unsigned char rsvd6[26]; unsigned char data_units_read[16]; unsigned char data_units_written[16]; unsigned char host_reads[16]; unsigned char host_writes[16]; unsigned char ctrl_busy_time[16]; unsigned char power_cycles[16]; unsigned char power_on_hours[16]; unsigned char unsafe_shutdowns[16]; unsigned char media_errors[16]; unsigned char num_err_log_entries[16]; unsigned int warning_temp_time; unsigned int critical_comp_time; unsigned short temp_sensor[8]; unsigned int thm_temp1_trans_count; unsigned int thm_temp2_trans_count; unsigned int thm_temp1_total_time; unsigned int thm_temp2_total_time; unsigned char rsvd232[280]; }; enum nvme_admin_opcode { //nvme_admin_delete_sq = 0x00, //nvme_admin_create_sq = 0x01, nvme_admin_get_log_page = 0x02, //nvme_admin_delete_cq = 0x04, //nvme_admin_create_cq = 0x05, nvme_admin_identify = 0x06, //nvme_admin_abort_cmd = 0x08, //nvme_admin_set_features = 0x09, //nvme_admin_get_features = 0x0a, //nvme_admin_async_event = 0x0c, //nvme_admin_ns_mgmt = 0x0d, //nvme_admin_activate_fw = 0x10, //nvme_admin_download_fw = 0x11, //nvme_admin_ns_attach = 0x15, //nvme_admin_format_nvm = 0x80, //nvme_admin_security_send = 0x81, //nvme_admin_security_recv = 0x82, }; // END: From //////////////////////////////////////////////////////////////////////////// } // namespace smartmontools class nvme_device; // Print NVMe debug messages? extern unsigned char nvme_debugmode; // Read NVMe Identify Controller data structure. bool nvme_read_id_ctrl(nvme_device * device, smartmontools::nvme_id_ctrl & id_ctrl); // Read NVMe Identify Namespace data structure for namespace NSID. bool nvme_read_id_ns(nvme_device * device, unsigned nsid, smartmontools::nvme_id_ns & id_ns); // Read NVMe log page with identifier LID. bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size, bool broadcast_nsid); // Read NVMe Error Information Log. bool nvme_read_error_log(nvme_device * device, smartmontools::nvme_error_log_page * error_log, unsigned num_entries); // Read NVMe SMART/Health Information log. bool nvme_read_smart_log(nvme_device * device, smartmontools::nvme_smart_log & smart_log); #endif // NVMECMDS_H smartmontools-7.0/nvmeprint.cpp0000644000175000010010000005277713405512350013755 00000000000000/* * nvmeprint.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include "nvmeprint.h" const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4859 2018-12-16 18:09:44Z chrfranke $" NVMEPRINT_H_CVSID; #include "utility.h" #include "dev_interface.h" #include "nvmecmds.h" #include "atacmds.h" // dont_print_serial_number #include "scsicmds.h" // dStrHex() #include "smartctl.h" #include "sg_unaligned.h" #include using namespace smartmontools; // Return true if 128 bit LE integer is != 0. static bool le128_is_non_zero(const unsigned char (& val)[16]) { for (int i = 0; i < 16; i++) { if (val[i]) return true; } return false; } // Format 128 bit integer for printing. // Add value with SI prefixes if BYTES_PER_UNIT is specified. static const char * le128_to_str(char (& str)[64], uint64_t hi, uint64_t lo, unsigned bytes_per_unit) { if (!hi) { // Up to 64-bit, print exact value format_with_thousands_sep(str, sizeof(str)-16, lo); if (lo && bytes_per_unit && lo < 0xffffffffffffffffULL / bytes_per_unit) { int i = strlen(str); str[i++] = ' '; str[i++] = '['; format_capacity(str+i, (int)sizeof(str)-i-1, lo * bytes_per_unit); i = strlen(str); str[i++] = ']'; str[i] = 0; } } else { // More than 64-bit, prepend '~' flag on low precision int i = 0; if (uint128_to_str_precision_bits() < 128) str[i++] = '~'; uint128_hilo_to_str(str + i, (int)sizeof(str) - i, hi, lo); } return str; } // Format 128 bit LE integer for printing. // Add value with SI prefixes if BYTES_PER_UNIT is specified. static const char * le128_to_str(char (& str)[64], const unsigned char (& val)[16], unsigned bytes_per_unit = 0) { uint64_t hi = val[15]; for (int i = 15-1; i >= 8; i--) { hi <<= 8; hi += val[i]; } uint64_t lo = val[7]; for (int i = 7-1; i >= 0; i--) { lo <<= 8; lo += val[i]; } return le128_to_str(str, hi, lo, bytes_per_unit); } // Format capacity specified as 64bit LBA count for printing. static const char * lbacap_to_str(char (& str)[64], uint64_t lba_cnt, int lba_bits) { return le128_to_str(str, (lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits), 1); } // Output capacity specified as 64bit LBA count to JSON static void lbacap_to_js(const json::ref & jref, uint64_t lba_cnt, int lba_bits) { jref["blocks"].set_unsafe_uint64(lba_cnt); jref["bytes"].set_unsafe_uint128((lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits)); } // Format a Kelvin temperature value in Celsius. static const char * kelvin_to_str(char (& str)[64], int k) { if (!k) // unsupported? str[0] = '-', str[1] = 0; else snprintf(str, sizeof(str), "%d Celsius", k - 273); return str; } static void print_drive_info(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns, unsigned nsid, bool show_all) { char buf[64]; jout("Model Number: %s\n", format_char_array(buf, id_ctrl.mn)); jglb["model_name"] = buf; if (!dont_print_serial_number) { jout("Serial Number: %s\n", format_char_array(buf, id_ctrl.sn)); jglb["serial_number"] = buf; } jout("Firmware Version: %s\n", format_char_array(buf, id_ctrl.fr)); jglb["firmware_version"] = buf; // Vendor and Subsystem IDs are usually equal if (show_all || id_ctrl.vid != id_ctrl.ssvid) { jout("PCI Vendor ID: 0x%04x\n", id_ctrl.vid); jout("PCI Vendor Subsystem ID: 0x%04x\n", id_ctrl.ssvid); } else { jout("PCI Vendor/Subsystem ID: 0x%04x\n", id_ctrl.vid); } jglb["nvme_pci_vendor"]["id"] = id_ctrl.vid; jglb["nvme_pci_vendor"]["subsystem_id"] = id_ctrl.ssvid; jout("IEEE OUI Identifier: 0x%02x%02x%02x\n", id_ctrl.ieee[2], id_ctrl.ieee[1], id_ctrl.ieee[0]); jglb["nvme_ieee_oui_identifier"] = sg_get_unaligned_le(3, id_ctrl.ieee); // Capacity info is optional for devices without namespace management if (show_all || le128_is_non_zero(id_ctrl.tnvmcap) || le128_is_non_zero(id_ctrl.unvmcap)) { jout("Total NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1)); jglb["nvme_total_capacity"].set_unsafe_le128(id_ctrl.tnvmcap); jout("Unallocated NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1)); jglb["nvme_unallocated_capacity"].set_unsafe_le128(id_ctrl.unvmcap); } jout("Controller ID: %d\n", id_ctrl.cntlid); jglb["nvme_controller_id"] = id_ctrl.cntlid; // Print namespace info if available jout("Number of Namespaces: %u\n", id_ctrl.nn); jglb["nvme_number_of_namespaces"] = id_ctrl.nn; if (nsid && id_ns.nsze) { const char * align = &(" "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]); int fmt_lba_bits = id_ns.lbaf[id_ns.flbas & 0xf].ds; json::ref jrns = jglb["nvme_namespaces"][0]; jrns["id"] = nsid; // Size and Capacity are equal if thin provisioning is not supported if (show_all || id_ns.ncap != id_ns.nsze || (id_ns.nsfeat & 0x01)) { jout("Namespace %u Size: %s%s\n", nsid, align, lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits)); jout("Namespace %u Capacity: %s%s\n", nsid, align, lbacap_to_str(buf, id_ns.ncap, fmt_lba_bits)); } else { jout("Namespace %u Size/Capacity: %s%s\n", nsid, align, lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits)); } lbacap_to_js(jrns["size"], id_ns.nsze, fmt_lba_bits); lbacap_to_js(jrns["capacity"], id_ns.ncap, fmt_lba_bits); lbacap_to_js(jglb["user_capacity"], id_ns.ncap, fmt_lba_bits); // TODO: use nsze? // Utilization may be always equal to Capacity if thin provisioning is not supported if (show_all || id_ns.nuse != id_ns.ncap || (id_ns.nsfeat & 0x01)) jout("Namespace %u Utilization: %s%s\n", nsid, align, lbacap_to_str(buf, id_ns.nuse, fmt_lba_bits)); lbacap_to_js(jrns["utilization"], id_ns.nuse, fmt_lba_bits); jout("Namespace %u Formatted LBA Size: %s%u\n", nsid, align, (1U << fmt_lba_bits)); jrns["formatted_lba_size"] = (1U << fmt_lba_bits); jglb["logical_block_size"] = (1U << fmt_lba_bits); if (show_all || nonempty(id_ns.eui64, sizeof(id_ns.eui64))) { jout("Namespace %u IEEE EUI-64: %s%02x%02x%02x %02x%02x%02x%02x%02x\n", nsid, align, id_ns.eui64[0], id_ns.eui64[1], id_ns.eui64[2], id_ns.eui64[3], id_ns.eui64[4], id_ns.eui64[5], id_ns.eui64[6], id_ns.eui64[7]); jrns["eui64"]["oui"] = sg_get_unaligned_be(3, id_ns.eui64); jrns["eui64"]["ext_id"] = sg_get_unaligned_be(5, id_ns.eui64 + 3); } } time_t now = time(0); char td[DATEANDEPOCHLEN]; dateandtimezoneepoch(td, now); jout("Local Time is: %s\n", td); jglb["local_time"]["time_t"] = now; jglb["local_time"]["asctime"] = td; } // Format scaled power value. static const char * format_power(char (& str)[16], unsigned power, unsigned scale) { switch (scale & 0x3) { case 0: // not reported str[0] = '-'; str[1] = ' '; str[2] = 0; break; case 1: // 0.0001W snprintf(str, sizeof(str), "%u.%04uW", power / 10000, power % 10000); break; case 2: // 0.01W snprintf(str, sizeof(str), "%u.%02uW", power / 100, power % 100); break; default: // reserved str[0] = '?'; str[1] = 0; break; } return str; } static void print_drive_capabilities(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns, unsigned nsid, bool show_all) { pout("Firmware Updates (0x%02x): %d Slot%s%s%s\n", id_ctrl.frmw, ((id_ctrl.frmw >> 1) & 0x7), (((id_ctrl.frmw >> 1) & 0x7) != 1 ? "s" : ""), ((id_ctrl.frmw & 0x01) ? ", Slot 1 R/O" : ""), ((id_ctrl.frmw & 0x10) ? ", no Reset required" : "")); if (show_all || id_ctrl.oacs) pout("Optional Admin Commands (0x%04x): %s%s%s%s%s%s%s%s%s%s%s\n", id_ctrl.oacs, (!id_ctrl.oacs ? " -" : ""), ((id_ctrl.oacs & 0x0001) ? " Security" : ""), ((id_ctrl.oacs & 0x0002) ? " Format" : ""), ((id_ctrl.oacs & 0x0004) ? " Frmw_DL" : ""), ((id_ctrl.oacs & 0x0008) ? " NS_Mngmt" : ""), ((id_ctrl.oacs & 0x0010) ? " Self_Test" : ""), // NVMe 1.3 ... ((id_ctrl.oacs & 0x0020) ? " Directvs" : ""), ((id_ctrl.oacs & 0x0040) ? " MI_Snd/Rec" : ""), ((id_ctrl.oacs & 0x0080) ? " Vrt_Mngmt" : ""), ((id_ctrl.oacs & 0x0100) ? " Drbl_Bf_Cfg" : ""), ((id_ctrl.oacs & ~0x01ff) ? " *Other*" : "")); if (show_all || id_ctrl.oncs) pout("Optional NVM Commands (0x%04x): %s%s%s%s%s%s%s%s%s\n", id_ctrl.oncs, (!id_ctrl.oncs ? " -" : ""), ((id_ctrl.oncs & 0x0001) ? " Comp" : ""), ((id_ctrl.oncs & 0x0002) ? " Wr_Unc" : ""), ((id_ctrl.oncs & 0x0004) ? " DS_Mngmt" : ""), ((id_ctrl.oncs & 0x0008) ? " Wr_Zero" : ""), ((id_ctrl.oncs & 0x0010) ? " Sav/Sel_Feat" : ""), ((id_ctrl.oncs & 0x0020) ? " Resv" : ""), ((id_ctrl.oncs & 0x0040) ? " Timestmp" : ""), // NVMe 1.3 ((id_ctrl.oncs & ~0x007f) ? " *Other*" : "")); if (id_ctrl.mdts) pout("Maximum Data Transfer Size: %u Pages\n", (1U << id_ctrl.mdts)); else if (show_all) pout("Maximum Data Transfer Size: -\n"); // Temperature thresholds are optional char buf[64]; if (show_all || id_ctrl.wctemp) pout("Warning Comp. Temp. Threshold: %s\n", kelvin_to_str(buf, id_ctrl.wctemp)); if (show_all || id_ctrl.cctemp) pout("Critical Comp. Temp. Threshold: %s\n", kelvin_to_str(buf, id_ctrl.cctemp)); if (nsid && (show_all || id_ns.nsfeat)) { const char * align = &(" "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]); pout("Namespace %u Features (0x%02x): %s%s%s%s%s%s%s\n", nsid, id_ns.nsfeat, align, (!id_ns.nsfeat ? " -" : ""), ((id_ns.nsfeat & 0x01) ? " Thin_Prov" : ""), ((id_ns.nsfeat & 0x02) ? " NA_Fields" : ""), ((id_ns.nsfeat & 0x04) ? " Dea/Unw_Error" : ""), ((id_ns.nsfeat & 0x08) ? " No_ID_Reuse" : ""), // NVMe 1.3 ((id_ns.nsfeat & ~0x0f) ? " *Other*" : "")); } // Print Power States pout("\nSupported Power States\n"); pout("St Op Max Active Idle RL RT WL WT Ent_Lat Ex_Lat\n"); for (int i = 0; i <= id_ctrl.npss /* 1-based */ && i < 32; i++) { char p1[16], p2[16], p3[16]; const nvme_id_power_state & ps = id_ctrl.psd[i]; pout("%2d %c %9s %8s %8s %3d %2d %2d %2d %8u %7u\n", i, ((ps.flags & 0x02) ? '-' : '+'), format_power(p1, ps.max_power, ((ps.flags & 0x01) ? 1 : 2)), format_power(p2, ps.active_power, ps.active_work_scale), format_power(p3, ps.idle_power, ps.idle_scale), ps.read_lat & 0x1f, ps.read_tput & 0x1f, ps.write_lat & 0x1f, ps.write_tput & 0x1f, ps.entry_lat, ps.exit_lat); } // Print LBA sizes if (nsid && id_ns.lbaf[0].ds) { pout("\nSupported LBA Sizes (NSID 0x%x)\n", nsid); pout("Id Fmt Data Metadt Rel_Perf\n"); for (int i = 0; i <= id_ns.nlbaf /* 1-based */ && i < 16; i++) { const nvme_lbaf & lba = id_ns.lbaf[i]; pout("%2d %c %7u %7d %9d\n", i, (i == id_ns.flbas ? '+' : '-'), (1U << lba.ds), lba.ms, lba.rp); } } } static void print_critical_warning(unsigned char w) { jout("SMART overall-health self-assessment test result: %s\n", (!w ? "PASSED" : "FAILED!")); jglb["smart_status"]["passed"] = !w; json::ref jref = jglb["smart_status"]["nvme"]; jref["value"] = w; if (w) { if (w & 0x01) jout("- available spare has fallen below threshold\n"); jref["spare_below_threshold"] = !!(w & 0x01); if (w & 0x02) jout("- temperature is above or below threshold\n"); jref["temperature_above_or_below_threshold"] = !!(w & 0x02); if (w & 0x04) jout("- NVM subsystem reliability has been degraded\n"); jref["reliability_degraded"] = !!(w & 0x04); if (w & 0x08) jout("- media has been placed in read only mode\n"); jref["media_read_only"] = !!(w & 0x08); if (w & 0x10) jout("- volatile memory backup device has failed\n"); jref["volatile_memory_backup_failed"] = !!(w & 0x10); if (w & ~0x1f) jout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f); jref["other"] = w & ~0x1f; } jout("\n"); } static void print_smart_log(const nvme_smart_log & smart_log, const nvme_id_ctrl & id_ctrl, bool show_all) { json::ref jref = jglb["nvme_smart_health_information_log"]; char buf[64]; jout("SMART/Health Information (NVMe Log 0x02)\n"); jout("Critical Warning: 0x%02x\n", smart_log.critical_warning); jref["critical_warning"] = smart_log.critical_warning; int k = sg_get_unaligned_le16(smart_log.temperature); jout("Temperature: %s\n", kelvin_to_str(buf, k)); if (k) { jref["temperature"] = k - 273; jglb["temperature"]["current"] = k - 273; } jout("Available Spare: %u%%\n", smart_log.avail_spare); jref["available_spare"] = smart_log.avail_spare; jout("Available Spare Threshold: %u%%\n", smart_log.spare_thresh); jref["available_spare_threshold"] = smart_log.spare_thresh; jout("Percentage Used: %u%%\n", smart_log.percent_used); jref["percentage_used"] = smart_log.percent_used; jout("Data Units Read: %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512)); jref["data_units_read"].set_unsafe_le128(smart_log.data_units_read); jout("Data Units Written: %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512)); jref["data_units_written"].set_unsafe_le128(smart_log.data_units_written); jout("Host Read Commands: %s\n", le128_to_str(buf, smart_log.host_reads)); jref["host_reads"].set_unsafe_le128(smart_log.host_reads); jout("Host Write Commands: %s\n", le128_to_str(buf, smart_log.host_writes)); jref["host_writes"].set_unsafe_le128(smart_log.host_writes); jout("Controller Busy Time: %s\n", le128_to_str(buf, smart_log.ctrl_busy_time)); jref["controller_busy_time"].set_unsafe_le128(smart_log.ctrl_busy_time); jout("Power Cycles: %s\n", le128_to_str(buf, smart_log.power_cycles)); jref["power_cycles"].set_unsafe_le128(smart_log.power_cycles); jglb["power_cycle_count"].set_if_safe_le128(smart_log.power_cycles); jout("Power On Hours: %s\n", le128_to_str(buf, smart_log.power_on_hours)); jref["power_on_hours"].set_unsafe_le128(smart_log.power_on_hours); jglb["power_on_time"]["hours"].set_if_safe_le128(smart_log.power_on_hours); jout("Unsafe Shutdowns: %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns)); jref["unsafe_shutdowns"].set_unsafe_le128(smart_log.unsafe_shutdowns); jout("Media and Data Integrity Errors: %s\n", le128_to_str(buf, smart_log.media_errors)); jref["media_errors"].set_unsafe_le128(smart_log.media_errors); jout("Error Information Log Entries: %s\n", le128_to_str(buf, smart_log.num_err_log_entries)); jref["num_err_log_entries"].set_unsafe_le128(smart_log.num_err_log_entries); // Temperature thresholds are optional if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time) { jout("Warning Comp. Temperature Time: %d\n", smart_log.warning_temp_time); jref["warning_temp_time"] = smart_log.warning_temp_time; } if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time) { jout("Critical Comp. Temperature Time: %d\n", smart_log.critical_comp_time); jref["critical_comp_time"] = smart_log.critical_comp_time; } // Temperature sensors are optional for (int i = 0; i < 8; i++) { int k = smart_log.temp_sensor[i]; if (show_all || k) { jout("Temperature Sensor %d: %s\n", i + 1, kelvin_to_str(buf, k)); if (k) jref["temperature_sensors"][i] = k - 273; } } if (show_all || smart_log.thm_temp1_trans_count) pout("Thermal Temp. 1 Transition Count: %d\n", smart_log.thm_temp1_trans_count); if (show_all || smart_log.thm_temp2_trans_count) pout("Thermal Temp. 2 Transition Count: %d\n", smart_log.thm_temp2_trans_count); if (show_all || smart_log.thm_temp1_total_time) pout("Thermal Temp. 1 Total Time: %d\n", smart_log.thm_temp1_total_time); if (show_all || smart_log.thm_temp2_total_time) pout("Thermal Temp. 2 Total Time: %d\n", smart_log.thm_temp2_total_time); pout("\n"); } static void print_error_log(const nvme_error_log_page * error_log, unsigned num_entries, unsigned print_entries) { pout("Error Information (NVMe Log 0x01, max %u entries)\n", num_entries); unsigned cnt = 0; for (unsigned i = 0; i < num_entries; i++) { const nvme_error_log_page & e = error_log[i]; if (!e.error_count) continue; // unused or invalid entry if (++cnt > print_entries) continue; if (cnt == 1) pout("Num ErrCount SQId CmdId Status PELoc LBA NSID VS\n"); char sq[16] = "-", cm[16] = "-", st[16] = "-", pe[16] = "-"; char lb[32] = "-", ns[16] = "-", vs[8] = "-"; if (e.sqid != 0xffff) snprintf(sq, sizeof(sq), "%d", e.sqid); if (e.cmdid != 0xffff) snprintf(cm, sizeof(cm), "0x%04x", e.cmdid); if (e.status_field != 0xffff) snprintf(st, sizeof(st), "0x%04x", e.status_field); if (e.parm_error_location != 0xffff) snprintf(pe, sizeof(pe), "0x%03x", e.parm_error_location); if (e.lba != 0xffffffffffffffffULL) snprintf(lb, sizeof(lb), "%" PRIu64, e.lba); if (e.nsid != 0xffffffffU) snprintf(ns, sizeof(ns), "%u", e.nsid); if (e.vs != 0x00) snprintf(vs, sizeof(vs), "0x%02x", e.vs); pout("%3u %10" PRIu64 " %5s %7s %7s %6s %12s %5s %5s\n", i, e.error_count, sq, cm, st, pe, lb, ns, vs); } if (!cnt) pout("No Errors Logged\n"); else if (cnt > print_entries) pout("... (%u entries not shown)\n", cnt - print_entries); pout("\n"); } int nvmePrintMain(nvme_device * device, const nvme_print_options & options) { if (!( options.drive_info || options.drive_capabilities || options.smart_check_status || options.smart_vendor_attrib || options.error_log_entries || options.log_page_size )) { pout("NVMe device successfully opened\n\n" "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n"); return 0; } // Show unset optional values only if debugging is enabled bool show_all = (nvme_debugmode > 0); // Read Identify Controller always nvme_id_ctrl id_ctrl; if (!nvme_read_id_ctrl(device, id_ctrl)) { jerr("Read NVMe Identify Controller failed: %s\n", device->get_errmsg()); return FAILID; } // Print Identify Controller/Namespace info if (options.drive_info || options.drive_capabilities) { pout("=== START OF INFORMATION SECTION ===\n"); nvme_id_ns id_ns; memset(&id_ns, 0, sizeof(id_ns)); unsigned nsid = device->get_nsid(); if (nsid == 0xffffffffU) { // Broadcast namespace if (id_ctrl.nn == 1) { // No namespace management, get size from single namespace nsid = 1; if (!nvme_read_id_ns(device, nsid, id_ns)) nsid = 0; } } else { // Identify current namespace if (!nvme_read_id_ns(device, nsid, id_ns)) { jerr("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg()); return FAILID; } } if (options.drive_info) print_drive_info(id_ctrl, id_ns, nsid, show_all); if (options.drive_capabilities) print_drive_capabilities(id_ctrl, id_ns, nsid, show_all); pout("\n"); } if ( options.smart_check_status || options.smart_vendor_attrib || options.error_log_entries) pout("=== START OF SMART DATA SECTION ===\n"); // Print SMART Status and SMART/Health Information int retval = 0; if (options.smart_check_status || options.smart_vendor_attrib) { nvme_smart_log smart_log; if (!nvme_read_smart_log(device, smart_log)) { jerr("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg()); return FAILSMART; } if (options.smart_check_status) { print_critical_warning(smart_log.critical_warning); if (smart_log.critical_warning) retval |= FAILSTATUS; } if (options.smart_vendor_attrib) { print_smart_log(smart_log, id_ctrl, show_all); } } // Print Error Information Log if (options.error_log_entries) { unsigned num_entries = id_ctrl.elpe + 1; // 0-based value raw_buffer error_log_buf(num_entries * sizeof(nvme_error_log_page)); nvme_error_log_page * error_log = reinterpret_cast(error_log_buf.data()); if (!nvme_read_error_log(device, error_log, num_entries)) { jerr("Read Error Information Log failed: %s\n\n", device->get_errmsg()); return retval | FAILSMART; } print_error_log(error_log, num_entries, options.error_log_entries); } // Dump log page if (options.log_page_size) { // Align size to dword boundary unsigned size = ((options.log_page_size + 4-1) / 4) * 4; bool broadcast_nsid; raw_buffer log_buf(size); switch (options.log_page) { case 1: case 2: case 3: broadcast_nsid = true; break; default: broadcast_nsid = false; break; } if (!nvme_read_log_page(device, options.log_page, log_buf.data(), size, broadcast_nsid)) { jerr("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg()); return retval | FAILSMART; } pout("NVMe Log 0x%02x (0x%04x bytes)\n", options.log_page, size); dStrHex(log_buf.data(), size, 0); pout("\n"); } return retval; } smartmontools-7.0/nvmeprint.h0000644000175000010010000000157113336335341013412 00000000000000/* * nvmeprint.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2016 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef NVMEPRINT_H #define NVMEPRINT_H #define NVMEPRINT_H_CVSID "$Id: nvmeprint.h 4760 2018-08-19 18:45:53Z chrfranke $" #include "nvmecmds.h" // options for nvmePrintMain struct nvme_print_options { bool drive_info; bool drive_capabilities; bool smart_check_status; bool smart_vendor_attrib; unsigned error_log_entries; unsigned char log_page; unsigned log_page_size; nvme_print_options() : drive_info(false), drive_capabilities(false), smart_check_status(false), smart_vendor_attrib(false), error_log_entries(0), log_page(0), log_page_size(0) { } }; int nvmePrintMain(nvme_device * device, const nvme_print_options & options); #endif // NVMEPRINT_H smartmontools-7.0/os_darwin/0000755000175000010010000000000013412155413013253 500000000000000smartmontools-7.0/os_darwin/com.smartmontools.smartd.plist.in0000644000175000010010000000101012651453605021644 00000000000000 KeepAlive SuccessfulExit Label com.smartmontools.smartd ProgramArguments /usr/local/sbin/smartd -n RunAtLoad smartmontools-7.0/os_darwin/pkg/0000755000175000010010000000000013412155413014034 500000000000000smartmontools-7.0/os_darwin/pkg/Distribution.in0000644000175000010010000000146212572330314016767 00000000000000 S.M.A.R.T. disk monitoring tools @pkgname@ smartmontools-7.0/os_darwin/pkg/installer/0000755000175000010010000000000013412155413016031 500000000000000smartmontools-7.0/os_darwin/pkg/installer/README.html0000644000175000010010000000301412651410013017564 00000000000000

About this package

The smartmontools package contains two utility programs (smartctl and smartd) to control and monitor storage systems using the Self-Monitoring, Analysis and Reporting Technology System (SMART) built into most modern ATA and SCSI harddisks. In many cases, these utilities will provide advanced warning of disk degradation and failure.

Installing

To install package click on the smartmontools.pkg icon and follow installation process. Files will be installed to the /usr/local/ directory.

Usage

If you are having trouble understanding the output of smartctl or smartd, please first read the manual pages installed on your system:
  man 8 smartctl
  man 8 smartd
  man 8 update-smart-drivedb
  man 5 smartd.conf
To use smartmontools with USB drives please download and install Max OS X kernel driver for providing access to external drive SMART data. SAT SMART Driver is a free open source project (published under Apple Public Source License) by Jarkko Sonninen. If you are using OS X El Capitan 10.11+ it is recommended to use signed version available from DriveDx web site.

More information could be found on the www.smartmontools.org website.

Uninstalling

If you want to uninstall already installed package run 'sudo smart-pkg-uninstall' in the terminal. smartmontools-7.0/os_darwin/pkg/PackageInfo.in0000644000175000010010000000040212572330314016450 00000000000000 smartmontools-7.0/os_darwin/pkg/root/0000755000175000010010000000000013412155406015021 500000000000000smartmontools-7.0/os_darwin/pkg/root/usr/0000755000175000010010000000000013412155406015632 500000000000000smartmontools-7.0/os_darwin/pkg/root/usr/local/0000755000175000010010000000000013412155406016724 500000000000000smartmontools-7.0/os_darwin/pkg/root/usr/local/sbin/0000755000175000010010000000000013412155413017655 500000000000000smartmontools-7.0/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall0000755000175000010010000000157312572035731023453 00000000000000#!/bin/sh echo "Smartmontools package uninstaller:" # check if we are running with root uid if [[ $EUID -ne 0 ]]; then echo " Error: this script must be run as root" exit 1 fi # check if package is installed pkgutil --info com.smartmontools.pkg > /dev/null 2>/dev/null if [ $? -ne 0 ]; then echo " Error: smartmontools package is not installed" exit 1 fi # smartmontools pkg could be installed only on system volume, so this should be safe cd / echo " - removing files" for str in `pkgutil --files com.smartmontools.pkg` do if [ -f "$str" ] then rm -f "$str" fi done echo " - removing empty directories" for str in `pkgutil --files com.smartmontools.pkg` do if [ -d "$str" ] then rmdir -p "$str" 2>/dev/null fi done echo " - removing package system entry" pkgutil --forget com.smartmontools.pkg echo "Done, smartmontolls package removed" smartmontools-7.0/os_darwin.cpp0000644000175000010010000005344213371232277013717 00000000000000/* * os_darwin.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 Geoffrey Keating * Copyright (C) 2014 Alex Samorukov * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "atacmds.h" #include "scsicmds.h" #include "nvmecmds.h" #include "utility.h" #include "os_darwin.h" #include "dev_interface.h" #define ARGUSED(x) ((void)(x)) // Needed by '-V' option (CVS versioning) of smartd/smartctl const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $" \ ATACMDS_H_CVSID CONFIG_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; // examples for smartctl static const char smartctl_examples[] = "=================================================== SMARTCTL EXAMPLES =====\n\n" " smartctl -a disk0 (Prints all SMART information)\n\n" " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n" " 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" " 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" ; // Information that we keep about each device. static struct { io_object_t ioob; IOCFPlugInInterface **plugin; IOATASMARTInterface **smartIf; // ATA devices IONVMeSMARTInterface **smartIfNVMe; } devices[20]; const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $" DEV_INTERFACE_H_CVSID; ///////////////////////////////////////////////////////////////////////////// namespace os { // No need to publish anything, name provided for Doxygen ///////////////////////////////////////////////////////////////////////////// /// Implement shared open/close routines with old functions. class darwin_smart_device : virtual public /*implements*/ smart_device { public: explicit darwin_smart_device(const char * mode) : smart_device(never_called), m_fd(-1), m_mode(mode) { } virtual ~darwin_smart_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: /// Return filedesc for derived classes. int get_fd() const { return m_fd; } private: int m_fd; ///< filedesc, -1 if not open. const char * m_mode; ///< Mode string for deviceopen(). }; darwin_smart_device::~darwin_smart_device() throw() { if (m_fd >= 0) darwin_smart_device::close(); } bool darwin_smart_device::is_open() const { return (m_fd >= 0); } // Determine whether 'dev' is a SMART-capable device. static bool is_smart_capable (io_object_t dev, const char * type) { CFTypeRef smartCapableKey = NULL; CFDictionaryRef diskChars; // If the device has kIOPropertySMARTCapableKey, then it's capable, // no matter what it looks like. if (!strcmp("ATA", type)) { smartCapableKey = IORegistryEntryCreateCFProperty (dev, CFSTR (kIOPropertySMARTCapableKey), kCFAllocatorDefault, 0); } else if (!strcmp("NVME", type)) { smartCapableKey = IORegistryEntryCreateCFProperty (dev, CFSTR (kIOPropertyNVMeSMARTCapableKey), kCFAllocatorDefault, 0); } if (smartCapableKey) { CFRelease (smartCapableKey); return true; } // If it's an kIOATABlockStorageDeviceClass then we're successful // only if its ATA features indicate it supports SMART. // This will be broken for NVMe, however it is not needed if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass) && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, kNilOptions)) != NULL) { CFNumberRef diskFeatures = NULL; UInt32 ataFeatures = 0; if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"), (const void **)&diskFeatures)) CFNumberGetValue (diskFeatures, kCFNumberLongType, &ataFeatures); CFRelease (diskChars); if (diskFeatures) CFRelease (diskFeatures); return (ataFeatures & kIOATAFeatureSMART) != 0; } return false; } bool darwin_smart_device::open() { // Acceptable device names are: // /dev/disk* // /dev/rdisk* // disk* // IOService:* // IODeviceTree:* size_t devnum; const char *devname; io_object_t disk; const char *pathname = get_dev_name(); char *type = const_cast(m_mode); if (!(strcmp("ATA", type) || strcmp("NVME", type))) { set_err (EINVAL); return false; } // 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])) { set_err (EMFILE); return false; } 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. This part should be the same for the NVMe and ATA if (devname) { CFMutableDictionaryRef matcher; matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); } else { disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname); } if (! disk) { set_err(ENOENT); return false; } // Find a SMART-capable driver which is a parent of this device. while (! is_smart_capable (disk, type)) { IOReturn err; io_object_t prevdisk = disk; // Find this device's parent and try again. err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk); if (err != kIOReturnSuccess || ! disk) { set_err(ENODEV); IOObjectRelease (prevdisk); return false; } } devices[devnum].ioob = disk; { SInt32 dummy; devices[devnum].plugin = NULL; devices[devnum].smartIf = NULL; devices[devnum].smartIfNVMe = NULL; CFUUIDRef pluginType = NULL; CFUUIDRef smartInterfaceId = NULL; void ** SMARTptr = NULL; if (!strcmp("ATA", type)) { pluginType = kIOATASMARTUserClientTypeID; smartInterfaceId = kIOATASMARTInterfaceID; SMARTptr = (void **)&devices[devnum].smartIf; } else if (!strcmp("NVME", type)) { pluginType = kIONVMeSMARTUserClientTypeID; smartInterfaceId = kIONVMeSMARTInterfaceID; SMARTptr = (void **)&devices[devnum].smartIfNVMe; } // Create an interface to the ATA SMART library. if (IOCreatePlugInInterfaceForService (disk, pluginType, kIOCFPlugInInterfaceID, &devices[devnum].plugin, &dummy) == kIOReturnSuccess) (*devices[devnum].plugin)->QueryInterface (devices[devnum].plugin, CFUUIDGetUUIDBytes ( smartInterfaceId), SMARTptr); else return set_err(ENOSYS, "IOCreatePlugInInterfaceForService failed"); } m_fd = devnum; if (m_fd < 0) { set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); return false; } return true; } bool darwin_smart_device::close() { int fd = m_fd; m_fd = -1; if (devices[fd].smartIf) (*devices[fd].smartIf)->Release (devices[fd].smartIf); if (devices[fd].smartIfNVMe) (*devices[fd].smartIfNVMe)->Release (devices[fd].smartIfNVMe); if (devices[fd].plugin) IODestroyPlugInInterface (devices[fd].plugin); IOObjectRelease (devices[fd].ioob); devices[fd].ioob = MACH_PORT_NULL; return true; } // 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). static int make_device_names (char*** devlist, const char* name) { IOReturn err; io_iterator_t i; io_object_t device = MACH_PORT_NULL; int result; int index; if (!(strcmp("ATA", name) || strcmp("NVME", name))) { return 0; } err = IOServiceGetMatchingServices (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i); if (err != kIOReturnSuccess) return -1; // Count the devices. result = 0; while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) { if (is_smart_capable (device, name)) result++; IOObjectRelease (device); } // Create an array of service names. IOIteratorReset (i); if (! result) goto error; *devlist = (char**)calloc (result, sizeof (char *)); index = 0; while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) { if (is_smart_capable (device, name)) { io_string_t devName; IORegistryEntryGetPath(device, kIOServicePlane, devName); (*devlist)[index] = strdup (devName); if (! (*devlist)[index]) goto error; index++; } IOObjectRelease (device); } IOObjectRelease (i); return result; error: if (device != MACH_PORT_NULL) IOObjectRelease (device); IOObjectRelease (i); if (*devlist) { for (index = 0; index < result; index++) if ((*devlist)[index]) free ((*devlist)[index]); free (*devlist); } if(!result) // no devs found return 0; return -1; } ///////////////////////////////////////////////////////////////////////////// /// Implement standard ATA support class darwin_ata_device : public /*implements*/ ata_device, public /*extends*/ darwin_smart_device { public: darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: // virtual int ata_command_interface(smart_command_set command, int select, char * data); }; darwin_ata_device::darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), darwin_smart_device("ATA") { } bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_ok(in, true, // data_out_support true, // multi_sector_support false) // not supported by API ) return false; int select = 0; char * data = (char *)in.buffer; int fd = get_fd(); IOATASMARTInterface **ifp = devices[fd].smartIf; IOATASMARTInterface *smartIf; IOReturn err; int timeoutCount = 5; int rc = 0; if (! ifp) return false; smartIf = *ifp; clear_err(); errno = 0; do { switch (in.in_regs.command) { case ATA_IDENTIFY_DEVICE: { UInt32 dummy; err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy); if (err != kIOReturnSuccess && err != kIOReturnTimeout && err != kIOReturnNotResponding) printf ("identify failed: %#x\n", (unsigned) rc); 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 ATA_IDENTIFY_PACKET_DEVICE: case ATA_CHECK_POWER_MODE: errno = ENOTSUP; err = -1; break; case ATA_SMART_CMD: switch (in.in_regs.features) { case ATA_SMART_READ_VALUES: err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data); break; case ATA_SMART_READ_THRESHOLDS: err = smartIf->SMARTReadDataThresholds (ifp, (ATASMARTDataThresholds *)data); break; case ATA_SMART_READ_LOG_SECTOR: err = smartIf->SMARTReadLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count); break; case ATA_SMART_WRITE_LOG_SECTOR: err = smartIf->SMARTWriteLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count); break; case ATA_SMART_ENABLE: case ATA_SMART_DISABLE: err = smartIf->SMARTEnableDisableOperations (ifp, in.in_regs.features == ATA_SMART_ENABLE); break; case ATA_SMART_STATUS: if (in.out_needed.lba_high) // statuscheck { Boolean is_failing; err = smartIf->SMARTReturnStatus (ifp, &is_failing); if (err == kIOReturnSuccess && is_failing) { err = -1; // thresholds exceeded condition out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4; } else { out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f; } break; } else err = 0; break; case ATA_SMART_AUTOSAVE: err = smartIf->SMARTEnableDisableAutosave (ifp, (in.in_regs.sector_count == 241 ? true : false)); break; case ATA_SMART_IMMEDIATE_OFFLINE: select = in.in_regs.lba_low; if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST) { errno = EINVAL; return set_err(ENOSYS, "Unsupported SMART self-test mode"); } err = smartIf->SMARTExecuteOffLineImmediate (ifp, select == EXTEND_SELF_TEST); break; case ATA_SMART_AUTO_OFFLINE: return set_err(ENOSYS, "SMART command not supported"); default: return set_err(ENOSYS, "Unknown SMART command"); } break; default: return set_err(ENOSYS, "Non-SMART commands not implemented"); } } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding) && timeoutCount-- > 0); if (err == kIOReturnExclusiveAccess) errno = EBUSY; rc = err == kIOReturnSuccess ? 0 : -1; if (rc < 0) { if (!get_errno()) set_err(errno); return false; } return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement platform interface class darwin_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_os_version_str(); virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); virtual smart_device * autodetect_smart_device(const char * name); }; ///////////////////////////////////////////////////////////////////////////// /// NVMe support class darwin_nvme_device : public /*implements*/ nvme_device, public /*extends*/ darwin_smart_device { public: darwin_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); }; darwin_nvme_device::darwin_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid), darwin_smart_device("NVME") { } bool darwin_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { ARGUSED(out); int fd = get_fd(); IONVMeSMARTInterface **ifp = devices[fd].smartIfNVMe; IONVMeSMARTInterface *smartIfNVMe ; IOReturn err = 0; unsigned int page = in.cdw10 & 0xff; if (! ifp) return false; smartIfNVMe = *ifp; // currently only GetIdentifyData and SMARTReadData are supported switch (in.opcode) { case smartmontools::nvme_admin_identify: err = smartIfNVMe->GetIdentifyData(ifp, (struct nvme_id_ctrl *) in.buffer, in.nsid); // FIXME break; case smartmontools::nvme_admin_get_log_page: if(page == 0x02) err = smartIfNVMe->SMARTReadData(ifp, (struct nvme_smart_log *) in.buffer); else /* GetLogPage() is not working yet */ return set_err(ENOSYS, "NVMe admin command:0x%02x/page:0x%02x is not supported", in.opcode, page); break; default: return set_err(ENOSYS, "NVMe admin command 0x%02x is not supported", in.opcode); } return true; } ////////////////////////////////////////////////////////////////////// std::string darwin_smart_interface::get_os_version_str() { // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta struct utsname osname; uname(&osname); return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); } std::string darwin_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) return smartctl_examples; return ""; // ... so don't print again. } ata_device * darwin_smart_interface::get_ata_device(const char * name, const char * type) { return new darwin_ata_device(this, name, type); } scsi_device * darwin_smart_interface::get_scsi_device(const char *, const char *) { return 0; // scsi devices are not supported [yet] } nvme_device * darwin_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) { return new darwin_nvme_device(this, name, type, nsid); } smart_device * darwin_smart_interface::autodetect_smart_device(const char * name) { // TODO - refactor as a function // Acceptable device names are: // /dev/disk* // /dev/rdisk* // disk* // IOService:* // IODeviceTree:* const char *devname = NULL; io_object_t disk; if (strncmp (name, "/dev/rdisk", 10) == 0) devname = name + 6; else if (strncmp (name, "/dev/disk", 9) == 0) devname = name + 5; else if (strncmp (name, "disk", 4) == 0) // allow user to just say 'disk0' devname = name; // Find the device. This part should be the same for the NVMe and ATA if (devname) { CFMutableDictionaryRef matcher; matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); } else { disk = IORegistryEntryFromPath (kIOMasterPortDefault, name); } if (! disk) { return 0; } io_registry_entry_t tmpdisk=disk; while (! is_smart_capable (tmpdisk, "ATA")) { IOReturn err; io_object_t prevdisk = tmpdisk; // Find this device's parent and try again. err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk); if (err != kIOReturnSuccess || ! tmpdisk) { IOObjectRelease (prevdisk); break; } } if (tmpdisk) return new darwin_ata_device(this, name, ""); tmpdisk=disk; while (! is_smart_capable (tmpdisk, "NVME")) { IOReturn err; io_object_t prevdisk = tmpdisk; // Find this device's parent and try again. err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk); if (err != kIOReturnSuccess || ! tmpdisk) { IOObjectRelease (prevdisk); break; } } if (tmpdisk) return new darwin_nvme_device(this, name, "", 0); // try ATA as a last option, for compatibility return new darwin_ata_device(this, name, ""); } static void free_devnames(char * * devnames, int numdevs) { for (int i = 0; i < numdevs; i++) free(devnames[i]); free(devnames); } bool darwin_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /*= 0*/) { if (pattern) { set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); return false; } // Make namelists char * * atanames = 0; int numata = 0; if (!type || !strcmp(type, "ata")) { numata = make_device_names(&atanames, "ATA"); if (numata < 0) { set_err(ENOMEM); return false; } } char * * nvmenames = 0; int numnvme = 0; if ( #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL !type || #else type && #endif !strcmp(type, "nvme")) { numnvme = make_device_names(&nvmenames, "NVME"); if (numnvme < 0) { set_err(ENOMEM); return false; } } // Add to devlist int i; if (!type) type=""; for (i = 0; i < numata; i++) { ata_device * atadev = get_ata_device(atanames[i], type); if (atadev) devlist.push_back(atadev); } free_devnames(atanames, numata); for (i = 0; i < numnvme; i++) { nvme_device * nvmedev = get_nvme_device(nvmenames[i], type, 0); // default nsid if (nvmedev) devlist.push_back(nvmedev); } free_devnames(nvmenames, numnvme); return true; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static os::darwin_smart_interface the_interface; smart_interface::set(&the_interface); } smartmontools-7.0/os_darwin.h0000644000175000010010000001102713336335341013352 00000000000000/* * os_generic.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 Geoff Keating * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_DARWIN_H_ #define OS_DARWIN_H_ #define OS_DARWIN_H_CVSID "$Id: os_darwin.h 4760 2018-08-19 18:45:53Z chrfranke $\n" #define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice" // Isn't in 10.3.9? #ifndef kIOPropertySMARTCapableKey #define kIOPropertySMARTCapableKey "SMART Capable" #endif // NVMe definitions, non documented, experimental #define kIOPropertyNVMeSMARTCapableKey "NVMe SMART Capable" // Constant to init driver #define kIONVMeSMARTUserClientTypeID CFUUIDGetConstantUUIDWithBytes(NULL, \ 0xAA, 0x0F, 0xA6, 0xF9, 0xC2, 0xD6, 0x45, 0x7F, 0xB1, 0x0B, \ 0x59, 0xA1, 0x32, 0x53, 0x29, 0x2F) // Constant to use plugin interface #define kIONVMeSMARTInterfaceID CFUUIDGetConstantUUIDWithBytes(NULL, \ 0xcc, 0xd1, 0xdb, 0x19, 0xfd, 0x9a, 0x4d, 0xaf, 0xbf, 0x95, \ 0x12, 0x45, 0x4b, 0x23, 0xa, 0xb6) // interface structure, obtained using lldb, could be incomplete or wrong typedef struct IONVMeSMARTInterface { IUNKNOWN_C_GUTS; UInt16 version; UInt16 revision; // NVMe smart data, returns nvme_smart_log structure IOReturn ( *SMARTReadData )( void * interface, struct nvme_smart_log * NVMeSMARTData ); // NVMe IdentifyData, returns nvme_id_ctrl per namespace IOReturn ( *GetIdentifyData )( void * interface, struct nvme_id_ctrl * NVMeIdentifyControllerStruct, unsigned int ns ); // Always getting kIOReturnDeviceError IOReturn ( *GetFieldCounters )( void * interface, char * FieldCounters ); // Returns 0 IOReturn ( *ScheduleBGRefresh )( void * interface); // Always returns kIOReturnDeviceError, probably expects pointer to some // structure as an argument IOReturn ( *GetLogPage )( void * interface, void * data, unsigned int, unsigned int); /* GetSystemCounters Looks like a table with an attributes. Sample result: 0x101022200: 0x01 0x00 0x08 0x00 0x00 0x00 0x00 0x00 0x101022208: 0x00 0x00 0x00 0x00 0x02 0x00 0x08 0x00 0x101022210: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x101022218: 0x03 0x00 0x08 0x00 0xf1 0x74 0x26 0x01 0x101022220: 0x00 0x00 0x00 0x00 0x04 0x00 0x08 0x00 0x101022228: 0x0a 0x91 0xb1 0x00 0x00 0x00 0x00 0x00 0x101022230: 0x05 0x00 0x08 0x00 0x24 0x9f 0xfe 0x02 0x101022238: 0x00 0x00 0x00 0x00 0x06 0x00 0x08 0x00 0x101022240: 0x9b 0x42 0x38 0x02 0x00 0x00 0x00 0x00 0x101022248: 0x07 0x00 0x08 0x00 0xdd 0x08 0x00 0x00 0x101022250: 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x00 0x101022258: 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x101022260: 0x09 0x00 0x08 0x00 0x00 0x00 0x00 0x00 0x101022268: 0x00 0x00 0x00 0x00 0x0a 0x00 0x04 0x00 ......... 0x101022488: 0x74 0x00 0x08 0x00 0x00 0x00 0x00 0x00 0x101022490: 0x00 0x00 0x00 0x00 0x75 0x00 0x40 0x02 0x101022498: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 */ IOReturn ( *GetSystemCounters )( void * interface, char *, unsigned int *); /* GetAlgorithmCounters returns mostly 0 0x102004000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004038: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004040: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004048: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004050: 0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00 0x102004058: 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004060: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004068: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004070: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004078: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004080: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004088: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x102004090: 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x102004098: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 */ IOReturn ( *GetAlgorithmCounters )( void * interface, char *, unsigned int *); } IONVMeSMARTInterface; #endif /* OS_DARWIN_H_ */ smartmontools-7.0/os_freebsd.cpp0000644000175000010010000017330213402014526014031 00000000000000/* * os_freebsd.c * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-10 Eduard Martinescu * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #if defined(__DragonFly__) #include #else #include #endif #include #include #include #include #include #include #include "config.h" // set by /usr/include/sys/ata.h, suppress warning #undef ATA_READ_LOG_EXT #include "atacmds.h" #include "scsicmds.h" #include "cciss.h" #include "utility.h" #include "os_freebsd.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" #include "dev_areca.h" #define USBDEV "/dev/usb" #if defined(__FreeBSD_version) // This way we define one variable for the GNU/kFreeBSD and FreeBSD #define FREEBSDVER __FreeBSD_version #else #define FREEBSDVER __FreeBSD_kernel_version #endif #if (FREEBSDVER >= 800000) #include #include #elif defined(__DragonFly__) #include #include #else #include #include #endif // based on "/sys/dev/nvme/nvme.h" from FreeBSD kernel sources #include "freebsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error #define CONTROLLER_3WARE_9000_CHAR 0x01 #define CONTROLLER_3WARE_678K_CHAR 0x02 #ifndef PATHINQ_SETTINGS_SIZE #define PATHINQ_SETTINGS_SIZE 128 #endif const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 4848 2018-12-05 18:30:46Z chrfranke $" \ ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; #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) { if (msgNo >= 0 && msgNo <= MAX_MSG) { static int printed[] = {0,0,0,0}; if (!printed[msgNo]) { 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" }; printed[msgNo] = 1; pout("%s", message[msgNo]); if (extra) pout("%s",extra); } } return; } // 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) ) #ifndef ATA_DEVICE #define ATA_DEVICE "/dev/ata" #endif #define ARGUSED(x) ((void)(x)) extern unsigned char failuretest_permissive; ///////////////////////////////////////////////////////////////////////////// namespace os_freebsd { // No need to publish anything, name provided for Doxygen ///////////////////////////////////////////////////////////////////////////// /// Implement shared open/close routines with old functions. class freebsd_smart_device : virtual public /*implements*/ smart_device { public: explicit freebsd_smart_device() : smart_device(never_called), m_fd(-1) { } virtual ~freebsd_smart_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: /// Return filedesc for derived classes. int get_fd() const { return m_fd; } void set_fd(int fd) { m_fd = fd; } private: int m_fd; ///< filedesc, -1 if not open. }; #ifdef __GLIBC__ static inline void * reallocf(void *ptr, size_t size) { void *rv = realloc(ptr, size); if((rv == NULL) && (size != 0)) free(ptr); return rv; } #endif freebsd_smart_device::~freebsd_smart_device() throw() { if (m_fd >= 0) os_freebsd::freebsd_smart_device::close(); } // migration from the old_style unsigned char m_controller_type; unsigned char m_controller_port; // examples for smartctl static const char smartctl_examples[] = "=================================================== SMARTCTL EXAMPLES =====\n\n" " 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" " smartctl -a --device=3ware,2 /dev/tws0\n" " (Prints all SMART information for ATA disk on\n" " third port of first 3ware RAID controller)\n" " smartctl -a --device=cciss,0 /dev/ciss0\n" " (Prints all SMART information for first disk \n" " on Common Interface for SCSI-3 Support driver)\n" " smartctl -a --device=areca,3/1 /dev/arcmsr0\n" " (Prints all SMART information for 3rd disk in the 1st enclosure \n" " on first ARECA RAID controller)\n" ; bool freebsd_smart_device::is_open() const { return (m_fd >= 0); } bool freebsd_smart_device::open() { const char *dev = get_dev_name(); if ((m_fd = ::open(dev,O_RDONLY))<0) { set_err(errno); return false; } return true; } bool freebsd_smart_device::close() { int failed = 0; // close device, if open if (is_open()) failed=::close(get_fd()); set_fd(-1); if(failed) return false; else return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement standard ATA support class freebsd_ata_device : public /*implements*/ ata_device, public /*extends*/ freebsd_smart_device { public: freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: virtual int do_cmd(struct ata_ioc_request* request, bool is_48bit_cmd); }; freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), freebsd_smart_device() { } int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { int fd = get_fd(), ret; ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST ret = ioctl(fd, IOCATAREQUEST, request); if (ret) set_err(errno); return ret; } bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { bool ata_48bit = false; // no ata_48bit_support via IOCATAREQUEST if(!strcmp("atacam",get_dev_type())) // enable for atacam interface ata_48bit = true; if (!ata_cmd_is_ok(in, true, // data_out_support true, // multi_sector_support ata_48bit) ) { set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); return false; } struct ata_ioc_request request; bzero(&request,sizeof(struct ata_ioc_request)); request.timeout=SCSI_TIMEOUT_DEFAULT; request.u.ata.command=in.in_regs.command; request.u.ata.feature=in.in_regs.features; request.u.ata.count = in.in_regs.sector_count_16; request.u.ata.lba = in.in_regs.lba_48; switch (in.direction) { case ata_cmd_in::no_data: request.flags=ATA_CMD_CONTROL; break; case ata_cmd_in::data_in: request.flags=ATA_CMD_READ | ATA_CMD_CONTROL; request.data=(char *)in.buffer; request.count=in.size; break; case ata_cmd_in::data_out: request.flags=ATA_CMD_WRITE | ATA_CMD_CONTROL; request.data=(char *)in.buffer; request.count=in.size; break; default: return set_err(ENOSYS); } clear_err(); errno = 0; if (do_cmd(&request, in.in_regs.is_48bit_cmd())) return false; if (request.error) return set_err(EIO, "request failed, error code 0x%02x", request.error); out.out_regs.error = request.error; out.out_regs.sector_count_16 = request.u.ata.count; out.out_regs.lba_48 = request.u.ata.lba; return true; } #if FREEBSDVER > 800100 class freebsd_atacam_device : public freebsd_ata_device { public: freebsd_atacam_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "atacam", req_type), freebsd_ata_device(intf, dev_name, req_type) {} virtual bool open(); virtual bool close(); protected: int m_fd; struct cam_device *m_camdev; virtual int do_cmd( struct ata_ioc_request* request , bool is_48bit_cmd); }; bool freebsd_atacam_device::open(){ const char *dev = get_dev_name(); if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { set_err(errno); return false; } set_fd(m_camdev->fd); return true; } bool freebsd_atacam_device::close(){ cam_close_device(m_camdev); set_fd(-1); return true; } int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { union ccb ccb; int camflags; // 48bit commands are broken in ATACAM before r242422/HEAD // and may cause system hang // First version with working support should be FreeBSD 9.2.0/RELEASE #if (FREEBSDVER < 902001) if(!strcmp("ata",m_camdev->sim_name) && is_48bit_cmd) { set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); return -1; } #endif memset(&ccb, 0, sizeof(ccb)); if (request->count == 0) camflags = CAM_DIR_NONE; else if (request->flags & ATA_CMD_READ) camflags = CAM_DIR_IN; else camflags = CAM_DIR_OUT; cam_fill_ataio(&ccb.ataio, 0, NULL, camflags, MSG_SIMPLE_Q_TAG, (u_int8_t*)request->data, request->count, request->timeout * 1000); // timeout in seconds ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT | (is_48bit_cmd ? CAM_ATAIO_48BIT : 0); // ata_28bit_cmd ccb.ataio.cmd.command = request->u.ata.command; ccb.ataio.cmd.features = request->u.ata.feature; ccb.ataio.cmd.lba_low = request->u.ata.lba; ccb.ataio.cmd.lba_mid = request->u.ata.lba >> 8; ccb.ataio.cmd.lba_high = request->u.ata.lba >> 16; // ata_48bit cmd ccb.ataio.cmd.lba_low_exp = request->u.ata.lba >> 24; ccb.ataio.cmd.lba_mid_exp = request->u.ata.lba >> 32; ccb.ataio.cmd.lba_high_exp = request->u.ata.lba >> 40; ccb.ataio.cmd.device = 0x40 | ((request->u.ata.lba >> 24) & 0x0f); ccb.ataio.cmd.sector_count = request->u.ata.count; ccb.ataio.cmd.sector_count_exp = request->u.ata.count >> 8;; ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(m_camdev, &ccb) < 0) { set_err(EIO, "cam_send_ccb failed"); return -1; } if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if(scsi_debugmode > 0) cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); set_err(EIO); return -1; } request->u.ata.lba = ((u_int64_t)(ccb.ataio.res.lba_low)) | ((u_int64_t)(ccb.ataio.res.lba_mid) << 8) | ((u_int64_t)(ccb.ataio.res.lba_high) << 16) | ((u_int64_t)(ccb.ataio.res.lba_low_exp) << 24) | ((u_int64_t)(ccb.ataio.res.lba_mid_exp) << 32) | ((u_int64_t)(ccb.ataio.res.lba_high_exp) << 40); request->u.ata.count = ccb.ataio.res.sector_count | (ccb.ataio.res.sector_count_exp << 8); request->error = ccb.ataio.res.error; return 0; } #endif ///////////////////////////////////////////////////////////////////////////// /// NVMe support class freebsd_nvme_device : public /*implements*/ nvme_device, public /*extends*/ freebsd_smart_device { public: freebsd_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); }; freebsd_nvme_device::freebsd_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid), freebsd_smart_device() { } bool freebsd_nvme_device::open() { const char *dev = get_dev_name(); if (!strnstr(dev, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) { set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'", NVME_CTRLR_PREFIX); return false; } int nsid = -1, ctrlid = -1; char tmp; if(sscanf(dev, NVME_CTRLR_PREFIX"%d%c", &ctrlid, &tmp) == 1) { if(ctrlid < 0) { set_err(EINVAL, "Invalid NVMe controller number"); return false; } nsid = 0xFFFFFFFF; // broadcast id } else if (sscanf(dev, NVME_CTRLR_PREFIX"%d" NVME_NS_PREFIX "%d%c", &ctrlid, &nsid, &tmp) == 2) { if(ctrlid < 0 || nsid < 0) { set_err(EINVAL, "Invalid NVMe controller/namespace number"); return false; } } else { set_err(EINVAL, "Invalid NVMe controller/namespace syntax"); return false; } // we should always open controller, not namespace device char full_path[64]; snprintf(full_path, sizeof(full_path), NVME_CTRLR_PREFIX"%d", ctrlid); int fd; if ((fd = ::open(full_path, O_RDWR))<0) { set_err(errno); return false; } set_fd(fd); if (!get_nsid()) { set_nsid(nsid); } return true; } bool freebsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { // nvme_passthru_cmd pt; struct nvme_pt_command pt; struct nvme_completion *cp_p; memset(&pt, 0, sizeof(pt)); #if __FreeBSD_version >= 1200058 && __FreeBSD_version < 1200081 pt.cmd.opc_fuse = NVME_CMD_SET_OPC(in.opcode); #else pt.cmd.opc = in.opcode; #endif pt.cmd.opc = in.opcode; pt.cmd.nsid = in.nsid; pt.buf = in.buffer; pt.len = in.size; pt.cmd.cdw10 = in.cdw10; pt.cmd.cdw11 = in.cdw11; pt.cmd.cdw12 = in.cdw12; pt.cmd.cdw13 = in.cdw13; pt.cmd.cdw14 = in.cdw14; pt.cmd.cdw15 = in.cdw15; pt.is_read = 1; // should we use in.direction()? int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt); if (status < 0) return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno)); cp_p = &pt.cpl; out.result=cp_p->cdw0; // Command specific result (DW0) if (nvme_completion_is_error(cp_p)) { /* ignore DNR and More bits */ uint16_t nvme_status = ((cp_p->status.sct << 8) | cp_p->status.sc) & 0x3ff; return set_nvme_err(out, nvme_status); } return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement AMCC/3ware RAID support class freebsd_escalade_device : public /*implements*/ ata_device, public /*extends*/ freebsd_smart_device { public: freebsd_escalade_device(smart_interface * intf, const char * dev_name, int escalade_type, int disknum); protected: virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); virtual bool open(); private: int m_escalade_type; ///< Type string for escalade_command_interface(). int m_disknum; ///< Disk number. }; freebsd_escalade_device::freebsd_escalade_device(smart_interface * intf, const char * dev_name, int escalade_type, int disknum) : smart_device(intf, dev_name, "3ware", "3ware"), freebsd_smart_device(), m_escalade_type(escalade_type), m_disknum(disknum) { set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum); } bool freebsd_escalade_device::open() { const char *dev = get_dev_name(); int fd; if ((fd = ::open(dev,O_RDWR))<0) { set_err(errno); return false; } set_fd(fd); return true; } bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { // to hold true file descriptor int fd = get_fd(); if (!ata_cmd_is_ok(in, true, // data_out_support false, // TODO: multi_sector_support true) // ata_48bit_support ) return false; struct twe_usercommand* cmd_twe = NULL; TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; TWE_Command_ATA* ata = NULL; // Used by both the SCSI and char interfaces char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; if (m_disknum < 0) { printwarning(NO_DISK_3WARE,NULL); return false; } memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; cmd_twa->driver_pkt.buffer_length = in.size; // using "old" packet format to speak with SATA devices ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { cmd_twe = (struct twe_usercommand*)ioctl_buffer; ata = &cmd_twe->tu_command.ata; } else { return set_err(ENOSYS, "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum); } ata->opcode = TWE_OP_ATA_PASSTHROUGH; // Same for (almost) all commands - but some reset below ata->request_id = 0xFF; ata->unit = m_disknum; ata->status = 0; ata->flags = 0x1; ata->size = 0x5; // TODO: multisector support // Set registers { const ata_in_regs_48bit & r = in.in_regs; ata->features = r.features_16; ata->sector_count = r.sector_count_16; ata->sector_num = r.lba_low_16; ata->cylinder_lo = r.lba_mid_16; ata->cylinder_hi = r.lba_high_16; ata->drive_head = r.device; ata->command = r.command; } // Is this a command that reads or returns 512 bytes? // passthru->param values are: // 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 bool readdata = false; if (in.direction == ata_cmd_in::data_in) { if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { cmd_twe->tu_data = in.buffer; cmd_twe->tu_size = 512; } readdata=true; ata->sgl_offset = 0x5; ata->param = 0xD; // 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 if (in.direction == ata_cmd_in::no_data) { // Non data command -- but doesn't use large sector // count register values. ata->sgl_offset = 0x0; ata->param = 0x8; ata->sector_count = 0x0; } else if (in.direction == ata_cmd_in::data_out) { ata->sgl_offset = 0x5; ata->param = 0xF; // PIO data write if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { cmd_twe->tu_data = in.buffer; cmd_twe->tu_size = 512; } else if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { memcpy(cmd_twa->pdata, in.buffer, in.size); } } else return set_err(EINVAL); // 3WARE controller can NOT have packet device internally if (in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) { return set_err(ENODEV, "No drive on port %d", m_disknum); } // Now send the command down through an ioctl() int ioctlreturn; if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); } else { ioctlreturn=ioctl(fd,TWEIO_COMMAND,cmd_twe); } // Deal with the different error cases if (ioctlreturn) { return set_err(EIO); } // 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)) { if (scsi_debugmode) pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags); return set_err(EIO); } // If this is a read data command, copy data to output buffer if (readdata) { if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) memcpy(in.buffer, cmd_twa->pdata, in.size); else if(m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { memcpy(in.buffer, cmd_twe->tu_data, in.size); // untested } } // Return register values if (ata) { ata_out_regs_48bit & r = out.out_regs; r.error = ata->features; r.sector_count_16 = ata->sector_count; r.lba_low_16 = ata->sector_num; r.lba_mid_16 = ata->cylinder_lo; r.lba_high_16 = ata->cylinder_hi; r.device = ata->drive_head; r.status = ata->command; } // look for nonexistent devices/ports if (in.in_regs.command == ATA_IDENTIFY_DEVICE && !nonempty((unsigned char *)in.buffer, in.size)) { return set_err(ENODEV, "No drive on port %d", m_disknum); } return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement Highpoint RAID support with old functions class freebsd_highpoint_device : public /*implements*/ ata_device_with_command_set, public /*extends*/ freebsd_smart_device { public: freebsd_highpoint_device(smart_interface * intf, const char * dev_name, unsigned char controller, unsigned char channel, unsigned char port); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); virtual bool open(); private: unsigned char m_hpt_data[3]; ///< controller/channel/port }; freebsd_highpoint_device::freebsd_highpoint_device(smart_interface * intf, const char * dev_name, unsigned char controller, unsigned char channel, unsigned char port) : smart_device(intf, dev_name, "hpt", "hpt"), freebsd_smart_device() { m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port; set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]); } bool freebsd_highpoint_device::open() { const char *dev = get_dev_name(); int fd; if ((fd = ::open(dev,O_RDWR))<0) { set_err(errno); return false; } set_fd(fd); return true; } int freebsd_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data) { int fd=get_fd(); int ids[2]; HPT_IOCTL_PARAM param; HPT_CHANNEL_INFO_V2 info; unsigned char* buff[512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)]; PHPT_PASS_THROUGH_HEADER pide_pt_hdr, pide_pt_hdr_out; // get internal deviceid ids[0] = m_hpt_data[0] - 1; ids[1] = m_hpt_data[1] - 1; memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); param.magic = HPT_IOCTL_MAGIC; param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO_V2; param.in = (unsigned char *)ids; param.in_size = sizeof(unsigned int) * 2; param.out = (unsigned char *)&info; param.out_size = sizeof(HPT_CHANNEL_INFO_V2); if (m_hpt_data[2]==1) { param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO; param.out_size = sizeof(HPT_CHANNEL_INFO); } if (ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0 || info.devices[m_hpt_data[2]-1]==0) { return -1; } // perform smart action memset(buff, 0, 512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)); pide_pt_hdr = (PHPT_PASS_THROUGH_HEADER)buff; pide_pt_hdr->lbamid = 0x4f; pide_pt_hdr->lbahigh = 0xc2; pide_pt_hdr->command = ATA_SMART_CMD; pide_pt_hdr->id = info.devices[m_hpt_data[2] - 1]; switch (command){ case READ_VALUES: pide_pt_hdr->feature=ATA_SMART_READ_VALUES; pide_pt_hdr->protocol=HPT_READ; break; case READ_THRESHOLDS: pide_pt_hdr->feature=ATA_SMART_READ_THRESHOLDS; pide_pt_hdr->protocol=HPT_READ; break; case READ_LOG: pide_pt_hdr->feature=ATA_SMART_READ_LOG_SECTOR; pide_pt_hdr->lbalow=select; pide_pt_hdr->protocol=HPT_READ; break; case IDENTIFY: pide_pt_hdr->command=ATA_IDENTIFY_DEVICE; pide_pt_hdr->protocol=HPT_READ; break; case ENABLE: pide_pt_hdr->feature=ATA_SMART_ENABLE; break; case DISABLE: pide_pt_hdr->feature=ATA_SMART_DISABLE; break; case AUTO_OFFLINE: pide_pt_hdr->feature=ATA_SMART_AUTO_OFFLINE; pide_pt_hdr->sectorcount=select; break; case AUTOSAVE: pide_pt_hdr->feature=ATA_SMART_AUTOSAVE; pide_pt_hdr->sectorcount=select; break; case IMMEDIATE_OFFLINE: pide_pt_hdr->feature=ATA_SMART_IMMEDIATE_OFFLINE; pide_pt_hdr->lbalow=select; break; case STATUS_CHECK: case STATUS: pide_pt_hdr->feature=ATA_SMART_STATUS; break; case CHECK_POWER_MODE: pide_pt_hdr->command=ATA_CHECK_POWER_MODE; break; case WRITE_LOG: memcpy(buff+sizeof(HPT_PASS_THROUGH_HEADER), data, 512); pide_pt_hdr->feature=ATA_SMART_WRITE_LOG_SECTOR; pide_pt_hdr->lbalow=select; pide_pt_hdr->protocol=HPT_WRITE; break; default: pout("Unrecognized command %d in highpoint_command_interface()\n" "Please contact " PACKAGE_BUGREPORT "\n", command); errno=ENOSYS; return -1; } if (pide_pt_hdr->protocol!=0) { pide_pt_hdr->sectors = 1; pide_pt_hdr->sectorcount = 1; } memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); param.magic = HPT_IOCTL_MAGIC; param.ctrl_code = HPT_IOCTL_IDE_PASS_THROUGH; param.in = (unsigned char *)buff; param.in_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? 0 : pide_pt_hdr->sectors * 512); param.out = (unsigned char *)buff+param.in_size; param.out_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? pide_pt_hdr->sectors * 512 : 0); pide_pt_hdr_out = (PHPT_PASS_THROUGH_HEADER)param.out; if ((ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0) || (pide_pt_hdr_out->command & 1)) { 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; high = pide_pt_hdr_out->lbahigh; low = pide_pt_hdr_out->lbamid; // 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]; snprintf(buf, sizeof(buf), "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", (int)pide_pt_hdr_out->command, (int)pide_pt_hdr_out->feature, (int)pide_pt_hdr_out->sectorcount, (int)pide_pt_hdr_out->lbalow, (int)pide_pt_hdr_out->lbamid, (int)pide_pt_hdr_out->lbahigh, (int)pide_pt_hdr_out->sectors); printwarning(BAD_SMART,buf); } else if (command==CHECK_POWER_MODE) data[0] = pide_pt_hdr_out->sectorcount & 0xff; else if (pide_pt_hdr->protocol==HPT_READ) memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), pide_pt_hdr->sectors * 512); return 0; } ///////////////////////////////////////////////////////////////////////////// /// Standard SCSI support class freebsd_scsi_device : public /*implements*/ scsi_device, public /*extends*/ freebsd_smart_device { public: freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual smart_device * autodetect_open(); virtual bool scsi_pass_through(scsi_cmnd_io * iop); virtual bool open(); virtual bool close(); private: struct cam_device *m_camdev; }; bool freebsd_scsi_device::open(){ const char *dev = get_dev_name(); if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { set_err(errno); return false; } set_fd(m_camdev->fd); return true; } bool freebsd_scsi_device::close(){ cam_close_device(m_camdev); set_fd(-1); return true; } freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "scsi", req_type), freebsd_smart_device(), m_camdev(0) { } bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { union ccb *ccb; if (scsi_debugmode) { 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 ((scsi_debugmode > 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("]\n"); } if(m_camdev==NULL) { if (scsi_debugmode) pout(" error: camdev=0!\n"); return set_err(ENOTTY); } if (!(ccb = cam_getccb(m_camdev))) { if (scsi_debugmode) pout(" error allocating ccb\n"); return set_err(ENOMEM); } // mfi SAT layer is known to be buggy if(!strcmp("mfi",m_camdev->sim_name)) { if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { // Controller does not return ATA output registers in SAT sense data if (iop->cmnd[2] & (1 << 5)) // chk_cond return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); } // SMART WRITE LOG SECTOR causing media errors if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) { if(!failuretest_permissive) return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); } } // clear out structure, except for header that was filled in for us bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); cam_fill_csio(&ccb->csio, /* retries */ 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, /* timeout (converted to seconds) */ iop->timeout*1000); memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); if (cam_send_ccb(m_camdev,ccb) < 0) { if (scsi_debugmode) { pout(" error sending SCSI ccb\n"); cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); } cam_freeccb(ccb); return set_err(EIO); } if (scsi_debugmode) { pout(" CAM status=0x%x, SCSI status=0x%x, resid=0x%x\n", ccb->ccb_h.status, ccb->csio.scsi_status, ccb->csio.resid); if ((scsi_debugmode > 1) && (DXFER_FROM_DEVICE == iop->dxfer_dir)) { int trunc, len; len = iop->dxfer_len - ccb->csio.resid; trunc = (len > 256) ? 1 : 0; if (len > 0) { pout(" Incoming data, len=%d%s:\n", len, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : len), 1); } else pout(" Incoming data trimmed to nothing by resid\n"); } } if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR)) { if (scsi_debugmode) cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); cam_freeccb(ccb); return set_err(EIO); } iop->resid = ccb->csio.resid; iop->scsi_status = ccb->csio.scsi_status; if (iop->sensep && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) { if (scsi_debugmode) pout(" sense_len=0x%x, sense_resid=0x%x\n", ccb->csio.sense_len, ccb->csio.sense_resid); iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid; /* Some SCSI controller device drivers miscalculate the sense_resid field so cap resp_sense_len on max_sense_len. */ if (iop->resp_sense_len > iop->max_sense_len) iop->resp_sense_len = iop->max_sense_len; if (iop->resp_sense_len > 0) { memcpy(iop->sensep, &(ccb->csio.sense_data), iop->resp_sense_len); if (scsi_debugmode) { if (scsi_debugmode > 1) { pout(" >>> Sense buffer, len=%zu:\n", iop->resp_sense_len); dStrHex(iop->sensep, iop->resp_sense_len, 1); } if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=0x%x: [desc] sense_key=0x%x asc=0x%x ascq=0x%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else pout(" status=0x%x: sense_key=0x%x asc=0x%x ascq=0x%x\n", iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12], iop->sensep[13]); } } else if (scsi_debugmode) pout(" status=0x%x\n", iop->scsi_status); } else if (scsi_debugmode) pout(" status=0x%x\n", iop->scsi_status); cam_freeccb(ccb); // mfip replacing PDT of the device so response does not make a sense // this sets PDT to 00h - direct-access block device if((!strcmp("mfi", m_camdev->sim_name) || !strcmp("mpt", m_camdev->sim_name)) && iop->cmnd[0] == INQUIRY) { if (scsi_debugmode) { pout(" device on %s controller, patching PDT\n", m_camdev->sim_name); } iop->dxferp[0] = iop->dxferp[0] & 0xe0; } return true; } ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support /////////////////////////////////////////////////////////////////// // SATA(ATA) device behind Areca RAID Controller class freebsd_areca_ata_device : public /*implements*/ areca_ata_device, public /*extends*/ freebsd_smart_device { public: freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; /////////////////////////////////////////////////////////////////// // SAS(SCSI) device behind Areca RAID Controller class freebsd_areca_scsi_device : public /*implements*/ areca_scsi_device, public /*extends*/ freebsd_smart_device { public: freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; // Areca RAID Controller(SATA Disk) freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), freebsd_smart_device() { set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } smart_device * freebsd_areca_ata_device::autodetect_open() { // autodetect device type int is_ata = arcmsr_get_dev_type(); if(is_ata < 0) { set_err(EIO); return this; } if(is_ata == 1) { // SATA device return this; } // SAS device smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); close(); delete this; newdev->open(); // TODO: Can possibly pass open fd return newdev.release(); } int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; if(!is_open()) { if(!open()){ } } ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); if (ioctlreturn) { // errors found return -1; } return ioctlreturn; } bool freebsd_areca_ata_device::arcmsr_lock() { return true; } bool freebsd_areca_ata_device::arcmsr_unlock() { return true; } // Areca RAID Controller(SAS Device) freebsd_areca_scsi_device::freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), freebsd_smart_device() { set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } smart_device * freebsd_areca_scsi_device::autodetect_open() { return this; } int freebsd_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; if(!is_open()) { if(!open()){ } } ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); if (ioctlreturn) { // errors found return -1; } return ioctlreturn; } bool freebsd_areca_scsi_device::arcmsr_lock() { return true; } bool freebsd_areca_scsi_device::arcmsr_unlock() { return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement CCISS RAID support with old functions class freebsd_cciss_device : public /*implements*/ scsi_device, public /*extends*/ freebsd_smart_device { public: freebsd_cciss_device(smart_interface * intf, const char * name, unsigned char disknum); virtual bool scsi_pass_through(scsi_cmnd_io * iop); virtual bool open(); private: unsigned char m_disknum; ///< Disk number. }; bool freebsd_cciss_device::open() { const char *dev = get_dev_name(); int fd; if ((fd = ::open(dev,O_RDWR))<0) { set_err(errno); return false; } set_fd(fd); return true; } freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, const char * dev_name, unsigned char disknum) : smart_device(intf, dev_name, "cciss", "cciss"), freebsd_smart_device(), m_disknum(disknum) { set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum); } bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; // not reached return true; } ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support smart_device * freebsd_scsi_device::autodetect_open() { // Open device if (!open()) return this; // No Autodetection if device type was specified by user if (*get_req_type()) return this; // The code below is based on smartd.cpp:SCSIFilterKnown() // Get INQUIRY unsigned char req_buff[64] = {0, }; int req_len = 36; if (scsiStdInquiry(this, 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(this, req_buff, req_len)) { // device doesn't like INQUIRY commands close(); set_err(EIO, "INQUIRY failed"); return this; } } int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) return this; // Use INQUIRY to detect type // 3ware ? if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4) || !strcmp("tws",m_camdev->sim_name) || !strcmp("twa",m_camdev->sim_name)) { close(); set_err(EINVAL, "3ware/LSI controller, please try adding '-d 3ware,N',\n" "you may need to replace %s with /dev/twaN, /dev/tweN or /dev/twsN", get_dev_name()); return this; } // SAT or USB, skip MFI controllers because of bugs { smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); if (newdev) { // NOTE: 'this' is now owned by '*newdev' if(!strcmp("mfi",m_camdev->sim_name)) { newdev->close(); newdev->set_err(ENOSYS, "SATA device detected,\n" "MegaRAID SAT layer is reportedly buggy, use '-d sat' to try anyhow"); } return newdev; } } // Nothing special found return this; } ///////////////////////////////////////////////////////////////////////////// /// Implement platform interface with old functions. class freebsd_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_os_version_str(); virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); #if FREEBSDVER > 800100 virtual ata_device * get_atacam_device(const char * name, const char * type); #endif virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); virtual std::string get_valid_custom_dev_types_str(); private: bool get_nvme_devlist(smart_device_list & devlist, const char * type); }; ////////////////////////////////////////////////////////////////////// std::string freebsd_smart_interface::get_os_version_str() { struct utsname osname; uname(&osname); return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); } std::string freebsd_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) return smartctl_examples; return ""; } ata_device * freebsd_smart_interface::get_ata_device(const char * name, const char * type) { return new freebsd_ata_device(this, name, type); } #if FREEBSDVER > 800100 ata_device * freebsd_smart_interface::get_atacam_device(const char * name, const char * type) { return new freebsd_atacam_device(this, name, type); } #endif scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type) { return new freebsd_scsi_device(this, name, type); } nvme_device * freebsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) { return new freebsd_nvme_device(this, name, type, nsid); } // we are using CAM subsystem XPT enumerator to found all CAM (scsi/usb/ada/...) // devices on system despite of it's names // // If any errors occur, leave errno set as it was returned by the // system call, and return <0. // // arguments: // names: resulting array // show_all - export duplicate device name or not // // Return values: // -1: error // >=0: number of discovered devices bool get_dev_names_cam(std::vector & names, bool show_all) { int fd; if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { if (errno == ENOENT) /* There are no CAM device on this computer */ return 0; int serrno = errno; pout("%s control device couldn't opened: %s\n", XPT_DEVICE, strerror(errno)); errno = serrno; return false; } union ccb ccb; bzero(&ccb, sizeof(union ccb)); ccb.ccb_h.path_id = CAM_XPT_PATH_ID; ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; ccb.ccb_h.func_code = XPT_DEV_MATCH; int bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV; ccb.cdm.match_buf_len = bufsize; // TODO: Use local buffer instead of malloc() if possible ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); bzero(ccb.cdm.matches,bufsize); // clear ccb.cdm.matches structure if (ccb.cdm.matches == NULL) { close(fd); throw std::bad_alloc(); } ccb.cdm.num_matches = 0; ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; /* * We do the ioctl multiple times if necessary, in case there are * more than MAX_NUM_DEV nodes in the EDT. */ int skip_device = 0, skip_bus = 0, changed = 0; // TODO: bool std::string devname; do { if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { int serrno = errno; pout("error sending CAMIOCOMMAND ioctl: %s\n", strerror(errno)); free(ccb.cdm.matches); close(fd); errno = serrno; return false; } if ((ccb.ccb_h.status != CAM_REQ_CMP) || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { pout("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); free(ccb.cdm.matches); close(fd); errno = ENXIO; return false; } for (unsigned i = 0; i < ccb.cdm.num_matches; i++) { struct device_match_result *dev_result; struct periph_match_result *periph_result; if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) { struct bus_match_result *bus_result; bus_result = &ccb.cdm.matches[i].result.bus_result; if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ skip_bus = 1; else skip_bus = 0; changed = 1; } else if (ccb.cdm.matches[i].type == DEV_MATCH_DEVICE) { dev_result = &ccb.cdm.matches[i].result.device_result; if (dev_result->flags & DEV_RESULT_UNCONFIGURED || skip_bus == 1) skip_device = 1; else skip_device = 0; // /* Shall we skip non T_DIRECT devices ? */ // if (dev_result->inq_data.device != T_DIRECT) // skip_device = 1; changed = 1; } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && (skip_device == 0 || show_all)) { /* One device may be populated as many peripherals (pass0 & da0 for example). * We are searching for best name */ periph_result = &ccb.cdm.matches[i].result.periph_result; /* Prefer non-"pass" names */ if (devname.empty() || strncmp(periph_result->periph_name, "pass", 4) != 0) { devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); } changed = 0; }; if ((changed == 1 || show_all) && !devname.empty()) { names.push_back(devname); devname.erase(); changed = 0; }; } } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); if (!devname.empty()) names.push_back(devname); free(ccb.cdm.matches); close(fd); return true; } // we are using ATA subsystem enumerator to found all ATA devices on system // despite of it's names // // If any errors occur, leave errno set as it was returned by the // system call, and return <0. // Return values: // -1: error // >=0: number of discovered devices int get_dev_names_ata(char*** names) { struct ata_ioc_devices devices; int fd=-1,maxchannel,serrno=-1,n=0; char **mp = NULL; *names=NULL; if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) { if (errno == ENOENT) /* There are no ATA device on this computer */ return 0; serrno = errno; pout("%s control device can't be opened: %s\n", ATA_DEVICE, strerror(errno)); n = -1; goto end; }; if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) { serrno = errno; pout("ioctl(IOCATAGMAXCHANNEL) on /dev/ata failed: %s\n", strerror(errno)); n = -1; goto end; }; // allocate space for up to MAX_NUM_DEV number of ATA devices mp = (char **)calloc(MAX_NUM_DEV, sizeof(char*)); if (mp == NULL) { serrno=errno; pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); n = -1; goto end; }; for (devices.channel = 0; devices.channel < maxchannel && n < MAX_NUM_DEV; devices.channel++) { int j; if (ioctl(fd, IOCATADEVICES, &devices) < 0) { if (errno == ENXIO) continue; /* such channel not exist */ pout("ioctl(IOCATADEVICES) on %s channel %d failed: %s\n", ATA_DEVICE, devices.channel, strerror(errno)); n = -1; goto end; }; for (j=0;j<=1 && n=0) close(fd); if (n <= 0) { free(mp); mp = NULL; } *names=mp; if (serrno>-1) errno=serrno; return n; } bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /*= 0*/) { if (pattern) { set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); return false; } #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL bool scan_nvme = !type || !strcmp(type, "nvme"); #else bool scan_nvme = type && !strcmp(type, "nvme"); #endif // Make namelists char * * atanames = 0; int numata = 0; if (!type || !strcmp(type, "ata")) { numata = get_dev_names_ata(&atanames); if (numata < 0) { set_err(ENOMEM); return false; } } std::vector scsinames; if (!type || !strcmp(type, "scsi")) { // do not export duplicated names if (!get_dev_names_cam(scsinames, false)) { set_err(errno); return false; } } // Add to devlist int i; if (type==NULL) type=""; for (i = 0; i < numata; i++) { ata_device * atadev = get_ata_device(atanames[i], type); if (atadev) devlist.push_back(atadev); free(atanames[i]); } if(numata) free(atanames); for (i = 0; i < (int)scsinames.size(); i++) { if(!*type) { // try USB autodetection if no type specified smart_device * smartdev = autodetect_smart_device(scsinames[i].c_str()); if(smartdev) devlist.push_back(smartdev); } else { scsi_device * scsidev = get_scsi_device(scsinames[i].c_str(), type); if (scsidev) devlist.push_back(scsidev); } } if (scan_nvme) get_nvme_devlist(devlist, type); return true; } bool freebsd_smart_interface::get_nvme_devlist(smart_device_list & devlist, const char * type) { char ctrlpath[64]; for (int ctrlr = 0;; ctrlr++) { sprintf(ctrlpath, "%s%d", NVME_CTRLR_PREFIX, ctrlr); int fd = ::open(ctrlpath, O_RDWR); if (fd < 0) break; ::close(fd); nvme_device * nvmedev = get_nvme_device(ctrlpath, type, 0); if (nvmedev) devlist.push_back(nvmedev); else break; } return true; } #if (FREEBSDVER < 800000) // without this build fail on FreeBSD 8 static char done[USB_MAX_DEVICES]; static int usbdevinfo(int f, int a, int rec, int busno, unsigned short & vendor_id, unsigned short & product_id, unsigned short & version) { struct usb_device_info di; int e, p, i; char devname[256]; snprintf(devname, sizeof(devname),"umass%d",busno); di.udi_addr = a; e = ioctl(f, USB_DEVICEINFO, &di); if (e) { if (errno != ENXIO) printf("addr %d: I/O error\n", a); return 0; } done[a] = 1; // list devices for (i = 0; i < USB_MAX_DEVNAMES; i++) { if (di.udi_devnames[i][0]) { if(strcmp(di.udi_devnames[i],devname)==0) { // device found! vendor_id = di.udi_vendorNo; product_id = di.udi_productNo; version = di.udi_releaseNo; return 1; // FIXME } } } if (!rec) return 0; for (p = 0; p < di.udi_nports; p++) { int s = di.udi_ports[p]; if (s >= USB_MAX_DEVICES) { continue; } if (s == 0) printf("addr 0 should never happen!\n"); else { if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1; } } return 0; } #endif static int usbdevlist(int busno,unsigned short & vendor_id, unsigned short & product_id, unsigned short & version) { #if (FREEBSDVER >= 800000) // libusb2 interface struct libusb20_device *pdev = NULL; struct libusb20_backend *pbe; uint32_t matches = 0; char buf[128]; // do not change! char devname[128]; uint8_t n; struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; pbe = libusb20_be_alloc_default(); while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { matches++; if (libusb20_dev_open(pdev, 0)) { warnx("libusb20_dev_open: could not open device"); return 0; } pdesc=libusb20_dev_get_device_desc(pdev); snprintf(devname, sizeof(devname),"umass%d:",busno); for (n = 0; n != 255; n++) { if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf))) break; if (buf[0] == 0) continue; if(strncmp(buf,devname,strlen(devname))==0){ // found! vendor_id = pdesc->idVendor; product_id = pdesc->idProduct; version = pdesc->bcdDevice; libusb20_dev_close(pdev); libusb20_be_free(pbe); return 1; } } libusb20_dev_close(pdev); } if (matches == 0) { printf("No device match or lack of permissions.\n"); } libusb20_be_free(pbe); return false; #else // freebsd < 8.0 USB stack, ioctl interface int i, a, rc; char buf[50]; int ncont; for (ncont = 0, i = 0; i < 10; i++) { snprintf(buf, sizeof(buf), "%s%d", USBDEV, i); int f = open(buf, O_RDONLY); if (f >= 0) { memset(done, 0, sizeof done); for (a = 1; a < USB_MAX_DEVICES; a++) { if (!done[a]) { rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version); if(rc) return 1; } } close(f); } else { if (errno == ENOENT || errno == ENXIO) continue; warn("%s", buf); } ncont++; } return 0; #endif } smart_device * freebsd_smart_interface::autodetect_smart_device(const char * name) { unsigned short vendor_id = 0, product_id = 0, version = 0; struct cam_device *cam_dev; union ccb ccb; int bus=-1; int i; const char * test_name = name; memset(&ccb, 0, sizeof(ccb)); // if dev_name null, or string length zero if (!name || !*name) return 0; // Dereference symlinks struct stat st; std::string pathbuf; if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { char * p = realpath(name, (char *)0); if (p) { pathbuf = p; free(p); test_name = pathbuf.c_str(); } } // check ATA bus char * * atanames = 0; int numata = 0; numata = get_dev_names_ata(&atanames); if (numata > 0) { // check ATA/ATAPI devices for (i = 0; i < numata; i++) { if(!strcmp(atanames[i],test_name)) { for (int c = i; c < numata; c++) free(atanames[c]); free(atanames); return new freebsd_ata_device(this, test_name, ""); } else free(atanames[i]); } if(numata) free(atanames); } else { if (numata < 0) pout("Unable to get ATA device list\n"); } // check CAM std::vector scsinames; if (!get_dev_names_cam(scsinames, true)) pout("Unable to get CAM device list\n"); else if (!scsinames.empty()) { // check all devices on CAM bus for (i = 0; i < (int)scsinames.size(); i++) { if(strcmp(scsinames[i].c_str(), test_name)==0) { // our disk device is CAM if(strncmp(scsinames[i].c_str(), "/dev/pmp", strlen("/dev/pmp")) == 0) { pout("Skipping port multiplier [%s]\n", scsinames[i].c_str()); set_err(EINVAL); return 0; } if ((cam_dev = cam_open_device(test_name, O_RDWR)) == NULL) { // open failure set_err(errno); return 0; } // zero the payload bzero(&(&ccb.ccb_h)[1], PATHINQ_SETTINGS_SIZE); ccb.ccb_h.func_code = XPT_PATH_INQ; // send PATH_INQ to the device if (ioctl(cam_dev->fd, CAMIOCOMMAND, &ccb) == -1) { warn("Get Transfer Settings CCB failed\n" "%s", strerror(errno)); cam_close_device(cam_dev); return 0; } // now check if we are working with USB device, see umass.c if(strcmp(ccb.cpi.dev_name,"umass-sim") == 0) { // USB device found usbdevlist(bus,vendor_id, product_id, version); int bus=ccb.cpi.unit_number; // unit_number will match umass number cam_close_device(cam_dev); if(usbdevlist(bus,vendor_id, product_id, version)){ const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); if (usbtype) return get_scsi_passthrough_device(usbtype, new freebsd_scsi_device(this, test_name, "")); } return 0; } #if FREEBSDVER > 800100 // check if we have ATA device connected to CAM (ada) if(ccb.cpi.protocol == PROTO_ATA){ cam_close_device(cam_dev); return new freebsd_atacam_device(this, test_name, ""); } #endif // close cam device, we don`t need it anymore cam_close_device(cam_dev); // handle as usual scsi return new freebsd_scsi_device(this, test_name, ""); } } } // device is LSI raid supported by mfi driver if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid"))) set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information"); // form /dev/nvme* or nvme* if(!strncmp("/dev/nvme", test_name, strlen("/dev/nvme"))) return new freebsd_nvme_device(this, name, "", 0 /* use default nsid */); if(!strncmp("/dev/nvd", test_name, strlen("/dev/nvd"))) set_err(EINVAL, "To monitor NVMe disks use /dev/nvme* device names"); // device type unknown return 0; } smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type) { int disknum = -1, n1 = -1, n2 = -1; if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { // 3Ware ? static const char * fbsd_dev_twe_ctrl = "/dev/twe"; static const char * fbsd_dev_twa_ctrl = "/dev/twa"; static const char * fbsd_dev_tws_ctrl = "/dev/tws"; int contr = -1; if (n2 != (int)strlen(type)) { set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer"); return 0; } if (!(0 <= disknum && disknum <= 127)) { set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } // guess 3ware device type based on device name if (str_starts_with(name, fbsd_dev_twa_ctrl) || str_starts_with(name, fbsd_dev_tws_ctrl) ) { contr=CONTROLLER_3WARE_9000_CHAR; } if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){ contr=CONTROLLER_3WARE_678K_CHAR; } if(contr == -1){ set_err(EINVAL, "3ware controller type unknown, use %sX, %sX or %sX devices", fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl, fbsd_dev_tws_ctrl); return 0; } return new freebsd_escalade_device(this, name, contr, disknum); } // Highpoint ? int controller = -1, channel = -1; disknum = 1; n1 = n2 = -1; int n3 = -1; if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) { int len = strlen(type); if (!(n2 == len || n3 == len)) { set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items"); return 0; } if (!(1 <= controller && controller <= 8)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied"); return 0; } if (!(1 <= channel && channel <= 128)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); return 0; } if (!(1 <= disknum && disknum <= 15)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied"); return 0; } return new freebsd_highpoint_device(this, name, controller, channel, disknum); } // CCISS ? disknum = n1 = n2 = -1; if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { if (n2 != (int)strlen(type)) { set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); return 0; } if (!(0 <= disknum && disknum <= 127)) { set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } return get_sat_device("sat,auto", new freebsd_cciss_device(this, name, disknum)); } #if FREEBSDVER > 800100 // adaX devices ? if(!strcmp(type,"atacam")) return new freebsd_atacam_device(this, name, ""); #endif // Areca? disknum = n1 = n2 = -1; int encnum = 1; if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { if (!(1 <= disknum && disknum <= 128)) { set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); return 0; } if (!(1 <= encnum && encnum <= 8)) { set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } return new freebsd_areca_ata_device(this, name, disknum, encnum); } return 0; } std::string freebsd_smart_interface::get_valid_custom_dev_types_str() { return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E" #if FREEBSDVER > 800100 ", atacam" #endif ; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static os_freebsd::freebsd_smart_interface the_interface; smart_interface::set(&the_interface); } smartmontools-7.0/os_freebsd.h0000644000175000010010000003753713336335341013516 00000000000000/* * os_freebsd.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 Eduard Martinescu * * SPDX-License-Identifier: GPL-2.0-or-later */ /*- * 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 4760 2018-08-19 18:45:53Z chrfranke $" #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; 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 #define HPT_CTL_CODE(x) (x+0xFF00) #define HPT_IOCTL_GET_CHANNEL_INFO HPT_CTL_CODE(3) #define HPT_IOCTL_GET_CHANNEL_INFO_V2 HPT_CTL_CODE(53) #define HPT_IOCTL_IDE_PASS_THROUGH HPT_CTL_CODE(24) #define HPT_READ 1 #define HPT_WRITE 2 #define HPT_IOCTL_MAGIC 0xA1B2C3D4 #define MAXDEV_PER_CHANNEL 2 #define PMPORT_PER_CHANNEL 15 /* max devices connected to this channel via pmport */ #pragma pack(1) typedef struct _HPT_CHANNEL_INFO { unsigned int reserve1; unsigned int reserve2; unsigned int devices[MAXDEV_PER_CHANNEL]; } HPT_CHANNEL_INFO, *PHPT_CHANNEL_INFO; typedef struct _HPT_CHANNEL_INFO_V2 { unsigned int reserve1; unsigned int reserve2; unsigned int devices[PMPORT_PER_CHANNEL]; } HPT_CHANNEL_INFO_V2, *PHPT_CHANNEL_INFO_V2; typedef struct _HPT_IOCTL_PARAM { unsigned int magic; /* used to check if it's a valid ioctl packet */ unsigned int ctrl_code; /* operation control code */ void* in; /* input data buffer */ unsigned int in_size; /* size of input data buffer */ void* out; /* output data buffer */ unsigned int out_size; /* size of output data buffer */ void* returned_size; /* count of chars returned */ } HPT_IOCTL_PARAM, *PHPT_IOCTL_PARAM; #define HPT_DO_IOCONTROL _IOW('H', 0, HPT_IOCTL_PARAM) typedef struct _HPT_PASS_THROUGH_HEADER { unsigned int id; /* disk ID */ unsigned char feature; unsigned char sectorcount; unsigned char lbalow; unsigned char lbamid; unsigned char lbahigh; unsigned char driverhead; unsigned char command; unsigned char sectors; /* data size in sectors, if the command has data transfer */ unsigned char protocol; /* HPT_(READ,WRITE) or zero for non-DATA */ unsigned char reserve[3]; } HPT_PASS_THROUGH_HEADER, *PHPT_PASS_THROUGH_HEADER; #pragma pack() #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif #endif /* OS_FREEBSD_H_ */ smartmontools-7.0/os_generic.cpp0000644000175000010010000001477413401001476014041 00000000000000/* * os_generic.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) YEAR YOUR_NAME * Copyright (C) 2003-8 Bruce Allen * Copyright (C) 2008-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ /* NOTE: The code in this file is only called when smartmontools has been compiled on an unrecognized/unsupported platform. This file can then serve as a "template" to make os_myOS.cpp if you wish to build support for that platform. PORTING NOTES AND COMMENTS -------------------------- To port smartmontools to the OS of your choice, please: [0] Contact smartmontools-support@listi.jpberlin.de to check that it's not already been done. [1] Make copies of os_generic.h and os_generic.cpp called os_myOS.h and os_myOS.cpp . [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] Now that you have a working build environment, you have to replace the 'stub' function calls provided in this file. Provide the functions defined in this file by fleshing out the skeletons below. [5] Contact smartmontools-support@listi.jpberlin.de 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 "atacmds.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_cpp_cvsid="$Id: os_generic.cpp 4842 2018-12-02 16:07:26Z chrfranke $" ATACMDS_H_CVSID CONFIG_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID; // This is here to prevent compiler warnings for unused arguments of // functions. #define ARGUSED(x) ((void)(x)) // 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). static void print_smartctl_examples(){ printf("=================================================== SMARTCTL EXAMPLES =====\n\n" " 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" ); return; } ///////////////////////////////////////////////////////////////////////////// namespace generic { // No need to publish anything, name provided for Doxygen class generic_smart_interface : public /*implements*/ smart_interface { public: #ifdef HAVE_GET_OS_VERSION_STR virtual const char * get_os_version_str(); #endif virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); virtual std::string get_valid_custom_dev_types_str(); }; ////////////////////////////////////////////////////////////////////// #ifdef HAVE_GET_OS_VERSION_STR /// Return build host and OS version as static string const char * generic_smart_interface::get_os_version_str() { return ::get_os_version_str(); } #endif std::string generic_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) ::print_smartctl_examples(); // this prints to stdout ... return ""; // ... so don't print again. } // Return ATA device object for the given device name or NULL // the type is always set to "ata" ata_device * generic_smart_interface::get_ata_device(const char * name, const char * type) { ARGUSED(name); ARGUSED(type); return NULL; } // Return SCSI device object for the given device name or NULL // the type is always set to "scsi" scsi_device * generic_smart_interface::get_scsi_device(const char * name, const char * type) { ARGUSED(name); ARGUSED(type); return NULL; } // Return device object for the given device name (autodetect the device type) smart_device * generic_smart_interface::autodetect_smart_device(const char * name) { ARGUSED(name); // for the given name return the appropriate device type return NULL; } // Fill devlist with all OS's disk devices of given type that match the pattern bool generic_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /*= 0*/) { ARGUSED(devlist); ARGUSED(type); ARGUSED(pattern); return false; } // Return device object of the given type with specified name or NULL smart_device * generic_smart_interface::get_custom_smart_device(const char * name, const char * type) { ARGUSED(name); ARGUSED(type); return NULL; } std::string generic_smart_interface::get_valid_custom_dev_types_str() { return ""; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static generic::generic_smart_interface the_interface; smart_interface::set(&the_interface); } smartmontools-7.0/os_generic.h0000644000175000010010000000124613336335341013504 00000000000000/* * os_generic.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) YEAR YOUR_NAME * Copyright (C) 2003-8 Bruce Allen * * SPDX-License-Identifier: GPL-2.0-or-later */ // 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 4760 2018-08-19 18:45:53Z chrfranke $\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_ */ smartmontools-7.0/os_linux.cpp0000644000175000010010000033163313404017335013564 00000000000000/* * os_linux.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-11 Bruce Allen * Copyright (C) 2003-11 Doug Gilbert * Copyright (C) 2008-18 Christian Franke * * Original AACRaid code: * Copyright (C) 2014 Raghava Aditya * * Original Areca code: * Copyright (C) 2008-12 Hank Wu * Copyright (C) 2008 Oliver Bock * * Original MegaRAID code: * Copyright (C) 2008 Jordan Hargrave * * 3ware code was 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 compatibility 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 * * SPDX-License-Identifier: GPL-2.0-or-later */ // 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for offsetof() #include #include #include #ifdef HAVE_SYS_SYSMACROS_H // glibc 2.25: The inclusion of by is // deprecated. A warning is printed if major(), minor() or makedev() // is used but is not included. #include #endif #ifdef HAVE_LIBSELINUX #include #endif #include "atacmds.h" #include "os_linux.h" #include "scsicmds.h" #include "utility.h" #include "cciss.h" #include "megaraid.h" #include "aacraid.h" #include "nvmecmds.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" #include "dev_areca.h" // "include/uapi/linux/nvme_ioctl.h" from Linux kernel sources #include "linux_nvme_ioctl.h" // nvme_passthru_cmd, NVME_IOCTL_ADMIN_CMD #ifndef ENOTSUP #define ENOTSUP ENOSYS #endif #define ARGUSED(x) ((void)(x)) const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 4854 2018-12-11 20:32:29Z chrfranke $" OS_LINUX_H_CVSID; extern unsigned char failuretest_permissive; namespace os_linux { // No need to publish anything, name provided for Doxygen ///////////////////////////////////////////////////////////////////////////// /// Shared open/close routines class linux_smart_device : virtual public /*implements*/ smart_device { public: explicit linux_smart_device(int flags, int retry_flags = -1) : smart_device(never_called), m_fd(-1), m_flags(flags), m_retry_flags(retry_flags) { } virtual ~linux_smart_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: /// Return filedesc for derived classes. int get_fd() const { return m_fd; } void set_fd(int fd) { m_fd = fd; } private: int m_fd; ///< filedesc, -1 if not open. int m_flags; ///< Flags for ::open() int m_retry_flags; ///< Flags to retry ::open(), -1 if no retry }; linux_smart_device::~linux_smart_device() throw() { if (m_fd >= 0) ::close(m_fd); } bool linux_smart_device::is_open() const { return (m_fd >= 0); } bool linux_smart_device::open() { m_fd = ::open(get_dev_name(), m_flags); if (m_fd < 0 && errno == EROFS && m_retry_flags != -1) // Retry m_fd = ::open(get_dev_name(), m_retry_flags); if (m_fd < 0) { if (errno == EBUSY && (m_flags & O_EXCL)) // device is locked return set_err(EBUSY, "The requested controller is used exclusively by another process!\n" "(e.g. smartctl or smartd)\n" "Please quit the impeding process or try again later..."); return set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); } if (m_fd >= 0) { // sets FD_CLOEXEC on the opened device file descriptor. The // descriptor is otherwise leaked to other applications (mail // sender) which may be considered a security risk and may result // in AVC messages on SELinux-enabled systems. if (-1 == fcntl(m_fd, F_SETFD, FD_CLOEXEC)) // TODO: Provide an error printing routine in class smart_interface pout("fcntl(set FD_CLOEXEC) failed, errno=%d [%s]\n", errno, strerror(errno)); } return true; } // equivalent to close(file descriptor) bool linux_smart_device::close() { int fd = m_fd; m_fd = -1; if (::close(fd) < 0) return set_err(errno); return true; } // examples for smartctl static const char smartctl_examples[] = "=================================================== SMARTCTL EXAMPLES =====\n\n" " smartctl --all /dev/sda (Prints all SMART information)\n\n" " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n" " (Enables SMART on first disk)\n\n" " smartctl --test=long /dev/sda (Executes extended disk self-test)\n\n" " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n" " (Prints Self-Test & Attribute errors)\n" " smartctl --all --device=3ware,2 /dev/sda\n" " smartctl --all --device=3ware,2 /dev/twe0\n" " smartctl --all --device=3ware,2 /dev/twa0\n" " smartctl --all --device=3ware,2 /dev/twl0\n" " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" " smartctl --all --device=hpt,1/1/3 /dev/sda\n" " (Prints all SMART info for the SATA disk attached to the 3rd PMPort\n" " of the 1st channel on the 1st HighPoint RAID controller)\n" " smartctl --all --device=areca,3/1 /dev/sg2\n" " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n" " on Areca RAID controller)\n" ; ///////////////////////////////////////////////////////////////////////////// /// Linux ATA support class linux_ata_device : public /*implements*/ ata_device_with_command_set, public /*extends*/ linux_smart_device { public: linux_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); }; linux_ata_device::linux_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), linux_smart_device(O_RDONLY | O_NONBLOCK) { } // 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" #define BUFFER_LENGTH (4+512) int linux_ata_device::ata_command_interface(smart_command_set command, int select, char * data) { unsigned char buff[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, 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: // NOTE: According to ATAPI 4 and UP, this command is obsolete // select == 241 for enable but no data transfer. Use TASK ioctl. buff[1]=ATA_SMART_AUTO_OFFLINE; buff[2]=select; break; case AUTOSAVE: // select == 248 for enable but no data transfer. Use TASK ioctl. buff[1]=ATA_SMART_AUTOSAVE; buff[2]=select; 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; 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 (ioctl(get_fd(), HDIO_DRIVE_TASKFILE, task)) { if (errno==EINVAL) pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASK_IOCTL set\n"); return -1; } return 0; } // There are two different types of ioctls(). The HDIO_DRIVE_TASK // one is this: if (command==STATUS_CHECK || command==AUTOSAVE || command==AUTO_OFFLINE){ // 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 (ioctl(get_fd(), HDIO_DRIVE_TASK, buff)) { if (errno==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("ST =0x%02x\n",(int)buff[0]); pout("ERR=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(get_fd(), 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(get_fd(), 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_DID_ERROR 0x7 /* Need to work around aacraid driver quirk */ #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_USE_DETECT 0 #define SG_IO_UNSUPP 1 #define SG_IO_USE_V3 3 #define SG_IO_USE_V4 4 static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, int sgio_ver); static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); static int sg_io_state = SG_IO_USE_DETECT; /* 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 sg_io_ver) { #ifndef SG_IO ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report); return -ENOTTY; #else /* we are filling structures for both versions, but using only one requested */ struct sg_io_hdr io_hdr_v3; struct sg_io_v4 io_hdr_v4; #ifdef SCSI_CDB_CHECK bool ok = is_scsi_cdb(iop->cmnd, iop->cmnd_len); if (! ok) { int n = iop->cmnd_len; const unsigned char * ucp = iop->cmnd; pout(">>>>>>>> %s: cdb seems invalid, opcode=0x%x, len=%d, cdb:\n", __func__, ((n > 0) ? ucp[0] : 0), n); if (n > 0) { if (n > 16) pout(" <>\n"); dStrHex((const uint8_t *)ucp, ((n > 16) ? 16 : n), 1); } } #endif if (report > 0) { int k, j; const unsigned char * ucp = iop->cmnd; const char * np; char buff[256]; const int sz = (int)sizeof(buff); pout(">>>> do_scsi_cmnd_io: sg_io_ver=%d\n", sg_io_ver); 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; 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 snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", buff); } memset(&io_hdr_v3, 0, sizeof(struct sg_io_hdr)); memset(&io_hdr_v4, 0, sizeof(struct sg_io_v4)); io_hdr_v3.interface_id = 'S'; io_hdr_v3.cmd_len = iop->cmnd_len; io_hdr_v3.mx_sb_len = iop->max_sense_len; io_hdr_v3.dxfer_len = iop->dxfer_len; io_hdr_v3.dxferp = iop->dxferp; io_hdr_v3.cmdp = iop->cmnd; io_hdr_v3.sbp = iop->sensep; /* sg_io_hdr interface timeout has millisecond units. Timeout of 0 defaults to 60 seconds. */ io_hdr_v3.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; io_hdr_v4.guard = 'Q'; io_hdr_v4.request_len = iop->cmnd_len; io_hdr_v4.request = __u64(iop->cmnd); io_hdr_v4.max_response_len = iop->max_sense_len; io_hdr_v4.response = __u64(iop->sensep); io_hdr_v4.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; // msec switch (iop->dxfer_dir) { case DXFER_NONE: io_hdr_v3.dxfer_direction = SG_DXFER_NONE; break; case DXFER_FROM_DEVICE: io_hdr_v3.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr_v4.din_xfer_len = iop->dxfer_len; io_hdr_v4.din_xferp = __u64(iop->dxferp); break; case DXFER_TO_DEVICE: io_hdr_v3.dxfer_direction = SG_DXFER_TO_DEV; io_hdr_v4.dout_xfer_len = iop->dxfer_len; io_hdr_v4.dout_xferp = __u64(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; void * io_hdr = NULL; switch (sg_io_ver) { case SG_IO_USE_V3: io_hdr = &io_hdr_v3; break; case SG_IO_USE_V4: io_hdr = &io_hdr_v4; break; default: // should never be reached errno = EOPNOTSUPP; return -errno; } if (ioctl(dev_fd, SG_IO, io_hdr) < 0) { if (report) pout(" SG_IO ioctl failed, errno=%d [%s], SG_IO_V%d\n", errno, strerror(errno), sg_io_ver); return -errno; } unsigned int sg_driver_status = 0, sg_transport_status = 0, sg_info = 0, sg_duration = 0; if (sg_io_ver == SG_IO_USE_V3) { iop->resid = io_hdr_v3.resid; iop->scsi_status = io_hdr_v3.status; sg_driver_status = io_hdr_v3.driver_status; sg_transport_status = io_hdr_v3.host_status; sg_info = io_hdr_v3.info; iop->resp_sense_len = io_hdr_v3.sb_len_wr; sg_duration = io_hdr_v3.duration; } if (sg_io_ver == SG_IO_USE_V4) { switch (iop->dxfer_dir) { case DXFER_NONE: iop->resid = 0; break; case DXFER_FROM_DEVICE: iop->resid = io_hdr_v4.din_resid; break; case DXFER_TO_DEVICE: iop->resid = io_hdr_v4.dout_resid; break; } iop->scsi_status = io_hdr_v4.device_status; sg_driver_status = io_hdr_v4.driver_status; sg_transport_status = io_hdr_v4.transport_status; sg_info = io_hdr_v4.info; iop->resp_sense_len = io_hdr_v4.response_len; sg_duration = io_hdr_v4.duration; } if (report > 0) { pout(" scsi_status=0x%x, sg_transport_status=0x%x, sg_driver_status=0x%x\n" " sg_info=0x%x sg_duration=%d milliseconds resid=%d\n", iop->scsi_status, sg_transport_status, sg_driver_status, sg_info, sg_duration, iop->resid); if (report > 1) { if (DXFER_FROM_DEVICE == iop->dxfer_dir) { int trunc, len; len = iop->dxfer_len - iop->resid; trunc = (len > 256) ? 1 : 0; if (len > 0) { pout(" Incoming data, len=%d%s:\n", len, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : len), 1); } else pout(" Incoming data trimmed to nothing by resid\n"); } } } if (sg_info & SG_INFO_CHECK) { /* error or warning */ int masked_driver_status = (LSCSI_DRIVER_MASK & sg_driver_status); if (0 != sg_transport_status) { if ((LSCSI_DID_NO_CONNECT == sg_transport_status) || (LSCSI_DID_BUS_BUSY == sg_transport_status) || (LSCSI_DID_TIME_OUT == sg_transport_status)) return -ETIMEDOUT; else /* Check for DID_ERROR - workaround for aacraid driver quirk */ if (LSCSI_DID_ERROR != sg_transport_status) { return -EIO; /* catch all if not DID_ERR */ } } 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; 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(iop->sensep, iop->resp_sense_len , 1); } } if (report) { if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status && iop->sensep) { if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else 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_t 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)) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; 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 snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", 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(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(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 */ static int do_normal_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_USE_DETECT: /* ignore report argument */ /* Try SG_IO V3 first */ if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, SG_IO_USE_V3))) { sg_io_state = SG_IO_USE_V3; return 0; } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res)) return res; /* wait until we see a device */ /* See if we can use SG_IO V4 * */ if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, SG_IO_USE_V4))) { sg_io_state = SG_IO_USE_V4; return 0; } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res)) return res; /* wait until we see a device */ /* fallback to the SCSI_IOCTL_SEND_COMMAND */ sg_io_state = SG_IO_UNSUPP; /* FALLTHRU */ case SG_IO_UNSUPP: /* deprecated SCSI_IOCTL_SEND_COMMAND ioctl */ return sisc_cmnd_io(dev_fd, iop, report); case SG_IO_USE_V3: case SG_IO_USE_V4: /* use SG_IO V3 or V4 ioctl, depending on availabiliy */ return sg_io_cmnd_io(dev_fd, iop, report, sg_io_state); default: pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); sg_io_state = SG_IO_USE_DETECT; return -EIO; /* report error and reset state */ } } // >>>>>> End of general SCSI specific linux code ///////////////////////////////////////////////////////////////////////////// /// Standard SCSI support class linux_scsi_device : public /*implements*/ scsi_device, public /*extends*/ linux_smart_device { public: linux_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning = false); virtual smart_device * autodetect_open(); virtual bool scsi_pass_through(scsi_cmnd_io * iop); private: bool m_scanning; ///< true if created within scan_smart_devices }; linux_scsi_device::linux_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning /*= false*/) : smart_device(intf, dev_name, "scsi", req_type), // If opened with O_RDWR, a SATA disk in standby mode // may spin-up after device close(). linux_smart_device(O_RDONLY | O_NONBLOCK), m_scanning(scanning) { } bool linux_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; } ///////////////////////////////////////////////////////////////////////////// /// PMC AacRAID support class linux_aacraid_device :public scsi_device, public /*extends */ linux_smart_device { public: linux_aacraid_device(smart_interface *intf, const char *dev_name, unsigned int host, unsigned int channel, unsigned int device); virtual ~linux_aacraid_device() throw(); virtual bool open(); virtual bool scsi_pass_through(scsi_cmnd_io *iop); private: //Device Host number int aHost; //Channel(Lun) of the device int aLun; //Id of the device int aId; }; linux_aacraid_device::linux_aacraid_device(smart_interface *intf, const char *dev_name, unsigned int host, unsigned int channel, unsigned int device) : smart_device(intf,dev_name,"aacraid","aacraid"), linux_smart_device(O_RDWR|O_NONBLOCK), aHost(host), aLun(channel), aId(device) { set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]",dev_name,aHost,aLun,aId); set_info().dev_type = strprintf("aacraid,%d,%d,%d",aHost,aLun,aId); } linux_aacraid_device::~linux_aacraid_device() throw() { } bool linux_aacraid_device::open() { //Create the character device name based on the host number //Required for get stats from disks connected to different controllers char dev_name[128]; snprintf(dev_name, sizeof(dev_name), "/dev/aac%d", aHost); //Initial open of dev name to check if it exsists int afd = ::open(dev_name,O_RDWR); if(afd < 0 && errno == ENOENT) { FILE *fp = fopen("/proc/devices","r"); if(NULL == fp) return set_err(errno,"cannot open /proc/devices:%s", strerror(errno)); char line[256]; int mjr = -1; while(fgets(line,sizeof(line),fp) !=NULL) { int nc = -1; if(sscanf(line,"%d aac%n",&mjr,&nc) == 1 && nc > 0 && '\n' == line[nc]) break; mjr = -1; } //work with /proc/devices is done fclose(fp); if (mjr < 0) return set_err(ENOENT, "aac entry not found in /proc/devices"); //Create misc device file in /dev/ used for communication with driver if(mknod(dev_name,S_IFCHR,makedev(mjr,aHost))) return set_err(errno,"cannot create %s:%s",dev_name,strerror(errno)); afd = ::open(dev_name,O_RDWR); } if(afd < 0) return set_err(errno,"cannot open %s:%s",dev_name,strerror(errno)); set_fd(afd); return true; } bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop) { int report = scsi_debugmode; 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; 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 snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", buff); } //return test commands if (iop->cmnd[0] == 0x00) return true; user_aac_reply *pReply; #ifdef ENVIRONMENT64 // Create user 64 bit request user_aac_srb64 *pSrb; uint8_t aBuff[sizeof(user_aac_srb64) + sizeof(user_aac_reply)] = {0,}; pSrb = (user_aac_srb64*)aBuff; pSrb->count = sizeof(user_aac_srb64) - sizeof(user_sgentry64); #elif defined(ENVIRONMENT32) //Create user 32 bit request user_aac_srb32 *pSrb; uint8_t aBuff[sizeof(user_aac_srb32) + sizeof(user_aac_reply)] = {0,}; pSrb = (user_aac_srb32*)aBuff; pSrb->count = sizeof(user_aac_srb32) - sizeof(user_sgentry32); #endif pSrb->function = SRB_FUNCTION_EXECUTE_SCSI; //channel is 0 always pSrb->channel = 0; pSrb->id = aId; pSrb->lun = aLun; pSrb->timeout = 0; pSrb->retry_limit = 0; pSrb->cdb_size = iop->cmnd_len; switch(iop->dxfer_dir) { case DXFER_NONE: pSrb->flags = SRB_NoDataXfer; break; case DXFER_FROM_DEVICE: pSrb->flags = SRB_DataIn; break; case DXFER_TO_DEVICE: pSrb->flags = SRB_DataOut; break; default: pout("aacraid: bad dxfer_dir\n"); return set_err(EINVAL, "aacraid: bad dxfer_dir\n"); } if(iop->dxfer_len > 0) { #ifdef ENVIRONMENT64 pSrb->sg64.count = 1; pSrb->sg64.sg64[0].addr64.lo32 = ((intptr_t)iop->dxferp) & 0x00000000ffffffff; pSrb->sg64.sg64[0].addr64.hi32 = ((intptr_t)iop->dxferp) >> 32; pSrb->sg64.sg64[0].length = (uint32_t)iop->dxfer_len; pSrb->count += pSrb->sg64.count * sizeof(user_sgentry64); #elif defined(ENVIRONMENT32) pSrb->sg32.count = 1; pSrb->sg32.sg32[0].addr32 = (intptr_t)iop->dxferp; pSrb->sg32.sg32[0].length = (uint32_t)iop->dxfer_len; pSrb->count += pSrb->sg32.count * sizeof(user_sgentry32); #endif } pReply = (user_aac_reply*)(aBuff+pSrb->count); memcpy(pSrb->cdb,iop->cmnd,iop->cmnd_len); int rc = 0; errno = 0; rc = ioctl(get_fd(),FSACTL_SEND_RAW_SRB,pSrb); if (rc != 0) return set_err(errno, "aacraid send_raw_srb: %d.%d = %s", aLun, aId, strerror(errno)); /* see kernel aacraid.h and MSDN SCSI_REQUEST_BLOCK documentation */ #define SRB_STATUS_SUCCESS 0x1 #define SRB_STATUS_ERROR 0x4 #define SRB_STATUS_NO_DEVICE 0x08 #define SRB_STATUS_SELECTION_TIMEOUT 0x0a #define SRB_STATUS_AUTOSENSE_VALID 0x80 iop->scsi_status = pReply->scsi_status; if (pReply->srb_status == (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR) && iop->scsi_status == SCSI_STATUS_CHECK_CONDITION) { memcpy(iop->sensep, pReply->sense_data, pReply->sense_data_size); iop->resp_sense_len = pReply->sense_data_size; return true; /* request completed with sense data */ } switch (pReply->srb_status & 0x3f) { case SRB_STATUS_SUCCESS: return true; /* request completed successfully */ case SRB_STATUS_NO_DEVICE: return set_err(EIO, "aacraid: Device %d %d does not exist", aLun, aId); case SRB_STATUS_SELECTION_TIMEOUT: return set_err(EIO, "aacraid: Device %d %d not responding", aLun, aId); default: return set_err(EIO, "aacraid result: %d.%d = 0x%x", aLun, aId, pReply->srb_status); } } ///////////////////////////////////////////////////////////////////////////// /// LSI MegaRAID support class linux_megaraid_device : public /* implements */ scsi_device, public /* extends */ linux_smart_device { public: linux_megaraid_device(smart_interface *intf, const char *name, unsigned int tgt); virtual ~linux_megaraid_device() throw(); virtual smart_device * autodetect_open(); virtual bool open(); virtual bool close(); virtual bool scsi_pass_through(scsi_cmnd_io *iop); private: unsigned int m_disknum; unsigned int m_hba; int m_fd; bool (linux_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data, int senseLen, void *sense, int report, int direction); bool megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, int senseLen, void *sense, int report, int direction); bool megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data, int senseLen, void *sense, int report, int direction); }; linux_megaraid_device::linux_megaraid_device(smart_interface *intf, const char *dev_name, unsigned int tgt) : smart_device(intf, dev_name, "megaraid", "megaraid"), linux_smart_device(O_RDWR | O_NONBLOCK), m_disknum(tgt), m_hba(0), m_fd(-1), pt_cmd(0) { set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum); set_info().dev_type = strprintf("megaraid,%d", tgt); } linux_megaraid_device::~linux_megaraid_device() throw() { if (m_fd >= 0) ::close(m_fd); } smart_device * linux_megaraid_device::autodetect_open() { int report = scsi_debugmode; // Open device if (!open()) return this; // The code below is based on smartd.cpp:SCSIFilterKnown() if (strcmp(get_req_type(), "megaraid")) return this; // Get INQUIRY unsigned char req_buff[64] = {0, }; int req_len = 36; if (scsiStdInquiry(this, req_buff, req_len)) { close(); set_err(EIO, "INQUIRY failed"); return this; } int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) return this; if (report) pout("Got MegaRAID inquiry.. %s\n", req_buff+8); // Use INQUIRY to detect type { // SAT? ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); if (newdev) // NOTE: 'this' is now owned by '*newdev' return newdev; } // Nothing special found return this; } bool linux_megaraid_device::open() { char line[128]; int mjr; int report = scsi_debugmode; if (sscanf(get_dev_name(), "/dev/bus/%u", &m_hba) == 0) { if (!linux_smart_device::open()) return false; /* Get device HBA */ struct sg_scsi_id sgid; if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) { m_hba = sgid.host_no; } else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) { int err = errno; linux_smart_device::close(); return set_err(err, "can't get bus number"); } // we don't need this device anymore linux_smart_device::close(); } /* Perform mknod of device ioctl node */ FILE * fp = fopen("/proc/devices", "r"); if (fp) { while (fgets(line, sizeof(line), fp) != NULL) { int n1 = 0; if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); if(report > 0) pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); if (n1 >= 0 || errno == EEXIST) break; } else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) { n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0)); if(report > 0) pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno); if (n1 >= 0 || errno == EEXIST) break; } } fclose(fp); } /* Open Device IOCTL node */ if ((m_fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) >= 0) { pt_cmd = &linux_megaraid_device::megasas_cmd; } else if ((m_fd = ::open("/dev/megadev0", O_RDWR)) >= 0) { pt_cmd = &linux_megaraid_device::megadev_cmd; } else { int err = errno; linux_smart_device::close(); return set_err(err, "cannot open /dev/megaraid_sas_ioctl_node or /dev/megadev0"); } set_fd(m_fd); return true; } bool linux_megaraid_device::close() { if (m_fd >= 0) ::close(m_fd); m_fd = -1; m_hba = 0; pt_cmd = 0; set_fd(m_fd); return true; } bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop) { int report = scsi_debugmode; 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; 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 snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", buff); } // Controller rejects Test Unit Ready if (iop->cmnd[0] == 0x00) return true; if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { // Controller does not return ATA output registers in SAT sense data if (iop->cmnd[2] & (1 << 5)) // chk_cond return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); } // SMART WRITE LOG SECTOR causing media errors if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 // SAT16 WRITE LOG && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 // SAT12 WRITE LOG && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) { if(!failuretest_permissive) return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); } if (pt_cmd == NULL) return false; return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, iop->dxfer_len, iop->dxferp, iop->max_sense_len, iop->sensep, report, iop->dxfer_dir); } /* Issue passthrough scsi command to PERC5/6 controllers */ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data, int /*senseLen*/, void * /*sense*/, int /*report*/, int dxfer_dir) { struct megasas_pthru_frame *pthru; struct megasas_iocpacket uio; memset(&uio, 0, sizeof(uio)); pthru = &uio.frame.pthru; pthru->cmd = MFI_CMD_PD_SCSI_IO; pthru->cmd_status = 0xFF; pthru->scsi_status = 0x0; pthru->target_id = m_disknum; pthru->lun = 0; pthru->cdb_len = cdbLen; pthru->timeout = 0; switch (dxfer_dir) { case DXFER_NONE: pthru->flags = MFI_FRAME_DIR_NONE; break; case DXFER_FROM_DEVICE: pthru->flags = MFI_FRAME_DIR_READ; break; case DXFER_TO_DEVICE: pthru->flags = MFI_FRAME_DIR_WRITE; break; default: pout("megasas_cmd: bad dxfer_dir\n"); return set_err(EINVAL, "megasas_cmd: bad dxfer_dir\n"); } if (dataLen > 0) { pthru->sge_count = 1; pthru->data_xfer_len = dataLen; pthru->sgl.sge32[0].phys_addr = (intptr_t)data; pthru->sgl.sge32[0].length = (uint32_t)dataLen; } memcpy(pthru->cdb, cdb, cdbLen); uio.host_no = m_hba; if (dataLen > 0) { uio.sge_count = 1; uio.sgl_off = offsetof(struct megasas_pthru_frame, sgl); uio.sgl[0].iov_base = data; uio.sgl[0].iov_len = dataLen; } errno = 0; int rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio); if (pthru->cmd_status || rc != 0) { if (pthru->cmd_status == 12) { return set_err(EIO, "megasas_cmd: Device %d does not exist\n", m_disknum); } return set_err((errno ? errno : EIO), "megasas_cmd result: %d.%d = %d/%d", m_hba, m_disknum, errno, pthru->cmd_status); } return true; } /* Issue passthrough scsi commands to PERC2/3/4 controllers */ bool linux_megaraid_device::megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data, int /*senseLen*/, void * /*sense*/, int /*report*/, int /* dir */) { struct uioctl_t uio; int rc; /* Don't issue to the controller */ if (m_disknum == 7) return false; memset(&uio, 0, sizeof(uio)); uio.inlen = dataLen; uio.outlen = dataLen; memset(data, 0, dataLen); uio.ui.fcs.opcode = 0x80; // M_RD_IOCTL_CMD uio.ui.fcs.adapno = MKADAP(m_hba); uio.data.pointer = (uint8_t *)data; uio.mbox.cmd = MEGA_MBOXCMD_PASSTHRU; uio.mbox.xferaddr = (intptr_t)&uio.pthru; uio.pthru.ars = 1; uio.pthru.timeout = 2; uio.pthru.channel = 0; uio.pthru.target = m_disknum; uio.pthru.cdblen = cdbLen; uio.pthru.reqsenselen = MAX_REQ_SENSE_LEN; uio.pthru.dataxferaddr = (intptr_t)data; uio.pthru.dataxferlen = dataLen; memcpy(uio.pthru.cdb, cdb, cdbLen); rc=ioctl(m_fd, MEGAIOCCMD, &uio); if (uio.pthru.scsistatus || rc != 0) { return set_err((errno ? errno : EIO), "megadev_cmd result: %d.%d = %d/%d", m_hba, m_disknum, errno, uio.pthru.scsistatus); } return true; } ///////////////////////////////////////////////////////////////////////////// /// CCISS RAID support #ifdef HAVE_LINUX_CCISS_IOCTL_H class linux_cciss_device : public /*implements*/ scsi_device, public /*extends*/ linux_smart_device { public: linux_cciss_device(smart_interface * intf, const char * name, unsigned char disknum); virtual bool scsi_pass_through(scsi_cmnd_io * iop); private: unsigned char m_disknum; ///< Disk number. }; linux_cciss_device::linux_cciss_device(smart_interface * intf, const char * dev_name, unsigned char disknum) : smart_device(intf, dev_name, "cciss", "cciss"), linux_smart_device(O_RDWR | O_NONBLOCK), m_disknum(disknum) { set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum); } bool linux_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) { int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; } #endif // HAVE_LINUX_CCISS_IOCTL_H ///////////////////////////////////////////////////////////////////////////// /// AMCC/3ware RAID support class linux_escalade_device : public /*implements*/ ata_device, public /*extends*/ linux_smart_device { public: enum escalade_type_t { AMCC_3WARE_678K, AMCC_3WARE_678K_CHAR, AMCC_3WARE_9000_CHAR, AMCC_3WARE_9700_CHAR }; linux_escalade_device(smart_interface * intf, const char * dev_name, escalade_type_t escalade_type, int disknum); virtual bool open(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); private: escalade_type_t m_escalade_type; ///< Controller type int m_disknum; ///< Disk number. }; linux_escalade_device::linux_escalade_device(smart_interface * intf, const char * dev_name, escalade_type_t escalade_type, int disknum) : smart_device(intf, dev_name, "3ware", "3ware"), linux_smart_device(O_RDONLY | O_NONBLOCK), m_escalade_type(escalade_type), m_disknum(disknum) { set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum); } /* 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 static int setup_3ware_nodes(const char *nodename, const 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; int retval = 0; #ifdef HAVE_LIBSELINUX security_context_t orig_context = NULL; security_context_t node_context = NULL; int selinux_enabled = is_selinux_enabled(); int selinux_enforced = security_getenforce(); #endif /* First try to open up /proc/devices */ if (!(file = fopen("/proc/devices", "r"))) { pout("Error opening /proc/devices to check/create 3ware device nodes\n"); 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; } #ifdef HAVE_LIBSELINUX /* Prepare a database of contexts for files in /dev * and save the current context */ if (selinux_enabled) { if (matchpathcon_init_prefix(NULL, "/dev") < 0) pout("Error initializing contexts database for /dev"); if (getfscreatecon(&orig_context) < 0) { pout("Error retrieving original SELinux fscreate context"); if (selinux_enforced) { matchpathcon_fini(); return 6; } } } #endif /* Now check if nodes are correct */ for (index=0; index<16; index++) { snprintf(nodestring, sizeof(nodestring), "/dev/%s%d", nodename, index); #ifdef HAVE_LIBSELINUX /* Get context of the node and set it as the default */ if (selinux_enabled) { if (matchpathcon(nodestring, S_IRUSR | S_IWUSR, &node_context) < 0) { pout("Could not retrieve context for %s", nodestring); if (selinux_enforced) { retval = 6; break; } } if (setfscreatecon(node_context) < 0) { pout ("Error setting default fscreate context"); if (selinux_enforced) { retval = 6; break; } } } #endif /* Try to stat the node */ if ((stat(nodestring, &stat_buf))) { pout("Node %s does not exist and must be created. Check the udev rules.\n", nodestring); /* 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"); retval = 3; break; } else { #ifdef HAVE_LIBSELINUX if (selinux_enabled && node_context) { freecon(node_context); node_context = NULL; } #endif continue; } } /* 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))) { pout("Node %s has wrong major/minor number and must be created anew." " Check the udev rules.\n", nodestring); /* Delete the old node */ if (unlink(nodestring)) { pout("problem unlinking stale 3ware device node %s", nodestring); syserror("unlink"); retval = 4; break; } /* Make a new node */ if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) { pout("problem creating 3ware device nodes %s", nodestring); syserror("mknod"); retval = 5; break; } } #ifdef HAVE_LIBSELINUX if (selinux_enabled && node_context) { freecon(node_context); node_context = NULL; } #endif } #ifdef HAVE_LIBSELINUX if (selinux_enabled) { if(setfscreatecon(orig_context) < 0) { pout("Error re-setting original fscreate context"); if (selinux_enforced) retval = 6; } if(orig_context) freecon(orig_context); if(node_context) freecon(node_context); matchpathcon_fini(); } #endif return retval; } bool linux_escalade_device::open() { if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR || m_escalade_type == AMCC_3WARE_678K_CHAR) { // the device nodes for these controllers are dynamically assigned, // so we need to check that they exist with the correct major // numbers and if not, create them const char * node = (m_escalade_type == AMCC_3WARE_9700_CHAR ? "twl" : m_escalade_type == AMCC_3WARE_9000_CHAR ? "twa" : "twe" ); const char * driver = (m_escalade_type == AMCC_3WARE_9700_CHAR ? "3w-sas" : m_escalade_type == AMCC_3WARE_9000_CHAR ? "3w-9xxx" : "3w-xxxx" ); if (setup_3ware_nodes(node, driver)) return set_err((errno ? errno : ENXIO), "setup_3ware_nodes(\"%s\", \"%s\") failed", node, driver); } // Continue with default open return linux_smart_device::open(); } // TODO: Function no longer useful //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) ) bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_ok(in, true, // data_out_support false, // TODO: multi_sector_support true) // ata_48bit_support ) return false; // 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); // TODO: Handle controller differences by different classes if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_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 (m_escalade_type==AMCC_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 (m_escalade_type==AMCC_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 { return set_err(ENOSYS, "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum); } // Same for (almost) all commands - but some reset below passthru->byte0.opcode = TW_OP_ATA_PASSTHRU; passthru->request_id = 0xFF; passthru->unit = m_disknum; passthru->status = 0; passthru->flags = 0x1; // Set registers { const ata_in_regs_48bit & r = in.in_regs; passthru->features = r.features_16; passthru->sector_count = r.sector_count_16; passthru->sector_num = r.lba_low_16; passthru->cylinder_lo = r.lba_mid_16; passthru->cylinder_hi = r.lba_high_16; passthru->drive_head = r.device; passthru->command = r.command; } // 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 bool readdata = false; if (in.direction == ata_cmd_in::data_in) { readdata=true; passthru->byte0.sgloff = 0x5; passthru->size = 0x7; // TODO: Other value for multi-sector ? passthru->param = 0xD; // 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 ((m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) && sizeof(long) == 8) passthru->size++; } else if (in.direction == ata_cmd_in::no_data) { // 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; } else if (in.direction == ata_cmd_in::data_out) { if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) memcpy(tw_ioctl_apache->data_buffer, in.buffer, in.size); else if (m_escalade_type == AMCC_3WARE_678K_CHAR) memcpy(tw_ioctl_char->data_buffer, in.buffer, in.size); else { // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE // memcpy(tw_output->output_data, data, 512); // printwarning(command); // TODO: Parameter no longer valid return set_err(ENOTSUP, "DATA OUT not supported for this 3ware controller type"); } passthru->byte0.sgloff = 0x5; passthru->size = 0x7; // TODO: Other value for multi-sector ? passthru->param = 0xF; // PIO data write if ((m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) && sizeof(long) == 8) passthru->size++; } else return set_err(EINVAL); // Now send the command down through an ioctl() int ioctlreturn; if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) ioctlreturn=ioctl(get_fd(), TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache); else if (m_escalade_type==AMCC_3WARE_678K_CHAR) ioctlreturn=ioctl(get_fd(), TW_CMD_PACKET_WITH_DATA, tw_ioctl_char); else ioctlreturn=ioctl(get_fd(), SCSI_IOCTL_SEND_COMMAND, tw_ioctl); // Deal with the different error cases if (ioctlreturn) { if (AMCC_3WARE_678K==m_escalade_type && in.in_regs.command==ATA_SMART_CMD && ( in.in_regs.features == ATA_SMART_AUTO_OFFLINE || in.in_regs.features == ATA_SMART_AUTOSAVE ) && in.in_regs.lba_low) { // error here is probably a kernel driver whose version is too old // printwarning(command); // TODO: Parameter no longer valid return set_err(ENOTSUP, "Probably kernel driver too old"); } return set_err(EIO); } // 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 (AMCC_3WARE_678K==m_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))) { return set_err(EIO); } // If this is a read data command, copy data to output buffer if (readdata) { if (m_escalade_type == AMCC_3WARE_9700_CHAR || m_escalade_type == AMCC_3WARE_9000_CHAR) memcpy(in.buffer, tw_ioctl_apache->data_buffer, in.size); else if (m_escalade_type==AMCC_3WARE_678K_CHAR) memcpy(in.buffer, tw_ioctl_char->data_buffer, in.size); else memcpy(in.buffer, tw_output->output_data, in.size); } // Return register values if (passthru) { ata_out_regs_48bit & r = out.out_regs; r.error = passthru->features; r.sector_count_16 = passthru->sector_count; r.lba_low_16 = passthru->sector_num; r.lba_mid_16 = passthru->cylinder_lo; r.lba_high_16 = passthru->cylinder_hi; r.device = passthru->drive_head; r.status = passthru->command; } // look for nonexistent devices/ports if ( in.in_regs.command == ATA_IDENTIFY_DEVICE && !nonempty(in.buffer, in.size)) { return set_err(ENODEV, "No drive on port %d", m_disknum); } return true; } ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support /////////////////////////////////////////////////////////////////// // SATA(ATA) device behind Areca RAID Controller class linux_areca_ata_device : public /*implements*/ areca_ata_device, public /*extends*/ linux_smart_device { public: linux_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; /////////////////////////////////////////////////////////////////// // SAS(SCSI) device behind Areca RAID Controller class linux_areca_scsi_device : public /*implements*/ areca_scsi_device, public /*extends*/ linux_smart_device { public: linux_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; // Looks in /proc/scsi to suggest correct areca devices static int find_areca_in_proc() { const char* proc_format_string="host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"; // check data formwat FILE *fp=fopen("/proc/scsi/sg/device_hdr", "r"); if (!fp) { pout("Unable to open /proc/scsi/sg/device_hdr for reading\n"); return 1; } // get line, compare to format char linebuf[256]; linebuf[255]='\0'; char *out = fgets(linebuf, 256, fp); fclose(fp); if (!out) { pout("Unable to read contents of /proc/scsi/sg/device_hdr\n"); return 2; } if (strcmp(linebuf, proc_format_string)) { // wrong format! // Fix this by comparing only tokens not white space!! pout("Unexpected format %s in /proc/scsi/sg/device_hdr\n", proc_format_string); return 3; } // Format is understood, now search for correct device fp=fopen("/proc/scsi/sg/devices", "r"); if (!fp) return 1; int host, chan, id, lun, type, opens, qdepth, busy, online; int dev=-1; int found=0; // search all lines of /proc/scsi/sg/devices while (9 == fscanf(fp, "%d %d %d %d %d %d %d %d %d", &host, &chan, &id, &lun, &type, &opens, &qdepth, &busy, &online)) { dev++; if (id == 16 && type == 3) { // devices with id=16 and type=3 might be Areca controllers pout("Device /dev/sg%d appears to be an Areca controller.\n", dev); found++; } } fclose(fp); return 0; } // Areca RAID Controller(SATA Disk) linux_areca_ata_device::linux_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK) { set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } smart_device * linux_areca_ata_device::autodetect_open() { // autodetect device type int is_ata = arcmsr_get_dev_type(); if(is_ata < 0) { set_err(EIO); return this; } if(is_ata == 1) { // SATA device return this; } // SAS device smart_device_auto_ptr newdev(new linux_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); close(); delete this; newdev->open(); // TODO: Can possibly pass open fd return newdev.release(); } int linux_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; if(!is_open()) { if(!open()){ find_areca_in_proc(); } } ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); if ( ioctlreturn || iop->scsi_status ) { // errors found return -1; } return ioctlreturn; } bool linux_areca_ata_device::arcmsr_lock() { return true; } bool linux_areca_ata_device::arcmsr_unlock() { return true; } // Areca RAID Controller(SAS Device) linux_areca_scsi_device::linux_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca"), linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK) { set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } smart_device * linux_areca_scsi_device::autodetect_open() { return this; } int linux_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; if(!is_open()) { if(!open()){ find_areca_in_proc(); } } ioctlreturn = do_normal_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); if ( ioctlreturn || iop->scsi_status ) { // errors found return -1; } return ioctlreturn; } bool linux_areca_scsi_device::arcmsr_lock() { return true; } bool linux_areca_scsi_device::arcmsr_unlock() { return true; } ///////////////////////////////////////////////////////////////////////////// /// Marvell support class linux_marvell_device : public /*implements*/ ata_device_with_command_set, public /*extends*/ linux_smart_device { public: linux_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); }; linux_marvell_device::linux_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "marvell", req_type), linux_smart_device(O_RDONLY | O_NONBLOCK) { } int linux_marvell_device::ata_command_interface(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); errno = EINVAL; return -1; } // 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(get_fd(), 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; } ///////////////////////////////////////////////////////////////////////////// /// Highpoint RAID support class linux_highpoint_device : public /*implements*/ ata_device_with_command_set, public /*extends*/ linux_smart_device { public: linux_highpoint_device(smart_interface * intf, const char * dev_name, unsigned char controller, unsigned char channel, unsigned char port); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); private: unsigned char m_hpt_data[3]; ///< controller/channel/port }; linux_highpoint_device::linux_highpoint_device(smart_interface * intf, const char * dev_name, unsigned char controller, unsigned char channel, unsigned char port) : smart_device(intf, dev_name, "hpt", "hpt"), linux_smart_device(O_RDONLY | O_NONBLOCK) { m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port; set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]); } // this implementation is derived from ata_command_interface with a header // packing for highpoint linux driver ioctl interface // // ioctl(fd,HPTIO_CTL,buff) // ^^^^^^^^^ // // structure of hpt_buff // +----+----+----+----+--------------------.....---------------------+ // | 1 | 2 | 3 | 4 | 5 | // +----+----+----+----+--------------------.....---------------------+ // // 1: The target controller [ int ( 4 Bytes ) ] // 2: The channel of the target controllee [ int ( 4 Bytes ) ] // 3: HDIO_ ioctl call [ int ( 4 Bytes ) ] // available from ${LINUX_KERNEL_SOURCE}/Documentation/ioctl/hdio // 4: the pmport that disk attached, [ int ( 4 Bytes ) ] // if no pmport device, set to 1 or leave blank // 5: data [ void * ( var leangth ) ] // #define STRANGE_BUFFER_LENGTH (4+512*0xf8) int linux_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data) { unsigned char hpt_buff[4*sizeof(int) + STRANGE_BUFFER_LENGTH]; unsigned int *hpt = (unsigned int *)hpt_buff; unsigned char *buff = &hpt_buff[4*sizeof(int)]; int copydata = 0; const int HDIO_DRIVE_CMD_OFFSET = 4; memset(hpt_buff, 0, 4*sizeof(int) + STRANGE_BUFFER_LENGTH); hpt[0] = m_hpt_data[0]; // controller id hpt[1] = m_hpt_data[1]; // channel number hpt[3] = m_hpt_data[2]; // pmport number 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: buff[2]=ATA_SMART_STATUS; break; case AUTO_OFFLINE: buff[2]=ATA_SMART_AUTO_OFFLINE; buff[3]=select; break; case AUTOSAVE: buff[2]=ATA_SMART_AUTOSAVE; buff[3]=select; break; case IMMEDIATE_OFFLINE: buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; buff[1]=select; break; case STATUS_CHECK: buff[1]=ATA_SMART_STATUS; break; default: pout("Unrecognized command %d in linux_highpoint_command_interface()\n" "Please contact " PACKAGE_BUGREPORT "\n", command); errno=ENOSYS; return -1; } if (command==WRITE_LOG) { unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512]; unsigned int *hpt_tf = (unsigned int *)task; ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]); task_struct_t *taskfile = (task_struct_t *)reqtask->io_ports; memset(task, 0, sizeof(task)); hpt_tf[0] = m_hpt_data[0]; // controller id hpt_tf[1] = m_hpt_data[1]; // channel number hpt_tf[3] = m_hpt_data[2]; // pmport number hpt_tf[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl 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; memcpy(task+sizeof(ide_task_request_t)+4*sizeof(int), data, 512); if (ioctl(get_fd(), HPTIO_CTL, task)) return -1; return 0; } if (command==STATUS_CHECK){ 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; hpt[2] = HDIO_DRIVE_TASK; if (ioctl(get_fd(), HPTIO_CTL, hpt_buff)) return -1; if (buff[4]==normal_lo && buff[5]==normal_hi) return 0; if (buff[4]==failed_lo && buff[5]==failed_hi) return 1; 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 if (command==IDENTIFY || command==PIDENTIFY) { unsigned char deviceid[4*sizeof(int)+512*sizeof(char)]; unsigned int *hpt_id = (unsigned int *)deviceid; hpt_id[0] = m_hpt_data[0]; // controller id hpt_id[1] = m_hpt_data[1]; // channel number hpt_id[3] = m_hpt_data[2]; // pmport number hpt_id[2] = HDIO_GET_IDENTITY; if (!ioctl(get_fd(), HPTIO_CTL, deviceid) && (deviceid[4*sizeof(int)] & 0x8000)) buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE; } #endif hpt[2] = HDIO_DRIVE_CMD; if ((ioctl(get_fd(), HPTIO_CTL, hpt_buff))) return -1; if (command==CHECK_POWER_MODE) buff[HDIO_DRIVE_CMD_OFFSET]=buff[2]; if (copydata) memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata); return 0; } #if 0 // TODO: Migrate from 'smart_command_set' to 'ata_in_regs' OR remove the function // Utility function for printing warnings void printwarning(smart_command_set command){ 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; } #endif ///////////////////////////////////////////////////////////////////////////// /// SCSI open with autodetection support smart_device * linux_scsi_device::autodetect_open() { // Open device if (!open()) return this; // No Autodetection if device type was specified by user bool sat_only = false; if (*get_req_type()) { // Detect SAT if device object was created by scan_smart_devices(). if (!(m_scanning && !strcmp(get_req_type(), "sat"))) return this; sat_only = true; } // The code below is based on smartd.cpp:SCSIFilterKnown() // Get INQUIRY unsigned char req_buff[64] = {0, }; int req_len = 36; if (scsiStdInquiry(this, 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(this, req_buff, req_len)) { // device doesn't like INQUIRY commands close(); set_err(EIO, "INQUIRY failed"); return this; } } int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) { if (sat_only) { close(); set_err(EIO, "INQUIRY too short for SAT"); } return this; } // Use INQUIRY to detect type if (!sat_only) { // 3ware ? if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) { close(); set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n" "you may need to replace %s with /dev/twlN, /dev/twaN or /dev/tweN", get_dev_name()); return this; } // DELL? if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8) || !memcmp(req_buff + 16, "PERC H700", 9) || !memcmp(req_buff + 8, "LSI\0",4) ) { close(); set_err(EINVAL, "DELL or MegaRaid controller, please try adding '-d megaraid,N'"); return this; } // Marvell ? if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) { //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name()); close(); smart_device_auto_ptr newdev( new linux_marvell_device(smi(), get_dev_name(), get_req_type()) ); newdev->open(); // TODO: Can possibly pass open fd delete this; return newdev.release(); } } // SAT or USB ? { smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); if (newdev) // NOTE: 'this' is now owned by '*newdev' return newdev; } // Nothing special found if (sat_only) { close(); set_err(EIO, "Not a SAT device"); } return this; } ///////////////////////////////////////////////////////////////////////////// /// NVMe support class linux_nvme_device : public /*implements*/ nvme_device, public /*extends*/ linux_smart_device { public: linux_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); }; linux_nvme_device::linux_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid), linux_smart_device(O_RDONLY | O_NONBLOCK) { } bool linux_nvme_device::open() { if (!linux_smart_device::open()) return false; if (!get_nsid()) { // Use actual NSID (/dev/nvmeXnN) if available, // else use broadcast namespace (/dev/nvmeX) int nsid = ioctl(get_fd(), NVME_IOCTL_ID, (void*)0); set_nsid(nsid); } return true; } bool linux_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { nvme_passthru_cmd pt; memset(&pt, 0, sizeof(pt)); pt.opcode = in.opcode; pt.nsid = in.nsid; pt.addr = (uint64_t)in.buffer; pt.data_len = in.size; pt.cdw10 = in.cdw10; pt.cdw11 = in.cdw11; pt.cdw12 = in.cdw12; pt.cdw13 = in.cdw13; pt.cdw14 = in.cdw14; pt.cdw15 = in.cdw15; // Kernel default for NVMe admin commands is 60 seconds // pt.timeout_ms = 60 * 1000; int status = ioctl(get_fd(), NVME_IOCTL_ADMIN_CMD, &pt); if (status < 0) return set_err(errno, "NVME_IOCTL_ADMIN_CMD: %s", strerror(errno)); if (status > 0) return set_nvme_err(out, status); out.result = pt.result; return true; } ////////////////////////////////////////////////////////////////////// // USB bridge ID detection // Read USB ID from /sys file static bool read_id(const std::string & path, unsigned short & id) { FILE * f = fopen(path.c_str(), "r"); if (!f) return false; int n = -1; bool ok = (fscanf(f, "%hx%n", &id, &n) == 1 && n == 4); fclose(f); return ok; } // Get USB bridge ID for "sdX" or "sgN" static bool get_usb_id(const char * name, unsigned short & vendor_id, unsigned short & product_id, unsigned short & version) { // Only "sdX" or "sgN" supported if (!(name[0] == 's' && (name[1] == 'd' || name[1] == 'g') && !strchr(name, '/'))) return false; // Start search at dir referenced by symlink // "/sys/block/sdX/device" or // "/sys/class/scsi_generic/sgN" // -> "/sys/devices/.../usb*/.../host*/target*/..." std::string dir = strprintf("/sys/%s/%s%s", (name[1] == 'd' ? "block" : "class/scsi_generic"), name, (name[1] == 'd' ? "/device" : "")); // Stop search at "/sys/devices" struct stat st; if (stat("/sys/devices", &st)) return false; ino_t stop_ino = st.st_ino; // Search in parent directories until "idVendor" is found, // fail if "/sys/devices" reached or too many iterations int cnt = 0; do { dir += "/.."; if (!(++cnt < 10 && !stat(dir.c_str(), &st) && st.st_ino != stop_ino)) return false; } while (access((dir + "/idVendor").c_str(), 0)); // Read IDs if (!( read_id(dir + "/idVendor", vendor_id) && read_id(dir + "/idProduct", product_id) && read_id(dir + "/bcdDevice", version) )) return false; if (scsi_debugmode > 1) pout("USB ID = 0x%04x:0x%04x (0x%03x)\n", vendor_id, product_id, version); return true; } ////////////////////////////////////////////////////////////////////// /// Linux interface class linux_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_os_version_str(); virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const smart_devtype_list & types, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); virtual std::string get_valid_custom_dev_types_str(); private: static const int devxy_to_n_max = 103; // Max value of devxy_to_n() below void get_dev_list(smart_device_list & devlist, const char * pattern, bool scan_scsi, bool (* p_dev_sdxy_seen)[devxy_to_n_max+1], bool scan_nvme, const char * req_type, bool autodetect); bool get_dev_megasas(smart_device_list & devlist); smart_device * missing_option(const char * opt); int megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp); int megasas_pd_add_list(int bus_no, smart_device_list & devlist); }; std::string linux_smart_interface::get_os_version_str() { struct utsname u; if (!uname(&u)) return strprintf("%s-linux-%s", u.machine, u.release); else return SMARTMONTOOLS_BUILD_HOST; } std::string linux_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) return smartctl_examples; return ""; } // "/dev/sdXY" -> 0-103 // "/dev/disk/by-id/NAME" -> "../../sdXY" -> 0-103 // Other -> -1 static int devxy_to_n(const char * name, bool debug) { const char * xy; char dest[256]; if (str_starts_with(name, "/dev/sd")) { // Assume "/dev/sdXY" xy = name + sizeof("/dev/sd") - 1; } else { // Assume "/dev/disk/by-id/NAME", check link target int sz = readlink(name, dest, sizeof(dest)-1); if (!(0 < sz && sz < (int)sizeof(dest))) return -1; dest[sz] = 0; if (!str_starts_with(dest, "../../sd")) return -1; if (debug) pout("%s -> %s\n", name, dest); xy = dest + sizeof("../../sd") - 1; } char x = xy[0]; if (!('a' <= x && x <= 'z')) return -1; char y = xy[1]; if (!y) // "[a-z]" -> 0-25 return x - 'a'; if (!(x <= 'c' && 'a' <= y && y <= 'z' && !xy[2])) return -1; // "[a-c][a-z]" -> 26-103 return (x - 'a' + 1) * ('z' - 'a' + 1) + (y - 'a'); } void linux_smart_interface::get_dev_list(smart_device_list & devlist, const char * pattern, bool scan_scsi, bool (* p_dev_sdxy_seen)[devxy_to_n_max+1], bool scan_nvme, const char * req_type, bool autodetect) { bool debug = (ata_debugmode || scsi_debugmode || nvme_debugmode); // Use glob to look for any directory entries matching the pattern glob_t globbuf; memset(&globbuf, 0, sizeof(globbuf)); int retglob = glob(pattern, GLOB_ERR, NULL, &globbuf); if (retglob) { // glob failed: free memory and return globfree(&globbuf); if (debug) pout("glob(3) error %d for pattern %s\n", retglob, pattern); if (retglob == GLOB_NOSPACE) throw std::bad_alloc(); return; } // did we find too many paths? const int max_pathc = 1024; int n = (int)globbuf.gl_pathc; if (n > max_pathc) { pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", n, max_pathc, pattern, n - max_pathc); n = max_pathc; } // now step through the list returned by glob. for (int i = 0; i < n; i++) { const char * name = globbuf.gl_pathv[i]; if (p_dev_sdxy_seen) { // Follow "/dev/disk/by-id/*" symlink and check for duplicate "/dev/sdXY" int dev_n = devxy_to_n(name, debug); if (!(0 <= dev_n && dev_n <= devxy_to_n_max)) continue; if ((*p_dev_sdxy_seen)[dev_n]) { if (debug) pout("%s: duplicate, ignored\n", name); continue; } (*p_dev_sdxy_seen)[dev_n] = true; } smart_device * dev; if (autodetect) { dev = autodetect_smart_device(name); if (!dev) continue; } else if (scan_scsi) dev = new linux_scsi_device(this, name, req_type, true /*scanning*/); else if (scan_nvme) dev = new linux_nvme_device(this, name, req_type, 0 /* use default nsid */); else dev = new linux_ata_device(this, name, req_type); devlist.push_back(dev); } // free memory globfree(&globbuf); } // getting devices from LSI SAS MegaRaid, if available bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist) { /* Scanning of disks on MegaRaid device */ /* Perform mknod of device ioctl node */ int mjr, n1; char line[128]; bool scan_megasas = false; FILE * fp = fopen("/proc/devices", "r"); if (!fp) return false; while (fgets(line, sizeof(line), fp) != NULL) { n1=0; if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) { scan_megasas = true; n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0)); if(scsi_debugmode > 0) pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno); if (n1 >= 0 || errno == EEXIST) break; } } fclose(fp); if(!scan_megasas) return false; // getting bus numbers with megasas devices // we are using sysfs to get list of all scsi hosts DIR * dp = opendir ("/sys/class/scsi_host/"); if (dp != NULL) { struct dirent *ep; while ((ep = readdir (dp)) != NULL) { unsigned int host_no = 0; if (!sscanf(ep->d_name, "host%u", &host_no)) continue; /* proc_name should be megaraid_sas */ char sysfsdir[256]; snprintf(sysfsdir, sizeof(sysfsdir) - 1, "/sys/class/scsi_host/host%u/proc_name", host_no); if((fp = fopen(sysfsdir, "r")) == NULL) continue; if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line,"megaraid_sas",12)) { megasas_pd_add_list(host_no, devlist); } fclose(fp); } (void) closedir (dp); } else { /* sysfs not mounted ? */ for(unsigned i = 0; i <=16; i++) // trying to add devices on first 16 buses megasas_pd_add_list(i, devlist); } return true; } bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist, const smart_devtype_list & types, const char * pattern /*= 0*/) { if (pattern) return set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); // Scan type list bool by_id = false; const char * type_ata = 0, * type_scsi = 0, * type_sat = 0, * type_nvme = 0; for (unsigned i = 0; i < types.size(); i++) { const char * type = types[i].c_str(); if (!strcmp(type, "by-id")) by_id = true; else if (!strcmp(type, "ata")) type_ata = "ata"; else if (!strcmp(type, "scsi")) type_scsi = "scsi"; else if (!strcmp(type, "sat")) type_sat = "sat"; else if (!strcmp(type, "nvme")) type_nvme = "nvme"; else return set_err(EINVAL, "Invalid type '%s', valid arguments are: by-id, ata, scsi, sat, nvme", type); } // Use default if no type specified if (!(type_ata || type_scsi || type_sat || type_nvme)) { type_ata = type_scsi = type_sat = ""; #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL type_nvme = ""; #endif } if (type_ata) get_dev_list(devlist, "/dev/hd[a-t]", false, 0, false, type_ata, false); if (type_scsi || type_sat) { // "sat" detection will be later handled in linux_scsi_device::autodetect_open() const char * type_scsi_sat = ((type_scsi && type_sat) ? "" // detect both : (type_scsi ? type_scsi : type_sat)); bool autodetect = !*type_scsi_sat; // If no type specified, detect USB also bool dev_sdxy_seen[devxy_to_n_max+1] = {false, }; bool (*p_dev_sdxy_seen)[devxy_to_n_max+1] = 0; if (by_id) { // Scan unique symlinks first get_dev_list(devlist, "/dev/disk/by-id/*", true, &dev_sdxy_seen, false, type_scsi_sat, autodetect); p_dev_sdxy_seen = &dev_sdxy_seen; // Check for duplicates below } get_dev_list(devlist, "/dev/sd[a-z]", true, p_dev_sdxy_seen, false, type_scsi_sat, autodetect); get_dev_list(devlist, "/dev/sd[a-c][a-z]", true, p_dev_sdxy_seen, false, type_scsi_sat, autodetect); // get device list from the megaraid device get_dev_megasas(devlist); } if (type_nvme) { get_dev_list(devlist, "/dev/nvme[0-9]", false, 0, true, type_nvme, false); get_dev_list(devlist, "/dev/nvme[1-9][0-9]", false, 0, true, type_nvme, false); } return true; } ata_device * linux_smart_interface::get_ata_device(const char * name, const char * type) { return new linux_ata_device(this, name, type); } scsi_device * linux_smart_interface::get_scsi_device(const char * name, const char * type) { return new linux_scsi_device(this, name, type); } nvme_device * linux_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) { return new linux_nvme_device(this, name, type, nsid); } smart_device * linux_smart_interface::missing_option(const char * opt) { set_err(EINVAL, "requires option '%s'", opt); return 0; } int linux_smart_interface::megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf, size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp) { struct megasas_iocpacket ioc; if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || (mbox == NULL && mboxlen != 0)) { errno = EINVAL; return (-1); } bzero(&ioc, sizeof(ioc)); struct megasas_dcmd_frame * dcmd = &ioc.frame.dcmd; ioc.host_no = bus_no; if (mbox) bcopy(mbox, dcmd->mbox.w, mboxlen); dcmd->cmd = MFI_CMD_DCMD; dcmd->timeout = 0; dcmd->flags = 0; dcmd->data_xfer_len = bufsize; dcmd->opcode = opcode; if (bufsize > 0) { dcmd->sge_count = 1; dcmd->data_xfer_len = bufsize; dcmd->sgl.sge32[0].phys_addr = (intptr_t)buf; dcmd->sgl.sge32[0].length = (uint32_t)bufsize; ioc.sge_count = 1; ioc.sgl_off = offsetof(struct megasas_dcmd_frame, sgl); ioc.sgl[0].iov_base = buf; ioc.sgl[0].iov_len = bufsize; } int fd; if ((fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) <= 0) { return (errno); } int r = ioctl(fd, MEGASAS_IOC_FIRMWARE, &ioc); ::close(fd); if (r < 0) { return (r); } if (statusp != NULL) *statusp = dcmd->cmd_status; else if (dcmd->cmd_status != MFI_STAT_OK) { fprintf(stderr, "command %x returned error status %x\n", opcode, dcmd->cmd_status); errno = EIO; return (-1); } return (0); } int linux_smart_interface::megasas_pd_add_list(int bus_no, smart_device_list & devlist) { /* * Keep fetching the list in a loop until we have a large enough * buffer to hold the entire list. */ megasas_pd_list * list = 0; for (unsigned list_size = 1024; ; ) { list = reinterpret_cast(realloc(list, list_size)); if (!list) throw std::bad_alloc(); bzero(list, list_size); if (megasas_dcmd_cmd(bus_no, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 0, NULL) < 0) { free(list); return (-1); } if (list->size <= list_size) break; list_size = list->size; } // adding all SCSI devices for (unsigned i = 0; i < list->count; i++) { if(list->addr[i].scsi_dev_type) continue; /* non disk device found */ char line[128]; snprintf(line, sizeof(line) - 1, "/dev/bus/%d", bus_no); smart_device * dev = new linux_megaraid_device(this, line, list->addr[i].device_id); devlist.push_back(dev); } free(list); return (0); } // Return kernel release as integer ("2.6.31" -> 206031) static unsigned get_kernel_release() { struct utsname u; if (uname(&u)) return 0; unsigned x = 0, y = 0, z = 0; if (!(sscanf(u.release, "%u.%u.%u", &x, &y, &z) == 3 && x < 100 && y < 100 && z < 1000 )) return 0; return x * 100000 + y * 1000 + z; } // Check for SCSI host proc_name "hpsa" static bool is_hpsa(const char * name) { char path[128]; snprintf(path, sizeof(path), "/sys/block/%s/device", name); char * syshostpath = realpath(path, (char *)0); if (!syshostpath) return false; char * syshost = strrchr(syshostpath, '/'); if (!syshost) { free(syshostpath); return false; } char * hostsep = strchr(++syshost, ':'); if (hostsep) *hostsep = 0; snprintf(path, sizeof(path), "/sys/class/scsi_host/host%s/proc_name", syshost); free(syshostpath); int fd = open(path, O_RDONLY); if (fd < 0) return false; char proc_name[32]; ssize_t n = read(fd, proc_name, sizeof(proc_name) - 1); close(fd); if (n < 4) return false; proc_name[n] = 0; if (proc_name[n - 1] == '\n') proc_name[n - 1] = 0; if (scsi_debugmode > 1) pout("%s -> %s: \"%s\"\n", name, path, proc_name); if (strcmp(proc_name, "hpsa")) return false; return true; } // 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. smart_device * linux_smart_interface::autodetect_smart_device(const char * name) { const char * test_name = name; // Dereference symlinks struct stat st; std::string pathbuf; if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { char * p = realpath(name, (char *)0); if (p) { pathbuf = p; free(p); test_name = pathbuf.c_str(); } } // Remove the leading /dev/... if it's there static const char dev_prefix[] = "/dev/"; if (str_starts_with(test_name, dev_prefix)) test_name += strlen(dev_prefix); // form /dev/h* or h* if (str_starts_with(test_name, "h")) return new linux_ata_device(this, name, ""); // form /dev/ide/* or ide/* if (str_starts_with(test_name, "ide/")) return new linux_ata_device(this, name, ""); // form /dev/s* or s* if (str_starts_with(test_name, "s")) { // Try to detect possible USB->(S)ATA bridge unsigned short vendor_id = 0, product_id = 0, version = 0; if (get_usb_id(test_name, vendor_id, product_id, version)) { const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); if (!usbtype) return 0; // Kernels before 2.6.29 do not support the sense data length // required for SAT ATA PASS-THROUGH(16) if (!strcmp(usbtype, "sat") && get_kernel_release() < 206029) usbtype = "sat,12"; // Return SAT/USB device for this type // (Note: linux_scsi_device::autodetect_open() will not be called in this case) return get_scsi_passthrough_device(usbtype, new linux_scsi_device(this, name, "")); } // Fail if hpsa driver if (is_hpsa(test_name)) return missing_option("-d cciss,N"); // No USB bridge or hpsa driver found, assume regular SCSI device return new linux_scsi_device(this, name, ""); } // form /dev/scsi/* or scsi/* if (str_starts_with(test_name, "scsi/")) return new linux_scsi_device(this, name, ""); // form /dev/bsg/* or bsg/* if (str_starts_with(test_name, "bsg/")) return new linux_scsi_device(this, name, ""); // form /dev/ns* or ns* if (str_starts_with(test_name, "ns")) return new linux_scsi_device(this, name, ""); // form /dev/os* or os* if (str_starts_with(test_name, "os")) return new linux_scsi_device(this, name, ""); // form /dev/nos* or nos* if (str_starts_with(test_name, "nos")) return new linux_scsi_device(this, name, ""); // form /dev/nvme* or nvme* if (str_starts_with(test_name, "nvme")) return new linux_nvme_device(this, name, "", 0 /* use default nsid */); // form /dev/tw[ael]* or tw[ael]* if (str_starts_with(test_name, "tw") && strchr("ael", test_name[2])) return missing_option("-d 3ware,N"); // form /dev/cciss/* or cciss/* if (str_starts_with(test_name, "cciss/")) return missing_option("-d cciss,N"); // we failed to recognize any of the forms return 0; } smart_device * linux_smart_interface::get_custom_smart_device(const char * name, const char * type) { // Marvell ? if (!strcmp(type, "marvell")) return new linux_marvell_device(this, name, type); // 3Ware ? int disknum = -1, n1 = -1, n2 = -1; if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { if (n2 != (int)strlen(type)) { set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer"); return 0; } if (!(0 <= disknum && disknum <= 127)) { set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } if (!strncmp(name, "/dev/twl", 8)) return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_9700_CHAR, disknum); else if (!strncmp(name, "/dev/twa", 8)) return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_9000_CHAR, disknum); else if (!strncmp(name, "/dev/twe", 8)) return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_678K_CHAR, disknum); else return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_678K, disknum); } // Areca? disknum = n1 = n2 = -1; int encnum = 1; if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { if (!(1 <= disknum && disknum <= 128)) { set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); return 0; } if (!(1 <= encnum && encnum <= 8)) { set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } return new linux_areca_ata_device(this, name, disknum, encnum); } // Highpoint ? int controller = -1, channel = -1; disknum = 1; n1 = n2 = -1; int n3 = -1; if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) { int len = strlen(type); if (!(n2 == len || n3 == len)) { set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items"); return 0; } if (!(1 <= controller && controller <= 8)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied"); return 0; } if (!(1 <= channel && channel <= 128)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); return 0; } if (!(1 <= disknum && disknum <= 15)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied"); return 0; } return new linux_highpoint_device(this, name, controller, channel, disknum); } #ifdef HAVE_LINUX_CCISS_IOCTL_H // CCISS ? disknum = n1 = n2 = -1; if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { if (n2 != (int)strlen(type)) { set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); return 0; } if (!(0 <= disknum && disknum <= 127)) { set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } return get_sat_device("sat,auto", new linux_cciss_device(this, name, disknum)); } #endif // HAVE_LINUX_CCISS_IOCTL_H // MegaRAID ? if (sscanf(type, "megaraid,%d", &disknum) == 1) { return new linux_megaraid_device(this, name, disknum); } //aacraid? unsigned host, chan, device; if (sscanf(type, "aacraid,%u,%u,%u", &host, &chan, &device) == 3) { //return new linux_aacraid_device(this,name,channel,device); return get_sat_device("sat,auto", new linux_aacraid_device(this, name, host, chan, device)); } return 0; } std::string linux_smart_interface::get_valid_custom_dev_types_str() { return "marvell, areca,N/E, 3ware,N, hpt,L/M/N, megaraid,N, aacraid,H,L,ID" #ifdef HAVE_LINUX_CCISS_IOCTL_H ", cciss,N" #endif ; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static os_linux::linux_smart_interface the_interface; smart_interface::set(&the_interface); } smartmontools-7.0/os_linux.h0000644000175000010010000002510013401001476013212 00000000000000/* * os_linux.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 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 compatibility By: Andre Hedrick * Non-Copyright (C) 2000 Andre Hedrick * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_LINUX_H_ #define OS_LINUX_H_ #define OS_LINUX_H_CVSID "$Id: os_linux.h 4842 2018-12-02 16:07:26Z chrfranke $\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; unsigned char unit; 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 #define HPTIO_CTL 0x03ff // ioctl interface for HighPoint raid device #endif /* OS_LINUX_H_ */ smartmontools-7.0/os_netbsd.cpp0000644000175000010010000005254413347470272013716 00000000000000/* * os_netbsd.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 Sergey Svishchev * Copyright (C) 2016 Kimihiro Nonaka * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "atacmds.h" #include "scsicmds.h" #include "utility.h" #include "os_netbsd.h" #include #include #include #include #include // based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources #include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4780 2018-09-16 15:03:22Z chrfranke $" OS_NETBSD_H_CVSID; #define ARGUSED(x) ((void)(x)) ///////////////////////////////////////////////////////////////////////////// namespace os_netbsd { // No need to publish anything, name provided for Doxygen static const char *net_dev_prefix = "/dev/"; static const char *net_dev_raw_prefix = "/dev/r"; static const char *net_dev_ata_disk = "wd"; static const char *net_dev_scsi_disk = "sd"; static const char *net_dev_scsi_tape = "enrst"; static const char *net_dev_nvme_ctrl = "nvme"; ///////////////////////////////////////////////////////////////////////////// /// Implement shared open/close routines with old functions. class netbsd_smart_device : virtual public /*implements*/ smart_device { public: explicit netbsd_smart_device() : smart_device(never_called), m_fd(-1) { } virtual ~netbsd_smart_device() throw(); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: /// Return filedesc for derived classes. int get_fd() const { return m_fd; } void set_fd(int fd) { m_fd = fd; } private: int m_fd; ///< filedesc, -1 if not open. }; netbsd_smart_device::~netbsd_smart_device() throw() { if (m_fd >= 0) os_netbsd::netbsd_smart_device::close(); } bool netbsd_smart_device::is_open() const { return (m_fd >= 0); } bool netbsd_smart_device::open() { const char *dev = get_dev_name(); int fd; if (is_scsi()) { fd = ::open(dev,O_RDWR|O_NONBLOCK); if (fd < 0 && errno == EROFS) fd = ::open(dev,O_RDONLY|O_NONBLOCK); if (fd < 0) { set_err(errno); return false; } } else if (is_ata() || is_nvme()) { if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) { set_err(errno); return false; } } else return false; set_fd(fd); return true; } bool netbsd_smart_device::close() { int failed = 0; // close device, if open if (is_open()) failed=::close(get_fd()); set_fd(-1); if(failed) return false; else return true; } ///////////////////////////////////////////////////////////////////////////// /// Implement standard ATA support class netbsd_ata_device : public /*implements*/ ata_device, public /*extends*/ netbsd_smart_device { public: netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: virtual int do_cmd(struct atareq* request, bool is_48bit_cmd); }; netbsd_ata_device::netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), netbsd_smart_device() { } int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd) { int fd = get_fd(), ret; ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the ATAIOCCOMMAND ret = ioctl(fd, ATAIOCCOMMAND, request); if (ret) set_err(errno); return ret; } bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { bool ata_48bit = false; // no ata_48bit_support via ATAIOCCOMMAND if (!ata_cmd_is_ok(in, true, // data_out_support true, // multi_sector_support ata_48bit) ) { set_err(ENOSYS, "48-bit ATA commands not implemented"); return false; } struct atareq req; memset(&req, 0, sizeof(req)); req.timeout = 1000; req.command = in.in_regs.command; req.features = in.in_regs.features; req.sec_count = in.in_regs.sector_count; req.sec_num = in.in_regs.lba_low; req.head = in.in_regs.device; req.cylinder = in.in_regs.lba_mid | (in.in_regs.lba_high << 8); switch (in.direction) { case ata_cmd_in::no_data: req.flags = ATACMD_READREG; break; case ata_cmd_in::data_in: req.flags = ATACMD_READ | ATACMD_READREG; req.databuf = (char *)in.buffer; req.datalen = in.size; break; case ata_cmd_in::data_out: req.flags = ATACMD_WRITE | ATACMD_READREG; req.databuf = (char *)in.buffer; req.datalen = in.size; break; default: return set_err(ENOSYS); } clear_err(); errno = 0; if (do_cmd(&req, in.in_regs.is_48bit_cmd())) return false; if (req.retsts != ATACMD_OK) return set_err(EIO, "request failed, error code 0x%02x", req.retsts); out.out_regs.error = req.error; out.out_regs.sector_count = req.sec_count; out.out_regs.lba_low = req.sec_num; out.out_regs.device = req.head; out.out_regs.lba_mid = req.cylinder; out.out_regs.lba_high = req.cylinder >> 8; out.out_regs.status = req.command; /* Undo byte-swapping for IDENTIFY */ if (in.in_regs.command == ATA_IDENTIFY_DEVICE && isbigendian()) { for (int i = 0; i < 256; i+=2) swap2 ((char *)req.databuf + i); } return true; } ///////////////////////////////////////////////////////////////////////////// /// NVMe support class netbsd_nvme_device : public /*implements*/ nvme_device, public /*extends*/ netbsd_smart_device { public: netbsd_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); }; netbsd_nvme_device::netbsd_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid), netbsd_smart_device() { } bool netbsd_nvme_device::open() { const char *dev = get_dev_name(); if (strncmp(dev, NVME_PREFIX, strlen(NVME_PREFIX))) { set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'", NVME_PREFIX); return false; } int nsid = -1, ctrlid = -1; char tmp; if(sscanf(dev, NVME_PREFIX"%d%c", &ctrlid, &tmp) == 1) { if(ctrlid < 0) { set_err(EINVAL, "Invalid NVMe controller number"); return false; } nsid = 0xFFFFFFFF; // broadcast id } else if (sscanf(dev, NVME_PREFIX "%d" NVME_NS_PREFIX "%d%c", &ctrlid, &nsid, &tmp) == 2) { if(ctrlid < 0 || nsid <= 0) { set_err(EINVAL, "Invalid NVMe controller/namespace number"); return false; } } else { set_err(EINVAL, "Invalid NVMe controller/namespace syntax"); return false; } // we should always open controller, not namespace device char full_path[64]; snprintf(full_path, sizeof(full_path), NVME_PREFIX"%d", ctrlid); int fd; if ((fd = ::open(full_path, O_RDWR))<0) { set_err(errno); return false; } set_fd(fd); if (!get_nsid()) { set_nsid(nsid); } return true; } bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opcode = in.opcode; pt.cmd.nsid = in.nsid; pt.buf = in.buffer; pt.len = in.size; pt.cmd.cdw10 = in.cdw10; pt.cmd.cdw11 = in.cdw11; pt.cmd.cdw12 = in.cdw12; pt.cmd.cdw13 = in.cdw13; pt.cmd.cdw14 = in.cdw14; pt.cmd.cdw15 = in.cdw15; pt.is_read = 1; // should we use in.direction()? int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt); if (status < 0) return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno)); out.result=pt.cpl.cdw0; // Command specific result (DW0) if (nvme_completion_is_error(&pt.cpl)) return set_nvme_err(out, nvme_completion_is_error(&pt.cpl)); return true; } ///////////////////////////////////////////////////////////////////////////// /// Standard SCSI support class netbsd_scsi_device : public /*implements*/ scsi_device, public /*extends*/ netbsd_smart_device { public: netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning = false); virtual smart_device * autodetect_open(); virtual bool scsi_pass_through(scsi_cmnd_io * iop); private: bool m_scanning; ///< true if created within scan_smart_devices }; netbsd_scsi_device::netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning /* = false */) : smart_device(intf, dev_name, "scsi", req_type), netbsd_smart_device(), m_scanning(scanning) { } bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { struct scsireq sc; int fd = get_fd(); if (scsi_debugmode) { 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 ((scsi_debugmode > 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("]\n"); } memset(&sc, 0, sizeof(sc)); memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); sc.cmdlen = iop->cmnd_len; sc.databuf = (char *)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 : (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) { if (scsi_debugmode) { pout(" error sending SCSI ccb\n"); } return set_err(EIO); } 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 (scsi_debugmode) { 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); } switch (sc.retsts) { case SCCMD_OK: break; case SCCMD_TIMEOUT: return set_err(ETIMEDOUT); case SCCMD_BUSY: return set_err(EBUSY); default: return set_err(EIO); } return true; } ///////////////////////////////////////////////////////////////////////////// ///// SCSI open with autodetection support smart_device * netbsd_scsi_device::autodetect_open() { // Open device if (!open()) return this; // No Autodetection if device type was specified by user bool sat_only = false; if (*get_req_type()) { // Detect SAT if device object was created by scan_smart_devices(). if (!(m_scanning && !strcmp(get_req_type(), "sat"))) return this; sat_only = true; } // The code below is based on smartd.cpp:SCSIFilterKnown() // Get INQUIRY unsigned char req_buff[64] = {0, }; int req_len = 36; if (scsiStdInquiry(this, 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(this, req_buff, req_len)) { // device doesn't like INQUIRY commands close(); set_err(EIO, "INQUIRY failed"); return this; } } int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) { if (sat_only) { close(); set_err(EIO, "INQUIRY too short for SAT"); } return this; } // Use INQUIRY to detect type // SAT or USB, skip MFI controllers because of bugs { smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); if (newdev) { // NOTE: 'this' is now owned by '*newdev' return newdev; } } // Nothing special found if (sat_only) { close(); set_err(EIO, "Not a SAT device"); } return this; } ///////////////////////////////////////////////////////////////////////////// /// Implement platform interface with old functions. class netbsd_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_os_version_str(); virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); virtual std::string get_valid_custom_dev_types_str(); private: int get_dev_names(char ***, const char *); bool get_nvme_devlist(smart_device_list & devlist, const char * type); }; ////////////////////////////////////////////////////////////////////// std::string netbsd_smart_interface::get_os_version_str() { struct utsname osname; uname(&osname); return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); } std::string netbsd_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) { char p; p = 'a' + getrawpartition(); return strprintf( "=================================================== SMARTCTL EXAMPLES =====\n\n" " 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); } return ""; } ata_device * netbsd_smart_interface::get_ata_device(const char * name, const char * type) { return new netbsd_ata_device(this, name, type); } scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type) { return new netbsd_scsi_device(this, name, type); } nvme_device * netbsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) { return new netbsd_nvme_device(this, name, type, nsid); } int netbsd_smart_interface::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 = (char *)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] = (char *)malloc(strlen(net_dev_raw_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_raw_prefix, p, 'a' + getrawpartition()); n++; } char ** tmp = (char **)realloc(mp, n * (sizeof(char *))); if (NULL == tmp) { pout("Out of memory constructing scan device list\n"); free(mp); return -1; } else mp = tmp; *names = mp; return n; } bool netbsd_smart_interface::get_nvme_devlist(smart_device_list & devlist, const char * type) { char ctrlpath[64], nspath[64]; struct stat sb; struct devlistargs laa; nvme_device * nvmedev; int drvfd = ::open(DRVCTLDEV, O_RDONLY, 0); if (drvfd < 0) { set_err(errno); return false; } for (int ctrl = 0;; ctrl++) { snprintf(ctrlpath, sizeof(ctrlpath), NVME_PREFIX"%d", ctrl); if (stat(ctrlpath, &sb) == -1 || !S_ISCHR(sb.st_mode)) break; snprintf(laa.l_devname, sizeof(laa.l_devname), "%s%d", net_dev_nvme_ctrl, ctrl); laa.l_childname = NULL; laa.l_children = 0; if (ioctl(drvfd, DRVLISTDEV, &laa) == -1) { if (errno == ENXIO) continue; break; } nvmedev = get_nvme_device(ctrlpath, type, 0); if (nvmedev) devlist.push_back(nvmedev); uint32_t n = 0; for (int nsid = 1; n < laa.l_children; nsid++) { snprintf(nspath, sizeof(nspath), NVME_PREFIX "%d" NVME_NS_PREFIX "%d", ctrl, nsid); if (stat(nspath, &sb) == -1 || !S_ISCHR(sb.st_mode)) break; int nsfd = ::open(nspath, O_RDONLY, 0); if (nsfd < 0) continue; ::close(nsfd); n++; nvmedev = get_nvme_device(nspath, type, nsid); if (nvmedev) devlist.push_back(nvmedev); } } ::close(drvfd); return true; } bool netbsd_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /*= 0*/) { if (pattern) { set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); return false; } if (type == NULL) type = ""; bool scan_ata = !*type || !strcmp(type, "ata"); bool scan_scsi = !*type || !strcmp(type, "scsi") || !strcmp(type, "sat"); #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL bool scan_nvme = !*type || !strcmp(type, "nvme"); #else bool scan_nvme = !strcmp(type, "nvme"); #endif // Make namelists char * * atanames = 0; int numata = 0; if (scan_ata) { numata = get_dev_names(&atanames, net_dev_ata_disk); if (numata < 0) { set_err(ENOMEM); return false; } } char * * scsinames = 0; int numscsi = 0; char * * scsitapenames = 0; int numscsitape = 0; if (scan_scsi) { numscsi = get_dev_names(&scsinames, net_dev_scsi_disk); if (numscsi < 0) { set_err(ENOMEM); return false; } numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape); if (numscsitape < 0) { set_err(ENOMEM); return false; } } // Add to devlist int i; for (i = 0; i < numata; i++) { ata_device * atadev = get_ata_device(atanames[i], type); if (atadev) devlist.push_back(atadev); free(atanames[i]); } if(numata) free(atanames); for (i = 0; i < numscsi; i++) { scsi_device * scsidev = new netbsd_scsi_device(this, scsinames[i], type, true /*scanning*/); if (scsidev) devlist.push_back(scsidev); free(scsinames[i]); } if(numscsi) free(scsinames); for (i = 0; i < numscsitape; i++) { scsi_device * scsidev = get_scsi_device(scsitapenames[i], type); if (scsidev) devlist.push_back(scsidev); free(scsitapenames[i]); } if(numscsitape) free(scsitapenames); if (scan_nvme) get_nvme_devlist(devlist, type); return true; } smart_device * netbsd_smart_interface::autodetect_smart_device(const char * name) { const char * test_name = name; // if dev_name null, or string length zero if (!name || !*name) return 0; // Dereference symlinks struct stat st; std::string pathbuf; if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { char * p = realpath(name, (char *)0); if (p) { pathbuf = p; free(p); test_name = pathbuf.c_str(); } } if (str_starts_with(test_name, net_dev_raw_prefix)) test_name += strlen(net_dev_raw_prefix); else if (str_starts_with(test_name, net_dev_prefix)) test_name += strlen(net_dev_prefix); else return 0; // device is not starting with /dev/ or /dev/r* if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk))) return get_ata_device(name, "ata"); if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) { // XXX Try to detect possible USB->(S)ATA bridge // XXX get USB vendor ID, product ID and version from sd(4)/umass(4). // XXX check sat device via get_usb_dev_type_by_id(). // No USB bridge found, assume regular SCSI device return get_scsi_device(name, "scsi"); } if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape))) return get_scsi_device(name, "scsi"); if (!strncmp(net_dev_nvme_ctrl, test_name, strlen(net_dev_nvme_ctrl))) return get_nvme_device(name, "nvme", 0 /* use default nsid */); // device type unknown return 0; } smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type) { ARGUSED(name); ARGUSED(type); return 0; } std::string netbsd_smart_interface::get_valid_custom_dev_types_str() { return ""; } } // namespace ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi() void smart_interface::init() { static os_netbsd::netbsd_smart_interface the_interface; smart_interface::set(&the_interface); } /* vim: set ts=2 sw=2 et ff=unix : */ smartmontools-7.0/os_netbsd.h0000644000175000010010000000152613336335341013350 00000000000000/* * os_netbsd.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 Sergey Svishchev * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_NETBSD_H_ #define OS_NETBSD_H_ #define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 4760 2018-08-19 18:45:53Z chrfranke $\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_ */ smartmontools-7.0/os_openbsd.cpp0000644000175000010010000002676113401001476014056 00000000000000/* * os_openbsd.c * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-10 David Snyder * * Derived from os_netbsd.cpp by Sergey Svishchev, Copyright (C) 2003-8 * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "atacmds.h" #include "scsicmds.h" #include "utility.h" #include "os_openbsd.h" #include const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 4842 2018-12-02 16:07:26Z chrfranke $" OS_OPENBSD_H_CVSID; enum warnings { BAD_SMART, 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 currently 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 = (char *)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; } char * u = strchr(p, ':'); if (u) *u = 0; mp[n] = (char *)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()); n++; } char ** tmp = (char **)realloc(mp, n * (sizeof(char *))); if (NULL == tmp) { pout("Out of memory constructing scan device list\n"); free(mp); return -1; } else mp = tmp; *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 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; retval = ioctl(fd, ATAIOCCOMMAND, &req); if (retval < 0) { 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 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 = (char *)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 : (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" " 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 ); return; } smartmontools-7.0/os_openbsd.h0000644000175000010010000000175613336335341013530 00000000000000/* * os_openbsd.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 David Snyder * * Derived from os_netbsd.c by Sergey Svishchev, Copyright (C) 2003-8 * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_OPENBSD_H_ #define OS_OPENBSD_H_ #define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 4760 2018-08-19 18:45:53Z chrfranke $\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 #include #endif /* OS_OPENBSD_H_ */ smartmontools-7.0/os_os2.cpp0000644000175000010010000004021613401001476013116 00000000000000/* * os_os2.c * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 Yuri Dario * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * * Thanks to Daniela Engert for providing sample code for SMART ioctl access. * */ // These are needed to define prototypes for the functions defined below #include "config.h" #include #include #include "atacmds.h" #include "scsicmds.h" #include "utility.h" // This is to include whatever prototypes you define in os_generic.h #include "os_os2.h" // Needed by '-V' option (CVS versioning) of smartd/smartctl const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \ ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; // global handle to device driver static HFILE hDevice; // 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" " smartctl -a hd0 (Prints all SMART information)\n\n" " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n" " (Enables SMART on first disk)\n\n" " smartctl -t long hd0 (Executes extended disk self-test)\n\n" " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n" " (Prints Self-Test & Attribute errors)\n" ); return; } 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) { //printf( "dev_name %s\n", dev_name); dev_name = skipdev(dev_name); if (!strncmp(dev_name, "hd", 2) || !strncmp(dev_name, "ahci", 4)) return CONTROLLER_ATA; 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) { int result; int index; const int max_dev = 32; // scan only first 32 devices // SCSI is not supported if (strcmp (name, "ATA") != 0) return 0; // try to open DANIS APIRET rc; ULONG ActionTaken; HFILE danisDev, ahciDev; bool is_danis = 0, is_ahci = 0; rc = DosOpen ((const char unsigned *)danisdev, &danisDev, &ActionTaken, 0, FILE_SYSTEM, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL); if (!rc) is_danis = 1; rc = DosOpen ((const char unsigned *)ahcidev, &ahciDev, &ActionTaken, 0, FILE_SYSTEM, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL); if (!rc) is_ahci = 1; // Count the devices. result = 0; DSKSP_CommandParameters Parms; ULONG PLen = 1; ULONG IDLen = 512; struct ata_identify_device Id; for(int i = 0; i < max_dev; i++) { if (is_ahci) { Parms.byPhysicalUnit = i; rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen); if (!rc) result++; } if (is_danis) { Parms.byPhysicalUnit = i + 0x80; rc = DosDevIOCtl (danisDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen); if (!rc) result++; } } *devlist = (char**)calloc (result, sizeof (char *)); if (! *devlist) goto error; index = 0; // add devices for(int i = 0; i < max_dev; i++) { if (is_ahci) { Parms.byPhysicalUnit = i; rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen); if (!rc) { asprintf(&(*devlist)[index], "ahci%d", i); if (! (*devlist)[index]) goto error; index++; } } if (is_danis) { Parms.byPhysicalUnit = i + 0x80; rc = DosDevIOCtl (danisDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen); if (!rc) { asprintf(&(*devlist)[index], "hd%d", i); if (! (*devlist)[index]) goto error; index++; } } } if (is_danis) DosClose( danisDev); if (is_ahci) DosClose( ahciDev); return result; error: if (*devlist) { for (index = 0; index < result; index++) if ((*devlist)[index]) free ((*devlist)[index]); free (*devlist); } if (is_danis) DosClose( danisDev); if (is_ahci) DosClose( ahciDev); return -1; } // 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.cpp 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 */ ){ int fd = 0; APIRET rc; ULONG ActionTaken; char * activedev = NULL; pathname = skipdev(pathname); // DANIS506 driver if(strlen(pathname) > strlen(danispref) && strncmp(pathname, danispref, strlen(danispref)) == 0) { fd = strtol(pathname + strlen(danispref), NULL, 10) + 0x80; activedev = (char *)danisdev; } // OS2AHCI driver if(strlen(pathname) > strlen(ahcipref) && strncmp(pathname, ahcipref, strlen(ahcipref)) == 0) { fd = strtol(pathname + strlen(ahcipref), NULL, 10); activedev = (char *)ahcidev; } if(!activedev) { pout("Error: please specify hdX or ahciX device name\n"); return -1; } //printf( "deviceopen pathname %s\n", pathname); rc = DosOpen ((const char unsigned *)activedev, &hDevice, &ActionTaken, 0, FILE_SYSTEM, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE | OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL); if (rc) { char errmsg[256]; snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc); errmsg[255]='\0'; syserror(errmsg); return -1; } return fd; } // Like close(). Acts only on integer handles returned by // deviceopen() above. int deviceclose(int /* fd */){ DosClose( hDevice); hDevice = NULL; return 0; } // // OS/2 direct ioctl interface to IBMS506$/OS2AHCI$ // static int dani_ioctl( int device, void* arg) { unsigned char* buff = (unsigned char*) arg; APIRET rc; DSKSP_CommandParameters Parms; ULONG PLen = 1; ULONG DLen = 512; //sizeof (*buf); ULONG value = 0; // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]); Parms.byPhysicalUnit = device; switch( buff[0]) { case ATA_IDENTIFY_DEVICE: rc = DosDevIOCtl (hDevice, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen); if (rc != 0) { printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc); return -1; } break; case ATA_SMART_CMD: switch( buff[2]) { case ATA_SMART_STATUS: DLen = sizeof(value); // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk) // value: -1=not supported, 0=ok, 1=failing rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GETSTATUS, (PVOID)&Parms, PLen, &PLen, (PVOID)&value, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc); return -1; } buff[4] = (unsigned char)value; break; case ATA_SMART_READ_VALUES: rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_ATTRIBUTES, (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc); return -1; } break; case ATA_SMART_READ_THRESHOLDS: rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_THRESHOLDS, (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc); return -1; } break; case ATA_SMART_READ_LOG_SECTOR: buff[4] = buff[1]; // copy select field rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_LOG, (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc); return -1; } break; case ATA_SMART_ENABLE: buff[0] = 1; // enable DLen = 1; rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF, (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc); return -1; } break; case ATA_SMART_DISABLE: buff[0] = 0; // disable DLen = 1; rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF, (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc); return -1; } break; #if 0 case ATA_SMART_AUTO_OFFLINE: buff[0] = buff[3]; // select field DLen = 1; rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTO_OFFLINE, (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc); return -1; } break; #endif case ATA_SMART_AUTOSAVE: buff[0] = buff[3]; // select field DLen = 1; rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTOSAVE_ONOFF, (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); if (rc) { printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc); return -1; } break; case ATA_SMART_IMMEDIATE_OFFLINE: buff[0] = buff[1]; // select field DLen = 1; rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EXEC_OFFLINE, (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); if (rc) { printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc); return -1; } break; default: fprintf( stderr, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device, buff[0], buff[2]); fprintf( stderr, "unknown ioctl\n"); return -1; break; } break; //case WIN_PIDENTIFY: // break; default: fprintf( stderr, "unknown ioctl\n"); return -1; break; } // ok return 0; } // Interface to ATA devices. See os_linux.cpp for the canonical 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" // 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); //printf( "command, select %d,%d\n", command, select); 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: case STATUS_CHECK: // this command only says if SMART is working. It could be // replaced with STATUS_CHECK below. buff[2]=ATA_SMART_STATUS; buff[4]=0; 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; } // We are now calling ioctl wrapper to the driver. // TODO: use PASSTHRU in case of OS2AHCI driver if ((dani_ioctl(device, buff))) return -1; // There are two different types of ioctls(). The HDIO_DRIVE_TASK // one is this: if (command==STATUS_CHECK){ // Cyl low and Cyl high unchanged means "Good SMART status" if (buff[4]==0) return 0; // These values mean "Bad SMART status" if (buff[4]==1) 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"); 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; } // Interface to SCSI devices. N/A under OS/2 int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) { pout("SCSI interface is not implemented\n"); return -ENOSYS; } smartmontools-7.0/os_os2.h0000644000175000010010000000502013336335341012565 00000000000000/* * os_os2.c * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 Yuri Dario * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_OS2_H_ #define OS_OS2_H_ #define OS_XXXX_H_CVSID "$Id: os_os2.h 4760 2018-08-19 18:45:53Z chrfranke $\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 #define INCL_DOS #include #include "os_linux.h" #pragma pack(1) /* IOCTL definitions from s506oem.h (primarily required for SMART calls) */ #define DSKSP_CAT_SMART 0x80 /* SMART IOCTL category */ #define DSKSP_SMART_ONOFF 0x20 /* turn SMART on or off */ #define DSKSP_SMART_AUTOSAVE_ONOFF 0x21 /* turn SMART autosave on or off */ #define DSKSP_SMART_SAVE 0x22 /* force save of SMART data */ #define DSKSP_SMART_GETSTATUS 0x23 /* get SMART status (pass/fail) */ #define DSKSP_SMART_GET_ATTRIBUTES 0x24 /* get SMART attributes table */ #define DSKSP_SMART_GET_THRESHOLDS 0x25 /* get SMART thresholds table */ #define DSKSP_SMART_GET_LOG 0x26 /* get SMART log table */ #define DSKSP_SMART_AUTO_OFFLINE 0x27 /* set SMART offline autosave timer */ #define DSKSP_SMART_EXEC_OFFLINE 0x28 /* execute SMART immediate offline */ #define SMART_CMD_ON 1 /* on value for related SMART functions */ #define SMART_CMD_OFF 0 /* off value for related SMART functions */ #define DSKSP_CAT_GENERIC 0x90 /* generic IOCTL category */ #define DSKSP_GET_INQUIRY_DATA 0x42 /* get ATA/ATAPI inquiry data */ typedef struct _DSKSP_CommandParameters { BYTE byPhysicalUnit; /* physical unit number 0-n */ /* 0 = 1st disk, 1 = 2nd disk, ...*/ /* 0x80 = Pri/Mas, 0x81=Pri/Sla, 0x82=Sec/Mas,*/ } DSKSP_CommandParameters, *PDSKSP_CommandParameters; struct SMART_ParamExt { UCHAR byPhysicalUnit; // 0=Pri/Mas, 1=Pri/Sla, 2=Sec/Mas, etc. ULONG LogAddress; // valid values 0-255. See ATA/ATPI standard // for details ULONG SectorCount; // valid values 0-255 See ATA/ATPI standard // for details ULONG reserved; // reserved. must be set to 0 }; const char * danisdev="\\DEV\\IBMS506$"; // DANIS506 const char * danispref="hd"; const char * ahcidev="\\DEV\\OS2AHCI$"; // OS2AHCI const char * ahcipref="ahci"; #endif /* OS_GENERIC_H_ */ smartmontools-7.0/os_qnxnto.cpp0000644000175000010010000005207213401001476013745 00000000000000/* * os_qnxnto.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2007 Joerg Hering * * SPDX-License-Identifier: GPL-2.0-or-later */ // 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 "atacmds.h" #include "scsicmds.h" #include "utility.h" // This is to include whatever structures and prototypes you define in // os_generic.h #include "os_qnxnto.h" #include // Needed by '-V' option (CVS versioning) of smartd/smartctl. You // should have one *_H_CVSID macro appearing below for each file // appearing with #include "*.h" above. Please list these (below) in // alphabetic/dictionary order. const char *os_XXXX_c_cvsid="$Id: os_qnxnto.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_H_CVSID OS_QNXNTO_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)) //---------------------------------------------------------------------------------------------- // private Functions static int ata_sense_data(void *sdata,int *error,int *key,int *asc,int *ascq); static int ata_interpret_sense(struct cam_pass_thru *cpt,void *sense,int *status,int rcount); static int ata_pass_thru(int fd,struct cam_pass_thru *pcpt); // 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" " smartctl -a /dev/hd0 (Prints all SMART information)\n\n" " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hd0\n" " (Enables SMART on first disk)\n\n" " smartctl -t long /dev/hd0 (Executes extended disk self-test)\n\n" " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hd0\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" ); return; } // tries to guess device type given the name (a path). See utility.h // for return values. static const char *net_dev_prefix = "/dev/"; static const char *net_dev_ata_disk = "hd"; int guess_device_type (const char* dev_name) { int len,dev_prefix_len; 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); 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); 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.cpp 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) { if(!strcmp(type, "ATA")) return(open(pathname,O_RDWR|O_NONBLOCK)); else return(-1); } // Like close(). Acts only on integer handles returned by // deviceopen() above. int deviceclose(int fd) { return(close(fd)); } //---------------------------------------------------------------------------------------------- // Interface to ATA devices. See os_linux.cpp for the canonical 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) { struct cam_pass_thru cpt; ATA_SENSE sense; CDB *cdb; int status,rc; memset(&cpt,0x00,sizeof(struct cam_pass_thru)); cdb=(CDB *)cpt.cam_cdb; rc=-1; switch(command) { case READ_VALUES: cpt.cam_flags = CAM_DIR_IN; cpt.cam_cdb_len = 16; cpt.cam_dxfer_len = 512; cpt.cam_data_ptr = (uint32_t)data; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_PIO_DATA_IN; cdb->ata_pass_thru.flags = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_READ_VALUES; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case READ_THRESHOLDS: cpt.cam_flags = CAM_DIR_IN; cpt.cam_cdb_len = 16; cpt.cam_dxfer_len = 512; cpt.cam_data_ptr = (uint32_t)data; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_PIO_DATA_IN; cdb->ata_pass_thru.flags = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_READ_THRESHOLDS; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case READ_LOG: cpt.cam_flags = CAM_DIR_IN; cpt.cam_cdb_len = 16; cpt.cam_dxfer_len = 512; cpt.cam_data_ptr = (uint32_t)data; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_PIO_DATA_IN; cdb->ata_pass_thru.flags = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_READ_LOG_SECTOR; cdb->ata_pass_thru.sector_count= 1; cdb->ata_pass_thru.lba_low = select; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case WRITE_LOG: return(-1); break; case IDENTIFY: cpt.cam_flags = CAM_DIR_IN; cpt.cam_cdb_len = 16; cpt.cam_dxfer_len = 512; cpt.cam_data_ptr = (uint32_t)data; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_PIO_DATA_IN; cdb->ata_pass_thru.flags = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU; cdb->ata_pass_thru.command = ATA_IDENTIFY_DEVICE; break; case PIDENTIFY: cpt.cam_flags = CAM_DIR_IN; cpt.cam_cdb_len = 16; cpt.cam_dxfer_len = 512; cpt.cam_data_ptr = (uint32_t)data; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_PIO_DATA_IN; cdb->ata_pass_thru.flags = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU; cdb->ata_pass_thru.command = ATA_IDENTIFY_PACKET_DEVICE; break; case ENABLE: cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_ENABLE; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case DISABLE: cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_DISABLE; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case AUTO_OFFLINE: // NOTE: According to ATAPI 4 and UP, this command is obsolete cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_AUTO_OFFLINE; cdb->ata_pass_thru.lba_low = select; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case AUTOSAVE: cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_AUTOSAVE; cdb->ata_pass_thru.sector_count= select; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case IMMEDIATE_OFFLINE: // NOTE: According to ATAPI 4 and UP, this command is obsolete cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_IMMEDIATE_OFFLINE; cdb->ata_pass_thru.lba_low = select; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case STATUS_CHECK: // same command, no HDIO in NetBSD case STATUS: cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.flags = ATA_FLG_CK_COND; cdb->ata_pass_thru.command = ATA_SMART_CMD; cdb->ata_pass_thru.features = ATA_SMART_STATUS; cdb->ata_pass_thru.lba_mid = ATA_SMART_LBA_MID_SIG; cdb->ata_pass_thru.lba_high = ATA_SMART_LBA_HI_SIG; break; case CHECK_POWER_MODE: cpt.cam_flags = CAM_DIR_NONE; cpt.cam_cdb_len = 16; cpt.cam_sense_len = sizeof(sense); cpt.cam_sense_ptr = (uint32_t)&sense; cdb->ata_pass_thru.opcode = SC_ATA_PT16; cdb->ata_pass_thru.protocol = ATA_PROTO_DATA_NONE; cdb->ata_pass_thru.flags = ATA_FLG_CK_COND; cdb->ata_pass_thru.command = ATA_CHECK_POWER_MODE; break; default: pout("Unrecognized command %d in ata_command_interface()\n", command); errno=ENOSYS; return(-1); } // execute now if((status=ata_pass_thru(fd,&cpt))==EOK) { rc=status==EOK?0:-1; if(cpt.cam_status!=CAM_REQ_CMP) { ata_interpret_sense(&cpt,&sense,&status,0); if(command==STATUS||command==STATUS_CHECK) rc=((sense.desc.lba_high<<8)|sense.desc.lba_mid)==ATA_SMART_SIG?0:1; } } if(command==CHECK_POWER_MODE) data[0]=cdb->ata_pass_thru.sector_count; // finish return(rc); } //---------------------------------------------------------------------------------------------- // 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); return -ENOSYS; } //---------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------- static int ata_sense_data(void *sdata,int *error,int *key,int *asc,int *ascq) { SCSI_SENSE *sf; SCSI_SENSE_DESCRIPTOR *sd; sf=(SCSI_SENSE *)sdata; sd=(SCSI_SENSE_DESCRIPTOR *)sdata; *error=sf->error; if(*error & SENSE_DATA_FMT_DESCRIPTOR) { *key=sd->sense & SK_MSK; *asc=sd->asc; *ascq=sd->ascq; } else { *key=sf->sense & SK_MSK; *asc=sf->asc; *ascq=sf->ascq; } return(CAM_SUCCESS); } //---------------------------------------------------------------------------------------------- static int ata_interpret_sense(struct cam_pass_thru *cpt,void *sense,int *status,int rcount) { int retry; int key; int asc; int ascq; int error; *status=EIO; retry=CAM_TRUE; if(cpt->cam_status&CAM_AUTOSNS_VALID) { ata_sense_data(sense,&error,&key,&asc,&ascq); switch(key) { case SK_NO_SENSE: // No sense data (no error) retry=CAM_FALSE; *status=EOK; break; case SK_RECOVERED: // Recovered error switch(asc) { case ASC_ATA_PASS_THRU: switch(ascq) { case ASCQ_ATA_PASS_THRU_INFO_AVAIL: break; default: break; } break; default: break; } retry=CAM_FALSE; *status=EOK; break; case SK_NOT_RDY: // Device not ready *status=EAGAIN; switch(asc) { case ASC_NOT_READY: switch(ascq) { case ASCQ_BECOMING_READY: case ASCQ_CAUSE_NOT_REPORTABLE: default: retry=CAM_FALSE; break; } break; case ASC_MEDIA_NOT_PRESENT: *status=ENXIO; retry=CAM_FALSE; break; } break; case SK_MEDIUM: // Medium error case SK_HARDWARE: // Hardware error retry=CAM_FALSE; *status=EIO; break; case SK_ILLEGAL: // Illegal Request (bad command) retry=CAM_FALSE; *status=EINVAL; break; case SK_UNIT_ATN: // Unit Attention switch(asc) { case ASC_MEDIUM_CHANGED: *status=ESTALE; retry=CAM_FALSE; break; case ASC_BUS_RESET: break; } break; case SK_DATA_PROT: // Data Protect retry=CAM_FALSE; *status=EROFS; break; case SK_VENDOR: // Vendor Specific case SK_CPY_ABORT: // Copy Aborted retry=CAM_FALSE; *status=EIO; break; case SK_CMD_ABORT: // Aborted Command retry=CAM_FALSE; *status=ECANCELED; break; case SK_EQUAL: // Equal case SK_VOL_OFL: // Volume Overflow case SK_MISCMP: // Miscompare case SK_RESERVED: // Reserved break; } if(*status==EOK) { switch(cpt->cam_status&CAM_STATUS_MASK) { case CAM_REQ_CMP_ERR: // CCB request completed with an err retry=CAM_FALSE; *status=EIO; break; case CAM_BUSY: // CAM subsystem is busy *status=EAGAIN; break; case CAM_REQ_INVALID: // CCB request is invalid case CAM_PATH_INVALID: // Path ID supplied is invalid case CAM_DEV_NOT_THERE: // SCSI device not installed/there case CAM_SEL_TIMEOUT: // Target selection timeout case CAM_LUN_INVALID: // LUN supplied is invalid case CAM_TID_INVALID: // Target ID supplied is invalid retry=CAM_FALSE; *status=ENXIO; break; case CAM_CMD_TIMEOUT: // Command timeout *status=rcount?EAGAIN:EIO; break; case CAM_MSG_REJECT_REC: // Message reject received case CAM_SCSI_BUS_RESET: // SCSI bus reset sent/received case CAM_UNCOR_PARITY: // Uncorrectable parity err occurred case CAM_AUTOSENSE_FAIL: // Autosense: Request sense cmd fail case CAM_NO_HBA: // No HBA detected Error case CAM_DATA_RUN_ERR: // Data overrun/underrun error retry=CAM_FALSE; *status=EIO; break; case CAM_UNEXP_BUSFREE: // Unexpected BUS free case CAM_SEQUENCE_FAIL: // Target bus phase sequence failure *status=EIO; break; case CAM_PROVIDE_FAIL: // Unable to provide requ. capability retry=CAM_FALSE; *status=ENOTTY; break; case CAM_CCB_LEN_ERR: // CCB length supplied is inadequate case CAM_BDR_SENT: // A SCSI BDR msg was sent to target case CAM_REQ_TERMIO: // CCB request terminated by the host case CAM_FUNC_NOTAVAIL: // The requ. func is not available case CAM_NO_NEXUS: // Nexus is not established case CAM_IID_INVALID: // The initiator ID is invalid case CAM_CDB_RECVD: // The SCSI CDB has been received retry=CAM_FALSE; *status=EIO; break; case CAM_SCSI_BUSY: // SCSI bus busy *status=EAGAIN; break; } } } return(retry); } //---------------------------------------------------------------------------------------------- static int ata_pass_thru(int fd,struct cam_pass_thru *pcpt) { int icnt; int status; iov_t iov[3]; struct cam_pass_thru cpt; cpt=*pcpt; icnt=1; SETIOV(&iov[0],&cpt,sizeof(cpt)); cpt.cam_timeout=cpt.cam_timeout?cpt.cam_timeout:CAM_TIME_DEFAULT; if(cpt.cam_sense_len) { SETIOV(&iov[1],(void *)cpt.cam_sense_ptr,cpt.cam_sense_len); cpt.cam_sense_ptr=sizeof(cpt); icnt++; } if(cpt.cam_dxfer_len) { SETIOV(&iov[2],(void *)cpt.cam_data_ptr,cpt.cam_dxfer_len); cpt.cam_data_ptr=(paddr_t)sizeof(cpt)+cpt.cam_sense_len; icnt++; } if((status=devctlv(fd,DCMD_CAM_PASS_THRU,icnt,icnt,iov,iov,NULL))) pout("ata_pass_thru devctl: %s\n",strerror(status)); pcpt->cam_status=cpt.cam_status; pcpt->cam_scsi_status=cpt.cam_scsi_status; return(status); } //---------------------------------------------------------------------------------------------- smartmontools-7.0/os_qnxnto.h0000644000175000010010000005472013336613560013426 00000000000000/* * os_qnxnto.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2007 Joerg Hering * Copyright (C) 2003-8 Bruce Allen * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_QNXNTO_H_ #define OS_QNXNTO_H_ #define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 4761 2018-08-20 19:33:04Z chrfranke $\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 #include #ifndef __TYPES_H_INCLUDED #include #endif #include #include #include #include #include #include #include "atacmds.h" //---------------------------------------------------------------------------------------------------------- typedef struct _ata_pass_thru ATA_PASS_THRU; typedef struct _eide_identify EIDE_IDENTIFY; typedef struct _ata_sense ATA_SENSE; typedef void CCB; struct _sim_hba; struct _resmgr_context; typedef struct _drive_attribute { int id; int threshold; char *name; }DRIVE_ATTRIBUTE; //---------------------------------------------------------------------------------------------------------- /* UNIVOS OSD defines and data structures. */ #define INQLEN 36 /* Inquiry string length to store. */ #define CAM_SUCCESS 0 /* For signaling general success */ #define CAM_FAILURE 1 /* For signaling general failure */ #define CAM_FALSE 0 /* General purpose flag value */ #define CAM_TRUE 1 /* General purpose flag value */ //---------------------------------------------------------------------------------------------------------- // Group 3 and 4, command codes 60H-9FH are reserved #define SC_ATA_PT16 0x85 // ATA Pass-through //---------------------------------------------------------------------------------------------------------- #define ATA_SMART_LBA_MID_SIG 0x4f #define ATA_SMART_LBA_HI_SIG 0xc2 #define ATA_SMART_SIG 0xc24f //---------------------------------------------------------------------------------------------------------- struct _ata_pass_thru { uchar_t opcode; #define ATA_PROTO_MSK 0x1e #define ATA_PROTO_RESPONSE (15 << 1) #define ATA_PROTO_FPDMA (12 << 1) #define ATA_PROTO_UDMA_DATA_OUT (11 << 1) #define ATA_PROTO_UDMA_DATA_IN (10 << 1) #define ATA_PROTO_DEVICE_RESET (9 << 1) #define ATA_PROTO_DEVICE_DIAGNOSTIC (8 << 1) #define ATA_PROTO_DMA_QUEUED (7 << 1) #define ATA_PROTO_DMA (6 << 1) #define ATA_PROTO_PIO_DATA_OUT (5 << 1) #define ATA_PROTO_PIO_DATA_IN (4 << 1) #define ATA_PROTO_DATA_NONE (3 << 1) #define ATA_PROTO_SRST (1 << 1) #define ATA_PROTO_HRST (0 << 1) #define ATA_PROTO_EXTEND 0x01 uchar_t protocol; // multiple count, protocol #define ATA_MCOUNT_MSK 0xe0 #define ATA_FLG_CK_COND 0x20 #define ATA_FLG_T_DIR 0x08 // data from device #define ATA_FLG_BYT_BLOK 0x04 #define ATA_FLG_TLEN_STPSIU 0x03 #define ATA_FLG_TLEN_SECTOR_COUNT 0x02 #define ATA_FLG_TLEN_FEATURE 0x01 uchar_t flags; uchar_t efeatures; uchar_t features; uchar_t esector_count; uchar_t sector_count; uchar_t elba_low; uchar_t lba_low; uchar_t elba_mid; uchar_t lba_mid; uchar_t elba_high; uchar_t lba_high; uchar_t device; uchar_t command; uchar_t control; } ata_pass_thru_; //---------------------------------------------------------------------------------------------------------- #define SENSE_DATA_FMT_DESCRIPTOR 0x02 // Fixed Format Sense Data Structure // Note: The field "error" has the following format: // bit 7 - Address valid bit // bits 6-4 - Error class // bits 3-0 - Error code // // Error classes 0-6 are vendor unique and also indicate that the // sense data is in _nonextended_ format. (i.e. not usually used) // struct _scsi_nonextended_sense { // uchar_t sd_err; // ulong_t sd_block_address; // }; // // An error class of 7 and an error code of 0 (70H) indicate SCSI-1 // extended sense data format (or SCSI-2 sense data format). // // An error class of 7 and an error code of 1 (71H) indicate SCSI-2 // deferred errors. // // Error codes 74H to 7EH are reserved and error code 7FH indicates // a vendor-specific sense data format. typedef struct _scsi_sense { uchar_t error; // Error Code uchar_t segment; // Segment number uchar_t sense; // Sense key/flags uchar_t info[4]; // Information (32bit big-endian value) uchar_t asl; // Additional Sense Length uchar_t csinfo[4]; // Command-Specific Information uchar_t asc; // Additional Sense Code uchar_t ascq; // Additional Sense Code Qualifier uchar_t fruc; // Field Replaceable Unit Code uchar_t sks; // Sense Key Specific ushort_t sks_data; // Sense Key Specific Data (16bit big-endian) ushort_t asb; // Additional Sense uchar_ts (Max 256-18) } SCSI_SENSE; // Descriptor Format Sense Data Structure // error code of 72 current, 73 deferred // extended sense data format (or SCSI-2 sense data format). typedef struct _scsi_sense_descriptor { uchar_t error; // Error Code uchar_t sense; // Sense key/flags uchar_t asc; // Additional Sense Code uchar_t ascq; // Additional Sense Code Qualifier uchar_t rsvd[3]; uchar_t asl; // Additional Sense Length } SCSI_SENSE_DESCRIPTOR; typedef struct _scsi_sense_desriptor_header { uchar_t descriptor_type; uchar_t descriptor_len; } SCSI_SENSE_DESCRIPTOR_HEADER; #define SENSE_DTYPE_INFORMATION 0x00 #define SENSE_DTYPE_CSI 0x01 // Command Specific Information #define SENSE_DTYPE_SKS 0x02 // Sense Key Specific #define SENSE_DTYPE_FRU 0x03 // Field Replaceable Unit #define SENSE_DTYPE_STREAM 0x04 #define SENSE_DTYPE_BLOCK 0x05 #define SENSE_DTYPE_OSD_OBJ_IDENT 0x06 // OSD Object Identification #define SENSE_DTYPE_OSD_INTEGRITY 0x07 // OSD Response Integrity Check Value #define SENSE_DTYPE_OSD_ATR_IDENT 0x08 // OSD Attribute Identification #define SENSE_DTYPE_ATA 0x09 typedef struct _ata_status_descriptor { uchar_t descriptor_type; #define ATA_SD_DLEN 0x0c uchar_t descriptor_len; /* 0xc */ #define ATA_SD_FLG_EXTEND 0x01 uchar_t flags; uchar_t error; uchar_t esector_count; /* (15:8) */ uchar_t sector_count; /* (7:0) */ uchar_t elba_low; /* (15:8) */ uchar_t lba_low; /* (7:0) */ uchar_t elba_mid; /* (15:8) */ uchar_t lba_mid; /* (7:0) */ uchar_t elba_high; /* (15:8) */ uchar_t lba_high; /* (7:0) */ uchar_t device; uchar_t status; } ATA_STATUS_DESCRIPTOR; //---------------------------------------------------------------------------------------------------------- // Sense Keys #define SK_MSK 0x0F // mask to sd_sense field for key #define SK_NO_SENSE 0 // No sense data (no error) #define ASCQ_FILEMARK_DETECTED 0x01 #define ASCQ_EOPM_DETECTED 0x02 // End of Partition/Medium Detected #define ASCQ_SETMARK_DETECTED 0x03 #define ASCQ_BOPM_DETECTED 0x04 // Beginning of Partition/Medium Detected #define SK_RECOVERED 1 // Recovered error #define ASC_ATA_PASS_THRU 0x00 #define ASCQ_ATA_PASS_THRU_INFO_AVAIL 0x1d #define SK_NOT_RDY 2 // Device not ready #define ASC_NO_SEEK_COMPLETE 0x02 #define ASC_NOT_READY 0x04 #define ASCQ_CAUSE_NOT_REPORTABLE 0x00 #define ASCQ_BECOMING_READY 0x01 #define ASCQ_INIT_COMMAND_REQUIRED 0x02 #define ASCQ_MANUAL_INTERVENTION_REQUIRED 0x03 #define ASCQ_FORMAT_IN_PROGRESS 0x04 #define ASCQ_UNKNOWN_CHANGED 0xff // NTO extension for fdc's #define ASC_MEDIA_FORMAT 0x30 // bad format #define ASC_MEDIA_NOT_PRESENT 0x3a #define ASC_NOT_CONFIGURED 0x3e #define SK_MEDIUM 3 // Medium error #define ASC_UNRECOVERABLE_READ_ERROR 0x11 #define ASC_RECORD_NOT_FOUND 0x14 #define ASCQ_RECORD_NOT_FOUND 0x01 #define ASC_UNABLE_TO_RECOVER_TOC 0x57 #define ASC_INCOMPATIBLE_MEDIUM 0x64 #define SK_HARDWARE 4 // Hardware error #define ASC_INTERNAL_TARGET_FAILURE 0x44 #define ASC_MEDIA_LOAD_EJECT_FAILURE 0x53 #define ASCQ_UNRECOVERABLE_CIRC 0x06 #define SK_ILLEGAL 5 // Illegal Request (bad command) #define ASC_INVALID_COMMAND 0x20 #define ASC_INVALID_FIELD 0x24 #define ASC_INVALID_FIELD_PARAMETER 0x26 #define ASC_COMMAND_SEQUENCE_ERROR 0x2c #define ASCQ_READ_SCRAMBLED 0x03 #define ASC_ILLEGAL_MODE 0x64 #define ASC_COPY_PROTECTION 0x6f #define SK_UNIT_ATN 6 // Unit Attention #define ASC_MEDIUM_CHANGED 0x28 #define ASC_BUS_RESET 0x29 #define ASC_INSUFFICIENT_TIME_FOR_OPERATION 0x2e #define ASC_OPERATOR_REQUEST 0x5a #define ASCQ_OPERATOR_MEDIUM_REMOVAL 0x01 #define SK_DATA_PROT 7 // Data Protect #define ASC_WRITE_PROTECTED 0x27 #define SK_BLNK_CHK 8 // Blank Check #define SK_VENDOR 9 // Vendor Specific #define SK_CPY_ABORT 10 // Copy Aborted #define SK_CMD_ABORT 11 // Aborted Command #define SK_EQUAL 12 // Equal #define SK_VOL_OFL 13 // Volume Overflow #define SK_MISCMP 14 // Miscompare #define SK_RESERVED 15 // Reserved //---------------------------------------------------------------------------------------------------------- // Command Descriptor Block structure definitions // CDB Flags #define CF_LINK 0x01 // Linked-command indication #define CF_FLAG 0x02 // Linked-command with flag bit #define CF_VENDOR0 0x40 // Vendor unique bits #define CF_VENDOR1 0x80 #define CF_FUA 0x08 #define CF_DPO 0x10 typedef union _cdb { // generic 6 byte command descriptor block struct { uchar_t opcode; uchar_t lun_opt; uchar_t lba_byte1; uchar_t lba_byte0; // LSB uchar_t transfer_len; uchar_t control; } gen6; // generic 10 byte command descriptor block struct { uchar_t opcode; uchar_t lun_opt; uchar_t lba_byte3; uchar_t lba_byte4; uchar_t lba_byte1; uchar_t lba_byte0; uchar_t rsvd; uchar_t transfer_len[2]; uchar_t control; } gen10; // generic 12 byte command descriptor block struct { uchar_t opcode; uchar_t lun_opt; uchar_t lba_byte3; uchar_t lba_byte4; uchar_t lba_byte1; uchar_t lba_byte0; uchar_t transfer_len[4]; uchar_t rsvd10; uchar_t control; } gen12; struct _format_unit { uchar_t op_code; #define FU_RSVD0 0xc0 // reserved bits #define FU_FMTDAT 0x10 #define FU_CMPLIST 0x08 uchar_t defect_list_fmt; uchar_t track_num; ushort_t interleave; uchar_t rsvd1[7]; } format_unit; struct _format_unit_old { uchar_t op_code; uchar_t rsvd0; uchar_t medium_type_code; uchar_t rsvd1; uchar_t interleave; uchar_t rsvd2; #define FMT_RSVD3 0x80 #define FMT_SECT_SIZE_CD 0x70 #define FMT_IMMED 0x08 #define FMT_HEAD 0x04 #define FMT_ST 0x02 #define FMT_CERT 0x01 uchar_t cert; uchar_t track_addr; uchar_t rsvd4[4]; } format_unit_old; #define RW_OPT_RELADR 0x01 #define RW_OPT_CORRCT 0x02 // Disable Corrections #define RW_OPT_FUA 0x08 // Force Unit Access #define RW_OPT_DPO 0x10 // Disable Page Out struct { uchar_t opcode; uchar_t lun_lba; uchar_t lba[2]; uchar_t transfer_len; uchar_t control; } read_write6; struct { uchar_t opcode; uchar_t lun_opt; uchar_t lba[4]; uchar_t rsvd2; uchar_t transfer_len[2]; uchar_t control; } read_write10; struct { uchar_t opcode; uchar_t lun_opt; uchar_t lba[4]; uchar_t transfer_len[4]; uchar_t rsvd2; uchar_t control; } read_write12; #define MSEL_OPT_PF 0x10 // Page Format #define MSEL_OPT_SP 0x01 // Save Page struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t param_length; uchar_t control; } mode_select; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t param_length[2]; uchar_t control; } mode_select10; struct { uchar_t opcode; #define LS_OPT_SP 0x01 // Save Parameters #define LS_OPT_PCR 0x02 // Parameter Code Reset uchar_t lun_opt; #define LS_PC_CUR_THRESHOLD 0x00 #define LS_PC_CUR_CUMULATIVE 0x01 #define LS_PC_DFLT_THRESHOLD 0x02 #define LS_PC_DFLT_CUMULATIVE 0x03 uchar_t pc; // Page Control uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t param_length[2]; uchar_t control; } log_select; struct { uchar_t opcode; #define MSNS_OPT_DBD 0x08 // Disable Block Descriptors uchar_t lun_opt; #define PC_CURRENT 0x00 #define PC_CHANGEABLE 0x40 #define PC_DEFAULT 0x80 #define PC_SAVED 0xC0 #define PC_MSK 0xC0 uchar_t pc_page; uchar_t subpage; uchar_t allocation_length; uchar_t control; } mode_sense; struct _mode_sense10 { uchar_t opcode; uchar_t lun_opt; uchar_t pc_page; uchar_t subpage; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t allocation_length[2]; uchar_t control; } mode_sense10; struct { uchar_t opcode; uchar_t lun_opt; uchar_t pc_page; uchar_t rsvd3; uchar_t rsvd4; uchar_t parameter_pointer[2]; uchar_t allocation_length[2]; uchar_t control; } log_sense; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t prevent; uchar_t control; } removal; struct { uchar_t opcode; #define LD_OPT_IMMED 0x01 uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; #define LD_CMD_START 0x01 #define LD_CMD_LOEJ 0x02 #define LD_CMD_STOP 0x00 #define LD_CMD_EJECT 0x02 #define LD_CMD_LOAD 0x03 // Sequential-Access #define LD_CMD_SA_HOLD 0x08 #define LD_CMD_SA_EOT 0x04 #define LD_CMD_SA_RT 0x02 // re-tension #define LD_CMD_SA_LOEJ 0x01 // Block #define LD_CMD_PC_MSK 0xf0 #define LD_CMD_PC_NC 0 #define LD_CMD_PC_ACTIVE 1 #define LD_CMD_PC_IDLE 2 #define LD_CMD_PC_STANDBY 3 #define LD_CMD_PC_SLEEP 5 uchar_t cmd; uchar_t control; } load; struct { uchar_t opcode; uchar_t lun_opt; #define SC_OPT_RELADR 0x01 #define SC_OPT_IMMED 0x02 uchar_t lba[4]; uchar_t num_blocks[2]; uchar_t control; } synchronize_cache; // cdrom commands struct { uchar_t opcode; uchar_t rsvd1; uchar_t rsvd2; uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t allocation_length[2]; uchar_t control; } read_disc_information; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t rsvd7; uchar_t resume; uchar_t control; } pause_resume; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t start_minute; uchar_t start_second; uchar_t start_frame; uchar_t end_minute; uchar_t end_second; uchar_t end_frame; uchar_t control; } play_audio_msf; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t start_track; uchar_t start_index; uchar_t rsvd6; uchar_t end_track; uchar_t end_index; uchar_t control; } play_audio_ti; struct { uchar_t opcode; #define CD_SCAN_DIR_FORWARD 0x00 #define CD_SCAN_DIR_REVERSE 0x10 uchar_t opt; uchar_t start_address[4]; #define CD_SCAN_TYPE_LBA 0x00 #define CD_SCAN_TYPE_MSF 0x40 #define CD_SCAN_TYPE_TRK 0x80 #define CD_SCAN_TYPE_MSK 0xc0 uchar_t rsvd6; uchar_t rsvd7; uchar_t rsvd8; uchar_t type; uchar_t rsvd10; uchar_t rsvd11; } cd_scan; struct { uchar_t opcode; #define RTOC_OPT_MSF 0x02 uchar_t lun_opt; #define RTOC_FMT_TOC 0x0 #define RTOC_FMT_SESSION 0x1 #define RTOC_FMT_QSUBCODE 0x2 #define RTOC_FMT_QSUBCHNL 0x3 #define RTOC_FMT_ATIP 0x4 #define RTOC_FMT_CDTEXT 0x5 uchar_t format; uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t start_track; uchar_t allocation_length[2]; #define RTOC_CNTL_FMT_SESSION 0x40 uchar_t control_format; } read_toc; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2[6]; uchar_t allocation_length[2]; uchar_t rsvd3[2]; } mechanism_status; struct { uchar_t opcode; #define EXCHANGE_OPT_IMMED 0x01 uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; #define EXCHANGE_CMD_START 0x01 #define EXCHANGE_CMD_LOEJ 0x02 uchar_t cmd; uchar_t rsvd5; uchar_t rsvd6; uchar_t rsvd7; uchar_t slot; uchar_t rsvd9; uchar_t rsvd10; uchar_t rsvd11; } exchange; struct { uchar_t opcode; uchar_t rt; uchar_t feature_number[2]; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t allocation_length[2]; uchar_t control; } get_configuration; struct { uchar_t opcode; #define GE_OPT_POLLED 0x01 uchar_t opt; uchar_t rsvd2; uchar_t rsvd3; #define NCR_OPERATIONAL_CHANGE 0x02 #define NCR_POWER_MANAGEMENT 0x04 #define NCR_EXTERNAL_REQUEST 0x08 #define NCR_MEDIA 0x10 #define NCR_MULTI_INITIATOR 0x20 #define NCR_DEVICE_BUSY 0x40 uchar_t ncr; // notification class request uchar_t rsvd5; uchar_t rsvd6; uchar_t allocation_length[2]; uchar_t control; } get_event; struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2; uchar_t rsvd3; uchar_t rsvd4; uchar_t rsvd5; uchar_t rsvd6; uchar_t allocation_length[2]; uchar_t control; } read_formated_capacities; struct { uchar_t opcode; uchar_t lun_opt; uchar_t read_speed[2]; uchar_t write_speed[2]; uchar_t rsvd2[6]; } cd_speed; struct { uchar_t opcode; #define RSCHNL_OPT_MSF 0x02 uchar_t lun_opt; #define RSCHNL_DATA_SUBQ 0x40 uchar_t data; uchar_t data_format; uchar_t rsvd4; uchar_t rsvd5; uchar_t track; uchar_t allocation_length[2]; uchar_t control; } read_subchannel; #define CD_FRAME_SYNC_SIZE 12 #define CD_FRAME_HDR_SIZE 4 #define CD_FRAME_SUB_HDR_SIZE 8 #define CD_FRAME_EDC_SIZE 4 #define CD_FRAME_ECC_SIZE 276 #define CD_FRAME_AUX_SIZE 8 #define CD_FRAME_ZERO_SIZE 8 #define CD_FRAME_SPARE_SIZE 4 #define CD_FRAME_C2_ERR_SIZE 294 #define CD_FRAME_BLOCK_ERR_SIZE 2 struct { uchar_t opcode; uchar_t lun_stype; // expected sector type #define RDCD_EST_ANY_SECTOR (0 << 2) #define RDCD_EST_CDDA_SECTOR (1 << 2) #define RDCD_EST_YELLOW_MODE1_SECTOR (2 << 2) #define RDCD_EST_YELLOW_MODE2_SECTOR (3 << 2) #define RDCD_EST_XA_SECTOR (4 << 2) #define RDCD_EST_XA_FORM2_SECTOR (5 << 2) #define RDCD_EST_MSK (7 << 2) uchar_t lba[4]; uchar_t transfer_len[3]; uchar_t flags; #define RDCD_FLG_SYNC 0x80 #define RDCD_FLG_UDATA 0x10 #define RDCD_FLG_ECC 0x08 #define RDCD_FLG_CD_ERR 0x02 #define RDCD_FLG_CD_BLOCK_ERR 0x04 #define RDCD_FLG_HC_NONE ( 0x00 << 5 ) #define RDCD_FLG_HC_HDR ( 0x01 << 5 ) #define RDCD_FLG_HC_SUBHEADER ( 0x02 << 5 ) #define RDCD_FLG_HC_ALL_HEADERS ( 0x03 << 5 ) uchar_t subch_selection; uchar_t rsvd3; } read_cd; struct { uchar_t opcode; uchar_t lun_stype; uchar_t rsvd2; uchar_t start_minute; uchar_t start_second; uchar_t start_frame; uchar_t end_minute; uchar_t end_second; uchar_t end_frame; uchar_t flags; uchar_t subch_selection; uchar_t rsvd11; } read_cd_msf; struct _ata_pass_thru { uchar_t opcode; #define ATA_PROTO_MSK 0x1e #define ATA_PROTO_RESPONSE (15 << 1) #define ATA_PROTO_FPDMA (12 << 1) #define ATA_PROTO_UDMA_DATA_OUT (11 << 1) #define ATA_PROTO_UDMA_DATA_IN (10 << 1) #define ATA_PROTO_DEVICE_RESET (9 << 1) #define ATA_PROTO_DEVICE_DIAGNOSTIC (8 << 1) #define ATA_PROTO_DMA_QUEUED (7 << 1) #define ATA_PROTO_DMA (6 << 1) #define ATA_PROTO_PIO_DATA_OUT (5 << 1) #define ATA_PROTO_PIO_DATA_IN (4 << 1) #define ATA_PROTO_DATA_NONE (3 << 1) #define ATA_PROTO_SRST (1 << 1) #define ATA_PROTO_HRST (0 << 1) #define ATA_PROTO_EXTEND 0x01 uchar_t protocol; // multiple count, protocol #define ATA_MCOUNT_MSK 0xe0 #define ATA_FLG_CK_COND 0x20 #define ATA_FLG_T_DIR 0x08 // data from device #define ATA_FLG_BYT_BLOK 0x04 #define ATA_FLG_TLEN_STPSIU 0x03 #define ATA_FLG_TLEN_SECTOR_COUNT 0x02 #define ATA_FLG_TLEN_FEATURE 0x01 uchar_t flags; uchar_t efeatures; uchar_t features; uchar_t esector_count; uchar_t sector_count; uchar_t elba_low; uchar_t lba_low; uchar_t elba_mid; uchar_t lba_mid; uchar_t elba_high; uchar_t lba_high; uchar_t device; uchar_t command; uchar_t control; } ata_pass_thru; // sequential access commands struct { uchar_t opcode; #define ERASE_OPT_LONG 0x01 uchar_t opt; uchar_t rsvd[3]; uchar_t control; } erase; struct { uchar_t opcode; #define LOCATE_OPT_CP 0x2 #define LOCATE_OPT_BT 0x4 uchar_t opt; uchar_t rsvd2; uchar_t ba[4]; // block address uchar_t rsvd7; uchar_t partition; uchar_t control; } locate; struct { uchar_t opcode; uchar_t opt; uchar_t rsvd2[3]; uchar_t control; } read_block_limits; #define RP_OPT_BT 0x01 // block address type #define RP_OPT_LNG 0x02 // long format #define RP_OPT_TCLP 0x04 // total current logical position struct { uchar_t opcode; uchar_t lun_opt; uchar_t rsvd2[7]; uchar_t control; } read_position; #define SRW_OPT_FIXED 0x01 #define SRW_OPT_SILI 0x02 struct { uchar_t opcode; uchar_t opt; uchar_t transfer_len[3]; uchar_t control; } sa_read_write; struct { uchar_t opcode; uchar_t opt; uchar_t rsvd[3]; uchar_t control; } rewind; struct { uchar_t opcode; #define SPACE_CODE_BLOCKS 0x00 #define SPACE_CODE_FMRKS 0x01 #define SPACE_CODE_SEQ_FMRKS 0x02 #define SPACE_CODE_EOD 0x03 #define SPACE_CODE_SMRKS 0x04 #define SPACE_CODE_SEQ_SMRKS 0x05 uchar_t lun_code; uchar_t count[3]; uchar_t control; } space; struct { uchar_t opcode; #define WF_OPT_IMMED 0x01 #define WF_OPT_WSMK 0x02 uchar_t opt; uchar_t transfer_length[3]; uchar_t control; } write_filemarks; struct { uchar_t opcode; #define RD_OPT_MEDIA 0x01 uchar_t opt; uchar_t rsvd[5]; uchar_t allocation_length[2]; uchar_t control; } report_density; struct { uchar_t opcode; #define FM_OPT_IMMED 0x01 #define FM_OPT_VERIFY 0x02 uchar_t opt; #define FM_FMT_DFLT 0x00 #define FM_FMT_PARTITION 0x01 #define FM_FMT_FORMAT_PARTITION 0x02 uchar_t format; uchar_t transfer_length[2]; uchar_t control; } format_media; } CDB; //---------------------------------------------------------------------------------------------------------- struct _ata_sense { SCSI_SENSE_DESCRIPTOR sense; ATA_STATUS_DESCRIPTOR desc; }; //---------------------------------------------------------------------------------------------------------- #endif /* OS_QNXNTO_H_ */ smartmontools-7.0/os_solaris.cpp0000644000175000010010000002762013357201326014101 00000000000000/* * os_solaris.c * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-08 SAWADA Keiji * Copyright (C) 2003-15 Casper Dik * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include // These are needed to define prototypes for the functions defined below #include "config.h" #include "atacmds.h" #include "scsicmds.h" #include "utility.h" // This is to include whatever prototypes you define in os_solaris.h #include "os_solaris.h" #define ARGUSED(x) ((void)(x)) const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 4805 2018-10-09 19:34:46Z chrfranke $" \ ATACMDS_H_CVSID CONFIG_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" " 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" ); return; } static const char *uscsidrvrs[] = { "sd", "ssd", "disk", // SATA devices "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 = static_cast(realloc(res->names, res->maxnames * sizeof (char *))); if (res->names == NULL) return -1; } if (!(res->names[res->nnames-1] = strdup(path))) 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 = static_cast(realloc(res.names, res.nnames * sizeof (char *))); // 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); } #if defined(WITH_SOLARIS_SPARC_ATA) // swap each 2-byte pairs in a sector static void swap_sector(void *p) { int i; char t, *cp = static_cast(p); for(i = 0; i < 256; i++) { t = cp[0]; cp[0] = cp[1]; cp[1] = t; cp += 2; } } #endif // Interface to ATA devices. See os_linux.c int ata_command_interface(int fd, smart_command_set command, int select, char *data){ #if defined(WITH_SOLARIS_SPARC_ATA) 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(static_cast(data)); return 0; case PIDENTIFY: err = ata_pidentify(fd, data); if(err) return err; swap_sector(static_cast(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); errno = EINVAL; return -1; } #else /* WITH_SOLARIS_SPARC_ATA */ ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data); /* 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; } #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]); pout("]\n"); if ((report > 1) && (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; pout(" 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); } } memset(&uscsi, 0, sizeof (uscsi)); uscsi.uscsi_cdb = reinterpret_cast(iop->cmnd); uscsi.uscsi_cdblen = iop->cmnd_len; if (iop->timeout == 0) uscsi.uscsi_timeout = 60; /* 60 seconds */ else uscsi.uscsi_timeout = iop->timeout; uscsi.uscsi_bufaddr = reinterpret_cast(iop->dxferp); uscsi.uscsi_buflen = iop->dxfer_len; uscsi.uscsi_rqbuf = reinterpret_cast(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 | USCSI_RQENABLE | USCSI_SILENT); if (ioctl(fd, USCSICMD, &uscsi)) { int err = errno; if (! ((EIO == err) && uscsi.uscsi_status)) return -err; /* errno is set to EIO when a non-zero SCSI completion status given */ } 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; int len = iop->resp_sense_len; if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && iop->sensep && (len > 3)) { if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12], iop->sensep[13]); if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", len); dStrHex(iop->sensep, ((len > 252) ? 252 : len) , 1); } } else if (iop->scsi_status) pout(" status=%x\n", iop->scsi_status); if (iop->resid) pout(" dxfer_len=%d, resid=%d\n", iop->dxfer_len, iop->resid); if (report > 1) { len = iop->dxfer_len - iop->resid; if (len > 0) { trunc = (len > 256) ? 1 : 0; pout(" Incoming data, len=%d%s:\n", len, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : len) , 1); } } } return 0; } smartmontools-7.0/os_solaris.h0000644000175000010010000000254613336335341013550 00000000000000/* * os_solaris.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2003-8 SAWADA Keiji * Copyright (C) 2003-8 Casper Dik * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef OS_SOLARIS_H_ #define OS_SOLARIS_H_ #define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 4760 2018-08-19 18:45:53Z chrfranke $\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 extern "C" { 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_ */ smartmontools-7.0/os_win32/0000755000175000010010000000000013412155413012731 500000000000000smartmontools-7.0/os_win32/daemon_win32.cpp0000644000175000010010000007072113401001476015646 00000000000000/* * os_win32/daemon_win32.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #define WINVER 0x0600 #define _WIN32_WINNT WINVER #include "daemon_win32.h" const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 4842 2018-12-02 16:07:26Z chrfranke $" DAEMON_WIN32_H_CVSID; #include #include #include #include #define WIN32_LEAN_AND_MEAN #include #ifdef _DEBUG #include #endif ///////////////////////////////////////////////////////////////////////////// // 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; } ///////////////////////////////////////////////////////////////////////////// // Initd Functions static int wait_signaled(HANDLE h, int seconds) { int i; for (i = 0; ; ) { if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) return 0; if (++i >= seconds) return -1; fputchar('.'); fflush(stdout); } } static int wait_evt_running(int seconds, int exists) { int i; if (event_exists(EVT_RUNNING) == exists) return 0; for (i = 0; ; ) { Sleep(1000); if (event_exists(EVT_RUNNING) == exists) return 0; if (++i >= seconds) return -1; fputchar('.'); fflush(stdout); } } static int is_initd_command(char * s) { if (!strcmp(s, "status")) return EVT_RUNNING; if (!strcmp(s, "stop")) return SIGTERM; if (!strcmp(s, "reload")) return SIGHUP; if (!strcmp(s, "sigusr1")) return SIGUSR1; if (!strcmp(s, "sigusr2")) return SIGUSR2; if (!strcmp(s, "restart")) return EVT_RESTART; return -1; } static int initd_main(const char * ident, int argc, char **argv) { int rc; if (argc < 2) return -1; if ((rc = is_initd_command(argv[1])) < 0) return -1; if (argc != 2) { printf("%s: no arguments allowed for command %s\n", ident, argv[1]); return 1; } switch (rc) { default: case EVT_RUNNING: printf("Checking for %s:", ident); fflush(stdout); rc = event_exists(EVT_RUNNING); puts(rc ? " running" : " not running"); return (rc ? 0 : 1); case SIGTERM: printf("Stopping %s:", ident); fflush(stdout); rc = sig_event(SIGTERM); if (rc <= 0) { puts(rc < 0 ? " not running" : " error"); return (rc < 0 ? 0 : 1); } rc = wait_evt_running(10, 0); puts(!rc ? " done" : " timeout"); return (!rc ? 0 : 1); case SIGHUP: printf("Reloading %s:", ident); fflush(stdout); rc = sig_event(SIGHUP); puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); return (rc > 0 ? 0 : 1); case SIGUSR1: case SIGUSR2: printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); rc = sig_event(rc); puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); return (rc > 0 ? 0 : 1); case EVT_RESTART: { HANDLE rst; printf("Stopping %s:", ident); fflush(stdout); if (event_exists(EVT_DETACHED)) { puts(" not detached, cannot restart"); return 1; } if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { puts(" error"); return 1; } rc = sig_event(SIGTERM); if (rc <= 0) { puts(rc < 0 ? " not running" : " error"); CloseHandle(rst); return 1; } rc = wait_signaled(rst, 10); CloseHandle(rst); if (rc) { puts(" timeout"); return 1; } puts(" done"); Sleep(100); printf("Starting %s:", ident); fflush(stdout); rc = wait_evt_running(10, 1); puts(!rc ? " done" : " error"); return (!rc ? 0 : 1); } } } ///////////////////////////////////////////////////////////////////////////// // Windows Service Functions int daemon_winsvc_exitcode; // Set by app to exit(code) static SERVICE_STATUS_HANDLE svc_handle; static SERVICE_STATUS svc_status; // Report status to SCM static void service_report_status(int state, int seconds) { // TODO: Avoid race static DWORD checkpoint = 1; svc_status.dwCurrentState = state; svc_status.dwWaitHint = seconds*1000; switch (state) { default: svc_status.dwCheckPoint = checkpoint++; break; case SERVICE_RUNNING: case SERVICE_STOPPED: svc_status.dwCheckPoint = 0; } switch (state) { case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: svc_status.dwControlsAccepted = 0; break; default: svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN| SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE; break; } SetServiceStatus(svc_handle, &svc_status); } // Control the service, called by SCM static void WINAPI service_control(DWORD ctrlcode) { switch (ctrlcode) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: service_report_status(SERVICE_STOP_PENDING, 30); svc_paused = 0; 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; // Register control handler svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); // Init service status svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_report_status(SERVICE_START_PENDING, 10); // 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 // Make registry key name for event message file static bool make_evtkey(char * buf, unsigned size, const char * ident) { static const char prefix[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\"; const unsigned pfxlen = sizeof(prefix)-1; unsigned idlen = strlen(ident); if (pfxlen + idlen >= size) { printf(" Buffer overflow\n"); return false; } memcpy(buf, prefix, pfxlen); memcpy(buf+pfxlen, ident, idlen+1); return true; } // Install this exe as event message file static void inst_evtmsg(const char * ident) { printf("Installing event message file for %s:", ident); fflush(stdout); char mypath[MAX_PATH]; if (!GetModuleFileNameA((HMODULE)0, mypath, sizeof(mypath))) { printf(" unknown program path, Error=%ld\n", GetLastError()); return; } char subkey[MAX_PATH]; if (!make_evtkey(subkey, sizeof(subkey), ident)) return; HKEY hk; LONG err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, (char *)0, 0, KEY_ALL_ACCESS, (SECURITY_ATTRIBUTES *)0, &hk, (DWORD *)0); if (err != ERROR_SUCCESS) { printf(" RegCreateKeyEx failed, error=%ld\n", err); return; } err = RegSetValueExA(hk, "EventMessageFile", 0, REG_SZ, (const BYTE *)mypath, strlen(mypath)+1); if (err == ERROR_SUCCESS) { DWORD val = EVENTLOG_INFORMATION_TYPE |EVENTLOG_WARNING_TYPE |EVENTLOG_ERROR_TYPE; err = RegSetValueExA(hk, "TypesSupported", 0, REG_DWORD, (const BYTE *)&val, sizeof(val)); } if (err != ERROR_SUCCESS) printf(" RegSetValueEx failed, error=%ld\n", err); RegCloseKey(hk); puts(" done"); } // Uninstall event message file static void uninst_evtmsg(const char * ident) { printf("Removing event message file for %s:", ident); fflush(stdout); char subkey[MAX_PATH]; if (!make_evtkey(subkey, sizeof(subkey), ident)) return; LONG err = RegDeleteKeyA(HKEY_LOCAL_MACHINE, subkey); if (err != ERROR_SUCCESS && err != ERROR_FILE_NOT_FOUND) { printf(" RegDeleteKey failed, error=%ld\n", err); return; } puts(" done"); } // 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 printf(" cannot open SCManager, Error=%ld\n", err); return 1; } if (!remove) { char path[MAX_PATH+100]; int i; // Get program path if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { printf(" unknown program path, Error=%ld\n", GetLastError()); CloseServiceHandle(hm); return 1; } // Add quotes if necessary if (strchr(path, ' ')) { i = strlen(path); path[i+1] = '"'; path[i+2] = 0; while (--i >= 0) path[i+1] = path[i]; path[0] = '"'; } // Append options strcat(path, " "); strcat(path, svc_opts->cmd_opt); for (i = 2; i < argc; i++) { const char * s = argv[i]; if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) break; // Add quotes if necessary if (strchr(s, ' ') && !strchr(s, '"')) { strcat(path, " \""); strcat(path, s); strcat(path, "\""); } else { strcat(path, " "); strcat(path, s); } } // Create if (!(hs = CreateService(hm, svc_opts->svcname, svc_opts->dispname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, NULL/*no load ordering*/, NULL/*no tag id*/, ""/*no dependencies*/, NULL/*local system account*/, NULL/*no pw*/))) { if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) puts(" the service is already installed"); else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) puts(" service is still running and marked for deletion\n" "Stop the service and retry install"); else printf(" failed, Error=%ld\n", err); CloseServiceHandle(hm); return 1; } // Set optional description if (svc_opts->descript) { SERVICE_DESCRIPTIONA sd = { const_cast(svc_opts->descript) }; ChangeServiceConfig2A(hs, SERVICE_CONFIG_DESCRIPTION, &sd); } // Enable delayed auto start if supported OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver); if ( GetVersionExA(&ver) && ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 6 /* Vista */ ) { // SERVICE_{,CONFIG_}DELAYED_AUTO_START_INFO are missing in older MinGW headers struct /* SERVICE_DELAYED_AUTO_START_INFO */ { BOOL fDelayedAutostart; } sdasi = { TRUE }; // typedef char ASSERT_sizeof_sdasi[sizeof(sdasi) == sizeof(SERVICE_DELAYED_AUTO_START_INFO) ? 1 : -1]; // typedef char ASSERT_const_scdasi[SERVICE_CONFIG_DELAYED_AUTO_START_INFO == 3 ? 1 : -1]; ChangeServiceConfig2A(hs, 3 /* SERVICE_CONFIG_DELAYED_AUTO_START_INFO */, &sdasi); } } else { // Open if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { puts(" not found"); CloseServiceHandle(hm); return 1; } // TODO: Stop service if running // Remove if (!DeleteService(hs)) { if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) puts(" service is still running and marked for deletion\n" "Stop the service to remove it"); else printf(" failed, Error=%ld\n", err); CloseServiceHandle(hs); CloseServiceHandle(hm); return 1; } } puts(" done"); CloseServiceHandle(hs); CloseServiceHandle(hm); // Install/Remove event message file registry entry if (!remove) { inst_evtmsg(ident); } else { uninst_evtmsg(ident); } return 0; } ///////////////////////////////////////////////////////////////////////////// // 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 smartmontools-7.0/os_win32/daemon_win32.h0000644000175000010010000000261313361544541015320 00000000000000/* * os_win32/daemon_win32.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DAEMON_WIN32_H #define DAEMON_WIN32_H #define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 4818 2018-10-17 05:32:17Z chrfranke $" #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); #endif // DAEMON_WIN32_H smartmontools-7.0/os_win32/default.manifest0000644000175000010010000000172512622134616016036 00000000000000 smartmontools-7.0/os_win32/installer.nsi0000644000175000010010000010066713336335341015400 00000000000000; ; os_win32/installer.nsi - smartmontools install NSIS script ; ; Home page of code is: http://www.smartmontools.org ; ; Copyright (C) 2006-17 Christian Franke ; ; SPDX-License-Identifier: GPL-2.0-or-later ; ; $Id: installer.nsi 4760 2018-08-19 18:45:53Z chrfranke $ ; ;-------------------------------------------------------------------- ; Command line arguments: ; makensis -DINPDIR= -DINPDIR64= \ ; -DOUTFILE= -DVERSTR= installer.nsi !ifndef INPDIR !define INPDIR "." !endif !ifndef OUTFILE !define OUTFILE "smartmontools.win32-setup.exe" !endif ;-------------------------------------------------------------------- ; General Name "smartmontools" OutFile "${OUTFILE}" RequestExecutionLevel admin SetCompressor /solid lzma XPStyle on InstallColors /windows ; Set in .onInit ;InstallDir "$PROGRAMFILES\smartmontools" ;InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation" !ifdef VERSION VIProductVersion "${VERSION}" VIAddVersionKey /LANG=1033-English "CompanyName" "www.smartmontools.org" VIAddVersionKey /LANG=1033-English "FileDescription" "SMART Monitoring Tools" VIAddVersionKey /LANG=1033-English "FileVersion" "${VERSION}" !ifdef YY VIAddVersionKey /LANG=1033-English "LegalCopyright" "(C) 2002-20${YY}, Bruce Allen, Christian Franke, www.smartmontools.org" !endif VIAddVersionKey /LANG=1033-English "OriginalFilename" "${OUTFILE}" VIAddVersionKey /LANG=1033-English "ProductName" "smartmontools" VIAddVersionKey /LANG=1033-English "ProductVersion" "${VERSION}" !endif Var EDITOR !ifdef INPDIR64 Var X64 Var INSTDIR32 Var INSTDIR64 !endif LicenseData "${INPDIR}\doc\COPYING.txt" !include "FileFunc.nsh" !include "LogicLib.nsh" !include "Sections.nsh" ;-------------------------------------------------------------------- ; Pages Page license Page components !ifdef INPDIR64 Page directory CheckX64 !else Page directory !endif Page instfiles UninstPage uninstConfirm UninstPage instfiles InstType "Full" InstType "Extract files only" InstType "Drive menu" !ifdef INPDIR64 InstType "Full (x64)" InstType "Extract files only (x64)" InstType "Drive menu (x64)" !endif ;-------------------------------------------------------------------- ; Sections !ifdef INPDIR64 Section "64-bit version" X64_SECTION SectionIn 4 5 6 ; Handled in Function CheckX64 SectionEnd !define FULL_TYPES "1 4" !define EXTRACT_TYPES "2 5" !define DRIVEMENU_TYPE "3 6" !else !define FULL_TYPES "1" !define EXTRACT_TYPES "2" !define DRIVEMENU_TYPE "3" !endif SectionGroup "!Program files" !macro FileExe path option !ifdef INPDIR64 ; Use dummy SetOutPath to control archive location of executables ${If} $X64 != "" Goto +2 SetOutPath "$INSTDIR\bin64" File ${option} '${INPDIR64}\${path}' ${Else} Goto +2 SetOutPath "$INSTDIR\bin" File ${option} '${INPDIR}\${path}' ${EndIf} !else File ${option} '${INPDIR}\${path}' !endif !macroend Section "smartctl" SMARTCTL_SECTION SectionIn ${FULL_TYPES} ${EXTRACT_TYPES} SetOutPath "$INSTDIR\bin" !insertmacro FileExe "bin\smartctl.exe" "" SectionEnd Section "smartd" SMARTD_SECTION SectionIn ${FULL_TYPES} ${EXTRACT_TYPES} SetOutPath "$INSTDIR\bin" ; Stop service ? StrCpy $1 "" ${If} ${FileExists} "$INSTDIR\bin\smartd.exe" ReadRegStr $0 HKLM "System\CurrentControlSet\Services\smartd" "ImagePath" ${If} $0 != "" ExecWait "net stop smartd" $1 ${EndIf} ${EndIf} !insertmacro FileExe "bin\smartd.exe" "" IfFileExists "$INSTDIR\bin\smartd.conf" 0 +2 MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Replace existing configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO +2 File "${INPDIR}\doc\smartd.conf" File "${INPDIR}\bin\smartd_mailer.ps1" File "${INPDIR}\bin\smartd_mailer.conf.sample.ps1" File "${INPDIR}\bin\smartd_warning.cmd" !insertmacro FileExe "bin\wtssendmsg.exe" "" ; Restart service ? ${If} $1 == "0" MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Restart smartd service ?" /SD IDNO IDYES 0 IDNO +2 ExecWait "net start smartd" ${EndIf} SectionEnd Section "smartctl-nc (GSmartControl)" SMARTCTL_NC_SECTION SectionIn ${FULL_TYPES} ${EXTRACT_TYPES} SetOutPath "$INSTDIR\bin" !insertmacro FileExe "bin\smartctl-nc.exe" "" SectionEnd Section "drivedb.h (Drive Database)" DRIVEDB_SECTION SectionIn ${FULL_TYPES} ${EXTRACT_TYPES} SetOutPath "$INSTDIR\bin" File "${INPDIR}\bin\drivedb.h" File "${INPDIR}\bin\update-smart-drivedb.exe" SectionEnd SectionGroupEnd Section "!Documentation" DOC_SECTION SectionIn ${FULL_TYPES} ${EXTRACT_TYPES} SetOutPath "$INSTDIR\doc" File "${INPDIR}\doc\AUTHORS.txt" File "${INPDIR}\doc\ChangeLog.txt" File "${INPDIR}\doc\ChangeLog-5.0-6.0.txt" File "${INPDIR}\doc\COPYING.txt" File "${INPDIR}\doc\INSTALL.txt" File "${INPDIR}\doc\NEWS.txt" File "${INPDIR}\doc\README.txt" File "${INPDIR}\doc\TODO.txt" !ifdef INPDIR64 ${If} $X64 != "" File "${INPDIR64}\doc\checksums64.txt" ${Else} File "${INPDIR}\doc\checksums32.txt" ${EndIf} !else File "${INPDIR}\doc\checksums??.txt" !endif File "${INPDIR}\doc\smartctl.8.html" File "${INPDIR}\doc\smartctl.8.pdf" Delete "$INSTDIR\doc\smartctl.8.txt" ; TODO: Remove after smartmontools 6.6 File "${INPDIR}\doc\smartd.8.html" File "${INPDIR}\doc\smartd.8.pdf" Delete "$INSTDIR\doc\smartd.8.txt" ; TODO: Remove after smartmontools 6.6 File "${INPDIR}\doc\smartd.conf" File "${INPDIR}\doc\smartd.conf.5.html" File "${INPDIR}\doc\smartd.conf.5.pdf" Delete "$INSTDIR\doc\smartd.conf.5.txt" ; TODO: Remove after smartmontools 6.6 SectionEnd Section "Uninstaller" UNINST_SECTION SectionIn ${FULL_TYPES} AddSize 40 CreateDirectory "$INSTDIR" ; Remove old "Install_Dir" registry entry (smartmontools < r3911/6.3) ; No longer needed for GSmartControl DeleteRegKey HKLM "Software\smartmontools" ; TODO: Remove after smartmontools 6.7 ; Write uninstall keys and program WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayName" "smartmontools" !ifdef VERSTR WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayVersion" "${VERSTR}" !endif ; Important: GSmartControl (>= 1.0.0) reads "InstallLocation" to detect location of bin\smartctl-nc.exe WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "UninstallString" '"$INSTDIR\uninst-smartmontools.exe"' WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "Publisher" "smartmontools.org" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLInfoAbout" "https://www.smartmontools.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "HelpLink" "https://www.smartmontools.org/wiki/Help" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLUpdateInfo" "https://builds.smartmontools.org/" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoRepair" 1 WriteUninstaller "uninst-smartmontools.exe" SectionEnd Section "Start Menu Shortcuts" MENU_SECTION SectionIn ${FULL_TYPES} SetShellVarContext all CreateDirectory "$SMPROGRAMS\smartmontools" !macro CreateAdminShortCut link target args CreateShortCut '${link}' '${target}' '${args}' push '${link}' Call ShellLinkSetRunAs !macroend ; runcmdu ${If} ${FileExists} "$INSTDIR\bin\smartctl.exe" ${OrIf} ${FileExists} "$INSTDIR\bin\smartd.exe" SetOutPath "$INSTDIR\bin" !insertmacro FileExe "bin\runcmdu.exe" "" ${EndIf} ; smartctl ${If} ${FileExists} "$INSTDIR\bin\smartctl.exe" SetOutPath "$INSTDIR\bin" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl (Admin CMD).lnk" "$WINDIR\system32\cmd.exe" '/k PATH=$INSTDIR\bin;%PATH%&cd /d "$INSTDIR\bin"' CreateDirectory "$SMPROGRAMS\smartmontools\smartctl Examples" FileOpen $0 "$SMPROGRAMS\smartmontools\smartctl Examples\!Read this first!.txt" "w" FileWrite $0 "All the example commands in this directory$\r$\napply to the first drive (sda).$\r$\n" FileClose $0 !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\All info (-x).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -x sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Identify drive (-i).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -i sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART attributes (-A -f brief).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -A -f brief sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART capabilities (-c).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -c sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART health status (-H).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -H sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART error log (-l error).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -l error sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART selftest log (-l selftest).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -l selftest sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start long selftest (-t long).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -t long sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start offline test (-t offline).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -t offline sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start short selftest (-t short).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -t short sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Stop(Abort) selftest (-X).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -X sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART off (-s off).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -s off sda" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART on (-s on).lnk" "$INSTDIR\bin\runcmdu.exe" "smartctl -s on sda" ${EndIf} ; smartd ${If} ${FileExists} "$INSTDIR\bin\smartd.exe" SetOutPath "$INSTDIR\bin" CreateDirectory "$SMPROGRAMS\smartmontools\smartd Examples" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon start, smartd.log.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd -l local0" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon start, eventlog.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon stop.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd stop" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Do all tests once (-q onecheck).lnk" "$INSTDIR\bin\runcmdu.exe" "smartd -q onecheck" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Debug mode (-d).lnk" "$INSTDIR\bin\runcmdu.exe" "smartd -d" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd.conf (edit).lnk" "$EDITOR" '"$INSTDIR\bin\smartd.conf"' CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd.conf (view).lnk" "$EDITOR" '"$INSTDIR\bin\smartd.conf"' CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd.log (view).lnk" "$EDITOR" '"$INSTDIR\bin\smartd.log"' CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd_mailer.conf.sample.ps1 (view).lnk" "$EDITOR" '"$INSTDIR\bin\smartd_mailer.conf.sample.ps1"' !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\smartd_mailer.conf.ps1 (create, edit).lnk" "$EDITOR" '"$INSTDIR\bin\smartd_mailer.conf.ps1"' ; smartd service !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, eventlog, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 10min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0 -i 600" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, smartd.log, 30min.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd install -l local0" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service remove.lnk" "$INSTDIR\bin\runcmdu.exe" "smartd remove" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service start.lnk" "$INSTDIR\bin\runcmdu.exe" "net start smartd" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service stop.lnk" "$INSTDIR\bin\runcmdu.exe" "net stop smartd" ${EndIf} ; Documentation ${If} ${FileExists} "$INSTDIR\doc\README.TXT" SetOutPath "$INSTDIR\doc" CreateDirectory "$SMPROGRAMS\smartmontools\Documentation" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartctl manual page (html).lnk" "$INSTDIR\doc\smartctl.8.html" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd manual page (html).lnk" "$INSTDIR\doc\smartd.8.html" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf manual page (html).lnk" "$INSTDIR\doc\smartd.conf.5.html" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartctl manual page (pdf).lnk" "$INSTDIR\doc\smartctl.8.pdf" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd manual page (pdf).lnk" "$INSTDIR\doc\smartd.8.pdf" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf manual page (pdf).lnk" "$INSTDIR\doc\smartd.conf.5.pdf" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf sample.lnk" "$EDITOR" '"$INSTDIR\doc\smartd.conf"' ${If} ${FileExists} "$INSTDIR\bin\drivedb.h" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb.h (view).lnk" "$EDITOR" '"$INSTDIR\bin\drivedb.h"' !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb-add.h (create, edit).lnk" "$EDITOR" '"$INSTDIR\bin\drivedb-add.h"' ${EndIf} CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\ChangeLog.lnk" "$INSTDIR\doc\ChangeLog.txt" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\COPYING.lnk" "$INSTDIR\doc\COPYING.txt" CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\NEWS.lnk" "$INSTDIR\doc\NEWS.txt" ${EndIf} ; Homepage CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Home Page.lnk" "https://www.smartmontools.org/" CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Daily Builds.lnk" "https://builds.smartmontools.org/" ; drivedb.h update ${If} ${FileExists} "$INSTDIR\bin\update-smart-drivedb.exe" SetOutPath "$INSTDIR\bin" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\drivedb.h update.lnk" "$INSTDIR\bin\update-smart-drivedb.exe" "" ${EndIf} ; Uninstall ${If} ${FileExists} "$INSTDIR\uninst-smartmontools.exe" SetOutPath "$INSTDIR" !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\Uninstall smartmontools.lnk" "$INSTDIR\uninst-smartmontools.exe" "" ${EndIf} SectionEnd Section "Add install dir to PATH" PATH_SECTION SectionIn ${FULL_TYPES} Push "$INSTDIR\bin" Call AddToPath SectionEnd SectionGroup "Add smartctl to drive menu" !macro DriveMenuRemove DetailPrint "Remove drive menu entries" DeleteRegKey HKCR "Drive\shell\smartctl0" DeleteRegKey HKCR "Drive\shell\smartctl1" DeleteRegKey HKCR "Drive\shell\smartctl2" DeleteRegKey HKCR "Drive\shell\smartctl3" DeleteRegKey HKCR "Drive\shell\smartctl4" DeleteRegKey HKCR "Drive\shell\smartctl5" !macroend Section "Remove existing entries first" DRIVE_REMOVE_SECTION SectionIn ${DRIVEMENU_TYPE} !insertmacro DriveMenuRemove SectionEnd !macro DriveSection id name args Section 'smartctl ${args} ...' DRIVE_${id}_SECTION SectionIn ${DRIVEMENU_TYPE} Call CheckRunCmdA DetailPrint 'Add drive menu entry "${name}": smartctl ${args} ...' WriteRegStr HKCR "Drive\shell\smartctl${id}" "" "${name}" WriteRegStr HKCR "Drive\shell\smartctl${id}\command" "" '"$INSTDIR\bin\runcmda.exe" "$INSTDIR\bin\smartctl.exe" ${args} %L' SectionEnd !macroend !insertmacro DriveSection 0 "SMART all info" "-x" !insertmacro DriveSection 1 "SMART status" "-Hc" !insertmacro DriveSection 2 "SMART attributes" "-A -f brief" !insertmacro DriveSection 3 "SMART short selftest" "-t short" !insertmacro DriveSection 4 "SMART long selftest" "-t long" !insertmacro DriveSection 5 "SMART continue selective selftest" '-t "selective,cont"' SectionGroupEnd ;-------------------------------------------------------------------- Section "Uninstall" ; Stop & remove service ${If} ${FileExists} "$INSTDIR\bin\smartd.exe" ReadRegStr $0 HKLM "System\CurrentControlSet\Services\smartd" "ImagePath" ${If} $0 != "" ExecWait "net stop smartd" MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Remove smartd service ?" /SD IDNO IDYES 0 IDNO +2 ExecWait "$INSTDIR\bin\smartd.exe remove" ${EndIf} ${EndIf} ; Remove installer registry key DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" ; Remove conf file ? ${If} ${FileExists} "$INSTDIR\bin\smartd.conf" ; Assume unchanged if timestamp is equal to sample file GetFileTime "$INSTDIR\bin\smartd.conf" $0 $1 GetFileTime "$INSTDIR\doc\smartd.conf" $2 $3 StrCmp "$0:$1" "$2:$3" +2 0 MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\smartd.conf" ${EndIf} ; Remove log file ? ${If} ${FileExists} "$INSTDIR\bin\smartd.log" MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete log file$\n$INSTDIR\bin\smartd.log ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\smartd.log" ${EndIf} ; Remove drivedb-add file ? ${If} ${FileExists} "$INSTDIR\bin\drivedb-add.h" MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete local drive database file$\n$INSTDIR\bin\drivedb-add.h ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\drivedb-add.h" ${EndIf} ; Remove smartd_mailer.conf.ps1 file ? ${If} ${FileExists} "$INSTDIR\bin\smartd_mailer.conf.ps1" MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Delete mailer configuration file$\n$INSTDIR\bin\smartd_mailer.conf.ps1 ?" /SD IDNO IDYES 0 IDNO +2 Delete "$INSTDIR\bin\smartd_mailer.conf.ps1" ${EndIf} ; Remove files Delete "$INSTDIR\bin\smartctl.exe" Delete "$INSTDIR\bin\smartctl-nc.exe" Delete "$INSTDIR\bin\smartd.exe" Delete "$INSTDIR\bin\smartd_mailer.ps1" Delete "$INSTDIR\bin\smartd_mailer.conf.sample.ps1" Delete "$INSTDIR\bin\smartd_warning.cmd" ; TODO: Check for modifications? Delete "$INSTDIR\bin\drivedb.h" Delete "$INSTDIR\bin\drivedb.h.error" Delete "$INSTDIR\bin\drivedb.h.lastcheck" Delete "$INSTDIR\bin\drivedb.h.old" Delete "$INSTDIR\bin\update-smart-drivedb.exe" Delete "$INSTDIR\bin\runcmda.exe" Delete "$INSTDIR\bin\runcmdu.exe" Delete "$INSTDIR\bin\wtssendmsg.exe" Delete "$INSTDIR\doc\AUTHORS.txt" Delete "$INSTDIR\doc\ChangeLog.txt" Delete "$INSTDIR\doc\ChangeLog-5.0-6.0.txt" Delete "$INSTDIR\doc\COPYING.txt" Delete "$INSTDIR\doc\INSTALL.txt" Delete "$INSTDIR\doc\NEWS.txt" Delete "$INSTDIR\doc\README.txt" Delete "$INSTDIR\doc\TODO.txt" Delete "$INSTDIR\doc\checksums*.txt" Delete "$INSTDIR\doc\smartctl.8.html" Delete "$INSTDIR\doc\smartctl.8.pdf" Delete "$INSTDIR\doc\smartctl.8.txt" ; TODO: Remove after smartmontools 6.6 Delete "$INSTDIR\doc\smartd.8.html" Delete "$INSTDIR\doc\smartd.8.pdf" Delete "$INSTDIR\doc\smartd.8.txt" ; TODO: Remove after smartmontools 6.6 Delete "$INSTDIR\doc\smartd.conf" Delete "$INSTDIR\doc\smartd.conf.5.html" Delete "$INSTDIR\doc\smartd.conf.5.pdf" Delete "$INSTDIR\doc\smartd.conf.5.txt" ; TODO: Remove after smartmontools 6.6 Delete "$INSTDIR\uninst-smartmontools.exe" ; Remove shortcuts SetShellVarContext all Delete "$SMPROGRAMS\smartmontools\*.*" Delete "$SMPROGRAMS\smartmontools\Documentation\*.*" Delete "$SMPROGRAMS\smartmontools\smartctl Examples\*.*" Delete "$SMPROGRAMS\smartmontools\smartd Examples\*.*" ; Remove folders RMDir "$SMPROGRAMS\smartmontools\Documentation" RMDir "$SMPROGRAMS\smartmontools\smartctl Examples" RMDir "$SMPROGRAMS\smartmontools\smartd Examples" RMDir "$SMPROGRAMS\smartmontools" RMDir "$INSTDIR\bin" RMDir "$INSTDIR\doc" RMDir "$INSTDIR" ; Remove install dir from PATH Push "$INSTDIR\bin" Call un.RemoveFromPath ; Remove drive menu registry entries !insertmacro DriveMenuRemove ; Check for still existing entries ${If} ${FileExists} "$INSTDIR\bin\smartd.exe" MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR\bin\smartd.exe could not be removed.$\nsmartd is possibly still running." /SD IDOK ${ElseIf} ${FileExists} "$INSTDIR" MessageBox MB_OK "Note: $INSTDIR could not be removed." /SD IDOK ${EndIf} ${If} ${FileExists} "$SMPROGRAMS\smartmontools" MessageBox MB_OK "Note: $SMPROGRAMS\smartmontools could not be removed." /SD IDOK ${EndIf} SectionEnd ;-------------------------------------------------------------------- ; Functions !macro AdjustSectionSize section SectionGetSize ${section} $0 IntOp $0 $0 / 2 SectionSetSize ${section} $0 !macroend Function .onInit ; Set default install directories ${If} $INSTDIR == "" ; /D=PATH option not specified ? ReadRegStr $INSTDIR HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation" ${If} $INSTDIR == "" ; Not already installed ? StrCpy $INSTDIR "$PROGRAMFILES\smartmontools" !ifdef INPDIR64 StrCpy $INSTDIR32 $INSTDIR StrCpy $INSTDIR64 "$PROGRAMFILES64\smartmontools" !endif ${EndIf} ${EndIf} !ifdef INPDIR64 ; Check for 64-bit unless already installed in 32-bit location ${If} $INSTDIR64 != "" ${OrIf} $INSTDIR != "$PROGRAMFILES\smartmontools" ; $1 = IsWow64Process(GetCurrentProcess(), ($0=FALSE, &$0)) System::Call "kernel32::GetCurrentProcess() i.s" System::Call "kernel32::IsWow64Process(i s, *i 0 r0) i.r1" ${If} "$0 $1" == "1 1" ; 64-bit Windows ? !insertmacro SelectSection ${X64_SECTION} ${EndIf} ${EndIf} ; Sizes of binary sections include 32-bit and 64-bit executables !insertmacro AdjustSectionSize ${SMARTCTL_SECTION} !insertmacro AdjustSectionSize ${SMARTD_SECTION} !insertmacro AdjustSectionSize ${SMARTCTL_NC_SECTION} !endif ; Use 32-bit or 64-bit Notepad++ if installed StrCpy $EDITOR "$PROGRAMFILES\Notepad++\notepad++.exe" ${IfNot} ${FileExists} "$EDITOR" StrCpy $EDITOR "$PROGRAMFILES64\Notepad++\notepad++.exe" ${IfNot} ${FileExists} "$EDITOR" StrCpy $EDITOR "notepad.exe" ${EndIf} ${EndIf} Call ParseCmdLine !ifdef INPDIR64 Call CheckX64 !endif FunctionEnd ; Check x64 section and update INSTDIR accordingly !ifdef INPDIR64 Function CheckX64 ${IfNot} ${SectionIsSelected} ${X64_SECTION} StrCpy $X64 "" ${If} $INSTDIR32 != "" ${AndIf} $INSTDIR == $INSTDIR64 StrCpy $INSTDIR $INSTDIR32 ${EndIf} ${Else} StrCpy $X64 "t" ${If} $INSTDIR64 != "" ${AndIf} $INSTDIR == $INSTDIR32 StrCpy $INSTDIR $INSTDIR64 ${EndIf} ${EndIf} FunctionEnd !endif ; Command line parsing !macro GetCmdLineOption var name Push ",$opts," Push ",${name}," Call StrStr Pop ${var} ${If} ${var} != "" StrCpy $nomatch "" ${EndIf} !macroend !macro CheckCmdLineOption name section StrCpy $allopts "$allopts,${name}" !insertmacro GetCmdLineOption $0 ${name} ${If} $0 == "" !insertmacro UnselectSection ${section} ${Else} !insertmacro SelectSection ${section} ${EndIf} !macroend Function ParseCmdLine ; get /SO option Var /global opts ${GetParameters} $R0 ${GetOptions} $R0 "/SO" $opts ${If} ${Errors} Return ${EndIf} Var /global allopts Var /global nomatch StrCpy $nomatch "t" !ifdef INPDIR64 ; Change previous 64-bit setting StrCpy $allopts ",x32|x64" !insertmacro GetCmdLineOption $0 "x32" ${If} $0 != "" !insertmacro UnselectSection ${X64_SECTION} ${EndIf} !insertmacro GetCmdLineOption $0 "x64" ${If} $0 != "" !insertmacro SelectSection ${X64_SECTION} ${EndIf} ; Leave other sections unchanged if only "x32" or "x64" is specified ${If} $opts == "x32" ${OrIf} $opts == "x64" Return ${EndIf} !endif ; Turn sections on or off !insertmacro CheckCmdLineOption "smartctl" ${SMARTCTL_SECTION} !insertmacro CheckCmdLineOption "smartd" ${SMARTD_SECTION} !insertmacro CheckCmdLineOption "smartctlnc" ${SMARTCTL_NC_SECTION} !insertmacro CheckCmdLineOption "drivedb" ${DRIVEDB_SECTION} !insertmacro CheckCmdLineOption "doc" ${DOC_SECTION} !insertmacro CheckCmdLineOption "uninst" ${UNINST_SECTION} !insertmacro CheckCmdLineOption "menu" ${MENU_SECTION} !insertmacro CheckCmdLineOption "path" ${PATH_SECTION} !insertmacro CheckCmdLineOption "driveremove" ${DRIVE_REMOVE_SECTION} !insertmacro CheckCmdLineOption "drive0" ${DRIVE_0_SECTION} !insertmacro CheckCmdLineOption "drive1" ${DRIVE_1_SECTION} !insertmacro CheckCmdLineOption "drive2" ${DRIVE_2_SECTION} !insertmacro CheckCmdLineOption "drive3" ${DRIVE_3_SECTION} !insertmacro CheckCmdLineOption "drive4" ${DRIVE_4_SECTION} !insertmacro CheckCmdLineOption "drive5" ${DRIVE_5_SECTION} ${If} $opts != "-" ${If} $nomatch != "" StrCpy $0 "$allopts,-" "" 1 MessageBox MB_OK "Usage: smartmontools-VERSION.win32-setup [/S] [/SO component,...] [/D=INSTDIR]$\n$\ncomponents:$\n $0" Abort ${EndIf} ${EndIf} FunctionEnd ; Install runcmda.exe only once Function CheckRunCmdA Var /global runcmda ${If} $runcmda == "" StrCpy $runcmda "t" SetOutPath "$INSTDIR\bin" !insertmacro FileExe "bin\runcmda.exe" "" ${EndIf} FunctionEnd ;-------------------------------------------------------------------- ; Path functions ; ; Based on example from: ; http://nsis.sourceforge.net/Path_Manipulation ; !include "WinMessages.nsh" ; Registry Entry for environment (NT4,2000,XP) ; All users: ;!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' ; Current user only: !define Environ 'HKCU "Environment"' ; AddToPath - Appends dir to PATH ; (does not work on Win9x/ME) ; ; Usage: ; Push "dir" ; Call AddToPath Function AddToPath Exch $0 Push $1 Push $2 Push $3 Push $4 ; NSIS ReadRegStr returns empty string on string overflow ; Native calls are used here to check actual length of PATH ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3) System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4" IntCmp $4 0 0 done done ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2)) ; RegCloseKey($3) System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4" System::Call "advapi32::RegCloseKey(i $3)" ${If} $4 = 234 ; ERROR_MORE_DATA DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}" MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}" /SD IDOK Goto done ${EndIf} ${If} $4 <> 0 ; NO_ERROR ${If} $4 <> 2 ; ERROR_FILE_NOT_FOUND DetailPrint "AddToPath: unexpected error code $4" Goto done ${EndIf} StrCpy $1 "" ${EndIf} ; Check if already in PATH Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" 0 done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" 0 done ; Prevent NSIS string overflow StrLen $2 $0 StrLen $3 $1 IntOp $2 $2 + $3 IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";") ${If} $2 > ${NSIS_MAX_STRLEN} DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}" MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}." /SD IDOK Goto done ${EndIf} ; Append dir to PATH DetailPrint "Add to PATH: $0" StrCpy $2 $1 1 -1 ${If} $2 == ";" StrCpy $1 $1 -1 ; remove trailing ';' ${EndIf} ${If} $1 != "" ; no leading ';' StrCpy $0 "$1;$0" ${EndIf} WriteRegExpandStr ${Environ} "PATH" $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 done: Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Removes dir from PATH ; ; Usage: ; Push "dir" ; Call RemoveFromPath Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 ReadRegStr $1 ${Environ} "PATH" StrCpy $5 $1 1 -1 ${If} $5 != ";" StrCpy $1 "$1;" ; ensure trailing ';' ${EndIf} Push $1 Push "$0;" Call un.StrStr Pop $2 ; pos of our dir StrCmp $2 "" done DetailPrint "Remove from PATH: $0" StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove StrCpy $3 "$5$6" StrCpy $5 $3 1 -1 ${If} $5 == ";" StrCpy $3 $3 -1 ; remove trailing ';' ${EndIf} WriteRegExpandStr ${Environ} "PATH" $3 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; StrStr - find substring in a string ; ; Usage: ; Push "this is some string" ; Push "some" ; Call StrStr ; Pop $0 ; "some string" !macro StrStr un Function ${un}StrStr Exch $R1 ; $R1=substring, stack=[old$R1,string,...] Exch ; stack=[string,old$R1,...] Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...] Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=substring, $R2=string, $R3=strlen(substring) ; $R4=count, $R5=tmp ${Do} StrCpy $R5 $R2 $R3 $R4 ${IfThen} $R5 == $R1 ${|} ${ExitDo} ${|} ${IfThen} $R5 == "" ${|} ${ExitDo} ${|} IntOp $R4 $R4 + 1 ${Loop} StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 ; $R1=old$R1, stack=[result,...] FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." ;-------------------------------------------------------------------- ; Set Run As Administrator flag in shortcut ; ; Slightly modified version from: ; http://nsis.sourceforge.net/IShellLink_Set_RunAs_flag ; !define IPersistFile {0000010b-0000-0000-c000-000000000046} !define CLSID_ShellLink {00021401-0000-0000-C000-000000000046} !define IID_IShellLinkA {000214EE-0000-0000-C000-000000000046} !define IID_IShellLinkW {000214F9-0000-0000-C000-000000000046} !define IShellLinkDataList {45e2b4ae-b1c3-11d0-b92f-00a0c90312e1} !ifdef NSIS_UNICODE !define IID_IShellLink ${IID_IShellLinkW} !else !define IID_IShellLink ${IID_IShellLinkA} !endif Function ShellLinkSetRunAs ; Set archive location of $PLUGINSDIR Goto +2 SetOutPath "$INSTDIR" System::Store S ; push $0-$9, $R0-$R9 pop $9 ; $0 = CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink, &$1) System::Call "ole32::CoCreateInstance(g'${CLSID_ShellLink}',i0,i1,g'${IID_IShellLink}',*i.r1)i.r0" ${If} $0 = 0 System::Call "$1->0(g'${IPersistFile}',*i.r2)i.r0" ; $0 = $1->QueryInterface(IPersistFile, &$2) ${If} $0 = 0 System::Call "$2->5(w '$9',i 0)i.r0" ; $0 = $2->Load($9, STGM_READ) ${If} $0 = 0 System::Call "$1->0(g'${IShellLinkDataList}',*i.r3)i.r0" ; $0 = $1->QueryInterface(IShellLinkDataList, &$3) ${If} $0 = 0 System::Call "$3->6(*i.r4)i.r0"; $0 = $3->GetFlags(&$4) ${If} $0 = 0 System::Call "$3->7(i $4|0x2000)i.r0" ; $0 = $3->SetFlags($4|SLDF_RUNAS_USER) ${If} $0 = 0 System::Call "$2->6(w '$9',i1)i.r0" ; $2->Save($9, TRUE) ${EndIf} ${EndIf} System::Call "$3->2()" ; $3->Release() ${EndIf} System::Call "$2->2()" ; $2->Release() ${EndIf} ${EndIf} System::Call "$1->2()" ; $1->Release() ${EndIf} ${If} $0 <> 0 DetailPrint "Set RunAsAdmin: $9 failed ($0)" ${Else} DetailPrint "Set RunAsAdmin: $9" ${EndIf} System::Store L ; pop $R9-$R0, $9-$0 FunctionEnd smartmontools-7.0/os_win32/popen.h0000644000175000010010000000236713361544541014162 00000000000000/* * os_win32/popen.h * * Home page of code is: https://www.smartmontools.org * * Copyright (C) 2018 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef POPEN_H #define POPEN_H #define POPEN_H_CVSID "$Id: popen.h 4818 2018-10-17 05:32:17Z chrfranke $" #include // MinGW defines these to _popen/_pclose #undef popen #undef pclose #ifdef __cplusplus extern "C" { #endif // popen(3) reimplementation for Windows // // The _popen() from MSVCRT is not useful as it always opens a new // console window if parent process has none. // // Differences to popen(3): // - Only modes "r[bt]" are supported // - stdin and stderr from parent are not inherited to child process // but redirected to null device // - Only one child process can be run at a time FILE * popen(const char * command, const char * mode); int pclose(FILE * f); #ifdef __cplusplus } #endif // wait(3) macros from #ifndef WIFEXITED #define WIFEXITED(status) (((status) & 0xff) == 0x00) #define WIFSIGNALED(status) (((status) & 0xff) != 0x00) #define WIFSTOPPED(status) (0) #define WEXITSTATUS(status) ((status) >> 8) #define WTERMSIG(status) ((status) & 0xff) #define WSTOPSIG(status) (0) #endif // WIFEXITED #endif // POPEN_H smartmontools-7.0/os_win32/popen_win32.cpp0000644000175000010010000001046613361544541015536 00000000000000/* * os_win32/popen_win32.cpp * * Home page of code is: https://www.smartmontools.org * * Copyright (C) 2018 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "popen.h" const char * popen_win32_cpp_cvsid = "$Id: popen_win32.cpp 4818 2018-10-17 05:32:17Z chrfranke $" POPEN_H_CVSID; #include #include #include // _open_osfhandle() #include // SIGSEGV #include #include #define WIN32_LEAN_AND_MEAN #include static FILE * s_popen_file; static HANDLE s_popen_process; extern "C" FILE * popen(const char * command, const char * mode) { // Fail if previous run is still in progress if (s_popen_file) { errno = EEXIST; return (FILE *)0; } // mode "w" is not implemented if (!(mode[0] == 'r' && (!mode[1] || !mode[2]))) { errno = EINVAL; return (FILE *)0; } // Set flags for text or binary mode // Note: _open_osfhandle() ignores _fmode and defaults to O_BINARY int oflags; const char * fomode; switch (mode[1]) { case 0: case 't': oflags = O_RDONLY|O_TEXT; fomode = "rt"; break; case 'b': oflags = O_RDONLY|O_BINARY; fomode = "rb"; break; default: errno = EINVAL; return (FILE *)0; } // Create stdout pipe with inheritable write end HANDLE pipe_out_r, pipe_out_w; if (!CreatePipe(&pipe_out_r, &pipe_out_w, (SECURITY_ATTRIBUTES *)0, 1024)) { errno = EMFILE; return (FILE *)0; } if (!SetHandleInformation(pipe_out_w, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); errno = EMFILE; return (FILE *)0; } // Connect pipe read end to new FD int fd = _open_osfhandle((intptr_t)pipe_out_r, oflags); if (fd < 0) { CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); return (FILE *)0; } // Connect FD to new FILE FILE * f = fdopen(fd, fomode); if (!f) { int err = errno; close(fd); // CloseHandle(pipe_out_r) CloseHandle(pipe_out_w); errno = err; return (FILE *)0; } // Build command line "cmd /c COMMAND" int cmdlen = strlen(command); char * shellcmd = (char *)malloc(7 + cmdlen + 1); if (!shellcmd) { fclose(f); // CloseHandle(pipe_out_r) CloseHandle(pipe_out_w); errno = ENOMEM; return (FILE *)0; } memcpy(shellcmd, "cmd /c ", 7); memcpy(shellcmd + 7, command, cmdlen + 1); // Redirect stdin stderr to null device // Don't inherit parent's stdin, script may hang if parent has no console. SECURITY_ATTRIBUTES sa_inherit = { sizeof(sa_inherit), (SECURITY_DESCRIPTOR *)0, TRUE }; HANDLE null_in = CreateFile("nul", GENERIC_READ , 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0); HANDLE null_err = CreateFile("nul", GENERIC_WRITE, 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0); // Set stdio handles STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.hStdInput = null_in; si.hStdOutput = pipe_out_w; si.hStdError = null_err; si.dwFlags = STARTF_USESTDHANDLES; // Create process PROCESS_INFORMATION pi; BOOL ok = CreateProcessA( getenv("COMSPEC"), // "C:\WINDOWS\system32\cmd.exe" or nullptr shellcmd, // "cmd /c COMMAND" ("cmd" searched in PATH if COMSPEC not set) (SECURITY_ATTRIBUTES *)0, (SECURITY_ATTRIBUTES *)0, TRUE, // inherit CREATE_NO_WINDOW, // DETACHED_PROCESS would open new console(s) (void *)0, (char *)0, &si, &pi ); free(shellcmd); // Close inherited handles CloseHandle(null_err); CloseHandle(null_in); CloseHandle(pipe_out_w); if (!ok) { fclose(f); // CloseHandle(pipe_out_r) errno = ENOENT; return (FILE *)0; } // Store process and FILE for pclose() CloseHandle(pi.hThread); s_popen_process = pi.hProcess; s_popen_file = f; return f; } extern "C" int pclose(FILE * f) { if (f != s_popen_file) { errno = EBADF; return -1; } fclose(f); s_popen_file = 0; // Wait for process exitcode DWORD exitcode = 42; bool ok = ( WaitForSingleObject(s_popen_process, INFINITE) == WAIT_OBJECT_0 && GetExitCodeProcess(s_popen_process, &exitcode)); CloseHandle(s_popen_process); s_popen_process = 0; if (!ok) { errno = ECHILD; return -1; } // Modify exitcode for wait(3) macros if (exitcode >> 23) return ((exitcode << 9) >> 1) | SIGSEGV; else return exitcode << 8; } smartmontools-7.0/os_win32/runcmd.c0000644000175000010010000000324313336335341014314 00000000000000/* * Run console command and wait for user input * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2011 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ char svnid[] = "$Id: runcmd.c 4760 2018-08-19 18:45:53Z chrfranke $"; #include #include int main(int argc, char **argv) { char * cmd = GetCommandLineA(); DWORD exitcode; STARTUPINFOA si = { sizeof(si), }; PROCESS_INFORMATION pi; int key; if (*cmd == '"') { cmd++; while (*cmd && !(*cmd == '"' && cmd[-1] != '\\')) cmd++; if (*cmd) cmd++; } else { while (*cmd && !(*cmd == ' ' || *cmd == '\t')) cmd++; } while (*cmd == ' ' || *cmd == '\t') cmd++; if (*cmd) { printf("%s\n\n", cmd); fflush(stdout); } if (!*cmd) { printf("Usage: %s COMMAND [ARG ...]\n", argv[0]); exitcode = 1; } else if (!CreateProcessA((char *)0, cmd, (SECURITY_ATTRIBUTES *)0, (SECURITY_ATTRIBUTES *)0, TRUE/*inherit*/, 0/*no flags*/, (void *)0, (char *)0, &si, &pi) ) { DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) printf("Command not found\n"); else printf("CreateProcess() failed with error=%u\n", err); exitcode = 1; } else { CloseHandle(pi.hThread); exitcode = 42; WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, &exitcode); CloseHandle(pi.hProcess); if (exitcode) printf("\nExitcode: %u (0x%02x)", exitcode, exitcode); } printf("\nType to exit: "); fflush(stdout); while (!((key = getc(stdin)) == EOF || key == '\n' || key == '\r')) ; printf("\n"); return exitcode; } smartmontools-7.0/os_win32/smartd_mailer.conf.sample.ps10000644000175000010010000000124412764065420020335 00000000000000# Sample file for smartd_mailer.conf.ps1 # # Home page of code is: http://www.smartmontools.org # $Id: smartd_mailer.conf.sample.ps1 4338 2016-09-07 19:31:28Z chrfranke $ # SMTP Server $smtpServer = "smtp.domain.local" # Optional settings [default values in square brackets] # Sender address ["smartd daemon "] #$from = "Administrator " # SMTP Port [25] #$port = 587 # Use STARTTLS [$false] #$useSsl = $true # SMTP user name [] #$username = "USER" # Plain text SMTP password [] #$password = "PASSWORD" # Encrypted SMTP password [] # (embedded newlines, tabs and spaces are ignored) #$passwordEnc = " # 0123456789abcdef... # ... #" smartmontools-7.0/os_win32/smartd_mailer.ps10000644000175000010010000000423413336335341016131 00000000000000# # smartd mailer script # # Home page of code is: http://www.smartmontools.org # # Copyright (C) 2016 Christian Franke # # SPDX-License-Identifier: GPL-2.0-or-later # # $Id: smartd_mailer.ps1 4760 2018-08-19 18:45:53Z chrfranke $ # $ErrorActionPreference = "Stop" # Parse command line and check environment $dryrun = $false if (($args.Count -eq 1) -and ($args[0] -eq "--dryrun")) { $dryrun = $true } $toCsv = $env:SMARTD_ADDRCSV $subject = $env:SMARTD_SUBJECT $file = $env:SMARTD_FULLMSGFILE if (!((($args.Count -eq 0) -or $dryrun) -and $toCsv -and $subject -and $file)) { echo ` "smartd mailer script Usage: set SMARTD_ADDRCSV='Comma separated mail addresses' set SMARTD_SUBJECT='Mail Subject' set SMARTD_FULLMSGFILE='X:\PATH\TO\Message.txt' .\$($MyInvocation.MyCommand.Name) [--dryrun] " exit 1 } # Set default sender address if ($env:COMPUTERNAME -match '^[-_A-Za-z0-9]+$') { $hostname = $env:COMPUTERNAME.ToLower() } else { $hostname = "unknown" } if ($env:USERDNSDOMAIN -match '^[-._A-Za-z0-9]+$') { $hostname += ".$($env:USERDNSDOMAIN.ToLower())" } elseif ( ($env:USERDOMAIN -match '^[-_A-Za-z0-9]+$') ` -and ($env:USERDOMAIN -ne $env:COMPUTERNAME) ) { $hostname += ".$($env:USERDOMAIN.ToLower()).local" } else { $hostname += ".local" } $from = "smartd daemon " # Read configuration . .\smartd_mailer.conf.ps1 # Create parameters $to = $toCsv.Split(",") $body = Get-Content -Path $file | Out-String $parm = @{ SmtpServer = $smtpServer; From = $from; To = $to Subject = $subject; Body = $body } if ($port) { $parm += @{ Port = $port } } if ($useSsl) { $parm += @{ useSsl = $true } } if ($username -and ($password -or $passwordEnc)) { if (!$passwordEnc) { $secureString = ConvertTo-SecureString -String $password -AsPlainText -Force } else { $passwordEnc = $passwordEnc -replace '[\r\n\t ]','' $secureString = ConvertTo-SecureString -String $passwordEnc } $credential = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist $username,$secureString $parm += @{ Credential = $credential } } # Send mail if ($dryrun) { echo "Send-MailMessage" @parm } else { Send-MailMessage @parm } smartmontools-7.0/os_win32/smartd_warning.cmd0000644000175000010010000001424213336335341016365 00000000000000@echo off :: :: smartd warning script :: :: Home page of code is: http://www.smartmontools.org :: :: Copyright (C) 2012-17 Christian Franke :: :: SPDX-License-Identifier: GPL-2.0-or-later :: :: $Id: smartd_warning.cmd 4760 2018-08-19 18:45:53Z chrfranke $ :: verify other 2>nul setlocal enableextensions enabledelayedexpansion if errorlevel 1 goto UNSUPPORTED set err= :: Change to script directory (not necessary if run from smartd service) cd /d %~dp0 if errorlevel 1 goto ERROR :: Parse options set dryrun= if "%1" == "--dryrun" ( set dryrun=--dryrun shift ) if not "!dryrun!" == "" echo cd /d !cd! if not "%1" == "" ( echo smartd warning message script echo. echo Usage: echo set SMARTD_MAILER='Path to external script, empty for "blat"' echo set SMARTD_ADDRESS='Space separated mail addresses, empty if none' echo set SMARTD_MESSAGE='Error Message' echo set SMARTD_FAILTYPE='Type of failure, "EMailTest" for tests' echo set SMARTD_TFIRST='Date of first message sent, empty if none' echo :: set SMARTD_TFIRSTEPOCH='time_t format of above' echo set SMARTD_PREVCNT='Number of previous messages, 0 if none' echo set SMARTD_NEXTDAYS='Number of days until next message, empty if none' echo set SMARTD_DEVICEINFO='Device identify information' echo :: set SMARTD_DEVICE='Device name' echo :: set SMARTD_DEVICESTRING='Annotated device name' echo :: set SMARTD_DEVICETYPE='Device type from -d directive, "auto" if none' echo smartd_warning.cmd [--dryrun] goto ERROR ) if "!SMARTD_ADDRESS!!SMARTD_MAILER!" == "" ( echo smartd_warning.cmd: SMARTD_ADDRESS or SMARTD_MAILER must be set goto ERROR ) :: USERDNSDOMAIN may be unset if running as service if "!USERDNSDOMAIN!" == "" ( for /f "delims== tokens=2 usebackq" %%d in (`wmic PATH Win32_Computersystem WHERE "PartOfDomain=TRUE" GET Domain /VALUE ^nul`) do set USERDNSDOMAIN=%%~d ) :: Remove possible trailing \r appended by above command (requires %...%) set USERDNSDOMAIN=%USERDNSDOMAIN% :: Format subject set SMARTD_SUBJECT=SMART error (!SMARTD_FAILTYPE!) detected on host: !COMPUTERNAME! :: Temp file for message if not "!TMP!" == "" set SMARTD_FULLMSGFILE=!TMP!\smartd_warning-!RANDOM!.txt if "!TMP!" == "" set SMARTD_FULLMSGFILE=smartd_warning-!RANDOM!.txt :: Format message ( echo This message was generated by the smartd service running on: echo. echo. host name: !COMPUTERNAME! if not "!USERDNSDOMAIN!" == "" echo. DNS domain: !USERDNSDOMAIN! if "!USERDNSDOMAIN!" == "" echo. DNS domain: [Empty] if not "!USERDOMAIN!" == "" echo. Win domain: !USERDOMAIN! echo. echo The following warning/error was logged by the smartd service: echo. if not "!SMARTD_MESSAGE!" == "" echo !SMARTD_MESSAGE! if "!SMARTD_MESSAGE!" == "" echo [SMARTD_MESSAGE] echo. echo Device info: if not "!SMARTD_DEVICEINFO!" == "" echo !SMARTD_DEVICEINFO! if "!SMARTD_DEVICEINFO!" == "" echo [SMARTD_DEVICEINFO] echo. echo For details see the event log or log file of smartd. if not "!SMARTD_FAILTYPE!" == "EmailTest" ( echo. echo You can also use the smartctl utility for further investigation. if not "!SMARTD_PREVCNT!" == "0" echo The original message about this issue was sent at !SMARTD_TFIRST! if "!SMARTD_NEXTDAYS!" == "" ( echo No additional messages about this problem will be sent. ) else ( if "!SMARTD_NEXTDAYS!" == "1" ( echo Another message will be sent in 24 hours if the problem persists. ) else ( echo Another message will be sent in !SMARTD_NEXTDAYS! days if the problem persists. )) ) ) > "!SMARTD_FULLMSGFILE!" if errorlevel 1 goto ERROR if not "!dryrun!" == "" ( echo !SMARTD_FULLMSGFILE!: type "!SMARTD_FULLMSGFILE!" echo --EOF-- ) :: Check first address set first= for /f "tokens=1*" %%a in ("!SMARTD_ADDRESS!") do (set first=%%a) set wtssend= if "!first!" == "console" set wtssend=-c if "!first!" == "active" set wtssend=-a if "!first!" == "connected" set wtssend=-s if not "!wtssend!" == "" ( :: Show Message box(es) via WTSSendMessage() if not "!dryrun!" == "" ( echo call .\wtssendmsg !wtssend! "!SMARTD_SUBJECT!" - ^< "!SMARTD_FULLMSGFILE!" ) else ( call .\wtssendmsg !wtssend! "!SMARTD_SUBJECT!" - < "!SMARTD_FULLMSGFILE!" if errorlevel 1 set err=t ) :: Remove first address for /f "tokens=1*" %%a in ("!SMARTD_ADDRESS!") do (set SMARTD_ADDRESS=%%b) ) :: Make comma separated address list set SMARTD_ADDRCSV= if not "!SMARTD_ADDRESS!" == "" set SMARTD_ADDRCSV=!SMARTD_ADDRESS: =,! :: Default mailer is smartd_mailer.ps1 (if configured) or blat.exe if not "!SMARTD_ADDRESS!" == "" if "!SMARTD_MAILER!" == "" ( if not exist smartd_mailer.conf.ps1 set SMARTD_MAILER=blat ) :: Get mailer extension set ext= for /f "delims=" %%f in ("!SMARTD_MAILER!") do (set ext=%%~xf) :: Send mail or run command if "!ext!" == ".ps1" ( :: Run PowerShell script if not "!dryrun!" == "" ( set esc=^^ echo PowerShell -NoProfile -ExecutionPolicy Bypass -Command !esc!^& '!SMARTD_MAILER!' ^nul 2>nul if not "!err!" == "" goto ERROR endlocal exit /b 0 :ERROR endlocal exit /b 1 smartmontools-7.0/os_win32/syslog.h0000644000175000010010000000243413336335341014352 00000000000000/* * os_win32/syslog.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-8 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef SYSLOG_H #define SYSLOG_H #define SYSLOG_H_CVSID "$Id: syslog.h 4760 2018-08-19 18:45:53Z chrfranke $\n" #include #ifdef __cplusplus extern "C" { #endif /* 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); #ifdef __cplusplus } #endif #endif /* SYSLOG_H */ smartmontools-7.0/os_win32/syslogevt.mc0000644000175000010010000000417413336335341015244 00000000000000;/* ; * os_win32/syslogevt.mc ; * ; * Home page of code is: http://www.smartmontools.org ; * ; * Copyright (C) 2004-10 Christian Franke ; * ; * SPDX-License-Identifier: GPL-2.0-or-later ; */ ; ;// $Id: syslogevt.mc 4760 2018-08-19 18:45:53Z chrfranke $ ; ;// Use message compiler "mc" or "windmc" to generate ;// syslogevt.rc, syslogevt.h, msg00001.bin ;// from this file. ;// MSG_SYSLOG in syslogmsg.h must be zero ;// MSG_SYSLOG_nn must be == nn ; ;// MS and binutils message compiler defaults for FacilityNames differ: ;// mc: Application = 0x000 ;// windmc: Application = 0xfff FacilityNames = (Application = 0x000) MessageId=0x0 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG Language=English %1 . ;// 1-10 Line SYSLOG Messages ;// %1=Ident, %2=PID, %3=Severity, %[4-13]=Line 1-10 MessageId=0x1 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_01 Language=English %1[%2]:%3: %4 . MessageId=0x2 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_02 Language=English %1[%2]:%3%n %4%n %5 . MessageId=0x3 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_03 Language=English %1[%2]:%3%n %4%n %5%n %6 . MessageId=0x4 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_04 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7 . MessageId=0x5 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_05 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8 . MessageId=0x6 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_06 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8%n %9 . MessageId=0x7 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_07 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8%n %9%n %10 . MessageId=0x8 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_08 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8%n %9%n %10%n %11 . MessageId=0x9 Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_09 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8%n %9%n %10%n %11%n %12 . MessageId=0xa Severity=Success Facility=Application SymbolicName=MSG_SYSLOG_10 Language=English %1[%2]:%3%n %4%n %5%n %6%n %7%n %8%n %9%n %10%n %11%n %12%n %13 . smartmontools-7.0/os_win32/syslog_win32.cpp0000644000175000010010000002321213336335341015724 00000000000000/* * os_win32/syslog_win32.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-15 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ // Win32 Emulation of syslog() for smartd // Writes to windows event log on NT4/2000/XP // (Register syslogevt.exe as event message file) // 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_cpp_cvsid = "$Id: syslog_win32.cpp 4760 2018-08-19 18:45:53Z chrfranke $" 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"); 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 smartmontools-7.0/os_win32/update-smart-drivedb.nsi0000644000175000010010000000616713336335341017426 00000000000000; ; smartmontools drive database update NSIS script ; ; Home page of code is: http://www.smartmontools.org ; ; Copyright (C) 2011-13 Christian Franke ; ; SPDX-License-Identifier: GPL-2.0-or-later ; ; $Id: update-smart-drivedb.nsi 4760 2018-08-19 18:45:53Z chrfranke $ ; ;-------------------------------------------------------------------- ; Command line arguments: ; makensis -DBRANCH= update-smart-drivedb.nsi !include "FileFunc.nsh" Name "update-smart-drivedb" Caption "Update smartmontools drivedb.h" OutFile "update-smart-drivedb.exe" SetCompressor /solid lzma XPStyle on InstallColors /windows Page instfiles Section "" SetOutPath $INSTDIR !ifdef BRANCH StrCpy $0 "branches/${BRANCH}" Push $0 Call Download IfErrors 0 endload !endif StrCpy $0 "trunk" Push $0 Call Download IfErrors 0 endload MessageBox MB_OK "Download failed" /SD IDOK Abort "Download failed" endload: ; Check syntax Delete "drivedb.h.error" IfFileExists "smartctl-nc.exe" 0 endsyntax ExecWait '.\smartctl-nc.exe -B drivedb.h.new -P showall' $1 StrCmp $1 "0" endsyntax Rename "drivedb.h.new" "drivedb.h.error" MessageBox MB_OK "drivedb.h.error: rejected by smartctl, probably no longer compatible" /SD IDOK Abort "drivedb.h.error: rejected by smartctl, probably no longer compatible" endsyntax: ; Keep old file if identical Delete "drivedb.h.lastcheck" IfFileExists "drivedb.h" 0 endcomp Call Cmp IfErrors changed 0 DetailPrint "drivedb.h is already up to date" MessageBox MB_OK "$INSTDIR\drivedb.h is already up to date" /SD IDOK Delete "drivedb.h.new" DetailPrint "Create file: drivedb.h.lastcheck" FileOpen $1 "drivedb.h.lastcheck" w FileClose $1 Return changed: Delete "drivedb.h.old" Rename "drivedb.h" "drivedb.h.old" endcomp: Rename "drivedb.h.new" "drivedb.h" MessageBox MB_OK "$INSTDIR\drivedb.h updated from $0" /SD IDOK SectionEnd Function .onInit ; Install in same directory ${GetExePath} $INSTDIR FunctionEnd ; Download from branch or trunk on stack, SetErrors on error Function Download Pop $R0 DetailPrint "Download from $R0" ; SVN repository read-only URL ; (SF code browser does not return ContentLength required for NSISdl::download) StrCpy $R1 "http://svn.code.sf.net/p/smartmontools/code/$R0/smartmontools/drivedb.h" DetailPrint "($R1)" NSISdl::download $R1 "drivedb.h.new" Pop $R0 DetailPrint "Download: $R0" ClearErrors StrCmp $R0 "success" 0 err ; File must start with comment FileOpen $R0 "drivedb.h.new" r FileReadByte $R0 $R1 FileClose $R0 ClearErrors StrCmp $R1 "47" 0 +2 Return DetailPrint "drivedb.h.new: syntax error ($R1)" err: Delete "drivedb.h.new" SetErrors FunctionEnd ; Compare drivedb.h drivedb.h.new, SetErrors if different ; TODO: ignore differences in Id string Function Cmp ClearErrors FileOpen $R0 "drivedb.h" r FileOpen $R1 "drivedb.h.new" r readloop: FileRead $R0 $R2 FileRead $R1 $R3 StrCmp $R2 $R3 0 +2 IfErrors 0 readloop FileClose $R0 FileClose $R1 ClearErrors StrCmp $R2 $R3 0 +2 Return SetErrors FunctionEnd smartmontools-7.0/os_win32/versioninfo.rc.in0000644000175000010010000000167313166443502016161 00000000000000// // os_win32/versioninfo.rc.in // // $Id: versioninfo.rc.in 4519 2017-10-08 15:41:54Z chrfranke $ // 1 VERSIONINFO FILEVERSION @BINARY_VERSION@ PRODUCTVERSION @BINARY_VERSION@ FILEFLAGSMASK 0x0 FILEFLAGS 0x0 FILEOS 0x4 // VOS__WINDOWS32 FILETYPE 0x1 // VFT_APP FILESUBTYPE 0x0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "04090000" BEGIN VALUE "CompanyName", "www.smartmontools.org" VALUE "FileDescription", "@DESC@" VALUE "FileVersion", "@TEXT_VERSION@" VALUE "InternalName", "@NAME@" VALUE "LegalCopyright", "(C) 2002-@YY@, Bruce Allen, Christian Franke, www.smartmontools.org" VALUE "OriginalFilename", "@NAME@.exe" VALUE "ProductName", "smartmontools" VALUE "ProductVersion", "@TEXT_VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 0x0000 END END smartmontools-7.0/os_win32/wmiquery.cpp0000644000175000010010000001145413336335341015251 00000000000000/* * os_win32/wmiquery.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2011-13 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define WINVER 0x0400 #define _WIN32_WINNT WINVER #include "wmiquery.h" #include const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 4760 2018-08-19 18:45:53Z chrfranke $" WMIQUERY_H_CVSID; ///////////////////////////////////////////////////////////////////////////// // com_bstr com_bstr::com_bstr(const char * str) : m_bstr(0) { int sz = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, (LPWSTR)0, 0); if (sz <= 0) return; m_bstr = SysAllocStringLen((OLECHAR*)0, sz-1); if (!m_bstr) return; // throw std::bad_alloc MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, m_bstr, sz); } bool com_bstr::to_str(const BSTR & bstr, std::string & str) { if (!bstr) return false; int sz = WideCharToMultiByte(CP_ACP, 0, bstr, -1, (LPSTR)0, 0, (LPCSTR)0, (LPBOOL)0); if (sz <= 0) return false; char * buf = new char[sz]; WideCharToMultiByte(CP_ACP, 0, bstr, -1, buf, sz, (LPCSTR)0, (LPBOOL)0); str = buf; delete [] buf; return true; } ///////////////////////////////////////////////////////////////////////////// // wbem_object std::string wbem_object::get_str(const char * name) /*const*/ { std::string s; if (!m_intf) return s; VARIANT var; VariantInit(&var); if (m_intf->Get(com_bstr(name), 0L, &var, (CIMTYPE*)0, (LPLONG)0) /* != WBEM_S_NO_ERROR */) return s; if (var.vt == VT_BSTR) com_bstr::to_str(var.bstrVal, s); VariantClear(&var); return s; } ///////////////////////////////////////////////////////////////////////////// // wbem_enumerator bool wbem_enumerator::next(wbem_object & obj) { if (!m_intf) return false; ULONG n = 0; HRESULT rc = m_intf->Next(5000 /*5s*/, 1 /*count*/, obj.m_intf.replace(), &n); if (FAILED(rc) || n != 1) return false; return true; } ///////////////////////////////////////////////////////////////////////////// // wbem_services const CLSID xCLSID_WbemLocator = {0x4590f811, 0x1d3a, 0x11d0, {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24}}; const IID xIID_IWbemLocator = {0xdc12a687, 0x737f, 0x11cf, {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24}}; bool wbem_services::connect() { // Init COM during first call. static HRESULT init_rc = -1; static bool init_tried = false; if (!init_tried) { init_tried = true; init_rc = CoInitialize((LPVOID)0); } if (!(init_rc == S_OK || init_rc == S_FALSE)) return false; /// Create locator. com_intf_ptr locator; HRESULT rc = CoCreateInstance(xCLSID_WbemLocator, (LPUNKNOWN)0, CLSCTX_INPROC_SERVER, xIID_IWbemLocator, (LPVOID*)locator.replace()); if (FAILED(rc)) return false; // Set timeout flag if supported. long flags = 0; OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver); if (GetVersionExA(&ver) && ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ( ver.dwMajorVersion >= 6 // Vista || (ver.dwMajorVersion == 5 && ver.dwMinorVersion >= 1))) // XP flags = WBEM_FLAG_CONNECT_USE_MAX_WAIT; // return in 2min or less // Connect to local server. rc = locator->ConnectServer(com_bstr("\\\\.\\root\\cimv2"), (BSTR)0, (BSTR)0, (BSTR)0, // User, Password, Locale flags, (BSTR)0, (IWbemContext*)0, m_intf.replace()); if (FAILED(rc)) return false; // Set authentication information, rc = CoSetProxyBlanket(m_intf.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, (OLECHAR*)0, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, (RPC_AUTH_IDENTITY_HANDLE*)0, EOAC_NONE); if (FAILED(rc)) { m_intf.reset(); return false; } return true; } bool wbem_services::vquery(wbem_enumerator & result, const char * qstr, va_list args) /*const*/ { if (!m_intf) return false; char qline[1024]; vsnprintf(qline, sizeof(qline), qstr, args); qline[sizeof(qline)-1] = 0; HRESULT rc = m_intf->ExecQuery( com_bstr("WQL"), com_bstr(qline), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, (IWbemContext*)0, result.m_intf.replace()); if (FAILED(rc)) return false; return true; } bool wbem_services::vquery1(wbem_object & obj, const char * qstr, va_list args) /*const*/ { wbem_enumerator result; if (!vquery(result, qstr, args)) return false; if (!result.next(obj)) return false; wbem_object peek; if (result.next(peek)) return false; return true; } bool wbem_services::query(wbem_enumerator & result, const char * qstr, ...) /*const*/ { va_list args; va_start(args, qstr); bool ok = vquery(result, qstr, args); va_end(args); return ok; } bool wbem_services::query1(wbem_object & obj, const char * qstr, ...) /*const*/ { va_list args; va_start(args, qstr); bool ok = vquery1(obj, qstr, args); va_end(args); return ok; } smartmontools-7.0/os_win32/wmiquery.h0000644000175000010010000000775513336335341014727 00000000000000/* * os_win32/wmiquery.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2011-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef WMIQUERY_H #define WMIQUERY_H #define WMIQUERY_H_CVSID "$Id: wmiquery.h 4760 2018-08-19 18:45:53Z chrfranke $" #include #include #ifndef __GNUC__ #define __attribute_format_printf(x, y) /**/ #elif defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO // Check format of __mingw_*printf() instead of MSVCRT.DLL:*printf() #define __attribute_format_printf(x, y) __attribute__((format (gnu_printf, x, y))) #else #define __attribute_format_printf(x, y) __attribute__((format (printf, x, y))) #endif ///////////////////////////////////////////////////////////////////////////// // com_bstr /// Wrapper class for COM BSTR class com_bstr { public: /// Construct from string. explicit com_bstr(const char * str); /// Destructor frees BSTR. ~com_bstr() { SysFreeString(m_bstr); } /// Implicit conversion to BSTR. operator BSTR() { return m_bstr; } /// Convert BSTR back to std::string. static bool to_str(const BSTR & bstr, std::string & str); private: BSTR m_bstr; com_bstr(const com_bstr &); void operator=(const com_bstr &); }; ///////////////////////////////////////////////////////////////////////////// // com_intf_ptr /// Wrapper class for COM Interface pointer template class com_intf_ptr { public: /// Construct empty object com_intf_ptr() : m_ptr(0) { } /// Destructor releases the interface. ~com_intf_ptr() { reset(); } /// Release interface and clear the pointer. void reset() { if (m_ptr) { m_ptr->Release(); m_ptr = 0; } } /// Return the pointer. T * get() { return m_ptr; } /// Pointer dereferencing. T * operator->() { return m_ptr; } /// Return address of pointer for replacement. T * * replace() { reset(); return &m_ptr; } /// For (ptr != 0) check. operator bool() const { return !!m_ptr; } /// For (ptr == 0) check. bool operator!() const { return !m_ptr; } private: T * m_ptr; com_intf_ptr(const com_intf_ptr &); void operator=(const com_intf_ptr &); }; ///////////////////////////////////////////////////////////////////////////// // wbem_object class wbem_enumerator; /// Wrapper class for IWbemClassObject class wbem_object { public: /// Get string representation. std::string get_str(const char * name) /*const*/; private: /// Contents is set by wbem_enumerator. friend class wbem_enumerator; com_intf_ptr m_intf; }; ///////////////////////////////////////////////////////////////////////////// // wbem_enumerator class wbem_services; /// Wrapper class for IEnumWbemClassObject class wbem_enumerator { public: /// Get next object, return false if none or error. bool next(wbem_object & obj); private: /// Contents is set by wbem_services. friend class wbem_services; com_intf_ptr m_intf; }; ///////////////////////////////////////////////////////////////////////////// // wbem_services /// Wrapper class for IWbemServices class wbem_services { public: /// Connect to service, return false on error. bool connect(); /// Execute query, get result list. /// Return false on error. bool vquery(wbem_enumerator & result, const char * qstr, va_list args) /*const*/ __attribute_format_printf(3, 0); /// Execute query, get single result object. /// Return false on error or result size != 1. bool vquery1(wbem_object & obj, const char * qstr, va_list args) /*const*/ __attribute_format_printf(3, 0); /// Version of vquery() with printf() formatting. bool query(wbem_enumerator & result, const char * qstr, ...) /*const*/ __attribute_format_printf(3, 4); /// Version of vquery1() with printf() formatting. bool query1(wbem_object & obj, const char * qstr, ...) /*const*/ __attribute_format_printf(3, 4); private: com_intf_ptr m_intf; }; #endif // WMIQUERY_H smartmontools-7.0/os_win32/wtssendmsg.c0000644000175000010010000000643413336335341015227 00000000000000/* * WTSSendMessage() command line tool * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2012 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later */ #define WINVER 0x0500 #define _WIN32_WINNT WINVER char svnid[] = "$Id: wtssendmsg.c 4760 2018-08-19 18:45:53Z chrfranke $"; #include #include #define WIN32_LEAN_AND_MEAN #include #include static int usage() { printf("wtssendmsg $Revision: 4760 $ - Display a message box on client desktops\n" "Copyright (C) 2012 Christian Franke, smartmontools.org\n\n" "Usage: wtssendmsg [-cas] [-v] [\"Caption\"] \"Message\"|-\n" " wtssendmsg -v\n\n" " -c Console session [default]\n" " -a Active sessions\n" " -s Connected sessions\n" " -v List sessions\n" ); return 1; } int main(int argc, const char **argv) { int mode = 0, verbose = 0, status = 0, i; const char * message = 0, * caption = ""; char msgbuf[1024]; WTS_SESSION_INFOA * sessions; DWORD count; for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1]; i++) { int j; for (j = 1; argv[i][j]; j++) switch (argv[i][j]) { case 'c': mode = 0; break; case 'a': mode = 1; break; case 's': mode = 2; break; case 'v': verbose = 1; break; default: return usage(); } } if (i < argc) { if (i+1 < argc) caption = argv[i++]; message = argv[i++]; if (i < argc) return usage(); if (!strcmp(message, "-")) { // Read message from stdin i = fread(msgbuf, 1, sizeof(msgbuf)-1, stdin); if (i < 0) { perror("stdin"); return 1; } msgbuf[i] = 0; message = msgbuf; } } else { if (!verbose) return usage(); } // Get session list if (!WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &count)) { fprintf(stderr, "WTSEnumerateSessions() failed\n"); return 1; } for (i = 0; i < (int)count; i++) { if (verbose) { printf("Session %d (\"%s\", State=%d)%s", i, sessions[i].pWinStationName, sessions[i].State, (!message ? "\n" : ": ")); if (!message) continue; // List sessions only fflush(stdout); } if ( !strcmpi(sessions[i].pWinStationName, "Console") || (mode >= 1 && sessions[i].State == WTSActive) || (mode >= 2 && sessions[i].State == WTSConnected)) { // Send Message, don't wait for OK button DWORD result; if (WTSSendMessageA(WTS_CURRENT_SERVER_HANDLE, sessions[i].SessionId, (char *)caption, strlen(caption), (char *)message, strlen(message), MB_OK|MB_ICONEXCLAMATION, 0 /*Timeout*/, &result, FALSE /*!Wait*/)) { if (verbose) printf("message sent\n"); } else { status = 1; if (verbose) printf("WTSSendMessage() failed with error=%d\n", (int)GetLastError()); else fprintf(stderr, "Session %d (\"%s\", State=%d): WTSSendMessage() failed with error=%d\n", i, sessions[i].pWinStationName, sessions[i].State, (int)GetLastError()); } } else { if (verbose) printf("ignored\n"); } } WTSFreeMemory(sessions); return status; } smartmontools-7.0/os_win32.cpp0000644000175000010010000042743713402014526013374 00000000000000/* * os_win32.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2004-18 Christian Franke * * Original AACRaid code: * Copyright (C) 2015 Nidhi Malhotra * * Original Areca code: * Copyright (C) 2012 Hank Wu * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define WINVER 0x0502 #define _WIN32_WINNT WINVER #include "atacmds.h" #include "scsicmds.h" #include "nvmecmds.h" #include "utility.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" #include "dev_areca.h" #include "os_win32/wmiquery.h" #include "os_win32/popen.h" // TODO: Move from smartctl.h to other include file extern unsigned char failuretest_permissive; #include #ifdef _DEBUG #include #else #undef assert #define assert(x) /* */ #endif #include // offsetof() #include // access() // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of #define WIN32_LEAN_AND_MEAN #include #if HAVE_NTDDDISK_H // i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32 // (Missing: FILE_DEVICE_SCSI) #include #include #include #include #elif HAVE_DDK_NTDDDISK_H // older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI) #include #include #include #else // MSVC10, older MinGW // (Missing: IOCTL_SCSI_MINIPORT_*) #include #include #endif #ifndef _WIN32 // csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin // (aacraid.h also checks for _WIN64 which is also set on Cygwin x64) #define _WIN32 #endif // CSMI support #include "csmisas.h" // aacraid support #include "aacraid.h" // Silence -Wunused-local-typedefs warning from g++ >= 4.8 #if __GNUC__ >= 4 #define ATTR_UNUSED __attribute__((unused)) #else #define ATTR_UNUSED /**/ #endif // Macro to check constants at compile time using a dummy typedef #define ASSERT_CONST(c, n) \ typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED #define ASSERT_SIZEOF(t, n) \ typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED #ifndef _WIN64 #define SELECT_WIN_32_64(x32, x64) (x32) #else #define SELECT_WIN_32_64(x32, x64) (x64) #endif // Cygwin does no longer provide strn?icmp() compatibility macros // MSVCRT does not provide strn?casecmp() #if defined(__CYGWIN__) && !defined(stricmp) #define stricmp strcasecmp #define strnicmp strncasecmp #endif const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4848 2018-12-05 18:30:46Z chrfranke $"; ///////////////////////////////////////////////////////////////////////////// // Windows I/O-controls, some declarations are missing in the include files extern "C" { // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) ASSERT_CONST(SMART_GET_VERSION, 0x074080); ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084); ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088); ASSERT_SIZEOF(GETVERSIONINPARAMS, 24); ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1); ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1); // IDE PASS THROUGH (2000, XP, undocumented) #ifndef IOCTL_IDE_PASS_THROUGH #define IOCTL_IDE_PASS_THROUGH \ CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #endif // IOCTL_IDE_PASS_THROUGH #pragma pack(1) typedef struct { IDEREGS IdeReg; ULONG DataBufferSize; UCHAR DataBuffer[1]; } ATA_PASS_THROUGH; #pragma pack() ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028); ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1); // ATA PASS THROUGH (Win2003, XP SP2) #ifndef IOCTL_ATA_PASS_THROUGH #define IOCTL_ATA_PASS_THROUGH \ CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) typedef struct _ATA_PASS_THROUGH_EX { USHORT Length; USHORT AtaFlags; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR ReservedAsUchar; ULONG DataTransferLength; ULONG TimeOutValue; ULONG ReservedAsUlong; ULONG_PTR DataBufferOffset; UCHAR PreviousTaskFile[8]; UCHAR CurrentTaskFile[8]; } ATA_PASS_THROUGH_EX; #define ATA_FLAGS_DRDY_REQUIRED 0x01 #define ATA_FLAGS_DATA_IN 0x02 #define ATA_FLAGS_DATA_OUT 0x04 #define ATA_FLAGS_48BIT_COMMAND 0x08 #define ATA_FLAGS_USE_DMA 0x10 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista #endif // IOCTL_ATA_PASS_THROUGH ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c); ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48)); // IOCTL_SCSI_PASS_THROUGH[_DIRECT] ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004); ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014); ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56)); ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT, SELECT_WIN_32_64(44, 56)); // SMART IOCTL via SCSI MINIPORT ioctl #ifndef FILE_DEVICE_SCSI #define FILE_DEVICE_SCSI 0x001b #endif #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500) #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502) #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503) #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504) #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505) #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506) #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507) #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508) #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509) #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a) #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b) #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c) #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008); ASSERT_SIZEOF(SRB_IO_CONTROL, 28); // IOCTL_STORAGE_QUERY_PROPERTY #ifndef IOCTL_STORAGE_QUERY_PROPERTY #define IOCTL_STORAGE_QUERY_PROPERTY \ CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) typedef struct _STORAGE_DEVICE_DESCRIPTOR { ULONG Version; ULONG Size; UCHAR DeviceType; UCHAR DeviceTypeModifier; BOOLEAN RemovableMedia; BOOLEAN CommandQueueing; ULONG VendorIdOffset; ULONG ProductIdOffset; ULONG ProductRevisionOffset; ULONG SerialNumberOffset; STORAGE_BUS_TYPE BusType; ULONG RawPropertiesLength; UCHAR RawDeviceProperties[1]; } STORAGE_DEVICE_DESCRIPTOR; typedef enum _STORAGE_QUERY_TYPE { PropertyStandardQuery = 0, PropertyExistsQuery, PropertyMaskQuery, PropertyQueryMaxDefined } STORAGE_QUERY_TYPE; typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0, StorageAdapterProperty, StorageDeviceIdProperty, StorageDeviceUniqueIdProperty, StorageDeviceWriteCacheProperty, StorageMiniportProperty, StorageAccessAlignmentProperty } STORAGE_PROPERTY_ID; typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; UCHAR AdditionalParameters[1]; } STORAGE_PROPERTY_QUERY; #endif // IOCTL_STORAGE_QUERY_PROPERTY ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400); ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3); ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3); // IOCTL_STORAGE_QUERY_PROPERTY: Windows 10 enhancements namespace win10 { // enum STORAGE_PROPERTY_ID: new values const STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty = (STORAGE_PROPERTY_ID)49; const STORAGE_PROPERTY_ID StorageDeviceProtocolSpecificProperty = (STORAGE_PROPERTY_ID)50; typedef enum _STORAGE_PROTOCOL_TYPE { ProtocolTypeUnknown = 0, ProtocolTypeScsi, ProtocolTypeAta, ProtocolTypeNvme, ProtocolTypeSd } STORAGE_PROTOCOL_TYPE; typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE { NVMeDataTypeUnknown = 0, NVMeDataTypeIdentify, NVMeDataTypeLogPage, NVMeDataTypeFeature } STORAGE_PROTOCOL_NVME_DATA_TYPE; typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA { STORAGE_PROTOCOL_TYPE ProtocolType; ULONG DataType; ULONG ProtocolDataRequestValue; ULONG ProtocolDataRequestSubValue; ULONG ProtocolDataOffset; ULONG ProtocolDataLength; ULONG FixedProtocolReturnData; ULONG Reserved[3]; } STORAGE_PROTOCOL_SPECIFIC_DATA; ASSERT_SIZEOF(STORAGE_PROTOCOL_SPECIFIC_DATA, 40); } // namespace win10 // IOCTL_STORAGE_PREDICT_FAILURE ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100); ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512); // 3ware specific versions of SMART ioctl structs #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters #pragma pack(1) typedef struct _GETVERSIONINPARAMS_EX { BYTE bVersion; BYTE bRevision; BYTE bReserved; BYTE bIDEDeviceMap; DWORD fCapabilities; DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map WORD wIdentifier; // Vendor specific identifier WORD wControllerId; // 3ware specific: Controller ID (0,1,...) ULONG dwReserved[2]; } GETVERSIONINPARAMS_EX; typedef struct _SENDCMDINPARAMS_EX { DWORD cBufferSize; IDEREGS irDriveRegs; BYTE bDriveNumber; BYTE bPortNumber; // 3ware specific: port number WORD wIdentifier; // Vendor specific identifier DWORD dwReserved[4]; BYTE bBuffer[1]; } SENDCMDINPARAMS_EX; #pragma pack() ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS)); ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS)); // NVME_PASS_THROUGH #ifndef NVME_PASS_THROUGH_SRB_IO_CODE #define NVME_SIG_STR "NvmeMini" #define NVME_STORPORT_DRIVER 0xe000 #define NVME_PASS_THROUGH_SRB_IO_CODE \ CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS) #pragma pack(1) typedef struct _NVME_PASS_THROUGH_IOCTL { SRB_IO_CONTROL SrbIoCtrl; ULONG VendorSpecific[6]; ULONG NVMeCmd[16]; // Command DW[0...15] ULONG CplEntry[4]; // Completion DW[0...3] ULONG Direction; // 0=No, 1=Out, 2=In, 3=I/O ULONG QueueId; // 0=AdminQ ULONG DataBufferLen; // sizeof(DataBuffer) if Data In ULONG MetaDataLen; ULONG ReturnBufferLen; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out UCHAR DataBuffer[1]; } NVME_PASS_THROUGH_IOCTL; #pragma pack() #endif // NVME_PASS_THROUGH_SRB_IO_CODE ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE, (int)0xe0002000); ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, 152+1); ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer)+1); // CSMI structs ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL)); ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204); ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080); ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168); // aacraid struct ASSERT_SIZEOF(SCSI_REQUEST_BLOCK, SELECT_WIN_32_64(64, 88)); } // extern "C" ///////////////////////////////////////////////////////////////////////////// namespace os_win32 { // no need to publish anything, name provided for Doxygen #ifdef _MSC_VER #pragma warning(disable:4250) #endif static int is_permissive() { if (!failuretest_permissive) { pout("To continue, add one or more '-T permissive' options.\n"); return 0; } failuretest_permissive--; return 1; } // return number for drive letter, -1 on error // "[A-Za-z]:([/\\][.]?)?" => 0-25 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files static int drive_letter(const char * s) { return ( (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z')) && s[1] == ':' && (!s[2] || ( strchr("/\\\"", s[2]) && (!s[3] || (s[3] == '.' && !s[4]))) ) ? (s[0] & 0x1f) - 1 : -1); } // Skip trailing "/dev/", do not allow "/dev/X:" static const char * skipdev(const char * s) { return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s); } // "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701 static int sdxy_to_phydrive(const char (& xy)[2+1]) { int phydrive = xy[0] - 'a'; if (xy[1]) phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a'); return phydrive; } static void copy_swapped(unsigned char * dest, const char * src, int destsize) { int srclen = strcspn(src, "\r\n"); int i; for (i = 0; i < destsize-1 && i < srclen-1; i+=2) { dest[i] = src[i+1]; dest[i+1] = src[i]; } if (i < destsize-1 && i < srclen) dest[i+1] = src[i]; } ///////////////////////////////////////////////////////////////////////////// // win_smart_device class win_smart_device : virtual public /*implements*/ smart_device { public: win_smart_device() : smart_device(never_called), m_fh(INVALID_HANDLE_VALUE) { } virtual ~win_smart_device() throw(); virtual bool is_open() const; virtual bool close(); protected: /// Set handle for open() in derived classes. void set_fh(HANDLE fh) { m_fh = fh; } /// Return handle for derived classes. HANDLE get_fh() const { return m_fh; } private: HANDLE m_fh; ///< File handle }; // Common routines for devices with HANDLEs win_smart_device::~win_smart_device() throw() { if (m_fh != INVALID_HANDLE_VALUE) ::CloseHandle(m_fh); } bool win_smart_device::is_open() const { return (m_fh != INVALID_HANDLE_VALUE); } bool win_smart_device::close() { if (m_fh == INVALID_HANDLE_VALUE) return true; BOOL rc = ::CloseHandle(m_fh); m_fh = INVALID_HANDLE_VALUE; return !!rc; } ///////////////////////////////////////////////////////////////////////////// #define SMART_CYL_LOW 0x4F #define SMART_CYL_HI 0xC2 static void print_ide_regs(const IDEREGS * r, int out) { pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); } 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_GET_VERSION, return device map or -1 on error static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0) { GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers)); const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers; DWORD num_out; if (!DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { if (ata_debugmode) pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } assert(num_out == sizeof(GETVERSIONINPARAMS)); if (ata_debugmode > 1) { pout(" SMART_GET_VERSION succeeded, bytes returned: %u\n" " Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n", (unsigned)num_out, vers.bVersion, vers.bRevision, (unsigned)vers.fCapabilities, vers.bIDEDeviceMap); if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n", vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx); } if (ata_version_ex) *ata_version_ex = vers_ex; // TODO: Check vers.fCapabilities here? return vers.bIDEDeviceMap; } // call SMART_* ioctl static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port) { SENDCMDINPARAMS inpar; SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar; unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512]; const SENDCMDOUTPARAMS * outpar; DWORD code, num_out; unsigned int size_out; const char * name; memset(&inpar, 0, sizeof(inpar)); inpar.irDriveRegs = *regs; // Older drivers may require bits 5 and 7 set // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete inpar.irDriveRegs.bDriveHeadReg |= 0xa0; // Drive number 0-3 was required on Win9x/ME only //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4; //inpar.bDriveNumber = drive; if (port >= 0) { // Set RAID port inpar_ex.wIdentifier = SMART_VENDOR_3WARE; inpar_ex.bPortNumber = port; } if (datasize == 512) { code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA"; inpar.cBufferSize = size_out = 512; } else if (datasize == 0) { 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; } else { errno = EINVAL; return -1; } 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 win_ata_device::ata_pass_through() long err = GetLastError(); if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) { pout(" %s failed, Error=%ld\n", name, err); print_ide_regs_io(regs, NULL); } errno = ( err == ERROR_INVALID_FUNCTION/*9x*/ || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); return -1; } // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs outpar = (const SENDCMDOUTPARAMS *)outbuf; if (outpar->DriverStatus.bDriverError) { if (ata_debugmode) { pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name, outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError); print_ide_regs_io(regs, NULL); } errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO); return -1; } if (ata_debugmode > 1) { pout(" %s succeeded, bytes returned: %u (buffer %u)\n", name, (unsigned)num_out, (unsigned)outpar->cBufferSize); print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ? (const IDEREGS *)(outpar->bBuffer) : NULL)); } if (datasize) memcpy(data, outpar->bBuffer, 512); else if (regs->bFeaturesReg == ATA_SMART_STATUS) { if (nonempty(outpar->bBuffer, sizeof(IDEREGS))) memcpy(regs, outpar->bBuffer, sizeof(IDEREGS)); else { // Workaround for driver not returning regs if (ata_debugmode) pout(" WARNING: driver does not return ATA registers in output buffer!\n"); *regs = inpar.irDriveRegs; } } return 0; } ///////////////////////////////////////////////////////////////////////////// // IDE PASS THROUGH (2000, XP, undocumented) // // Based on WinATA.cpp, 2002 c't/Matthias Withopf // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) { if (datasize > 512) { errno = EINVAL; return -1; } 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; 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 (ata_debugmode) { pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } VirtualFree(buf, 0, MEM_RELEASE); errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); return -1; } // Check ATA status if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) { if (ata_debugmode) { 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 (ata_debugmode) { pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n", (unsigned)num_out, (unsigned)buf->DataBufferSize); print_ide_regs_io(regs, &buf->IdeReg); } VirtualFree(buf, 0, MEM_RELEASE); errno = EIO; return -1; } memcpy(data, buf->DataBuffer, datasize); } if (ata_debugmode > 1) { pout(" IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %u (buffer %u)\n", (unsigned)num_out, (unsigned)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 (Win2003, XP SP2) // Warning: // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data // transfer per command. Therefore, multi-sector transfers are only supported // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS // or READ/WRITE LOG EXT work only with single sector transfers. // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE. // See: // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize) { const int max_sectors = 32; // TODO: Allocate dynamic buffer typedef struct { ATA_PASS_THROUGH_EX apt; ULONG Filler; UCHAR ucDataBuf[max_sectors * 512]; } ATA_PASS_THROUGH_EX_WITH_BUFFERS; const unsigned char magic = 0xcf; ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab)); ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX); //ab.apt.PathId = 0; //ab.apt.TargetId = 0; //ab.apt.Lun = 0; ab.apt.TimeOutValue = 60; // seconds unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf); ab.apt.DataBufferOffset = size; if (datasize > 0) { if (datasize > (int)sizeof(ab.ucDataBuf)) { errno = EINVAL; return -1; } ab.apt.AtaFlags = ATA_FLAGS_DATA_IN; ab.apt.DataTransferLength = datasize; size += datasize; ab.ucDataBuf[0] = magic; } else if (datasize < 0) { if (-datasize > (int)sizeof(ab.ucDataBuf)) { errno = EINVAL; return -1; } ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT; ab.apt.DataTransferLength = -datasize; size += -datasize; memcpy(ab.ucDataBuf, data, -datasize); } else { assert(ab.apt.AtaFlags == 0); assert(ab.apt.DataTransferLength == 0); } assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS)); IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile; IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile; *ctfregs = *regs; if (prev_regs) { *ptfregs = *prev_regs; ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND; } DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH, &ab, size, &ab, size, &num_out, NULL)) { long err = GetLastError(); if (ata_debugmode) { pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); return -1; } // Check ATA status if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) { if (ata_debugmode) { pout(" IOCTL_ATA_PASS_THROUGH command failed:\n"); print_ide_regs_io(regs, ctfregs); } errno = EIO; return -1; } // Check and copy data if (datasize > 0) { if ( num_out != size || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) { if (ata_debugmode) { pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } errno = EIO; return -1; } memcpy(data, ab.ucDataBuf, datasize); } if (ata_debugmode > 1) { pout(" IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } *regs = *ctfregs; if (prev_regs) *prev_regs = *ptfregs; return 0; } ///////////////////////////////////////////////////////////////////////////// // SMART IOCTL via SCSI MINIPORT ioctl // This function is handled by ATAPI port driver (atapi.sys) or by SCSI // miniport driver (via SCSI port driver scsiport.sys). // It can be used to skip the missing or broken handling of some SMART // command codes (e.g. READ_LOG) in the disk class driver (disk.sys) static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize) { // Select code DWORD code = 0; const char * name = 0; if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) { code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY"; } else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) { case ATA_SMART_READ_VALUES: code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break; case ATA_SMART_READ_THRESHOLDS: code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break; case ATA_SMART_ENABLE: code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break; case ATA_SMART_DISABLE: code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break; case ATA_SMART_STATUS: code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break; case ATA_SMART_AUTOSAVE: code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break; //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break; case ATA_SMART_IMMEDIATE_OFFLINE: code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break; case ATA_SMART_AUTO_OFFLINE: code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break; case ATA_SMART_READ_LOG_SECTOR: code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break; case ATA_SMART_WRITE_LOG_SECTOR: code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break; } if (!code) { errno = ENOSYS; return -1; } // Set SRB struct { SRB_IO_CONTROL srbc; union { SENDCMDINPARAMS in; SENDCMDOUTPARAMS out; } params; char space[512-1]; } sb; ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512); memset(&sb, 0, sizeof(sb)); unsigned size; if (datasize > 0) { if (datasize > (int)sizeof(sb.space)+1) { errno = EINVAL; return -1; } size = datasize; } else if (datasize < 0) { if (-datasize > (int)sizeof(sb.space)+1) { errno = EINVAL; return -1; } size = -datasize; memcpy(sb.params.in.bBuffer, data, size); } else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS) size = sizeof(IDEREGS); else size = 0; sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL); memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys sb.srbc.Timeout = 60; // seconds sb.srbc.ControlCode = code; //sb.srbc.ReturnCode = 0; sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size; sb.params.in.irDriveRegs = *regs; sb.params.in.cBufferSize = size; // Call miniport ioctl size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1; DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &sb, size, &sb, size, &num_out, NULL)) { long err = GetLastError(); if (ata_debugmode) { pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err); print_ide_regs_io(regs, NULL); } errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); return -1; } // Check result if (sb.srbc.ReturnCode) { if (ata_debugmode) { pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode); print_ide_regs_io(regs, NULL); } errno = EIO; return -1; } if (sb.params.out.DriverStatus.bDriverError) { if (ata_debugmode) { pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name, sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError); print_ide_regs_io(regs, NULL); } errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO); return -1; } if (ata_debugmode > 1) { pout(" IOCTL_SCSI_MINIPORT_%s succeeded, bytes returned: %u (buffer %u)\n", name, (unsigned)num_out, (unsigned)sb.params.out.cBufferSize); print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ? (const IDEREGS *)(sb.params.out.bBuffer) : 0)); } if (datasize > 0) memcpy(data, sb.params.out.bBuffer, datasize); else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS) memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS)); return 0; } ///////////////////////////////////////////////////////////////////////////// // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port) { struct { SRB_IO_CONTROL srbc; IDEREGS regs; UCHAR buffer[512]; } sb; ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512); if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) { errno = EINVAL; return -1; } memset(&sb, 0, sizeof(sb)); strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature)); sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL); sb.srbc.Timeout = 60; // seconds sb.srbc.ControlCode = 0xA0000000; sb.srbc.ReturnCode = 0; sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1); sb.regs = *regs; sb.regs.bReserved = port; DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) { long err = GetLastError(); if (ata_debugmode) { pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); return -1; } if (sb.srbc.ReturnCode) { if (ata_debugmode) { pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode); print_ide_regs_io(regs, NULL); } errno = EIO; return -1; } // Copy data if (datasize > 0) memcpy(data, sb.buffer, datasize); if (ata_debugmode > 1) { pout(" ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, &sb.regs); } *regs = sb.regs; return 0; } ///////////////////////////////////////////////////////////////////////////// // 3ware specific call to update the devicemap returned by SMART_GET_VERSION. // 3DM/CLI "Rescan Controller" function does not to always update it. static int update_3ware_devicemap_ioctl(HANDLE hdevice) { SRB_IO_CONTROL srbc; memset(&srbc, 0, sizeof(srbc)); strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature)); srbc.HeaderLength = sizeof(SRB_IO_CONTROL); srbc.Timeout = 60; // seconds srbc.ControlCode = 0xCC010014; srbc.ReturnCode = 0; srbc.Length = 0; DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) { long err = GetLastError(); if (ata_debugmode) pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err); errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); return -1; } if (srbc.ReturnCode) { if (ata_debugmode) pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode); errno = EIO; return -1; } if (ata_debugmode > 1) pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n"); return 0; } ///////////////////////////////////////////////////////////////////////////// // IOCTL_STORAGE_QUERY_PROPERTY union STORAGE_DEVICE_DESCRIPTOR_DATA { STORAGE_DEVICE_DESCRIPTOR desc; char raw[256]; }; // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device. // (This works without admin rights) static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data) { STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} }; memset(data, 0, sizeof(*data)); DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) { if (ata_debugmode > 1 || scsi_debugmode > 1) pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } if (ata_debugmode > 1 || scsi_debugmode > 1) { pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n" " Vendor: \"%s\"\n" " Product: \"%s\"\n" " Revision: \"%s\"\n" " Removable: %s\n" " BusType: 0x%02x\n", (data->desc.VendorIdOffset ? data->raw+data->desc.VendorIdOffset : "(null)"), (data->desc.ProductIdOffset ? data->raw+data->desc.ProductIdOffset : "(null)"), (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"), (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType ); } return 0; } ///////////////////////////////////////////////////////////////////////////// // IOCTL_STORAGE_PREDICT_FAILURE // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value // or -1 on error, optionally return VendorSpecific data. // (This works without admin rights) static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0) { STORAGE_PREDICT_FAILURE pred; memset(&pred, 0, sizeof(pred)); DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE, 0, 0, &pred, sizeof(pred), &num_out, NULL)) { if (ata_debugmode > 1) pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } if (ata_debugmode > 1) { pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n" " PredictFailure: 0x%08x\n" " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n", (unsigned)pred.PredictFailure, pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2], pred.VendorSpecific[sizeof(pred.VendorSpecific)-1] ); } if (data) memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific)); return (!pred.PredictFailure ? 0 : 1); } // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id) { STORAGE_DEVICE_DESCRIPTOR_DATA data; if (storage_query_property_ioctl(hdevice, &data)) return -1; memset(id, 0, sizeof(*id)); // Some drivers split ATA model string into VendorId and ProductId, // others return it as ProductId only. char model[sizeof(id->model) + 1] = ""; unsigned i = 0; if (data.desc.VendorIdOffset) { for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++) model[i] = data.raw[data.desc.VendorIdOffset+i]; } if (data.desc.ProductIdOffset) { while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId i--; // Ignore VendorId "ATA" if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' ')) i = 0; for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++) model[i] = data.raw[data.desc.ProductIdOffset+j]; } while (i > 0 && model[i-1] == ' ') i--; model[i] = 0; copy_swapped(id->model, model, sizeof(id->model)); if (data.desc.ProductRevisionOffset) copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev)); id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid return 0; } // Get Serial Number in IDENTIFY from WMI static bool get_serial_from_wmi(int drive, ata_identify_device * id) { bool debug = (ata_debugmode > 1); wbem_services ws; if (!ws.connect()) { if (debug) pout("WMI connect failed\n"); return false; } wbem_object wo; if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE " "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive)) return false; std::string serial = wo.get_str("SerialNumber"); if (debug) pout(" WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str()); copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no)); return true; } ///////////////////////////////////////////////////////////////////////////// // USB ID detection using WMI // Get USB ID for a physical or logical drive number static bool get_usb_id(int phydrive, int logdrive, unsigned short & vendor_id, unsigned short & product_id) { bool debug = (scsi_debugmode > 1); wbem_services ws; if (!ws.connect()) { if (debug) pout("WMI connect failed\n"); return false; } // Get device name std::string name; wbem_object wo; if (0 <= logdrive && logdrive <= 'Z'-'A') { // Drive letter -> Partition info if (!ws.query1(wo, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition", 'A'+logdrive)) return false; std::string partid = wo.get_str("DeviceID"); if (debug) pout("%c: --> \"%s\" -->\n", 'A'+logdrive, partid.c_str()); // Partition ID -> Physical drive info if (!ws.query1(wo, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive", partid.c_str())) return false; name = wo.get_str("Model"); if (debug) pout("%s --> \"%s\":\n", wo.get_str("DeviceID").c_str(), name.c_str()); } else if (phydrive >= 0) { // Physical drive number -> Physical drive info if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive)) return false; name = wo.get_str("Model"); if (debug) pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive, name.c_str()); } else return false; // Get USB_CONTROLLER -> DEVICE associations wbem_enumerator we; if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice")) return false; unsigned short usb_venid = 0, prev_usb_venid = 0; unsigned short usb_proid = 0, prev_usb_proid = 0; std::string prev_usb_ant; std::string prev_ant, ant, dep; const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\""); while (we.next(wo)) { prev_ant = ant; // Find next 'USB_CONTROLLER, DEVICE' pair ant = wo.get_str("Antecedent"); dep = wo.get_str("Dependent"); if (debug && ant != prev_ant) pout(" %s:\n", ant.c_str()); // Extract DeviceID regular_expression::match_range match[2]; if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) { if (debug) pout(" | (\"%s\")\n", dep.c_str()); continue; } std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so); if (str_starts_with(devid, "USB\\\\VID_")) { // USB bridge entry, save CONTROLLER, ID int nc = -1; if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n", &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) { prev_usb_venid = prev_usb_proid = 0; } prev_usb_ant = ant; if (debug) pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid); } else if (str_starts_with(devid, "USBSTOR\\\\") || str_starts_with(devid, "SCSI\\\\")) { // USBSTORage or SCSI device found if (debug) pout(" +--> \"%s\"\n", devid.c_str()); // Retrieve name wbem_object wo2; if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str())) continue; std::string name2 = wo2.get_str("Name"); // Continue if not name of physical disk drive if (name2 != name) { if (debug) pout(" +---> (\"%s\")\n", name2.c_str()); continue; } // Fail if previous USB bridge is associated to other controller or ID is unknown if (!(ant == prev_usb_ant && prev_usb_venid)) { if (debug) pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str()); return false; } // Handle multiple devices with same name if (usb_venid) { // Fail if multiple devices with same name have different USB bridge types if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) { if (debug) pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str()); return false; } } // Found usb_venid = prev_usb_venid; usb_proid = prev_usb_proid; if (debug) pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid); // Continue to check for duplicate names ... } else { if (debug) pout(" | \"%s\"\n", devid.c_str()); } } if (!usb_venid) return false; vendor_id = usb_venid; product_id = usb_proid; return true; } ///////////////////////////////////////////////////////////////////////////// // Call GetDevicePowerState() // returns: 1=active, 0=standby, -1=error // (This would also work for SCSI drives) static int get_device_power_state(HANDLE hdevice) { BOOL state = TRUE; if (!GetDevicePowerState(hdevice, &state)) { long err = GetLastError(); if (ata_debugmode) pout(" GetDevicePowerState() failed, Error=%ld\n", err); errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); // TODO: This may not work as expected on transient errors, // because smartd interprets -1 as SLEEP mode regardless of errno. return -1; } if (ata_debugmode > 1) pout(" GetDevicePowerState() succeeded, state=%d\n", state); return state; } ///////////////////////////////////////////////////////////////////////////// // win_ata_device class win_ata_device : public /*implements*/ ata_device, public /*extends*/ win_smart_device { public: win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual ~win_ata_device() throw(); virtual bool open(); virtual bool is_powered_down(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); virtual bool ata_identify_is_cached() const; private: bool open(bool query_device); bool open(int phydrive, int logdrive, const char * options, int port, bool query_device); std::string m_options; bool m_usr_options; // options set by user? bool m_admin; // open with admin access? int m_phydrive; // PhysicalDriveN or -1 bool m_id_is_cached; // ata_identify_is_cached() return value. bool m_is_3ware; // LSI/3ware controller detected? int m_port; // LSI/3ware port int m_smartver_state; }; win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), m_usr_options(false), m_admin(false), m_phydrive(-1), m_id_is_cached(false), m_is_3ware(false), m_port(-1), m_smartver_state(0) { } win_ata_device::~win_ata_device() throw() { } // Get default ATA device options static const char * ata_get_def_options() { return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH, // STORAGE_*, SCSI_MINIPORT_* } // Open ATA device bool win_ata_device::open() { // Open device for r/w operations return open(false); } bool win_ata_device::open(bool query_device) { const char * name = skipdev(get_dev_name()); int len = strlen(name); // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1; if ( sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1 && ((n1 == len && !options[0]) || n2 == len) ) { return open(sdxy_to_phydrive(drive), -1, options, -1, query_device); } // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1; unsigned port = ~0; if ( sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2 && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) { return open(sdxy_to_phydrive(drive), -1, options, port, query_device); } // pd,N => Physical drive , RAID port N int phydrive = -1; port = ~0; n1 = -1; n2 = -1; if ( sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1 && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) { return open(phydrive, -1, "", (int)port, query_device); } // [a-zA-Z]: => Physical drive behind logical drive 0-25 int logdrive = drive_letter(name); if (logdrive >= 0) { return open(-1, logdrive, "", -1, query_device); } return set_err(EINVAL); } bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port, bool query_device) { m_phydrive = -1; char devpath[30]; if (0 <= phydrive && phydrive <= 255) snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive)); else if (0 <= logdrive && logdrive <= 'Z'-'A') snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive); else return set_err(ENOENT); // Open device HANDLE h = INVALID_HANDLE_VALUE; if (!(*options && !options[strspn(options, "fp")]) && !query_device) { // Open with admin rights m_admin = true; h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); } if (h == INVALID_HANDLE_VALUE) { // Open without admin rights m_admin = false; h = CreateFileA(devpath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); } if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) set_err(EACCES, "%s: access denied", devpath); else set_err(EIO, "%s: Error=%ld", devpath, err); return false; } set_fh(h); // Warn once if admin rights are missing if (!m_admin && !query_device) { static bool noadmin_warning = false; if (!noadmin_warning) { pout("Warning: Limited functionality due to missing admin rights\n"); noadmin_warning = true; } } if (ata_debugmode > 1) pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :"")); m_usr_options = false; if (*options) { // Save user options m_options = options; m_usr_options = true; } else if (port >= 0) // RAID: SMART_* and SCSI_MINIPORT m_options = "s3"; else { // Set default options according to Windows version static const char * def_options = ata_get_def_options(); m_options = def_options; } // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call m_port = port; if (port < 0) return true; // 3ware RAID: Get port map GETVERSIONINPARAMS_EX vers_ex; int devmap = smart_get_version(h, &vers_ex); // 3ware RAID if vendor id present m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE); unsigned portmap = 0; if (port >= 0 && devmap >= 0) { // 3ware RAID: check vendor id if (!m_is_3ware) { pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n" "This is no 3ware 9000 controller or driver has no SMART support.\n", vers_ex.wIdentifier); devmap = -1; } else portmap = vers_ex.dwDeviceMapEx; } if (devmap < 0) { pout("%s: ATA driver has no SMART support\n", devpath); if (!is_permissive()) { close(); return set_err(ENOSYS); } } m_smartver_state = 1; { // 3ware RAID: update devicemap first if (!update_3ware_devicemap_ioctl(h)) { if ( smart_get_version(h, &vers_ex) >= 0 && vers_ex.wIdentifier == SMART_VENDOR_3WARE ) portmap = vers_ex.dwDeviceMapEx; } // Check port existence if (!(portmap & (1U << port))) { if (!is_permissive()) { close(); return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port); } } } return true; } ///////////////////////////////////////////////////////////////////////////// // Query OS if device is powered up or down. bool win_ata_device::is_powered_down() { // To check power mode, we open device for query operations only. // Opening for SMART r/w operations can already spin up the disk. bool self_open = !is_open(); if (self_open) if (!open(true)) return false; int rc = get_device_power_state(get_fh()); if (self_open) close(); return !rc; } ///////////////////////////////////////////////////////////////////////////// // Interface to ATA devices bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { // No multi-sector support for now, see above // warning about IOCTL_ATA_PASS_THROUGH if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_output_regs | ata_device::supports_48bit) ) return false; // 3ware RAID: SMART DISABLE without port number disables SMART functions if ( m_is_3ware && m_port < 0 && in.in_regs.command == ATA_SMART_CMD && in.in_regs.features == ATA_SMART_DISABLE) return set_err(ENOSYS, "SMART DISABLE requires 3ware port number"); // Determine ioctl functions valid for this ATA cmd const char * valid_options = 0; switch (in.in_regs.command) { case ATA_IDENTIFY_DEVICE: case ATA_IDENTIFY_PACKET_DEVICE: // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE // and SCSI_MINIPORT_* if requested by user valid_options = (m_usr_options ? "saimf" : "saif"); break; case ATA_CHECK_POWER_MODE: // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk valid_options = "pai3"; break; case ATA_SMART_CMD: switch (in.in_regs.features) { case ATA_SMART_READ_VALUES: case ATA_SMART_READ_THRESHOLDS: case ATA_SMART_AUTOSAVE: case ATA_SMART_ENABLE: case ATA_SMART_DISABLE: case ATA_SMART_AUTO_OFFLINE: // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE // and SCSI_MINIPORT_* if requested by user valid_options = (m_usr_options ? "saimf" : "saif"); break; case ATA_SMART_IMMEDIATE_OFFLINE: // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ? "saim3" : "aim3"); break; case ATA_SMART_READ_LOG_SECTOR: // SMART_RCV_DRIVE_DATA does not support READ_LOG // Try SCSI_MINIPORT also to skip buggy class driver // SMART functions do not support multi sector I/O. if (in.size == 512) valid_options = (m_usr_options ? "saim3" : "aim3"); else valid_options = "a"; break; case ATA_SMART_WRITE_LOG_SECTOR: // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT // but SCSI_MINIPORT_* only if requested by user and single sector. valid_options = (in.size == 512 && m_usr_options ? "am" : "a"); break; case ATA_SMART_STATUS: valid_options = (m_usr_options ? "saimf" : "saif"); break; default: // Unknown SMART command, handle below break; } break; default: // Other ATA command, handle below break; } if (!valid_options) { // No special ATA command found above, select a generic pass through ioctl. if (!( in.direction == ata_cmd_in::no_data || (in.direction == ata_cmd_in::data_in && in.size == 512)) || in.in_regs.is_48bit_cmd() ) // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only valid_options = "a"; else // ATA/IDE_PASS_THROUGH valid_options = "ai"; } if (!m_admin) { // Restrict to IOCTL_STORAGE_* if (strchr(valid_options, 'f')) valid_options = "f"; else if (strchr(valid_options, 'p')) valid_options = "p"; else return set_err(ENOSYS, "Function requires admin rights"); } // Set IDEREGS IDEREGS regs, prev_regs; { const ata_in_regs & lo = in.in_regs; regs.bFeaturesReg = lo.features; regs.bSectorCountReg = lo.sector_count; regs.bSectorNumberReg = lo.lba_low; regs.bCylLowReg = lo.lba_mid; regs.bCylHighReg = lo.lba_high; regs.bDriveHeadReg = lo.device; regs.bCommandReg = lo.command; regs.bReserved = 0; } if (in.in_regs.is_48bit_cmd()) { const ata_in_regs & hi = in.in_regs.prev; prev_regs.bFeaturesReg = hi.features; prev_regs.bSectorCountReg = hi.sector_count; prev_regs.bSectorNumberReg = hi.lba_low; prev_regs.bCylLowReg = hi.lba_mid; prev_regs.bCylHighReg = hi.lba_high; prev_regs.bDriveHeadReg = hi.device; prev_regs.bCommandReg = hi.command; prev_regs.bReserved = 0; } // Set data direction int datasize = 0; char * data = 0; switch (in.direction) { case ata_cmd_in::no_data: break; case ata_cmd_in::data_in: datasize = (int)in.size; data = (char *)in.buffer; break; case ata_cmd_in::data_out: datasize = -(int)in.size; data = (char *)in.buffer; break; default: return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d", (int)in.direction); } // Try all valid ioctls in the order specified in m_options bool powered_up = false; bool out_regs_set = false; bool id_is_cached = false; const char * options = m_options.c_str(); for (int i = 0; ; i++) { char opt = options[i]; if (!opt) { if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) { // Power up reported by GetDevicePowerState() and no ioctl available // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE. regs.bSectorCountReg = 0xff; out_regs_set = true; break; } // No IOCTL found return set_err(ENOSYS); } if (!strchr(valid_options, opt)) // Invalid for this command continue; errno = 0; assert( datasize == 0 || datasize == 512 || (datasize == -512 && strchr("am", opt)) || (datasize > 512 && opt == 'a')); int rc; switch (opt) { default: assert(0); case 's': // call SMART_GET_VERSION once for each drive if (m_smartver_state > 1) { rc = -1; errno = ENOSYS; break; } if (!m_smartver_state) { assert(m_port == -1); GETVERSIONINPARAMS_EX vers_ex; if (smart_get_version(get_fh(), &vers_ex) < 0) { if (!failuretest_permissive) { m_smartver_state = 2; rc = -1; errno = ENOSYS; break; } failuretest_permissive--; } else { // 3ware RAID if vendor id present m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE); } m_smartver_state = 1; } rc = smart_ioctl(get_fh(), ®s, data, datasize, m_port); out_regs_set = (in.in_regs.features == ATA_SMART_STATUS); id_is_cached = (m_port < 0); // Not cached by 3ware driver break; case 'm': rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s, data, datasize); id_is_cached = (m_port < 0); break; case 'a': rc = ata_pass_through_ioctl(get_fh(), ®s, (in.in_regs.is_48bit_cmd() ? &prev_regs : 0), data, datasize); out_regs_set = true; break; case 'i': rc = ide_pass_through_ioctl(get_fh(), ®s, data, datasize); out_regs_set = true; break; case 'f': if (in.in_regs.command == ATA_IDENTIFY_DEVICE) { ata_identify_device * id = reinterpret_cast(data); rc = get_identify_from_device_property(get_fh(), id); if (rc == 0 && m_phydrive >= 0) get_serial_from_wmi(m_phydrive, id); id_is_cached = true; } else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) { case ATA_SMART_READ_VALUES: rc = storage_predict_failure_ioctl(get_fh(), data); if (rc > 0) rc = 0; break; case ATA_SMART_ENABLE: rc = 0; break; case ATA_SMART_STATUS: rc = storage_predict_failure_ioctl(get_fh()); if (rc == 0) { // Good SMART status out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f; } else if (rc > 0) { // Bad SMART status out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4; rc = 0; } break; default: errno = ENOSYS; rc = -1; } else { errno = ENOSYS; rc = -1; } break; case '3': rc = ata_via_3ware_miniport_ioctl(get_fh(), ®s, data, datasize, m_port); out_regs_set = true; break; case 'p': assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0); rc = get_device_power_state(get_fh()); if (rc == 0) { // Power down reported by GetDevicePowerState(), using a passthrough ioctl would // spin up the drive => simulate ATA result STANDBY. regs.bSectorCountReg = 0x00; out_regs_set = true; } else if (rc > 0) { // Power up reported by GetDevicePowerState(), but this reflects the actual mode // only if it is selected by the device driver => try a passthrough ioctl to get the // actual mode, if none available simulate ACTIVE/IDLE. powered_up = true; rc = -1; errno = ENOSYS; } break; } if (!rc) // Working ioctl found break; if (errno != ENOSYS) // Abort on I/O error return set_err(errno); out_regs_set = false; // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case } // Return IDEREGS if set if (out_regs_set) { ata_out_regs & lo = out.out_regs; lo.error = regs.bFeaturesReg; lo.sector_count = regs.bSectorCountReg; lo.lba_low = regs.bSectorNumberReg; lo.lba_mid = regs.bCylLowReg; lo.lba_high = regs.bCylHighReg; lo.device = regs.bDriveHeadReg; lo.status = regs.bCommandReg; if (in.in_regs.is_48bit_cmd()) { ata_out_regs & hi = out.out_regs.prev; hi.sector_count = prev_regs.bSectorCountReg; hi.lba_low = prev_regs.bSectorNumberReg; hi.lba_mid = prev_regs.bCylLowReg; hi.lba_high = prev_regs.bCylHighReg; } } if ( in.in_regs.command == ATA_IDENTIFY_DEVICE || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) // Update ata_identify_is_cached() result according to ioctl used. m_id_is_cached = id_is_cached; return true; } // Return true if OS caches the ATA identify sector bool win_ata_device::ata_identify_is_cached() const { return m_id_is_cached; } ////////////////////////////////////////////////////////////////////// // csmi_device class csmi_device : virtual public /*extends*/ smart_device { public: enum { max_number_of_ports = 32 }; /// Get bitmask of used ports unsigned get_ports_used(); protected: csmi_device() : smart_device(never_called) { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); } typedef signed char port_2_index_map[max_number_of_ports]; /// Get phy info and port mapping, return #ports or -1 on error int get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p2i); /// Select physical drive bool select_port(int port); /// Get info for selected physical drive const CSMI_SAS_PHY_ENTITY & get_phy_ent() const { return m_phy_ent; } /// Call platform-specific CSMI ioctl virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer, unsigned csmi_bufsiz) = 0; private: CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy }; ///////////////////////////////////////////////////////////////////////////// int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p2i) { // max_number_of_ports must match CSMI_SAS_PHY_INFO.Phy[] array size typedef char ASSERT_phy_info_size[ (int)(sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0])) == max_number_of_ports ? 1 : -1] ATTR_UNUSED; // Get driver info to check CSMI support CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf; memset(&driver_info_buf, 0, sizeof(driver_info_buf)); if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf))) return -1; if (scsi_debugmode > 1) { const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information; pout("CSMI_SAS_DRIVER_INFO:\n"); pout(" Name: \"%.81s\"\n", driver_info.szName); pout(" Description: \"%.81s\"\n", driver_info.szDescription); pout(" Revision: %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision); } // Get Phy info CSMI_SAS_PHY_INFO_BUFFER phy_info_buf; memset(&phy_info_buf, 0, sizeof(phy_info_buf)); if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf))) return -1; phy_info = phy_info_buf.Information; if (phy_info.bNumberOfPhys > max_number_of_ports) { set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys); return -1; } // Create port -> index map // IRST Release // Phy[i].Value 9.x 10.x 14.8 15.2 16.0 // ---------------------------------------------------------- // bPortIdentifier 0xff 0xff port 0x00 port // Identify.bPhyIdentifier index? index? index index port // Attached.bPhyIdentifier 0x00 0x00 0x00 index 0x00 // // Empty ports with hotplug support may appear in Phy[]. int number_of_ports; for (int mode = 0; ; mode++) { for (int i = 0; i < max_number_of_ports; i++) p2i[i] = -1; number_of_ports = 0; bool found = false; for (int i = 0; i < max_number_of_ports; i++) { const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) continue; // Try to detect which field contains the actual port number. // Use a bPhyIdentifier or the bPortIdentifier if unique // and not always identical to table index, otherwise use index. int port; switch (mode) { case 0: port = pe.Attached.bPhyIdentifier; break; case 1: port = pe.Identify.bPhyIdentifier; break; case 2: port = pe.bPortIdentifier; break; default: port = i; break; } if (!(port < max_number_of_ports && p2i[port] == -1)) { found = false; break; } p2i[port] = i; if (number_of_ports <= port) number_of_ports = port + 1; if (port != i) found = true; } if (found || mode > 2) break; } if (scsi_debugmode > 1) { pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys); for (int i = 0; i < max_number_of_ports; i++) { const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached; if (id.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) continue; int port = -1; for (int p = 0; p < max_number_of_ports && port < 0; p++) { if (p2i[p] == i) port = p; } pout("Phy[%d] Port: %d\n", i, port); pout(" Type: 0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType); pout(" InitProto: 0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol); pout(" TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol); pout(" PortIdent: 0x%02x\n", pe.bPortIdentifier); pout(" PhyIdent: 0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier); const unsigned char * b = id.bSASAddress; pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); b = at.bSASAddress; pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); } } return number_of_ports; } unsigned csmi_device::get_ports_used() { CSMI_SAS_PHY_INFO phy_info; port_2_index_map p2i; int number_of_ports = get_phy_info(phy_info, p2i); if (number_of_ports < 0) return 0; unsigned ports_used = 0; for (int p = 0; p < max_number_of_ports; p++) { int i = p2i[p]; if (i < 0) continue; const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) continue; switch (pe.Attached.bTargetPortProtocol) { case CSMI_SAS_PROTOCOL_SATA: case CSMI_SAS_PROTOCOL_STP: break; default: continue; } ports_used |= (1U << p); } return ports_used; } bool csmi_device::select_port(int port) { if (!(0 <= port && port < max_number_of_ports)) return set_err(EINVAL, "Invalid port number %d", port); CSMI_SAS_PHY_INFO phy_info; port_2_index_map p2i; int number_of_ports = get_phy_info(phy_info, p2i); if (number_of_ports < 0) return false; int port_index = p2i[port]; if (port_index < 0) { if (port < number_of_ports) return set_err(ENOENT, "Port %d is disabled", port); else return set_err(ENOENT, "Port %d does not exist (#ports: %d)", port, number_of_ports); } const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[port_index]; if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) return set_err(ENOENT, "No device on port %d", port); switch (phy_ent.Attached.bTargetPortProtocol) { case CSMI_SAS_PROTOCOL_SATA: case CSMI_SAS_PROTOCOL_STP: break; default: return set_err(ENOENT, "No SATA device on port %d (protocol: %d)", port, phy_ent.Attached.bTargetPortProtocol); } m_phy_ent = phy_ent; return true; } ////////////////////////////////////////////////////////////////////// // csmi_ata_device class csmi_ata_device : virtual public /*extends*/ csmi_device, virtual public /*implements*/ ata_device { public: virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); protected: csmi_ata_device() : smart_device(never_called) { } }; ////////////////////////////////////////////////////////////////////// bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_output_regs | ata_device::supports_multi_sector | ata_device::supports_48bit, "CSMI") ) return false; // Create buffer with appropriate size raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size); CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data(); // Set addresses from Phy info CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters; const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent(); pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier; pthru.bPortIdentifier = phy_ent.bPortIdentifier; memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress, sizeof(pthru.bDestinationSASAddress)); pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED; // Set transfer mode switch (in.direction) { case ata_cmd_in::no_data: pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED; break; case ata_cmd_in::data_in: pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ; pthru.uDataLength = in.size; break; case ata_cmd_in::data_out: pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE; pthru.uDataLength = in.size; memcpy(pthru_buf->bDataBuffer, in.buffer, in.size); break; default: return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d", (int)in.direction); } // Set host-to-device FIS { unsigned char * fis = pthru.bCommandFIS; const ata_in_regs & lo = in.in_regs; const ata_in_regs & hi = in.in_regs.prev; fis[ 0] = 0x27; // Type: host-to-device FIS fis[ 1] = 0x80; // Bit7: Update command register fis[ 2] = lo.command; fis[ 3] = lo.features; fis[ 4] = lo.lba_low; fis[ 5] = lo.lba_mid; fis[ 6] = lo.lba_high; fis[ 7] = lo.device; fis[ 8] = hi.lba_low; fis[ 9] = hi.lba_mid; fis[10] = hi.lba_high; fis[11] = hi.features; fis[12] = lo.sector_count; fis[13] = hi.sector_count; } // Call ioctl if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) { return false; } // Get device-to-host FIS { const unsigned char * fis = pthru_buf->Status.bStatusFIS; ata_out_regs & lo = out.out_regs; lo.status = fis[ 2]; lo.error = fis[ 3]; lo.lba_low = fis[ 4]; lo.lba_mid = fis[ 5]; lo.lba_high = fis[ 6]; lo.device = fis[ 7]; lo.sector_count = fis[12]; if (in.in_regs.is_48bit_cmd()) { ata_out_regs & hi = out.out_regs.prev; hi.lba_low = fis[ 8]; hi.lba_mid = fis[ 9]; hi.lba_high = fis[10]; hi.sector_count = fis[13]; } } // Get data if (in.direction == ata_cmd_in::data_in) // TODO: Check ptru_buf->Status.uDataBytes memcpy(in.buffer, pthru_buf->bDataBuffer, in.size); return true; } ////////////////////////////////////////////////////////////////////// // win_csmi_device class win_csmi_device : public /*implements*/ csmi_ata_device { public: win_csmi_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual ~win_csmi_device() throw(); virtual bool open(); virtual bool close(); virtual bool is_open() const; bool open_scsi(); protected: virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer, unsigned csmi_bufsiz); private: HANDLE m_fh; ///< Controller device handle int m_port; ///< Port number }; ////////////////////////////////////////////////////////////////////// win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "ata", req_type), m_fh(INVALID_HANDLE_VALUE), m_port(-1) { } win_csmi_device::~win_csmi_device() throw() { if (m_fh != INVALID_HANDLE_VALUE) CloseHandle(m_fh); } bool win_csmi_device::is_open() const { return (m_fh != INVALID_HANDLE_VALUE); } bool win_csmi_device::close() { if (m_fh == INVALID_HANDLE_VALUE) return true; BOOL rc = CloseHandle(m_fh); m_fh = INVALID_HANDLE_VALUE; return !!rc; } bool win_csmi_device::open_scsi() { // Parse name unsigned contr_no = ~0, port = ~0; int nc = -1; const char * name = skipdev(get_dev_name()); if (!( sscanf(name, "csmi%u,%u%n", &contr_no, &port, &nc) >= 0 && nc == (int)strlen(name) && contr_no <= 9 && port < 32) ) return set_err(EINVAL); // Open controller handle char devpath[30]; snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no); HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) set_err(EACCES, "%s: access denied", devpath); else set_err(EIO, "%s: Error=%ld", devpath, err); return false; } if (scsi_debugmode > 1) pout(" %s: successfully opened\n", devpath); m_fh = h; m_port = port; return true; } bool win_csmi_device::open() { if (!open_scsi()) return false; // Get Phy info for this drive if (!select_port(m_port)) { close(); return false; } return true; } bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer, unsigned csmi_bufsiz) { // Determine signature const char * sig; switch (code) { case CC_CSMI_SAS_GET_DRIVER_INFO: sig = CSMI_ALL_SIGNATURE; break; case CC_CSMI_SAS_GET_PHY_INFO: case CC_CSMI_SAS_STP_PASSTHRU: sig = CSMI_SAS_SIGNATURE; break; default: return set_err(ENOSYS, "Unknown CSMI code=%u", code); } // Set header csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER); strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature)); csmi_buffer->Timeout = CSMI_SAS_TIMEOUT; csmi_buffer->ControlCode = code; csmi_buffer->ReturnCode = 0; csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER); // Call function DWORD num_out = 0; if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT, csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) { long err = GetLastError(); if (scsi_debugmode) pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err); if ( err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_DEV_NOT_EXIST) return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err); else return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err); } // Check result if (csmi_buffer->ReturnCode) { if (scsi_debugmode) { pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n", code, (unsigned)csmi_buffer->ReturnCode); } return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode); } if (scsi_debugmode > 1) pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out); return true; } ////////////////////////////////////////////////////////////////////// // win_tw_cli_device // Routines for pseudo device /dev/tw_cli/* // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window // TODO: This is OS independent class win_tw_cli_device : public /*implements*/ ata_device_with_command_set { public: win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual bool is_open() const; virtual bool open(); virtual bool close(); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); private: bool m_ident_valid, m_smart_valid; ata_identify_device m_ident_buf; ata_smart_values m_smart_buf; }; ///////////////////////////////////////////////////////////////////////////// win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "tw_cli", req_type), m_ident_valid(false), m_smart_valid(false) { memset(&m_ident_buf, 0, sizeof(m_ident_buf)); memset(&m_smart_buf, 0, sizeof(m_smart_buf)); } bool win_tw_cli_device::is_open() const { return (m_ident_valid || m_smart_valid); } // Get clipboard data static int get_clipboard(char * data, int datasize) { if (!OpenClipboard(NULL)) return -1; HANDLE h = GetClipboardData(CF_TEXT); if (!h) { CloseClipboard(); return 0; } const void * p = GlobalLock(h); int n = GlobalSize(h); if (n > datasize) n = datasize; memcpy(data, p, n); GlobalFree(h); CloseClipboard(); return n; } static const char * findstr(const char * str, const char * sub) { const char * s = strstr(str, sub); return (s ? s+strlen(sub) : ""); } bool win_tw_cli_device::open() { m_ident_valid = m_smart_valid = false; const char * name = skipdev(get_dev_name()); // Read tw_cli or 3DM browser output into buffer char buffer[4096]; int size = -1, n1 = -1, n2 = -1; if (!strcmp(name, "tw_cli/clip")) { // read clipboard size = get_clipboard(buffer, sizeof(buffer)); } else if (!strcmp(name, "tw_cli/stdin")) { // read stdin size = fread(buffer, 1, sizeof(buffer), stdin); } else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) { // tw_cli/cx/py => read output from "tw_cli /cx/py show all" char cmd[100]; snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1); if (ata_debugmode > 1) pout("%s: Run: \"%s\"\n", name, cmd); FILE * f = popen(cmd, "rb"); if (f) { size = fread(buffer, 1, sizeof(buffer), f); pclose(f); } } else { return set_err(EINVAL); } if (ata_debugmode > 1) pout("%s: Read %d bytes\n", name, size); if (size <= 0) return set_err(ENOENT); if (size >= (int)sizeof(buffer)) return set_err(EIO); buffer[size] = 0; if (ata_debugmode > 1) pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":"")); // Fake identify sector ASSERT_SIZEOF(ata_identify_device, 512); ata_identify_device * id = &m_ident_buf; memset(id, 0, sizeof(*id)); copy_swapped(id->model , findstr(buffer, " Model = " ), sizeof(id->model)); copy_swapped(id->fw_rev , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev)); copy_swapped(id->serial_no, findstr(buffer, " Serial = " ), sizeof(id->serial_no)); unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)" sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks); if (nblocks) { id->words047_079[49-47] = 0x0200; // size valid id->words047_079[60-47] = (unsigned short)(nblocks ); // secs_16 id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32 } id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid // Parse smart data hex dump const char * s = findstr(buffer, "Drive Smart Data:"); if (!*s) s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x if (!*s) { s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window if (*s) { const char * s1 = findstr(s, " 0)) break; s += n; if (*s == '<') // "
" s += strcspn(s, "\r\n"); } if (i < 512) { if (!id->model[1]) { // No useful data found char * err = strstr(buffer, "Error:"); if (!err) err = strstr(buffer, "error :"); if (err && (err = strchr(err, ':'))) { // Show tw_cli error message err++; err[strcspn(err, "\r\n")] = 0; return set_err(EIO, "%s", err); } return set_err(EIO); } sd = 0; } m_ident_valid = true; m_smart_valid = !!sd; return true; } bool win_tw_cli_device::close() { m_ident_valid = m_smart_valid = false; return true; } int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data) { switch (command) { case IDENTIFY: if (!m_ident_valid) break; memcpy(data, &m_ident_buf, 512); return 0; case READ_VALUES: if (!m_smart_valid) break; memcpy(data, &m_smart_buf, 512); return 0; case ENABLE: case STATUS: case STATUS_CHECK: // Fake "good" SMART status return 0; default: break; } // Arrive here for all unsupported commands set_err(ENOSYS); return -1; } ///////////////////////////////////////////////////////////////////////////// // win_scsi_device // SPT Interface (for SCSI devices and ATA devices behind SATLs) class win_scsi_device : public /*implements*/ scsi_device, virtual public /*extends*/ win_smart_device { public: win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); virtual bool open(); virtual bool scsi_pass_through(scsi_cmnd_io * iop); private: bool open(int pd_num, int ld_num, int tape_num, int sub_addr); }; ///////////////////////////////////////////////////////////////////////////// win_scsi_device::win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type) : smart_device(intf, dev_name, "scsi", req_type) { } bool win_scsi_device::open() { const char * name = skipdev(get_dev_name()); int len = strlen(name); // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1; if ( sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0)) ) { return open(sdxy_to_phydrive(drive), -1, -1, sub_addr); } // pd,N => Physical drive , RAID port N int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1; if ( sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1 && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) { return open(pd_num, -1, -1, sub_addr); } // [a-zA-Z]: => Physical drive behind logical drive 0-25 int logdrive = drive_letter(name); if (logdrive >= 0) { return open(-1, logdrive, -1, -1); } // n?st => tape drive (same names used in Cygwin's /dev emulation) int tape_num = -1; n1 = -1; if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) { return open(-1, -1, tape_num, -1); } tape_num = -1; n1 = -1; if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) { return open(-1, -1, tape_num, -1); } // tape => tape drive tape_num = -1; n1 = -1; if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) { return open(-1, -1, tape_num, -1); } return set_err(EINVAL); } bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/) { char b[128]; b[sizeof(b) - 1] = '\0'; if (pd_num >= 0) snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num); else if (ld_num >= 0) snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num); else if (tape_num >= 0) snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num); else { set_err(EINVAL); return false; } // Open device HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError()); return false; } set_fh(h); return true; } typedef struct { SCSI_PASS_THROUGH_DIRECT spt; ULONG Filler; UCHAR ucSenseBuf[64]; } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT. // Used if DataTransferLength not supported by *_DIRECT. static long scsi_pass_through_indirect(HANDLE h, SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd) { struct SCSI_PASS_THROUGH_WITH_BUFFERS { SCSI_PASS_THROUGH spt; ULONG Filler; UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)]; UCHAR ucDataBuf[512]; }; SCSI_PASS_THROUGH_WITH_BUFFERS sb; memset(&sb, 0, sizeof(sb)); // DATA_OUT not implemented yet if (!( sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf))) return ERROR_INVALID_PARAMETER; sb.spt.Length = sizeof(sb.spt); sb.spt.CdbLength = sbd->spt.CdbLength; memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb)); sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf); sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); sb.spt.DataIn = sbd->spt.DataIn; sb.spt.DataTransferLength = sbd->spt.DataTransferLength; sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf); sb.spt.TimeOutValue = sbd->spt.TimeOutValue; DWORD num_out; if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH, &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0)) return GetLastError(); sbd->spt.ScsiStatus = sb.spt.ScsiStatus; if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION) memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf)); sbd->spt.DataTransferLength = sb.spt.DataTransferLength; if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0) memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength); return 0; } // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) { int report = scsi_debugmode; // TODO 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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", buff); } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) { set_err(EINVAL, "cmnd_len too large"); return false; } memset(&sb, 0, sizeof(sb)); sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); sb.spt.CdbLength = iop->cmnd_len; memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len); sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf); sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60); bool direct = true; switch (iop->dxfer_dir) { case DXFER_NONE: sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; break; case DXFER_FROM_DEVICE: sb.spt.DataIn = SCSI_IOCTL_DATA_IN; sb.spt.DataTransferLength = iop->dxfer_len; sb.spt.DataBuffer = iop->dxferp; // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte // transfers (needed for SMART STATUS check of JMicron USB bridges) if (sb.spt.DataTransferLength == 1) direct = false; break; case DXFER_TO_DEVICE: sb.spt.DataIn = SCSI_IOCTL_DATA_OUT; sb.spt.DataTransferLength = iop->dxfer_len; sb.spt.DataBuffer = iop->dxferp; break; default: set_err(EINVAL, "bad dxfer_dir"); return false; } long err = 0; if (direct) { DWORD num_out; if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT, &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0)) err = GetLastError(); } else err = scsi_pass_through_indirect(get_fh(), &sb); if (err) return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO), "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld", (direct ? "_DIRECT" : ""), err); iop->scsi_status = sb.spt.ScsiStatus; if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { int slen = sb.ucSenseBuf[7] + 8; if (slen > (int)sizeof(sb.ucSenseBuf)) slen = sizeof(sb.ucSenseBuf); if (slen > (int)iop->max_sense_len) slen = iop->max_sense_len; memcpy(iop->sensep, sb.ucSenseBuf, slen); iop->resp_sense_len = slen; if (report) { if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", slen); dStrHex(iop->sensep, slen , 1); } if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else 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 iop->resp_sense_len = 0; if (iop->dxfer_len > sb.spt.DataTransferLength) iop->resid = iop->dxfer_len - sb.spt.DataTransferLength; else iop->resid = 0; if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } return true; } ///////////////////////////////////////////////////////////////////////////// /// Areca RAID support // TODO: combine with above scsi_pass_through_direct() static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop) { int report = scsi_debugmode; // TODO 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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("%s", buff); } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) { return EINVAL; } memset(&sb, 0, sizeof(sb)); sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); //sb.spt.PathId = 0; sb.spt.TargetId = targetid; //sb.spt.Lun = 0; sb.spt.CdbLength = iop->cmnd_len; memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len); sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf); sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60); bool direct = true; switch (iop->dxfer_dir) { case DXFER_NONE: sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; break; case DXFER_FROM_DEVICE: sb.spt.DataIn = SCSI_IOCTL_DATA_IN; sb.spt.DataTransferLength = iop->dxfer_len; sb.spt.DataBuffer = iop->dxferp; // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte // transfers (needed for SMART STATUS check of JMicron USB bridges) if (sb.spt.DataTransferLength == 1) direct = false; break; case DXFER_TO_DEVICE: sb.spt.DataIn = SCSI_IOCTL_DATA_OUT; sb.spt.DataTransferLength = iop->dxfer_len; sb.spt.DataBuffer = iop->dxferp; break; default: return EINVAL; } long err = 0; if (direct) { DWORD num_out; if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0)) err = GetLastError(); } else err = scsi_pass_through_indirect(fd, &sb); if (err) { return err; } iop->scsi_status = sb.spt.ScsiStatus; if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { int slen = sb.ucSenseBuf[7] + 8; if (slen > (int)sizeof(sb.ucSenseBuf)) slen = sizeof(sb.ucSenseBuf); if (slen > (int)iop->max_sense_len) slen = iop->max_sense_len; memcpy(iop->sensep, sb.ucSenseBuf, slen); iop->resp_sense_len = slen; if (report) { if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", slen); dStrHex(iop->sensep, slen , 1); } if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else 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 iop->resp_sense_len = 0; if (iop->dxfer_len > sb.spt.DataTransferLength) iop->resid = iop->dxfer_len - sb.spt.DataTransferLength; else iop->resid = 0; if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } return 0; } ///////////////////////////////////////////////////////////////////////////// // win_areca_scsi_device // SAS(SCSI) device behind Areca RAID Controller class win_areca_scsi_device : public /*implements*/ areca_scsi_device, public /*extends*/ win_smart_device { public: win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual bool open(); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); private: HANDLE m_mutex; }; ///////////////////////////////////////////////////////////////////////////// win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca") { set_fh(INVALID_HANDLE_VALUE); set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } bool win_areca_scsi_device::open() { HANDLE hFh; if( is_open() ) { return true; } hFh = CreateFile( get_dev_name(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hFh == INVALID_HANDLE_VALUE) { return false; } set_fh(hFh); return true; } smart_device * win_areca_scsi_device::autodetect_open() { return this; } int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop); if ( ioctlreturn || iop->scsi_status ) { ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop); if ( ioctlreturn || iop->scsi_status ) { // errors found return -1; } } return ioctlreturn; } bool win_areca_scsi_device::arcmsr_lock() { #define SYNCOBJNAME "Global\\SynIoctlMutex" int ctlrnum = -1; char mutexstr[64]; if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1) return set_err(EINVAL, "unable to parse device name"); snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum); m_mutex = CreateMutex(NULL, FALSE, mutexstr); if ( m_mutex == NULL ) { return set_err(EIO, "CreateMutex failed"); } // atomic access to driver WaitForSingleObject(m_mutex, INFINITE); return true; } bool win_areca_scsi_device::arcmsr_unlock() { if( m_mutex != NULL) { ReleaseMutex(m_mutex); CloseHandle(m_mutex); } return true; } ///////////////////////////////////////////////////////////////////////////// // win_areca_ata_device // SATA(ATA) device behind Areca RAID Controller class win_areca_ata_device : public /*implements*/ areca_ata_device, public /*extends*/ win_smart_device { public: win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual bool open(); virtual smart_device * autodetect_open(); virtual bool arcmsr_lock(); virtual bool arcmsr_unlock(); virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); private: HANDLE m_mutex; }; ///////////////////////////////////////////////////////////////////////////// win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) : smart_device(intf, dev_name, "areca", "areca") { set_fh(INVALID_HANDLE_VALUE); set_disknum(disknum); set_encnum(encnum); set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } bool win_areca_ata_device::open() { HANDLE hFh; if( is_open() ) { return true; } hFh = CreateFile( get_dev_name(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hFh == INVALID_HANDLE_VALUE) { return false; } set_fh(hFh); return true; } smart_device * win_areca_ata_device::autodetect_open() { // autodetect device type int is_ata = arcmsr_get_dev_type(); if(is_ata < 0) { set_err(EIO); return this; } if(is_ata == 1) { // SATA device return this; } // SAS device smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); close(); delete this; newdev->open(); // TODO: Can possibly pass open fd return newdev.release(); } int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) { int ioctlreturn = 0; ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop); if ( ioctlreturn || iop->scsi_status ) { ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop); if ( ioctlreturn || iop->scsi_status ) { // errors found return -1; } } return ioctlreturn; } bool win_areca_ata_device::arcmsr_lock() { #define SYNCOBJNAME "Global\\SynIoctlMutex" int ctlrnum = -1; char mutexstr[64]; if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1) return set_err(EINVAL, "unable to parse device name"); snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum); m_mutex = CreateMutex(NULL, FALSE, mutexstr); if ( m_mutex == NULL ) { return set_err(EIO, "CreateMutex failed"); } // atomic access to driver WaitForSingleObject(m_mutex, INFINITE); return true; } bool win_areca_ata_device::arcmsr_unlock() { if( m_mutex != NULL) { ReleaseMutex(m_mutex); CloseHandle(m_mutex); } return true; } ///////////////////////////////////////////////////////////////////////////// // win_aacraid_device // PMC aacraid Support class win_aacraid_device :public /*implements*/ scsi_device, public /*extends*/ win_smart_device { public: win_aacraid_device(smart_interface *intf, const char *dev_name,unsigned int ctrnum, unsigned int target, unsigned int lun); virtual ~win_aacraid_device() throw(); virtual bool open(); virtual bool scsi_pass_through(struct scsi_cmnd_io *iop); private: //Device Host number int m_ctrnum; //Channel(Lun) of the device int m_lun; //Id of the device int m_target; }; ///////////////////////////////////////////////////////////////////////////// win_aacraid_device::win_aacraid_device(smart_interface * intf, const char *dev_name, unsigned ctrnum, unsigned target, unsigned lun) : smart_device(intf, dev_name, "aacraid", "aacraid"), m_ctrnum(ctrnum), m_lun(lun), m_target(target) { set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name, m_ctrnum, m_lun, m_target); set_info().dev_type = strprintf("aacraid,%d,%d,%d", m_ctrnum, m_lun, m_target); } win_aacraid_device::~win_aacraid_device() throw() { } bool win_aacraid_device::open() { if (is_open()) return true; HANDLE hFh = CreateFile( get_dev_name(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (hFh == INVALID_HANDLE_VALUE) return set_err(ENODEV, "Open failed, Error=%u", (unsigned)GetLastError()); set_fh(hFh); return true; } bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop) { int report = scsi_debugmode; 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(iop->dxferp, (trunc ? 256 : (int)iop->dxfer_len) , 1); } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout("buff %s\n",buff); } char ioBuffer[1000]; SRB_IO_CONTROL * pSrbIO = (SRB_IO_CONTROL *) ioBuffer; SCSI_REQUEST_BLOCK * pScsiIO = (SCSI_REQUEST_BLOCK *) (ioBuffer + sizeof(SRB_IO_CONTROL)); DWORD scsiRequestBlockSize = sizeof(SCSI_REQUEST_BLOCK); char *pRequestSenseIO = (char *) (ioBuffer + sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize); DWORD dataOffset = (sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize + 7) & 0xfffffff8; char *pDataIO = (char *) (ioBuffer + dataOffset); memset(pScsiIO, 0, scsiRequestBlockSize); pScsiIO->Length = (USHORT) scsiRequestBlockSize; pScsiIO->Function = SRB_FUNCTION_EXECUTE_SCSI; pScsiIO->PathId = 0; pScsiIO->TargetId = m_target; pScsiIO->Lun = m_lun; pScsiIO->CdbLength = (int)iop->cmnd_len; switch(iop->dxfer_dir){ case DXFER_NONE: pScsiIO->SrbFlags = SRB_NoDataXfer; break; case DXFER_FROM_DEVICE: pScsiIO->SrbFlags |= SRB_DataIn; break; case DXFER_TO_DEVICE: pScsiIO->SrbFlags |= SRB_DataOut; break; default: pout("aacraid: bad dxfer_dir\n"); return set_err(EINVAL, "aacraid: bad dxfer_dir\n"); } pScsiIO->DataTransferLength = (ULONG)iop->dxfer_len; pScsiIO->TimeOutValue = iop->timeout; UCHAR *pCdb = (UCHAR *) pScsiIO->Cdb; memcpy(pCdb, iop->cmnd, 16); if (iop->max_sense_len){ memset(pRequestSenseIO, 0, iop->max_sense_len); } if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_OUT){ memcpy(pDataIO, iop->dxferp, iop->dxfer_len); } else if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_IN){ memset(pDataIO, 0, iop->dxfer_len); } DWORD bytesReturned = 0; memset(pSrbIO, 0, sizeof(SRB_IO_CONTROL)); pSrbIO->HeaderLength = sizeof(SRB_IO_CONTROL); memcpy(pSrbIO->Signature, "AACAPI", 7); pSrbIO->ControlCode = ARCIOCTL_SEND_RAW_SRB; pSrbIO->Length = (dataOffset + iop->dxfer_len - sizeof(SRB_IO_CONTROL) + 7) & 0xfffffff8; pSrbIO->Timeout = 3*60; if (!DeviceIoControl( get_fh(), IOCTL_SCSI_MINIPORT, ioBuffer, sizeof(SRB_IO_CONTROL) + pSrbIO->Length, ioBuffer, sizeof(SRB_IO_CONTROL) + pSrbIO->Length, &bytesReturned, NULL) ) { return set_err(EIO, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError()); } iop->scsi_status = pScsiIO->ScsiStatus; if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { int slen = sizeof(pRequestSenseIO) + 8; if (slen > (int)sizeof(pRequestSenseIO)) slen = sizeof(pRequestSenseIO); if (slen > (int)iop->max_sense_len) slen = (int)iop->max_sense_len; memcpy(iop->sensep, pRequestSenseIO, slen); iop->resp_sense_len = slen; if (report) { if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", slen); dStrHex(iop->sensep, slen , 1); } if ((iop->sensep[0] & 0x7f) > 0x71) pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", iop->scsi_status, iop->sensep[1] & 0xf, iop->sensep[2], iop->sensep[3]); else 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 { iop->resp_sense_len = 0; } if (iop->dxfer_dir == DXFER_FROM_DEVICE){ memcpy(iop->dxferp,pDataIO, iop->dxfer_len); } if((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)){ int trunc = (iop->dxfer_len > 256) ? 1 : 0; pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex((const uint8_t *)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1); } return true; } ///////////////////////////////////////////////////////////////////////////// // win_nvme_device class win_nvme_device : public /*implements*/ nvme_device, public /*extends*/ win_smart_device { public: win_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); bool open_scsi(int n); bool probe(); private: int m_scsi_no; }; ///////////////////////////////////////////////////////////////////////////// win_nvme_device::win_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid), m_scsi_no(-1) { } bool win_nvme_device::open_scsi(int n) { // TODO: Use common open function for all devices using "\\.\ScsiN:" char devpath[32]; snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%d:", n); HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); if (nvme_debugmode > 1) pout(" %s: Open failed, Error=%ld\n", devpath, err); if (err == ERROR_FILE_NOT_FOUND) set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) set_err(EACCES, "%s: access denied", devpath); else set_err(EIO, "%s: Error=%ld", devpath, err); return false; } if (nvme_debugmode > 1) pout(" %s: successfully opened\n", devpath); set_fh(h); return true; } // Check if NVMe DeviceIoControl(IOCTL_SCSI_MINIPORT) pass-through works. // On Win10 and later that returns false with an errorNumber of 1 // ("Incorrect function"). Win10 has new pass-through: // DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND). However for commonly // requested NVMe commands like Identify and Get Features Microsoft want // "Protocol specific queries" sent. bool win_nvme_device::probe() { smartmontools::nvme_id_ctrl id_ctrl; nvme_cmd_in in; in.set_data_in(smartmontools::nvme_admin_identify, &id_ctrl, sizeof(id_ctrl)); // in.nsid = 0; in.cdw10 = 0x1; nvme_cmd_out out; bool ok = nvme_pass_through(in, out); if (!ok && nvme_debugmode > 1) pout(" nvme probe failed: %s\n", get_errmsg()); return ok; } bool win_nvme_device::open() { if (m_scsi_no < 0) { // First open -> search of NVMe devices const char * name = skipdev(get_dev_name()); char s[2+1] = ""; int n1 = -1, n2 = -1, len = strlen(name); unsigned no = ~0, nsid = 0xffffffff; sscanf(name, "nvm%2[es]%u%nn%u%n", s, &no, &n1, &nsid, &n2); if (!( (n1 == len || (n2 == len && nsid > 0)) && s[0] == 'e' && (!s[1] || s[1] == 's') )) return set_err(EINVAL); if (!s[1]) { // /dev/nvmeN* -> search for nth NVMe device unsigned nvme_cnt = 0; for (int i = 0; i < 32; i++) { if (!open_scsi(i)) { if (get_errno() == EACCES) return false; continue; } // Done if pass-through works and correct number if (probe()) { if (nvme_cnt == no) { m_scsi_no = i; break; } nvme_cnt++; } close(); } if (!is_open()) return set_err(ENOENT); clear_err(); } else { // /dev/nvmesN* -> use "\\.\ScsiN:" if (!open_scsi(no)) return false; m_scsi_no = no; } if (!get_nsid()) set_nsid(nsid); } else { // Reopen same "\\.\ScsiN:" if (!open_scsi(m_scsi_no)) return false; } return true; } bool win_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { // Create buffer with appropriate size raw_buffer pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer) + in.size); NVME_PASS_THROUGH_IOCTL * pthru = reinterpret_cast(pthru_raw_buf.data()); // Set NVMe command pthru->SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL); memcpy(pthru->SrbIoCtrl.Signature, NVME_SIG_STR, sizeof(NVME_SIG_STR)-1); pthru->SrbIoCtrl.Timeout = 60; pthru->SrbIoCtrl.ControlCode = NVME_PASS_THROUGH_SRB_IO_CODE; pthru->SrbIoCtrl.ReturnCode = 0; pthru->SrbIoCtrl.Length = pthru_raw_buf.size() - sizeof(SRB_IO_CONTROL); pthru->NVMeCmd[0] = in.opcode; pthru->NVMeCmd[1] = in.nsid; pthru->NVMeCmd[10] = in.cdw10; pthru->NVMeCmd[11] = in.cdw11; pthru->NVMeCmd[12] = in.cdw12; pthru->NVMeCmd[13] = in.cdw13; pthru->NVMeCmd[14] = in.cdw14; pthru->NVMeCmd[15] = in.cdw15; pthru->Direction = in.direction(); // pthru->QueueId = 0; // AdminQ // pthru->DataBufferLen = 0; if (in.direction() & nvme_cmd_in::data_out) { pthru->DataBufferLen = in.size; memcpy(pthru->DataBuffer, in.buffer, in.size); } // pthru->MetaDataLen = 0; pthru->ReturnBufferLen = pthru_raw_buf.size(); // Call NVME_PASS_THROUGH DWORD num_out = 0; BOOL ok = DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT, pthru, pthru_raw_buf.size(), pthru, pthru_raw_buf.size(), &num_out, (OVERLAPPED*)0); // Check status unsigned status = pthru->CplEntry[3] >> 17; if (status) return set_nvme_err(out, status); if (!ok) return set_err(EIO, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError()); if (in.direction() & nvme_cmd_in::data_in) memcpy(in.buffer, pthru->DataBuffer, in.size); out.result = pthru->CplEntry[0]; return true; } ///////////////////////////////////////////////////////////////////////////// // win10_nvme_device class win10_nvme_device : public /*implements*/ nvme_device, public /*extends*/ win_smart_device { public: win10_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); private: bool open(int phydrive); }; ///////////////////////////////////////////////////////////////////////////// win10_nvme_device::win10_nvme_device(smart_interface * intf, const char * dev_name, const char * req_type, unsigned nsid) : smart_device(intf, dev_name, "nvme", req_type), nvme_device(nsid) { } bool win10_nvme_device::open() { // TODO: Use common /dev/ parsing functions const char * name = skipdev(get_dev_name()); int len = strlen(name); // sd[a-z]([a-z])? => Physical drive 0-701 char drive[2 + 1] = ""; int n = -1; if (sscanf(name, "sd%2[a-z]%n", drive, &n) == 1 && n == len) return open(sdxy_to_phydrive(drive)); // pdN => Physical drive N int phydrive = -1; n = -1; if (sscanf(name, "pd%d%n", &phydrive, &n) == 1 && phydrive >= 0 && n == len) return open(phydrive); return set_err(EINVAL); } bool win10_nvme_device::open(int phydrive) { // TODO: Use common open function for all devices using "\\.\PhysicalDriveN" char devpath[64]; snprintf(devpath, sizeof(devpath) - 1, "\\\\.\\PhysicalDrive%d", phydrive); // No GENERIC_READ/WRITE access required, this works without admin rights HANDLE h = CreateFileA(devpath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, (HANDLE)0); if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); if (nvme_debugmode > 1) pout(" %s: Open failed, Error=%ld\n", devpath, err); if (err == ERROR_FILE_NOT_FOUND) set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) set_err(EACCES, "%s: access denied", devpath); else set_err(EIO, "%s: Error=%ld", devpath, err); return false; } if (nvme_debugmode > 1) pout(" %s: successfully opened\n", devpath); set_fh(h); // Use broadcast namespace if no NSID specified // TODO: Get NSID of current device if (!get_nsid()) set_nsid(0xffffffff); return true; } struct STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER { struct { // STORAGE_PROPERTY_QUERY without AdditionalsParameters[1] STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; } PropertyQuery; win10::STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecific; BYTE DataBuffer[1]; }; bool win10_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { // Create buffer with appropriate size raw_buffer spsq_raw_buf(offsetof(STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER, DataBuffer) + in.size); STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER * spsq = reinterpret_cast(spsq_raw_buf.data()); // Set NVMe specific STORAGE_PROPERTY_QUERY spsq->PropertyQuery.QueryType = PropertyStandardQuery; spsq->ProtocolSpecific.ProtocolType = win10::ProtocolTypeNvme; switch (in.opcode) { case smartmontools::nvme_admin_identify: if (!in.nsid) // Identify controller spsq->PropertyQuery.PropertyId = win10::StorageAdapterProtocolSpecificProperty; else spsq->PropertyQuery.PropertyId = win10::StorageDeviceProtocolSpecificProperty; spsq->ProtocolSpecific.DataType = win10::NVMeDataTypeIdentify; spsq->ProtocolSpecific.ProtocolDataRequestValue = in.cdw10; break; case smartmontools::nvme_admin_get_log_page: spsq->PropertyQuery.PropertyId = win10::StorageDeviceProtocolSpecificProperty; spsq->ProtocolSpecific.DataType = win10::NVMeDataTypeLogPage; spsq->ProtocolSpecific.ProtocolDataRequestValue = in.cdw10 & 0xff; // LID only ? break; // TODO: nvme_admin_get_features default: return set_err(ENOSYS, "NVMe admin command 0x%02x not supported", in.opcode); } spsq->ProtocolSpecific.ProtocolDataRequestSubValue = in.nsid; // ? spsq->ProtocolSpecific.ProtocolDataOffset = sizeof(spsq->ProtocolSpecific); spsq->ProtocolSpecific.ProtocolDataLength = in.size; if (in.direction() & nvme_cmd_in::data_out) memcpy(spsq->DataBuffer, in.buffer, in.size); if (nvme_debugmode > 1) pout(" [STORAGE_QUERY_PROPERTY: Id=%u, Type=%u, Value=0x%08x, SubVal=0x%08x]\n", (unsigned)spsq->PropertyQuery.PropertyId, (unsigned)spsq->ProtocolSpecific.DataType, (unsigned)spsq->ProtocolSpecific.ProtocolDataRequestValue, (unsigned)spsq->ProtocolSpecific.ProtocolDataRequestSubValue); // Call IOCTL_STORAGE_QUERY_PROPERTY DWORD num_out = 0; long err = 0; if (!DeviceIoControl(get_fh(), IOCTL_STORAGE_QUERY_PROPERTY, spsq, spsq_raw_buf.size(), spsq, spsq_raw_buf.size(), &num_out, (OVERLAPPED*)0)) { err = GetLastError(); } if (nvme_debugmode > 1) pout(" [STORAGE_QUERY_PROPERTY: ReturnData=0x%08x, Reserved[3]={0x%x, 0x%x, 0x%x}]\n", (unsigned)spsq->ProtocolSpecific.FixedProtocolReturnData, (unsigned)spsq->ProtocolSpecific.Reserved[0], (unsigned)spsq->ProtocolSpecific.Reserved[1], (unsigned)spsq->ProtocolSpecific.Reserved[2]); // NVMe status is checked by IOCTL if (err) return set_err(EIO, "IOCTL_STORAGE_QUERY_PROPERTY(NVMe) failed, Error=%ld", err); if (in.direction() & nvme_cmd_in::data_in) memcpy(in.buffer, spsq->DataBuffer, in.size); out.result = spsq->ProtocolSpecific.FixedProtocolReturnData; // Completion DW0 ? return true; } ///////////////////////////////////////////////////////////////////////////// // win_smart_interface // Platform specific interface class win_smart_interface : public /*implements*/ smart_interface { public: virtual std::string get_os_version_str(); virtual std::string get_app_examples(const char * appname); #ifndef __CYGWIN__ virtual int64_t get_timer_usec(); #endif virtual bool disable_system_auto_standby(bool disable); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); virtual std::string get_valid_custom_dev_types_str(); private: smart_device * get_usb_device(const char * name, int phydrive, int logdrive = -1); }; ///////////////////////////////////////////////////////////////////////////// #ifndef _WIN64 // Running on 64-bit Windows as 32-bit app ? static bool is_wow64() { BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) = (BOOL (WINAPI *)(HANDLE, PBOOL)) GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); if (!IsWow64Process_p) return false; BOOL w64 = FALSE; if (!IsWow64Process_p(GetCurrentProcess(), &w64)) return false; return !!w64; } #endif // _WIN64 // Return info string about build host and OS version std::string win_smart_interface::get_os_version_str() { char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13] = SMARTMONTOOLS_BUILD_HOST; if (vstr[1] < '6') vstr[1] = '6'; char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1; const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST); assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr)); // Starting with Windows 8.1, GetVersionEx() does no longer report the // actual OS version. RtlGetVersion() is not affected. LONG /*NTSTATUS*/ (WINAPI /*NTAPI*/ * RtlGetVersion_p)(LPOSVERSIONINFOEXW) = (LONG (WINAPI *)(LPOSVERSIONINFOEXW)) GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion"); OSVERSIONINFOEXW vi; memset(&vi, 0, sizeof(vi)); vi.dwOSVersionInfoSize = sizeof(vi); if (!RtlGetVersion_p || RtlGetVersion_p(&vi)) { if (!GetVersionExW((OSVERSIONINFOW *)&vi)) return vstr; } const char * w = 0; unsigned build = 0; if ( vi.dwPlatformId == VER_PLATFORM_WIN32_NT && vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { switch ( (vi.dwMajorVersion << 4 | vi.dwMinorVersion) << 1 | (vi.wProductType > VER_NT_WORKSTATION ? 1 : 0) ) { case 0x50<<1 : case 0x50<<1 | 1: w = "2000"; break; case 0x51<<1 : w = "xp"; break; case 0x52<<1 : w = "xp64"; break; case 0x52<<1 | 1: w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003" : "2003r2"); break; case 0x60<<1 : w = "vista"; break; case 0x60<<1 | 1: w = "2008"; break; case 0x61<<1 : w = "win7"; break; case 0x61<<1 | 1: w = "2008r2"; break; case 0x62<<1 : w = "win8"; break; case 0x62<<1 | 1: w = "2012"; break; case 0x63<<1 : w = "win8.1"; break; case 0x63<<1 | 1: w = "2012r2"; break; case 0xa0<<1 : switch (vi.dwBuildNumber) { case 10240: w = "w10-1507"; break; case 10586: w = "w10-1511"; break; case 14393: w = "w10-1607"; break; case 15063: w = "w10-1703"; break; case 16299: w = "w10-1709"; break; case 17134: w = "w10-1803"; break; case 17763: w = "w10-1809"; break; default: w = "w10"; build = vi.dwBuildNumber; break; } break; case 0xa0<<1 | 1: switch (vi.dwBuildNumber) { case 14393: w = "2016"; break; case 16299: w = "2016-1709"; break; case 17134: w = "2016-1803"; break; case 17763: w = "2019"; break; default: w = (vi.dwBuildNumber < 17763 ? "2016" : "2019"); build = vi.dwBuildNumber; break; } break; } } const char * w64 = ""; #ifndef _WIN64 if (is_wow64()) w64 = "(64)"; #endif if (!w) snprintf(vptr, vlen, "-%s%u.%u%s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"), (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); else if (build) snprintf(vptr, vlen, "-%s-b%u%s", w, build, w64); else if (vi.wServicePackMinor) snprintf(vptr, vlen, "-%s-sp%u.%u%s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64); else if (vi.wServicePackMajor) snprintf(vptr, vlen, "-%s-sp%u%s", w, vi.wServicePackMajor, w64); else snprintf(vptr, vlen, "-%s%s", w, w64); return vstr; } #ifndef __CYGWIN__ // MSVCRT only provides ftime() which uses GetSystemTime() // This provides only ~15ms resolution by default. // Use QueryPerformanceCounter instead (~300ns). // (Cygwin provides CLOCK_MONOTONIC which has the same effect) int64_t win_smart_interface::get_timer_usec() { static int64_t freq = 0; LARGE_INTEGER t; if (freq == 0) freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1); if (freq <= 0) return smart_interface::get_timer_usec(); if (!QueryPerformanceCounter(&t)) return -1; if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000)) return -1; return (t.QuadPart * 1000000LL) / freq; } #endif // __CYGWIN__ ata_device * win_smart_interface::get_ata_device(const char * name, const char * type) { const char * testname = skipdev(name); if (!strncmp(testname, "csmi", 4)) return new win_csmi_device(this, name, type); if (!strncmp(testname, "tw_cli", 6)) return new win_tw_cli_device(this, name, type); return new win_ata_device(this, name, type); } scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type) { return new win_scsi_device(this, name, type); } nvme_device * win_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) { if (str_starts_with(skipdev(name), "nvme")) return new win_nvme_device(this, name, type, nsid); return new win10_nvme_device(this, name, type, nsid); } smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type) { // Areca? int disknum = -1, n1 = -1, n2 = -1; int encnum = 1; char devpath[32]; if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { if (!(1 <= disknum && disknum <= 128)) { set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); return 0; } if (!(1 <= encnum && encnum <= 8)) { set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } name = skipdev(name); #define ARECA_MAX_CTLR_NUM 16 n1 = -1; int ctlrindex = 0; if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) { /* 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and 2. map arcmsrX into "\\\\.\\scsiX" */ for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) { memset(devpath, 0, sizeof(devpath)); snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx); win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum); if(arcdev->arcmsr_probe()) { if(ctlrindex-- == 0) { return arcdev; } } delete arcdev; } set_err(ENOENT, "No Areca controller found"); } else set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX"); return 0; } // aacraid? unsigned ctrnum, lun, target; n1 = -1; if ( sscanf(type, "aacraid,%u,%u,%u%n", &ctrnum, &lun, &target, &n1) >= 3 && n1 == (int)strlen(type)) { #define aacraid_MAX_CTLR_NUM 16 if (ctrnum >= aacraid_MAX_CTLR_NUM) { set_err(EINVAL, "aacraid: invalid host number %u", ctrnum); return 0; } /* 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and 2. map ARCX into "\\\\.\\scsiX" */ memset(devpath, 0, sizeof(devpath)); unsigned ctlrindex = 0; for (int portNum = 0; portNum < aacraid_MAX_CTLR_NUM; portNum++){ char subKey[63]; snprintf(subKey, sizeof(subKey), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum); HKEY hScsiKey = 0; long regStatus = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hScsiKey); if (regStatus == ERROR_SUCCESS){ char driverName[20]; DWORD driverNameSize = sizeof(driverName); DWORD regType = 0; regStatus = RegQueryValueExA(hScsiKey, "Driver", NULL, ®Type, (LPBYTE) driverName, &driverNameSize); if (regStatus == ERROR_SUCCESS){ if (regType == REG_SZ){ if (stricmp(driverName, "arcsas") == 0){ if(ctrnum == ctlrindex){ snprintf(devpath, sizeof(devpath), "\\\\.\\Scsi%d:", portNum); return get_sat_device("sat,auto", new win_aacraid_device(this, devpath, ctrnum, target, lun)); } ctlrindex++; } } } RegCloseKey(hScsiKey); } } set_err(EINVAL, "aacraid: host %u not found", ctrnum); return 0; } return 0; } std::string win_smart_interface::get_valid_custom_dev_types_str() { return "aacraid,H,L,ID, areca,N[/E]"; } // Return value for device detection functions enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB, DEV_NVME }; // Return true if ATA drive behind a SAT layer static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA * data) { if (!data->desc.VendorIdOffset) return false; if (strcmp(data->raw + data->desc.VendorIdOffset, "ATA ")) return false; return true; } // Return true if Intel ICHxR RAID volume static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data) { if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset)) return false; const char * vendor = data->raw + data->desc.VendorIdOffset; if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5))) return false; if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5)) return false; return true; } // get DEV_* for open handle static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex) { // Get BusType from device descriptor STORAGE_DEVICE_DESCRIPTOR_DATA data; if (storage_query_property_ioctl(hdevice, &data)) return DEV_UNKNOWN; // Newer BusType* values are missing in older includes switch ((int)data.desc.BusType) { case BusTypeAta: case 0x0b: // BusTypeSata // Certain Intel AHCI drivers (C600+/C220+) have broken // IOCTL_ATA_PASS_THROUGH support and a working SAT layer if (is_sat(&data)) return DEV_SAT; if (ata_version_ex) memset(ata_version_ex, 0, sizeof(*ata_version_ex)); return DEV_ATA; case BusTypeScsi: case BusTypeRAID: if (is_sat(&data)) return DEV_SAT; // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_* if (is_intel_raid_volume(&data)) return DEV_SCSI; // LSI/3ware RAID volume: supports SMART_* if (admin && smart_get_version(hdevice, ata_version_ex) >= 0) return DEV_ATA; return DEV_SCSI; case 0x09: // BusTypeiScsi case 0x0a: // BusTypeSas if (is_sat(&data)) return DEV_SAT; return DEV_SCSI; case BusTypeUsb: return DEV_USB; case 0x11: // BusTypeNvme return DEV_NVME; case 0x12: //BusTypeSCM case 0x13: //BusTypeUfs case 0x14: //BusTypeMax, default: return DEV_UNKNOWN; } /*NOTREACHED*/ } // get DEV_* for device path static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0) { bool admin = true; HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (h == INVALID_HANDLE_VALUE) { admin = false; h = CreateFileA(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (h == INVALID_HANDLE_VALUE) return DEV_UNKNOWN; } if (ata_debugmode > 1 || scsi_debugmode > 1) pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :"")); win_dev_type type = get_controller_type(h, admin, ata_version_ex); CloseHandle(h); return type; } // get DEV_* for physical drive number static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex) { char path[30]; snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive); return get_controller_type(path, ata_version_ex); } static win_dev_type get_phy_drive_type(int drive) { return get_phy_drive_type(drive, 0); } // get DEV_* for logical drive number static win_dev_type get_log_drive_type(int drive) { char path[30]; snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive); return get_controller_type(path); } static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdrive) { phydrive = logdrive = -1; name = skipdev(name); if (!strncmp(name, "st", 2)) return DEV_SCSI; if (!strncmp(name, "nst", 3)) return DEV_SCSI; if (!strncmp(name, "tape", 4)) return DEV_SCSI; logdrive = drive_letter(name); if (logdrive >= 0) { win_dev_type type = get_log_drive_type(logdrive); return (type != DEV_UNKNOWN ? type : DEV_SCSI); } char drive[2+1] = ""; if (sscanf(name, "sd%2[a-z]", drive) == 1) { phydrive = sdxy_to_phydrive(drive); return get_phy_drive_type(phydrive); } if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0) return get_phy_drive_type(phydrive); return DEV_UNKNOWN; } smart_device * win_smart_interface::get_usb_device(const char * name, int phydrive, int logdrive /* = -1 */) { // Get USB bridge ID unsigned short vendor_id = 0, product_id = 0; if (!get_usb_id(phydrive, logdrive, vendor_id, product_id)) { set_err(EINVAL, "Unable to read USB device ID"); return 0; } // Get type name for this ID const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id); if (!usbtype) return 0; // Return SAT/USB device for this type return get_scsi_passthrough_device(usbtype, new win_scsi_device(this, name, "")); } smart_device * win_smart_interface::autodetect_smart_device(const char * name) { const char * testname = skipdev(name); if (str_starts_with(testname, "hd")) return new win_ata_device(this, name, ""); if (str_starts_with(testname, "tw_cli")) return new win_tw_cli_device(this, name, ""); if (str_starts_with(testname, "csmi")) return new win_csmi_device(this, name, ""); if (str_starts_with(testname, "nvme")) return new win_nvme_device(this, name, "", 0 /* use default nsid */); int phydrive = -1, logdrive = -1; win_dev_type type = get_dev_type(name, phydrive, logdrive); if (type == DEV_ATA) return new win_ata_device(this, name, ""); if (type == DEV_SCSI) return new win_scsi_device(this, name, ""); if (type == DEV_SAT) return get_sat_device("sat", new win_scsi_device(this, name, "")); if (type == DEV_USB) return get_usb_device(name, phydrive, logdrive); if (type == DEV_NVME) return new win10_nvme_device(this, name, "", 0 /* use default nsid */); return 0; } // Scan for devices bool win_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /* = 0*/) { if (pattern) { set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); return false; } // Check for "[*,]pd" type bool pd = false; char type2[16+1] = ""; if (type) { int nc = -1; if (!strcmp(type, "pd")) { pd = true; type = 0; } else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 && nc == (int)strlen(type)) { pd = true; type = type2; } } // Set valid types bool ata, scsi, sat, usb, csmi, nvme; if (!type) { ata = scsi = usb = sat = csmi = true; #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL nvme = true; #else nvme = false; #endif } else { ata = scsi = usb = sat = csmi = nvme = false; if (!strcmp(type, "ata")) ata = true; else if (!strcmp(type, "scsi")) scsi = true; else if (!strcmp(type, "sat")) sat = true; else if (!strcmp(type, "usb")) usb = true; else if (!strcmp(type, "csmi")) csmi = true; else if (!strcmp(type, "nvme")) nvme = true; else { set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], " "sat[,pd], usb[,pd], csmi, nvme, pd", type); return false; } } char name[32]; if (ata || scsi || sat || usb || nvme) { // Scan up to 128 drives and 2 3ware controllers const int max_raid = 2; bool raid_seen[max_raid] = {false, false}; for (int i = 0; i < 128; i++) { if (pd) snprintf(name, sizeof(name), "/dev/pd%d", i); else if (i + 'a' <= 'z') snprintf(name, sizeof(name), "/dev/sd%c", i + 'a'); else snprintf(name, sizeof(name), "/dev/sd%c%c", i / ('z'-'a'+1) - 1 + 'a', i % ('z'-'a'+1) + 'a'); smart_device * dev = 0; GETVERSIONINPARAMS_EX vers_ex; switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) { case DEV_ATA: // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA if (!ata) continue; // Interpret RAID drive map if present if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) { // Skip if too many controllers or logical drive from this controller already seen if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId])) continue; raid_seen[vers_ex.wControllerId] = true; // Add physical drives int len = strlen(name); for (unsigned int pi = 0; pi < 32; pi++) { if (vers_ex.dwDeviceMapEx & (1U << pi)) { snprintf(name+len, sizeof(name)-1-len, ",%u", pi); devlist.push_back( new win_ata_device(this, name, "ata") ); } } continue; } dev = new win_ata_device(this, name, "ata"); break; case DEV_SCSI: // STORAGE_QUERY_PROPERTY returned SCSI/SAS/... if (!scsi) continue; dev = new win_scsi_device(this, name, "scsi"); break; case DEV_SAT: // STORAGE_QUERY_PROPERTY returned VendorId "ATA " if (!sat) continue; dev = get_sat_device("sat", new win_scsi_device(this, name, "")); break; case DEV_USB: // STORAGE_QUERY_PROPERTY returned USB if (!usb) continue; dev = get_usb_device(name, i); if (!dev) // Unknown or unsupported USB ID, return as SCSI dev = new win_scsi_device(this, name, ""); break; case DEV_NVME: // STORAGE_QUERY_PROPERTY returned NVMe if (!nvme) continue; dev = new win10_nvme_device(this, name, "", 0 /* use default nsid */); break; default: // Unknown type continue; } devlist.push_back(dev); } } if (csmi) { // Scan CSMI devices for (int i = 0; i <= 9; i++) { snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i); win_csmi_device test_dev(this, name, ""); if (!test_dev.open_scsi()) continue; unsigned ports_used = test_dev.get_ports_used(); if (!ports_used) continue; for (int pi = 0; pi < 32; pi++) { if (!(ports_used & (1U << pi))) continue; snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi); devlist.push_back( new win_csmi_device(this, name, "ata") ); } } } if (nvme) { // Scan \\.\Scsi[0-31] for up to 10 NVMe devices int nvme_cnt = 0; for (int i = 0; i < 32; i++) { snprintf(name, sizeof(name)-1, "/dev/nvme%d", i); win_nvme_device test_dev(this, name, "", 0); if (!test_dev.open_scsi(i)) { if (test_dev.get_errno() == EACCES) break; continue; } if (!test_dev.probe()) continue; if (++nvme_cnt >= 10) break; } for (int i = 0; i < nvme_cnt; i++) { snprintf(name, sizeof(name)-1, "/dev/nvme%d", i); devlist.push_back( new win_nvme_device(this, name, "nvme", 0) ); } } return true; } // get examples for smartctl std::string win_smart_interface::get_app_examples(const char * appname) { if (strcmp(appname, "smartctl")) return ""; return "=================================================== SMARTCTL EXAMPLES =====\n\n" " smartctl -a /dev/sda (Prints all SMART information)\n\n" " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n" " (Enables SMART on first disk)\n\n" " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n" " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n" " (Prints Self-Test & Attribute errors)\n" " smartctl -a /dev/sda\n" " (Prints all information for disk on PhysicalDrive 0)\n" " smartctl -a /dev/pd3\n" " (Prints all information for disk on PhysicalDrive 3)\n" " smartctl -a /dev/tape1\n" " (Prints all information for SCSI tape on Tape 1)\n" " smartctl -A /dev/hdb,3\n" " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n" " smartctl -A /dev/tw_cli/c0/p1\n" " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n" " smartctl --all --device=areca,3/1 /dev/arcmsr0\n" " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n" " on 1st Areca RAID controller)\n" "\n" " ATA SMART access methods and ordering may be specified by modifiers\n" " following the device name: /dev/hdX:[saicm], where\n" " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n" " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n" " 'm': IOCTL_SCSI_MINIPORT_*.\n" + strprintf( " The default on this system is /dev/sdX:%s\n", ata_get_def_options() ); } bool win_smart_interface::disable_system_auto_standby(bool disable) { if (disable) { SYSTEM_POWER_STATUS ps; if (!GetSystemPowerStatus(&ps)) return set_err(ENOSYS, "Unknown power status"); if (ps.ACLineStatus != 1) { SetThreadExecutionState(ES_CONTINUOUS); if (ps.ACLineStatus == 0) set_err(EIO, "AC offline"); else set_err(EIO, "Unknown AC line status"); return false; } } if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0))) return set_err(ENOSYS); return true; } } // namespace ///////////////////////////////////////////////////////////////////////////// // Initialize platform interface and register with smi() void smart_interface::init() { { // Remove "." from DLL search path if supported // to prevent DLL preloading attacks BOOL (WINAPI * SetDllDirectoryA_p)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR)) GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA"); if (SetDllDirectoryA_p) SetDllDirectoryA_p(""); } static os_win32::win_smart_interface the_win_interface; smart_interface::set(&the_win_interface); } #ifndef __CYGWIN__ // Get exe directory // (prototype in utiliy.h) std::string get_exe_dir() { char path[MAX_PATH]; // Get path of this exe if (!GetModuleFileNameA(GetModuleHandleA(0), path, sizeof(path))) throw std::runtime_error("GetModuleFileName() failed"); // Replace backslash by slash int sl = -1; for (int i = 0; path[i]; i++) if (path[i] == '\\') { path[i] = '/'; sl = i; } // Remove filename if (sl >= 0) path[sl] = 0; return path; } #endif smartmontools-7.0/README0000644000175000010010000000566713336335341012111 00000000000000========================================================== smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows. ========================================================== $Id: README 4760 2018-08-19 18:45:53Z chrfranke $ == HOME == The home for smartmontools is located at: http://www.smartmontools.org/ 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: https://listi.jpberlin.de/mailman/listinfo/smartmontools-support == COPYING == Copyright (C) 2002-9 Bruce Allen Copyright (C) 2004-18 Christian Franke This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. You should have received a copy of the GNU General Public License (for example COPYING). If not, see . SPDX-License-Identifier: GPL-2.0-or-later == CREDITS == See AUTHORS file. == OVERVIEW == smartmontools contains utilities that control and monitor storage devices using the Self-Monitoring, Analysis and Reporting Technology (SMART) system build into ATA/SATA and SCSI/SAS hard drives and solid-state drives. This is used to check the reliability of the drive and to predict drive failures. == 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/projects/smartmontools/files/ SVN --- svn co http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools smartmontools This will create a subdirectory called smartmontools containing the code. To instead get the 5.38 release: svn co http://svn.code.sf.net/p/smartmontools/code/tags/RELEASE_5_38/sm5 smartmontools You can see what the different tags are by looking at http://sourceforge.net/p/smartmontools/code/HEAD/tree/tags/ == BUILDING/INSTALLING SMARTMONTOOLS == Refer to the "INSTALL" file for detailed installation instructions. == GETTING STARTED == To examine SMART data from a disk, try: smartctl -a /dev/sda 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. smartmontools-7.0/regex/0000755000175000010010000000000013412155412012377 500000000000000smartmontools-7.0/regex/regcomp.c0000644000175000010010000033536513336325511014142 00000000000000/* Extended regular expression matching and search library. Copyright (C) 2002-2018 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, see . */ #ifdef _LIBC # include #endif static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, size_t 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, size_t pat_len); #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); #ifdef RE_ENABLE_I18N static void optimize_utf8 (re_dfa_t *dfa); #endif static reg_errcode_t analyze (regex_t *preg); static reg_errcode_t preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra); static reg_errcode_t postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra); static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node); static reg_errcode_t calc_first (void *extra, bin_tree_t *node); static reg_errcode_t calc_next (void *extra, bin_tree_t *node); static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint); static Idx search_duplicated_node (const re_dfa_t *dfa, Idx 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, Idx node, bool root); static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); static Idx fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax); static int peek_token (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, Idx 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, Idx 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, Idx 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, Idx 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, bool accept_hyphen); static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token); #ifdef RE_ENABLE_I18N static reg_errcode_t build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, Idx *equiv_class_alloc, const unsigned char *name); static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, re_charset_t *mbcset, Idx *char_class_alloc, const char *class_name, reg_syntax_t syntax); #else /* not RE_ENABLE_I18N */ static reg_errcode_t build_equiv_class (bitset_t sbcset, const unsigned char *name); static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, const char *class_name, reg_syntax_t syntax); #endif /* not RE_ENABLE_I18N */ static bin_tree_t *build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, const char *class_name, const char *extra, bool non_match, reg_errcode_t *err); static bin_tree_t *create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, re_token_type_t type); static bin_tree_t *create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, const re_token_t *token); static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); static void free_token (re_token_t *node); static reg_errcode_t free_tree (void *extra, bin_tree_t *node); static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); /* 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? */ static const char __re_error_msgid[] = { #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 */ }; static const size_t __re_error_msgid_idx[] = { 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 (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, unless RE_NO_SUB is set. */ bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); /* 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 (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 (struct re_pattern_buffer *bufp) { re_dfa_t *dfa = 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 __attribute__ ((always_inline)) re_set_fastmap (char *fastmap, bool 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 (regex_t *bufp, const re_dfastate_t *init_state, char *fastmap) { re_dfa_t *dfa = bufp->buffer; Idx node_cnt; bool icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) { Idx 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); #ifdef RE_ENABLE_I18N if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) { unsigned char buf[MB_LEN_MAX]; unsigned char *p; wchar_t wc; mbstate_t state; p = buf; *p++ = dfa->nodes[node].opr.c; while (++node < dfa->nodes_len && dfa->nodes[node].type == CHARACTER && dfa->nodes[node].mb_partial) *p++ = dfa->nodes[node].opr.c; memset (&state, '\0', sizeof (state)); if (__mbrtowc (&wc, (const char *) buf, p - buf, &state) == p - buf && (__wcrtomb ((char *) buf, __towlower (wc), &state) != (size_t) -1)) re_set_fastmap (fastmap, false, buf[0]); } #endif } else if (type == SIMPLE_BRACKET) { int i, ch; for (i = 0, ch = 0; i < BITSET_WORDS; ++i) { int j; bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) if (w & ((bitset_word_t) 1 << j)) re_set_fastmap (fastmap, icase, ch); } } #ifdef RE_ENABLE_I18N else if (type == COMPLEX_BRACKET) { re_charset_t *cset = dfa->nodes[node].opr.mbcset; Idx i; # ifdef _LIBC /* See if we have to try all bytes which start multiple 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' (and it is caught by SIMPLE_BRACKET). */ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0 && (cset->ncoll_syms || cset->nranges)) { const int32_t *table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); for (i = 0; i < SBC_MAX; ++i) if (table[i] < 0) re_set_fastmap (fastmap, icase, i); } # endif /* _LIBC */ /* See if we have to start the match at all multibyte characters, i.e. where we would not find an invalid sequence. This only applies to multibyte character sets; for single byte character sets, the SIMPLE_BRACKET again suffices. */ if (dfa->mb_cur_max > 1 && (cset->nchar_classes || cset->non_match || cset->nranges # ifdef _LIBC || cset->nequiv_classes # endif /* _LIBC */ )) { unsigned char c = 0; do { mbstate_t mbs; memset (&mbs, 0, sizeof (mbs)); if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2) re_set_fastmap (fastmap, false, (int) c); } while (++c != 0); } else { /* ... Else catch all bytes which can start the mbchars. */ for (i = 0; i < cset->nmbchars; ++i) { char buf[256]; mbstate_t state; memset (&state, '\0', sizeof (state)); if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) re_set_fastmap (fastmap, icase, *(unsigned char *) buf); if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) { if (__wcrtomb (buf, __towlower (cset->mbchars[i]), &state) != (size_t) -1) re_set_fastmap (fastmap, false, *(unsigned char *) buf); } } } } #endif /* RE_ENABLE_I18N */ else if (type == OP_PERIOD #ifdef RE_ENABLE_I18N || type == OP_UTF8_PERIOD #endif /* RE_ENABLE_I18N */ || type == END_OF_RE) { 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 (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 never 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 libc_hidden_def (__regcomp) 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 (int errcode, const regex_t *_Restrict_ preg, char *_Restrict_ 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)) { size_t cpy_size = msg_size; if (BE (msg_size > errbuf_size, 0)) { cpy_size = errbuf_size - 1; errbuf[cpy_size] = '\0'; } memcpy (errbuf, msg, cpy_size); } return msg_size; } #ifdef _LIBC weak_alias (__regerror, regerror) #endif #ifdef RE_ENABLE_I18N /* This static array is used for the map to single-byte characters when UTF-8 is used. Otherwise we would allocate memory just to initialize it the same all the time. UTF-8 is the preferred encoding so this is a worthwhile optimization. */ static const bitset_t utf8_sb_map = { /* Set the first 128 bits. */ # if defined __GNUC__ && !defined __STRICT_ANSI__ [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX # else # if 4 * BITSET_WORD_BITS < ASCII_CHARS # error "bitset_word_t is narrower than 32 bits" # elif 3 * BITSET_WORD_BITS < ASCII_CHARS BITSET_WORD_MAX, BITSET_WORD_MAX, BITSET_WORD_MAX, # elif 2 * BITSET_WORD_BITS < ASCII_CHARS BITSET_WORD_MAX, BITSET_WORD_MAX, # elif 1 * BITSET_WORD_BITS < ASCII_CHARS BITSET_WORD_MAX, # endif (BITSET_WORD_MAX >> (SBC_MAX % BITSET_WORD_BITS == 0 ? 0 : BITSET_WORD_BITS - SBC_MAX % BITSET_WORD_BITS)) # endif }; #endif static void free_dfa_content (re_dfa_t *dfa) { Idx i, j; if (dfa->nodes) for (i = 0; i < dfa->nodes_len; ++i) free_token (dfa->nodes + i); 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); if (dfa->state_table) 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); #ifdef RE_ENABLE_I18N if (dfa->sb_char != utf8_sb_map) re_free (dfa->sb_char); #endif re_free (dfa->subexp_map); #ifdef DEBUG re_free (dfa->re_str); #endif re_free (dfa); } /* Free dynamically allocated space used by PREG. */ void regfree (regex_t *preg) { re_dfa_t *dfa = preg->buffer; if (BE (dfa != NULL, 1)) { lock_fini (dfa->lock); free_dfa_content (dfa); } preg->buffer = NULL; preg->allocated = 0; re_free (preg->fastmap); preg->fastmap = NULL; re_free (preg->translate); preg->translate = NULL; } #ifdef _LIBC libc_hidden_def (__regfree) 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 (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 = re_malloc (char, 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 (regex_t *preg, const char * pattern, size_t 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 = preg->buffer; if (BE (preg->allocated < sizeof (re_dfa_t), 0)) { /* 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 = dfa; } preg->used = sizeof (re_dfa_t); err = init_dfa (dfa, length); if (BE (err == REG_NOERROR && lock_init (dfa->lock) != 0, 0)) err = REG_ESPACE; if (BE (err != REG_NOERROR, 0)) { free_dfa_content (dfa); preg->buffer = NULL; preg->allocated = 0; return err; } #ifdef DEBUG /* Note: length+1 will not overflow since it is checked in init_dfa. */ 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) != 0, dfa); if (BE (err != REG_NOERROR, 0)) { re_compile_internal_free_return: free_workarea_compile (preg); re_string_destruct (®exp); lock_fini (dfa->lock); free_dfa_content (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 create the nfa. */ err = analyze (preg); if (BE (err != REG_NOERROR, 0)) goto re_compile_internal_free_return; #ifdef RE_ENABLE_I18N /* If possible, do searching in single byte encoding to speed things up. */ if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) optimize_utf8 (dfa); #endif /* 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)) { lock_fini (dfa->lock); 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 (re_dfa_t *dfa, size_t pat_len) { __re_size_t table_size; #if !defined(_LIBC) && !defined(_REGEX_STANDALONE) const char *codeset_name; #endif #ifdef RE_ENABLE_I18N size_t max_i18n_object_size = MAX (sizeof (wchar_t), sizeof (wctype_t)); #else size_t max_i18n_object_size = 0; #endif size_t max_object_size = MAX (sizeof (struct re_state_table_entry), MAX (sizeof (re_token_t), MAX (sizeof (re_node_set), MAX (sizeof (regmatch_t), max_i18n_object_size)))); memset (dfa, '\0', sizeof (re_dfa_t)); /* Force allocation of str_tree_storage the first time. */ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; /* Avoid overflows. The extra "/ 2" is for the table_size doubling calculation below, and for similar doubling calculations elsewhere. And it's <= rather than <, because some of the doubling calculations add 1 afterwards. */ if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) / 2 <= pat_len, 0)) return REG_ESPACE; dfa->nodes_alloc = pat_len + 1; dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); /* table_size = 2 ^ ceil(log pat_len) */ for (table_size = 1; ; 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->mb_cur_max = MB_CUR_MAX; #ifdef _LIBC if (dfa->mb_cur_max == 6 && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) dfa->is_utf8 = 1; dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) != 0); #else # if !defined(_REGEX_STANDALONE) codeset_name = nl_langinfo (CODESET); if ((codeset_name[0] == 'U' || codeset_name[0] == 'u') && (codeset_name[1] == 'T' || codeset_name[1] == 't') && (codeset_name[2] == 'F' || codeset_name[2] == 'f') && strcmp (codeset_name + 3 + (codeset_name[3] == '-'), "8") == 0) dfa->is_utf8 = 1; # endif /* We check exhaustively in the loop below if this charset is a superset of ASCII. */ dfa->map_notascii = 0; #endif #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { if (dfa->is_utf8) dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; else { int i, j, ch; dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); if (BE (dfa->sb_char == NULL, 0)) return REG_ESPACE; /* Set the bits corresponding to single byte chars. */ for (i = 0, ch = 0; i < BITSET_WORDS; ++i) for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) { wint_t wch = __btowc (ch); if (wch != WEOF) dfa->sb_char[i] |= (bitset_word_t) 1 << j; # ifndef _LIBC if (isascii (ch) && wch != ch) dfa->map_notascii = 1; # endif } } } #endif if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) 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 void init_word_char (re_dfa_t *dfa) { int i = 0; int j; int ch = 0; dfa->word_ops_used = 1; if (BE (dfa->map_notascii == 0, 1)) { /* Avoid uint32_t and uint64_t as some non-GCC platforms lack them, an issue when this code is used in Gnulib. */ bitset_word_t bits0 = 0x00000000; bitset_word_t bits1 = 0x03ff0000; bitset_word_t bits2 = 0x87fffffe; bitset_word_t bits3 = 0x07fffffe; if (BITSET_WORD_BITS == 64) { /* Pacify gcc -Woverflow on 32-bit platformns. */ dfa->word_char[0] = bits1 << 31 << 1 | bits0; dfa->word_char[1] = bits3 << 31 << 1 | bits2; i = 2; } else if (BITSET_WORD_BITS == 32) { dfa->word_char[0] = bits0; dfa->word_char[1] = bits1; dfa->word_char[2] = bits2; dfa->word_char[3] = bits3; i = 4; } else goto general_case; ch = 128; if (BE (dfa->is_utf8, 1)) { memset (&dfa->word_char[i], '\0', (SBC_MAX - ch) / 8); return; } } general_case: for (; i < BITSET_WORDS; ++i) for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) if (isalnum (ch) || ch == '_') dfa->word_char[i] |= (bitset_word_t) 1 << j; } /* Free the work area which are only used while compiling. */ static void free_workarea_compile (regex_t *preg) { re_dfa_t *dfa = preg->buffer; bin_tree_storage_t *storage, *next; for (storage = dfa->str_tree_storage; storage; storage = next) { next = storage->next; re_free (storage); } dfa->str_tree_storage = NULL; dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; 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 (re_dfa_t *dfa) { Idx 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->node_idx; 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) { Idx node_idx = init_nodes.elems[i]; re_token_type_t type = dfa->nodes[node_idx].type; Idx 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 == dfa->nodes[node_idx].opr.idx) break; } if (clexp_idx == init_nodes.nelem) continue; if (type == OP_BACK_REF) { Idx dest_idx = dfa->edests[node_idx].elems[0]; if (!re_node_set_contains (&init_nodes, dest_idx)) { reg_errcode_t merge_err = re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); if (merge_err != REG_NOERROR) return merge_err; 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; } #ifdef RE_ENABLE_I18N /* If it is possible to do searching in single byte encoding instead of UTF-8 to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change DFA nodes where needed. */ static void optimize_utf8 (re_dfa_t *dfa) { Idx node; int i; bool mb_chars = false; bool has_period = false; for (node = 0; node < dfa->nodes_len; ++node) switch (dfa->nodes[node].type) { case CHARACTER: if (dfa->nodes[node].opr.c >= ASCII_CHARS) mb_chars = true; break; case ANCHOR: switch (dfa->nodes[node].opr.ctx_type) { case LINE_FIRST: case LINE_LAST: case BUF_FIRST: case BUF_LAST: break; default: /* Word anchors etc. cannot be handled. It's okay to test opr.ctx_type since constraints (for all DFA nodes) are created by ORing one or more opr.ctx_type values. */ return; } break; case OP_PERIOD: has_period = true; break; case OP_BACK_REF: case OP_ALT: case END_OF_RE: case OP_DUP_ASTERISK: case OP_OPEN_SUBEXP: case OP_CLOSE_SUBEXP: break; case COMPLEX_BRACKET: return; case SIMPLE_BRACKET: /* Just double check. */ { int rshift = (ASCII_CHARS % BITSET_WORD_BITS == 0 ? 0 : BITSET_WORD_BITS - ASCII_CHARS % BITSET_WORD_BITS); for (i = ASCII_CHARS / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) { if (dfa->nodes[node].opr.sbcset[i] >> rshift != 0) return; rshift = 0; } } break; default: abort (); } if (mb_chars || has_period) for (node = 0; node < dfa->nodes_len; ++node) { if (dfa->nodes[node].type == CHARACTER && dfa->nodes[node].opr.c >= ASCII_CHARS) dfa->nodes[node].mb_partial = 0; else if (dfa->nodes[node].type == OP_PERIOD) dfa->nodes[node].type = OP_UTF8_PERIOD; } /* The search can be in single byte locale. */ dfa->mb_cur_max = 1; dfa->is_utf8 = 0; dfa->has_mb_node = dfa->nbackref > 0 || has_period; } #endif /* Analyze the structure tree, and calculate "first", "next", "edest", "eclosure", and "inveclosure". */ static reg_errcode_t analyze (regex_t *preg) { re_dfa_t *dfa = preg->buffer; reg_errcode_t ret; /* Allocate arrays. */ dfa->nexts = re_malloc (Idx, dfa->nodes_alloc); dfa->org_indices = re_malloc (Idx, dfa->nodes_alloc); dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL || dfa->eclosures == NULL, 0)) return REG_ESPACE; dfa->subexp_map = re_malloc (Idx, preg->re_nsub); if (dfa->subexp_map != NULL) { Idx i; for (i = 0; i < preg->re_nsub; i++) dfa->subexp_map[i] = i; preorder (dfa->str_tree, optimize_subexps, dfa); for (i = 0; i < preg->re_nsub; i++) if (dfa->subexp_map[i] != i) break; if (i == preg->re_nsub) { re_free (dfa->subexp_map); dfa->subexp_map = NULL; } } ret = postorder (dfa->str_tree, lower_subexps, preg); if (BE (ret != REG_NOERROR, 0)) return ret; ret = postorder (dfa->str_tree, calc_first, dfa); if (BE (ret != REG_NOERROR, 0)) return ret; preorder (dfa->str_tree, calc_next, dfa); ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); if (BE (ret != REG_NOERROR, 0)) return ret; ret = calc_eclosure (dfa); if (BE (ret != REG_NOERROR, 0)) return ret; /* We only need this during the prune_impossible_nodes pass in regexec.c; skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) || dfa->nbackref) { dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); if (BE (dfa->inveclosures == NULL, 0)) return REG_ESPACE; ret = calc_inveclosure (dfa); } return ret; } /* Our parse trees are very unbalanced, so we cannot use a stack to implement parse tree visits. Instead, we use parent pointers and some hairy code in these two functions. */ static reg_errcode_t postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra) { bin_tree_t *node, *prev; for (node = root; ; ) { /* Descend down the tree, preferably to the left (or to the right if that's the only child). */ while (node->left || node->right) if (node->left) node = node->left; else node = node->right; do { reg_errcode_t err = fn (extra, node); if (BE (err != REG_NOERROR, 0)) return err; if (node->parent == NULL) return REG_NOERROR; prev = node; node = node->parent; } /* Go up while we have a node that is reached from the right. */ while (node->right == prev || node->right == NULL); node = node->right; } } static reg_errcode_t preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), void *extra) { bin_tree_t *node; for (node = root; ; ) { reg_errcode_t err = fn (extra, node); if (BE (err != REG_NOERROR, 0)) return err; /* Go to the left node, or up and to the right. */ if (node->left) node = node->left; else { bin_tree_t *prev = NULL; while (node->right == prev || node->right == NULL) { prev = node; node = node->parent; if (!node) return REG_NOERROR; } node = node->right; } } } /* Optimization pass: if a SUBEXP is entirely contained, strip it and tell re_search_internal to map the inner one's opr.idx to this one's. Adjust backreferences as well. Requires a preorder visit. */ static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; if (node->token.type == OP_BACK_REF && dfa->subexp_map) { int idx = node->token.opr.idx; node->token.opr.idx = dfa->subexp_map[idx]; dfa->used_bkref_map |= 1 << node->token.opr.idx; } else if (node->token.type == SUBEXP && node->left && node->left->token.type == SUBEXP) { Idx other_idx = node->left->token.opr.idx; node->left = node->left->left; if (node->left) node->left->parent = node; dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; if (other_idx < BITSET_WORD_BITS) dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); } return REG_NOERROR; } /* Lowering pass: Turn each SUBEXP node into the appropriate concatenation of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node) { regex_t *preg = (regex_t *) extra; reg_errcode_t err = REG_NOERROR; if (node->left && node->left->token.type == SUBEXP) { node->left = lower_subexp (&err, preg, node->left); if (node->left) node->left->parent = node; } if (node->right && node->right->token.type == SUBEXP) { node->right = lower_subexp (&err, preg, node->right); if (node->right) node->right->parent = node; } return err; } static bin_tree_t * lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) { re_dfa_t *dfa = preg->buffer; bin_tree_t *body = node->left; bin_tree_t *op, *cls, *tree1, *tree; if (preg->no_sub /* We do not optimize empty subexpressions, because otherwise we may have bad CONCAT nodes with NULL children. This is obviously not very common, so we do not lose much. An example that triggers this case is the sed "script" /\(\)/x. */ && node->left != NULL && (node->token.opr.idx >= BITSET_WORD_BITS || !(dfa->used_bkref_map & ((bitset_word_t) 1 << node->token.opr.idx)))) return node->left; /* Convert the SUBEXP node to the concatenation of an OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; tree = create_tree (dfa, op, tree1, CONCAT); if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) { *err = REG_ESPACE; return NULL; } op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; return tree; } /* Pass 1 in building the NFA: compute FIRST and create unlinked automaton nodes. Requires a postorder visit. */ static reg_errcode_t calc_first (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; if (node->token.type == CONCAT) { node->first = node->left->first; node->node_idx = node->left->node_idx; } else { node->first = node; node->node_idx = re_dfa_add_node (dfa, node->token); if (BE (node->node_idx == -1, 0)) return REG_ESPACE; if (node->token.type == ANCHOR) dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type; } return REG_NOERROR; } /* Pass 2: compute NEXT on the tree. Preorder visit. */ static reg_errcode_t calc_next (void *extra, bin_tree_t *node) { switch (node->token.type) { case OP_DUP_ASTERISK: node->left->next = node; break; case CONCAT: node->left->next = node->right->first; node->right->next = node->next; break; default: if (node->left) node->left->next = node->next; if (node->right) node->right->next = node->next; break; } return REG_NOERROR; } /* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node) { re_dfa_t *dfa = (re_dfa_t *) extra; Idx idx = node->node_idx; reg_errcode_t err = REG_NOERROR; switch (node->token.type) { case CONCAT: break; case END_OF_RE: assert (node->next == NULL); break; case OP_DUP_ASTERISK: case OP_ALT: { Idx left, right; dfa->has_plural_match = 1; if (node->left != NULL) left = node->left->first->node_idx; else left = node->next->node_idx; if (node->right != NULL) right = node->right->first->node_idx; else right = node->next->node_idx; assert (left > -1); assert (right > -1); err = re_node_set_init_2 (dfa->edests + idx, left, right); } break; case ANCHOR: case OP_OPEN_SUBEXP: case OP_CLOSE_SUBEXP: err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); break; case OP_BACK_REF: dfa->nexts[idx] = node->next->node_idx; if (node->token.type == OP_BACK_REF) err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); break; default: assert (!IS_EPSILON_NODE (node->token.type)); dfa->nexts[idx] = node->next->node_idx; break; } return err; } /* 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 (re_dfa_t *dfa, Idx top_org_node, Idx top_clone_node, Idx root_node, unsigned int init_constraint) { Idx org_node, clone_node; bool ok; unsigned int constraint = init_constraint; for (org_node = top_org_node, clone_node = top_clone_node;;) { Idx 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); clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; dfa->nexts[clone_node] = dfa->nexts[org_node]; ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (! ok, 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 the node is root_node itself, it means the epsilon closure has a loop. Then tie it to the destination of the root_node. */ if (org_node == root_node && clone_node != org_node) { ok = re_node_set_insert (dfa->edests + clone_node, org_dest); if (BE (! ok, 0)) return REG_ESPACE; break; } /* In case the node has another constraint, append it. */ constraint |= dfa->nodes[org_node].constraint; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (! ok, 0)) return REG_ESPACE; } else /* dfa->edests[org_node].nelem == 2 */ { /* In case of the node can epsilon-transit, and it has two destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ 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 is no such duplicated node, create a new one. */ reg_errcode_t err; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (! ok, 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 is a duplicated node which satisfies the constraint, use it to avoid infinite loop. */ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (! ok, 0)) return REG_ESPACE; } org_dest = dfa->edests[org_node].elems[1]; clone_dest = duplicate_node (dfa, org_dest, constraint); if (BE (clone_dest == -1, 0)) return REG_ESPACE; ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); if (BE (! ok, 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 Idx search_duplicated_node (const re_dfa_t *dfa, Idx org_node, unsigned int constraint) { Idx 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. Return the index of the new node, or -1 if insufficient storage is available. */ static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint) { Idx dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); if (BE (dup_idx != -1, 1)) { dfa->nodes[dup_idx].constraint = constraint; dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint; dfa->nodes[dup_idx].duplicated = 1; /* Store the index of the original node. */ dfa->org_indices[dup_idx] = org_idx; } return dup_idx; } static reg_errcode_t calc_inveclosure (re_dfa_t *dfa) { Idx src, idx; bool ok; for (idx = 0; idx < dfa->nodes_len; ++idx) re_node_set_init_empty (dfa->inveclosures + idx); for (src = 0; src < dfa->nodes_len; ++src) { Idx *elems = dfa->eclosures[src].elems; for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) { ok = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); if (BE (! ok, 0)) return REG_ESPACE; } } return REG_NOERROR; } /* Calculate "eclosure" for all the node in DFA. */ static reg_errcode_t calc_eclosure (re_dfa_t *dfa) { Idx node_idx; bool incomplete; #ifdef DEBUG assert (dfa->nodes_len > 0); #endif incomplete = false; /* 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 = false; 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, true); if (BE (err != REG_NOERROR, 0)) return err; if (dfa->eclosures[node_idx].nelem == 0) { incomplete = true; re_node_set_free (&eclosure_elem); } } return REG_NOERROR; } /* Calculate epsilon closure of NODE. */ static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) { reg_errcode_t err; Idx i; re_node_set eclosure; bool ok; bool incomplete = false; 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; /* If the current node has constraints, duplicate all nodes since they must inherit the constraints. */ if (dfa->nodes[node].constraint && dfa->edests[node].nelem && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) { err = duplicate_node_closure (dfa, node, node, node, dfa->nodes[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; Idx 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 = true; 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, false); if (BE (err != REG_NOERROR, 0)) return err; } else eclosure_elem = dfa->eclosures[edest]; /* Merge the epsilon closure of 'edest'. */ err = re_node_set_merge (&eclosure, &eclosure_elem); if (BE (err != REG_NOERROR, 0)) return err; /* If the epsilon closure of 'edest' is incomplete, the epsilon closure of this node is also incomplete. */ if (dfa->eclosures[edest].nelem == 0) { incomplete = true; re_node_set_free (&eclosure_elem); } } /* An epsilon closure includes itself. */ ok = re_node_set_insert (&eclosure, node); if (BE (! ok, 0)) return REG_ESPACE; 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 void fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) { re_string_skip_bytes (input, peek_token (result, input, syntax)); } /* 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 (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; token->word_char = 0; #ifdef RE_ENABLE_I18N token->mb_partial = 0; if (input->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; #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input) + 1); token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; } else #endif token->word_char = IS_WORD_CHAR (c2) != 0; 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 - '1'; } break; case '<': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_FIRST; } break; case '>': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_LAST; } break; case 'b': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = WORD_DELIM; } break; case 'B': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = NOT_WORD_DELIM; } 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 's': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_SPACE; break; case 'S': if (!(syntax & RE_NO_GNU_OPS)) token->type = OP_NOTSPACE; break; case '`': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = BUF_FIRST; } break; case '\'': if (!(syntax & RE_NO_GNU_OPS)) { token->type = ANCHOR; token->opr.ctx_type = 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; #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; } else #endif token->word_char = IS_WORD_CHAR (token->opr.c); 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_CARET_ANCHORS_HERE)) && re_string_cur_idx (input) != 0) { char prev = re_string_peek_byte (input, -1); if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') break; } token->type = ANCHOR; token->opr.ctx_type = 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.ctx_type = 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 (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 (input->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) && re_string_cur_idx (input) + 1 < re_string_length (input)) { /* 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; if (re_string_cur_idx (input) + 1 < re_string_length (input)) c2 = re_string_peek_byte (input, 1); else c2 = 0; 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; } FALLTHROUGH; 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 occurs, 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 (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, reg_errcode_t *err) { re_dfa_t *dfa = preg->buffer; bin_tree_t *tree, *eor, *root; re_token_t current_token; dfa->syntax = syntax; fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; eor = create_tree (dfa, NULL, NULL, END_OF_RE); if (tree != NULL) root = create_tree (dfa, tree, eor, CONCAT); else root = eor; if (BE (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 (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, Idx nest, reg_errcode_t *err) { re_dfa_t *dfa = preg->buffer; bin_tree_t *tree, *branch = NULL; bitset_word_t initial_bkref_map = dfa->completed_bkref_map; tree = parse_branch (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; while (token->type == OP_ALT) { fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); if (token->type != OP_ALT && token->type != END_OF_RE && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) { bitset_word_t accumulated_bkref_map = dfa->completed_bkref_map; dfa->completed_bkref_map = initial_bkref_map; branch = parse_branch (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && branch == NULL, 0)) { if (tree != NULL) postorder (tree, free_tree, NULL); return NULL; } dfa->completed_bkref_map |= accumulated_bkref_map; } else branch = NULL; tree = create_tree (dfa, tree, branch, OP_ALT); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } return tree; } /* This function build the following tree, from regular expression : CAT / \ / \ CAT means concatenation. */ static bin_tree_t * parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, Idx nest, reg_errcode_t *err) { bin_tree_t *tree, *expr; re_dfa_t *dfa = preg->buffer; 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)) { expr = parse_expression (regexp, preg, token, syntax, nest, err); if (BE (*err != REG_NOERROR && expr == NULL, 0)) { if (tree != NULL) postorder (tree, free_tree, NULL); return NULL; } if (tree != NULL && expr != NULL) { bin_tree_t *newtree = create_tree (dfa, tree, expr, CONCAT); if (newtree == NULL) { postorder (expr, free_tree, NULL); postorder (tree, free_tree, NULL); *err = REG_ESPACE; return NULL; } tree = newtree; } else if (tree == NULL) tree = expr; /* Otherwise expr == 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 (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, Idx nest, reg_errcode_t *err) { re_dfa_t *dfa = preg->buffer; bin_tree_t *tree; switch (token->type) { case CHARACTER: tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { while (!re_string_eoi (regexp) && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) { bin_tree_t *mbc_remain; fetch_token (token, regexp, syntax); mbc_remain = create_token_tree (dfa, NULL, NULL, token); tree = create_tree (dfa, tree, mbc_remain, CONCAT); if (BE (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 (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) { *err = REG_ESUBREG; return NULL; } dfa->used_bkref_map |= 1 << token->opr.idx; tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } ++dfa->nbackref; dfa->has_mb_node = 1; break; case OP_OPEN_DUP_NUM: if (syntax & RE_CONTEXT_INVALID_DUP) { *err = REG_BADRPT; return NULL; } FALLTHROUGH; case OP_DUP_ASTERISK: case OP_DUP_PLUS: case OP_DUP_QUESTION: if (syntax & RE_CONTEXT_INVALID_OPS) { *err = REG_BADRPT; return NULL; } else if (syntax & RE_CONTEXT_INDEP_OPS) { fetch_token (token, regexp, syntax); return parse_expression (regexp, preg, token, syntax, nest, err); } FALLTHROUGH; case OP_CLOSE_SUBEXP: if ((token->type == OP_CLOSE_SUBEXP) && !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) { *err = REG_ERPAREN; return NULL; } FALLTHROUGH; case OP_CLOSE_DUP_NUM: /* We treat it as a normal character. */ /* Then we can these characters as normal characters. */ token->type = CHARACTER; /* mb_partial and word_char bits should be initialized already by peek_token. */ tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } break; case ANCHOR: if ((token->opr.ctx_type & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) && dfa->word_ops_used == 0) init_word_char (dfa); if (token->opr.ctx_type == WORD_DELIM || token->opr.ctx_type == NOT_WORD_DELIM) { bin_tree_t *tree_first, *tree_last; if (token->opr.ctx_type == WORD_DELIM) { token->opr.ctx_type = WORD_FIRST; tree_first = create_token_tree (dfa, NULL, NULL, token); token->opr.ctx_type = WORD_LAST; } else { token->opr.ctx_type = INSIDE_WORD; tree_first = create_token_tree (dfa, NULL, NULL, token); token->opr.ctx_type = INSIDE_NOTWORD; } tree_last = create_token_tree (dfa, NULL, NULL, token); tree = create_tree (dfa, tree_first, tree_last, OP_ALT); if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } } else { tree = create_token_tree (dfa, NULL, NULL, token); if (BE (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 "". */ fetch_token (token, regexp, syntax); return tree; case OP_PERIOD: tree = create_token_tree (dfa, NULL, NULL, token); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } if (dfa->mb_cur_max > 1) dfa->has_mb_node = 1; break; case OP_WORD: case OP_NOTWORD: tree = build_charclass_op (dfa, regexp->trans, "alnum", "_", token->type == OP_NOTWORD, err); if (BE (*err != REG_NOERROR && tree == NULL, 0)) return NULL; break; case OP_SPACE: case OP_NOTSPACE: tree = build_charclass_op (dfa, regexp->trans, "space", "", token->type == OP_NOTSPACE, 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; } fetch_token (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) { bin_tree_t *dup_tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); if (BE (*err != REG_NOERROR && dup_tree == NULL, 0)) { if (tree != NULL) postorder (tree, free_tree, NULL); return NULL; } tree = dup_tree; /* In BRE consecutive duplications are not allowed. */ if ((syntax & RE_CONTEXT_INVALID_DUP) && (token->type == OP_DUP_ASTERISK || token->type == OP_OPEN_DUP_NUM)) { if (tree != NULL) postorder (tree, free_tree, NULL); *err = REG_BADRPT; return NULL; } } return tree; } /* This function build the following tree, from regular expression (): SUBEXP | */ static bin_tree_t * parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, reg_syntax_t syntax, Idx nest, reg_errcode_t *err) { re_dfa_t *dfa = preg->buffer; bin_tree_t *tree; size_t cur_nsub; cur_nsub = preg->re_nsub++; fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); /* 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 && token->type != OP_CLOSE_SUBEXP, 0)) { if (tree != NULL) postorder (tree, free_tree, NULL); *err = REG_EPAREN; } if (BE (*err != REG_NOERROR, 0)) return NULL; } if (cur_nsub <= '9' - '1') dfa->completed_bkref_map |= 1 << cur_nsub; tree = create_tree (dfa, tree, NULL, SUBEXP); if (BE (tree == NULL, 0)) { *err = REG_ESPACE; return NULL; } tree->token.opr.idx = cur_nsub; return tree; } /* This function parse repetition operators like "*", "+", "{1,3}" etc. */ static bin_tree_t * parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) { bin_tree_t *tree = NULL, *old_tree = NULL; Idx i, start, end, start_idx = re_string_cur_idx (regexp); re_token_t start_token = *token; if (token->type == OP_OPEN_DUP_NUM) { end = 0; start = fetch_number (regexp, token, syntax); 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 (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) { if (token->type == END_OF_RE) *err = REG_EBRACE; else *err = REG_BADBR; return NULL; } /* If the syntax bit is set, rollback. */ re_string_set_index (regexp, start_idx); *token = start_token; token->type = CHARACTER; /* mb_partial and word_char bits should be already initialized by peek_token. */ return elem; } if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0)) { /* First number greater than second. */ *err = REG_BADBR; return NULL; } if (BE (RE_DUP_MAX < (end == -1 ? start : end), 0)) { *err = REG_ESIZE; return NULL; } } else { start = (token->type == OP_DUP_PLUS) ? 1 : 0; end = (token->type == OP_DUP_QUESTION) ? 1 : -1; } fetch_token (token, regexp, syntax); if (BE (elem == NULL, 0)) return NULL; if (BE (start == 0 && end == 0, 0)) { postorder (elem, free_tree, NULL); return NULL; } /* Extract "{n,m}" to "...{0,}". */ if (BE (start > 0, 0)) { tree = elem; for (i = 2; i <= start; ++i) { elem = duplicate_tree (elem, dfa); tree = create_tree (dfa, tree, elem, CONCAT); if (BE (elem == NULL || tree == NULL, 0)) goto parse_dup_op_espace; } if (start == end) return tree; /* Duplicate ELEM before it is marked optional. */ elem = duplicate_tree (elem, dfa); if (BE (elem == NULL, 0)) goto parse_dup_op_espace; old_tree = tree; } else old_tree = NULL; if (elem->token.type == SUBEXP) { uintptr_t subidx = elem->token.opr.idx; postorder (elem, mark_opt_subexp, (void *) subidx); } tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT)); if (BE (tree == NULL, 0)) goto parse_dup_op_espace; /* This loop is actually executed only when end != -1, to rewrite {0,n} as ((...?)?)?... We have already created the start+1-th copy. */ if (TYPE_SIGNED (Idx) || end != -1) for (i = start + 2; i <= end; ++i) { elem = duplicate_tree (elem, dfa); tree = create_tree (dfa, tree, elem, CONCAT); if (BE (elem == NULL || tree == NULL, 0)) goto parse_dup_op_espace; tree = create_tree (dfa, tree, NULL, OP_ALT); if (BE (tree == NULL, 0)) goto parse_dup_op_espace; } if (old_tree) tree = create_tree (dfa, old_tree, tree, CONCAT); return tree; parse_dup_op_espace: *err = REG_ESPACE; return NULL; } /* 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 # ifdef RE_ENABLE_I18N /* Convert the byte B to the corresponding wide character. In a unibyte locale, treat B as itself if it is an encoding error. In a multibyte locale, return WEOF if B is an encoding error. */ static wint_t parse_byte (unsigned char b, re_charset_t *mbcset) { wint_t wc = __btowc (b); return wc == WEOF && !mbcset ? b : wc; } #endif /* 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 since we may update it. */ static reg_errcode_t # ifdef RE_ENABLE_I18N build_range_exp (const reg_syntax_t syntax, bitset_t sbcset, re_charset_t *mbcset, Idx *range_alloc, const bracket_elem_t *start_elem, const bracket_elem_t *end_elem) # else /* not RE_ENABLE_I18N */ build_range_exp (const reg_syntax_t syntax, bitset_t sbcset, const bracket_elem_t *start_elem, const bracket_elem_t *end_elem) # endif /* not RE_ENABLE_I18N */ { 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; wint_t start_wc; wint_t end_wc; 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) ? parse_byte (start_ch, mbcset) : start_elem->opr.wch); end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) ? parse_byte (end_ch, mbcset) : end_elem->opr.wch); if (start_wc == WEOF || end_wc == WEOF) return REG_ECOLLATE; else if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_wc > end_wc, 0)) return REG_ERANGE; /* Got valid collation sequence values, add them as a new entry. However, for !_LIBC we have no collation elements: if the character set is single byte, the single byte character set that we build below suffices. parse_bracket_exp passes no MBCSET if dfa->mb_cur_max == 1. */ if (mbcset) { /* Check the space of the arrays. */ if (BE (*range_alloc == mbcset->nranges, 0)) { /* There is not enough space, need realloc. */ wchar_t *new_array_start, *new_array_end; Idx 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)) { re_free (new_array_start); re_free (new_array_end); 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) { if (start_wc <= wc && wc <= end_wc) 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 (bitset_t sbcset, re_charset_t *mbcset, Idx *coll_sym_alloc, const unsigned char *name) # else /* not RE_ENABLE_I18N */ build_collating_symbol (bitset_t sbcset, const unsigned char *name) # endif /* not RE_ENABLE_I18N */ { 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 (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 environment. Seek the collating symbol entry corresponding to NAME. Return the index of the symbol in the SYMB_TABLE, or -1 if not found. */ auto inline int32_t __attribute__ ((always_inline)) seek_collating_symbol_entry (const unsigned char *name, size_t name_len) { int32_t elem; for (elem = 0; elem < table_size; elem++) if (symb_table[2 * elem] != 0) { int32_t idx = symb_table[2 * elem + 1]; /* Skip the name of collating element name. */ idx += 1 + extra[idx]; if (/* Compare the length of the name. */ name_len == extra[idx] /* Compare the name. */ && memcmp (name, &extra[idx + 1], name_len) == 0) /* Yep, this is the entry. */ return elem; } return -1; } /* Local function for parse_bracket_exp used in _LIBC environment. Look up the collation sequence value of BR_ELEM. Return the value if succeeded, UINT_MAX otherwise. */ auto inline unsigned int __attribute__ ((always_inline)) lookup_collation_sequence_value (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) { if (nrules != 0) 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 (elem != -1) { /* 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 (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 environment. 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 since we may update it. */ auto inline reg_errcode_t __attribute__ ((always_inline)) build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc, bracket_elem_t *start_elem, bracket_elem_t *end_elem) { unsigned int ch; uint32_t start_collseq; uint32_t end_collseq; /* 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; /* FIXME: Implement rational ranges here, too. */ 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; /* Got valid collation sequence values, add them as a new entry. However, if we have no collation elements, and the character set is single byte, the single byte character set that we build below suffices. */ if (nrules > 0 || dfa->mb_cur_max > 1) { /* Check the space of the arrays. */ if (BE (*range_alloc == mbcset->nranges, 0)) { /* There is not enough space, need realloc. */ uint32_t *new_array_start; uint32_t *new_array_end; Idx new_nranges; /* +1 in case of mbcset->nranges is 0. */ new_nranges = 2 * mbcset->nranges + 1; 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; } mbcset->range_starts[mbcset->nranges] = start_collseq; mbcset->range_ends[mbcset->nranges++] = end_collseq; } /* 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 environment. 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. */ auto inline reg_errcode_t __attribute__ ((always_inline)) build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset, Idx *coll_sym_alloc, 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 (elem != -1) { /* We found the entry. */ idx = symb_table[2 * elem + 1]; /* Skip the name of collating element name. */ idx += 1 + extra[idx]; } else if (name_len == 1) { /* No valid character, treat it as a normal character. */ bitset_set (sbcset, name[0]); return REG_NOERROR; } else return REG_ECOLLATE; /* Got valid collation sequence, add it as a new entry. */ /* Check the space of the arrays. */ if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->ncoll_syms is 0. */ Idx new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; /* Use realloc since mbcset->coll_syms is NULL if *alloc == 0. */ int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, new_coll_sym_alloc); if (BE (new_coll_syms == NULL, 0)) return REG_ESPACE; mbcset->coll_syms = new_coll_syms; *coll_sym_alloc = new_coll_sym_alloc; } mbcset->coll_syms[mbcset->ncoll_syms++] = idx; 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; Idx coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; Idx equiv_class_alloc = 0, char_class_alloc = 0; #endif /* not RE_ENABLE_I18N */ bool non_match = false; bin_tree_t *work_tree; int token_len; bool first_round = true; #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 (bitset_t), 1); #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 */ { re_free (sbcset); #ifdef RE_ENABLE_I18N re_free (mbcset); #endif *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 mbcset->non_match = 1; #endif /* not RE_ENABLE_I18N */ non_match = true; if (syntax & RE_HAT_LISTS_NOT_NEWLINE) bitset_set (sbcset, '\n'); 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; } } /* 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; bool is_range_exp = false; re_token_t token2; start_elem.opr.name = start_name_buf; start_elem.type = COLL_SYM; ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, syntax, first_round); if (BE (ret != REG_NOERROR, 0)) { *err = ret; goto parse_bracket_exp_free_return; } first_round = false; /* Get information about the next token. We need it in any case. */ token_len = peek_token_bracket (token, regexp, syntax); /* Do not check for ranges if we know they are not allowed. */ if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) { if (BE (token->type == END_OF_RE, 0)) { *err = REG_EBRACK; 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 (token2.type == END_OF_RE, 0)) { *err = REG_EBRACK; 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 = true; } } if (is_range_exp == true) { end_elem.opr.name = end_name_buf; end_elem.type = COLL_SYM; ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, dfa, syntax, true); if (BE (ret != REG_NOERROR, 0)) { *err = ret; goto parse_bracket_exp_free_return; } token_len = peek_token_bracket (token, regexp, syntax); #ifdef _LIBC *err = build_range_exp (sbcset, mbcset, &range_alloc, &start_elem, &end_elem); #else # ifdef RE_ENABLE_I18N *err = build_range_exp (syntax, sbcset, dfa->mb_cur_max > 1 ? mbcset : NULL, &range_alloc, &start_elem, &end_elem); # else *err = build_range_exp (syntax, sbcset, &start_elem, &end_elem); # endif #endif /* RE_ENABLE_I18N */ 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 (BE (mbchar_alloc == mbcset->nmbchars, 0)) { wchar_t *new_mbchars; /* 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. */ new_mbchars = re_realloc (mbcset->mbchars, wchar_t, mbchar_alloc); if (BE (new_mbchars == NULL, 0)) goto parse_bracket_exp_espace; mbcset->mbchars = new_mbchars; } 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 (regexp->trans, sbcset, #ifdef RE_ENABLE_I18N mbcset, &char_class_alloc, #endif /* RE_ENABLE_I18N */ (const char *) start_elem.opr.name, syntax); if (BE (*err != REG_NOERROR, 0)) goto parse_bracket_exp_free_return; break; default: assert (0); break; } } if (BE (token->type == END_OF_RE, 0)) { *err = REG_EBRACK; goto parse_bracket_exp_free_return; } if (token->type == OP_CLOSE_BRACKET) break; } re_string_skip_bytes (regexp, token_len); /* Skip a token. */ /* If it is non-matching list. */ if (non_match) bitset_not (sbcset); #ifdef RE_ENABLE_I18N /* Ensure only single byte characters are set. */ if (dfa->mb_cur_max > 1) bitset_mask (sbcset, dfa->sb_char); if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes || mbcset->non_match))) { bin_tree_t *mbc_tree; int sbc_idx; /* Build a tree for complex bracket. */ dfa->has_mb_node = 1; br_token.type = COMPLEX_BRACKET; br_token.opr.mbcset = mbcset; mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (mbc_tree == NULL, 0)) goto parse_bracket_exp_espace; for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) if (sbcset[sbc_idx]) break; /* If there are no bits set in sbcset, there is no point of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ if (sbc_idx < BITSET_WORDS) { /* Build a tree for simple bracket. */ br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; work_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; /* Then join them by ALT node. */ work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; } else { re_free (sbcset); work_tree = mbc_tree; } } else #endif /* not RE_ENABLE_I18N */ { #ifdef RE_ENABLE_I18N free_charset (mbcset); #endif /* Build a tree for simple bracket. */ br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; work_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (work_tree == NULL, 0)) goto parse_bracket_exp_espace; } return work_tree; 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 (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token, int token_len, re_dfa_t *dfa, reg_syntax_t syntax, bool accept_hyphen) { #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); if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) { /* A '-' must only appear as anything but a range indicator before the closing bracket. Everything else is an error. */ re_token_t token2; (void) peek_token_bracket (&token2, regexp, syntax); if (token2.type != OP_CLOSE_BRACKET) /* The actual error value is not standardized since this whole case is undefined. But ERANGE makes good sense. */ return REG_ERANGE; } 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 (bracket_elem_t *elem, re_string_t *regexp, re_token_t *token) { unsigned char ch, delim = token->opr.c; int i = 0; if (re_string_eoi(regexp)) return REG_EBRACK; for (;; ++i) { if (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 (re_string_eoi(regexp)) return REG_EBRACK; 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 since we may update it. */ static reg_errcode_t #ifdef RE_ENABLE_I18N build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, Idx *equiv_class_alloc, const unsigned char *name) #else /* not RE_ENABLE_I18N */ build_equiv_class (bitset_t sbcset, const unsigned char *name) #endif /* not RE_ENABLE_I18N */ { #ifdef _LIBC 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; /* 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 (table, indirect, extra, &cp, -1); if (BE (idx1 == 0 || *cp != '\0', 0)) /* This isn't a valid character. */ return REG_ECOLLATE; /* Build single byte matching table for this equivalence class. */ len = weights[idx1 & 0xffffff]; for (ch = 0; ch < SBC_MAX; ++ch) { char_buf[0] = ch; cp = char_buf; idx2 = findidx (table, indirect, extra, &cp, 1); /* idx2 = table[ch]; */ if (idx2 == 0) /* This isn't a valid character. */ continue; /* Compare only if the length matches and the collation rule index is the same. */ if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24) && memcmp (weights + (idx1 & 0xffffff) + 1, weights + (idx2 & 0xffffff) + 1, len) == 0) bitset_set (sbcset, ch); } /* Check whether the array has enough space. */ if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->nequiv_classes is 0. */ Idx new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; /* Use realloc since the array is NULL if *alloc == 0. */ int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, int32_t, new_equiv_class_alloc); if (BE (new_equiv_classes == NULL, 0)) return REG_ESPACE; mbcset->equiv_classes = new_equiv_classes; *equiv_class_alloc = new_equiv_class_alloc; } mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; } else #endif /* _LIBC */ { 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 since we may update it. */ static reg_errcode_t #ifdef RE_ENABLE_I18N build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, re_charset_t *mbcset, Idx *char_class_alloc, const char *class_name, reg_syntax_t syntax) #else /* not RE_ENABLE_I18N */ build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, const char *class_name, reg_syntax_t syntax) #endif /* not RE_ENABLE_I18N */ { int i; const char *name = 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 (BE (*char_class_alloc == mbcset->nchar_classes, 0)) { /* Not enough, realloc it. */ /* +1 in case of mbcset->nchar_classes is 0. */ Idx new_char_class_alloc = 2 * mbcset->nchar_classes + 1; /* Use realloc since array is NULL if *alloc == 0. */ wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, new_char_class_alloc); if (BE (new_char_classes == NULL, 0)) return REG_ESPACE; mbcset->char_classes = new_char_classes; *char_class_alloc = new_char_class_alloc; } mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); #endif /* RE_ENABLE_I18N */ #define BUILD_CHARCLASS_LOOP(ctype_func) \ do { \ if (BE (trans != NULL, 0)) \ { \ for (i = 0; i < SBC_MAX; ++i) \ if (ctype_func (i)) \ bitset_set (sbcset, trans[i]); \ } \ else \ { \ for (i = 0; i < SBC_MAX; ++i) \ if (ctype_func (i)) \ bitset_set (sbcset, i); \ } \ } while (0) 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_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, const char *class_name, const char *extra, bool non_match, reg_errcode_t *err) { re_bitset_ptr_t sbcset; #ifdef RE_ENABLE_I18N re_charset_t *mbcset; Idx alloc = 0; #endif /* not RE_ENABLE_I18N */ reg_errcode_t ret; re_token_t br_token; bin_tree_t *tree; sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); if (BE (sbcset == NULL, 0)) { *err = REG_ESPACE; return NULL; } #ifdef RE_ENABLE_I18N mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); if (BE (mbcset == NULL, 0)) { re_free (sbcset); *err = REG_ESPACE; return NULL; } mbcset->non_match = non_match; #endif /* RE_ENABLE_I18N */ /* We don't care the syntax in this case. */ ret = build_charclass (trans, sbcset, #ifdef RE_ENABLE_I18N mbcset, &alloc, #endif /* RE_ENABLE_I18N */ class_name, 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. */ for (; *extra; extra++) bitset_set (sbcset, *extra); /* If it is non-matching list. */ if (non_match) bitset_not (sbcset); #ifdef RE_ENABLE_I18N /* Ensure only single byte characters are set. */ if (dfa->mb_cur_max > 1) bitset_mask (sbcset, dfa->sb_char); #endif /* Build a tree for simple bracket. */ #if defined GCC_LINT || defined lint memset (&br_token, 0, sizeof br_token); #endif br_token.type = SIMPLE_BRACKET; br_token.opr.sbcset = sbcset; tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (tree == NULL, 0)) goto build_word_op_espace; #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { 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; mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); if (BE (mbc_tree == NULL, 0)) goto build_word_op_espace; /* Then join them by ALT node. */ tree = create_tree (dfa, tree, mbc_tree, OP_ALT); if (BE (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 RE_DUP_MAX + 1 if the number field is too large. Return -2 if an error occurred. */ static Idx fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) { Idx num = -1; unsigned char c; while (1) { fetch_token (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' : MIN (RE_DUP_MAX + 1, num * 10 + c - '0')); } 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 tree node. */ static bin_tree_t * create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, re_token_type_t type) { re_token_t t; #if defined GCC_LINT || defined lint memset (&t, 0, sizeof t); #endif t.type = type; return create_token_tree (dfa, left, right, &t); } static bin_tree_t * create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, const re_token_t *token) { bin_tree_t *tree; if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) { bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); if (storage == NULL) return NULL; storage->next = dfa->str_tree_storage; dfa->str_tree_storage = storage; dfa->str_tree_storage_idx = 0; } tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; tree->parent = NULL; tree->left = left; tree->right = right; tree->token = *token; tree->token.duplicated = 0; tree->token.opt_subexp = 0; tree->first = NULL; tree->next = NULL; tree->node_idx = -1; if (left != NULL) left->parent = tree; if (right != NULL) right->parent = tree; return tree; } /* Mark the tree SRC as an optional subexpression. To be called from preorder or postorder. */ static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node) { Idx idx = (uintptr_t) extra; if (node->token.type == SUBEXP && node->token.opr.idx == idx) node->token.opt_subexp = 1; return REG_NOERROR; } /* Free the allocated memory inside NODE. */ static void free_token (re_token_t *node) { #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); } /* Worker function for tree walking. Free the allocated memory inside NODE and its children. */ static reg_errcode_t free_tree (void *extra, bin_tree_t *node) { free_token (&node->token); return REG_NOERROR; } /* Duplicate the node SRC, and return new node. This is a preorder visit similar to the one implemented by the generic visitor, but we need more infrastructure to maintain two parallel trees --- so, it's easier to duplicate. */ static bin_tree_t * duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) { const bin_tree_t *node; bin_tree_t *dup_root; bin_tree_t **p_new = &dup_root, *dup_node = root->parent; for (node = root; ; ) { /* Create a new tree and link it back to the current parent. */ *p_new = create_token_tree (dfa, NULL, NULL, &node->token); if (*p_new == NULL) return NULL; (*p_new)->parent = dup_node; (*p_new)->token.duplicated = 1; dup_node = *p_new; /* Go to the left node, or up and to the right. */ if (node->left) { node = node->left; p_new = &dup_node->left; } else { const bin_tree_t *prev = NULL; while (node->right == prev || node->right == NULL) { prev = node; node = node->parent; dup_node = dup_node->parent; if (!node) return dup_root; } node = node->right; p_new = &dup_node->right; } } } smartmontools-7.0/regex/regex.c0000644000175000010010000000625013336325511013604 00000000000000/* Extended regular expression matching and search library. Copyright (C) 2002-2018 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, see . */ #if !defined(_LIBC) && !defined(_REGEX_STANDALONE) # include # if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__ # pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" # endif # if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ # pragma GCC diagnostic ignored "-Wold-style-definition" # pragma GCC diagnostic ignored "-Wtype-limits" # endif #endif /* Make sure no one compiles this code with a C++ compiler. */ #if defined __cplusplus && defined _LIBC # error "This is C code, use a C compiler" #endif #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) # include "../locale/localeinfo.h" #endif /* On some systems, limits.h sets RE_DUP_MAX to a lower value than GNU regex allows. Include it before , which correctly #undefs RE_DUP_MAX and sets it to the right value. */ #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 smartmontools-7.0/regex/regex.h0000644000175000010010000006025713336325511013620 00000000000000/* Definitions for data structures and routines for the regular expression library. Copyright (C) 1985, 1989-2018 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, see . */ #ifndef _REGEX_H #define _REGEX_H 1 #include /* Allow the use in C++ code. */ #ifdef __cplusplus extern "C" { #endif /* Define __USE_GNU to declare GNU extensions that violate the POSIX name space rules. */ #if defined(_GNU_SOURCE) || defined(_REGEX_STANDALONE) # define __USE_GNU 1 #endif #ifdef _REGEX_LARGE_OFFSETS /* Use types and values that are wide enough to represent signed and unsigned byte offsets in memory. This currently works only when the regex code is used outside of the GNU C library; it is not yet supported within glibc itself, and glibc users should not define _REGEX_LARGE_OFFSETS. */ /* The type of object sizes. */ typedef size_t __re_size_t; /* The type of object sizes, in places where the traditional code uses unsigned long int. */ typedef size_t __re_long_size_t; #else /* The traditional GNU regex implementation mishandles strings longer than INT_MAX. */ typedef unsigned int __re_size_t; typedef unsigned long int __re_long_size_t; #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; #ifdef __USE_GNU /* 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 bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only for ^, because it is difficult to scan the regex backwards to find whether ^ should be special. */ # define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) /* If this bit is set, then \{ cannot be first in a regex or immediately after an alternation, open-group or \} operator. */ # define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) /* If this bit is set, then no_sub will be set to 1 during re_compile_pattern. */ # define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) #endif /* 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; #ifdef __USE_GNU /* 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_CHAR_CLASSES \ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) # define RE_SYNTAX_GNU_AWK \ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ | RE_INVALID_INTERVAL_ORD) \ & ~(RE_DOT_NOT_NULL | 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 \ | RE_INVALID_INTERVAL_ORD) # define RE_SYNTAX_GREP \ ((RE_SYNTAX_POSIX_BASIC | RE_NEWLINE_ALT) \ & ~(RE_CONTEXT_INVALID_DUP | RE_DOT_NOT_NULL)) # define RE_SYNTAX_EGREP \ ((RE_SYNTAX_POSIX_EXTENDED | RE_INVALID_INTERVAL_ORD | RE_NEWLINE_ALT) \ & ~(RE_CONTEXT_INVALID_OPS | RE_DOT_NOT_NULL)) /* POSIX grep -E behavior is no longer incompatible with GNU. */ # define RE_SYNTAX_POSIX_EGREP \ RE_SYNTAX_EGREP /* 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 | RE_CONTEXT_INVALID_DUP) /* 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. POSIX-conforming systems might define this in , but we want our value, so remove any previous define. */ # ifdef _REGEX_INCLUDE_LIMITS_H # include # endif # ifdef RE_DUP_MAX # undef RE_DUP_MAX # endif /* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored the counter as a 2-byte signed integer. This is no longer true, so RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to ((SIZE_MAX - 9) / 10) if _REGEX_LARGE_OFFSETS is defined. However, there would be a huge performance problem if someone actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains its historical value. */ # define RE_DUP_MAX (0x7fff) #endif /* 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 (1 << 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 (1 << 2) /* 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 (1 << 3) /* 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) /* Use PMATCH[0] to delimit the start and end of the search in the buffer. */ #define REG_STARTEND (1 << 2) /* If any error codes are removed, changed, or added, update the '__re_error_msgid' table in regcomp.c. */ typedef enum { _REG_ENOSYS = -1, /* This will never happen for this implementation. */ _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, /* Invalid collating element. */ _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, /* Too large (e.g., repeat count too large). */ _REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; #if defined _XOPEN_SOURCE || defined __USE_XOPEN2K # define REG_ENOSYS _REG_ENOSYS #endif #define REG_NOERROR _REG_NOERROR #define REG_NOMATCH _REG_NOMATCH #define REG_BADPAT _REG_BADPAT #define REG_ECOLLATE _REG_ECOLLATE #define REG_ECTYPE _REG_ECTYPE #define REG_EESCAPE _REG_EESCAPE #define REG_ESUBREG _REG_ESUBREG #define REG_EBRACK _REG_EBRACK #define REG_EPAREN _REG_EPAREN #define REG_EBRACE _REG_EBRACE #define REG_BADBR _REG_BADBR #define REG_ERANGE _REG_ERANGE #define REG_ESPACE _REG_ESPACE #define REG_BADRPT _REG_BADRPT #define REG_EEND _REG_EEND #define REG_ESIZE _REG_ESIZE #define REG_ERPAREN _REG_ERPAREN /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields 'buffer', 'allocated', 'fastmap', and 'translate' can be set. After the pattern has been compiled, the fields 're_nsub', 'not_bol' and 'not_eol' are available. All other fields are private to the regex routines. */ #ifndef RE_TRANSLATE_TYPE # define __RE_TRANSLATE_TYPE unsigned char * # ifdef __USE_GNU # define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE # endif #endif #ifdef __USE_GNU # define __REPB_PREFIX(name) name #else # define __REPB_PREFIX(name) __##name #endif struct re_pattern_buffer { /* Space that holds the compiled pattern. The type 'struct re_dfa_t' is private and is not declared here. */ struct re_dfa_t *__REPB_PREFIX(buffer); /* Number of bytes to which 'buffer' points. */ __re_long_size_t __REPB_PREFIX(allocated); /* Number of bytes actually used in 'buffer'. */ __re_long_size_t __REPB_PREFIX(used); /* Syntax setting with which the pattern was compiled. */ reg_syntax_t __REPB_PREFIX(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 *__REPB_PREFIX(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 __REPB_PREFIX(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 __REPB_PREFIX(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. */ #ifdef __USE_GNU # define REGS_UNALLOCATED 0 # define REGS_REALLOCATE 1 # define REGS_FIXED 2 #endif unsigned __REPB_PREFIX(regs_allocated) : 2; /* Set to zero when 're_compile_pattern' compiles a pattern; set to one by 're_compile_fastmap' if it updates the fastmap. */ unsigned __REPB_PREFIX(fastmap_accurate) : 1; /* If set, 're_match_2' does not return information about subexpressions. */ unsigned __REPB_PREFIX(no_sub) : 1; /* If set, a beginning-of-line anchor doesn't match at the beginning of the string. */ unsigned __REPB_PREFIX(not_bol) : 1; /* Similarly for an end-of-line anchor. */ unsigned __REPB_PREFIX(not_eol) : 1; /* If true, an anchor at a newline matches. */ unsigned __REPB_PREFIX(newline_anchor) : 1; }; typedef struct re_pattern_buffer regex_t; /* Type for byte offsets within the string. POSIX mandates this. */ #ifdef _REGEX_LARGE_OFFSETS /* POSIX 1003.1-2008 requires that regoff_t be at least as wide as ptrdiff_t and ssize_t. We don't know of any hosts where ptrdiff_t is wider than ssize_t, so ssize_t is safe. ptrdiff_t is not visible here, so use ssize_t. */ typedef ssize_t regoff_t; #else /* The traditional GNU regex implementation mishandles strings longer than INT_MAX. */ typedef int regoff_t; #endif #ifdef __USE_GNU /* This is the structure we store register match data in. See regex.texinfo for a full description of what registers match. */ struct re_registers { __re_size_t 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 #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. */ #ifdef __USE_GNU /* 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 (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. To free the allocated storage, you must call 'regfree' on BUFFER. Note that the translate table must either have been initialized by 'regcomp', with a malloc'ed value, or set to NULL before calling 'regfree'. */ extern const char *re_compile_pattern (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 (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 regoff_t re_search (struct re_pattern_buffer *__buffer, const char *__String, regoff_t __length, regoff_t __start, regoff_t __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 regoff_t re_search_2 (struct re_pattern_buffer *__buffer, const char *__string1, regoff_t __length1, const char *__string2, regoff_t __length2, regoff_t __start, regoff_t __range, struct re_registers *__regs, regoff_t __stop); /* Like 're_search', but return how many characters in STRING the regexp in BUFFER matched, starting at position START. */ extern regoff_t re_match (struct re_pattern_buffer *__buffer, const char *__String, regoff_t __length, regoff_t __start, struct re_registers *__regs); /* Relates to 're_match' as 're_search_2' relates to 're_search'. */ extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer, const char *__string1, regoff_t __length1, const char *__string2, regoff_t __length2, regoff_t __start, struct re_registers *__regs, regoff_t __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 BUFFER will allocate its own register data, without freeing the old data. */ extern void re_set_registers (struct re_pattern_buffer *__buffer, struct re_registers *__regs, __re_size_t __num_regs, regoff_t *__starts, regoff_t *__ends); #endif /* Use GNU */ #if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_MISC) # ifndef _CRAY /* 4.2 bsd compatibility. */ extern char *re_comp (const char *); extern int re_exec (const char *); # endif #endif /* For plain 'restrict', use glibc's __restrict if defined. Otherwise, GCC 2.95 and later have "__restrict"; C99 compilers have "restrict", and "configure" may have defined "restrict". Other compilers use __restrict, __restrict__, and _Restrict, and 'configure' might #define 'restrict' to those words, so pick a different name. */ #ifndef _Restrict_ # if defined __restrict || 2 < __GNUC__ + (95 <= __GNUC_MINOR__) # define _Restrict_ __restrict # elif 199901L <= __STDC_VERSION__ || defined restrict # define _Restrict_ restrict # else # define _Restrict_ # endif #endif /* For [restrict], use glibc's __restrict_arr if available. Otherwise, GCC 3.1 (not in C++ mode) and C99 support [restrict]. */ #ifndef _Restrict_arr_ # ifdef __restrict_arr # define _Restrict_arr_ __restrict_arr # elif ((199901L <= __STDC_VERSION__ || 3 < __GNUC__ + (1 <= __GNUC_MINOR__)) \ && !defined __GNUG__) # define _Restrict_arr_ _Restrict_ # else # define _Restrict_arr_ # endif #endif /* POSIX compatibility. */ extern int regcomp (regex_t *_Restrict_ __preg, const char *_Restrict_ __pattern, int __cflags); extern int regexec (const regex_t *_Restrict_ __preg, const char *_Restrict_ __String, size_t __nmatch, regmatch_t __pmatch[_Restrict_arr_], int __eflags); extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg, char *_Restrict_ __errbuf, size_t __errbuf_size); extern void regfree (regex_t *__preg); #ifdef __cplusplus } #endif /* C++ */ #endif /* regex.h */ smartmontools-7.0/regex/regexec.c0000644000175000010010000037246513336323570014135 00000000000000/* Extended regular expression matching and search library. Copyright (C) 2002-2018 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, see . */ static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, Idx n); static void match_ctx_clean (re_match_context_t *mctx); static void match_ctx_free (re_match_context_t *cache); static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, Idx node, Idx str_idx, Idx from, Idx to); static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx); static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx); static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx); static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx); static reg_errcode_t re_search_internal (const regex_t *preg, const char *string, Idx length, Idx start, Idx last_start, Idx stop, size_t nmatch, regmatch_t pmatch[], int eflags); static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1, Idx length1, const char *string2, Idx length2, Idx start, regoff_t range, struct re_registers *regs, Idx stop, bool ret_len); static regoff_t re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length, Idx start, regoff_t range, Idx stop, struct re_registers *regs, bool ret_len); static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs, int regs_allocated); static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx); static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match, Idx *p_match_first); static Idx check_halt_state_context (const re_match_context_t *mctx, const re_dfastate_t *state, Idx idx); static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch); static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, Idx 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, bool 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 re_match_context_t *mctx, re_sift_context_t *sctx, Idx node_idx, Idx str_idx, Idx max_str_idx); #endif /* RE_ENABLE_I18N */ static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx); static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, re_node_set *cur_dest); static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, re_node_set *dest_nodes); static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates); static bool check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx); static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, Idx subexp_idx, Idx from_node, Idx bkref_idx); static int check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, Idx subexp_idx, Idx node, Idx str_idx, Idx bkref_idx); static reg_errcode_t check_subexp_limits (const 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, Idx str_idx); static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, const re_node_set *candidates); static reg_errcode_t merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, re_dfastate_t **src, Idx num); static re_dfastate_t *find_recover_state (reg_errcode_t *err, re_match_context_t *mctx); static re_dfastate_t *transit_state (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state); static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *next_state); static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, Idx str_idx); #if 0 static re_dfastate_t *transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *pstate); #endif #ifdef RE_ENABLE_I18N static reg_errcode_t transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate); #endif /* RE_ENABLE_I18N */ static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes); static reg_errcode_t get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx); static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str); static Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, Idx subexp_idx, int type); static reg_errcode_t check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node, Idx top_str, Idx last_node, Idx last_str, int type); static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx, re_node_set *cur_nodes, re_node_set *next_nodes); static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, Idx ex_subexp, int type); static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, Idx target, Idx ex_subexp, int type); static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, Idx cur_str, Idx subexp_num, int type); static bool build_trtable (const re_dfa_t *dfa, re_dfastate_t *state); #ifdef RE_ENABLE_I18N static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, const re_string_t *input, Idx 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 Idx group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, re_node_set *states_node, bitset_t *states_ch); static bool check_node_accept (const re_match_context_t *mctx, const re_token_t *node, Idx idx); static reg_errcode_t extend_buffers (re_match_context_t *mctx, int min_len); /* 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 (const regex_t *_Restrict_ preg, const char *_Restrict_ string, size_t nmatch, regmatch_t pmatch[], int eflags) { reg_errcode_t err; Idx start, length; re_dfa_t *dfa = preg->buffer; if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) return REG_BADPAT; if (eflags & REG_STARTEND) { start = pmatch[0].rm_so; length = pmatch[0].rm_eo; } else { start = 0; length = strlen (string); } lock_lock (dfa->lock); if (preg->no_sub) err = re_search_internal (preg, string, length, start, length, length, 0, NULL, eflags); else err = re_search_internal (preg, string, length, start, length, length, nmatch, pmatch, eflags); lock_unlock (dfa->lock); return err != REG_NOERROR; } #ifdef _LIBC libc_hidden_def (__regexec) # include versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) __typeof__ (__regexec) __compat_regexec; int attribute_compat_text_section __compat_regexec (const regex_t *_Restrict_ preg, const char *_Restrict_ string, size_t nmatch, regmatch_t pmatch[], int eflags) { return regexec (preg, string, nmatch, pmatch, eflags & (REG_NOTBOL | REG_NOTEOL)); } compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); # endif #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 stored 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. */ regoff_t re_match (struct re_pattern_buffer *bufp, const char *string, Idx length, Idx start, struct re_registers *regs) { return re_search_stub (bufp, string, length, start, 0, length, regs, true); } #ifdef _LIBC weak_alias (__re_match, re_match) #endif regoff_t re_search (struct re_pattern_buffer *bufp, const char *string, Idx length, Idx start, regoff_t range, struct re_registers *regs) { return re_search_stub (bufp, string, length, start, range, length, regs, false); } #ifdef _LIBC weak_alias (__re_search, re_search) #endif regoff_t re_match_2 (struct re_pattern_buffer *bufp, const char *string1, Idx length1, const char *string2, Idx length2, Idx start, struct re_registers *regs, Idx stop) { return re_search_2_stub (bufp, string1, length1, string2, length2, start, 0, regs, stop, true); } #ifdef _LIBC weak_alias (__re_match_2, re_match_2) #endif regoff_t re_search_2 (struct re_pattern_buffer *bufp, const char *string1, Idx length1, const char *string2, Idx length2, Idx start, regoff_t range, struct re_registers *regs, Idx stop) { return re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs, stop, false); } #ifdef _LIBC weak_alias (__re_search_2, re_search_2) #endif static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1, Idx length1, const char *string2, Idx length2, Idx start, regoff_t range, struct re_registers *regs, Idx stop, bool ret_len) { const char *str; regoff_t rval; Idx len; char *s = NULL; if (BE ((length1 < 0 || length2 < 0 || stop < 0 || INT_ADD_WRAPV (length1, length2, &len)), 0)) return -2; /* Concatenate the strings. */ if (length2 > 0) if (length1 > 0) { s = re_malloc (char, len); if (BE (s == NULL, 0)) return -2; #ifdef _LIBC memcpy (__mempcpy (s, string1, length1), string2, length2); #else memcpy (s, string1, length1); memcpy (s + length1, string2, length2); #endif str = s; } else str = string2; else str = string1; rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len); re_free (s); return rval; } /* The parameters have the same meaning as those of re_search. Additional parameters: If RET_LEN is true the length of the match is returned (re_match style); otherwise the position of the match is returned. */ static regoff_t re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length, Idx start, regoff_t range, Idx stop, struct re_registers *regs, bool ret_len) { reg_errcode_t result; regmatch_t *pmatch; Idx nregs; regoff_t rval; int eflags = 0; re_dfa_t *dfa = bufp->buffer; Idx last_start = start + range; /* Check for out-of-range. */ if (BE (start < 0 || start > length, 0)) return -1; if (BE (length < last_start || (0 <= range && last_start < start), 0)) last_start = length; else if (BE (last_start < 0 || (range < 0 && start <= last_start), 0)) last_start = 0; lock_lock (dfa->lock); eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; /* Compile fastmap if we haven't yet. */ if (start < last_start && 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, 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)) { rval = -2; goto out; } result = re_search_internal (bufp, string, length, start, last_start, stop, nregs, pmatch, eflags); rval = 0; /* I hope we needn't fill their regs with -1's when no match was found. */ if (result != REG_NOERROR) rval = result == REG_NOMATCH ? -1 : -2; 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); out: lock_unlock (dfa->lock); return rval; } static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs, int regs_allocated) { int rval = REGS_REALLOCATE; Idx i; Idx 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 (BE (need_regs > regs->num_regs, 0)) { regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); regoff_t *new_end; if (BE (new_start == NULL, 0)) return REGS_UNALLOCATED; new_end = re_realloc (regs->end, regoff_t, need_regs); if (BE (new_end == NULL, 0)) { re_free (new_start); return REGS_UNALLOCATED; } regs->start = new_start; regs->end = new_end; 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 (struct re_pattern_buffer *bufp, struct re_registers *regs, __re_size_t num_regs, regoff_t *starts, regoff_t *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 = NULL; } } #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 (const char *s) { return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); } #endif /* _REGEX_RE_COMP */ /* Internal entry point. */ /* Searches for a compiled pattern PREG in the string STRING, whose length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same meaning as with regexec. LAST_START is START + RANGE, where START and RANGE have the same meaning as 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. (0 <= LAST_START && LAST_START <= LENGTH) */ static reg_errcode_t __attribute_warn_unused_result__ re_search_internal (const regex_t *preg, const char *string, Idx length, Idx start, Idx last_start, Idx stop, size_t nmatch, regmatch_t pmatch[], int eflags) { reg_errcode_t err; const re_dfa_t *dfa = preg->buffer; Idx left_lim, right_lim; int incr; bool fl_longest_match; int match_kind; Idx match_first; Idx match_last = -1; Idx extra_nmatch; bool sb; int ch; #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) re_match_context_t mctx = { .dfa = dfa }; #else re_match_context_t mctx; #endif char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate && start != last_start && !preg->can_be_null) ? preg->fastmap : NULL); RE_TRANSLATE_TYPE t = preg->translate; #if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) memset (&mctx, '\0', sizeof (re_match_context_t)); mctx.dfa = dfa; #endif extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; nmatch -= extra_nmatch; /* 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; #ifdef DEBUG /* We assume front-end functions already check them. */ assert (0 <= last_start && last_start <= length); #endif /* If initial states with non-begbuf contexts have no elements, the regex must be anchored. If preg->newline_anchor is set, we'll never use init_state_nl, so do not check it. */ if (dfa->init_state->nodes.nelem == 0 && dfa->init_state_word->nodes.nelem == 0 && (dfa->init_state_nl->nodes.nelem == 0 || !preg->newline_anchor)) { if (start != 0 && last_start != 0) return REG_NOMATCH; start = last_start = 0; } /* We must check the longest matching, if nmatch > 0. */ fl_longest_match = (nmatch != 0 || dfa->nbackref); err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, preg->translate, (preg->syntax & RE_ICASE) != 0, dfa); if (BE (err != REG_NOERROR, 0)) goto free_return; mctx.input.stop = stop; mctx.input.raw_stop = stop; mctx.input.newline_anchor = preg->newline_anchor; err = match_ctx_init (&mctx, eflags, 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) { /* Avoid overflow. */ if (BE ((MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *)) <= mctx.input.bufs_len), 0)) { err = REG_ESPACE; goto free_return; } mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); if (BE (mctx.state_log == NULL, 0)) { err = REG_ESPACE; goto free_return; } } else mctx.state_log = NULL; match_first = start; mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF : CONTEXT_NEWLINE | CONTEXT_BEGBUF; /* Check incrementally whether the input string matches. */ incr = (last_start < start) ? -1 : 1; left_lim = (last_start < start) ? last_start : start; right_lim = (last_start < start) ? start : last_start; sb = dfa->mb_cur_max == 1; match_kind = (fastmap ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) | (start <= last_start ? 2 : 0) | (t != NULL ? 1 : 0)) : 8); for (;; match_first += incr) { err = REG_NOMATCH; if (match_first < left_lim || right_lim < match_first) goto free_return; /* Advance as rapidly as possible through the string, until we find a plausible place to start matching. This may be done with varying efficiency, so there are various possibilities: only the most common of them are specialized, in order to save on code size. We use a switch statement for speed. */ switch (match_kind) { case 8: /* No fastmap. */ break; case 7: /* Fastmap with single-byte translation, match forward. */ while (BE (match_first < right_lim, 1) && !fastmap[t[(unsigned char) string[match_first]]]) ++match_first; goto forward_match_found_start_or_reached_end; case 6: /* Fastmap without translation, match forward. */ while (BE (match_first < right_lim, 1) && !fastmap[(unsigned char) string[match_first]]) ++match_first; forward_match_found_start_or_reached_end: if (BE (match_first == right_lim, 0)) { ch = match_first >= length ? 0 : (unsigned char) string[match_first]; if (!fastmap[t ? t[ch] : ch]) goto free_return; } break; case 4: case 5: /* Fastmap without multi-byte translation, match backwards. */ while (match_first >= left_lim) { ch = match_first >= length ? 0 : (unsigned char) string[match_first]; if (fastmap[t ? t[ch] : ch]) break; --match_first; } if (match_first < left_lim) goto free_return; break; default: /* 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. */ for (;;) { /* If MATCH_FIRST is out of the valid range, reconstruct the buffers. */ __re_size_t offset = match_first - mctx.input.raw_mbs_idx; if (BE (offset >= (__re_size_t) mctx.input.valid_raw_len, 0)) { err = re_string_reconstruct (&mctx.input, match_first, eflags); if (BE (err != REG_NOERROR, 0)) goto free_return; offset = match_first - mctx.input.raw_mbs_idx; } /* 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 (&mctx.input, offset)); if (fastmap[ch]) break; match_first += incr; if (match_first < left_lim || match_first > right_lim) { err = REG_NOMATCH; goto free_return; } } break; } /* Reconstruct the buffers so that the matcher can assume that the matching starts from the beginning of the buffer. */ err = re_string_reconstruct (&mctx.input, match_first, eflags); if (BE (err != REG_NOERROR, 0)) goto free_return; #ifdef RE_ENABLE_I18N /* Don't consider this char as a possible match start if it part, yet isn't the head, of a multibyte character. */ if (!sb && !re_string_first_byte (&mctx.input, 0)) continue; #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 (&mctx, fl_longest_match, start <= last_start ? &match_first : NULL); 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 (&mctx, pstate, match_last); } if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) || dfa->nbackref) { err = prune_impossible_nodes (&mctx); if (err == REG_NOERROR) break; if (BE (err != REG_NOMATCH, 0)) goto free_return; match_last = -1; } else break; /* We found a match. */ } } match_ctx_clean (&mctx); } #ifdef DEBUG assert (match_last != -1); assert (err == REG_NOERROR); #endif /* Set pmatch[] if we need. */ if (nmatch > 0) { Idx reg_idx; /* Initialize registers. */ for (reg_idx = 1; 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; /* FIXME: This function should fail if mctx.match_last exceeds the maximum possible regoff_t value. We need a new error code REG_OVERFLOW. */ 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 each register, since we slid the buffers so that we could assume that the matching starts from 0. */ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) if (pmatch[reg_idx].rm_so != -1) { #ifdef RE_ENABLE_I18N if (BE (mctx.input.offsets_needed != 0, 0)) { pmatch[reg_idx].rm_so = (pmatch[reg_idx].rm_so == mctx.input.valid_len ? mctx.input.valid_raw_len : mctx.input.offsets[pmatch[reg_idx].rm_so]); pmatch[reg_idx].rm_eo = (pmatch[reg_idx].rm_eo == mctx.input.valid_len ? mctx.input.valid_raw_len : mctx.input.offsets[pmatch[reg_idx].rm_eo]); } #else assert (mctx.input.offsets_needed == 0); #endif pmatch[reg_idx].rm_so += match_first; pmatch[reg_idx].rm_eo += match_first; } for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) { pmatch[nmatch + reg_idx].rm_so = -1; pmatch[nmatch + reg_idx].rm_eo = -1; } if (dfa->subexp_map) for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) if (dfa->subexp_map[reg_idx] != reg_idx) { pmatch[reg_idx + 1].rm_so = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; pmatch[reg_idx + 1].rm_eo = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; } } free_return: re_free (mctx.state_log); if (dfa->nbackref) match_ctx_free (&mctx); re_string_destruct (&mctx.input); return err; } static reg_errcode_t __attribute_warn_unused_result__ prune_impossible_nodes (re_match_context_t *mctx) { const re_dfa_t *const dfa = mctx->dfa; Idx halt_node, match_last; reg_errcode_t ret; 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; /* Avoid overflow. */ if (BE (MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *)) <= match_last, 0)) return REG_ESPACE; 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)); sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); ret = sift_states_backward (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] == NULL || !mctx->state_log[match_last]->halt); halt_node = check_halt_state_context (mctx, mctx->state_log[match_last], 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); ret = sift_states_backward (mctx, &sctx); re_node_set_free (&sctx.limits); if (BE (ret != REG_NOERROR, 0)) goto free_return; if (sifted_states[0] == NULL) { ret = REG_NOMATCH; 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 * __attribute__ ((always_inline)) acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, Idx idx) { const re_dfa_t *const dfa = mctx->dfa; if (dfa->init_state->has_constraint) { unsigned int context; context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); 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 there is no match, and return -2 in case of an error. FL_LONGEST_MATCH means we want the POSIX longest matching. If P_MATCH_FIRST is not NULL, and the match fails, it is set to the next place where we may want to try matching. Note that the matcher assumes that the matching starts from the current index of the buffer. */ static Idx __attribute_warn_unused_result__ check_matching (re_match_context_t *mctx, bool fl_longest_match, Idx *p_match_first) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; Idx match = 0; Idx match_last = -1; Idx cur_str_idx = re_string_cur_idx (&mctx->input); re_dfastate_t *cur_state; bool at_init_state = p_match_first != NULL; Idx next_start_idx = cur_str_idx; err = REG_NOERROR; cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); /* An initial state must not be NULL (invalid). */ if (BE (cur_state == NULL, 0)) { assert (err == REG_ESPACE); 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 (BE (dfa->nbackref, 0)) { at_init_state = false; err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); if (BE (err != REG_NOERROR, 0)) return err; if (cur_state->has_backref) { err = transit_state_bkref (mctx, &cur_state->nodes); if (BE (err != REG_NOERROR, 0)) return err; } } } /* If the RE accepts NULL string. */ if (BE (cur_state->halt, 0)) { if (!cur_state->has_constraint || check_halt_state_context (mctx, cur_state, 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)) { re_dfastate_t *old_state = cur_state; Idx next_char_idx = re_string_cur_idx (&mctx->input) + 1; if ((BE (next_char_idx >= mctx->input.bufs_len, 0) && mctx->input.bufs_len < mctx->input.len) || (BE (next_char_idx >= mctx->input.valid_len, 0) && mctx->input.valid_len < mctx->input.len)) { err = extend_buffers (mctx, next_char_idx + 1); if (BE (err != REG_NOERROR, 0)) { assert (err == REG_ESPACE); return -2; } } cur_state = transit_state (&err, mctx, cur_state); if (mctx->state_log != NULL) cur_state = merge_state_with_log (&err, mctx, cur_state); if (cur_state == NULL) { /* Reached the invalid state or an error. Try to recover a valid state using the state log, if available and if we have not already found a valid (even if not the longest) match. */ if (BE (err != REG_NOERROR, 0)) return -2; if (mctx->state_log == NULL || (match && !fl_longest_match) || (cur_state = find_recover_state (&err, mctx)) == NULL) break; } if (BE (at_init_state, 0)) { if (old_state == cur_state) next_start_idx = next_char_idx; else at_init_state = false; } if (cur_state->halt) { /* Reached a halt state. Check the halt state can satisfy the current context. */ if (!cur_state->has_constraint || check_halt_state_context (mctx, cur_state, re_string_cur_idx (&mctx->input))) { /* We found an appropriate halt state. */ match_last = re_string_cur_idx (&mctx->input); match = 1; /* We found a match, do not modify match_first below. */ p_match_first = NULL; if (!fl_longest_match) break; } } } if (p_match_first) *p_match_first += next_start_idx; return match_last; } /* Check NODE match the current context. */ static bool check_halt_node_context (const re_dfa_t *dfa, Idx 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 false; if (!constraint) return true; if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) return false; return true; } /* 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 Idx check_halt_state_context (const re_match_context_t *mctx, const re_dfastate_t *state, Idx idx) { Idx i; unsigned int context; #ifdef DEBUG assert (state->halt); #endif context = re_string_context_at (&mctx->input, idx, mctx->eflags); for (i = 0; i < state->nodes.nelem; ++i) if (check_halt_node_context (mctx->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 Idx proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, Idx *pidx, Idx node, re_node_set *eps_via_nodes, struct re_fail_stack_t *fs) { const re_dfa_t *const dfa = mctx->dfa; Idx i; bool ok; if (IS_EPSILON_NODE (dfa->nodes[node].type)) { re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; re_node_set *edests = &dfa->edests[node]; Idx dest_node; ok = re_node_set_insert (eps_via_nodes, node); if (BE (! ok, 0)) return -2; /* Pick up a valid destination, or return -1 if none is found. */ for (dest_node = -1, i = 0; i < edests->nelem; ++i) { Idx candidate = edests->elems[i]; if (!re_node_set_contains (cur_nodes, candidate)) continue; if (dest_node == -1) dest_node = candidate; else { /* In order to avoid infinite loop like "(a*)*", return the second epsilon-transition if the first was already considered. */ if (re_node_set_contains (eps_via_nodes, dest_node)) return candidate; /* Otherwise, push the second epsilon-transition on the fail stack. */ else if (fs != NULL && push_fail_stack (fs, *pidx, candidate, nregs, regs, eps_via_nodes)) return -2; /* We know we are going to exit. */ break; } } return dest_node; } else { Idx naccepted = 0; re_token_type_t type = dfa->nodes[node].type; #ifdef RE_ENABLE_I18N if (dfa->nodes[node].accept_mb) naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); else #endif /* RE_ENABLE_I18N */ if (type == OP_BACK_REF) { Idx subexp_idx = dfa->nodes[node].opr.idx + 1; 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) { Idx dest_node; ok = re_node_set_insert (eps_via_nodes, node); if (BE (! ok, 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 (mctx, dfa->nodes + node, *pidx)) { Idx 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 __attribute_warn_unused_result__ push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes) { reg_errcode_t err; Idx num = fs->num++; if (fs->num == fs->alloc) { struct re_fail_stack_ent_t *new_array; new_array = re_realloc (fs->stack, struct re_fail_stack_ent_t, fs->alloc * 2); if (new_array == NULL) return REG_ESPACE; fs->alloc *= 2; fs->stack = new_array; } fs->stack[num].idx = str_idx; fs->stack[num].node = dest_node; fs->stack[num].regs = re_malloc (regmatch_t, nregs); if (fs->stack[num].regs == NULL) return REG_ESPACE; 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 Idx pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes) { Idx 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 for 0 < i < nmatch. */ static reg_errcode_t __attribute_warn_unused_result__ set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, regmatch_t *pmatch, bool fl_backtrack) { const re_dfa_t *dfa = preg->buffer; Idx idx, cur_node; re_node_set eps_via_nodes; struct re_fail_stack_t *fs; struct re_fail_stack_t fs_body = { 0, 2, NULL }; regmatch_t *prev_idx_match; bool prev_idx_match_malloced = false; #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); if (fs->stack == NULL) return REG_ESPACE; } else fs = NULL; cur_node = dfa->init_node; re_node_set_init_empty (&eps_via_nodes); if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); else { prev_idx_match = re_malloc (regmatch_t, nmatch); if (prev_idx_match == NULL) { free_fail_stack_return (fs); return REG_ESPACE; } prev_idx_match_malloced = true; } memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) { update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) { Idx 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); if (prev_idx_match_malloced) re_free (prev_idx_match); 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); if (prev_idx_match_malloced) re_free (prev_idx_match); return REG_NOERROR; } } /* Proceed to next node. */ cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, &eps_via_nodes, fs); if (BE (cur_node < 0, 0)) { if (BE (cur_node == -2, 0)) { re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); free_fail_stack_return (fs); 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); if (prev_idx_match_malloced) re_free (prev_idx_match); return REG_NOMATCH; } } } re_node_set_free (&eps_via_nodes); if (prev_idx_match_malloced) re_free (prev_idx_match); return free_fail_stack_return (fs); } static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) { if (fs) { Idx 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 (const re_dfa_t *dfa, regmatch_t *pmatch, regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch) { int type = dfa->nodes[cur_node].type; if (type == OP_OPEN_SUBEXP) { Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; /* We are at the first node of this sub expression. */ if (reg_num < nmatch) { pmatch[reg_num].rm_so = cur_idx; pmatch[reg_num].rm_eo = -1; } } else if (type == OP_CLOSE_SUBEXP) { Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; if (reg_num < nmatch) { /* We are at the last node of this sub expression. */ if (pmatch[reg_num].rm_so < cur_idx) { pmatch[reg_num].rm_eo = cur_idx; /* This is a non-empty match or we are not inside an optional subexpression. Accept this right away. */ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); } else { if (dfa->nodes[cur_node].opt_subexp && prev_idx_match[reg_num].rm_so != -1) /* We transited through an empty match for an optional subexpression, like (a?)*, and this is not the subexp's first match. Copy back the old content of the registers so that matches of an inner subexpression are undone as well, like in ((a?))*. */ memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); else /* We completed a subexpression, but it may be part of an optional one, so do not update PREV_IDX_MATCH. */ pmatch[reg_num].rm_eo = cur_idx; } } } } /* 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 thrown away, we throw away the node 'a'. 3. When 0 <= STR_IDX < MATCH_LAST 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 thrown 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 (const re_match_context_t *mctx, re_sift_context_t *sctx) { reg_errcode_t err; int null_cnt = 0; Idx str_idx = sctx->last_str_idx; re_node_set cur_dest; #ifdef DEBUG assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); #endif /* 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 (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) { /* 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; if (mctx->state_log[str_idx]) { err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); if (BE (err != REG_NOERROR, 0)) 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 (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; } static reg_errcode_t __attribute_warn_unused_result__ build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, re_node_set *cur_dest) { const re_dfa_t *const dfa = mctx->dfa; const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; Idx i; /* 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]' (with the epsilon nodes pre-filtered out). */ for (i = 0; i < cur_src->nelem; i++) { Idx prev_node = cur_src->elems[i]; int naccepted = 0; bool ok; #ifdef DEBUG re_token_type_t type = dfa->nodes[prev_node].type; assert (!IS_EPSILON_NODE (type)); #endif #ifdef RE_ENABLE_I18N /* If the node may accept "multi byte". */ if (dfa->nodes[prev_node].accept_mb) naccepted = sift_states_iter_mb (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 (mctx, dfa->nodes + prev_node, 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) { Idx to_idx = str_idx + naccepted; if (check_dst_limits (mctx, &sctx->limits, dfa->nexts[prev_node], to_idx, prev_node, str_idx)) continue; } ok = re_node_set_insert (cur_dest, prev_node); if (BE (! ok, 0)) return REG_ESPACE; } return REG_NOERROR; } /* Helper functions. */ static reg_errcode_t clean_state_log_if_needed (re_match_context_t *mctx, Idx next_state_log_idx) { Idx top = mctx->state_log_top; if ((next_state_log_idx >= mctx->input.bufs_len && mctx->input.bufs_len < mctx->input.len) || (next_state_log_idx >= mctx->input.valid_len && mctx->input.valid_len < mctx->input.len)) { reg_errcode_t err; err = extend_buffers (mctx, next_state_log_idx + 1); 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 (const re_dfa_t *dfa, re_dfastate_t **dst, re_dfastate_t **src, Idx num) { Idx 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 (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, re_node_set *dest_nodes) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err = REG_NOERROR; const re_node_set *candidates; candidates = ((mctx->state_log[str_idx] == NULL) ? NULL : &mctx->state_log[str_idx]->nodes); if (dest_nodes->nelem == 0) sctx->sifted_states[str_idx] = NULL; else { if (candidates) { /* At first, add the nodes which can epsilon transit to a node in DEST_NODE. */ 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 (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; } } sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); if (BE (err != REG_NOERROR, 0)) return err; } if (candidates && mctx->state_log[str_idx]->has_backref) { err = sift_states_bkref (mctx, sctx, str_idx, candidates); if (BE (err != REG_NOERROR, 0)) return err; } return REG_NOERROR; } static reg_errcode_t __attribute_warn_unused_result__ add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, const re_node_set *candidates) { reg_errcode_t err = REG_NOERROR; Idx i; re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); if (BE (err != REG_NOERROR, 0)) return err; if (!state->inveclosure.alloc) { err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; for (i = 0; i < dest_nodes->nelem; i++) { err = re_node_set_merge (&state->inveclosure, dfa->inveclosures + dest_nodes->elems[i]); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; } } return re_node_set_add_intersect (dest_nodes, candidates, &state->inveclosure); } static reg_errcode_t sub_epsilon_src_nodes (const re_dfa_t *dfa, Idx node, re_node_set *dest_nodes, const re_node_set *candidates) { Idx 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) { Idx cur_node = inv_eclosure->elems[ecl_idx]; if (cur_node == node) continue; if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) { Idx edst1 = dfa->edests[cur_node].elems[0]; Idx 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) { Idx cur_node = inv_eclosure->elems[ecl_idx]; if (!re_node_set_contains (&except_nodes, cur_node)) { Idx 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 bool check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx) { const re_dfa_t *const dfa = mctx->dfa; Idx lim_idx, src_pos, dst_pos; Idx dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); Idx src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) { Idx subexp_idx; struct re_backref_cache_entry *ent; ent = mctx->bkref_ents + limits->elems[lim_idx]; subexp_idx = dfa->nodes[ent->node].opr.idx; dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], subexp_idx, dst_node, dst_idx, dst_bkref_idx); src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], subexp_idx, src_node, src_idx, src_bkref_idx); /* In case of: ( ) ( ) ( ) */ if (src_pos == dst_pos) continue; /* This is unrelated limitation. */ else return true; } return false; } static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, Idx subexp_idx, Idx from_node, Idx bkref_idx) { const re_dfa_t *const dfa = mctx->dfa; const re_node_set *eclosures = dfa->eclosures + from_node; Idx node_idx; /* Else, we are on the boundary: examine the nodes on the epsilon closure. */ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) { Idx node = eclosures->elems[node_idx]; switch (dfa->nodes[node].type) { case OP_BACK_REF: if (bkref_idx != -1) { struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; do { Idx dst; int cpos; if (ent->node != node) continue; if (subexp_idx < BITSET_WORD_BITS && !(ent->eps_reachable_subexps_map & ((bitset_word_t) 1 << subexp_idx))) continue; /* Recurse trying to reach the OP_OPEN_SUBEXP and OP_CLOSE_SUBEXP cases below. But, if the destination node is the same node as the source node, don't recurse because it would cause an infinite loop: a regex that exhibits this behavior is ()\1*\1* */ dst = dfa->edests[node].elems[0]; if (dst == from_node) { if (boundaries & 1) return -1; else /* if (boundaries & 2) */ return 0; } cpos = check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, dst, bkref_idx); if (cpos == -1 /* && (boundaries & 1) */) return -1; if (cpos == 0 && (boundaries & 2)) return 0; if (subexp_idx < BITSET_WORD_BITS) ent->eps_reachable_subexps_map &= ~((bitset_word_t) 1 << subexp_idx); } while (ent++->more); } break; case OP_OPEN_SUBEXP: if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) return -1; break; case OP_CLOSE_SUBEXP: if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) return 0; break; default: break; } } return (boundaries & 2) ? 1 : 0; } static int check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, Idx subexp_idx, Idx from_node, Idx str_idx, Idx bkref_idx) { struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; int boundaries; /* If we are outside the range of the subexpression, return -1 or 1. */ if (str_idx < lim->subexp_from) return -1; if (lim->subexp_to < str_idx) return 1; /* If we are within the subexpression, return 0. */ boundaries = (str_idx == lim->subexp_from); boundaries |= (str_idx == lim->subexp_to) << 1; if (boundaries == 0) return 0; /* Else, examine epsilon closure. */ return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, from_node, bkref_idx); } /* 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 (const 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, Idx str_idx) { reg_errcode_t err; Idx node_idx, lim_idx; for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) { Idx 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; if (ent->subexp_to == str_idx) { Idx ops_node = -1; Idx cls_node = -1; for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) { Idx 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. */ if (cls_node >= 0) for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) { Idx 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) { Idx 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; /* 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 __attribute_warn_unused_result__ sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx str_idx, const re_node_set *candidates) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; Idx node_idx, node; re_sift_context_t local_sctx; Idx first_idx = search_cur_bkref_entry (mctx, str_idx); if (first_idx == -1) return REG_NOERROR; local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) { Idx enabled_idx; re_token_type_t type; struct re_backref_cache_entry *entry; node = candidates->elems[node_idx]; type = dfa->nodes[node].type; /* 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) continue; entry = mctx->bkref_ents + first_idx; enabled_idx = first_idx; do { Idx subexp_len; Idx to_idx; Idx dst_node; bool ok; re_dfastate_t *cur_state; 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 (mctx, &sctx->limits, node, str_idx, dst_node, to_idx)) continue; 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; ok = re_node_set_insert (&local_sctx.limits, enabled_idx); if (BE (! ok, 0)) { err = REG_ESPACE; goto free_return; } cur_state = local_sctx.sifted_states[str_idx]; err = sift_states_backward (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); /* mctx->bkref_ents may have changed, reload the pointer. */ entry = mctx->bkref_ents + enabled_idx; } while (enabled_idx++, entry++->more); } 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 (const re_match_context_t *mctx, re_sift_context_t *sctx, Idx node_idx, Idx str_idx, Idx max_str_idx) { const re_dfa_t *const dfa = mctx->dfa; int naccepted; /* Check the node can accept "multi byte". */ naccepted = check_node_accept_bytes (dfa, 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 thrown 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 * __attribute_warn_unused_result__ transit_state (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state) { re_dfastate_t **trtable; unsigned char ch; #ifdef RE_ENABLE_I18N /* If the current state can accept multibyte. */ if (BE (state->accept_mb, 0)) { *err = transit_state_mb (mctx, state); if (BE (*err != REG_NOERROR, 0)) return NULL; } #endif /* RE_ENABLE_I18N */ /* Then decide the next state with the single byte. */ #if 0 if (0) /* don't use transition table */ return transit_state_sb (err, mctx, state); #endif /* Use transition table */ ch = re_string_fetch_byte (&mctx->input); for (;;) { trtable = state->trtable; if (BE (trtable != NULL, 1)) return trtable[ch]; trtable = state->word_trtable; if (BE (trtable != NULL, 1)) { unsigned int context; context = re_string_context_at (&mctx->input, re_string_cur_idx (&mctx->input) - 1, mctx->eflags); if (IS_WORD_CONTEXT (context)) return trtable[ch + SBC_MAX]; else return trtable[ch]; } if (!build_trtable (mctx->dfa, state)) { *err = REG_ESPACE; return NULL; } /* Retry, we now have a transition table. */ } } /* Update the state_log if we need */ static re_dfastate_t * merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *next_state) { const re_dfa_t *const dfa = mctx->dfa; Idx cur_idx = re_string_cur_idx (&mctx->input); 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); 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); } if (BE (dfa->nbackref, 0) && next_state != NULL) { /* 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. */ *err = check_subexp_matching_top (mctx, &next_state->nodes, cur_idx); if (BE (*err != REG_NOERROR, 0)) return NULL; /* If the next state has back references. */ if (next_state->has_backref) { *err = transit_state_bkref (mctx, &next_state->nodes); if (BE (*err != REG_NOERROR, 0)) return NULL; next_state = mctx->state_log[cur_idx]; } } return next_state; } /* Skip bytes in the input that correspond to part of a multi-byte match, then look in the log for a state from which to restart matching. */ static re_dfastate_t * find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) { re_dfastate_t *cur_state; do { Idx max = mctx->state_log_top; Idx cur_str_idx = re_string_cur_idx (&mctx->input); do { if (++cur_str_idx > max) return NULL; re_string_skip_bytes (&mctx->input, 1); } while (mctx->state_log[cur_str_idx] == NULL); cur_state = merge_state_with_log (err, mctx, NULL); } while (*err == REG_NOERROR && cur_state == NULL); return cur_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 corresponding back references. */ static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, Idx str_idx) { const re_dfa_t *const dfa = mctx->dfa; Idx 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) { Idx node = cur_nodes->elems[node_idx]; if (dfa->nodes[node].type == OP_OPEN_SUBEXP && dfa->nodes[node].opr.idx < BITSET_WORD_BITS && (dfa->used_bkref_map & ((bitset_word_t) 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; } #if 0 /* 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 (reg_errcode_t *err, re_match_context_t *mctx, re_dfastate_t *state) { const re_dfa_t *const dfa = mctx->dfa; re_node_set next_nodes; re_dfastate_t *next_state; Idx 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) { Idx cur_node = state->nodes.elems[node_cnt]; if (check_node_accept (mctx, dfa->nodes + cur_node, 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; } } } context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); 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; } #endif #ifdef RE_ENABLE_I18N static reg_errcode_t transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; Idx i; for (i = 0; i < pstate->nodes.nelem; ++i) { re_node_set dest_nodes, *new_nodes; Idx cur_node_idx = pstate->nodes.elems[i]; int naccepted; Idx dest_idx; unsigned int context; re_dfastate_t *dest_state; if (!dfa->nodes[cur_node_idx].accept_mb) continue; if (dfa->nodes[cur_node_idx].constraint) { context = re_string_context_at (&mctx->input, re_string_cur_idx (&mctx->input), mctx->eflags); if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, context)) continue; } /* How many bytes the node can accept? */ naccepted = check_node_accept_bytes (dfa, 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_needed (mctx, dest_idx); if (BE (err != REG_NOERROR, 0)) return err; #ifdef DEBUG assert (dfa->nexts[cur_node_idx] != -1); #endif new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; 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); 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 (re_match_context_t *mctx, const re_node_set *nodes) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; Idx i; Idx cur_str_idx = re_string_cur_idx (&mctx->input); for (i = 0; i < nodes->nelem; ++i) { Idx dest_str_idx, prev_nelem, bkc_idx; Idx node_idx = nodes->elems[i]; unsigned int context; const 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); 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 (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) { Idx 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); 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 (mctx, new_dest_nodes, cur_str_idx); if (BE (err != REG_NOERROR, 0)) goto free_return; err = transit_state_bkref (mctx, new_dest_nodes); 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 __attribute_warn_unused_result__ get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx) { const re_dfa_t *const dfa = mctx->dfa; Idx subexp_num, sub_top_idx; const char *buf = (const char *) re_string_get_buffer (&mctx->input); /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ Idx cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); if (cache_idx != -1) { const struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx; do if (entry->node == bkref_node) return REG_NOERROR; /* We already checked it. */ while (entry++->more); } subexp_num = dfa->nodes[bkref_node].opr.idx; /* 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; Idx sub_last_idx, sl_str, bkref_str_off; if (dfa->nodes[sub_top->node].opr.idx != subexp_num) continue; /* It isn't related. */ sl_str = sub_top->str_idx; bkref_str_off = 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) { regoff_t 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) { if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) { /* Not enough chars for a successful match. */ if (bkref_str_off + sl_str_diff > mctx->input.len) break; err = clean_state_log_if_needed (mctx, bkref_str_off + sl_str_diff); if (BE (err != REG_NOERROR, 0)) return err; buf = (const char *) re_string_get_buffer (&mctx->input); } if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) /* We don't need to search this sub expression any more. */ break; } bkref_str_off += sl_str_diff; sl_str += sl_str_diff; err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, bkref_str_idx); /* Reload buf, since the preceding call might have reallocated the buffer. */ buf = (const char *) re_string_get_buffer (&mctx->input); 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) { Idx cls_node; regoff_t sl_str_off; const 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) { if (BE (bkref_str_off >= mctx->input.valid_len, 0)) { /* If we are at the end of the input, we cannot match. */ if (bkref_str_off >= mctx->input.len) break; err = extend_buffers (mctx, bkref_str_off + 1); if (BE (err != REG_NOERROR, 0)) return err; buf = (const char *) re_string_get_buffer (&mctx->input); } if (buf [bkref_str_off++] != buf[sl_str - 1]) 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, OP_CLOSE_SUBEXP); 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 (mctx, sub_top->path, sub_top->node, sub_top->str_idx, cls_node, sl_str, OP_CLOSE_SUBEXP); 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 (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 (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str) { reg_errcode_t err; Idx to_idx; /* Can the subexpression arrive the back reference? */ err = check_arrival (mctx, &sub_last->path, sub_last->node, sub_last->str_idx, bkref_node, bkref_str, OP_OPEN_SUBEXP); 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; return clean_state_log_if_needed (mctx, to_idx); } /* 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 Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, Idx subexp_idx, int type) { Idx cls_idx; for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) { Idx cls_node = nodes->elems[cls_idx]; const re_token_t *node = dfa->nodes + cls_node; if (node->type == type && 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 __attribute_warn_unused_result__ check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node, Idx top_str, Idx last_node, Idx last_str, int type) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err = REG_NOERROR; Idx 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 (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) { re_dfastate_t **new_array; Idx old_alloc = path->alloc; Idx incr_alloc = last_str + mctx->max_mb_elem_len + 1; Idx new_alloc; if (BE (IDX_MAX - old_alloc < incr_alloc, 0)) return REG_ESPACE; new_alloc = old_alloc + incr_alloc; if (BE (SIZE_MAX / sizeof (re_dfastate_t *) < new_alloc, 0)) return REG_ESPACE; new_array = re_realloc (path->array, re_dfastate_t *, new_alloc); if (BE (new_array == NULL, 0)) return REG_ESPACE; path->array = new_array; path->alloc = new_alloc; memset (new_array + old_alloc, '\0', sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); } str_idx = path->next_idx ? path->next_idx : top_str; /* 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); 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, type); 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 (mctx, &next_nodes, str_idx, subexp_num, type); 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 (mctx, str_idx, &cur_state->non_eps_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, type); if (BE (err != REG_NOERROR, 0)) { re_node_set_free (&next_nodes); return err; } err = expand_bkref_cache (mctx, &next_nodes, str_idx, subexp_num, type); 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); 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; /* Then check the current node set has the node LAST_NODE. */ if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) return REG_NOERROR; return 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 __attribute_warn_unused_result__ check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx, re_node_set *cur_nodes, re_node_set *next_nodes) { const re_dfa_t *const dfa = mctx->dfa; bool ok; Idx cur_idx; #ifdef RE_ENABLE_I18N reg_errcode_t err = REG_NOERROR; #endif 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; Idx cur_node = cur_nodes->elems[cur_idx]; #ifdef DEBUG re_token_type_t type = dfa->nodes[cur_node].type; assert (!IS_EPSILON_NODE (type)); #endif #ifdef RE_ENABLE_I18N /* If the node may accept "multi byte". */ if (dfa->nodes[cur_node].accept_mb) { naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, str_idx); if (naccepted > 1) { re_dfastate_t *dest_state; Idx next_node = dfa->nexts[cur_node]; Idx 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; } } ok = re_node_set_insert (&union_set, next_node); if (BE (! ok, 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 (mctx, dfa->nodes + cur_node, str_idx)) { ok = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); if (BE (! ok, 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 (const re_dfa_t *dfa, re_node_set *cur_nodes, Idx ex_subexp, int type) { reg_errcode_t err; Idx 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) { Idx cur_node = cur_nodes->elems[idx]; const re_node_set *eclosure = dfa->eclosures + cur_node; outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); 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, type); 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 __attribute_warn_unused_result__ check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, Idx target, Idx ex_subexp, int type) { Idx cur_node; for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) { bool ok; if (dfa->nodes[cur_node].type == type && dfa->nodes[cur_node].opr.idx == ex_subexp) { if (type == OP_CLOSE_SUBEXP) { ok = re_node_set_insert (dst_nodes, cur_node); if (BE (! ok, 0)) return REG_ESPACE; } break; } ok = re_node_set_insert (dst_nodes, cur_node); if (BE (! ok, 0)) return REG_ESPACE; if (dfa->edests[cur_node].nelem == 0) break; if (dfa->edests[cur_node].nelem == 2) { reg_errcode_t err; err = check_arrival_expand_ecl_sub (dfa, dst_nodes, dfa->edests[cur_node].elems[1], ex_subexp, type); 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 __attribute_warn_unused_result__ expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, Idx cur_str, Idx subexp_num, int type) { const re_dfa_t *const dfa = mctx->dfa; reg_errcode_t err; Idx cache_idx_start = search_cur_bkref_entry (mctx, cur_str); struct re_backref_cache_entry *ent; if (cache_idx_start == -1) return REG_NOERROR; restart: ent = mctx->bkref_ents + cache_idx_start; do { Idx to_idx, next_node; /* 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, type); 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... */ goto restart; } else { re_node_set union_set; next_node = dfa->nexts[ent->node]; if (mctx->state_log[to_idx]) { bool ok; 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); ok = re_node_set_insert (&union_set, next_node); if (BE (err != REG_NOERROR || ! ok, 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; } } while (ent++->more); return REG_NOERROR; } /* Build transition table for the state. Return true if successful. */ static bool build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) { reg_errcode_t err; Idx i, j; int ch; bool need_word_trtable = false; bitset_word_t elem, mask; bool dests_node_malloced = false; bool dest_states_malloced = false; Idx 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_t *dests_ch; bitset_t acceptable; struct dests_alloc { re_node_set dests_node[SBC_MAX]; bitset_t dests_ch[SBC_MAX]; } *dests_alloc; /* 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. */ if (__libc_use_alloca (sizeof (struct dests_alloc))) dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); else { dests_alloc = re_malloc (struct dests_alloc, 1); if (BE (dests_alloc == NULL, 0)) return false; dests_node_malloced = true; } dests_node = dests_alloc->dests_node; dests_ch = dests_alloc->dests_ch; /* Initialize transition table. */ state->word_trtable = state->trtable = NULL; /* At first, group all nodes belonging to 'state' into several destinations. */ ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); if (BE (ndests <= 0, 0)) { if (dests_node_malloced) re_free (dests_alloc); /* Return false in case of an error, true otherwise. */ if (ndests == 0) { state->trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); if (BE (state->trtable == NULL, 0)) return false; return true; } return false; } err = re_node_set_alloc (&follows, ndests + 1); if (BE (err != REG_NOERROR, 0)) goto out_free; /* Avoid arithmetic overflow in size calculation. */ if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX) / (3 * sizeof (re_dfastate_t *))) < ndests), 0)) goto out_free; if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX + ndests * 3 * sizeof (re_dfastate_t *))) dest_states = (re_dfastate_t **) alloca (ndests * 3 * sizeof (re_dfastate_t *)); else { dest_states = re_malloc (re_dfastate_t *, ndests * 3); if (BE (dest_states == NULL, 0)) { out_free: if (dest_states_malloced) re_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) re_free (dests_alloc); return false; } dest_states_malloced = true; } 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) { Idx 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; } } 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; if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) need_word_trtable = true; 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]); } if (!BE (need_word_trtable, 0)) { /* We don't care about whether the following character is a word character, or we are in a single-byte character set so we can discern by looking at the character code: allocate a 256-entry transition table. */ trtable = state->trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); if (BE (trtable == NULL, 0)) goto out_free; /* For all characters ch...: */ for (i = 0; i < BITSET_WORDS; ++i) for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; elem; mask <<= 1, elem >>= 1, ++ch) if (BE (elem & 1, 0)) { /* There must be exactly one destination which accepts character ch. See group_nodes_into_DFAstates. */ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) ; /* j-th destination accepts the word character ch. */ if (dfa->word_char[i] & mask) trtable[ch] = dest_states_word[j]; else trtable[ch] = dest_states[j]; } } else { /* We care about whether the following character is a word character, and we are in a multi-byte character set: discern by looking at the character code: build two 256-entry transition tables, one starting at trtable[0] and one starting at trtable[SBC_MAX]. */ trtable = state->word_trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); if (BE (trtable == NULL, 0)) goto out_free; /* For all characters ch...: */ for (i = 0; i < BITSET_WORDS; ++i) for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; elem; mask <<= 1, elem >>= 1, ++ch) if (BE (elem & 1, 0)) { /* There must be exactly one destination which accepts character ch. See group_nodes_into_DFAstates. */ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) ; /* j-th destination accepts the word character ch. */ trtable[ch] = dest_states[j]; trtable[ch + SBC_MAX] = dest_states_word[j]; } } /* new line */ if (bitset_contain (acceptable, NEWLINE_CHAR)) { /* The current state accepts newline character. */ for (j = 0; j < ndests; ++j) if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) { /* k-th destination accepts newline character. */ trtable[NEWLINE_CHAR] = dest_states_nl[j]; if (need_word_trtable) trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; /* There must be only one destination which accepts newline. See group_nodes_into_DFAstates. */ break; } } if (dest_states_malloced) re_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) re_free (dests_alloc); return true; } /* 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 Idx group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, re_node_set *dests_node, bitset_t *dests_ch) { reg_errcode_t err; bool ok; Idx i, j, k; Idx ndests; /* Number of the destinations from 'state'. */ bitset_t 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) { #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) bitset_merge (accepts, dfa->sb_char); else #endif bitset_set_all (accepts); if (!(dfa->syntax & RE_DOT_NEWLINE)) bitset_clear (accepts, '\n'); if (dfa->syntax & RE_DOT_NOT_NULL) bitset_clear (accepts, '\0'); } #ifdef RE_ENABLE_I18N else if (type == OP_UTF8_PERIOD) { if (ASCII_CHARS % BITSET_WORD_BITS == 0) memset (accepts, -1, ASCII_CHARS / CHAR_BIT); else bitset_merge (accepts, utf8_sb_map); if (!(dfa->syntax & RE_DOT_NEWLINE)) bitset_clear (accepts, '\n'); if (dfa->syntax & RE_DOT_NOT_NULL) bitset_clear (accepts, '\0'); } #endif else continue; /* Check the 'accepts' and sift the characters which are not match it the context. */ if (constraint) { if (constraint & NEXT_NEWLINE_CONSTRAINT) { bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); bitset_empty (accepts); if (accepts_newline) bitset_set (accepts, NEWLINE_CHAR); else continue; } if (constraint & NEXT_ENDBUF_CONSTRAINT) { bitset_empty (accepts); continue; } if (constraint & NEXT_WORD_CONSTRAINT) { bitset_word_t any_set = 0; if (type == CHARACTER && !node->word_char) { bitset_empty (accepts); continue; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); else #endif for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= dfa->word_char[j]); if (!any_set) continue; } if (constraint & NEXT_NOTWORD_CONSTRAINT) { bitset_word_t any_set = 0; if (type == CHARACTER && node->word_char) { bitset_empty (accepts); continue; } #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); else #endif for (j = 0; j < BITSET_WORDS; ++j) any_set |= (accepts[j] &= ~dfa->word_char[j]); if (!any_set) continue; } } /* Then divide 'accepts' into DFA states, or create a new state. Above, we make sure that accepts is not empty. */ for (j = 0; j < ndests; ++j) { bitset_t intersec; /* Intersection sets, see below. */ bitset_t remains; /* Flags, see below. */ bitset_word_t 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_WORDS; ++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_WORDS; ++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. */ ok = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); if (BE (! ok, 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. */ # ifdef _LIBC # include # endif static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, const re_string_t *input, Idx str_idx) { const re_token_t *node = dfa->nodes + node_idx; int char_len, elem_len; Idx i; if (BE (node->type == OP_UTF8_PERIOD, 0)) { unsigned char c = re_string_byte_at (input, str_idx), d; if (BE (c < 0xc2, 1)) return 0; if (str_idx + 2 > input->len) return 0; d = re_string_byte_at (input, str_idx + 1); if (c < 0xe0) return (d < 0x80 || d > 0xbf) ? 0 : 2; else if (c < 0xf0) { char_len = 3; if (c == 0xe0 && d < 0xa0) return 0; } else if (c < 0xf8) { char_len = 4; if (c == 0xf0 && d < 0x90) return 0; } else if (c < 0xfc) { char_len = 5; if (c == 0xf8 && d < 0x88) return 0; } else if (c < 0xfe) { char_len = 6; if (c == 0xfc && d < 0x84) return 0; } else return 0; if (str_idx + char_len > input->len) return 0; for (i = 1; i < char_len; ++i) { d = re_string_byte_at (input, str_idx + i); if (d < 0x80 || d > 0xbf) return 0; } return char_len; } char_len = re_string_char_size_at (input, str_idx); if (node->type == OP_PERIOD) { if (char_len <= 1) return 0; /* FIXME: I don't think this if is needed, as both '\n' and '\0' are char_len == 1. */ /* '.' accepts any one character except the following two cases. */ if ((!(dfa->syntax & RE_DOT_NEWLINE) && re_string_byte_at (input, str_idx) == '\n') || ((dfa->syntax & RE_DOT_NOT_NULL) && re_string_byte_at (input, str_idx) == '\0')) return 0; return char_len; } elem_len = re_string_elem_size_at (input, str_idx); if ((elem_len <= 1 && char_len <= 1) || char_len == 0) return 0; if (node->type == COMPLEX_BRACKET) { const re_charset_t *cset = node->opr.mbcset; # ifdef _LIBC const unsigned char *pin = ((const unsigned char *) re_string_get_buffer (input) + str_idx); Idx j; uint32_t nrules; # 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 nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); if (nrules != 0) { unsigned int in_collseq = 0; const int32_t *table, *indirect; const unsigned char *weights, *extra; const char *collseqwc; /* 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? */ /* FIXME: Implement rational ranges here, too. */ 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); int32_t idx = findidx (table, indirect, extra, &cp, elem_len); int32_t rule = idx >> 24; idx &= 0xffffff; if (idx > 0) { size_t weight_len = weights[idx]; for (i = 0; i < cset->nequiv_classes; ++i) { int32_t equiv_class_idx = cset->equiv_classes[i]; int32_t equiv_class_rule = equiv_class_idx >> 24; equiv_class_idx &= 0xffffff; if (weights[equiv_class_idx] == weight_len && equiv_class_rule == rule && memcmp (weights + idx + 1, weights + equiv_class_idx + 1, weight_len) == 0) { match_len = elem_len; goto check_node_accept_bytes_match; } } } } } else # endif /* _LIBC */ { /* match with range expression? */ for (i = 0; i < cset->nranges; ++i) { if (cset->range_starts[i] <= wc && wc <= cset->range_ends[i]) { 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 (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); int32_t extrasize = (const unsigned char *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; for (idx = 0; idx < extrasize;) { int mbs_cnt; bool found = false; 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 = true; } /* 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) * (*(int32_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); } return UINT_MAX; } } # endif /* _LIBC */ #endif /* RE_ENABLE_I18N */ /* Check whether the node accepts the byte which is IDX-th byte of the INPUT. */ static bool check_node_accept (const re_match_context_t *mctx, const re_token_t *node, Idx idx) { unsigned char ch; ch = re_string_byte_at (&mctx->input, idx); switch (node->type) { case CHARACTER: if (node->opr.c != ch) return false; break; case SIMPLE_BRACKET: if (!bitset_contain (node->opr.sbcset, ch)) return false; break; #ifdef RE_ENABLE_I18N case OP_UTF8_PERIOD: if (ch >= ASCII_CHARS) return false; FALLTHROUGH; #endif case OP_PERIOD: if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) return false; break; default: return false; } 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); if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) return false; } return true; } /* Extend the buffers, if the buffers have run out. */ static reg_errcode_t __attribute_warn_unused_result__ extend_buffers (re_match_context_t *mctx, int min_len) { reg_errcode_t ret; re_string_t *pstr = &mctx->input; /* Avoid overflow. */ if (BE (MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *)) / 2 <= pstr->bufs_len, 0)) return REG_ESPACE; /* Double the lengths of the buffers, but allocate at least MIN_LEN. */ ret = re_string_realloc_buffers (pstr, MAX (min_len, MIN (pstr->len, pstr->bufs_len * 2))); if (BE (ret != REG_NOERROR, 0)) return ret; if (mctx->state_log != NULL) { /* And double the length of state_log. */ /* XXX We have no indication of the size of this buffer. If this allocation fail we have no indication that the state_log array does not have the right size. */ re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, pstr->bufs_len + 1); 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 (pstr->mb_cur_max > 1) { ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; } else #endif /* RE_ENABLE_I18N */ build_upper_buffer (pstr); } else { #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) build_wcs_buffer (pstr); else #endif /* RE_ENABLE_I18N */ { if (pstr->trans != NULL) re_string_translate_buffer (pstr); } } return REG_NOERROR; } /* Functions for matching context. */ /* Initialize MCTX. */ static reg_errcode_t __attribute_warn_unused_result__ match_ctx_init (re_match_context_t *mctx, int eflags, Idx n) { mctx->eflags = eflags; mctx->match_last = -1; if (n > 0) { /* Avoid overflow. */ size_t max_object_size = MAX (sizeof (struct re_backref_cache_entry), sizeof (re_sub_match_top_t *)); if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < n, 0)) return REG_ESPACE; 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; } /* Already zero-ed by the caller. else mctx->bkref_ents = NULL; mctx->nbkref_ents = 0; mctx->nsub_tops = 0; */ mctx->abkref_ents = n; mctx->max_mb_elem_len = 1; 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 (re_match_context_t *mctx) { Idx st_idx; for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) { Idx 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); } re_free (top); } mctx->nsub_tops = 0; mctx->nbkref_ents = 0; } /* Free all the memory associated with MCTX. */ static void match_ctx_free (re_match_context_t *mctx) { /* First, free all the memory associated with MCTX->SUB_TOPS. */ match_ctx_clean (mctx); re_free (mctx->sub_tops); re_free (mctx->bkref_ents); } /* 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 __attribute_warn_unused_result__ match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from, Idx 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; } if (mctx->nbkref_ents > 0 && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; 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; /* This is a cache that saves negative results of check_dst_limits_calc_pos. If bit N is clear, means that this entry won't epsilon-transition to an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If it is set, check_dst_limits_calc_pos_1 will recurse and try to find one such node. A backreference does not epsilon-transition unless it is empty, so set to all zeros if FROM != TO. */ mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map = (from == to ? -1 : 0); mctx->bkref_ents[mctx->nbkref_ents++].more = 0; if (mctx->max_mb_elem_len < to - from) mctx->max_mb_elem_len = to - from; return REG_NOERROR; } /* Return the first entry with the same str_idx, or -1 if none is found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx) { Idx left, right, mid, last; last = 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; } if (left < last && mctx->bkref_ents[left].str_idx == str_idx) return left; else return -1; } /* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches at STR_IDX. */ static reg_errcode_t __attribute_warn_unused_result__ match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx) { #ifdef DEBUG assert (mctx->sub_tops != NULL); assert (mctx->asub_tops > 0); #endif if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) { Idx new_asub_tops = mctx->asub_tops * 2; re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, re_sub_match_top_t *, new_asub_tops); if (BE (new_array == NULL, 0)) return REG_ESPACE; mctx->sub_tops = new_array; mctx->asub_tops = new_asub_tops; } mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) 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 (re_sub_match_top_t *subtop, Idx node, Idx str_idx) { re_sub_match_last_t *new_entry; if (BE (subtop->nlasts == subtop->alasts, 0)) { Idx new_alasts = 2 * subtop->alasts + 1; re_sub_match_last_t **new_array = re_realloc (subtop->lasts, re_sub_match_last_t *, new_alasts); if (BE (new_array == NULL, 0)) return NULL; subtop->lasts = new_array; subtop->alasts = new_alasts; } new_entry = calloc (1, sizeof (re_sub_match_last_t)); if (BE (new_entry != NULL, 1)) { 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 (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx) { sctx->sifted_states = sifted_sts; sctx->limited_states = limited_sts; sctx->last_node = last_node; sctx->last_str_idx = last_str_idx; re_node_set_init_empty (&sctx->limits); } smartmontools-7.0/regex/regex_internal.c0000644000175000010010000013632513336323570015512 00000000000000/* Extended regular expression matching and search library. Copyright (C) 2002-2018 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, see . */ static void re_string_construct_common (const char *str, Idx len, re_string_t *pstr, RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa); static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, re_hashval_t hash); static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context, re_hashval_t hash); static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len); #ifdef RE_ENABLE_I18N static void build_wcs_buffer (re_string_t *pstr); static reg_errcode_t 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 unsigned int re_string_context_at (const re_string_t *input, Idx idx, int eflags) __attribute__ ((pure)); /* 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 __attribute_warn_unused_result__ re_string_allocate (re_string_t *pstr, const char *str, Idx len, Idx init_len, RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) { reg_errcode_t ret; Idx init_buf_len; /* Ensure at least one character fits into the buffers. */ if (init_len < dfa->mb_cur_max) init_len = dfa->mb_cur_max; init_buf_len = (len + 1 < init_len) ? len + 1: init_len; re_string_construct_common (str, len, pstr, trans, icase, dfa); ret = re_string_realloc_buffers (pstr, init_buf_len); if (BE (ret != REG_NOERROR, 0)) return ret; pstr->word_char = dfa->word_char; pstr->word_ops_used = dfa->word_ops_used; pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; pstr->valid_raw_len = pstr->valid_len; return REG_NOERROR; } /* This function allocate the buffers, and initialize them. */ static reg_errcode_t __attribute_warn_unused_result__ re_string_construct (re_string_t *pstr, const char *str, Idx len, RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) { reg_errcode_t ret; memset (pstr, '\0', sizeof (re_string_t)); re_string_construct_common (str, len, pstr, trans, icase, dfa); if (len > 0) { ret = re_string_realloc_buffers (pstr, len + 1); if (BE (ret != REG_NOERROR, 0)) return ret; } pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; if (icase) { #ifdef RE_ENABLE_I18N if (dfa->mb_cur_max > 1) { while (1) { ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; if (pstr->valid_raw_len >= len) break; if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) break; ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); if (BE (ret != REG_NOERROR, 0)) return ret; } } else #endif /* RE_ENABLE_I18N */ build_upper_buffer (pstr); } else { #ifdef RE_ENABLE_I18N if (dfa->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 = pstr->bufs_len; pstr->valid_raw_len = pstr->bufs_len; } } } return REG_NOERROR; } /* Helper functions for re_string_allocate, and re_string_construct. */ static reg_errcode_t __attribute_warn_unused_result__ re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len) { #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { wint_t *new_wcs; /* Avoid overflow in realloc. */ const size_t max_object_size = MAX (sizeof (wint_t), sizeof (Idx)); if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < new_buf_len, 0)) return REG_ESPACE; new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); if (BE (new_wcs == NULL, 0)) return REG_ESPACE; pstr->wcs = new_wcs; if (pstr->offsets != NULL) { Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len); if (BE (new_offsets == NULL, 0)) return REG_ESPACE; pstr->offsets = new_offsets; } } #endif /* RE_ENABLE_I18N */ if (pstr->mbs_allocated) { unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, new_buf_len); if (BE (new_mbs == NULL, 0)) return REG_ESPACE; pstr->mbs = new_mbs; } pstr->bufs_len = new_buf_len; return REG_NOERROR; } static void re_string_construct_common (const char *str, Idx len, re_string_t *pstr, RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) { pstr->raw_mbs = (const unsigned char *) str; pstr->len = len; pstr->raw_len = len; pstr->trans = trans; pstr->icase = icase; pstr->mbs_allocated = (trans != NULL || icase); pstr->mb_cur_max = dfa->mb_cur_max; pstr->is_utf8 = dfa->is_utf8; pstr->map_notascii = dfa->map_notascii; pstr->stop = pstr->len; pstr->raw_stop = pstr->stop; } #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 (re_string_t *pstr) { #ifdef _LIBC unsigned char buf[MB_LEN_MAX]; assert (MB_LEN_MAX >= pstr->mb_cur_max); #else unsigned char buf[64]; #endif mbstate_t prev_st; Idx byte_idx, end_idx, remain_len; size_t mbclen; /* 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; const char *p; remain_len = end_idx - byte_idx; prev_st = pstr->cur_state; /* Apply the translation if we need. */ if (BE (pstr->trans != NULL, 0)) { int i, ch; for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) { ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; } p = (const char *) buf; } else p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); if (BE (mbclen == (size_t) -1 || mbclen == 0 || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len), 0)) { /* We treat these cases as a singlebyte character. */ mbclen = 1; wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; if (BE (pstr->trans != NULL, 0)) wc = pstr->trans[wc]; pstr->cur_state = prev_st; } else if (BE (mbclen == (size_t) -2, 0)) { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } /* 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; pstr->valid_raw_len = byte_idx; } /* Build wide character buffer PSTR->WCS like build_wcs_buffer, but for REG_ICASE. */ static reg_errcode_t __attribute_warn_unused_result__ build_wcs_upper_buffer (re_string_t *pstr) { mbstate_t prev_st; Idx src_idx, byte_idx, end_idx, remain_len; size_t mbclen; #ifdef _LIBC char buf[MB_LEN_MAX]; assert (MB_LEN_MAX >= pstr->mb_cur_max); #else char buf[64]; #endif byte_idx = pstr->valid_len; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; /* The following optimization assumes that ASCII characters can be mapped to wide characters with a simple cast. */ if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) { while (byte_idx < end_idx) { wchar_t wc; if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) && mbsinit (&pstr->cur_state)) { /* In case of a singlebyte character. */ pstr->mbs[byte_idx] = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); /* The next step uses the assumption that wchar_t is encoded ASCII-safe: all ASCII values can be converted like this. */ pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; ++byte_idx; continue; } 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, 1)) { wchar_t wcu = __towupper (wc); if (wcu != wc) { size_t mbcdlen; mbcdlen = __wcrtomb (buf, wcu, &prev_st); if (BE (mbclen == mbcdlen, 1)) memcpy (pstr->mbs + byte_idx, buf, mbclen); else { src_idx = byte_idx; goto offsets_needed; } } else memcpy (pstr->mbs + byte_idx, pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); pstr->wcs[byte_idx++] = wcu; /* Write paddings. */ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) pstr->wcs[byte_idx++] = WEOF; } else if (mbclen == (size_t) -1 || mbclen == 0 || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len)) { /* It is an invalid character, an incomplete character at the end of the string, or '\0'. Just use the byte. */ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; pstr->mbs[byte_idx] = ch; /* And also cast it to wide char. */ pstr->wcs[byte_idx++] = (wchar_t) ch; if (BE (mbclen == (size_t) -1, 0)) pstr->cur_state = prev_st; } else { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } } pstr->valid_len = byte_idx; pstr->valid_raw_len = byte_idx; return REG_NOERROR; } else for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) { wchar_t wc; const char *p; offsets_needed: remain_len = end_idx - byte_idx; prev_st = pstr->cur_state; if (BE (pstr->trans != NULL, 0)) { int i, ch; for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) { ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; buf[i] = pstr->trans[ch]; } p = (const char *) buf; } else p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); if (BE (mbclen < (size_t) -2, 1)) { wchar_t wcu = __towupper (wc); if (wcu != wc) { size_t mbcdlen; mbcdlen = __wcrtomb ((char *) buf, wcu, &prev_st); if (BE (mbclen == mbcdlen, 1)) memcpy (pstr->mbs + byte_idx, buf, mbclen); else if (mbcdlen != (size_t) -1) { size_t i; if (byte_idx + mbcdlen > pstr->bufs_len) { pstr->cur_state = prev_st; break; } if (pstr->offsets == NULL) { pstr->offsets = re_malloc (Idx, pstr->bufs_len); if (pstr->offsets == NULL) return REG_ESPACE; } if (!pstr->offsets_needed) { for (i = 0; i < (size_t) byte_idx; ++i) pstr->offsets[i] = i; pstr->offsets_needed = 1; } memcpy (pstr->mbs + byte_idx, buf, mbcdlen); pstr->wcs[byte_idx] = wcu; pstr->offsets[byte_idx] = src_idx; for (i = 1; i < mbcdlen; ++i) { pstr->offsets[byte_idx + i] = src_idx + (i < mbclen ? i : mbclen - 1); pstr->wcs[byte_idx + i] = WEOF; } pstr->len += mbcdlen - mbclen; if (pstr->raw_stop > src_idx) pstr->stop += mbcdlen - mbclen; end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; byte_idx += mbcdlen; src_idx += mbclen; continue; } else memcpy (pstr->mbs + byte_idx, p, mbclen); } else memcpy (pstr->mbs + byte_idx, p, mbclen); if (BE (pstr->offsets_needed != 0, 0)) { size_t i; for (i = 0; i < mbclen; ++i) pstr->offsets[byte_idx + i] = src_idx + i; } src_idx += mbclen; pstr->wcs[byte_idx++] = wcu; /* Write paddings. */ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) pstr->wcs[byte_idx++] = WEOF; } else if (mbclen == (size_t) -1 || mbclen == 0 || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len)) { /* It is an invalid character or '\0'. Just use the byte. */ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; if (BE (pstr->trans != NULL, 0)) ch = pstr->trans [ch]; pstr->mbs[byte_idx] = ch; if (BE (pstr->offsets_needed != 0, 0)) pstr->offsets[byte_idx] = src_idx; ++src_idx; /* And also cast it to wide char. */ pstr->wcs[byte_idx++] = (wchar_t) ch; if (BE (mbclen == (size_t) -1, 0)) pstr->cur_state = prev_st; } else { /* The buffer doesn't have enough space, finish to build. */ pstr->cur_state = prev_st; break; } } pstr->valid_len = byte_idx; pstr->valid_raw_len = src_idx; return REG_NOERROR; } /* Skip characters until the index becomes greater than NEW_RAW_IDX. Return the index. */ static Idx re_string_skip_chars (re_string_t *pstr, Idx new_raw_idx, wint_t *last_wc) { mbstate_t prev_st; Idx rawbuf_idx; size_t mbclen; wint_t wc = WEOF; /* Skip the characters which are not necessary to check. */ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; rawbuf_idx < new_raw_idx;) { wchar_t wc2; Idx remain_len = pstr->raw_len - rawbuf_idx; prev_st = pstr->cur_state; mbclen = __mbrtowc (&wc2, (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 single byte character. */ if (mbclen == 0 || remain_len == 0) wc = L'\0'; else wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); mbclen = 1; pstr->cur_state = prev_st; } else wc = wc2; /* Then proceed the next character. */ rawbuf_idx += mbclen; } *last_wc = 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 (re_string_t *pstr) { Idx 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 (BE (pstr->trans != NULL, 0)) ch = pstr->trans[ch]; pstr->mbs[char_idx] = toupper (ch); } pstr->valid_len = char_idx; pstr->valid_raw_len = char_idx; } /* Apply TRANS to the buffer in PSTR. */ static void re_string_translate_buffer (re_string_t *pstr) { Idx 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[buf_idx] = pstr->trans[ch]; } pstr->valid_len = buf_idx; pstr->valid_raw_len = buf_idx; } /* This function re-construct the buffers. Concretely, convert to wide character in case of pstr->mb_cur_max > 1, convert to upper case in case of REG_ICASE, apply translation. */ static reg_errcode_t __attribute_warn_unused_result__ re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags) { Idx offset; if (BE (pstr->raw_mbs_idx <= idx, 0)) offset = idx - pstr->raw_mbs_idx; else { /* Reset buffer. */ #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); #endif /* RE_ENABLE_I18N */ pstr->len = pstr->raw_len; pstr->stop = pstr->raw_stop; pstr->valid_len = 0; pstr->raw_mbs_idx = 0; pstr->valid_raw_len = 0; pstr->offsets_needed = 0; pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF : CONTEXT_NEWLINE | CONTEXT_BEGBUF); if (!pstr->mbs_allocated) pstr->mbs = (unsigned char *) pstr->raw_mbs; offset = idx; } if (BE (offset != 0, 1)) { /* Should the already checked characters be kept? */ if (BE (offset < pstr->valid_raw_len, 1)) { /* Yes, move them to the front of the buffer. */ #ifdef RE_ENABLE_I18N if (BE (pstr->offsets_needed, 0)) { Idx low = 0, high = pstr->valid_len, mid; do { mid = (high + low) / 2; if (pstr->offsets[mid] > offset) high = mid; else if (pstr->offsets[mid] < offset) low = mid + 1; else break; } while (low < high); if (pstr->offsets[mid] < offset) ++mid; pstr->tip_context = re_string_context_at (pstr, mid - 1, eflags); /* This can be quite complicated, so handle specially only the common and easy case where the character with different length representation of lower and upper case is present at or after offset. */ if (pstr->valid_len > offset && mid == offset && pstr->offsets[mid] == offset) { memmove (pstr->wcs, pstr->wcs + offset, (pstr->valid_len - offset) * sizeof (wint_t)); memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); pstr->valid_len -= offset; pstr->valid_raw_len -= offset; for (low = 0; low < pstr->valid_len; low++) pstr->offsets[low] = pstr->offsets[low + offset] - offset; } else { /* Otherwise, just find out how long the partial multibyte character at offset is and fill it with WEOF/255. */ pstr->len = pstr->raw_len - idx + offset; pstr->stop = pstr->raw_stop - idx + offset; pstr->offsets_needed = 0; while (mid > 0 && pstr->offsets[mid - 1] == offset) --mid; while (mid < pstr->valid_len) if (pstr->wcs[mid] != WEOF) break; else ++mid; if (mid == pstr->valid_len) pstr->valid_len = 0; else { pstr->valid_len = pstr->offsets[mid] - offset; if (pstr->valid_len) { for (low = 0; low < pstr->valid_len; ++low) pstr->wcs[low] = WEOF; memset (pstr->mbs, 255, pstr->valid_len); } } pstr->valid_raw_len = pstr->valid_len; } } else #endif { pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags); #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) memmove (pstr->wcs, pstr->wcs + offset, (pstr->valid_len - offset) * sizeof (wint_t)); #endif /* RE_ENABLE_I18N */ if (BE (pstr->mbs_allocated, 0)) memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); pstr->valid_len -= offset; pstr->valid_raw_len -= offset; #if defined DEBUG && DEBUG assert (pstr->valid_len > 0); #endif } } else { #ifdef RE_ENABLE_I18N /* No, skip all characters until IDX. */ Idx prev_valid_len = pstr->valid_len; if (BE (pstr->offsets_needed, 0)) { pstr->len = pstr->raw_len - idx + offset; pstr->stop = pstr->raw_stop - idx + offset; pstr->offsets_needed = 0; } #endif pstr->valid_len = 0; #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { Idx wcs_idx; wint_t wc = WEOF; if (pstr->is_utf8) { const unsigned char *raw, *p, *end; /* Special case UTF-8. Multi-byte chars start with any byte other than 0x80 - 0xbf. */ raw = pstr->raw_mbs + pstr->raw_mbs_idx; end = raw + (offset - pstr->mb_cur_max); if (end < pstr->raw_mbs) end = pstr->raw_mbs; p = raw + offset - 1; #ifdef _LIBC /* We know the wchar_t encoding is UCS4, so for the simple case, ASCII characters, skip the conversion step. */ if (isascii (*p) && BE (pstr->trans == NULL, 1)) { memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); /* pstr->valid_len = 0; */ wc = (wchar_t) *p; } else #endif for (; p >= end; --p) if ((*p & 0xc0) != 0x80) { mbstate_t cur_state; wchar_t wc2; Idx mlen = raw + pstr->len - p; unsigned char buf[6]; size_t mbclen; const unsigned char *pp = p; if (BE (pstr->trans != NULL, 0)) { int i = mlen < 6 ? mlen : 6; while (--i >= 0) buf[i] = pstr->trans[p[i]]; pp = buf; } /* XXX Don't use mbrtowc, we know which conversion to use (UTF-8 -> UCS4). */ memset (&cur_state, 0, sizeof (cur_state)); mbclen = __mbrtowc (&wc2, (const char *) pp, mlen, &cur_state); if (raw + offset - p <= mbclen && mbclen < (size_t) -2) { memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); pstr->valid_len = mbclen - (raw + offset - p); wc = wc2; } break; } } if (wc == WEOF) pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; if (wc == WEOF) pstr->tip_context = re_string_context_at (pstr, prev_valid_len - 1, eflags); else pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) ? CONTEXT_WORD : ((IS_WIDE_NEWLINE (wc) && pstr->newline_anchor) ? CONTEXT_NEWLINE : 0)); if (BE (pstr->valid_len, 0)) { for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) pstr->wcs[wcs_idx] = WEOF; if (pstr->mbs_allocated) memset (pstr->mbs, 255, pstr->valid_len); } pstr->valid_raw_len = pstr->valid_len; } else #endif /* RE_ENABLE_I18N */ { int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; pstr->valid_raw_len = 0; if (pstr->trans) c = pstr->trans[c]; pstr->tip_context = (bitset_contain (pstr->word_char, c) ? CONTEXT_WORD : ((IS_NEWLINE (c) && pstr->newline_anchor) ? CONTEXT_NEWLINE : 0)); } } if (!BE (pstr->mbs_allocated, 0)) pstr->mbs += offset; } pstr->raw_mbs_idx = idx; pstr->len -= offset; pstr->stop -= offset; /* Then build the buffers. */ #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1) { if (pstr->icase) { reg_errcode_t ret = build_wcs_upper_buffer (pstr); if (BE (ret != REG_NOERROR, 0)) return ret; } else build_wcs_buffer (pstr); } else #endif /* RE_ENABLE_I18N */ if (BE (pstr->mbs_allocated, 0)) { if (pstr->icase) build_upper_buffer (pstr); else if (pstr->trans != NULL) re_string_translate_buffer (pstr); } else pstr->valid_len = pstr->len; pstr->cur_idx = 0; return REG_NOERROR; } static unsigned char __attribute__ ((pure)) re_string_peek_byte_case (const re_string_t *pstr, Idx idx) { int ch; Idx off; /* Handle the common (easiest) cases first. */ if (BE (!pstr->mbs_allocated, 1)) return re_string_peek_byte (pstr, idx); #ifdef RE_ENABLE_I18N if (pstr->mb_cur_max > 1 && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) return re_string_peek_byte (pstr, idx); #endif off = pstr->cur_idx + idx; #ifdef RE_ENABLE_I18N if (pstr->offsets_needed) off = pstr->offsets[off]; #endif ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; #ifdef RE_ENABLE_I18N /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I this function returns CAPITAL LETTER I instead of first byte of DOTLESS SMALL LETTER I. The latter would confuse the parser, since peek_byte_case doesn't advance cur_idx in any way. */ if (pstr->offsets_needed && !isascii (ch)) return re_string_peek_byte (pstr, idx); #endif return ch; } static unsigned char re_string_fetch_byte_case (re_string_t *pstr) { if (BE (!pstr->mbs_allocated, 1)) return re_string_fetch_byte (pstr); #ifdef RE_ENABLE_I18N if (pstr->offsets_needed) { Idx off; int ch; /* For tr_TR.UTF-8 [[:islower:]] there is [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip in that case the whole multi-byte character and return the original letter. On the other side, with [[: DOTLESS SMALL LETTER I return [[:I, as doing anything else would complicate things too much. */ if (!re_string_first_byte (pstr, pstr->cur_idx)) return re_string_fetch_byte (pstr); off = pstr->offsets[pstr->cur_idx]; ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; if (! isascii (ch)) return re_string_fetch_byte (pstr); re_string_skip_bytes (pstr, re_string_char_size_at (pstr, pstr->cur_idx)); return ch; } #endif return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; } static void re_string_destruct (re_string_t *pstr) { #ifdef RE_ENABLE_I18N re_free (pstr->wcs); re_free (pstr->offsets); #endif /* RE_ENABLE_I18N */ if (pstr->mbs_allocated) re_free (pstr->mbs); } /* Return the context at IDX in INPUT. */ static unsigned int re_string_context_at (const re_string_t *input, Idx idx, int eflags) { int c; if (BE (idx < 0, 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; if (BE (idx == input->len, 0)) return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF : CONTEXT_NEWLINE | CONTEXT_ENDBUF); #ifdef RE_ENABLE_I18N if (input->mb_cur_max > 1) { wint_t wc; Idx wc_idx = idx; while(input->wcs[wc_idx] == WEOF) { #if defined DEBUG && 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 (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) return CONTEXT_WORD; return (IS_WIDE_NEWLINE (wc) && input->newline_anchor ? CONTEXT_NEWLINE : 0); } else #endif { c = re_string_byte_at (input, idx); if (bitset_contain (input->word_char, c)) return CONTEXT_WORD; return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; } } /* Functions for set operation. */ static reg_errcode_t __attribute_warn_unused_result__ re_node_set_alloc (re_node_set *set, Idx size) { set->alloc = size; set->nelem = 0; set->elems = re_malloc (Idx, size); if (BE (set->elems == NULL, 0) && (MALLOC_0_IS_NONNULL || size != 0)) return REG_ESPACE; return REG_NOERROR; } static reg_errcode_t __attribute_warn_unused_result__ re_node_set_init_1 (re_node_set *set, Idx elem) { set->alloc = 1; set->nelem = 1; set->elems = re_malloc (Idx, 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 __attribute_warn_unused_result__ re_node_set_init_2 (re_node_set *set, Idx elem1, Idx elem2) { set->alloc = 2; set->elems = re_malloc (Idx, 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 __attribute_warn_unused_result__ re_node_set_init_copy (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 (Idx, dest->alloc); if (BE (dest->elems == NULL, 0)) { dest->alloc = dest->nelem = 0; return REG_ESPACE; } memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); } 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 __attribute_warn_unused_result__ re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, const re_node_set *src2) { Idx i1, i2, is, id, delta, sbase; if (src1->nelem == 0 || src2->nelem == 0) return REG_NOERROR; /* We need dest->nelem + 2 * elems_in_intersection; this is a conservative estimate. */ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) { Idx new_alloc = src1->nelem + src2->nelem + dest->alloc; Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc); if (BE (new_elems == NULL, 0)) return REG_ESPACE; dest->elems = new_elems; dest->alloc = new_alloc; } /* Find the items in the intersection of SRC1 and SRC2, and copy into the top of DEST those that are not already in DEST itself. */ sbase = dest->nelem + src1->nelem + src2->nelem; i1 = src1->nelem - 1; i2 = src2->nelem - 1; id = dest->nelem - 1; for (;;) { if (src1->elems[i1] == src2->elems[i2]) { /* Try to find the item in DEST. Maybe we could binary search? */ while (id >= 0 && dest->elems[id] > src1->elems[i1]) --id; if (id < 0 || dest->elems[id] != src1->elems[i1]) dest->elems[--sbase] = src1->elems[i1]; if (--i1 < 0 || --i2 < 0) break; } /* Lower the highest of the two items. */ else if (src1->elems[i1] < src2->elems[i2]) { if (--i2 < 0) break; } else { if (--i1 < 0) break; } } id = dest->nelem - 1; is = dest->nelem + src1->nelem + src2->nelem - 1; delta = is - sbase + 1; /* Now copy. When DELTA becomes zero, the remaining DEST elements are already in place; this is more or less the same loop that is in re_node_set_merge. */ dest->nelem += delta; if (delta > 0 && id >= 0) for (;;) { if (dest->elems[is] > dest->elems[id]) { /* Copy from the top. */ dest->elems[id + delta--] = dest->elems[is--]; if (delta == 0) break; } else { /* Slide from the bottom. */ dest->elems[id + delta] = dest->elems[id]; if (--id < 0) break; } } /* Copy remaining SRC elements. */ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx)); 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 __attribute_warn_unused_result__ re_node_set_init_union (re_node_set *dest, const re_node_set *src1, const re_node_set *src2) { Idx i1, i2, id; if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) { dest->alloc = src1->nelem + src2->nelem; dest->elems = re_malloc (Idx, 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 (Idx)); id += src1->nelem - i1; } else if (i2 < src2->nelem) { memcpy (dest->elems + id, src2->elems + i2, (src2->nelem - i2) * sizeof (Idx)); 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 __attribute_warn_unused_result__ re_node_set_merge (re_node_set *dest, const re_node_set *src) { Idx is, id, sbase, delta; if (src == NULL || src->nelem == 0) return REG_NOERROR; if (dest->alloc < 2 * src->nelem + dest->nelem) { Idx new_alloc = 2 * (src->nelem + dest->alloc); Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc); if (BE (new_buffer == NULL, 0)) return REG_ESPACE; dest->elems = new_buffer; dest->alloc = new_alloc; } if (BE (dest->nelem == 0, 0)) { dest->nelem = src->nelem; memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); return REG_NOERROR; } /* Copy into the top of DEST the items of SRC that are not found in DEST. Maybe we could binary search in DEST? */ for (sbase = dest->nelem + 2 * src->nelem, is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; ) { if (dest->elems[id] == src->elems[is]) is--, id--; else if (dest->elems[id] < src->elems[is]) dest->elems[--sbase] = src->elems[is--]; else /* if (dest->elems[id] > src->elems[is]) */ --id; } if (is >= 0) { /* If DEST is exhausted, the remaining items of SRC must be unique. */ sbase -= is + 1; memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (Idx)); } id = dest->nelem - 1; is = dest->nelem + 2 * src->nelem - 1; delta = is - sbase + 1; if (delta == 0) return REG_NOERROR; /* Now copy. When DELTA becomes zero, the remaining DEST elements are already in place. */ dest->nelem += delta; for (;;) { if (dest->elems[is] > dest->elems[id]) { /* Copy from the top. */ dest->elems[id + delta--] = dest->elems[is--]; if (delta == 0) break; } else { /* Slide from the bottom. */ dest->elems[id + delta] = dest->elems[id]; if (--id < 0) { /* Copy remaining SRC elements. */ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx)); break; } } } return REG_NOERROR; } /* Insert the new element ELEM to the re_node_set* SET. SET should not already have ELEM. Return true if successful. */ static bool __attribute_warn_unused_result__ re_node_set_insert (re_node_set *set, Idx elem) { Idx idx; /* In case the set is empty. */ if (set->alloc == 0) return BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1); if (BE (set->nelem, 0) == 0) { /* We already guaranteed above that set->alloc != 0. */ set->elems[0] = elem; ++set->nelem; return true; } /* Realloc if we need. */ if (set->alloc == set->nelem) { Idx *new_elems; set->alloc = set->alloc * 2; new_elems = re_realloc (set->elems, Idx, set->alloc); if (BE (new_elems == NULL, 0)) return false; set->elems = new_elems; } /* Move the elements which follows the new element. Test the first element separately to skip a check in the inner loop. */ if (elem < set->elems[0]) { idx = 0; for (idx = set->nelem; idx > 0; idx--) set->elems[idx] = set->elems[idx - 1]; } else { for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) set->elems[idx] = set->elems[idx - 1]; } /* Insert the new element. */ set->elems[idx] = elem; ++set->nelem; return true; } /* Insert the new element ELEM to the re_node_set* SET. SET should not already have any element greater than or equal to ELEM. Return true if successful. */ static bool __attribute_warn_unused_result__ re_node_set_insert_last (re_node_set *set, Idx elem) { /* Realloc if we need. */ if (set->alloc == set->nelem) { Idx *new_elems; set->alloc = (set->alloc + 1) * 2; new_elems = re_realloc (set->elems, Idx, set->alloc); if (BE (new_elems == NULL, 0)) return false; set->elems = new_elems; } /* Insert the new element. */ set->elems[set->nelem++] = elem; return true; } /* Compare two node sets SET1 and SET2. Return true if SET1 and SET2 are equivalent. */ static bool __attribute__ ((pure)) re_node_set_compare (const re_node_set *set1, const re_node_set *set2) { Idx i; if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) return false; for (i = set1->nelem ; --i >= 0 ; ) if (set1->elems[i] != set2->elems[i]) return false; return true; } /* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ static Idx __attribute__ ((pure)) re_node_set_contains (const re_node_set *set, Idx elem) { __re_size_t 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 (re_node_set *set, Idx idx) { if (idx < 0 || idx >= set->nelem) return; --set->nelem; for (; idx < set->nelem; idx++) set->elems[idx] = set->elems[idx + 1]; } /* Add the token TOKEN to dfa->nodes, and return the index of the token. Or return -1 if an error occurred. */ static Idx re_dfa_add_node (re_dfa_t *dfa, re_token_t token) { if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) { size_t new_nodes_alloc = dfa->nodes_alloc * 2; Idx *new_nexts, *new_indices; re_node_set *new_edests, *new_eclosures; re_token_t *new_nodes; /* Avoid overflows in realloc. */ const size_t max_object_size = MAX (sizeof (re_token_t), MAX (sizeof (re_node_set), sizeof (Idx))); if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < new_nodes_alloc, 0)) return -1; new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); if (BE (new_nodes == NULL, 0)) return -1; dfa->nodes = new_nodes; new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc); new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc); new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); if (BE (new_nexts == NULL || new_indices == NULL || new_edests == NULL || new_eclosures == NULL, 0)) { re_free (new_nexts); re_free (new_indices); re_free (new_edests); re_free (new_eclosures); return -1; } dfa->nexts = new_nexts; dfa->org_indices = new_indices; dfa->edests = new_edests; dfa->eclosures = new_eclosures; dfa->nodes_alloc = new_nodes_alloc; } dfa->nodes[dfa->nodes_len] = token; dfa->nodes[dfa->nodes_len].constraint = 0; #ifdef RE_ENABLE_I18N dfa->nodes[dfa->nodes_len].accept_mb = ((token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET); #endif dfa->nexts[dfa->nodes_len] = -1; re_node_set_init_empty (dfa->edests + dfa->nodes_len); re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); return dfa->nodes_len++; } static re_hashval_t calc_state_hash (const re_node_set *nodes, unsigned int context) { re_hashval_t hash = nodes->nelem + context; Idx 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 * __attribute_warn_unused_result__ re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, const re_node_set *nodes) { re_hashval_t hash; re_dfastate_t *new_state; struct re_state_table_entry *spot; Idx i; #if defined GCC_LINT || defined lint /* Suppress bogus uninitialized-variable warnings. */ *err = REG_NOERROR; #endif 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, 0)) *err = REG_ESPACE; return new_state; } /* 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 * __attribute_warn_unused_result__ re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context) { re_hashval_t hash; re_dfastate_t *new_state; struct re_state_table_entry *spot; Idx i; #if defined GCC_LINT || defined lint /* Suppress bogus uninitialized-variable warnings. */ *err = REG_NOERROR; #endif 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 (state->hash == hash && state->context == context && re_node_set_compare (state->entrance_nodes, nodes)) 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, 0)) *err = REG_ESPACE; return new_state; } /* Finish initialization of the new state NEWSTATE, and using its hash value HASH put in the appropriate bucket of DFA's state table. Return value indicates the error code if failed. */ static reg_errcode_t __attribute_warn_unused_result__ register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, re_hashval_t hash) { struct re_state_table_entry *spot; reg_errcode_t err; Idx i; newstate->hash = hash; err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); if (BE (err != REG_NOERROR, 0)) return REG_ESPACE; for (i = 0; i < newstate->nodes.nelem; i++) { Idx elem = newstate->nodes.elems[i]; if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) if (! re_node_set_insert_last (&newstate->non_eps_nodes, elem)) return REG_ESPACE; } spot = dfa->state_table + (hash & dfa->state_hash_mask); if (BE (spot->alloc <= spot->num, 0)) { Idx new_alloc = 2 * spot->num + 2; re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, new_alloc); if (BE (new_array == NULL, 0)) return REG_ESPACE; spot->array = new_array; spot->alloc = new_alloc; } spot->array[spot->num++] = newstate; return REG_NOERROR; } static void free_state (re_dfastate_t *state) { re_node_set_free (&state->non_eps_nodes); re_node_set_free (&state->inveclosure); 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->word_trtable); re_free (state->trtable); re_free (state); } /* Create the new state which is independent of contexts. Return the new state if succeeded, otherwise return NULL. */ static re_dfastate_t * __attribute_warn_unused_result__ create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, re_hashval_t hash) { Idx i; reg_errcode_t err; re_dfastate_t *newstate; 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->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; #ifdef RE_ENABLE_I18N newstate->accept_mb |= node->accept_mb; #endif /* RE_ENABLE_I18N */ /* If the state has the halt node, the state is a halt state. */ if (type == END_OF_RE) newstate->halt = 1; 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 * __attribute_warn_unused_result__ create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, unsigned int context, re_hashval_t hash) { Idx i, nctx_nodes = 0; reg_errcode_t err; re_dfastate_t *newstate; 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->context = context; 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; unsigned int constraint = node->constraint; if (type == CHARACTER && !constraint) continue; #ifdef RE_ENABLE_I18N newstate->accept_mb |= node->accept_mb; #endif /* RE_ENABLE_I18N */ /* If the state has the halt node, the state is a halt state. */ if (type == END_OF_RE) newstate->halt = 1; else if (type == OP_BACK_REF) newstate->has_backref = 1; 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; } if (re_node_set_init_copy (newstate->entrance_nodes, nodes) != REG_NOERROR) return NULL; 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; } smartmontools-7.0/regex/regex_internal.h0000644000175000010010000006247613336325511015521 00000000000000/* Extended regular expression matching and search library. Copyright (C) 2002-2018 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, see . */ #ifndef _REGEX_INTERNAL_H #define _REGEX_INTERNAL_H 1 #include #include #include #include #include #ifndef _REGEX_STANDALONE #include #endif #include #include #include #include #include /* Properties of integers. Although Gnulib has intprops.h, glibc does without for now. */ #if !defined(_LIBC) && !defined(_REGEX_STANDALONE) # include "intprops.h" #else /* True if the real type T is signed. */ # define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) /* True if adding the nonnegative Idx values A and B would overflow. If false, set *R to A + B. A, B, and R may be evaluated more than once, or zero times. Although this is not a full implementation of Gnulib INT_ADD_WRAPV, it is good enough for glibc regex code. FIXME: This implementation is a fragile stopgap, and this file would be simpler and more robust if intprops.h were migrated into glibc. */ # define INT_ADD_WRAPV(a, b, r) \ (IDX_MAX - (a) < (b) ? true : (*(r) = (a) + (b), false)) #endif #ifdef _LIBC # include # define lock_define(name) __libc_lock_define (, name) # define lock_init(lock) (__libc_lock_init (lock), 0) # define lock_fini(lock) ((void) 0) # define lock_lock(lock) __libc_lock_lock (lock) # define lock_unlock(lock) __libc_lock_unlock (lock) #elif defined GNULIB_LOCK && !defined USE_UNLOCKED_IO # include "glthread/lock.h" /* Use gl_lock_define if empty macro arguments are known to work. Otherwise, fall back on less-portable substitutes. */ # if ((defined __GNUC__ && !defined __STRICT_ANSI__) \ || (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__)) # define lock_define(name) gl_lock_define (, name) # elif USE_POSIX_THREADS # define lock_define(name) pthread_mutex_t name; # elif USE_PTH_THREADS # define lock_define(name) pth_mutex_t name; # elif USE_SOLARIS_THREADS # define lock_define(name) mutex_t name; # elif USE_WINDOWS_THREADS # define lock_define(name) gl_lock_t name; # else # define lock_define(name) # endif # define lock_init(lock) glthread_lock_init (&(lock)) # define lock_fini(lock) glthread_lock_destroy (&(lock)) # define lock_lock(lock) glthread_lock_lock (&(lock)) # define lock_unlock(lock) glthread_lock_unlock (&(lock)) #elif defined GNULIB_PTHREAD && !defined USE_UNLOCKED_IO # include # define lock_define(name) pthread_mutex_t name; # define lock_init(lock) pthread_mutex_init (&(lock), 0) # define lock_fini(lock) pthread_mutex_destroy (&(lock)) # define lock_lock(lock) pthread_mutex_lock (&(lock)) # define lock_unlock(lock) pthread_mutex_unlock (&(lock)) #else # define lock_define(name) # define lock_init(lock) 0 # define lock_fini(lock) ((void) 0) /* The 'dfa' avoids an "unused variable 'dfa'" warning from GCC. */ # define lock_lock(lock) ((void) dfa) # define lock_unlock(lock) ((void) 0) #endif /* In case that the system doesn't have isblank(). */ #if !defined _LIBC && ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK)) # define isblank(ch) ((ch) == ' ' || (ch) == '\t') #endif #ifdef _LIBC # ifndef _RE_DEFINE_LOCALE_FUNCTIONS # define _RE_DEFINE_LOCALE_FUNCTIONS 1 # include # include # endif #endif /* This is for other GNU distributions with internationalized messages. */ #if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC # include # ifdef _LIBC # undef gettext # define gettext(msgid) \ __dcgettext (_libc_intl_domainname, msgid, LC_MESSAGES) # endif #else # undef gettext # 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_WCTYPE_H && HAVE_ISWCTYPE) || _LIBC # define RE_ENABLE_I18N #endif #ifdef __GNUC__ #define BE(expr, val) __builtin_expect (expr, val) #else #define BE(expr, val) (expr) #endif /* Number of ASCII characters. */ #define ASCII_CHARS 0x80 /* Number of single byte characters. */ #define SBC_MAX (UCHAR_MAX + 1) #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 # undef __wctype # undef __iswctype # define __wctype wctype # define __iswalnum iswalnum # define __iswctype iswctype # define __towlower towlower # define __towupper towupper # define __btowc btowc # define __mbrtowc mbrtowc # define __wcrtomb wcrtomb # define __regfree regfree # define attribute_hidden #endif /* not _LIBC */ #if __GNUC__ < 3 + (__GNUC_MINOR__ < 1) # define __attribute__(arg) #endif #ifndef SSIZE_MAX # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) #endif /* The type of indexes into strings. This is signed, not size_t, since the API requires indexes to fit in regoff_t anyway, and using signed integers makes the code a bit smaller and presumably faster. The traditional GNU regex implementation uses int for indexes. The POSIX-compatible implementation uses a possibly-wider type. The name 'Idx' is three letters to minimize the hassle of reindenting a lot of regex code that formerly used 'int'. */ typedef regoff_t Idx; #ifdef _REGEX_LARGE_OFFSETS # define IDX_MAX SSIZE_MAX #else # define IDX_MAX INT_MAX #endif /* A hash value, suitable for computing hash tables. */ typedef __re_size_t re_hashval_t; /* An integer used to represent a set of bits. It must be unsigned, and must be at least as wide as unsigned int. */ typedef unsigned long int bitset_word_t; /* All bits set in a bitset_word_t. */ #define BITSET_WORD_MAX ULONG_MAX /* Number of bits in a bitset_word_t. For portability to hosts with padding bits, do not use '(sizeof (bitset_word_t) * CHAR_BIT)'; instead, deduce it directly from BITSET_WORD_MAX. Avoid greater-than-32-bit integers and unconditional shifts by more than 31 bits, as they're not portable. */ #if BITSET_WORD_MAX == 0xffffffffUL # define BITSET_WORD_BITS 32 #elif BITSET_WORD_MAX >> 31 >> 4 == 1 # define BITSET_WORD_BITS 36 #elif BITSET_WORD_MAX >> 31 >> 16 == 1 # define BITSET_WORD_BITS 48 #elif BITSET_WORD_MAX >> 31 >> 28 == 1 # define BITSET_WORD_BITS 60 #elif BITSET_WORD_MAX >> 31 >> 31 >> 1 == 1 # define BITSET_WORD_BITS 64 #elif BITSET_WORD_MAX >> 31 >> 31 >> 9 == 1 # define BITSET_WORD_BITS 72 #elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 3 == 1 # define BITSET_WORD_BITS 128 #elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 == 1 # define BITSET_WORD_BITS 256 #elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 > 1 # define BITSET_WORD_BITS 257 /* any value > SBC_MAX will do here */ # if BITSET_WORD_BITS <= SBC_MAX # error "Invalid SBC_MAX" # endif #else # error "Add case for new bitset_word_t size" #endif /* Number of bitset_word_t values in a bitset_t. */ #define BITSET_WORDS ((SBC_MAX + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS) typedef bitset_word_t bitset_t[BITSET_WORDS]; typedef bitset_word_t *re_bitset_ptr_t; typedef const bitset_word_t *re_const_bitset_ptr_t; #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 WORD_DELIM_CONSTRAINT 0x0100 #define NOT_WORD_DELIM_CONSTRAINT 0x0200 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, INSIDE_NOTWORD = PREV_NOTWORD_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 = WORD_DELIM_CONSTRAINT, NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT } re_context_type; typedef struct { Idx alloc; Idx nelem; Idx *elems; } re_node_set; typedef enum { NON_TYPE = 0, /* Node type, These are used by token, node, tree. */ CHARACTER = 1, END_OF_RE = 2, SIMPLE_BRACKET = 3, OP_BACK_REF = 4, OP_PERIOD = 5, #ifdef RE_ENABLE_I18N COMPLEX_BRACKET = 6, OP_UTF8_PERIOD = 7, #endif /* RE_ENABLE_I18N */ /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used when the debugger shows values of this enum type. */ #define EPSILON_BIT 8 OP_OPEN_SUBEXP = EPSILON_BIT | 0, OP_CLOSE_SUBEXP = EPSILON_BIT | 1, OP_ALT = EPSILON_BIT | 2, OP_DUP_ASTERISK = EPSILON_BIT | 3, ANCHOR = EPSILON_BIT | 4, /* Tree type, these are used only by tree. */ CONCAT = 16, SUBEXP = 17, /* Token type, these are used only by token. */ OP_DUP_PLUS = 18, OP_DUP_QUESTION, 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, OP_SPACE, OP_NOTSPACE, BACK_SLASH } 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. */ Idx nmbchars; /* # of collating symbols. */ Idx ncoll_syms; /* # of equivalence classes. */ Idx nequiv_classes; /* # of range expressions. */ Idx nranges; /* # of character classes. */ Idx 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 */ Idx idx; /* for BACK_REF */ re_context_type ctx_type; /* for ANCHOR */ } opr; #if __GNUC__ >= 2 && !defined __STRICT_ANSI__ re_token_type_t type : 8; #else re_token_type_t type; #endif unsigned int constraint : 10; /* context constraint */ unsigned int duplicated : 1; unsigned int opt_subexp : 1; #ifdef RE_ENABLE_I18N unsigned int accept_mb : 1; /* These 2 bits can be moved into the union if needed (e.g. if running out of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ unsigned int mb_partial : 1; #endif unsigned int word_char : 1; } re_token_t; #define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) 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; #ifdef RE_ENABLE_I18N /* Store the wide character string which is corresponding to MBS. */ wint_t *wcs; Idx *offsets; mbstate_t cur_state; #endif /* Index in RAW_MBS. Each character mbs[i] corresponds to raw_mbs[raw_mbs_idx + i]. */ Idx raw_mbs_idx; /* The length of the valid characters in the buffers. */ Idx valid_len; /* The corresponding number of bytes in raw_mbs array. */ Idx valid_raw_len; /* The length of the buffers MBS and WCS. */ Idx bufs_len; /* The index in MBS, which is updated by re_string_fetch_byte. */ Idx cur_idx; /* length of RAW_MBS array. */ Idx raw_len; /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ Idx 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. */ Idx raw_stop; /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ Idx 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; /* Copy of re_dfa_t's word_char. */ re_const_bitset_ptr_t word_char; /* true if REG_ICASE. */ unsigned char icase; unsigned char is_utf8; unsigned char map_notascii; unsigned char mbs_allocated; unsigned char offsets_needed; unsigned char newline_anchor; unsigned char word_ops_used; int mb_cur_max; }; typedef struct re_string_t re_string_t; struct re_dfa_t; typedef struct re_dfa_t re_dfa_t; #ifndef _LIBC # define IS_IN(libc) false #endif #define re_string_peek_byte(pstr, offset) \ ((pstr)->mbs[(pstr)->cur_idx + offset]) #define re_string_fetch_byte(pstr) \ ((pstr)->mbs[(pstr)->cur_idx++]) #define re_string_first_byte(pstr, idx) \ ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) #define re_string_is_single_byte_char(pstr, idx) \ ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ || (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)) #if defined _LIBC || HAVE_ALLOCA # include #endif #ifndef _LIBC # if HAVE_ALLOCA /* The OS usually guarantees only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely allocate anything larger than 4096 bytes. Also care for the possibility of a few compiler-allocated temporary stack slots. */ # define __libc_use_alloca(n) ((n) < 4032) # else /* alloca is implemented with malloc, so just use malloc. */ # define __libc_use_alloca(n) 0 # undef alloca # define alloca(n) malloc (n) # endif #endif #ifdef _LIBC # define MALLOC_0_IS_NONNULL 1 #elif !defined MALLOC_0_IS_NONNULL # define MALLOC_0_IS_NONNULL 0 #endif #ifndef MAX # define MAX(a,b) ((a) < (b) ? (b) : (a)) #endif #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #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; struct bin_tree_t *first; struct bin_tree_t *next; re_token_t token; /* 'node_idx' is the index in dfa->nodes, if 'type' == 0. Otherwise 'type' indicate the type of this node. */ Idx node_idx; }; typedef struct bin_tree_t bin_tree_t; #define BIN_TREE_STORAGE_SIZE \ ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) struct bin_tree_storage_t { struct bin_tree_storage_t *next; bin_tree_t data[BIN_TREE_STORAGE_SIZE]; }; typedef struct bin_tree_storage_t bin_tree_storage_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 { re_hashval_t hash; re_node_set nodes; re_node_set non_eps_nodes; re_node_set inveclosure; re_node_set *entrance_nodes; struct re_dfastate_t **trtable, **word_trtable; unsigned int context : 4; 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; struct re_state_table_entry { Idx num; Idx alloc; re_dfastate_t **array; }; /* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ typedef struct { Idx next_idx; Idx alloc; re_dfastate_t **array; } state_array_t; /* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ typedef struct { Idx node; Idx 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 { Idx str_idx; Idx node; state_array_t *path; Idx alasts; /* Allocation size of LASTS. */ Idx nlasts; /* The number of LASTS. */ re_sub_match_last_t **lasts; } re_sub_match_top_t; struct re_backref_cache_entry { Idx node; Idx str_idx; Idx subexp_from; Idx subexp_to; char more; char unused; unsigned short int eps_reachable_subexps_map; }; typedef struct { /* The string object corresponding to the input string. */ re_string_t input; #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) const re_dfa_t *const dfa; #else const re_dfa_t *dfa; #endif /* EFLAGS of the argument of regexec. */ int eflags; /* Where the matching ends. */ Idx match_last; Idx last_node; /* The state log used by the matcher. */ re_dfastate_t **state_log; Idx state_log_top; /* Back reference cache. */ Idx nbkref_ents; Idx abkref_ents; struct re_backref_cache_entry *bkref_ents; int max_mb_elem_len; Idx nsub_tops; Idx asub_tops; re_sub_match_top_t **sub_tops; } re_match_context_t; typedef struct { re_dfastate_t **sifted_states; re_dfastate_t **limited_states; Idx last_node; Idx last_str_idx; re_node_set limits; } re_sift_context_t; struct re_fail_stack_ent_t { Idx idx; Idx node; regmatch_t *regs; re_node_set eps_via_nodes; }; struct re_fail_stack_t { Idx num; Idx alloc; struct re_fail_stack_ent_t *stack; }; struct re_dfa_t { re_token_t *nodes; size_t nodes_alloc; size_t nodes_len; Idx *nexts; Idx *org_indices; re_node_set *edests; re_node_set *eclosures; re_node_set *inveclosures; struct re_state_table_entry *state_table; re_dfastate_t *init_state; re_dfastate_t *init_state_word; re_dfastate_t *init_state_nl; re_dfastate_t *init_state_begbuf; bin_tree_t *str_tree; bin_tree_storage_t *str_tree_storage; re_bitset_ptr_t sb_char; int str_tree_storage_idx; /* number of subexpressions 're_nsub' is in regex_t. */ re_hashval_t state_hash_mask; Idx init_node; Idx nbackref; /* The number of backreference in this dfa. */ /* Bitmap expressing which backreference is used. */ bitset_word_t used_bkref_map; bitset_word_t completed_bkref_map; 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; unsigned int is_utf8 : 1; unsigned int map_notascii : 1; unsigned int word_ops_used : 1; int mb_cur_max; bitset_t word_char; reg_syntax_t syntax; Idx *subexp_map; #ifdef DEBUG char* re_str; #endif lock_define (lock) }; #define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) #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) 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; /* Functions for bitset_t operation. */ static inline void bitset_set (bitset_t set, Idx i) { set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS; } static inline void bitset_clear (bitset_t set, Idx i) { set[i / BITSET_WORD_BITS] &= ~ ((bitset_word_t) 1 << i % BITSET_WORD_BITS); } static inline bool bitset_contain (const bitset_t set, Idx i) { return (set[i / BITSET_WORD_BITS] >> i % BITSET_WORD_BITS) & 1; } static inline void bitset_empty (bitset_t set) { memset (set, '\0', sizeof (bitset_t)); } static inline void bitset_set_all (bitset_t set) { memset (set, -1, sizeof (bitset_word_t) * (SBC_MAX / BITSET_WORD_BITS)); if (SBC_MAX % BITSET_WORD_BITS != 0) set[BITSET_WORDS - 1] = ((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1; } static inline void bitset_copy (bitset_t dest, const bitset_t src) { memcpy (dest, src, sizeof (bitset_t)); } static inline void bitset_not (bitset_t set) { int bitset_i; for (bitset_i = 0; bitset_i < SBC_MAX / BITSET_WORD_BITS; ++bitset_i) set[bitset_i] = ~set[bitset_i]; if (SBC_MAX % BITSET_WORD_BITS != 0) set[BITSET_WORDS - 1] = ((((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1) & ~set[BITSET_WORDS - 1]); } static inline void bitset_merge (bitset_t dest, const bitset_t src) { int bitset_i; for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) dest[bitset_i] |= src[bitset_i]; } static inline void bitset_mask (bitset_t dest, const bitset_t src) { int bitset_i; for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) dest[bitset_i] &= src[bitset_i]; } #ifdef RE_ENABLE_I18N /* Functions for re_string. */ static int __attribute__ ((pure, unused)) re_string_char_size_at (const re_string_t *pstr, Idx idx) { int byte_idx; if (pstr->mb_cur_max == 1) return 1; for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) if (pstr->wcs[idx + byte_idx] != WEOF) break; return byte_idx; } static wint_t __attribute__ ((pure, unused)) re_string_wchar_at (const re_string_t *pstr, Idx idx) { if (pstr->mb_cur_max == 1) return (wint_t) pstr->mbs[idx]; return (wint_t) pstr->wcs[idx]; } # ifdef _LIBC # include # endif static int __attribute__ ((pure, unused)) re_string_elem_size_at (const re_string_t *pstr, Idx idx) { # ifdef _LIBC const unsigned char *p, *extra; const int32_t *table, *indirect; 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; findidx (table, indirect, extra, &p, pstr->len - idx); return p - pstr->mbs - idx; } else # endif /* _LIBC */ return 1; } #endif /* RE_ENABLE_I18N */ #ifndef __GNUC_PREREQ # if defined __GNUC__ && defined __GNUC_MINOR__ # define __GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) # else # define __GNUC_PREREQ(maj, min) 0 # endif #endif #if __GNUC_PREREQ (3,4) # undef __attribute_warn_unused_result__ # define __attribute_warn_unused_result__ \ __attribute__ ((__warn_unused_result__)) #else # define __attribute_warn_unused_result__ /* empty */ #endif #ifndef FALLTHROUGH # if __GNUC__ < 7 # define FALLTHROUGH ((void) 0) # else # define FALLTHROUGH __attribute__ ((__fallthrough__)) # endif #endif #endif /* _REGEX_INTERNAL_H */ smartmontools-7.0/scsiata.cpp0000644000175000010010000015143113402014526013344 00000000000000/* * scsiata.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2006-15 Douglas Gilbert * Copyright (C) 2009-18 Christian Franke * * SPDX-License-Identifier: GPL-2.0-or-later * The code in this file is based on the SCSI to ATA Translation (SAT) * draft found at http://www.t10.org . The original draft used for this * code is sat-r08.pdf which is not too far away from becoming a * standard. The SAT commands of interest to smartmontools are the * ATA PASS THROUGH SCSI (16) and ATA PASS THROUGH SCSI (12) defined in * section 12 of that document. * * sat-r09.pdf is the most recent, easily accessible draft prior to the * original SAT standard (ANSI INCITS 431-2007). By mid-2009 the second * version of the SAT standard (SAT-2) is nearing standardization. In * their wisdom an incompatible change has been introduced in draft * sat2r08a.pdf in the area of the ATA RETURN DESCRIPTOR. A new "fixed * format" ATA RETURN buffer has been defined (sat2r08b.pdf section * 12.2.7) for the case when DSENSE=0 in the Control mode page. * Unfortunately this is the normal case. If the change stands our * code will need to be extended for this case. * * With more transports "hiding" SATA disks (and other S-ATAPI devices) * behind a SCSI command set, accessing special features like SMART * information becomes a challenge. The SAT standard offers ATA PASS * THROUGH commands for special usages. Note that the SAT layer may * be inside a generic OS layer (e.g. libata in linux), in a host * adapter (HA or HBA) firmware, or somewhere on the interconnect * between the host computer and the SATA devices (e.g. a RAID made * of SATA disks and the RAID talks "SCSI" to the host computer). * Note that in the latter case, this code does not solve the * addressing issue (i.e. which SATA disk to address behind the logical * SCSI (RAID) interface). * */ #include #include #include #include #include #include "config.h" #include "scsicmds.h" #include "atacmds.h" // ataReadHDIdentity() #include "knowndrives.h" // lookup_usb_device() #include "utility.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" // ata_device_with_command_set #include "dev_tunnelled.h" // tunnelled_device<> #include "sg_unaligned.h" const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 4848 2018-12-05 18:30:46Z chrfranke $"; /* This is a slightly stretched SCSI sense "descriptor" format header. The addition is to allow the 0x70 and 0x71 response codes. The idea is to place the salient data of both "fixed" and "descriptor" sense format into one structure to ease application processing. The original sense buffer should be kept around for those cases in which more information is required (e.g. the LBA of a MEDIUM ERROR). */ /// Abridged SCSI sense data struct sg_scsi_sense_hdr { unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */ unsigned char sense_key; unsigned char asc; unsigned char ascq; unsigned char byte4; unsigned char byte5; unsigned char byte6; unsigned char additional_length; }; /* Maps the salient data from a sense buffer which is in either fixed or descriptor format into a structure mimicking a descriptor format header (i.e. the first 8 bytes of sense descriptor format). If zero response code returns 0. Otherwise returns 1 and if 'sshp' is non-NULL then zero all fields and then set the appropriate fields in that structure. sshp::additional_length is always 0 for response codes 0x70 and 0x71 (fixed format). */ static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, struct sg_scsi_sense_hdr * sshp); #define SAT_ATA_PASSTHROUGH_12LEN 12 #define SAT_ATA_PASSTHROUGH_16LEN 16 #define DEF_SAT_ATA_PASSTHRU_SIZE 16 #define ATA_RETURN_DESCRIPTOR 9 namespace sat { // no need to publish anything, name provided for Doxygen /// SAT support. /// Implements ATA by tunnelling through SCSI. class sat_device : public tunnelled_device< /*implements*/ ata_device /*by tunnelling through a*/, scsi_device >, virtual public /*implements*/ scsi_device { public: enum sat_scsi_mode { sat_always, sat_auto, scsi_always }; sat_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, sat_scsi_mode mode = sat_always, int passthrulen = 0); virtual ~sat_device() throw(); virtual smart_device * autodetect_open(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); virtual bool scsi_pass_through(scsi_cmnd_io * iop); private: int m_passthrulen; sat_scsi_mode m_mode; }; sat_device::sat_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, sat_scsi_mode mode /* = sat_always */, int passthrulen /* = 0 */) : smart_device(intf, scsidev->get_dev_name(), (mode == sat_always ? "sat" : mode == sat_auto ? "sat,auto" : "scsi"), req_type), tunnelled_device(scsidev), m_passthrulen(passthrulen), m_mode(mode) { if (mode != sat_always) hide_ata(); // Start as SCSI, switch to ATA in autodetect_open() else hide_scsi(); // ATA always if (strcmp(scsidev->get_dev_type(), "scsi")) set_info().dev_type += strprintf("+%s", scsidev->get_dev_type()); set_info().info_name = strprintf("%s [%s]", scsidev->get_info_name(), (mode == sat_always ? "SAT" : mode == sat_auto ? "SCSI/SAT" : "SCSI")); } sat_device::~sat_device() throw() { } // cdb[0]: ATA PASS THROUGH (16) SCSI command opcode byte (0x85) // cdb[1]: multiple_count, protocol + extend // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length // cdb[3]: features (15:8) // cdb[4]: features (7:0) // cdb[5]: sector_count (15:8) // cdb[6]: sector_count (7:0) // cdb[7]: lba_low (15:8) // cdb[8]: lba_low (7:0) // cdb[9]: lba_mid (15:8) // cdb[10]: lba_mid (7:0) // cdb[11]: lba_high (15:8) // cdb[12]: lba_high (7:0) // cdb[13]: device // cdb[14]: (ata) command // cdb[15]: control (SCSI, leave as zero) // // 24 bit lba (from MSB): cdb[12] cdb[10] cdb[8] // 48 bit lba (from MSB): cdb[11] cdb[9] cdb[7] cdb[12] cdb[10] cdb[8] // // // cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1) // cdb[1]: multiple_count, protocol + extend // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length // cdb[3]: features (7:0) // cdb[4]: sector_count (7:0) // cdb[5]: lba_low (7:0) // cdb[6]: lba_mid (7:0) // cdb[7]: lba_high (7:0) // cdb[8]: device // cdb[9]: (ata) command // cdb[10]: reserved // cdb[11]: control (SCSI, leave as zero) // // // ATA Return Descriptor (component of descriptor sense data) // des[0]: descriptor code (0x9) // des[1]: additional descriptor length (0xc) // des[2]: extend (bit 0) // des[3]: error // des[4]: sector_count (15:8) // des[5]: sector_count (7:0) // des[6]: lba_low (15:8) // des[7]: lba_low (7:0) // des[8]: lba_mid (15:8) // des[9]: lba_mid (7:0) // des[10]: lba_high (15:8) // des[11]: lba_high (7:0) // des[12]: device // des[13]: status // // // ATA registers returned via fixed format sense (allowed >= SAT-2) // fxs[0]: info_valid (bit 7); response_code (6:0) // fxs[1]: (obsolete) // fxs[2]: sense_key (3:0) --> recovered error (formerly 'no sense') // fxs[3]: information (31:24) --> ATA Error register // fxs[4]: information (23:16) --> ATA Status register // fxs[5]: information (15:8) --> ATA Device register // fxs[6]: information (7:0) --> ATA Count (7:0) // fxs[7]: additional sense length [should be >= 10] // fxs[8]: command specific info (31:24) --> Extend (7), count_upper_nonzero // (6), lba_upper_nonzero(5), log_index (3:0) // fxs[9]: command specific info (23:16) --> ATA LBA (7:0) // fxs[10]: command specific info (15:8) --> ATA LBA (15:8) // fxs[11]: command specific info (7:0) --> ATA LBA (23:16) // fxs[12]: additional sense code (asc) --> 0x0 // fxs[13]: additional sense code qualifier (ascq) --> 0x1d // asc,ascq = 0x0,0x1d --> 'ATA pass through information available' // PURPOSE // This interface routine takes ATA SMART commands and packages // them in the SAT-defined ATA PASS THROUGH SCSI commands. There are // two available SCSI commands: a 12 byte and 16 byte variant; the // one used is chosen via this->m_passthrulen . // DETAILED DESCRIPTION OF ARGUMENTS // device: is the file descriptor provided by (a SCSI dvice type) open() // command: defines the different ATA 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" bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_output_regs | ata_device::supports_multi_sector | ata_device::supports_48bit, "SAT") ) return false; struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; struct sg_scsi_sense_hdr ssh; unsigned char cdb[SAT_ATA_PASSTHROUGH_16LEN]; unsigned char sense[32]; const unsigned char * ardp; int ard_len, have_sense; int extend = 0; int ck_cond = 0; /* set to 1 to read register(s) back */ int protocol = 3; /* non-data */ int t_dir = 1; /* 0 -> to device, 1 -> from device */ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ int t_length = 0; /* 0 -> no data transferred */ int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE; bool sense_descriptor = true; memset(cdb, 0, sizeof(cdb)); memset(sense, 0, sizeof(sense)); // Set data direction // TODO: This works only for commands where sector_count holds count! switch (in.direction) { case ata_cmd_in::no_data: break; case ata_cmd_in::data_in: protocol = 4; // PIO data-in t_length = 2; // sector_count holds count break; case ata_cmd_in::data_out: protocol = 5; // PIO data-out t_length = 2; // sector_count holds count t_dir = 0; // to device break; default: return set_err(EINVAL, "sat_device::ata_pass_through: invalid direction=%d", (int)in.direction); } // Check condition if any output register needed if (in.out_needed.is_set()) ck_cond = 1; if ((SAT_ATA_PASSTHROUGH_12LEN == m_passthrulen) || (SAT_ATA_PASSTHROUGH_16LEN == m_passthrulen)) passthru_size = m_passthrulen; // Set extend bit on 48-bit ATA command if (in.in_regs.is_48bit_cmd()) { if (passthru_size != SAT_ATA_PASSTHROUGH_16LEN) return set_err(ENOSYS, "48-bit ATA commands require SAT ATA PASS-THROUGH (16)"); extend = 1; } cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16; cdb[1] = (protocol << 1) | extend; cdb[2] = (ck_cond << 5) | (t_dir << 3) | (byte_block << 2) | t_length; if (passthru_size == SAT_ATA_PASSTHROUGH_12LEN) { // ATA PASS-THROUGH (12) const ata_in_regs & lo = in.in_regs; cdb[3] = lo.features; cdb[4] = lo.sector_count; cdb[5] = lo.lba_low; cdb[6] = lo.lba_mid; cdb[7] = lo.lba_high; cdb[8] = lo.device; cdb[9] = lo.command; } else { // ATA PASS-THROUGH (16) const ata_in_regs & lo = in.in_regs; const ata_in_regs & hi = in.in_regs.prev; // Note: all 'in.in_regs.prev.*' are always zero for 28-bit commands cdb[ 3] = hi.features; cdb[ 4] = lo.features; cdb[ 5] = hi.sector_count; cdb[ 6] = lo.sector_count; cdb[ 7] = hi.lba_low; cdb[ 8] = lo.lba_low; cdb[ 9] = hi.lba_mid; cdb[10] = lo.lba_mid; cdb[11] = hi.lba_high; cdb[12] = lo.lba_high; cdb[13] = lo.device; cdb[14] = lo.command; } memset(&io_hdr, 0, sizeof(io_hdr)); if (0 == t_length) { io_hdr.dxfer_dir = DXFER_NONE; io_hdr.dxfer_len = 0; } else if (t_dir) { /* from device */ io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; memset(in.buffer, 0, in.size); // prefill with zeroes } else { /* to device */ io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; } io_hdr.cmnd = cdb; io_hdr.cmnd_len = passthru_size; io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through(&io_hdr)) { if (scsi_debugmode > 0) pout("sat_device::ata_pass_through: scsi_pass_through() failed, " "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg()); return set_err(scsidev->get_err()); } ardp = NULL; ard_len = 0; have_sense = sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, &ssh); if (have_sense) { sense_descriptor = ssh.response_code >= 0x72; if (sense_descriptor) { /* look for SAT ATA Return Descriptor */ ardp = sg_scsi_sense_desc_find(io_hdr.sensep, io_hdr.resp_sense_len, ATA_RETURN_DESCRIPTOR); if (ardp) { ard_len = ardp[1] + 2; if (ard_len < 12) ard_len = 12; else if (ard_len > 14) ard_len = 14; } } scsi_do_sense_disect(&io_hdr, &sinfo); int status = scsiSimpleSenseFilter(&sinfo); // Workaround for bogus sense_key in sense data with SAT ATA Return Descriptor if ( status && ck_cond && ardp && ard_len > 13 && (ardp[13] & 0xc1) == 0x40 /* !BSY && DRDY && !ERR */) { if (scsi_debugmode > 0) pout("ATA status (0x%02x) indicates success, ignoring SCSI sense_key\n", ardp[13]); status = 0; } if (0 != status) { /* other than no_sense and recovered_error */ if (scsi_debugmode > 0) { pout("sat_device::ata_pass_through: scsi error: %s\n", scsiErrString(status)); if (ardp && (scsi_debugmode > 1)) { pout("Values from ATA Return Descriptor are:\n"); dStrHex((const uint8_t *)ardp, ard_len, 1); } } if (t_dir && (t_length > 0) && (in.direction == ata_cmd_in::data_in)) memset(in.buffer, 0, in.size); return set_err(EIO, "scsi error %s", scsiErrString(status)); } } if (ck_cond) { /* expecting SAT specific sense data */ if (have_sense) { if (ardp) { if (scsi_debugmode > 1) { pout("Values from ATA Return Descriptor are:\n"); dStrHex((const uint8_t *)ardp, ard_len, 1); } // Set output registers ata_out_regs & lo = out.out_regs; lo.error = ardp[ 3]; lo.sector_count = ardp[ 5]; lo.lba_low = ardp[ 7]; lo.lba_mid = ardp[ 9]; lo.lba_high = ardp[11]; lo.device = ardp[12]; lo.status = ardp[13]; if (in.in_regs.is_48bit_cmd()) { ata_out_regs & hi = out.out_regs.prev; hi.sector_count = ardp[ 4]; hi.lba_low = ardp[ 6]; hi.lba_mid = ardp[ 8]; hi.lba_high = ardp[10]; } } else if ((! sense_descriptor) && (0 == ssh.asc) && (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq) && (0 != io_hdr.sensep[4] /* Some ATA STATUS bit must be set */)) { /* in SAT-2 and later, ATA registers may be passed back via * fixed format sense data [ref: sat3r07 section 12.2.2.7] */ ata_out_regs & lo = out.out_regs; lo.error = io_hdr.sensep[ 3]; lo.status = io_hdr.sensep[ 4]; lo.device = io_hdr.sensep[ 5]; lo.sector_count = io_hdr.sensep[ 6]; lo.lba_low = io_hdr.sensep[ 9]; lo.lba_mid = io_hdr.sensep[10]; lo.lba_high = io_hdr.sensep[11]; if (in.in_regs.is_48bit_cmd()) { if (0 == (0x60 & io_hdr.sensep[8])) { ata_out_regs & hi = out.out_regs.prev; hi.sector_count = 0; hi.lba_low = 0; hi.lba_mid = 0; hi.lba_high = 0; } else { /* getting the "hi." values when either * count_upper_nonzero or lba_upper_nonzero are set * involves fetching the SCSI ATA PASS-THROUGH * Results log page and decoding the descriptor with * the matching log_index field. Painful. */ } } } } } else { /* ck_cond == 0 */ if (have_sense) { if (((SCSI_SK_NO_SENSE == ssh.sense_key) || (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) && (0 == ssh.asc) && (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) { if (scsi_debugmode > 0) { if (sense_descriptor && ardp) { pout("Values from ATA Return Descriptor are:\n"); dStrHex((const uint8_t *)ardp, ard_len, 1); } else if (! sense_descriptor) { pout("Values from ATA fixed format sense are:\n"); pout(" Error: 0x%x\n", io_hdr.sensep[3]); pout(" Status: 0x%x\n", io_hdr.sensep[4]); pout(" Device: 0x%x\n", io_hdr.sensep[5]); pout(" Count: 0x%x\n", io_hdr.sensep[6]); } } } return set_err(EIO, "SAT command failed"); } } return true; } bool sat_device::scsi_pass_through(scsi_cmnd_io * iop) { scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through(iop)) { set_err(scsidev->get_err()); return false; } return true; } smart_device * sat_device::autodetect_open() { if (!open() || m_mode != sat_auto) return this; scsi_device * scsidev = get_tunnel_dev(); unsigned char inqdata[36] = {0, }; if (scsiStdInquiry(scsidev, inqdata, sizeof(inqdata))) { smart_device::error_info err = scsidev->get_err(); close(); set_err(err.no, "INQUIRY [SAT]: %s", err.msg.c_str()); return this; } // Check for SAT "VENDOR" int inqsize = inqdata[4] + 5; bool sat = (inqsize >= 36 && !memcmp(inqdata + 8, "ATA ", 8)); // Change interface hide_ata(!sat); hide_scsi(sat); set_info().dev_type = (sat ? "sat" : scsidev->get_dev_type()); set_info().info_name = strprintf("%s [%s]", scsidev->get_info_name(), (sat ? "SAT" : "SCSI")); return this; } } // namespace ///////////////////////////////////////////////////////////////////////////// /* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface is false otherwise attempt IDENTIFY PACKET DEVICE. If successful return true, else false */ static bool has_sat_pass_through(ata_device * dev, bool packet_interface = false) { /* Note: malloc() ensures the read buffer lands on a single page. This avoids some bugs seen on LSI controllers under FreeBSD */ char *data = (char *)malloc(512); ata_cmd_in in; in.in_regs.command = (packet_interface ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE); in.set_data_in(data, 1); bool ret = dev->ata_pass_through(in); free(data); return ret; } ///////////////////////////////////////////////////////////////////////////// /* Next two functions are borrowed from sg_lib.c in the sg3_utils package. Same copyrght owner, same license as this file. */ static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, struct sg_scsi_sense_hdr * sshp) { if (sshp) memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0]))) return 0; if (sshp) { sshp->response_code = (0x7f & sensep[0]); if (sshp->response_code >= 0x72) { /* descriptor format */ if (sb_len > 1) sshp->sense_key = (0xf & sensep[1]); if (sb_len > 2) sshp->asc = sensep[2]; if (sb_len > 3) sshp->ascq = sensep[3]; if (sb_len > 7) sshp->additional_length = sensep[7]; } else { /* fixed format */ if (sb_len > 2) sshp->sense_key = (0xf & sensep[2]); if (sb_len > 7) { sb_len = (sb_len < (sensep[7] + 8)) ? sb_len : (sensep[7] + 8); if (sb_len > 12) sshp->asc = sensep[12]; if (sb_len > 13) sshp->ascq = sensep[13]; } } } return 1; } ///////////////////////////////////////////////////////////////////////////// namespace sat { /// Cypress USB Brigde support. class usbcypress_device : public tunnelled_device< /*implements*/ ata_device_with_command_set /*by tunnelling through a*/, scsi_device > { public: usbcypress_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, unsigned char signature); virtual ~usbcypress_device() throw(); protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); unsigned char m_signature; }; usbcypress_device::usbcypress_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, unsigned char signature) : smart_device(intf, scsidev->get_dev_name(), "usbcypress", req_type), tunnelled_device(scsidev), m_signature(signature) { set_info().info_name = strprintf("%s [USB Cypress]", scsidev->get_info_name()); } usbcypress_device::~usbcypress_device() throw() { } /* see cy7c68300c_8.pdf for more information */ #define USBCYPRESS_PASSTHROUGH_LEN 16 int usbcypress_device::ata_command_interface(smart_command_set command, int select, char *data) { struct scsi_cmnd_io io_hdr; unsigned char cdb[USBCYPRESS_PASSTHROUGH_LEN]; unsigned char sense[32]; int copydata = 0; int outlen = 0; int ck_cond = 0; /* set to 1 to read register(s) back */ int t_dir = 1; /* 0 -> to device, 1 -> from device */ int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ int t_length = 0; /* 0 -> no data transferred */ int feature = 0; int ata_command = 0; int sector_count = 0; int lba_low = 0; int lba_mid = 0; int lba_high = 0; int passthru_size = USBCYPRESS_PASSTHROUGH_LEN; memset(cdb, 0, sizeof(cdb)); memset(sense, 0, sizeof(sense)); ata_command = ATA_SMART_CMD; switch (command) { case CHECK_POWER_MODE: ata_command = ATA_CHECK_POWER_MODE; ck_cond = 1; copydata = 1; break; case READ_VALUES: /* READ DATA */ feature = ATA_SMART_READ_VALUES; sector_count = 1; /* one (512 byte) block */ t_length = 2; /* sector count holds count */ copydata = 512; break; case READ_THRESHOLDS: /* obsolete */ feature = ATA_SMART_READ_THRESHOLDS; sector_count = 1; /* one (512 byte) block */ lba_low = 1; t_length = 2; /* sector count holds count */ copydata=512; break; case READ_LOG: feature = ATA_SMART_READ_LOG_SECTOR; sector_count = 1; /* one (512 byte) block */ lba_low = select; t_length = 2; /* sector count holds count */ copydata = 512; break; case WRITE_LOG: feature = ATA_SMART_WRITE_LOG_SECTOR; sector_count = 1; /* one (512 byte) block */ lba_low = select; t_length = 2; /* sector count holds count */ t_dir = 0; /* to device */ outlen = 512; break; case IDENTIFY: ata_command = ATA_IDENTIFY_DEVICE; sector_count = 1; /* one (512 byte) block */ t_length = 2; /* sector count holds count */ copydata = 512; break; case PIDENTIFY: ata_command = ATA_IDENTIFY_PACKET_DEVICE; sector_count = 1; /* one (512 byte) block */ t_length = 2; /* sector count (7:0) holds count */ copydata = 512; break; case ENABLE: feature = ATA_SMART_ENABLE; lba_low = 1; break; case DISABLE: feature = ATA_SMART_DISABLE; lba_low = 1; break; case STATUS: // this command only says if SMART is working. It could be // replaced with STATUS_CHECK below. feature = ATA_SMART_STATUS; ck_cond = 1; break; case AUTO_OFFLINE: feature = ATA_SMART_AUTO_OFFLINE; sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! break; case AUTOSAVE: feature = ATA_SMART_AUTOSAVE; sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! break; case IMMEDIATE_OFFLINE: feature = ATA_SMART_IMMEDIATE_OFFLINE; lba_low = select; break; case STATUS_CHECK: // This command uses HDIO_DRIVE_TASK and has different syntax than // the other commands. feature = ATA_SMART_STATUS; /* SMART RETURN STATUS */ ck_cond = 1; break; default: pout("Unrecognized command %d in usbcypress_device::ata_command_interface()\n" "Please contact " PACKAGE_BUGREPORT "\n", command); errno=ENOSYS; return -1; } if (ATA_SMART_CMD == ata_command) { lba_mid = 0x4f; lba_high = 0xc2; } cdb[0] = m_signature; // bVSCBSignature : vendor-specific command cdb[1] = 0x24; // bVSCBSubCommand : 0x24 for ATACB cdb[2] = 0x0; if (ata_command == ATA_IDENTIFY_DEVICE || ata_command == ATA_IDENTIFY_PACKET_DEVICE) cdb[2] |= (1<<7); //set IdentifyPacketDevice for these cmds cdb[3] = 0xff - (1<<0) - (1<<6); //features, sector count, lba low, lba med // lba high, command are valid cdb[4] = byte_block; //TransferBlockCount : 512 cdb[6] = feature; cdb[7] = sector_count; cdb[8] = lba_low; cdb[9] = lba_mid; cdb[10] = lba_high; cdb[12] = ata_command; memset(&io_hdr, 0, sizeof(io_hdr)); if (0 == t_length) { io_hdr.dxfer_dir = DXFER_NONE; io_hdr.dxfer_len = 0; } else if (t_dir) { /* from device */ io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = copydata; io_hdr.dxferp = (unsigned char *)data; memset(data, 0, copydata); /* prefill with zeroes */ } else { /* to device */ io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = outlen; io_hdr.dxferp = (unsigned char *)data; } io_hdr.cmnd = cdb; io_hdr.cmnd_len = passthru_size; io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through(&io_hdr)) { if (scsi_debugmode > 0) pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, " "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg()); set_err(scsidev->get_err()); return -1; } // if there is a sense the command failed or the // device doesn't support usbcypress if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) { return -1; } if (ck_cond) { unsigned char ardp[8]; int ard_len = 8; /* XXX this is racy if there other scsi command between * the first usbcypress command and this one */ //pout("If you got strange result, please retry without traffic on the disc\n"); /* we use the same command as before, but we set * * the read taskfile bit, for not executing usbcypress command, * * but reading register selected in srb->cmnd[4] */ cdb[2] = (1<<0); /* ask read taskfile */ memset(sense, 0, sizeof(sense)); /* transfer 8 bytes */ memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = ard_len; io_hdr.dxferp = (unsigned char *)ardp; memset(ardp, 0, ard_len); /* prefill with zeroes */ io_hdr.cmnd = cdb; io_hdr.cmnd_len = passthru_size; io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!scsidev->scsi_pass_through(&io_hdr)) { if (scsi_debugmode > 0) pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, " "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg()); set_err(scsidev->get_err()); return -1; } // if there is a sense the command failed or the // device doesn't support usbcypress if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) { return -1; } if (scsi_debugmode > 1) { pout("Values from ATA Return Descriptor are:\n"); dStrHex((const uint8_t *)ardp, ard_len, 1); } if (ATA_CHECK_POWER_MODE == ata_command) data[0] = ardp[2]; /* sector count (0:7) */ else if (STATUS_CHECK == command) { if ((ardp[4] == 0x4f) && (ardp[5] == 0xc2)) return 0; /* GOOD smart status */ if ((ardp[4] == 0xf4) && (ardp[5] == 0x2c)) return 1; // smart predicting failure, "bad" status // We haven't gotten output that makes sense so // print out some debugging info syserror("Error SMART Status command failed"); pout("This may be due to a race in usbcypress\n"); pout("Retry without other disc access\n"); pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); pout("Values from ATA Return Descriptor are:\n"); dStrHex((const uint8_t *)ardp, ard_len, 1); return -1; } } return 0; } #if 0 // Not used, see autodetect_sat_device() below. static int isprint_string(const char *s) { while (*s) { if (isprint(*s) == 0) return 0; s++; } return 1; } /* Attempt an IDENTIFY DEVICE ATA or IDENTIFY PACKET DEVICE command If successful return 1, else 0 */ // TODO: Combine with has_sat_pass_through above static int has_usbcypress_pass_through(ata_device * atadev, const char *manufacturer, const char *product) { struct ata_identify_device drive; char model[40], serial[20], firm[8]; /* issue the command and do a checksum if possible */ if (ataReadHDIdentity(atadev, &drive) < 0) return 0; /* check if model string match, revision doesn't work for me */ format_ata_string(model, drive.model, 40); if (*model == 0 || isprint_string(model) == 0) return 0; if (manufacturer && strncmp(manufacturer, model, 8)) pout("manufacturer doesn't match in pass_through test\n"); if (product && strlen(model) > 8 && strncmp(product, model+8, strlen(model)-8)) pout("product doesn't match in pass_through test\n"); /* check serial */ format_ata_string(serial, drive.serial_no, 20); if (isprint_string(serial) == 0) return 0; format_ata_string(firm, drive.fw_rev, 8); if (isprint_string(firm) == 0) return 0; return 1; } #endif ///////////////////////////////////////////////////////////////////////////// /// JMicron USB Bridge support. class usbjmicron_device : public tunnelled_device< /*implements*/ ata_device, /*by tunnelling through a*/ scsi_device > { public: usbjmicron_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, bool prolific, bool ata_48bit_support, int port); virtual ~usbjmicron_device() throw(); virtual bool open(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); private: bool get_registers(unsigned short addr, unsigned char * buf, unsigned short size); bool m_prolific; bool m_ata_48bit_support; int m_port; }; usbjmicron_device::usbjmicron_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, bool prolific, bool ata_48bit_support, int port) : smart_device(intf, scsidev->get_dev_name(), "usbjmicron", req_type), tunnelled_device(scsidev), m_prolific(prolific), m_ata_48bit_support(ata_48bit_support), m_port(port >= 0 || !prolific ? port : 0) { set_info().info_name = strprintf("%s [USB JMicron]", scsidev->get_info_name()); } usbjmicron_device::~usbjmicron_device() throw() { } bool usbjmicron_device::open() { // Open USB first if (!tunnelled_device::open()) return false; // Detect port if not specified if (m_port < 0) { unsigned char regbuf[1] = {0}; if (!get_registers(0x720f, regbuf, sizeof(regbuf))) { close(); return false; } switch (regbuf[0] & 0x44) { case 0x04: m_port = 0; break; case 0x40: m_port = 1; break; case 0x44: close(); return set_err(EINVAL, "Two devices connected, try '-d usbjmicron,[01]'"); default: close(); return set_err(ENODEV, "No device connected"); } } return true; } bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_smart_status | (m_ata_48bit_support ? ata_device::supports_48bit_hi_null : 0), "JMicron") ) return false; if (m_port < 0) return set_err(EIO, "Unknown JMicron port"); scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr)); bool rwbit = true; unsigned char smart_status = 0xff; bool is_smart_status = ( in.in_regs.command == ATA_SMART_CMD && in.in_regs.features == ATA_SMART_STATUS); if (is_smart_status && in.out_needed.is_set()) { io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = 1; io_hdr.dxferp = &smart_status; } else switch (in.direction) { case ata_cmd_in::no_data: io_hdr.dxfer_dir = DXFER_NONE; break; case ata_cmd_in::data_in: io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; memset(in.buffer, 0, in.size); break; case ata_cmd_in::data_out: io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; rwbit = false; break; default: return set_err(EINVAL); } // Build pass through command unsigned char cdb[14]; cdb[ 0] = 0xdf; cdb[ 1] = (rwbit ? 0x10 : 0x00); cdb[ 2] = 0x00; sg_put_unaligned_be16(io_hdr.dxfer_len, cdb + 3); cdb[ 5] = in.in_regs.features; cdb[ 6] = in.in_regs.sector_count; cdb[ 7] = in.in_regs.lba_low; cdb[ 8] = in.in_regs.lba_mid; cdb[ 9] = in.in_regs.lba_high; cdb[10] = in.in_regs.device | (m_port == 0 ? 0xa0 : 0xb0); cdb[11] = in.in_regs.command; // Prolific PL3507 cdb[12] = 0x06; cdb[13] = 0x7b; io_hdr.cmnd = cdb; io_hdr.cmnd_len = (!m_prolific ? 12 : 14); scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbjmicron_device::ata_pass_through: ")) return set_err(scsidev->get_err()); if (in.out_needed.is_set()) { if (is_smart_status) { if (io_hdr.resid == 1) // Some (Prolific) USB bridges do not transfer a status byte return set_err(ENOSYS, "Incomplete response, status byte missing [JMicron]"); switch (smart_status) { case 0xc2: out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f; break; case 0x2c: out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4; break; default: // Some (JM20336) USB bridges always return 0x01, regardless of SMART Status return set_err(ENOSYS, "Invalid status byte (0x%02x) [JMicron]", smart_status); } } #if 0 // Not needed for SMART STATUS, see also notes below else { // Read ATA output registers // NOTE: The register addresses are not valid for some older chip revisions // NOTE: There is a small race condition here! unsigned char regbuf[16] = {0, }; if (!get_registers((m_port == 0 ? 0x8000 : 0x9000), regbuf, sizeof(regbuf))) return false; out.out_regs.sector_count = regbuf[ 0]; out.out_regs.lba_mid = regbuf[ 4]; out.out_regs.lba_low = regbuf[ 6]; out.out_regs.device = regbuf[ 9]; out.out_regs.lba_high = regbuf[10]; out.out_regs.error = regbuf[13]; out.out_regs.status = regbuf[14]; } #endif } return true; } bool usbjmicron_device::get_registers(unsigned short addr, unsigned char * buf, unsigned short size) { unsigned char cdb[14]; cdb[ 0] = 0xdf; cdb[ 1] = 0x10; cdb[ 2] = 0x00; sg_put_unaligned_be16(size, cdb + 3); cdb[ 5] = 0x00; sg_put_unaligned_be16(addr, cdb + 6); cdb[ 8] = 0x00; cdb[ 9] = 0x00; cdb[10] = 0x00; cdb[11] = 0xfd; // Prolific PL3507 cdb[12] = 0x06; cdb[13] = 0x7b; scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = size; io_hdr.dxferp = buf; io_hdr.cmnd = cdb; io_hdr.cmnd_len = (!m_prolific ? 12 : 14); scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbjmicron_device::get_registers: ")) return set_err(scsidev->get_err()); return true; } ///////////////////////////////////////////////////////////////////////////// /// Prolific USB Bridge support. (PL2773) (Probably works on PL2771 also...) class usbprolific_device : public tunnelled_device< /*implements*/ ata_device, /*by tunnelling through a*/ scsi_device > { public: usbprolific_device(smart_interface * intf, scsi_device * scsidev, const char * req_type); virtual ~usbprolific_device() throw(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); }; usbprolific_device::usbprolific_device(smart_interface * intf, scsi_device * scsidev, const char * req_type) : smart_device(intf, scsidev->get_dev_name(), "usbprolific", req_type), tunnelled_device(scsidev) { set_info().info_name = strprintf("%s [USB Prolific]", scsidev->get_info_name()); } usbprolific_device::~usbprolific_device() throw() { } bool usbprolific_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_48bit_hi_null | ata_device::supports_output_regs | ata_device::supports_smart_status, "Prolific" ) ) return false; scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr)); unsigned char cmd_rw = 0x10; // Read switch (in.direction) { case ata_cmd_in::no_data: io_hdr.dxfer_dir = DXFER_NONE; break; case ata_cmd_in::data_in: io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; memset(in.buffer, 0, in.size); break; case ata_cmd_in::data_out: io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; cmd_rw = 0x0; // Write break; default: return set_err(EINVAL); } // Based on reverse engineering of iSmart.exe with API Monitor. // Seen commands: // D0 0 0 0 06 7B 0 0 0 0 0 0 // Read Firmware info?, reads 16 bytes // F4 0 0 0 06 7B // ?? // D8 15 0 D8 06 7B 0 0 0 0 1 1 4F C2 A0 B0 // SMART Enable // D8 15 0 D0 06 7B 0 0 2 0 1 1 4F C2 A0 B0 // SMART Read values // D8 15 0 D1 06 7B 0 0 2 0 1 1 4F C2 A0 B0 // SMART Read thresholds // D8 15 0 D4 06 7B 0 0 0 0 0 1 4F C2 A0 B0 // SMART Execute self test // D7 0 0 0 06 7B 0 0 0 0 0 0 0 0 0 0 // Read status registers, Reads 16 bytes of data // Additional DATA OUT support based on document from Prolific // Build pass through command unsigned char cdb[16]; cdb[ 0] = 0xD8; // Operation Code (D8 = Prolific ATA pass through) cdb[ 1] = cmd_rw|0x5; // Read(0x10)/Write(0x0) | NORMAL(0x5)/PREFIX(0x0)(?) cdb[ 2] = 0x0; // Reserved cdb[ 3] = in.in_regs.features; // Feature register (SMART command) cdb[ 4] = 0x06; // Check Word (VendorID magic, Prolific: 0x067B) cdb[ 5] = 0x7B; // Check Word (VendorID magic, Prolific: 0x067B) sg_put_unaligned_be32(io_hdr.dxfer_len, cdb + 6); cdb[10] = in.in_regs.sector_count; // Sector Count cdb[11] = in.in_regs.lba_low; // LBA Low (7:0) cdb[12] = in.in_regs.lba_mid; // LBA Mid (15:8) cdb[13] = in.in_regs.lba_high; // LBA High (23:16) cdb[14] = in.in_regs.device | 0xA0; // Device/Head cdb[15] = in.in_regs.command; // ATA Command Register (only PIO supported) // Use '-r scsiioctl,1' to print CDB for debug purposes io_hdr.cmnd = cdb; io_hdr.cmnd_len = 16; scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbprolific_device::ata_pass_through: ")) return set_err(scsidev->get_err()); if (in.out_needed.is_set()) { // Read ATA output registers unsigned char regbuf[16] = {0, }; memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = sizeof(regbuf); io_hdr.dxferp = regbuf; memset(cdb, 0, sizeof(cdb)); cdb[ 0] = 0xD7; // Prolific read registers cdb[ 4] = 0x06; // Check Word (VendorID magic, Prolific: 0x067B) cdb[ 5] = 0x7B; // Check Word (VendorID magic, Prolific: 0x067B) io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbprolific_device::scsi_pass_through (get registers): ")) return set_err(scsidev->get_err()); // Use '-r scsiioctl,2' to print input registers for debug purposes // Example: 50 00 00 00 00 01 4f 00 c2 00 a0 da 00 b0 00 50 out.out_regs.status = regbuf[0]; // Status out.out_regs.error = regbuf[1]; // Error out.out_regs.sector_count = regbuf[2]; // Sector Count (7:0) out.out_regs.lba_low = regbuf[4]; // LBA Low (7:0) out.out_regs.lba_mid = regbuf[6]; // LBA Mid (7:0) out.out_regs.lba_high = regbuf[8]; // LBA High (7:0) out.out_regs.device = regbuf[10]; // Device/Head // = regbuf[11]; // ATA Feature (7:0) // = regbuf[13]; // ATA Command } return true; } ///////////////////////////////////////////////////////////////////////////// /// SunplusIT USB Bridge support. class usbsunplus_device : public tunnelled_device< /*implements*/ ata_device, /*by tunnelling through a*/ scsi_device > { public: usbsunplus_device(smart_interface * intf, scsi_device * scsidev, const char * req_type); virtual ~usbsunplus_device() throw(); virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); }; usbsunplus_device::usbsunplus_device(smart_interface * intf, scsi_device * scsidev, const char * req_type) : smart_device(intf, scsidev->get_dev_name(), "usbsunplus", req_type), tunnelled_device(scsidev) { set_info().info_name = strprintf("%s [USB Sunplus]", scsidev->get_info_name()); } usbsunplus_device::~usbsunplus_device() throw() { } bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { if (!ata_cmd_is_supported(in, ata_device::supports_data_out | ata_device::supports_output_regs | ata_device::supports_48bit, "Sunplus") ) return false; scsi_cmnd_io io_hdr; unsigned char cdb[12]; if (in.in_regs.is_48bit_cmd()) { // Set "previous" registers memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.dxfer_dir = DXFER_NONE; cdb[ 0] = 0xf8; cdb[ 1] = 0x00; cdb[ 2] = 0x23; // Subcommand: Pass through presetting cdb[ 3] = 0x00; cdb[ 4] = 0x00; cdb[ 5] = in.in_regs.prev.features; cdb[ 6] = in.in_regs.prev.sector_count; cdb[ 7] = in.in_regs.prev.lba_low; cdb[ 8] = in.in_regs.prev.lba_mid; cdb[ 9] = in.in_regs.prev.lba_high; cdb[10] = 0x00; cdb[11] = 0x00; io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbsunplus_device::scsi_pass_through (presetting): ")) return set_err(scsidev->get_err()); } // Run Pass through command memset(&io_hdr, 0, sizeof(io_hdr)); unsigned char protocol; switch (in.direction) { case ata_cmd_in::no_data: io_hdr.dxfer_dir = DXFER_NONE; protocol = 0x00; break; case ata_cmd_in::data_in: io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; memset(in.buffer, 0, in.size); protocol = 0x10; break; case ata_cmd_in::data_out: io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = in.size; io_hdr.dxferp = (unsigned char *)in.buffer; protocol = 0x11; break; default: return set_err(EINVAL); } cdb[ 0] = 0xf8; cdb[ 1] = 0x00; cdb[ 2] = 0x22; // Subcommand: Pass through cdb[ 3] = protocol; cdb[ 4] = (unsigned char)(io_hdr.dxfer_len >> 9); cdb[ 5] = in.in_regs.features; cdb[ 6] = in.in_regs.sector_count; cdb[ 7] = in.in_regs.lba_low; cdb[ 8] = in.in_regs.lba_mid; cdb[ 9] = in.in_regs.lba_high; cdb[10] = in.in_regs.device | 0xa0; cdb[11] = in.in_regs.command; io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbsunplus_device::scsi_pass_through: ")) // Returns sense key 0x03 (medium error) on ATA command error return set_err(scsidev->get_err()); if (in.out_needed.is_set()) { // Read ATA output registers unsigned char regbuf[8] = {0, }; memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = sizeof(regbuf); io_hdr.dxferp = regbuf; cdb[ 0] = 0xf8; cdb[ 1] = 0x00; cdb[ 2] = 0x21; // Subcommand: Get status memset(cdb+3, 0, sizeof(cdb)-3); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); if (!scsidev->scsi_pass_through_and_check(&io_hdr, "usbsunplus_device::scsi_pass_through (get registers): ")) return set_err(scsidev->get_err()); out.out_regs.error = regbuf[1]; out.out_regs.sector_count = regbuf[2]; out.out_regs.lba_low = regbuf[3]; out.out_regs.lba_mid = regbuf[4]; out.out_regs.lba_high = regbuf[5]; out.out_regs.device = regbuf[6]; out.out_regs.status = regbuf[7]; } return true; } } // namespace using namespace sat; ///////////////////////////////////////////////////////////////////////////// // Return ATA->SCSI filter for SAT or USB. ata_device * smart_interface::get_sat_device(const char * type, scsi_device * scsidev) { if (!scsidev) throw std::logic_error("smart_interface: get_sat_device() called with scsidev=0"); // Take temporary ownership of 'scsidev' to delete it on error scsi_device_auto_ptr scsidev_holder(scsidev); ata_device * satdev = 0; if (!strncmp(type, "sat", 3)) { const char * t = type + 3; sat_device::sat_scsi_mode mode = sat_device::sat_always; if (!strncmp(t, ",auto", 5)) { t += 5; mode = sat_device::sat_auto; } int ptlen = 0, n = -1; if (*t && !(sscanf(t, ",%d%n", &ptlen, &n) == 1 && n == (int)strlen(t) && (ptlen == 0 || ptlen == 12 || ptlen == 16))) { set_err(EINVAL, "Option '-d sat[,auto][,N]' requires N to be 0, 12 or 16"); return 0; } satdev = new sat_device(this, scsidev, type, mode, ptlen); } else if (!strcmp(type, "scsi")) { satdev = new sat_device(this, scsidev, type, sat_device::scsi_always); } else if (!strncmp(type, "usbcypress", 10)) { unsigned signature = 0x24; int n1 = -1, n2 = -1; if (!(((sscanf(type, "usbcypress%n,0x%x%n", &n1, &signature, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type)) && signature <= 0xff)) { set_err(EINVAL, "Option '-d usbcypress,' requires to be " "an hexadecimal number between 0x0 and 0xff"); return 0; } satdev = new usbcypress_device(this, scsidev, type, signature); } else if (!strncmp(type, "usbjmicron", 10)) { const char * t = type + 10; bool prolific = false; if (!strncmp(t, ",p", 2)) { t += 2; prolific = true; } bool ata_48bit_support = false; if (!strncmp(t, ",x", 2)) { t += 2; ata_48bit_support = true; } int port = -1, n = -1; if (*t && !( (sscanf(t, ",%d%n", &port, &n) == 1 && n == (int)strlen(t) && 0 <= port && port <= 1))) { set_err(EINVAL, "Option '-d usbjmicron[,p][,x],' requires to be 0 or 1"); return 0; } satdev = new usbjmicron_device(this, scsidev, type, prolific, ata_48bit_support, port); } else if (!strcmp(type, "usbprolific")) { satdev = new usbprolific_device(this, scsidev, type); } else if (!strcmp(type, "usbsunplus")) { satdev = new usbsunplus_device(this, scsidev, type); } else { set_err(EINVAL, "Unknown USB device type '%s'", type); return 0; } // 'scsidev' is now owned by 'satdev' scsidev_holder.release(); return satdev; } // Try to detect a SAT device behind a SCSI interface. ata_device * smart_interface::autodetect_sat_device(scsi_device * scsidev, const unsigned char * inqdata, unsigned inqsize) { if (!scsidev->is_open()) return 0; // SAT ? if (inqdata && inqsize >= 36 && !memcmp(inqdata + 8, "ATA ", 8)) { // TODO: Linux-specific? No, all SAT standards say the 'T10 Vendor // Identification' field shall be 'ATA '. ata_device_auto_ptr atadev( new sat_device(this, scsidev, "") , scsidev); if (has_sat_pass_through(atadev.get())) return atadev.release(); // Detected SAT } return 0; } ///////////////////////////////////////////////////////////////////////////// // USB device type detection // Format USB ID for error messages static std::string format_usb_id(int vendor_id, int product_id, int version) { if (version >= 0) return strprintf("[0x%04x:0x%04x (0x%03x)]", vendor_id, product_id, version); else return strprintf("[0x%04x:0x%04x]", vendor_id, product_id); } // Get type name for USB device with known VENDOR:PRODUCT ID. const char * smart_interface::get_usb_dev_type_by_id(int vendor_id, int product_id, int version /*= -1*/) { usb_dev_info info, info2; int n = lookup_usb_device(vendor_id, product_id, version, info, info2); if (n <= 0) { set_err(EINVAL, "Unknown USB bridge %s", format_usb_id(vendor_id, product_id, version).c_str()); return 0; } if (n > 1) { set_err(EINVAL, "USB bridge %s type is ambiguous: '%s' or '%s'", format_usb_id(vendor_id, product_id, version).c_str(), (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]"), (!info2.usb_type.empty() ? info2.usb_type.c_str() : "[unsupported]")); return 0; } if (info.usb_type.empty()) { set_err(ENOSYS, "Unsupported USB bridge %s", format_usb_id(vendor_id, product_id, version).c_str()); return 0; } // TODO: change return type to std::string static std::string type; type = info.usb_type; return type.c_str(); } smartmontools-7.0/scsicmds.cpp0000644000175000010010000030571313401001476013530 00000000000000/* * scsicmds.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-8 Bruce Allen * Copyright (C) 1999-2000 Michael Cornwell * Copyright (C) 2003-18 Douglas Gilbert * * SPDX-License-Identifier: GPL-2.0-or-later * * * 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 #include #include "config.h" #include "scsicmds.h" #include "atacmds.h" // FIXME: for smart_command_set only #include "dev_interface.h" #include "utility.h" #include "sg_unaligned.h" const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 4842 2018-12-02 16:07:26Z chrfranke $" SCSICMDS_H_CVSID; static const char * logSenStr = "Log Sense"; // Print SCSI debug messages? unsigned char scsi_debugmode = 0; supported_vpd_pages * supported_vpd_pages_p = NULL; supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0) { unsigned char b[0xfc]; /* pre SPC-3 INQUIRY max response size */ memset(b, 0, sizeof(b)); if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES, b, sizeof(b)))) { num_valid = sg_get_unaligned_be16(b + 2); int n = sizeof(pages); if (num_valid > n) num_valid = n; memcpy(pages, b + 4, num_valid); } } bool supported_vpd_pages::is_supported(int vpd_page_num) const { /* Supported VPD pages numbers start at offset 4 and should be in * ascending order but don't assume that. */ for (int k = 0; k < num_valid; ++k) { if (vpd_page_num == pages[k]) return true; } return false; } /* output binary in ASCII hex and optionally ASCII. Uses pout() for output. */ void dStrHex(const uint8_t * up, int len, int no_ascii) { const uint8_t * p = up; 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 = snprintf(buff+1, sizeof(buff)-1, "%.2x", a); buff[k + 1] = ' '; if (bpos >= ((bpstart + (9 * 3)))) bpos++; for(i = 0; i < len; i++) { uint8_t c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (unsigned int)c); buff[bpos + 2] = ' '; if (no_ascii) buff[cpos++] = ' '; else { if ((c < ' ') || (c >= 0x7f)) c='.'; buff[cpos++] = c; } if (cpos > (cpstart+15)) { while (cpos > 0 && buff[cpos-1] == ' ') cpos--; buff[cpos] = 0; pout("%s\n", buff); bpos = bpstart; cpos = cpstart; a += 16; memset(buff,' ',80); k = snprintf(buff+1, sizeof(buff)-1, "%.2x", a); buff[k + 1] = ' '; } } if (cpos > cpstart) { while (cpos > 0 && buff[cpos-1] == ' ') cpos--; buff[cpos] = 0; pout("%s\n", buff); } } /* This is a heuristic that takes into account the command bytes and length * to decide whether the presented unstructured sequence of bytes could be * a SCSI command. If so it returns true otherwise false. Vendor specific * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The * only SCSI commands considered above 16 bytes of length are the Variable * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). * Both have an inbuilt length field which can be cross checked with clen. * No NVMe commands (64 bytes long plus some extra added by some OSes) have * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS * structures that are sent across the wire. The FIS register structure is * used to move a command from a SATA host to device, but the ATA 'command' * is not the first byte. So it is harder to say what will happen if a * FIS structure is presented as a SCSI command, hopefully there is a low * probability this function will yield true in that case. */ bool is_scsi_cdb(const uint8_t * cdbp, int clen) { int ilen, sa; uint8_t opcode; uint8_t top3bits; if (clen < 6) return false; opcode = cdbp[0]; top3bits = opcode >> 5; if (0x3 == top3bits) { /* Opcodes 0x60 to 0x7f */ if ((clen < 12) || (clen % 4)) return false; /* must be modulo 4 and 12 or more bytes */ switch (opcode) { case 0x7e: /* Extended cdb (XCDB) */ ilen = 4 + sg_get_unaligned_be16(cdbp + 2); return (ilen == clen); case 0x7f: /* Variable Length cdb */ ilen = 8 + cdbp[7]; sa = sg_get_unaligned_be16(cdbp + 8); /* service action (sa) 0x0 is reserved */ return ((ilen == clen) && sa); default: return false; } } else if (clen <= 16) { switch (clen) { case 6: if (top3bits > 0x5) /* vendor */ return true; return (0x0 == top3bits); /* 6 byte cdb */ case 10: if (top3bits > 0x5) /* vendor */ return true; return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */ case 16: if (top3bits > 0x5) /* vendor */ return true; return (0x4 == top3bits); /* 16 byte cdb */ case 12: if (top3bits > 0x5) /* vendor */ return true; return (0x5 == top3bits); /* 12 byte cdb */ default: return false; } } /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or * opcode > 0x7f). */ return false; } struct scsi_opcode_name { uint8_t 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(6)"}, /* 0x15 */ {MODE_SENSE, "mode sense(6)"}, /* 0x1a */ {START_STOP_UNIT, "start stop unit"}, /* 0x1b */ {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */ {SEND_DIAGNOSTIC, "send diagnostic"}, /* 0x1d */ {READ_CAPACITY_10, "read capacity(10)"}, /* 0x25 */ {READ_DEFECT_10, "read defect list(10)"}, /* 0x37 */ {LOG_SELECT, "log select"}, /* 0x4c */ {LOG_SENSE, "log sense"}, /* 0x4d */ {MODE_SELECT_10, "mode select(10)"}, /* 0x55 */ {MODE_SENSE_10, "mode sense(10)"}, /* 0x5a */ {SAT_ATA_PASSTHROUGH_16, "ata pass-through(16)"}, /* 0x85 */ {READ_CAPACITY_16, "read capacity(16)"}, /* 0x9e,0x10 */ {REPORT_LUNS, "report luns"}, /* 0xa0 */ {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */ {READ_DEFECT_12, "read defect list(12)"}, /* 0xb7 */ }; static const char * vendor_specific = ""; /* Need to expand to take service action into account. For commands * of interest the service action is in the 2nd command byte */ const char * scsi_get_opcode_name(uint8_t opcode) { int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]); if (opcode >= 0xc0) return vendor_specific; for (int k = 0; k < len; ++k) { struct scsi_opcode_name * 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) { int resp_code = (io_buf->sensep[0] & 0x7f); out->resp_code = resp_code; if (resp_code >= 0x72) { out->sense_key = (io_buf->sensep[1] & 0xf); out->asc = io_buf->sensep[2]; out->ascq = io_buf->sensep[3]; } else if (resp_code >= 0x70) { 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]; } } } } int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo) { switch (sinfo->sense_key) { case SCSI_SK_NO_SENSE: case SCSI_SK_RECOVERED_ERR: return SIMPLE_NO_ERROR; case SCSI_SK_NOT_READY: if (SCSI_ASC_NO_MEDIUM == sinfo->asc) 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_INVALID_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; case SCSI_SK_ABORTED_COMMAND: return SIMPLE_ERR_ABORTED_COMMAND; default: return SIMPLE_ERR_UNKNOWN; } } 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)"; case SIMPLE_ERR_UNKNOWN: return "unknown error (unexpected sense key)"; case SIMPLE_ERR_ABORTED_COMMAND: return "aborted command"; default: return "unknown error"; } } /* Iterates to next designation descriptor in the device identification * VPD page. The 'initial_desig_desc' should point to start of first * descriptor with 'page_len' being the number of valid bytes in that * and following descriptors. To start, 'off' should point to a negative * value, thereafter it should point to the value yielded by the previous * call. If 0 returned then 'initial_desig_desc + *off' should be a valid * descriptor; returns -1 if normal end condition and -2 for an abnormal * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set) { const unsigned char * ucp; int k; for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) { k = (k < 0) ? 0 : (k + ucp[k + 3] + 4); if ((k + 4) > page_len) break; int c_set = (ucp[k] & 0xf); if ((m_code_set >= 0) && (m_code_set != c_set)) continue; int assoc = ((ucp[k + 1] >> 4) & 0x3); if ((m_assoc >= 0) && (m_assoc != assoc)) continue; int desig_type = (ucp[k + 1] & 0xf); if ((m_desig_type >= 0) && (m_desig_type != desig_type)) continue; *off = k; return 0; } return (k == page_len) ? -1 : -2; } /* Decode VPD page 0x83 logical unit designator into a string. If both * numeric address and SCSI name string present, prefer the former. * Returns 0 on success, -1 on error with error string in s. */ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen, int * transport) { if (transport) *transport = -1; if (slen < 32) { if (slen > 0) s[0] = '\0'; return -1; } s[0] = '\0'; int si = 0; int have_scsi_ns = 0; int off = -1; int u; while ((u = scsi_vpd_dev_id_iter(b, blen, &off, -1, -1, -1)) == 0) { const unsigned char * ucp = b + off; int i_len = ucp[3]; if ((off + i_len + 4) > blen) { snprintf(s+si, slen-si, "error: designator length"); return -1; } int assoc = ((ucp[1] >> 4) & 0x3); if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0)) *transport = (ucp[0] >> 4) & 0xf; if (0 != assoc) continue; const unsigned char * ip = ucp + 4; int c_set = (ucp[0] & 0xf); int desig_type = (ucp[1] & 0xf); int naa; switch (desig_type) { case 0: /* vendor specific */ case 1: /* T10 vendor identification */ break; case 2: /* EUI-64 based */ if ((8 != i_len) && (12 != i_len) && (16 != i_len)) { snprintf(s+si, slen-si, "error: EUI-64 length"); return -1; } if (have_scsi_ns) si = 0; si += snprintf(s+si, slen-si, "0x"); for (int m = 0; m < i_len; ++m) si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); break; case 3: /* NAA */ if (1 != c_set) { snprintf(s+si, slen-si, "error: NAA bad code_set"); return -1; } naa = (ip[0] >> 4) & 0xff; if ((naa < 2) || (naa > 6) || (4 == naa)) { snprintf(s+si, slen-si, "error: unexpected NAA"); return -1; } if (have_scsi_ns) si = 0; if (2 == naa) { /* NAA IEEE Extended */ if (8 != i_len) { snprintf(s+si, slen-si, "error: NAA 2 length"); return -1; } si += snprintf(s+si, slen-si, "0x"); for (int m = 0; m < 8; ++m) si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } else if ((3 == naa ) || (5 == naa)) { /* NAA=3 Locally assigned; NAA=5 IEEE Registered */ if (8 != i_len) { snprintf(s+si, slen-si, "error: NAA 3 or 5 length"); return -1; } si += snprintf(s+si, slen-si, "0x"); for (int m = 0; m < 8; ++m) si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } else if (6 == naa) { /* NAA IEEE Registered extended */ if (16 != i_len) { snprintf(s+si, slen-si, "error: NAA 6 length"); return -1; } si += snprintf(s+si, slen-si, "0x"); for (int m = 0; m < 16; ++m) si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]); } break; case 4: /* Relative target port */ case 5: /* (primary) Target port group */ case 6: /* Logical unit group */ case 7: /* MD5 logical unit identifier */ break; case 8: /* SCSI name string */ if (3 != c_set) { snprintf(s+si, slen-si, "error: SCSI name string"); return -1; } /* does %s print out UTF-8 ok?? */ if (si == 0) { si += snprintf(s+si, slen-si, "%s", (const char *)ip); ++have_scsi_ns; } break; default: /* reserved */ break; } } if (-2 == u) { snprintf(s+si, slen-si, "error: bad structure"); return -1; } return 0; } /* 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. If * known_resp_len < 0 then does single fetch for BufLen bytes. */ int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, uint8_t *pBuf, int bufLen, int known_resp_len) { int pageLen; struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; if (known_resp_len > bufLen) return -EIO; if (known_resp_len > 0) pageLen = known_resp_len; else if (known_resp_len < 0) pageLen = bufLen; else { /* 0 == known_resp_len */ /* 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[3] = subpagenum; /* 0 for no sub-page */ sg_put_unaligned_be16(pageLen, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); int res; if ((res = scsiSimpleSenseFilter(&sinfo))) return res; /* sanity check on response */ if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum)) return SIMPLE_ERR_BAD_RESP; uint16_t u = sg_get_unaligned_be16(pBuf + 2); if (0 == u) return SIMPLE_ERR_BAD_RESP; pageLen = u + 4; if (4 == pageLen) /* why define a lpage with no payload? */ pageLen = 252; /* some IBM tape drives don't like double fetch */ /* some SCSI HBA don't like "odd" length transfers */ if (pageLen % 2) pageLen += 1; 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[3] = subpagenum; sg_put_unaligned_be16(pageLen, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); int status = scsiSimpleSenseFilter(&sinfo); if (0 != status) return status; /* sanity check on response */ if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum)) return SIMPLE_ERR_BAD_RESP; if (0 == sg_get_unaligned_be16(pBuf + 2)) return SIMPLE_ERR_BAD_RESP; return 0; } /* Sends a LOG SELECT command. Can be used to set log page values * or reset one log page (or all of them) to its defaults (typically zero). * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if * field in command not supported, * 4 if bad parameter to command or * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */ int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum, int subpagenum, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_TO_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = LOG_SELECT; cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0); cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f); cdb[3] = (subpagenum & 0xff); sg_put_unaligned_be16(bufLen, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); return scsiSimpleSenseFilter(&sinfo); } /* 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(scsi_device * device, int pagenum, int subpagenum, int pc, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[6]; uint8_t sense[32]; 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[3] = subpagenum; 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; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); int status = scsiSimpleSenseFilter(&sinfo); if (SIMPLE_ERR_TRY_AGAIN == status) { if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); 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(scsi_device * device, int sp, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[6]; uint8_t sense[32]; int 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; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); 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(scsi_device * device, int pagenum, int subpagenum, int pc, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = MODE_SENSE_10; cdb[2] = (pc << 6) | (pagenum & 0x3f); cdb[3] = subpagenum; sg_put_unaligned_be16(bufLen, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); int status = scsiSimpleSenseFilter(&sinfo); if (SIMPLE_ERR_TRY_AGAIN == status) { if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); 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(scsi_device * device, int sp, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; int pg_offset, pg_len, hdr_plus_1_pg; pg_offset = 8 + sg_get_unaligned_be16(pBuf + 6); 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 */ /* make sure only one page sent */ sg_put_unaligned_be16(hdr_plus_1_pg, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); return scsiSimpleSenseFilter(&sinfo); } /* Standard INQUIRY returns 0 for ok, anything else is a major problem. * bufLen should be 36 for unsafe devices (like USB mass storage stuff) * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */ int scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen) { struct scsi_sense_disect sinfo; struct scsi_cmnd_io io_hdr; uint8_t cdb[6]; uint8_t sense[32]; if ((bufLen < 0) || (bufLen > 1023)) 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; sg_put_unaligned_be16(bufLen, cdb + 3); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); return scsiSimpleSenseFilter(&sinfo); } /* 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(scsi_device * device, int vpd_page, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[6]; uint8_t sense[32]; int res; /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */ if ((SCSI_VPD_SUPPORTED_VPD_PAGES != vpd_page) && supported_vpd_pages_p && (! supported_vpd_pages_p->is_supported(vpd_page))) return 3; if ((bufLen < 0) || (bufLen > 1023)) return -EINVAL; try_again: memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); if (bufLen > 1) 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; sg_put_unaligned_be16(bufLen, cdb + 3); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); if ((SCSI_STATUS_CHECK_CONDITION == io_hdr.scsi_status) && (SCSI_SK_ILLEGAL_REQUEST == sinfo.sense_key) && (SCSI_ASC_INVALID_FIELD == sinfo.asc) && (cdb[3] > 0)) { bufLen &= 0xff; /* make sure cdb[3] is 0 next time around */ goto try_again; } if ((res = scsiSimpleSenseFilter(&sinfo))) return res; /* Guard against devices that ignore EVPD bit and do standard INQUIRY */ 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(scsi_device * device, struct scsi_sense_disect * sense_info) { struct scsi_cmnd_io io_hdr; uint8_t cdb[6]; uint8_t sense[32]; uint8_t buff[18]; int sz_buff = sizeof(buff); memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = sz_buff; io_hdr.dxferp = buff; cdb[0] = REQUEST_SENSE; cdb[4] = sz_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; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); if (sense_info) { uint8_t resp_code = buff[0] & 0x7f; sense_info->resp_code = resp_code; sense_info->sense_key = buff[2] & 0xf; sense_info->asc = 0; sense_info->ascq = 0; if ((0x70 == resp_code) || (0x71 == resp_code)) { int len = buff[7] + 8; if (len > 13) { sense_info->asc = buff[12]; sense_info->ascq = buff[13]; } } // fill progress indicator, if available sense_info->progress = -1; switch (resp_code) { const unsigned char * ucp; int sk, sk_pr; case 0x70: case 0x71: sk = (buff[2] & 0xf); if (! ((SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk))) { break; } if (buff[15] & 0x80) { /* SKSV bit set */ sense_info->progress = sg_get_unaligned_be16(buff + 16); break; } else { break; } case 0x72: case 0x73: /* sense key specific progress (0x2) or progress descriptor (0xa) */ sk = (buff[1] & 0xf); sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk); if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sz_buff, 2))) && (0x6 == ucp[1]) && (0x80 & ucp[4])) { sense_info->progress = sg_get_unaligned_be16(ucp + 5); break; } else if (((ucp = sg_scsi_sense_desc_find(buff, sz_buff, 0xa))) && ((0x6 == ucp[1]))) { sense_info->progress = sg_get_unaligned_be16(ucp + 6); break; } else break; default: return 0; } } return 0; } /* SEND DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if command * not supported, 3 if field in command not supported or returns negated * errno. SPC-3 section 6.28 (rev 22a) */ int scsiSendDiagnostic(scsi_device * device, int functioncode, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[6]; uint8_t sense[32]; 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 */ sg_put_unaligned_be16(bufLen, cdb + 3); 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; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); return scsiSimpleSenseFilter(&sinfo); } /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */ static int _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo) { struct scsi_cmnd_io io_hdr; uint8_t cdb[6]; uint8_t sense[32]; 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; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); 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(scsi_device * 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, 101 if * defect list not found (e.g. SSD may not have defect list) or returns * negated errno. SBC-2 section 5.12 (rev 16) */ int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = READ_DEFECT_10; cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) | ((req_glist << 3) & 0x8) | (dl_format & 0x7)); sg_put_unaligned_be16(bufLen, cdb + 7); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); /* Look for "(Primary|Grown) defect list not found" */ if ((sinfo.resp_code >= 0x70) && (0x1c == sinfo.asc)) return 101; return scsiSimpleSenseFilter(&sinfo); } /* READ DEFECT (12) command. Returns 0 if ok, 1 if NOT READY, 2 if * command not supported, 3 if field in command not supported, 101 if * defect list not found (e.g. SSD may not have defect list) or returns * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */ int scsiReadDefect12(scsi_device * device, int req_plist, int req_glist, int dl_format, int addrDescIndex, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[12]; uint8_t sense[32]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = READ_DEFECT_12; cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) | ((req_glist << 3) & 0x8) | (dl_format & 0x7)); sg_put_unaligned_be32(addrDescIndex, cdb + 2); sg_put_unaligned_be32(bufLen, cdb + 6); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); /* Look for "(Primary|Grown) defect list not found" */ if ((sinfo.resp_code >= 0x70) && (0x1c == sinfo.asc)) return 101; return scsiSimpleSenseFilter(&sinfo); } /* READ CAPACITY (10) command. Returns 0 if ok, 1 if NOT READY, 2 if * command not supported, 3 if field in command not supported or returns * negated errno. SBC-3 section 5.15 (rev 26) */ int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap, unsigned int * lb_sizep) { int res; struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[10]; uint8_t sense[32]; uint8_t resp[8]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); memset(resp, 0, sizeof(resp)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = sizeof(resp); io_hdr.dxferp = resp; cdb[0] = READ_CAPACITY_10; io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); res = scsiSimpleSenseFilter(&sinfo); if (res) return res; if (last_lbap) *last_lbap = sg_get_unaligned_be32(resp + 0); if (lb_sizep) *lb_sizep = sg_get_unaligned_be32(resp + 4); return 0; } /* READ CAPACITY (16) command. The bufLen argument should be 32. Returns 0 * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */ int scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen) { struct scsi_cmnd_io io_hdr; struct scsi_sense_disect sinfo; uint8_t cdb[16]; uint8_t sense[32]; memset(&io_hdr, 0, sizeof(io_hdr)); memset(cdb, 0, sizeof(cdb)); io_hdr.dxfer_dir = DXFER_FROM_DEVICE; io_hdr.dxfer_len = bufLen; io_hdr.dxferp = pBuf; cdb[0] = READ_CAPACITY_16; cdb[1] = SAI_READ_CAPACITY_16; sg_put_unaligned_be32(bufLen, cdb + 10); io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); io_hdr.sensep = sense; io_hdr.max_sense_len = sizeof(sense); io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; if (!device->scsi_pass_through(&io_hdr)) return -device->get_errno(); scsi_do_sense_disect(&io_hdr, &sinfo); return scsiSimpleSenseFilter(&sinfo); } /* Return number of bytes of storage in 'device' or 0 if error. If * successful and lb_sizep is not NULL then the logical block size in bytes * is written to the location pointed to by lb_sizep. If the 'Logical Blocks * per Physical Block Exponent' pointer (lb_per_pb_expp,) is non-null then * the value is written. If 'Protection information Intervals Exponent'*/ uint64_t scsiGetSize(scsi_device * device, bool avoid_rcap16, struct scsi_readcap_resp * srrp) { bool try_16 = false; bool try_12 = false; unsigned int last_lba = 0, lb_size = 0; int res; uint64_t ret_val = 0; uint8_t rc16resp[32]; if (avoid_rcap16) { res = scsiReadCapacity10(device, &last_lba, &lb_size); if (res) { if (scsi_debugmode) pout("%s: READ CAPACITY(10) failed, res=%d\n", __func__, res); try_16 = true; } else { /* rcap10 succeeded */ if (0xffffffff == last_lba) { /* so number of blocks needs > 32 bits to represent */ try_16 = true; device->set_rcap16_first(); } else { ret_val = last_lba + 1; if (srrp) { memset(srrp, 0, sizeof(*srrp)); srrp->num_lblocks = ret_val; srrp->lb_size = lb_size; } } } } if (try_16 || (! avoid_rcap16)) { res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp)); if (res) { if (scsi_debugmode) pout("%s: READ CAPACITY(16) failed, res=%d\n", __func__, res); if (try_16) /* so already tried rcap10 */ return 0; try_12 = true; } else { /* rcap16 succeeded */ bool prot_en; uint8_t p_type; ret_val = sg_get_unaligned_be64(rc16resp + 0) + 1; lb_size = sg_get_unaligned_be32(rc16resp + 8); if (srrp) { /* writes to all fields */ srrp->num_lblocks = ret_val; srrp->lb_size = lb_size; prot_en = !!(0x1 & rc16resp[12]); p_type = ((rc16resp[12] >> 1) & 0x7); srrp->prot_type = prot_en ? (1 + p_type) : 0; srrp->p_i_exp = ((rc16resp[13] >> 4) & 0xf); srrp->lb_p_pb_exp = (rc16resp[13] & 0xf); srrp->lbpme = !!(0x80 & rc16resp[14]); srrp->lbprz = !!(0x40 & rc16resp[14]); srrp->l_a_lba = sg_get_unaligned_be16(rc16resp + 14) & 0x3fff; } } } if (try_12) { /* case where only rcap16 has been tried and failed */ res = scsiReadCapacity10(device, &last_lba, &lb_size); if (res) { if (scsi_debugmode) pout("%s: 2nd READ CAPACITY(10) failed, res=%d\n", __func__, res); return 0; } else { /* rcap10 succeeded */ ret_val = (uint64_t)last_lba + 1; if (srrp) { memset(srrp, 0, sizeof(*srrp)); srrp->num_lblocks = ret_val; srrp->lb_size = lb_size; } } } return (ret_val * lb_size); } /* 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_t * resp, int len, int modese_len) { int offset = -1; if (resp) { int resp_len, bd_len; if (10 == modese_len) { resp_len = sg_get_unaligned_be16(resp + 0) + 2; bd_len = sg_get_unaligned_be16(resp + 6); 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) || scsi_debugmode) 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(scsi_device * 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, 0, 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, 0, 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, 0, MPAGE_CONTROL_CHANGEABLE, iecp->raw_chg, sizeof(iecp->raw_chg)); else if (6 == iecp->modese_len) err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 0, MPAGE_CONTROL_CHANGEABLE, 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) { if (iecp && iecp->gotCurrent) { int 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) { if (iecp && iecp->gotCurrent) { int 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(scsi_device * device, int enabled, const struct scsi_iec_mode_page *iecp) { int offset, resp_len; int err = 0; uint8_t rout[SCSI_IECMP_RAW_LEN]; 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); /* mask out DPOFUA device specific (disk) parameter bit */ if (10 == iecp->modese_len) { resp_len = sg_get_unaligned_be16(rout + 0) + 2; rout[3] &= 0xef; } else { resp_len = rout[0] + 1; rout[2] &= 0xef; } int sp = !! (rout[offset] & 0x80); /* PS bit becomes 'SELECT's SP bit */ if (enabled) { rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED; if (scsi_debugmode > 2) rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK; rout[offset + 3] = SCSI_IEC_MP_MRIE; sg_put_unaligned_be32(SCSI_IEC_MP_INTERVAL_T, rout + offset + 4); sg_put_unaligned_be32(SCSI_IEC_MP_REPORT_COUNT, rout + offset + 8); if (iecp->gotChangeable) { uint8_t chg2 = iecp->raw_chg[offset + 2]; rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) : iecp->raw_curr[offset + 2]; for (int 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 (scsi_debugmode > 0) pout("scsiSetExceptionControlAndWarning: already enabled\n"); return 0; } } else { /* disabling Exception Control and (temperature) Warnings */ int eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1; int wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0; if ((! eCEnabled) && (! wEnabled)) { if (scsi_debugmode > 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(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp) { uint8_t tBuf[252]; int err; memset(tBuf, 0, sizeof(tBuf)); if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { *currenttemp = 0; *triptemp = 0; pout("%s for temperature failed [%s]\n", logSenStr, 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(scsi_device * device, int hasIELogPage, int hasTempLogPage, uint8_t *asc, uint8_t *ascq, uint8_t *currenttemp, uint8_t *triptemp) { uint8_t tBuf[252]; struct scsi_sense_disect sense_info; int err; uint8_t 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, 0, tBuf, sizeof(tBuf), 0))) { pout("%s failed, IE page [%s]\n", logSenStr, scsiErrString(err)); return err; } // pull out page size from response, don't forget to add 4 unsigned short pagesize = sg_get_unaligned_be16(tBuf + 2) + 4; if ((pagesize < 4) || tBuf[4] || tBuf[5]) { pout("%s failed, IE page, bad parameter code or length\n", logSenStr); 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 (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_t asc, uint8_t 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 */ } int scsiSmartDefaultSelfTest(scsi_device * 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(scsi_device * 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(scsi_device * 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(scsi_device * 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(scsi_device * 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(scsi_device * 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(scsi_device * device, int * durationSec, int modese_len) { int err, offset; uint8_t buff[64]; memset(buff, 0, sizeof(buff)); if (modese_len <= 6) { if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, 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, 0, 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) { int res = sg_get_unaligned_be16(buff + offset + 10); *durationSec = res; return 0; } else return -EINVAL; } void scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp) { memset(ecp, 0, sizeof(*ecp)); int num = sg_get_unaligned_be16(resp + 2); unsigned char * ucp = &resp[0] + 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); int pl = ucp[3] + 4; uint64_t * ullp; 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; } int k = pl - 4; unsigned char * xp = ucp + 4; if (k > (int)sizeof(*ullp)) { xp += (k - sizeof(*ullp)); k = sizeof(*ullp); } *ullp = sg_get_unaligned_be(k, xp); num -= pl; ucp += pl; } } void scsiDecodeNonMediumErrPage(unsigned char *resp, struct scsiNonMediumError *nmep) { memset(nmep, 0, sizeof(*nmep)); int num = sg_get_unaligned_be16(resp + 2); unsigned char * ucp = &resp[0] + 4; int szof = sizeof(nmep->counterPC0); while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); int pl = ucp[3] + 4; int k; unsigned char * xp; switch (pc) { case 0: nmep->gotPC0 = 1; k = pl - 4; xp = ucp + 4; if (k > szof) { xp += (k - szof); k = szof; } nmep->counterPC0 = sg_get_unaligned_be(k, xp + 0); break; case 0x8009: nmep->gotTFE_H = 1; k = pl - 4; xp = ucp + 4; if (k > szof) { xp += (k - szof); k = szof; } nmep->counterTFE_H = sg_get_unaligned_be(k, xp + 0); break; case 0x8015: nmep->gotPE_H = 1; k = pl - 4; xp = ucp + 4; if (k > szof) { xp += (k - szof); k = szof; } nmep->counterPE_H = sg_get_unaligned_be(k, xp + 0); 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(scsi_device * fd, int noisy) { int num, k, err, fails, fail_hour; uint8_t * ucp; unsigned char resp[LOG_RESP_SELF_TEST_LEN]; if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp, LOG_RESP_SELF_TEST_LEN, 0))) { if (noisy) pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err)); return -1; } if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { if (noisy) pout("Self-test %s Failed, page mismatch\n", logSenStr); return -1; } // compute page length num = sg_get_unaligned_be16(resp + 2); // Log sense page length 0x190 bytes if (num != 0x190) { if (noisy) pout("Self-test %s length is 0x%x not 0x190 bytes\n", logSenStr, 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) int n = sg_get_unaligned_be16(ucp + 6); // 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; int res = ucp[4] & 0xf; if ((res > 2) && (res < 8)) { fails++; if (1 == fails) fail_hour = sg_get_unaligned_be16(ucp + 6); } } 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(scsi_device * fd, int * inProgress) { int num; uint8_t * ucp; unsigned char resp[LOG_RESP_SELF_TEST_LEN]; if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp, LOG_RESP_SELF_TEST_LEN, 0)) return -1; if (resp[0] != SELFTEST_RESULTS_LPAGE) return -1; // compute page length num = sg_get_unaligned_be16(resp + 2); // 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 Control 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(scsi_device * device, int modese_len, int current) { int err, offset; uint8_t 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, 0, pc, buff, sizeof(buff)))) { if (SIMPLE_ERR_BAD_OPCODE == err) modese_len = 10; else return -EINVAL; } else if (0 == modese_len) modese_len = 6; } if (10 == modese_len) { err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, 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; } /* Returns a negative value on error, 0 if unknown and 1 if SSD, * otherwise the positive returned value is the speed in rpm. First checks * the Block Device Characteristics VPD page and if that fails it tries the * RIGID_DISK_DRIVE_GEOMETRY_PAGE mode page. */ int scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp, int * haw_zbcp) { int err, offset; uint8_t buff[64]; int pc = MPAGE_CONTROL_DEFAULT; memset(buff, 0, sizeof(buff)); if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS, buff, sizeof(buff))) && ((sg_get_unaligned_be16(buff + 2)) > 2)) { int speed = sg_get_unaligned_be16(buff + 4); if (form_factorp) *form_factorp = buff[7] & 0xf; if (haw_zbcp) *haw_zbcp = !!(0x10 & buff[8]); return speed; } if (form_factorp) *form_factorp = 0; if (haw_zbcp) *haw_zbcp = 0; if (modese_len <= 6) { if ((err = scsiModeSense(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc, buff, sizeof(buff)))) { if (SIMPLE_ERR_BAD_OPCODE == err) modese_len = 10; else return -EINVAL; } else if (0 == modese_len) modese_len = 6; } if (10 == modese_len) { err = scsiModeSense10(device, RIGID_DISK_DRIVE_GEOMETRY_PAGE, 0, pc, buff, sizeof(buff)); if (err) return -EINVAL; } offset = scsiModePageOffset(buff, sizeof(buff), modese_len); return sg_get_unaligned_be16(buff + offset + 20); } /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value, 0 - clear bit, 1 - set bit */ int scsiGetSetCache(scsi_device * device, int modese_len, short int * wcep, short int * rcdp) { int err, offset, resp_len, sp; uint8_t buff[64], ch_buff[64]; short set_wce = *wcep; short set_rcd = *rcdp; memset(buff, 0, sizeof(buff)); if (modese_len <= 6) { err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT, buff, sizeof(buff)); if (err) { if (SIMPLE_ERR_BAD_OPCODE == err) modese_len = 10; else { device->set_err(EINVAL, "SCSI MODE SENSE failed"); return -EINVAL; } } else if (0 == modese_len) modese_len = 6; } if (10 == modese_len) { err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT, buff, sizeof(buff)); if (err) { device->set_err(EINVAL, "SCSI MODE SENSE failed"); return -EINVAL; } } offset = scsiModePageOffset(buff, sizeof(buff), modese_len); if ((offset < 0) || (buff[offset + 1] < 0xa)) { device->set_err(EINVAL, "Bad response"); return SIMPLE_ERR_BAD_RESP; } *wcep = ((buff[offset + 2] & 0x04) != 0); *rcdp = ((buff[offset + 2] & 0x01) != 0); if((*wcep == set_wce || set_wce == -1) && ((*rcdp == set_rcd) || set_rcd == -1)) return 0; // no changes needed or nothing to set if (modese_len == 6) err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CHANGEABLE, ch_buff, sizeof(ch_buff)); else err = scsiModeSense10(device, CACHING_PAGE, 0, MPAGE_CONTROL_CHANGEABLE, ch_buff, sizeof(ch_buff)); if (err) { device->set_err(EINVAL, "WCE/RCD bits not changeable"); return err; } // set WCE bit if(set_wce >= 0 && *wcep != set_wce) { if (0 == (ch_buff[offset + 2] & 0x04)) { device->set_err(EINVAL, "WCE bit not changeable"); return 1; } if(set_wce) buff[offset + 2] |= 0x04; // set bit else buff[offset + 2] &= 0xfb; // clear bit } // set RCD bit if(set_rcd >= 0 && *rcdp != set_rcd) { if (0 == (ch_buff[offset + 2] & 0x01)) { device->set_err(EINVAL, "RCD bit not changeable"); return 1; } if(set_rcd) buff[offset + 2] |= 0x01; // set bit else buff[offset + 2] &= 0xfe; // clear bit } /* mask out DPOFUA device specific (disk) parameter bit */ if (10 == modese_len) { resp_len = sg_get_unaligned_be16(buff + 0) + 2; buff[3] &= 0xef; } else { resp_len = buff[0] + 1; buff[2] &= 0xef; } sp = 0; /* Do not change saved values */ if (10 == modese_len) err = scsiModeSelect10(device, sp, buff, resp_len); else if (6 == modese_len) err = scsiModeSelect(device, sp, buff, resp_len); if(err) device->set_err(EINVAL, "MODE SELECT command failed"); return err; } /* Attempts to set or clear GLTSD bit in Control mode page. If enabled is 0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if successful, negative if low level error, > 0 if higher level error (e.g. SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */ int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len) { int err, offset, resp_len, sp; uint8_t buff[64]; uint8_t ch_buff[64]; memset(buff, 0, sizeof(buff)); if (modese_len <= 6) { if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, 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, 0, 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, 0, MPAGE_CONTROL_CHANGEABLE, ch_buff, sizeof(ch_buff)); else err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, 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 changeable */ /* mask out DPOFUA device specific (disk) parameter bit */ if (10 == modese_len) { resp_len = sg_get_unaligned_be16(buff + 0) + 2; buff[3] &= 0xef; } else { resp_len = buff[0] + 1; buff[2] &= 0xef; } 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(scsi_device * device, int modese_len) { int err, offset; uint8_t buff[64]; memset(buff, 0, sizeof(buff)); if (modese_len <= 6) { if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0, 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, 0, 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; } const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, int desc_type) { int add_sen_len; const unsigned char * descp; if ((sense_len < 8) || (0 == (add_sen_len = sensep[7]))) return NULL; if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) return NULL; add_sen_len = (add_sen_len < (sense_len - 8)) ? add_sen_len : (sense_len - 8); descp = &sensep[8]; for (int desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { descp += desc_len; int add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; desc_len = add_len + 2; if (descp[0] == desc_type) return descp; if (add_len < 0) /* short descriptor ?? */ break; } return NULL; } // Convenience function for formatting strings from SCSI identify void scsi_format_id_string(char * out, const uint8_t * in, int n) { char tmp[65]; n = n > 64 ? 64 : n; strncpy(tmp, (const char *)in, n); tmp[n] = '\0'; // Find the first non-space character (maybe none). int first = -1; int i; for (i = 0; tmp[i]; i++) if (!isspace((int)tmp[i])) { first = i; break; } if (first == -1) { // There are only space characters. out[0] = '\0'; return; } // Find the last non-space character. for (i = strlen(tmp)-1; i >= first && isspace((int)tmp[i]); i--); int last = i; strncpy(out, tmp+first, last-first+1); out[last-first+1] = '\0'; } smartmontools-7.0/scsicmds.h0000644000175000010010000005024613401001476013173 00000000000000/* * scsicmds.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-8 Bruce Allen * Copyright (C) 2000 Michael Cornwell * Copyright (C) 2003-18 Douglas Gilbert * * SPDX-License-Identifier: GPL-2.0-or-later * * * 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 4842 2018-12-02 16:07:26Z chrfranke $\n" #include #include #include #include /* #define SCSI_DEBUG 1 */ /* Comment out to disable command debugging */ /* Following conditional defines just in case OS already has them defined. * If they are defined we hope they are defined correctly (for SCSI). */ #ifndef TEST_UNIT_READY #define TEST_UNIT_READY 0x0 #endif #ifndef LOG_SELECT #define LOG_SELECT 0x4c #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 #ifndef READ_DEFECT_12 #define READ_DEFECT_12 0xb7 #endif #ifndef START_STOP_UNIT #define START_STOP_UNIT 0x1b #endif #ifndef REPORT_LUNS #define REPORT_LUNS 0xa0 #endif #ifndef READ_CAPACITY_10 #define READ_CAPACITY_10 0x25 #endif #ifndef READ_CAPACITY_16 #define READ_CAPACITY_16 0x9e #endif #ifndef SAI_READ_CAPACITY_16 /* service action for READ_CAPACITY_16 */ #define SAI_READ_CAPACITY_16 0x10 #endif #ifndef SAT_ATA_PASSTHROUGH_12 #define SAT_ATA_PASSTHROUGH_12 0xa1 #endif #ifndef SAT_ATA_PASSTHROUGH_16 #define SAT_ATA_PASSTHROUGH_16 0x85 #endif #define DXFER_NONE 0 #define DXFER_FROM_DEVICE 1 #define DXFER_TO_DEVICE 2 struct scsi_cmnd_io { uint8_t * 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_t * dxferp; /* [in]: ptr to outgoing or incoming data buffer */ size_t dxfer_len; /* [in]: bytes to be transferred to/from dxferp */ uint8_t * 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_t 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_t resp_code; uint8_t sense_key; uint8_t asc; uint8_t ascq; int progress; /* -1 -> N/A, 0-65535 -> available */ }; /* Useful data from Informational Exception Control mode page (0x1c) */ #define SCSI_IECMP_RAW_LEN 64 struct scsi_iec_mode_page { uint8_t requestedCurrent; uint8_t gotCurrent; uint8_t requestedChangeable; uint8_t gotChangeable; uint8_t modese_len; /* 0 (don't know), 6 or 10 */ uint8_t raw_curr[SCSI_IECMP_RAW_LEN]; uint8_t raw_chg[SCSI_IECMP_RAW_LEN]; }; /* Carrier for Error counter log pages (e.g. read, write, verify ...) */ struct scsiErrorCounter { uint8_t gotPC[7]; uint8_t gotExtraPC; uint64_t counter[8]; }; /* Carrier for Non-medium error log page */ struct scsiNonMediumError { uint8_t gotPC0; uint8_t gotExtraPC; uint64_t counterPC0; uint8_t gotTFE_H; uint64_t counterTFE_H; /* Track following errors [Hitachi] */ uint8_t gotPE_H; uint64_t counterPE_H; /* Positioning errors [Hitachi] */ }; struct scsi_readcap_resp { uint64_t num_lblocks; /* Number of Logical Blocks on device */ uint32_t lb_size; /* should be available in all non-error cases */ /* following fields from READ CAPACITY(16) or set to 0 */ uint8_t prot_type; /* 0, 1, 2 or 3 protection type, deduced from * READ CAPACITY(16) P_TYPE and PROT_EN fields */ uint8_t p_i_exp; /* Protection information Intervals Exponent */ uint8_t lb_p_pb_exp;/* Logical Blocks per Physical Block Exponent */ bool lbpme; /* Logical Block Provisioning Management Enabled */ bool lbprz; /* Logical Block Provisioning Read Zeros */ uint16_t l_a_lba; /* Lowest Aligned Logical Block Address */ }; /* 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 #define SCSI_PT_HOST_MANAGED 0x14 /* Transport protocol identifiers or just Protocol identifiers */ #define SCSI_TPROTO_FCP 0 #define SCSI_TPROTO_SPI 1 #define SCSI_TPROTO_SSA 2 #define SCSI_TPROTO_1394 3 #define SCSI_TPROTO_SRP 4 /* SCSI over RDMA */ #define SCSI_TPROTO_ISCSI 5 #define SCSI_TPROTO_SAS 6 #define SCSI_TPROTO_ADT 7 #define SCSI_TPROTO_ATA 8 #define SCSI_TPROTO_UAS 9 /* USB attached SCSI */ #define SCSI_TPROTO_SOP 0xa /* SCSI over PCIe */ #define SCSI_TPROTO_PCIE 0xb /* includes NVMe */ #define SCSI_TPROTO_NONE 0xf /* SCSI Log Pages retrieved by LOG SENSE. 0x0 to 0x3f, 0x30 to 0x3e vendor */ #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_EVENTS_LPAGE 0x07 #define FORMAT_STATUS_LPAGE 0x08 #define LAST_N_DEFERRED_LPAGE 0x0b /* or async events */ #define LB_PROV_LPAGE 0x0c /* SBC-3 */ #define TEMPERATURE_LPAGE 0x0d #define STARTSTOP_CYCLE_COUNTER_LPAGE 0x0e #define APPLICATION_CLIENT_LPAGE 0x0f #define SELFTEST_RESULTS_LPAGE 0x10 #define SS_MEDIA_LPAGE 0x11 /* SBC-3 */ #define BACKGROUND_RESULTS_LPAGE 0x15 /* SBC-3 */ #define ATA_PT_RESULTS_LPAGE 0x16 /* SAT */ #define NONVOL_CACHE_LPAGE 0x17 /* SBC-3 */ #define PROTOCOL_SPECIFIC_LPAGE 0x18 #define GEN_STATS_PERF_LPAGE 0x19 #define POWER_COND_TRANS_LPAGE 0x1a #define IE_LPAGE 0x2f /* SCSI Log subpages (8 bits), added spc4r05 2006, standardized SPC-4 2015 */ #define NO_SUBPAGE_L_SPAGE 0x0 /* 0x0-0x3f,0x0 */ #define LAST_N_INQ_DAT_L_SPAGE 0x1 /* 0xb,0x1 */ #define LAST_N_MODE_PG_L_SPAGE 0x2 /* 0xb,0x2 */ #define ENVIRO_REP_L_SPAGE 0x1 /* 0xd,0x1 */ #define ENVIRO_LIMITS_L_SPAGE 0x2 /* 0xd,0x2 */ #define UTILIZATION_L_SPAGE 0x1 /* 0xe,0x1 */ #define ZB_DEV_STATS_L_SPAGE 0x1 /* 0x14,0x1 */ #define PEND_DEFECTS_L_SPAGE 0x1 /* 0x15,0x1 */ #define BACKGROUND_OP_L_SPAGE 0x2 /* 0x15,0x2 */ #define LPS_MISALIGN_L_SPAGE 0x3 /* 0x15,0x3 */ #define SUPP_SPAGE_L_SPAGE 0xff /* 0x0,0xff pages+subpages */ /* 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 . Earlier 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 /* Background control mode subpage is [0x1c,0x1] */ #define BACKGROUND_CONTROL_M_SUBPAGE 0x1 /* SBC-2 */ #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 /* SCSI Vital Product Data (VPD) pages */ #define SCSI_VPD_SUPPORTED_VPD_PAGES 0x0 #define SCSI_VPD_UNIT_SERIAL_NUMBER 0x80 #define SCSI_VPD_DEVICE_IDENTIFICATION 0x83 #define SCSI_VPD_EXTENDED_INQUIRY_DATA 0x86 #define SCSI_VPD_ATA_INFORMATION 0x89 #define SCSI_VPD_POWER_CONDITION 0x8a #define SCSI_VPD_POWER_CONSUMPTION 0x8d #define SCSI_VPD_BLOCK_LIMITS 0xb0 #define SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS 0xb1 #define SCSI_VPD_LOGICAL_BLOCK_PROVISIONING 0xb2 /* defines for useful SCSI Status codes */ #define SCSI_STATUS_CHECK_CONDITION 0x2 /* defines for useful Sense Key codes */ #define SCSI_SK_NO_SENSE 0x0 #define SCSI_SK_RECOVERED_ERR 0x1 #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 #define SCSI_SK_ABORTED_COMMAND 0xb /* 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_INVALID_FIELD 0x24 #define SCSI_ASC_UNKNOWN_PARAM 0x26 #define SCSI_ASC_WARNING 0xb #define SCSI_ASC_IMPENDING_FAILURE 0x5d #define SCSI_ASCQ_ATA_PASS_THROUGH 0x1d /* 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 */ #define SIMPLE_ERR_UNKNOWN 10 /* unknown sense value */ #define SIMPLE_ERR_ABORTED_COMMAND 11 /* probably transport 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 60 // should be longer than the spin up time // of a disk in JBOD. #define SCSI_TIMEOUT_SELF_TEST (5 * 60 * 60) /* allow max 5 hours for */ /* extended foreground self test */ #define LOGPAGEHDRSIZE 4 class scsi_device; // Set of supported SCSI VPD pages. Constructor fetches Supported VPD pages // VPD page and remembers the response for later queries. class supported_vpd_pages { public: explicit supported_vpd_pages(scsi_device * device); ~supported_vpd_pages() { num_valid = 0; } bool is_supported(int vpd_page_num) const; /* Returns 0 or less for VPD pages not supported or error */ int num_pages() const { return num_valid; } private: int num_valid; /* 0 or less for invalid */ unsigned char pages[256]; }; extern supported_vpd_pages * supported_vpd_pages_p; /* This is a heuristic that takes into account the command bytes and length * to decide whether the presented unstructured sequence of bytes could be * a SCSI command. If so it returns true otherwise false. Vendor specific * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The * only SCSI commands considered above 16 bytes of length are the Variable * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). * Both have an inbuilt length field which can be cross checked with clen. * No NVMe commands (64 bytes long plus some extra added by some OSes) have * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS * structures that are sent across the wire. The FIS register structure is * used to move a command from a SATA host to device, but the ATA 'command' * is not the first byte. So it is harder to say what will happen if a * FIS structure is presented as a SCSI command, hopefully there is a low * probability this function will yield true in that case. */ bool is_scsi_cdb(const uint8_t * cdbp, int clen); // Print SCSI debug messages? extern unsigned char scsi_debugmode; void scsi_do_sense_disect(const struct scsi_cmnd_io * in, struct scsi_sense_disect * out); int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo); const char * scsiErrString(int scsiErr); int scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set); int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen, int * transport); /* STANDARD SCSI Commands */ int scsiTestUnitReady(scsi_device * device); int scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen); int scsiInquiryVpd(scsi_device * device, int vpd_page, uint8_t *pBuf, int bufLen); int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, uint8_t *pBuf, int bufLen, int known_resp_len); int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum, int subpagenum, uint8_t *pBuf, int bufLen); int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc, uint8_t *pBuf, int bufLen); int scsiModeSelect(scsi_device * device, int sp, uint8_t *pBuf, int bufLen); int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc, uint8_t *pBuf, int bufLen); int scsiModeSelect10(scsi_device * device, int sp, uint8_t *pBuf, int bufLen); int scsiModePageOffset(const uint8_t * resp, int len, int modese_len); int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info); int scsiSendDiagnostic(scsi_device * device, int functioncode, uint8_t *pBuf, int bufLen); int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format, uint8_t *pBuf, int bufLen); int scsiReadDefect12(scsi_device * device, int req_plist, int req_glist, int dl_format, int addrDescIndex, uint8_t *pBuf, int bufLen); int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbp, unsigned int * lb_sizep); int scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen); /* SMART specific commands */ int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, uint8_t *asc, uint8_t *ascq, uint8_t *currenttemp, uint8_t *triptemp); int scsiFetchIECmpage(scsi_device * 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(scsi_device * 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(scsi_device * device, int * durationSec, int modese_len); int scsiCountFailedSelfTests(scsi_device * device, int noisy); int scsiSelfTestInProgress(scsi_device * device, int * inProgress); int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current); int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len); int scsiFetchTransportProtocol(scsi_device * device, int modese_len); int scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp, int * haw_zbcp); int scsiGetSetCache(scsi_device * device, int modese_len, short int * wce, short int * rcd); uint64_t scsiGetSize(scsi_device * device, bool avoid_rcap16, struct scsi_readcap_resp * srrp); /* T10 Standard IE Additional Sense Code strings taken from t10.org */ const char* scsiGetIEString(uint8_t asc, uint8_t ascq); int scsiGetTemp(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp); int scsiSmartDefaultSelfTest(scsi_device * device); int scsiSmartShortSelfTest(scsi_device * device); int scsiSmartExtendSelfTest(scsi_device * device); int scsiSmartShortCapSelfTest(scsi_device * device); int scsiSmartExtendCapSelfTest(scsi_device * device); int scsiSmartSelfTestAbort(scsi_device * device); const char * scsiTapeAlertsTapeDevice(unsigned short code); const char * scsiTapeAlertsChangerDevice(unsigned short code); const char * scsi_get_opcode_name(uint8_t opcode); void scsi_format_id_string(char * out, const uint8_t * in, int n); void dStrHex(const uint8_t * up, int len, int no_ascii); /* Attempt to find the first SCSI sense data descriptor that matches the given 'desc_type'. If found return pointer to start of sense data descriptor; otherwise (including fixed format sense data) returns NULL. */ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, int desc_type); /* SCSI command transmission interface function declaration. Its * definition is target OS specific (see os_.c file). * 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. */ // Moved to C++ interface //int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); #endif smartmontools-7.0/scsinvme.cpp0000644000175000010010000001665013404016030013541 00000000000000/* * scsinvme.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2018 Harry Mallon * * SPDX-License-Identifier: GPL-2.0-or-later * */ #include "config.h" #include "dev_interface.h" #include "dev_tunnelled.h" #include "scsicmds.h" #include "sg_unaligned.h" #include "utility.h" #include // SNT (SCSI NVMe Translation) namespace and prefix namespace snt { #define SNT_JMICRON_NVME_SIGNATURE 0x454d564eu // 'NVME' reversed (little endian) #define SNT_JMICRON_CDB_LEN 12 #define SNT_JMICRON_NVM_CMD_LEN 512 class sntjmicron_device : public tunnelled_device< /*implements*/ nvme_device, /*by tunnelling through a*/ scsi_device > { public: sntjmicron_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, unsigned nsid); virtual ~sntjmicron_device() throw(); virtual bool open(); virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out); private: enum { proto_nvm_cmd = 0x0, proto_non_data = 0x1, proto_dma_in = 0x2, proto_dma_out = 0x3, proto_response = 0xF }; }; sntjmicron_device::sntjmicron_device(smart_interface * intf, scsi_device * scsidev, const char * req_type, unsigned nsid) : smart_device(intf, scsidev->get_dev_name(), "sntjmicron", req_type), tunnelled_device(scsidev, nsid) { set_info().info_name = strprintf("%s [USB NVMe JMicron]", scsidev->get_info_name()); } sntjmicron_device::~sntjmicron_device() throw() { } bool sntjmicron_device::open() { // Open USB first if (!tunnelled_device::open()) return false; // No sure how multiple namespaces come up on device so we // cannot detect e.g. /dev/sdX is NSID 2. // Set to broadcast if not available if (!get_nsid()) { set_nsid(0xFFFFFFFF); } return true; } // cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1) // cdb[1]: [ is admin cmd: 1 ] [ protocol : 7 ] // cdb[2]: reserved // cdb[3]: parameter list length (23:16) // cdb[4]: parameter list length (15:08) // cdb[5]: parameter list length (07:00) // cdb[6]: reserved // cdb[7]: reserved // cdb[8]: reserved // cdb[9]: reserved // cdb[10]: reserved // cdb[11]: CONTROL (?) bool sntjmicron_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) { /* Only admin commands used */ bool admin = true; // 1: "NVM Command Set Payload" { unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 }; cdb[0] = SAT_ATA_PASSTHROUGH_12; cdb[1] = (admin ? 0x80 : 0x00) | proto_nvm_cmd; sg_put_unaligned_be24(SNT_JMICRON_NVM_CMD_LEN, &cdb[3]); unsigned nvm_cmd[SNT_JMICRON_NVM_CMD_LEN / sizeof(unsigned)] = { 0 }; nvm_cmd[0] = SNT_JMICRON_NVME_SIGNATURE; // nvm_cmd[1]: reserved nvm_cmd[2] = in.opcode; // More of CDW0 may go in here in future nvm_cmd[3] = in.nsid; // nvm_cmd[4-5]: reserved // nvm_cmd[6-7]: metadata pointer // nvm_cmd[8-11]: data ptr (?) nvm_cmd[12] = in.cdw10; nvm_cmd[13] = in.cdw11; nvm_cmd[14] = in.cdw12; nvm_cmd[15] = in.cdw13; nvm_cmd[16] = in.cdw14; nvm_cmd[17] = in.cdw15; // nvm_cmd[18-127]: reserved if (isbigendian()) for (unsigned i = 0; i < (SNT_JMICRON_NVM_CMD_LEN / sizeof(uint32_t)); i++) swapx(&nvm_cmd[i]); scsi_cmnd_io io_nvm; memset(&io_nvm, 0, sizeof(io_nvm)); io_nvm.cmnd = cdb; io_nvm.cmnd_len = SNT_JMICRON_CDB_LEN; io_nvm.dxfer_dir = DXFER_TO_DEVICE; io_nvm.dxferp = (uint8_t *)nvm_cmd; io_nvm.dxfer_len = SNT_JMICRON_NVM_CMD_LEN; scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_nvm, "sntjmicron_device::nvme_pass_through:NVM: ")) return set_err(scsidev->get_err()); } // 2: DMA or Non-Data { unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 }; cdb[0] = SAT_ATA_PASSTHROUGH_12; scsi_cmnd_io io_data; memset(&io_data, 0, sizeof(io_data)); io_data.cmnd = cdb; io_data.cmnd_len = SNT_JMICRON_CDB_LEN; switch (in.direction()) { case nvme_cmd_in::no_data: cdb[1] = (admin ? 0x80 : 0x00) | proto_non_data; io_data.dxfer_dir = DXFER_NONE; break; case nvme_cmd_in::data_out: cdb[1] = (admin ? 0x80 : 0x00) | proto_dma_out; sg_put_unaligned_be24(in.size, &cdb[3]); io_data.dxfer_dir = DXFER_TO_DEVICE; io_data.dxferp = (uint8_t *)in.buffer; io_data.dxfer_len = in.size; break; case nvme_cmd_in::data_in: cdb[1] = (admin ? 0x80 : 0x00) | proto_dma_in; sg_put_unaligned_be24(in.size, &cdb[3]); io_data.dxfer_dir = DXFER_FROM_DEVICE; io_data.dxferp = (uint8_t *)in.buffer; io_data.dxfer_len = in.size; memset(in.buffer, 0, in.size); break; case nvme_cmd_in::data_io: default: return set_err(EINVAL); } scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_data, "sntjmicron_device::nvme_pass_through:Data: ")) return set_err(scsidev->get_err()); } // 3: "Return Response Information" { unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 }; cdb[0] = SAT_ATA_PASSTHROUGH_12; cdb[1] = (admin ? 0x80 : 0x00) | proto_response; sg_put_unaligned_be24(SNT_JMICRON_NVM_CMD_LEN, &cdb[3]); unsigned nvm_reply[SNT_JMICRON_NVM_CMD_LEN / sizeof(unsigned)] = { 0 }; scsi_cmnd_io io_reply; memset(&io_reply, 0, sizeof(io_reply)); io_reply.cmnd = cdb; io_reply.cmnd_len = SNT_JMICRON_CDB_LEN; io_reply.dxfer_dir = DXFER_FROM_DEVICE; io_reply.dxferp = (uint8_t *)nvm_reply; io_reply.dxfer_len = SNT_JMICRON_NVM_CMD_LEN; scsi_device * scsidev = get_tunnel_dev(); if (!scsidev->scsi_pass_through_and_check(&io_reply, "sntjmicron_device::nvme_pass_through:Reply: ")) return set_err(scsidev->get_err()); if (isbigendian()) for (unsigned i = 0; i < (SNT_JMICRON_NVM_CMD_LEN / sizeof(uint32_t)); i++) swapx(&nvm_reply[i]); if (nvm_reply[0] != SNT_JMICRON_NVME_SIGNATURE) return set_err(EIO, "Out of spec JMicron NVMe reply"); int status = nvm_reply[5] >> 17; if (status > 0) return set_nvme_err(out, status); out.result = nvm_reply[2]; } return true; } } // namespace snt using namespace snt; nvme_device * smart_interface::get_snt_device(const char * type, scsi_device * scsidev) { if (!scsidev) throw std::logic_error("smart_interface: get_snt_device() called with scsidev=0"); // Take temporary ownership of 'scsidev' to delete it on error scsi_device_auto_ptr scsidev_holder(scsidev); nvme_device * sntdev = 0; // TODO: Remove this and adjust drivedb entry accordingly when no longer EXPERIMENTAL if (!strcmp(type, "sntjmicron#please_try")) { set_err(EINVAL, "USB to NVMe bridge [please try '-d sntjmicron' and report result to: " PACKAGE_BUGREPORT "]"); return 0; } if (!strncmp(type, "sntjmicron", 10)) { int n1 = -1, n2 = -1, len = strlen(type); unsigned nsid = 0; // invalid namespace id -> use default sscanf(type, "sntjmicron%n,0x%x%n", &n1, &nsid, &n2); if (!(n1 == len || n2 == len)) { set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type); return 0; } sntdev = new sntjmicron_device(this, scsidev, type, nsid); } else { set_err(EINVAL, "Unknown SNT device type '%s'", type); return 0; } // 'scsidev' is now owned by 'sntdev' scsidev_holder.release(); return sntdev; } smartmontools-7.0/scsiprint.cpp0000644000175000010010000025510513411203340013730 00000000000000/* * scsiprint.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2000 Michael Cornwell * Copyright (C) 2003-18 Douglas Gilbert * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include "scsicmds.h" #include "atacmds.h" // smart_command_set #include "dev_interface.h" #include "scsiprint.h" #include "smartctl.h" #include "utility.h" #include "sg_unaligned.h" #define GBUF_SIZE 65532 const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4870 2018-12-27 17:07:44Z chrfranke $" SCSIPRINT_H_CVSID; uint8_t gBuf[GBUF_SIZE]; #define LOG_RESP_LEN 252 #define LOG_RESP_LONG_LEN ((62 * 256) + 252) #define LOG_RESP_TAPE_ALERT_LEN 0x144 /* Log pages supported */ static bool gSmartLPage = false; /* Informational Exceptions log page */ static bool gTempLPage = false; static bool gSelfTestLPage = false; static bool gStartStopLPage = false; static bool gReadECounterLPage = false; static bool gWriteECounterLPage = false; static bool gVerifyECounterLPage = false; static bool gNonMediumELPage = false; static bool gLastNErrorEvLPage = false; static bool gBackgroundResultsLPage = false; static bool gProtocolSpecificLPage = false; static bool gTapeAlertsLPage = false; static bool gSSMediaLPage = false; static bool gFormatStatusLPage = false; static bool gEnviroReportingLPage = false; static bool gEnviroLimitsLPage = false; static bool gUtilizationLPage = false; static bool gPendDefectsLPage = false; static bool gBackgroundOpLPage = false; static bool gLPSMisalignLPage = false; /* Vendor specific log pages */ static bool gSeagateCacheLPage = false; static bool gSeagateFactoryLPage = false; /* Mode pages supported */ static bool gIecMPage = true; /* N.B. assume it until we know otherwise */ /* Remember last successful mode sense/select command */ static int modese_len = 0; /* Remember this value from the most recent INQUIRY */ static int scsi_version; #define SCSI_VERSION_SPC_4 0x6 #define SCSI_VERSION_SPC_5 0x7 #define SCSI_VERSION_HIGHEST SCSI_VERSION_SPC_5 /* T10 vendor identification. Should match entry in last Annex of SPC * drafts and standards (e.g. SPC-4). */ static char scsi_vendor[8+1]; #define T10_VENDOR_SEAGATE "SEAGATE" #define T10_VENDOR_HITACHI_1 "HITACHI" #define T10_VENDOR_HITACHI_2 "HL-DT-ST" #define T10_VENDOR_HITACHI_3 "HGST" static const char * logSenStr = "Log Sense"; static const char * logSenRspStr = "Log Sense response"; static bool seagate_or_hitachi(void) { return ((0 == memcmp(scsi_vendor, T10_VENDOR_SEAGATE, strlen(T10_VENDOR_SEAGATE))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_1, strlen(T10_VENDOR_HITACHI_1))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_2, strlen(T10_VENDOR_HITACHI_2))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_3, strlen(T10_VENDOR_HITACHI_3)))); } static bool all_ffs(const uint8_t * bp, int b_len) { if ((NULL == bp) || (b_len <= 0)) return false; for (--b_len; b_len >= 0; --b_len) { if (0xff != bp[b_len]) return false; } return true; } static void scsiGetSupportedLogPages(scsi_device * device) { bool got_subpages = false; int k, bump, err, payload_len, num_unreported, num_unreported_spg; const uint8_t * up; uint8_t sup_lpgs[LOG_RESP_LEN]; memset(gBuf, 0, LOG_RESP_LEN); if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) pout("%s for supported pages failed [%s]\n", logSenStr, scsiErrString(err)); /* try one more time with defined length, workaround for the bug #678 found with ST8000NM0075/E001 */ err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 68); /* 64 max pages + 4b header */ if (scsi_debugmode > 0) pout("%s for supported pages failed (second attempt) [%s]\n", logSenStr, scsiErrString(err)); if (err) return; memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); } else if ((scsi_version >= SCSI_VERSION_SPC_4) && (scsi_version <= SCSI_VERSION_HIGHEST)) { /* unclear what code T10 will choose for SPC-6 */ memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); if ((err = scsiLogSense(device, SUPPORTED_LPAGES, SUPP_SPAGE_L_SPAGE, gBuf, LOG_RESP_LONG_LEN, -1 /* just single not double fetch */))) { if (scsi_debugmode > 0) pout("%s for supported pages and subpages failed [%s]\n", logSenStr, scsiErrString(err)); } else { if (0 == memcmp(gBuf, sup_lpgs, LOG_RESP_LEN)) { if (scsi_debugmode > 0) pout("%s: %s ignored subpage field, bad\n", __func__, logSenRspStr); } else if (! ((0x40 & gBuf[0]) && (SUPP_SPAGE_L_SPAGE == gBuf[1]))) { if (scsi_debugmode > 0) pout("%s supported subpages is bad SPF=%u SUBPG=%u\n", logSenRspStr, !! (0x40 & gBuf[0]), gBuf[2]); } else got_subpages = true; } } else memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); if (got_subpages) { payload_len = sg_get_unaligned_be16(gBuf + 2); bump = 2; up = gBuf + LOGPAGEHDRSIZE; } else { payload_len = sup_lpgs[3]; bump = 1; up = sup_lpgs + LOGPAGEHDRSIZE; } num_unreported_spg = 0; for (num_unreported = 0, k = 0; k < payload_len; k += bump, up += bump) { uint8_t pg_num = 0x3f & up[0]; uint8_t sub_pg_num = (0x40 & up[0]) ? up[1] : 0; switch (pg_num) { case SUPPORTED_LPAGES: if (! ((NO_SUBPAGE_L_SPAGE == sub_pg_num) || (SUPP_SPAGE_L_SPAGE == sub_pg_num))) { if (scsi_debugmode > 1) pout("%s: Strange Log page number: 0x0,0x%x\n", __func__, sub_pg_num); } break; case READ_ERROR_COUNTER_LPAGE: gReadECounterLPage = true; break; case WRITE_ERROR_COUNTER_LPAGE: gWriteECounterLPage = true; break; case VERIFY_ERROR_COUNTER_LPAGE: gVerifyECounterLPage = true; break; case LAST_N_ERROR_EVENTS_LPAGE: gLastNErrorEvLPage = true; break; case NON_MEDIUM_ERROR_LPAGE: gNonMediumELPage = true; break; case TEMPERATURE_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gTempLPage = true; else if (ENVIRO_REP_L_SPAGE == sub_pg_num) gEnviroReportingLPage = true; else if (ENVIRO_LIMITS_L_SPAGE == sub_pg_num) gEnviroLimitsLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case STARTSTOP_CYCLE_COUNTER_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gStartStopLPage = true; else if (UTILIZATION_L_SPAGE == sub_pg_num) gUtilizationLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case SELFTEST_RESULTS_LPAGE: gSelfTestLPage = true; break; case IE_LPAGE: gSmartLPage = true; break; case BACKGROUND_RESULTS_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gBackgroundResultsLPage = true; else if (PEND_DEFECTS_L_SPAGE == sub_pg_num) gPendDefectsLPage = true; else if (BACKGROUND_OP_L_SPAGE == sub_pg_num) gBackgroundOpLPage = true; else if (LPS_MISALIGN_L_SPAGE == sub_pg_num) gLPSMisalignLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case PROTOCOL_SPECIFIC_LPAGE: gProtocolSpecificLPage = true; break; case TAPE_ALERTS_LPAGE: gTapeAlertsLPage = true; break; case SS_MEDIA_LPAGE: gSSMediaLPage = true; break; case FORMAT_STATUS_LPAGE: gFormatStatusLPage = true; break; case SEAGATE_CACHE_LPAGE: if (failuretest_permissive) { gSeagateCacheLPage = true; break; } if (seagate_or_hitachi()) gSeagateCacheLPage = true; break; case SEAGATE_FACTORY_LPAGE: if (failuretest_permissive) { gSeagateFactoryLPage = true; break; } if (seagate_or_hitachi()) gSeagateFactoryLPage = true; break; default: if (pg_num < 0x30) { /* don't count VS pages */ ++num_unreported; if (sub_pg_num > 0) ++num_unreported_spg; } break; } } if (scsi_debugmode > 1) pout("%s: number of unreported (standard) log pages: %d (sub-pages: " "%d)\n", __func__, num_unreported, num_unreported_spg); } /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad (or at least something to report). */ static int scsiGetSmartData(scsi_device * device, bool attribs) { uint8_t asc; uint8_t ascq; uint8_t currenttemp = 255; uint8_t triptemp = 255; const char * cp; int err = 0; print_on(); if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, ¤ttemp, &triptemp)) { /* error message already announced */ print_off(); return -1; } print_off(); cp = scsiGetIEString(asc, ascq); if (cp) { err = -2; print_on(); jout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); print_off(); jglb["smart_status"]["passed"] = false; jglb["smart_status"]["scsi"]["asc"] = asc; jglb["smart_status"]["scsi"]["ascq"] = ascq; jglb["smart_status"]["scsi"]["ie_string"] = cp; } else if (gIecMPage) { jout("SMART Health Status: OK\n"); jglb["smart_status"]["passed"] = true; } if (attribs && !gTempLPage) { if (255 == currenttemp) pout("Current Drive Temperature: \n"); else { jout("Current Drive Temperature: %d C\n", currenttemp); jglb["temperature"]["current"] = currenttemp; } if (255 == triptemp) pout("Drive Trip Temperature: \n"); else { jout("Drive Trip Temperature: %d C\n", triptemp); jglb["temperature"]["drive_trip"] = triptemp; } } pout("\n"); return err; } // Returns number of logged errors or zero if none or -1 if fetching // TapeAlerts fails static const char * const severities = "CWI"; static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) { unsigned short pagelength; unsigned short parametercode; int i, err; const char *s; const char *ts; int failures = 0; print_on(); if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf, LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return -1; } if (gBuf[0] != 0x2e) { pout("TapeAlerts %s Failed\n", logSenStr); print_off(); return -1; } pagelength = sg_get_unaligned_be16(gBuf + 2); for (s=severities; *s; s++) { for (i = 4; i < pagelength; i += 5) { parametercode = sg_get_unaligned_be16(gBuf + i); 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(); if (! failures) pout("TapeAlert: OK\n"); return failures; } static void scsiGetStartStopData(scsi_device * device) { int err, len, k, extra; unsigned char * ucp; if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return; } if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { print_on(); pout("StartStop %s Failed, page mismatch\n", logSenStr); print_off(); return; } len = sg_get_unaligned_be16(gBuf + 2); ucp = gBuf + 4; for (k = len; k > 0; k -= extra, ucp += extra) { if (k < 3) { print_on(); pout("StartStop %s: short\n", logSenRspStr); print_off(); return; } extra = ucp[3] + 4; int pc = sg_get_unaligned_be16(ucp + 0); uint32_t u = (extra > 7) ? sg_get_unaligned_be32(ucp + 4) : 0; bool is_all_ffs = (extra > 7) ? all_ffs(ucp + 4, 4) : false; switch (pc) { case 1: if (10 == extra) pout("Manufactured in week %.2s of year %.4s\n", ucp + 8, ucp + 4); break; case 2: /* ignore Accounting date */ break; case 3: if ((extra > 7) && (! is_all_ffs)) pout("Specified cycle count over device lifetime: %u\n", u); break; case 4: if ((extra > 7) && (! is_all_ffs)) pout("Accumulated start-stop cycles: %u\n", u); break; case 5: if ((extra > 7) && (! is_all_ffs)) pout("Specified load-unload count over device lifetime: " "%u\n", u); break; case 6: if ((extra > 7) && (! is_all_ffs)) pout("Accumulated load-unload cycles: %u\n", u); break; default: /* ignore */ break; } } } /* PENDING_DEFECTS_SUBPG [0x15,0x1] introduced: SBC-4 */ static void scsiPrintPendingDefectsLPage(scsi_device * device) { int num, pl, pc, err; uint32_t count; const uint8_t * bp; static const char * pDefStr = "Pending Defects"; static const char * jname = "pending_defects"; if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, PEND_DEFECTS_L_SPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return; } if (((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) && (gBuf[1] != PEND_DEFECTS_L_SPAGE)) { print_on(); pout("%s %s, page mismatch\n", pDefStr, logSenRspStr); print_off(); return; } num = sg_get_unaligned_be16(gBuf + 2); if (num > LOG_RESP_LONG_LEN) { print_on(); pout("%s %s too long\n", pDefStr, logSenRspStr); print_off(); return; } bp = gBuf + 4; while (num > 3) { pc = sg_get_unaligned_be16(bp + 0); pl = bp[3] + 4; switch (pc) { case 0x0: printf(" Pending defect count:"); if ((pl < 8) || (num < 8)) { print_on(); pout("%s truncated descriptor\n", pDefStr); print_off(); return; } count = sg_get_unaligned_be32(bp + 4); jglb[jname]["count"] = count; if (0 == count) jout("0 %s\n", pDefStr); else if (1 == count) jout("1 Pending Defect, LBA and accumulated_power_on_hours " "follow\n"); else jout("%u %s: index, LBA and accumulated_power_on_hours " "follow\n", count, pDefStr); break; default: if ((pl < 16) || (num < 16)) { print_on(); pout("%s truncated descriptor\n", pDefStr); print_off(); return; } jout(" %4d: 0x%-16" PRIx64 ", %5u\n", pc, sg_get_unaligned_be64(bp + 8), sg_get_unaligned_be32(bp + 4)); jglb[jname][pc]["LBA"] = sg_get_unaligned_be64(bp + 8); jglb[jname][pc]["accum_power_on_hours"] = sg_get_unaligned_be32(bp + 4); break; } num -= pl; bp += pl; } } static void scsiPrintGrownDefectListLen(scsi_device * device) { bool got_rd12; int err, dl_format; unsigned int dl_len, div; static const char * hname = "Read defect list"; memset(gBuf, 0, 8); if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */, 4 /* format: bytes from index */, 0 /* addr desc index */, gBuf, 8))) { if (2 == err) { /* command not supported */ err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, 4 /* format: bytes from index */, gBuf, 4); if (err) { if (scsi_debugmode > 0) { print_on(); pout("%s (10) Failed: %s\n", hname, scsiErrString(err)); print_off(); } return; } else got_rd12 = 0; } else if (101 == err) /* Defect list not found, leave quietly */ return; else { if (scsi_debugmode > 0) { print_on(); pout("%s (12) Failed: %s\n", hname, scsiErrString(err)); print_off(); } return; } } else got_rd12 = true; if (got_rd12) { int generation = sg_get_unaligned_be16(gBuf + 2); if ((generation > 1) && (scsi_debugmode > 0)) { print_on(); pout("%s (12): generation=%d\n", hname, generation); print_off(); } dl_len = sg_get_unaligned_be32(gBuf + 4); } else dl_len = sg_get_unaligned_be16(gBuf + 2); if (0x8 != (gBuf[1] & 0x18)) { print_on(); pout("%s: asked for grown list but didn't get it\n", hname); print_off(); return; } div = 0; dl_format = (gBuf[1] & 0x7); switch (dl_format) { case 0: /* short block */ div = 4; break; case 1: /* extended bytes from index */ case 2: /* extended physical sector */ /* extended = 1; # might use in future */ div = 8; break; case 3: /* long block */ case 4: /* bytes from index */ case 5: /* physical sector */ div = 8; break; default: print_on(); pout("defect list format %d unknown\n", dl_format); print_off(); break; } if (0 == dl_len) { jout("Elements in grown defect list: 0\n\n"); jglb["scsi_grown_defect_list"] = 0; } else { if (0 == div) pout("Grown defect list length=%u bytes [unknown " "number of elements]\n\n", dl_len); else { jout("Elements in grown defect list: %u\n\n", dl_len / div); jglb["scsi_grown_defect_list"] = dl_len; } } } static void scsiPrintSeagateCacheLPage(scsi_device * device) { int num, pl, pc, err, len; unsigned char * ucp; uint64_t ull; static const char * seaCacStr = "Seagate Cache"; if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) { print_on(); pout("%s %s Failed: %s\n", seaCacStr, logSenStr, scsiErrString(err)); print_off(); } return; } if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { if (scsi_debugmode > 0) { print_on(); pout("%s %s, page mismatch\n", seaCacStr, logSenRspStr); print_off(); } return; } len = sg_get_unaligned_be16(gBuf + 2) + 4; num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); pl = ucp[3] + 4; switch (pc) { case 0: case 1: case 2: case 3: case 4: break; default: if (scsi_debugmode > 0) { print_on(); pout("Vendor (%s) lpage has unexpected parameter, skip\n", seaCacStr); print_off(); } return; } num -= pl; ucp += pl; } pout("Vendor (%s) information\n", seaCacStr); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); 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; } int k = pl - 4; const int sz_ull = (int)sizeof(ull); unsigned char * xp = ucp + 4; if (k > sz_ull) { xp += (k - sz_ull); k = sz_ull; } ull = sg_get_unaligned_be(k, xp + 0); pout(" = %" PRIu64 "\n", ull); num -= pl; ucp += pl; } pout("\n"); } static void scsiPrintSeagateFactoryLPage(scsi_device * device) { int num, pl, pc, len, err, good, bad; unsigned char * ucp; uint64_t ull; if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); } return; } if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { if (scsi_debugmode > 0) { print_on(); pout("Seagate/Hitachi Factory %s, page mismatch\n", logSenRspStr); print_off(); } return; } len = sg_get_unaligned_be16(gBuf + 2) + 4; num = len - 4; ucp = &gBuf[0] + 4; good = 0; bad = 0; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); 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 (scsi_debugmode > 0) { print_on(); pout("\nVendor (Seagate/Hitachi) factory lpage has too many " "unexpected parameters, skip\n"); print_off(); } return; } pout("Vendor (Seagate/Hitachi) factory information\n"); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); 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 (scsi_debugmode > 0) { print_on(); pout("Vendor (Seagate/Hitachi) factory lpage: " "unknown parameter code [0x%x]\n", pc); print_off(); } break; } if (good) { int k = pl - 4; unsigned char * xp = ucp + 4; if (k > (int)sizeof(ull)) { xp += (k - (int)sizeof(ull)); k = (int)sizeof(ull); } ull = sg_get_unaligned_be(k, xp + 0); if (0 == pc) { pout(" = %.2f\n", ull / 60.0 ); jglb["power_on_time"]["hours"] = ull / 60; jglb["power_on_time"]["minutes"] = ull % 60; } else pout(" = %" PRIu64 "\n", ull); } num -= pl; ucp += pl; } pout("\n"); } static void scsiPrintErrorCounterLog(scsi_device * device) { struct scsiErrorCounter errCounterArr[3]; struct scsiErrorCounter * ecp; int found[3] = {0, 0, 0}; if (gReadECounterLPage && (0 == scsiLogSense(device, READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); found[0] = 1; } if (gWriteECounterLPage && (0 == scsiLogSense(device, WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); found[1] = 1; } if (gVerifyECounterLPage && (0 == scsiLogSense(device, VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]); ecp = &errCounterArr[2]; for (int k = 0; k < 7; ++k) { if (ecp->gotPC[k] && ecp->counter[k]) { found[2] = 1; break; } } } if (found[0] || found[1] || found[2]) { pout("Error 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"); json::ref jref = jglb["scsi_error_counter_log"]; for (int k = 0; k < 3; ++k) { if (! found[k]) continue; ecp = &errCounterArr[k]; static const char * const pageNames[3] = {"read: ", "write: ", "verify: "}; static const char * jpageNames[3] = {"read", "write", "verify"}; jout("%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]); double processed_gb = ecp->counter[5] / 1000000000.0; jout(" %12.3f %8" PRIu64 "\n", processed_gb, ecp->counter[6]); // Error counter log info jref[jpageNames[k]]["errors_corrected_by_eccfast"] = ecp->counter[0]; jref[jpageNames[k]]["errors_corrected_by_eccdelayed"] = ecp->counter[1]; jref[jpageNames[k]]["errors_corrected_by_rereads_rewrites"] = ecp->counter[2]; jref[jpageNames[k]]["total_errors_corrected"] = ecp->counter[3]; jref[jpageNames[k]]["correction_algorithm_invocations"] = ecp->counter[4]; jref[jpageNames[k]]["gigabytes_processed"] = strprintf("%.3f", processed_gb); jref[jpageNames[k]]["total_uncorrected_errors"] = ecp->counter[6]; } } else pout("Error Counter logging not supported\n"); if (gNonMediumELPage && (0 == scsiLogSense(device, NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { struct scsiNonMediumError nme; 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 (gLastNErrorEvLPage && (0 == scsiLogSense(device, LAST_N_ERROR_EVENTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { int num = sg_get_unaligned_be16(gBuf + 2) + 4; int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; unsigned char * ucp = gBuf + 4; num -= 4; if (num < 4) pout("\nNo error events logged\n"); else { pout("\nLast n error events log page\n"); for (int k = num, pl; k > 0; k -= pl, ucp += pl) { if (k < 3) { pout(" <>\n"); break; } pl = ucp[3] + 4; int pc = sg_get_unaligned_be16(ucp + 0); if (pl > 4) { if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { pout(" Error event %d:\n", pc); pout(" [binary]:\n"); dStrHex((const uint8_t *)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 (scsi_debugmode > 0) { pout(" Error event %d:\n", pc); pout(" [data counter??]:\n"); dStrHex((const uint8_t *)ucp + 4, pl - 4, 1); } } } } if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); } } pout("\n"); } 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 ", "Aborted (by user command)", "Aborted (device 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(scsi_device * device) { int num, k, err, durationSec; int noheader = 1; int retval = 0; uint8_t * ucp; uint64_t ull; struct scsi_sense_disect sense_info; static const char * hname = "Self-test"; // check if test is running if (!scsiRequestSense(device, &sense_info) && (sense_info.asc == 0x04 && sense_info.ascq == 0x09 && sense_info.progress != -1)) { pout("%s execution status:\t\t%d%% of test remaining\n", hname, 100 - ((sense_info.progress * 100) / 65535)); } if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, LOG_RESP_SELF_TEST_LEN, 0))) { print_on(); pout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2); // Log sense page length 0x190 bytes if (num != 0x190) { print_on(); pout("%s %s length is 0x%x not 0x190 bytes\n", hname, logSenStr, num); print_off(); return FAILSMART; } // loop through the twenty possible entries for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) { // timestamp in power-on hours (or zero if test in progress) int n = sg_get_unaligned_be16(ucp + 6); // 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("SMART %s log\n", hname); 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", sg_get_unaligned_be16(ucp + 0), 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: int res; 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 ull = sg_get_unaligned_be64(ucp + 8); bool is_all_ffs = all_ffs(ucp + 8, 8); // print Address of First Failure, if sensible if ((! is_all_ffs) && (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 %ss have been logged\n", hname); else if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, modese_len)) && (durationSec > 0)) { pout("\nLong (extended) %s duration: %d seconds " "[%.1f minutes]\n", hname, durationSec, durationSec / 60.0); } pout("\n"); return retval; } static const char * bms_status[] = { "no scans active", "scan is active", "pre-scan is active", "halted due to fatal error", "halted due to a vendor specific pattern of error", "halted due to medium formatted without P-List", "halted - vendor specific cause", "halted due to temperature out of range", "waiting until BMS interval timer expires", /* 8 */ }; static const char * reassign_status[] = { "Reserved [0x0]", "Require Write or Reassign Blocks command", "Successfully reassigned", "Reserved [0x3]", "Reassignment by disk failed", "Recovered via rewrite in-place", "Reassigned by app, has valid data", "Reassigned by app, has no valid data", "Unsuccessfully reassigned by app", /* 8 */ }; // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 . // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). static int scsiPrintBackgroundResults(scsi_device * device) { int num, j, m, err, truncated; int noheader = 1; int firstresult = 1; int retval = 0; uint8_t * ucp; static const char * hname = "Background scan results"; if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 20) { print_on(); pout("%s %s length is %d, no scan status\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; switch (pc) { case 0: if (noheader) { noheader = 0; pout("%s log\n", hname); } pout(" Status: "); if ((pl < 16) || (num < 16)) { pout("\n"); break; } j = ucp[9]; if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0]))) pout("%s\n", bms_status[j]); else pout("unknown [0x%x] background scan status value\n", j); j = sg_get_unaligned_be32(ucp + 4); pout(" Accumulated power on time, hours:minutes %d:%02d " "[%d minutes]\n", (j / 60), (j % 60), j); jglb["power_on_time"]["hours"] = j / 60; jglb["power_on_time"]["minutes"] = j % 60; pout(" Number of background scans performed: %d, ", sg_get_unaligned_be16(ucp + 10)); pout("scan progress: %.2f%%\n", (double)sg_get_unaligned_be16(ucp + 12) * 100.0 / 65536.0); pout(" Number of background medium scans performed: %d\n", sg_get_unaligned_be16(ucp + 14)); break; default: if (noheader) { noheader = 0; pout("\n%s log\n", hname); } if (firstresult) { firstresult = 0; pout("\n # when lba(hex) [sk,asc,ascq] " "reassign_status\n"); } pout(" %3d ", pc); if ((pl < 24) || (num < 24)) { if (pl < 24) pout("parameter length >= 24 expected, got %d\n", pl); break; } j = sg_get_unaligned_be32(ucp + 4); pout("%4d:%02d ", (j / 60), (j % 60)); for (m = 0; m < 8; ++m) pout("%02x", ucp[16 + m]); pout(" [%x,%x,%x] ", ucp[8] & 0xf, ucp[9], ucp[10]); j = (ucp[8] >> 4) & 0xf; if (j < (int)(sizeof(reassign_status) / sizeof(reassign_status[0]))) pout("%s\n", reassign_status[j]); else pout("Reassign status: reserved [0x%x]\n", j); break; } num -= pl; ucp += pl; } if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); pout("\n"); return retval; } // See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 . // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). static int scsiPrintSSMedia(scsi_device * device) { int num, err, truncated; int retval = 0; uint8_t * ucp; static const char * hname = "Solid state media"; if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 12) { print_on(); pout("%s %s length is %d, too short\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; switch (pc) { case 1: if (pl < 8) { print_on(); pout("%s Percentage used endurance indicator parameter " "too short (pl=%d)\n", hname, pl); print_off(); return FAILSMART; } jout("Percentage used endurance indicator: %d%%\n", ucp[7]); jglb["scsi_percentage_used_endurance_indicator"] = ucp[7]; default: /* ignore other parameter codes */ break; } num -= pl; ucp += pl; } return retval; } static int scsiPrintFormatStatus(scsi_device * device) { bool is_count; int k, num, err, truncated; int retval = 0; uint64_t ull; uint8_t * ucp; uint8_t * xp; const char * jout_str; const char * jglb_str; static const char * hname = "Format Status"; static const char * jname = "format_status"; if ((err = scsiLogSense(device, FORMAT_STATUS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); jout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != FORMAT_STATUS_LPAGE) { print_on(); jout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 12) { print_on(); jout("%s %s length is %d, too short\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; is_count = true; jout_str = ""; jglb_str = "x"; switch (pc) { case 0: if (scsi_debugmode > 1) { if (pl < 5) jout("Format data out: \n"); else { if (all_ffs(ucp + 4, pl - 4)) jout("Format data out: \n"); else { jout("Format data out:\n"); dStrHex((const uint8_t *)ucp + 4, pl - 4, 0); } } } is_count = false; break; case 1: jout_str = "Grown defects during certification"; jglb_str = "grown_defects_during_cert"; break; case 2: jout_str = "Total blocks reassigned during format"; jglb_str = "blocks_reassigned_during_format"; break; case 3: jout_str = "Total new blocks reassigned"; jglb_str = "total_new_block_since_format"; break; case 4: jout_str = "Power on minutes since format"; jglb_str = "power_on_minutes_since_format"; break; default: if (scsi_debugmode > 3) { pout(" Unknown Format parameter code = 0x%x\n", pc); dStrHex((const uint8_t *)ucp, pl, 0); } is_count = false; break; } if (is_count) { k = pl - 4; xp = ucp + 4; if (all_ffs(xp, k)) { pout("%s \n", jout_str); } else { if (k > (int)sizeof(ull)) { xp += (k - sizeof(ull)); k = sizeof(ull); } ull = sg_get_unaligned_be(k, xp); jout("%s = %" PRIu64 "\n", jout_str, ull); jglb[jname][jglb_str] = ull; } } else num -= pl; ucp += pl; } return retval; } static void show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val) { unsigned int u; switch (peis) { case 0: pout(" No event\n"); break; case 0x1: pout(" Invalid word count: %u\n", val); break; case 0x2: pout(" Running disparity error count: %u\n", val); break; case 0x3: pout(" Loss of dword synchronization count: %u\n", val); break; case 0x4: pout(" Phy reset problem count: %u\n", val); break; case 0x5: pout(" Elasticity buffer overflow count: %u\n", val); break; case 0x6: pout(" Received ERROR count: %u\n", val); break; case 0x20: pout(" Received address frame error count: %u\n", val); break; case 0x21: pout(" Transmitted abandon-class OPEN_REJECT count: %u\n", val); break; case 0x22: pout(" Received abandon-class OPEN_REJECT count: %u\n", val); break; case 0x23: pout(" Transmitted retry-class OPEN_REJECT count: %u\n", val); break; case 0x24: pout(" Received retry-class OPEN_REJECT count: %u\n", val); break; case 0x25: pout(" Received AIP (WAITING ON PARTIAL) count: %u\n", val); break; case 0x26: pout(" Received AIP (WAITING ON CONNECTION) count: %u\n", val); break; case 0x27: pout(" Transmitted BREAK count: %u\n", val); break; case 0x28: pout(" Received BREAK count: %u\n", val); break; case 0x29: pout(" Break timeout count: %u\n", val); break; case 0x2a: pout(" Connection count: %u\n", val); break; case 0x2b: pout(" Peak transmitted pathway blocked count: %u\n", val & 0xff); pout(" Peak value detector threshold: %u\n", thresh_val & 0xff); break; case 0x2c: u = val & 0xffff; if (u < 0x8000) pout(" Peak transmitted arbitration wait time (us): " "%u\n", u); else pout(" Peak transmitted arbitration wait time (ms): " "%u\n", 33 + (u - 0x8000)); u = thresh_val & 0xffff; if (u < 0x8000) pout(" Peak value detector threshold (us): %u\n", u); else pout(" Peak value detector threshold (ms): %u\n", 33 + (u - 0x8000)); break; case 0x2d: pout(" Peak arbitration time (us): %u\n", val); pout(" Peak value detector threshold: %u\n", thresh_val); break; case 0x2e: pout(" Peak connection time (us): %u\n", val); pout(" Peak value detector threshold: %u\n", thresh_val); break; case 0x40: pout(" Transmitted SSP frame count: %u\n", val); break; case 0x41: pout(" Received SSP frame count: %u\n", val); break; case 0x42: pout(" Transmitted SSP frame error count: %u\n", val); break; case 0x43: pout(" Received SSP frame error count: %u\n", val); break; case 0x44: pout(" Transmitted CREDIT_BLOCKED count: %u\n", val); break; case 0x45: pout(" Received CREDIT_BLOCKED count: %u\n", val); break; case 0x50: pout(" Transmitted SATA frame count: %u\n", val); break; case 0x51: pout(" Received SATA frame count: %u\n", val); break; case 0x52: pout(" SATA flow control buffer overflow count: %u\n", val); break; case 0x60: pout(" Transmitted SMP frame count: %u\n", val); break; case 0x61: pout(" Received SMP frame count: %u\n", val); break; case 0x63: pout(" Received SMP frame error count: %u\n", val); break; default: break; } } static void show_sas_port_param(unsigned char * ucp, int param_len) { int j, m, nphys, t, sz, spld_len; unsigned char * vcp; char s[64]; sz = sizeof(s); // pcb = ucp[2]; t = sg_get_unaligned_be16(ucp + 0); pout("relative target port id = %d\n", t); pout(" generation code = %d\n", ucp[6]); nphys = ucp[7]; pout(" number of phys = %d\n", nphys); for (j = 0, vcp = ucp + 8; j < (param_len - 8); vcp += spld_len, j += spld_len) { pout(" phy identifier = %d\n", vcp[1]); spld_len = vcp[3]; if (spld_len < 44) spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */ else spld_len += 4; t = ((0x70 & vcp[4]) >> 4); switch (t) { case 0: snprintf(s, sz, "no device attached"); break; case 1: snprintf(s, sz, "SAS or SATA device"); break; case 2: snprintf(s, sz, "expander device"); break; case 3: snprintf(s, sz, "expander device (fanout)"); break; default: snprintf(s, sz, "reserved [%d]", t); break; } pout(" attached device type: %s\n", s); t = 0xf & vcp[4]; switch (t) { case 0: snprintf(s, sz, "unknown"); break; case 1: snprintf(s, sz, "power on"); break; case 2: snprintf(s, sz, "hard reset"); break; case 3: snprintf(s, sz, "SMP phy control function"); break; case 4: snprintf(s, sz, "loss of dword synchronization"); break; case 5: snprintf(s, sz, "mux mix up"); break; case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); break; case 7: snprintf(s, sz, "break timeout timer expired"); break; case 8: snprintf(s, sz, "phy test function stopped"); break; case 9: snprintf(s, sz, "expander device reduced functionality"); break; default: snprintf(s, sz, "reserved [0x%x]", t); break; } pout(" attached reason: %s\n", s); t = (vcp[5] & 0xf0) >> 4; switch (t) { case 0: snprintf(s, sz, "unknown"); break; case 1: snprintf(s, sz, "power on"); break; case 2: snprintf(s, sz, "hard reset"); break; case 3: snprintf(s, sz, "SMP phy control function"); break; case 4: snprintf(s, sz, "loss of dword synchronization"); break; case 5: snprintf(s, sz, "mux mix up"); break; case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); break; case 7: snprintf(s, sz, "break timeout timer expired"); break; case 8: snprintf(s, sz, "phy test function stopped"); break; case 9: snprintf(s, sz, "expander device reduced functionality"); break; default: snprintf(s, sz, "reserved [0x%x]", t); break; } pout(" reason: %s\n", s); t = (0xf & vcp[5]); switch (t) { case 0: snprintf(s, sz, "phy enabled; unknown"); break; case 1: snprintf(s, sz, "phy disabled"); break; case 2: snprintf(s, sz, "phy enabled; speed negotiation failed"); break; case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state"); break; case 4: snprintf(s, sz, "phy enabled; port selector"); break; case 5: snprintf(s, sz, "phy enabled; reset in progress"); break; case 6: snprintf(s, sz, "phy enabled; unsupported phy attached"); break; case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break; case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break; case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break; case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break; default: snprintf(s, sz, "reserved [%d]", t); break; } pout(" negotiated logical link rate: %s\n", s); pout(" attached initiator port: ssp=%d stp=%d smp=%d\n", !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2)); pout(" attached target port: ssp=%d stp=%d smp=%d\n", !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2)); if (!dont_print_serial_number) { uint64_t ull = sg_get_unaligned_be64(vcp + 8); pout(" SAS address = 0x%" PRIx64 "\n", ull); ull = sg_get_unaligned_be64(vcp + 16); pout(" attached SAS address = 0x%" PRIx64 "\n", ull); } pout(" attached phy identifier = %d\n", vcp[24]); unsigned int ui = sg_get_unaligned_be32(vcp + 32); pout(" Invalid DWORD count = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 36); pout(" Running disparity error count = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 40); pout(" Loss of DWORD synchronization = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 44); pout(" Phy reset problem = %u\n", ui); if (spld_len > 51) { int num_ped; unsigned char * xcp; num_ped = vcp[51]; if (num_ped > 0) pout(" Phy event descriptors:\n"); xcp = vcp + 52; for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) { int peis; unsigned int pvdt; peis = xcp[3]; ui = sg_get_unaligned_be32(xcp + 4); pvdt = sg_get_unaligned_be32(xcp + 8); show_sas_phy_event_info(peis, ui, pvdt); } } } } // Returns 1 if okay, 0 if non SAS descriptors static int show_protocol_specific_page(unsigned char * resp, int len) { int k, num; unsigned char * ucp; num = len - 4; for (k = 0, ucp = resp + 4; k < num; ) { int param_len = ucp[3] + 4; if (SCSI_TPROTO_SAS != (0xf & ucp[4])) return 0; /* only decode SAS log page */ if (0 == k) pout("Protocol Specific port log page for SAS SSP\n"); show_sas_port_param(ucp, param_len); k += param_len; ucp += param_len; } pout("\n"); return 1; } // See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific // log page [0x18]. Returns 0 if ok else FAIL* bitmask. static int scsiPrintSasPhy(scsi_device * device, int reset) { int num, err; static const char * hname = "Protocol specific port"; if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s %s Failed [%s]\n\n", __func__, logSenStr, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) { print_on(); pout("%s %s, page mismatch\n\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2); if (1 != show_protocol_specific_page(gBuf, num + 4)) { print_on(); pout("Only support %s log page on SAS devices\n\n", hname); print_off(); return FAILSMART; } if (reset) { if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */, PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) { print_on(); pout("%s Log Select (reset) Failed [%s]\n\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } } return 0; } static const char * peripheral_dt_arr[32] = { "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", "reserved [0x10]", "object based storage", "automation/driver interface", "security manager device", "host managed zoned block device", "reserved [0x15]", "reserved [0x16]", "reserved [0x17]", "reserved [0x18]", "reserved [0x19]", "reserved [0x1a]", "reserved [0x1b]", "reserved [0x1c]", "reserved [0x1d]", "well known logical unit", "unknown or no device type", }; /* Symbolic indexes to this array SCSI_TPROTO_* in scscmds.h */ static const char * transport_proto_arr[] = { "Fibre channel (FCP-2)", "Parallel SCSI (SPI-4)", "SSA", "IEEE 1394 (SBP-2)", "RDMA (SRP)", "iSCSI", "SAS (SPL-3)", "ADT", "ATA (ACS-2)", "UAS", "SOP", "PCIe", "0xc", "0xd", "0xe", "None given [0xf]" }; /* Returns 0 on success, 1 on general error and 2 for early, clean exit */ static int scsiGetDriveInfo(scsi_device * device, uint8_t * peripheral_type, bool all) { struct scsi_iec_mode_page iec; int err, iec_err, len, req_len, avail_len; bool is_tape = false; int peri_dt = 0; int transport = -1; int form_factor = 0; int haw_zbc = 0; int protect = 0; memset(gBuf, 0, 96); req_len = 36; if ((err = scsiStdInquiry(device, gBuf, req_len))) { print_on(); pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); pout("Retrying with a 64 byte Standard Inquiry\n"); print_off(); /* Marvell controllers fail with 36 byte StdInquiry, but 64 is ok */ req_len = 64; if ((err = scsiStdInquiry(device, gBuf, req_len))) { print_on(); pout("Standard Inquiry (64 bytes) failed [%s]\n", scsiErrString(err)); print_off(); return 1; } } avail_len = gBuf[4] + 5; len = (avail_len < req_len) ? avail_len : req_len; peri_dt = gBuf[0] & 0x1f; *peripheral_type = peri_dt; if ((SCSI_PT_SEQUENTIAL_ACCESS == peri_dt) || (SCSI_PT_MEDIUM_CHANGER == peri_dt)) is_tape = true; if (len < 36) { print_on(); pout("Short INQUIRY response, skip product id\n"); print_off(); return 1; } // Upper bits of version bytes were used in older standards // Only interested in SPC-4 (0x6) and SPC-5 (assumed to be 0x7) scsi_version = gBuf[2] & 0x7; if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) { char product[16+1], revision[4+1]; scsi_format_id_string(scsi_vendor, &gBuf[8], 8); scsi_format_id_string(product, &gBuf[16], 16); scsi_format_id_string(revision, &gBuf[32], 4); pout("=== START OF INFORMATION SECTION ===\n"); jout("Vendor: %.8s\n", scsi_vendor); jglb["vendor"] = scsi_vendor; jout("Product: %.16s\n", product); jglb["product"] = product; jglb["model_name"] = strprintf("%s%s%s", scsi_vendor, (*scsi_vendor && *product ? " " : ""), product); if (gBuf[32] >= ' ') { jout("Revision: %.4s\n", revision); // jglb["firmware_version"] = revision; jglb["revision"] = revision; /* could be a hardware rev */ } if ((scsi_version > 0x3) && (scsi_version < 0x8)) { char sv_arr[8]; snprintf(sv_arr, sizeof(sv_arr), "SPC-%d", scsi_version - 2); jout("Compliance: %s\n", sv_arr); jglb["scsi_version"] = sv_arr; } } if (!*device->get_req_type()/*no type requested*/ && (0 == strncmp((char *)&gBuf[8], "ATA", 3))) { pout("\nProbable ATA device behind a SAT layer\n" "Try an additional '-d ata' or '-d sat' argument.\n"); return 2; } if (! all) return 0; protect = gBuf[5] & 0x1; /* from and including SPC-3 */ if (! is_tape) { /* assume disk if not tape drive (or tape changer) */ struct scsi_readcap_resp srr; int lbpme = -1; int lbprz = -1; unsigned char lb_prov_resp[8]; uint64_t capacity = scsiGetSize(device, false /*avoid_rcap16 */, &srr); if (capacity) { char cap_str[64], si_str[64]; format_with_thousands_sep(cap_str, sizeof(cap_str), capacity); format_capacity(si_str, sizeof(si_str), capacity); jout("User Capacity: %s bytes [%s]\n", cap_str, si_str); if (srr.lb_size) jglb["user_capacity"]["blocks"].set_unsafe_uint64(capacity / srr.lb_size); jglb["user_capacity"]["bytes"].set_unsafe_uint64(capacity); jout("Logical block size: %u bytes\n", srr.lb_size); jglb["logical_block_size"] = srr.lb_size; if (protect || srr.lb_p_pb_exp) { if (srr.lb_p_pb_exp > 0) { unsigned pb_size = srr.lb_size * (1 << srr.lb_p_pb_exp); jout("Physical block size: %u bytes\n", pb_size); jglb["physical_block_size"] = pb_size; if (srr.l_a_lba > 0) // not common so cut the clutter pout("Lowest aligned LBA: %u\n", srr.l_a_lba); } if (srr.prot_type > 0) { switch (srr.prot_type) { case 1 : pout("Formatted with type 1 protection\n"); break; case 2 : pout("Formatted with type 2 protection\n"); break; case 3 : pout("Formatted with type 3 protection\n"); break; default: pout("Formatted with unknown protection type [%d]\n", srr.prot_type); break; } unsigned p_i_per_lb = (1 << srr.p_i_exp); const unsigned pi_sz = 8; /* ref-tag(4 bytes), app-tag(2), tag-mask(2) */ if (p_i_per_lb > 1) pout("%d protection information intervals per " "logical block\n", p_i_per_lb); pout("%d bytes of protection information per logical " "block\n", pi_sz * p_i_per_lb); } /* Pick up some LB provisioning info since its available */ lbpme = (int)srr.lbpme; lbprz = (int)srr.lbprz; } } /* Thin Provisioning VPD page renamed Logical Block Provisioning VPD * page in sbc3r25; some fields changed their meaning so that the * new page covered both thin and resource provisioned LUs. */ if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING, lb_prov_resp, sizeof(lb_prov_resp))) { int prov_type = lb_prov_resp[6] & 0x7; /* added sbc3r27 */ int vpd_lbprz = ((lb_prov_resp[5] >> 2) & 0x7); /* sbc4r07 */ if (-1 == lbprz) lbprz = vpd_lbprz; else if ((0 == vpd_lbprz) && (1 == lbprz)) ; /* vpd_lbprz introduced in sbc3r27, expanded in sbc4r07 */ else lbprz = vpd_lbprz; switch (prov_type) { case 0: if (lbpme <= 0) { pout("LU is fully provisioned"); if (lbprz) pout(" [LBPRZ=%d]\n", lbprz); else pout("\n"); } else pout("LB provisioning type: not reported [LBPME=1, " "LBPRZ=%d]\n", lbprz); break; case 1: pout("LU is resource provisioned, LBPRZ=%d\n", lbprz); break; case 2: pout("LU is thin provisioned, LBPRZ=%d\n", lbprz); break; default: pout("LU provisioning type reserved [%d], LBPRZ=%d\n", prov_type, lbprz); break; } } else if (1 == lbpme) { if (scsi_debugmode > 0) pout("rcap_16 sets LBPME but no LB provisioning VPD page\n"); pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz); } int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc); if (rpm >= 0) { if (0 == rpm) ; // Not reported else if (1 == rpm) jout("Rotation Rate: Solid State Device\n"); else if ((rpm <= 0x400) || (0xffff == rpm)) ; // Reserved else jout("Rotation Rate: %d rpm\n", rpm); jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm); } if (form_factor > 0) { const char * cp = NULL; switch (form_factor) { case 1: cp = "5.25"; break; case 2: cp = "3.5"; break; case 3: cp = "2.5"; break; case 4: cp = "1.8"; break; case 5: cp = "< 1.8"; break; } jglb["form_factor"]["scsi_value"] = form_factor; if (cp) { jout("Form Factor: %s inches\n", cp); jglb["form_factor"]["name"] = strprintf("%s inches", cp); } } if (haw_zbc > 0) pout("Host aware zoned block capable\n"); } /* 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(); gIecMPage = 0; return 1; } } else modese_len = iec.modese_len; if (! dont_print_serial_number) { if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION, gBuf, 252))) { char s[256]; len = gBuf[3]; scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport); if (strlen(s) > 0) pout("Logical Unit id: %s\n", s); } else if (scsi_debugmode > 0) { print_on(); 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(); } if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER, gBuf, 252))) { char serial[256]; len = gBuf[3]; gBuf[4 + len] = '\0'; scsi_format_id_string(serial, &gBuf[4], len); jout("Serial number: %s\n", serial); jglb["serial_number"] = serial; } else if (scsi_debugmode > 0) { print_on(); 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(); } } // print SCSI peripheral device type jglb["device_type"]["scsi_value"] = peri_dt; if (peri_dt < (int)(sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0]))) { jout("Device type: %s\n", peripheral_dt_arr[peri_dt]); jglb["device_type"]["name"] = peripheral_dt_arr[peri_dt]; } else jout("Device type: <%d>\n", peri_dt); // See if transport protocol is known if (transport < 0) transport = scsiFetchTransportProtocol(device, modese_len); if ((transport >= 0) && (transport <= 0xf)) pout("Transport protocol: %s\n", transport_proto_arr[transport]); // print current time and date and timezone time_t now = time(0); char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now); jout("Local Time is: %s\n", timedatetz); jglb["local_time"]["time_t"] = now; jglb["local_time"]["asctime"] = timedatetz; // See if unit accepts SCSI commands from us if ((err = scsiTestUnitReady(device))) { if (SIMPLE_ERR_NOT_READY == err) { print_on(); 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(); } else if (SIMPLE_ERR_NO_MEDIUM == err) { print_on(); if (is_tape) pout("NO tape present in drive\n"); else pout("NO MEDIUM present in device\n"); print_off(); } else if (SIMPLE_ERR_BECOMING_READY == err) { print_on(); pout("device becoming ready (wait)\n"); print_off(); } else { print_on(); pout("device Test Unit Ready [%s]\n", scsiErrString(err)); print_off(); } if (! is_tape) { int returnval = 0; // TODO: exit with FAILID if failuretest returns failuretest(MANDATORY_CMD, returnval|=FAILID); } } if (iec_err) { if (!is_tape) { print_on(); pout("SMART support is: Unavailable - device lacks SMART " "capability.\n"); if (scsi_debugmode > 0) pout(" [%s]\n", scsiErrString(iec_err)); print_off(); } gIecMPage = 0; return 0; } if (!is_tape) pout("SMART support is: Available - device has SMART capability.\n" "SMART support is: %s\n", (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled"); pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? "Temperature Warning: Enabled" : "Temperature Warning: Disabled or Not Supported"); return 0; } static int scsiSmartEnable(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { print_on(); pout("unable to enable Exception control and warning [%s]\n", scsiErrString(err)); print_off(); 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(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { print_on(); pout("unable to disable Exception control and warning [%s]\n", scsiErrString(err)); print_off(); 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(scsi_device * device) { uint8_t temp = 255; uint8_t trip = 255; if (scsiGetTemp(device, &temp, &trip)) return; if (255 == temp) pout("Current Drive Temperature: \n"); else { jout("Current Drive Temperature: %d C\n", temp); jglb["temperature"]["current"] = temp; } if (255 == trip) pout("Drive Trip Temperature: \n"); else { jout("Drive Trip Temperature: %d C\n", trip); jglb["temperature"]["drive_trip"] = trip; } pout("\n"); } /* Main entry point used by smartctl command. Return 0 for success */ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) { int checkedSupportedLogPages = 0; uint8_t peripheral_type = 0; int returnval = 0; int res, durationSec; struct scsi_sense_disect sense_info; bool is_disk; bool is_tape; bool any_output = options.drive_info; if (supported_vpd_pages_p) { delete supported_vpd_pages_p; supported_vpd_pages_p = NULL; } supported_vpd_pages_p = new supported_vpd_pages(device); res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info); if (res) { if (2 == res) return 0; else failuretest(MANDATORY_CMD, returnval |= FAILID); any_output = true; } is_disk = ((SCSI_PT_DIRECT_ACCESS == peripheral_type) || (SCSI_PT_HOST_MANAGED == peripheral_type)); is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || (SCSI_PT_MEDIUM_CHANGER == peripheral_type)); short int wce = -1, rcd = -1; // Print read look-ahead status for disks if (options.get_rcd || options.get_wce) { if (is_disk) { res = scsiGetSetCache(device, modese_len, &wce, &rcd); if (options.get_rcd) pout("Read Cache is: %s\n", res ? "Unavailable" : // error rcd ? "Disabled" : "Enabled"); if (options.get_wce) pout("Writeback Cache is: %s\n", res ? "Unavailable" : // error !wce ? "Disabled" : "Enabled"); } } else any_output = true; if (options.drive_info) pout("\n"); // START OF THE ENABLE/DISABLE SECTION OF THE CODE if (options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable) pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); if (options.smart_enable) { if (scsiSmartEnable(device)) failuretest(MANDATORY_CMD, returnval |= FAILSMART); any_output = true; } if (options.smart_disable) { if (scsiSmartDisable(device)) failuretest(MANDATORY_CMD,returnval |= FAILSMART); any_output = true; } if (options.smart_auto_save_enable) { if (scsiSetControlGLTSD(device, 0, modese_len)) { pout("Enable autosave (clear GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Autosave enabled (GLTSD bit cleared).\n"); any_output = true; } // Enable/Disable write cache if (options.set_wce && is_disk) { short int enable = wce = (options.set_wce > 0); rcd = -1; if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Write cache %sabled\n", (enable ? "en" : "dis")); any_output = true; } // Enable/Disable read cache if (options.set_rcd && is_disk) { short int enable = (options.set_rcd > 0); rcd = !enable; wce = -1; if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Read cache %sabled\n", (enable ? "en" : "dis")); any_output = true; } if (options.smart_auto_save_disable) { if (scsiSetControlGLTSD(device, 1, modese_len)) { pout("Disable autosave (set GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Autosave disabled (GLTSD bit set).\n"); any_output = true; } if (options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable) pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE // START OF READ-ONLY OPTIONS APART FROM -V and -i if (options.smart_check_status || options.smart_ss_media_log || options.smart_vendor_attrib || options.smart_error_log || options.smart_selftest_log || options.smart_background_log || options.sasphy) pout("=== START OF READ SMART DATA SECTION ===\n"); if (options.smart_check_status) { scsiGetSupportedLogPages(device); checkedSupportedLogPages = 1; if (is_tape) { if (gTapeAlertsLPage) { if (options.drive_info) pout("TapeAlert Supported\n"); if (-1 == scsiGetTapeAlertsData(device, peripheral_type)) failuretest(OPTIONAL_CMD, returnval |= FAILSMART); } else pout("TapeAlert Not Supported\n"); } else { /* disk, cd/dvd, enclosure, etc */ if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) { if (-2 == res) returnval |= FAILSTATUS; else returnval |= FAILSMART; } } any_output = true; } if (is_disk && options.smart_ss_media_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gSSMediaLPage) res = scsiPrintSSMedia(device); if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); if (gFormatStatusLPage) res = scsiPrintFormatStatus(device); if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_vendor_attrib) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); if (gTempLPage) scsiPrintTemp(device); if (gStartStopLPage) scsiGetStartStopData(device); if (is_disk) { scsiPrintGrownDefectListLen(device); if (gSeagateCacheLPage) scsiPrintSeagateCacheLPage(device); if (gSeagateFactoryLPage) scsiPrintSeagateFactoryLPage(device); } any_output = true; } if (options.smart_error_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); scsiPrintErrorCounterLog(device); if (gPendDefectsLPage) scsiPrintPendingDefectsLPage(device); if (1 == scsiFetchControlGLTSD(device, modese_len, 1)) pout("\n[GLTSD (Global Logging Target Save Disable) set. " "Enable Save with '-S on']\n"); any_output = true; } if (options.smart_selftest_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gSelfTestLPage) res = scsiPrintSelfTest(device); else { pout("Device does not support Self Test logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_background_log && is_disk) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gBackgroundResultsLPage) res = scsiPrintBackgroundResults(device); else { pout("Device does not support Background scan results logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_default_selftest) { if (scsiSmartDefaultSelfTest(device)) return returnval | FAILSMART; pout("Default Self Test Successful\n"); any_output = true; } if (options.smart_short_cap_selftest) { if (scsiSmartShortCapSelfTest(device)) return returnval | FAILSMART; pout("Short Foreground Self Test Successful\n"); any_output = true; } // check if another test is running if (options.smart_short_selftest || options.smart_extend_selftest) { if (!scsiRequestSense(device, &sense_info) && (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) { if (!options.smart_selftest_force) { pout("Can't start self-test without aborting current test"); if (sense_info.progress != -1) pout(" (%d%% remaining)", 100 - sense_info.progress * 100 / 65535); pout(",\nadd '-t force' option to override, or run " "'smartctl -X' to abort test.\n"); return -1; } else scsiSmartSelfTestAbort(device); } } if (options.smart_short_selftest) { if (scsiSmartShortSelfTest(device)) return returnval | FAILSMART; pout("Short Background Self Test has begun\n"); pout("Use smartctl -X to abort test\n"); any_output = true; } if (options.smart_extend_selftest) { if (scsiSmartExtendSelfTest(device)) return returnval | FAILSMART; pout("Extended Background Self Test has begun\n"); if ((0 == scsiFetchExtendedSelfTestTime(device, &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"); any_output = true; } if (options.smart_extend_cap_selftest) { if (scsiSmartExtendCapSelfTest(device)) return returnval | FAILSMART; pout("Extended Foreground Self Test Successful\n"); } if (options.smart_selftest_abort) { if (scsiSmartSelfTestAbort(device)) return returnval | FAILSMART; pout("Self Test returned without error\n"); any_output = true; } if (options.sasphy && gProtocolSpecificLPage) { if (scsiPrintSasPhy(device, options.sasphy_reset)) return returnval | FAILSMART; any_output = true; } if (!any_output) pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') " "to print SMART (and more) information\n\n"); return returnval; } smartmontools-7.0/scsiprint.h0000644000175000010010000000363713336335341013413 00000000000000/* * scsiprint.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-9 Bruce Allen * Copyright (C) 2000 Michael Cornwell * * Additional SCSI work: * Copyright (C) 2003-18 Douglas Gilbert * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef SCSI_PRINT_H_ #define SCSI_PRINT_H_ #define SCSIPRINT_H_CVSID "$Id: scsiprint.h 4760 2018-08-19 18:45:53Z chrfranke $\n" // Options for scsiPrintMain struct scsi_print_options { bool drive_info; bool smart_check_status; bool smart_vendor_attrib; bool smart_error_log; bool smart_selftest_log; bool smart_background_log; bool smart_ss_media_log; bool smart_disable, smart_enable; bool smart_auto_save_disable, smart_auto_save_enable; bool smart_default_selftest; bool smart_short_selftest, smart_short_cap_selftest; bool smart_extend_selftest, smart_extend_cap_selftest; bool smart_selftest_abort; bool smart_selftest_force; // Ignore already running test bool sasphy, sasphy_reset; bool get_wce, get_rcd; short int set_wce, set_rcd; // disable(-1), enable(1) cache scsi_print_options() : drive_info(false), smart_check_status(false), smart_vendor_attrib(false), smart_error_log(false), smart_selftest_log(false), smart_background_log(false), smart_ss_media_log(false), smart_disable(false), smart_enable(false), smart_auto_save_disable(false), smart_auto_save_enable(false), smart_default_selftest(false), smart_short_selftest(false), smart_short_cap_selftest(false), smart_extend_selftest(false), smart_extend_cap_selftest(false), smart_selftest_abort(false), smart_selftest_force(false), sasphy(false), sasphy_reset(false), get_wce(false), get_rcd(false), set_wce(0), set_rcd(0) { } }; int scsiPrintMain(scsi_device * device, const scsi_print_options & options); #endif smartmontools-7.0/sg_unaligned.h0000644000175000010010000003144113265444700014027 00000000000000#ifndef SG_UNALIGNED_H #define SG_UNALIGNED_H /* * Copyright (c) 2014-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include /* for uint8_t and friends */ #include /* for memcpy */ #ifdef __cplusplus extern "C" { #endif /* These inline functions convert integers (always unsigned) to byte streams * and vice versa. They have two goals: * - change the byte ordering of integers between host order and big * endian ("_be") or little endian ("_le") * - copy the big or little endian byte stream so it complies with any * alignment that host integers require * * Host integer to given endian byte stream is a "_put_" function taking * two arguments (integer and pointer to byte stream) returning void. * Given endian byte stream to host integer is a "_get_" function that takes * one argument and returns an integer of appropriate size (uint32_t for 24 * bit operations, uint64_t for 48 bit operations). * * Big endian byte format "on the wire" is the default used by SCSI * standards (www.t10.org). Big endian is also the network byte order. * Little endian is used by ATA, PCI and NVMe. */ /* The generic form of these routines was borrowed from the Linux kernel, * via mhvtl. There is a specialised version of the main functions for * little endian or big endian provided that not-quite-standard defines for * endianness are available from the compiler and the header * (a GNU extension) has been detected by ./configure . To force the * generic version, use './configure --disable-fast-lebe ' . */ /* Note: Assumes that the source and destination locations do not overlap. * An example of overlapping source and destination: * sg_put_unaligned_le64(j, ((uint8_t *)&j) + 1); * Best not to do things like that. */ #ifdef HAVE_CONFIG_H #include "config.h" /* need this to see if HAVE_BYTESWAP_H */ #endif #undef GOT_UNALIGNED_SPECIALS /* just in case */ #if defined(__BYTE_ORDER__) && defined(HAVE_BYTESWAP_H) && \ ! defined(IGNORE_FAST_LEBE) #if defined(__LITTLE_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define GOT_UNALIGNED_SPECIALS 1 #include /* for bswap_16(), bswap_32() and bswap_64() */ // #warning ">>>>>> Doing Little endian special unaligneds" static inline uint16_t sg_get_unaligned_be16(const void *p) { uint16_t u; memcpy(&u, p, 2); return bswap_16(u); } static inline uint32_t sg_get_unaligned_be32(const void *p) { uint32_t u; memcpy(&u, p, 4); return bswap_32(u); } static inline uint64_t sg_get_unaligned_be64(const void *p) { uint64_t u; memcpy(&u, p, 8); return bswap_64(u); } static inline void sg_put_unaligned_be16(uint16_t val, void *p) { uint16_t u = bswap_16(val); memcpy(p, &u, 2); } static inline void sg_put_unaligned_be32(uint32_t val, void *p) { uint32_t u = bswap_32(val); memcpy(p, &u, 4); } static inline void sg_put_unaligned_be64(uint64_t val, void *p) { uint64_t u = bswap_64(val); memcpy(p, &u, 8); } static inline uint16_t sg_get_unaligned_le16(const void *p) { uint16_t u; memcpy(&u, p, 2); return u; } static inline uint32_t sg_get_unaligned_le32(const void *p) { uint32_t u; memcpy(&u, p, 4); return u; } static inline uint64_t sg_get_unaligned_le64(const void *p) { uint64_t u; memcpy(&u, p, 8); return u; } static inline void sg_put_unaligned_le16(uint16_t val, void *p) { memcpy(p, &val, 2); } static inline void sg_put_unaligned_le32(uint32_t val, void *p) { memcpy(p, &val, 4); } static inline void sg_put_unaligned_le64(uint64_t val, void *p) { memcpy(p, &val, 8); } #elif defined(__BIG_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define GOT_UNALIGNED_SPECIALS 1 #include // #warning ">>>>>> Doing BIG endian special unaligneds" static inline uint16_t sg_get_unaligned_le16(const void *p) { uint16_t u; memcpy(&u, p, 2); return bswap_16(u); } static inline uint32_t sg_get_unaligned_le32(const void *p) { uint32_t u; memcpy(&u, p, 4); return bswap_32(u); } static inline uint64_t sg_get_unaligned_le64(const void *p) { uint64_t u; memcpy(&u, p, 8); return bswap_64(u); } static inline void sg_put_unaligned_le16(uint16_t val, void *p) { uint16_t u = bswap_16(val); memcpy(p, &u, 2); } static inline void sg_put_unaligned_le32(uint32_t val, void *p) { uint32_t u = bswap_32(val); memcpy(p, &u, 4); } static inline void sg_put_unaligned_le64(uint64_t val, void *p) { uint64_t u = bswap_64(val); memcpy(p, &u, 8); } static inline uint16_t sg_get_unaligned_be16(const void *p) { uint16_t u; memcpy(&u, p, 2); return u; } static inline uint32_t sg_get_unaligned_be32(const void *p) { uint32_t u; memcpy(&u, p, 4); return u; } static inline uint64_t sg_get_unaligned_be64(const void *p) { uint64_t u; memcpy(&u, p, 8); return u; } static inline void sg_put_unaligned_be16(uint16_t val, void *p) { memcpy(p, &val, 2); } static inline void sg_put_unaligned_be32(uint32_t val, void *p) { memcpy(p, &val, 4); } static inline void sg_put_unaligned_be64(uint64_t val, void *p) { memcpy(p, &val, 8); } #endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */ #endif /* #if defined __BYTE_ORDER__ && defined && * ! defined IGNORE_FAST_LEBE */ #ifndef GOT_UNALIGNED_SPECIALS /* Now we have no tricks left, so use the only way this can be done * correctly in C safely: lots of shifts. */ // #warning ">>>>>> Doing GENERIC unaligneds" static inline uint16_t sg_get_unaligned_be16(const void *p) { return ((const uint8_t *)p)[0] << 8 | ((const uint8_t *)p)[1]; } static inline uint32_t sg_get_unaligned_be32(const void *p) { return ((const uint8_t *)p)[0] << 24 | ((const uint8_t *)p)[1] << 16 | ((const uint8_t *)p)[2] << 8 | ((const uint8_t *)p)[3]; } static inline uint64_t sg_get_unaligned_be64(const void *p) { return (uint64_t)sg_get_unaligned_be32(p) << 32 | sg_get_unaligned_be32((const uint8_t *)p + 4); } static inline void sg_put_unaligned_be16(uint16_t val, void *p) { ((uint8_t *)p)[0] = (uint8_t)(val >> 8); ((uint8_t *)p)[1] = (uint8_t)val; } static inline void sg_put_unaligned_be32(uint32_t val, void *p) { sg_put_unaligned_be16(val >> 16, p); sg_put_unaligned_be16(val, (uint8_t *)p + 2); } static inline void sg_put_unaligned_be64(uint64_t val, void *p) { sg_put_unaligned_be32(val >> 32, p); sg_put_unaligned_be32(val, (uint8_t *)p + 4); } static inline uint16_t sg_get_unaligned_le16(const void *p) { return ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0]; } static inline uint32_t sg_get_unaligned_le32(const void *p) { return ((const uint8_t *)p)[3] << 24 | ((const uint8_t *)p)[2] << 16 | ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0]; } static inline uint64_t sg_get_unaligned_le64(const void *p) { return (uint64_t)sg_get_unaligned_le32((const uint8_t *)p + 4) << 32 | sg_get_unaligned_le32(p); } static inline void sg_put_unaligned_le16(uint16_t val, void *p) { ((uint8_t *)p)[0] = val & 0xff; ((uint8_t *)p)[1] = val >> 8; } static inline void sg_put_unaligned_le32(uint32_t val, void *p) { sg_put_unaligned_le16(val >> 16, (uint8_t *)p + 2); sg_put_unaligned_le16(val, p); } static inline void sg_put_unaligned_le64(uint64_t val, void *p) { sg_put_unaligned_le32(val >> 32, (uint8_t *)p + 4); sg_put_unaligned_le32(val, p); } #endif /* #ifndef GOT_UNALIGNED_SPECIALS */ /* Following are lesser used conversions that don't have specializations * for endianness; big endian first. In summary these are the 24, 48 bit and * given-length conversions plus the "nz" conditional put conversions. */ /* Now big endian, get 24+48 then put 24+48 */ static inline uint32_t sg_get_unaligned_be24(const void *p) { return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[2]; } /* Assume 48 bit value placed in uint64_t */ static inline uint64_t sg_get_unaligned_be48(const void *p) { return (uint64_t)sg_get_unaligned_be16(p) << 32 | sg_get_unaligned_be32((const uint8_t *)p + 2); } /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is * an 8 byte unsigned integer. */ static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p) { if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) return 0; else { const uint8_t * xp = (const uint8_t *)p; uint64_t res = *xp; for (++xp; num_bytes > 1; ++xp, --num_bytes) res = (res << 8) | *xp; return res; } } static inline void sg_put_unaligned_be24(uint32_t val, void *p) { ((uint8_t *)p)[0] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[2] = val & 0xff; } /* Assume 48 bit value placed in uint64_t */ static inline void sg_put_unaligned_be48(uint64_t val, void *p) { sg_put_unaligned_be16(val >> 32, p); sg_put_unaligned_be32(val, (uint8_t *)p + 2); } /* Now little endian, get 24+48 then put 24+48 */ static inline uint32_t sg_get_unaligned_le24(const void *p) { return (uint32_t)sg_get_unaligned_le16(p) | ((const uint8_t *)p)[2] << 16; } /* Assume 48 bit value placed in uint64_t */ static inline uint64_t sg_get_unaligned_le48(const void *p) { return (uint64_t)sg_get_unaligned_le16((const uint8_t *)p + 4) << 32 | sg_get_unaligned_le32(p); } static inline void sg_put_unaligned_le24(uint32_t val, void *p) { ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } /* Assume 48 bit value placed in uint64_t */ static inline void sg_put_unaligned_le48(uint64_t val, void *p) { ((uint8_t *)p)[5] = (val >> 40) & 0xff; ((uint8_t *)p)[4] = (val >> 32) & 0xff; ((uint8_t *)p)[3] = (val >> 24) & 0xff; ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is * an 8 byte unsigned integer. */ static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p) { if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) return 0; else { const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1); uint64_t res = *xp; for (--xp; num_bytes > 1; --xp, --num_bytes) res = (res << 8) | *xp; return res; } } /* Since cdb and parameter blocks are often memset to zero before these * unaligned function partially fill them, then check for a val of zero * and ignore if it is with these variants. First big endian, then little */ static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) { if (val) sg_put_unaligned_be16(val, p); } static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) { if (val) { ((uint8_t *)p)[0] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[2] = val & 0xff; } } static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) { if (val) sg_put_unaligned_be32(val, p); } static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) { if (val) sg_put_unaligned_be64(val, p); } static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p) { if (val) sg_put_unaligned_le16(val, p); } static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p) { if (val) { ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } } static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p) { if (val) sg_put_unaligned_le32(val, p); } static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p) { if (val) sg_put_unaligned_le64(val, p); } #ifdef __cplusplus } #endif #endif /* SG_UNALIGNED_H */ smartmontools-7.0/smartctl.8.in0000644000175000010010000030421313411763225013545 00000000000000.ig Copyright (C) 2002-10 Bruce Allen Copyright (C) 2004-18 Christian Franke SPDX-License-Identifier: GPL-2.0-or-later $Id: smartctl.8.in 4882 2018-12-29 21:26:45Z chrfranke $ .. .\" Macros borrowed from pages generated with Pod::Man .de Sp \" Vertical space (when we can't use .PP) .if t .sp 0.4v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Use groff extension \(aq (apostrophe quote, ASCII 0x27) if possible .ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH SMARTCTL 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools" .SH NAME \fBsmartctl\fP \- Control and Monitor Utility for SMART Disks .Sp .SH SYNOPSIS .B smartctl [options] device .Sp .SH DESCRIPTION .\" %IF NOT OS ALL .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools. .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL \fBsmartctl\fP controls the Self-Monitoring, Analysis and Reporting Technology (SMART) system built into most ATA/SATA and SCSI/SAS hard drives and solid-state drives. The purpose of SMART is to monitor the reliability of the hard drive and predict drive failures, and to carry out different types of drive self-tests. \fBsmartctl\fP also supports some features not related to SMART. This version of \fBsmartctl\fP is compatible with ACS-3, ACS-2, ATA8-ACS, ATA/ATAPI-7 and earlier standards (see \fBREFERENCES\fP below). .PP \fBsmartctl\fP also provides support for polling TapeAlert messages from SCSI tape drives and changers. .PP The user must specify the device to be controlled or interrogated as the final argument to \fBsmartctl\fP. The command set used by the device is often derived from the device path but may need help with the \*(Aq\-d\*(Aq option (for more information see the section on "ATA, SCSI command sets and SAT" below). Device paths are as follows: .\" %IF OS Linux .IP \fBLINUX\fP: 9 Use the forms \fB"/dev/sd[a\-z]"\fP for ATA/SATA and SCSI/SAS devices. For SCSI Tape Drives and Changers with TapeAlert support use the devices \fB"/dev/nst*"\fP and \fB"/dev/sg*"\fP. For disks behind 3ware controllers you may need \fB"/dev/sd[a\-z]"\fP or \fB"/dev/twe[0\-9]"\fP, \fB"/dev/twa[0\-9]"\fP or \fB"/dev/twl[0\-9]"\fP: see details below. For disks behind HighPoint RocketRAID controllers you may need \fB"/dev/sd[a\-z]"\fP. For disks behind Areca SATA RAID controllers, you need \fB"/dev/sg[2\-9]"\fP (note that smartmontools interacts with the Areca controllers via a SCSI generic device which is different than the SCSI device used for reading and writing data)! For HP Smart Array RAID controllers, there are three currently supported drivers: cciss, hpsa, and hpahcisr. For disks accessed via the cciss driver the device nodes are of the form \fB"/dev/cciss/c[0\-9]d0"\fP. For disks accessed via the hpahcisr and hpsa drivers, the device nodes you need are \fB"/dev/sg[0\-9]*"\fP. ("lsscsi \-g" is helpful in determining which scsi generic device node corresponds to which device.) Use the nodes corresponding to the RAID controllers, not the nodes corresponding to logical drives. See the \fB\-d\fP option below, as well. Use the forms \fB"/dev/nvme[0\-9]"\fP (broadcast namespace) or \fB"/dev/nvme[0\-9]n[1\-9]"\fP (specific namespace 1\-9) for NVMe devices. .\" %ENDIF OS Linux .\" %IF OS Darwin .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 \*(Aq\-h\*(Aq to see some examples. .Sp [NEW EXPERIMENTAL SMARTCTL FEATURE] There is NVMe support based on the undocumented SMART API in OSX. Currently only SMART and Controller information pages are supported. .Sp Note that Darwin SCSI support is not yet implemented. .Sp Use the OS X SAT SMART Driver to access SMART data on SAT capable USB and Firewire devices (see INSTALL file). .\" %ENDIF OS Darwin .\" %IF OS FreeBSD .IP \fBFREEBSD\fP: 9 Use the forms \fB"/dev/ad[0\-9]+"\fP for IDE/ATA devices and \fB"/dev/da[0\-9]+"\fP or \fB"/dev/pass[0\-9]+"\fP for SCSI devices. For SATA devices on AHCI bus use \fB"/dev/ada[0\-9]+"\fP format. For HP Smart Array RAID controllers, use \fB"/dev/ciss[0\-9]"\fP (and see the \fB\-d\fP option, below). .\" %ENDIF OS FreeBSD .\" %IF OS NetBSD OpenBSD .IP \fBNETBSD/OPENBSD\fP: 9 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. .\" %ENDIF OS NetBSD OpenBSD .\" %IF OS Solaris .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. .\" %ENDIF OS Solaris .\" %IF OS Windows Cygwin .IP \fBWINDOWS\fP: 9 Use the forms \fB"/dev/sd[a\-z]"\fP for IDE/(S)ATA and SCSI disks "\\\\.\\PhysicalDrive[0\-25]" (where "a" maps to "0"). Use \fB"/dev/sd[a\-z][a\-z]"\fP for "\\\\.\\PhysicalDrive[26\-...]". These disks can also be referred to as \fB"/dev/pd[0\-255]"\fP for "\\\\.\\PhysicalDrive[0\-255]". ATA disks can also be referred to as \fB"/dev/hd[a\-z]"\fP for "\\\\.\\PhysicalDrive[0\-25]". Use one the forms \fB"/dev/tape[0\-255]"\fP, \fB"/dev/st[0\-255]"\fP, or \fB"/dev/nst[0\-255]"\fP for SCSI tape drives "\\\\.\\Tape[0\-255]". .Sp Alternatively, drive letters \fB"X:"\fP or \fB"X:\\"\fP may be used to specify the (\*(Aqbasic\*(Aq) disk behind a mounted partition. This does not work with \*(Aqdynamic\*(Aq disks. .Sp For disks behind 3ware 9000 controllers use \fB"/dev/sd[a\-z],N"\fP where N specifies the disk number (3ware \*(Aqport\*(Aq) behind the controller providing the logical drive (\*(Aqunit\*(Aq) specified by \fB"/dev/sd[a\-z]"\fP. Alternatively, use \fB"/dev/tw_cli/cx/py"\fP for controller x, port y to run the \*(Aqtw_cli\*(Aq tool and parse the output. This provides limited monitoring (\*(Aq\-i\*(Aq, \*(Aq\-c\*(Aq, \*(Aq\-A\*(Aq below) if SMART support is missing in the driver. Use \fB"/dev/tw_cli/stdin"\fP or \fB"/dev/tw_cli/clip"\fP to parse CLI or 3DM output from standard input or clipboard. The option \*(Aq\-d 3ware,N\*(Aq is not necessary on Windows. .Sp For disks behind an Intel ICHxR controller with RST driver use \fB"/dev/csmi[0\-9],N"\fP where N specifies the port behind the logical scsi controller "\\\\.\\Scsi[0\-9]:". .Sp For SATA or SAS disks behind an Areca controller use \fB"/dev/arcmsr[0\-9]"\fP, see \*(Aq\-d areca,N[/E]\*(Aq below. .Sp Use the forms \fB"/dev/nvme[0\-9]"\fP (broadcast namespace) or \fB"/dev/nvme[0\-9]n[1\-9]"\fP (specific namespace 1\-9) for first, second, ..., NVMe device. Alternatively use the forms \fB"/dev/nvmes[0\-9][n[1\-9]]"\fP for NVMe devices behind the logical scsi controller "\\\\.\\Scsi[0\-9]:". Both forms require a NVMe driver which supports NVME_PASS_THROUGH_IOCTL. .Sp [NEW EXPERIMENTAL SMARTCTL FEATURE] Use the forms \fB"/dev/sd[...]"\fP or \fB"/dev/pd[...]"\fP (see above) for NVMe devices behind Windows 10 NVMe driver (stornvme.sys). .Sp The prefix \fB"/dev/"\fP is optional. .\" %ENDIF OS Windows Cygwin .\" %IF OS OS2 .IP \fBOS/2,eComStation\fP: 9 Use the form \fB"/dev/hd[a\-z]"\fP for ATA/SATA devices using DANIS506 driver. .Sp Use the form \fB"/dev/ahci[a\-z]"\fP for ATA/SATA devices using OS2AHCI driver. .\" %ENDIF OS OS2 .PP if \*(Aq\-\*(Aq is specified as the device path, \fBsmartctl\fP reads and interprets it's own debug output from standard input. See \*(Aq\-r ataioctl\*(Aq below for details. .PP \fBsmartctl\fP guesses the device type if possible. If necessary, the \*(Aq\-d\*(Aq option can be used to override this guess. .PP Note that the printed output of \fBsmartctl\fP displays most numerical values in base 10 (decimal), but some values are displayed in base 16 (hexadecimal). 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. .Sp .SH OPTIONS 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. .Sp .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 SVN revision information for your copy of \fBsmartctl\fP to STDOUT and then exits. .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 \*(Aq\-v\*(Aq options below). If so, the drive model family may also be printed. If \*(Aq\-n\*(Aq (see below) is specified, the power mode of the drive is printed. .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp [NVMe] For NVMe devices the information is obtained from the Identify Controller and the Identify Namespace data structure. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-\-identify[=[w][nvb]] [ATA only] Prints an annotated table of the IDENTIFY DEVICE data. By default, only valid words (words not equal to 0x0000 or 0xffff) and nonzero bits and bit fields are printed. This can be changed by the optional argument which consists of one or two characters from the set \*(Aqwnvb\*(Aq. The character \*(Aqw\*(Aq enables printing of all 256 words. The character \*(Aqn\*(Aq suppresses printing of bits, \*(Aqv\*(Aq enables printing of all bits from valid words, \*(Aqb\*(Aq enables printing of all bits. For example \*(Aq\-\-identify=n\*(Aq (valid words, no bits) produces the shortest output and \*(Aq\-\-identify=wb\*(Aq (all words, all bits) produces the longest output. .TP .B \-a, \-\-all Prints all SMART information about the disk, or TapeAlert information about the tape drive or changer. For ATA devices this is equivalent to .br \*(Aq\-H \-i \-c \-A \-l error \-l selftest \-l selective\*(Aq .br and for SCSI, this is equivalent to .br \*(Aq\-H \-i \-A \-l error \-l selftest\*(Aq. .br .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin For NVMe, this is equivalent to .br \*(Aq\-H \-i \-c \-A \-l error\*(Aq. .br .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin Note that for ATA disks this does \fBnot\fP enable the non-SMART options and the SMART options which require support for 48-bit ATA commands. .TP .B \-x, \-\-xall Prints all SMART and non-SMART information about the device. For ATA devices this is equivalent to .br \*(Aq\-H \-i \-g all \-g wcreorder \-c \-A \-f brief \-l xerror,error \-l xselftest,selftest \-l selective \-l directory \-l scttemp \-l scterc \-l devstat \-l defects \-l sataphy\*(Aq. .br and for SCSI, this is equivalent to .br \*(Aq\-H \-i \-g all \-A \-l error \-l selftest \-l background \-l sasphy\*(Aq. .br .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin For NVMe, this is equivalent to .br \*(Aq\-H \-i \-c \-A \-l error\*(Aq. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-\-scan Scans for devices and prints each device name, device type and protocol ([ATA] or [SCSI]) info. May be used in conjunction with \*(Aq\-d TYPE\*(Aq to restrict the scan to a specific TYPE. See also info about platform specific device scan and the \fBDEVICESCAN\fP directive on \fBsmartd\fP(8) man page. .TP .B \-\-scan\-open Same as \-\-scan, but also tries to open each device before printing device info. The device open may change the device type due to autodetection (see also \*(Aq\-d test\*(Aq). .Sp This option can be used to create a draft \fBsmartd.conf\fP file. All options after \*(Aq\-\-\*(Aq are appended to each output line. For example: .Vb 1 smartctl \-\-scan\-open \-\- \-a \-W 4,45,50 \-m admin@work > smartd.conf .Ve .Sp Multiple \*(Aq\-d TYPE\*(Aq options may be specified with \*(Aq\-\-scan[\-open]\*(Aq to combine the scan results of more than one TYPE. .TP .B \-g NAME, \-\-get=NAME Get non-SMART device settings. See \*(Aq\-s, \-\-set\*(Aq below for further info. .Sp .TP .B RUN-TIME BEHAVIOR OPTIONS: .TP .B \-j, \-\-json[=cgiosuv] [NEW EXPERIMENTAL SMARTCTL FEATURE] Enables JSON output mode. .Sp The output could be modified or enhanced by the optional argument which consists of one or more characters from the set \*(Aqcgiosuv\*(Aq: .br \*(Aqc\*(Aq: Outputs \fBc\fRompact format without extra spaces and newlines. By default, output is pretty-printed. .br \*(Aqg\*(Aq: Outputs JSON structure as single assignments to allow the usage of \fBg\fRrep. Each assignment reflects the absolute path of a value. The syntax is compatible with \fBgron\fR: .br \*(Aqjson.KEY1[INDEX2].KEY3 = VALUE;\*(Aq. .br \*(Aqo\*(Aq: Includes the full \fBo\fRriginal plaintext \fBo\fRutput of \fBsmartctl\fR as a JSON array \*(Aqsmartctl.output[]\*(Aq. .br \*(Aqs\*(Aq: Outputs JSON object elements \fBs\fRorted by key. By default, object elements are ordered as generated internally. .br \*(Aqv\*(Aq: Enables \fBv\fRerbose output of possible unsafe integers. If specified, values which may exceed JSON safe integer (53-bit) range are always output as a number (with some \*(AqKEY\*(Aq) and a string (\*(AqKEY_s\*(Aq), regardless of the actual value. Values which may exceed 64-bit range are also output as a little endian byte array (\*(AqKEY_le\*(Aq). By default, the additional elements are only output if the value actually exceeds the range. .Sp The following two arguments are primarily indented for development: .br \*(Aqi\*(Aq: Includes lines from the plaintext output which print info already \fBi\fRmplemented for JSON output. The lines appear as objects with key \*(Aqsmartctl_NNNN_i\*(Aq. .br \*(Aqu\*(Aq: Includes lines from the plaintext output which print info still \fBu\fRnimplemented for JSON output. The lines appear as objects with key \*(Aqsmartctl_NNNN_u\*(Aq. .TP .B \-q TYPE, \-\-quietmode=TYPE Specifies that \fBsmartctl\fP should run in one of the quiet modes described here. The valid arguments to this option are: .Sp .I errorsonly \- only print: For the \*(Aq\-l error\*(Aq option, if nonzero, the number of errors recorded in the SMART error log and the power-on time when they occurred; For the \*(Aq\-l selftest\*(Aq option, errors recorded in the device self-test log; For the \*(Aq\-H\*(Aq option, SMART "disk failing" status or device Attributes (pre-failure or usage) which failed either now or in the past; For the \*(Aq\-A\*(Aq option, device Attributes (pre-failure or usage) which failed either now or in the past. .Sp .I silent \- print no output. The only way to learn about what was found is to use the exit status of \fBsmartctl\fP (see EXIT STATUS below). .Sp .I noserial \- Do not print the serial number of the device. .TP .B \-d TYPE, \-\-device=TYPE Specifies the type of the device. The valid arguments to this option are: .Sp .I auto \- attempt to guess the device type from the device name or from controller type info provided by the operating system or from a matching USB ID entry in the drive database. This is the default. .Sp .I test \- prints the guessed TYPE, then opens the device and prints the (possibly changed) TYPE name and then exits without performing any further commands. .Sp .I ata \- the device type is ATA. This prevents \fBsmartctl\fP from issuing SCSI commands to an ATA device. .Sp .\" %IF NOT OS Darwin .I scsi \- the device type is SCSI. This prevents \fBsmartctl\fP from issuing ATA commands to a SCSI device. .Sp .\" %ENDIF NOT OS Darwin .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I nvme[,NSID] \- the device type is NVM Express (NVMe). The optional parameter NSID specifies the namespace id (in hex) passed to the driver. Use 0xffffffff for the broadcast namespace id. The default for NSID is the namespace id addressed by the device name. .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .\" %IF NOT OS Darwin .I sat[,auto][,N] \- the device type is SCSI to ATA Translation (SAT). This is for ATA disks that have a SCSI to ATA Translation Layer (SATL) between the disk and the operating system. SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and the other 16 bytes long. The default is the 16 byte variant which can be overridden with either \*(Aq\-d sat,12\*(Aq or \*(Aq\-d sat,16\*(Aq. .Sp If \*(Aq\-d sat,auto\*(Aq is specified, device type SAT (for ATA/SATA disks) is only used if the SCSI INQUIRY data reports a SATL (VENDOR: "ATA "). Otherwise device type SCSI (for SCSI/SAS disks) is used. .Sp .I usbcypress \- this device type is for ATA disks that are behind a Cypress USB to PATA bridge. This will use the ATACB proprietary scsi pass through command. The default SCSI operation code is 0x24, but although it can be overridden with \*(Aq\-d usbcypress,0xN\*(Aq, where N is the scsi operation code, you're running the risk of damage to the device or filesystems on it. .Sp .I usbjmicron[,p][,x][,PORT] \- this device type is for SATA disks that are behind a JMicron USB to PATA/SATA bridge. The 48-bit ATA commands (required e.g.\& for \*(Aq\-l xerror\*(Aq, see below) do not work with all of these bridges and are therefore disabled by default. These commands can be enabled by \*(Aq\-d usbjmicron,x\*(Aq. If two disks are connected to a bridge with two ports, an error message is printed if no PORT is specified. The port can be specified by \*(Aq\-d usbjmicron[,x],PORT\*(Aq where PORT is 0 (master) or 1 (slave). This is not necessary if the device uses a port multiplier to connect multiple disks to one port. The disks appear under separate /dev/ice names then. CAUTION: Specifying \*(Aq,x\*(Aq for a device which does not support it results in I/O errors and may disconnect the drive. The same applies if the specified PORT does not exist or is not connected to a disk. .Sp The Prolific PL2507/3507 USB bridges with older firmware support a pass-through command similar to JMicron and work with \*(Aq\-d usbjmicron,0\*(Aq. Newer Prolific firmware requires a modified command which can be selected by \*(Aq\-d usbjmicron,p\*(Aq. Note that this does not yet support the SMART status command. .Sp .I usbprolific \- this device type is for SATA disks that are behind a Prolific PL2571/2771/2773/2775 USB to SATA bridge. .Sp .I usbsunplus \- this device type is for SATA disks that are behind a SunplusIT USB to SATA bridge. .Sp .I sntjmicron[,NSID] \- [NEW EXPERIMENTAL SMARTCTL FEATURE] this device type is for NVMe disks that are behind a JMicron USB to NVMe bridge. The optional parameter NSID specifies the namespace id (in hex) passed to the driver. The default namespace id is the broadcast namespace id (0xffffffff). .Sp .\" %ENDIF NOT OS Darwin .\" %IF OS Linux .I marvell \- [Linux only] interact with SATA disks behind Marvell chip-set controllers (using the Marvell rather than libata driver). .Sp .I megaraid,N \- [Linux only] the device consists of one or more SCSI/SAS disks connected to a MegaRAID controller. The non-negative integer N (in the range of 0 to 127 inclusive) denotes which disk on the controller is monitored. Use syntax such as: .br \fBsmartctl \-a \-d megaraid,2 /dev/sda\fP .br \fBsmartctl \-a \-d megaraid,0 /dev/sdb\fP .br \fBsmartctl \-a \-d megaraid,0 /dev/bus/0\fP .br This interface will also work for Dell PERC controllers. It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus number. .Sp The following entry in /proc/devices must exist: .br For PERC2/3/4 controllers: \fBmegadevN\fP .br For PERC5/6 controllers: \fBmegaraid_sas_ioctlN\fP .Sp .\" %ENDIF OS Linux .\" %IF OS Linux Windows Cygwin .I aacraid,H,L,ID \- [Linux, Windows and Cygwin only] the device consists of one or more SCSI/SAS or SATA disks connected to an AacRaid controller. The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk on the controller is monitored. Use syntax such as: .br \fBsmartctl \-a \-d aacraid,0,0,2 /dev/sda\fP .br \fBsmartctl \-a \-d aacraid,1,0,4 /dev/sdb\fP .Sp Option \*(Aq\-d sat,auto+...\*(Aq is implicitly enabled to detect SATA disks. Use \*(Aq\-d scsi+aacraid,H,L,ID\*(Aq to disable it. .Sp .\" %ENDIF OS Linux Windows Cygwin .\" %IF OS Linux On Linux, the following entry in /proc/devices must exist: \fBaac\fP. Character device nodes /dev/aacH (H=Host number) are created if required. .Sp .\" %ENDIF OS Linux .\" %IF OS Windows Cygwin On Windows, the device name parameter /dev/sdX is ignored if \*(Aq\-d aacraid\*(Aq is specified. .Sp .\" %ENDIF OS Windows Cygwin .\" %IF OS FreeBSD Linux .I 3ware,N \- [FreeBSD and Linux only] 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 127 inclusive) denotes which disk on the controller is monitored. Use syntax such as: .br \fBsmartctl \-a \-d 3ware,2 /dev/sda\fP [Linux only] .br \fBsmartctl \-a \-d 3ware,0 /dev/twe0\fP .br \fBsmartctl \-a \-d 3ware,1 /dev/twa0\fP .br \fBsmartctl \-a \-d 3ware,1 /dev/twl0\fP [Linux only] .br \fBsmartctl \-a \-d 3ware,1 /dev/tws0\fP [FreeBSD only] .br The first two forms, which refer to devices /dev/sda\-z (deprecated) and /dev/twe0\-15, may be used with 3ware series 6000, 7000, and 8000 series controllers that use the 3x-xxxx driver. The devices /dev/twa0\-15, must be used with 3ware 9000 series controllers, which use the 3w\-9xxx driver. The devices /dev/twl0\-15 [Linux] or /dev/tws0\-15 [FreeBSD] must be used with the 3ware/LSI 9750 series controllers which use the 3w-sas driver. .Sp Note that if the special character device nodes /dev/tw[ls]?, /dev/twa? and /dev/twe? do not exist, or exist with the incorrect major or minor numbers, smartctl will recreate them on the fly. .Sp .\" %ENDIF OS FreeBSD Linux .\" %IF OS FreeBSD Linux Windows Cygwin .I areca,N \- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one or more SATA disks connected to an Areca SATA RAID controller. The positive integer N (in the range from 1 to 24 inclusive) denotes which disk on the controller is monitored. .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS Linux On Linux use syntax such as: .br \fBsmartctl \-a \-d areca,2 /dev/sg2\fP .br \fBsmartctl \-a \-d areca,3 /dev/sg3\fP .br .\" %ENDIF OS Linux .\" %IF OS FreeBSD On FreeBSD use syntax such as: .br \fBsmartctl \-a \-d areca,2 /dev/arcmsr1\fP .br \fBsmartctl \-a \-d areca,3 /dev/arcmsr2\fP .br .\" %ENDIF OS FreeBSD .\" %IF OS Windows Cygwin On Windows and Cygwin use syntax such as: .br \fBsmartctl \-a \-d areca,2 /dev/arcmsr0\fP .br \fBsmartctl \-a \-d areca,3 /dev/arcmsr1\fP .br .\" %ENDIF OS Windows Cygwin .\" %IF OS FreeBSD Linux Windows Cygwin The first line above addresses the second disk on the first Areca RAID controller. The second line addresses the third disk on the second Areca RAID controller. .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS Linux To help identify the correct device on Linux, use the command: .br \fBcat /proc/scsi/sg/device_hdr /proc/scsi/sg/devices\fP .br to show the SCSI generic devices (one per line, starting with /dev/sg0). The correct SCSI generic devices to address for smartmontools are the ones with the type field equal to 3. If the incorrect device is addressed, please read the warning/error messages carefully. They should provide hints about what devices to use. .\" %ENDIF OS Linux .\" %IF OS FreeBSD Linux Windows Cygwin .Sp Important: the Areca controller must have firmware version 1.46 or later. Lower-numbered firmware versions will give (harmless) SCSI error messages and no SMART information. .Sp .I areca,N/E \- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller. The integer N (range 1 to 128) denotes the channel (slot) and E (range 1 to 8) denotes the enclosure. Important: This requires Areca SAS controller firmware version 1.51 or later. .Sp .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS FreeBSD Linux .I cciss,N \- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS or SATA disks connected to a cciss RAID controller. The non-negative integer N (in the range from 0 to 15 inclusive) denotes which disk on the controller is monitored. .Sp Option \*(Aq\-d sat,auto+...\*(Aq is implicitly enabled to detect SATA disks. Use \*(Aq\-d scsi+cciss,N\*(Aq to disable it. .Sp To look at disks behind HP Smart Array controllers, use syntax such as: .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux .br \fBsmartctl \-a \-d cciss,0 /dev/cciss/c0d0\fP (cciss driver under Linux) .br \fBsmartctl \-a \-d cciss,0 /dev/sg2\fP (hpsa or hpahcisr drivers under Linux) .\" %ENDIF OS Linux .\" %IF OS FreeBSD .br \fBsmartctl \-a \-d cciss,0 /dev/ciss0\fP (under FreeBSD) .\" %ENDIF OS FreeBSD .\" %IF OS FreeBSD Linux .Sp .I hpt,L/M/N \- [FreeBSD and Linux only] the device consists of one or more ATA disks connected to a HighPoint RocketRAID controller. The integer L is the controller id, the integer M is the channel number, and the integer N is the PMPort number if it is available. The allowed values of L are from 1 to 4 inclusive, M are from 1 to 128 inclusive and N from 1 to 4 if PMPort available. And also these values are limited by the model of the HighPoint RocketRAID controller. Use syntax such as: .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux .br \fBsmartctl \-a \-d hpt,1/3 /dev/sda\fP (under Linux) .br \fBsmartctl \-a \-d hpt,1/2/3 /dev/sda\fP (under Linux) .\" %ENDIF OS Linux .\" %IF OS FreeBSD .br \fBsmartctl \-a \-d hpt,1/3 /dev/hptrr\fP (under FreeBSD) .br \fBsmartctl \-a \-d hpt,1/2/3 /dev/hptrr\fP (under FreeBSD) .\" %ENDIF OS FreeBSD .\" %IF OS FreeBSD Linux .br Note that the /dev/sda\-z form should be the device node which stands for the disks derived from the HighPoint RocketRAID controllers under Linux and under FreeBSD, it is the character device which the driver registered (eg, /dev/hptrr, /dev/hptmv6). .\" %ENDIF OS FreeBSD Linux .Sp .I intelliprop,N[+TYPE] \- [NEW EXPERIMENTAL SMARTCTL FEATURE] the device consists of multiple ATA disks connected to an Intelliprop controller. The integer N is the port number from 0 to 3 of the ATA drive to be targeted. The TYPE can be ata(default), sat, or a USB controller listed above. Note: if a type of ATA does not work, try a type of sat. Use syntax such as: .br \fBsmartctl \-a \-d intelliprop,1 /dev/sda\fP (under Linux) .br \fBsmartctl \-a \-d intelliprop,1+sat /dev/sda\fP (under Linux) .br \fBWARNING: The disks are selected by write commands to the ATA Device Vendor Specific Log at address 0xc0. Using this option with other devices may have undesirable side effects.\fP .TP .B \-T TYPE, \-\-tolerance=TYPE [ATA only] Specifies how tolerant \fBsmartctl\fP should be of ATA and SMART command failures. .Sp The behavior of \fBsmartctl\fP depends upon whether the command is "\fBoptional\fP" or "\fBmandatory\fP". Here "\fBmandatory\fP" means "required by the ATA Specification if the device implements the SMART command set" and "\fBoptional\fP" means "not required by the ATA Specification even if the device implements the SMART command set." The "\fBmandatory\fP" ATA and SMART commands are: (1) ATA IDENTIFY DEVICE, (2) SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE, (3) SMART ENABLE/DISABLE, and (4) SMART RETURN STATUS. .Sp The valid arguments to this option are: .Sp .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. .Sp .I conservative \- exit on failure of any \fBoptional\fP SMART command. .Sp .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 supported", followed shortly by "Feature X enable failed". In a few such cases, contrary to the final message, Feature X \fBis\fP enabled. .Sp .I verypermissive \- equivalent to giving a large number of \*(Aq\-T permissive\*(Aq options: ignore failures of \fBany number\fP of \fBmandatory\fP SMART commands. Please see the note above. .TP .B \-b TYPE, \-\-badsum=TYPE [ATA only] Specifies the action \fBsmartctl\fP should take if a checksum error is detected in the: (1) Device Identity Structure, (2) SMART Self-Test Log Structure, (3) SMART Attribute Value Structure, (4) SMART Attribute Threshold Structure, or (5) ATA Error Log Structure. .Sp The valid arguments to this option are: .Sp .I warn \- report the incorrect checksum but carry on in spite of it. This is the default. .Sp .I exit \- exit \fBsmartctl\fP. .Sp .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: .Sp .I ioctl \- report all ioctl() transactions. .Sp .I ataioctl \- report only ioctl() transactions with ATA devices. .Sp .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. .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I nvmeioctl \- report only ioctl() transactions with NVMe devices. .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin 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 \*(Aq\-r ataioctl,1\*(Aq and \*(Aq\-r ataioctl\*(Aq are equivalent. .Sp For testing purposes, the output of \*(Aq\-r ataioctl,2\*(Aq can later be parsed by \fBsmartctl\fP itself if \*(Aq\-\*(Aq is used as device path argument. The ATA command input parameters, sector data and return values are reconstructed from the debug report read from stdin. Then \fBsmartctl\fP internally simulates an ATA device with the same behaviour. This is does not work for SCSI devices yet. .TP .B \-n POWERMODE[,STATUS], \-\-nocheck=POWERMODE[,STATUS] [ATA only] Specifies if \fBsmartctl\fP should exit before performing any checks when the device is in a low-power mode. It may be used to prevent a disk from being spun-up by \fBsmartctl\fP. The power mode is ignored by default. .Sp Note: If this option is used it may also be necessary to specify the device type with the \*(Aq\-d\*(Aq option. Otherwise the device may spin up due to commands issued during device type autodetection. .Sp By default, exit status 2 is returned if the device is in one of the specified low-power modes. This status is also returned if the device open or identification failed (see EXIT STATUS below). .Sp [NEW EXPERIMENTAL SMARTCTL FEATURE] The optional STATUS parameter allows to override this default. STATUS is an integer in the range from 0 to 255 inclusive. For example use \*(Aq\-n standby,0\*(Aq to return success if a device is in SLEEP or STANDBY mode. Use \*(Aq\-n standby,3\*(Aq to return a unique exit status in this case. .Sp The valid arguments to this option are: .Sp .I never \- check the device always, but print the power mode if \*(Aq\-i\*(Aq is specified. .Sp .I sleep[,STATUS] \- check the device unless it is in SLEEP mode. .Sp .I standby[,STATUS] \- 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 disk from spinning up, this is probably what you want. .Sp .I idle[,STATUS] \- 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. .Sp .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. .Sp [ATA] Note that the ATA commands SMART ENABLE/DISABLE OPERATIONS were declared obsolete in ATA ACS-4 Revision 10 (Nov 2015). .Sp [SCSI tape drive or changer] It is not necessary (or useful) to enable SMART to see the TapeAlert messages. .TP .B \-o VALUE, \-\-offlineauto=VALUE [ATA only] 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. .Sp 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. You can tell if automatic offline testing is supported by seeing if this command enables and disables it, as indicated by the \*(AqAuto Offline Data Collection\*(Aq part of the SMART capabilities report (displayed with \*(Aq\-c\*(Aq). .Sp 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 \*(Aq\-s on\*(Aq option. .Sp The \fBsecond\fP category of testing is called "offline" testing. This type of test can, in principle, degrade the device performance. The \*(Aq\-o on\*(Aq 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 \*(Aq\-t offline\*(Aq option below, which causes a one-time offline test to be carried out immediately. .Sp 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. .Sp 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 \*(Aq\-A\*(Aq and \*(Aq\-l error\*(Aq options respectively. .Sp 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 \*(Aq\-A\*(Aq option indicates this in the UPDATED column. Attributes of the first type are labeled "Offline" and Attributes of the second type are labeled "Always". .Sp The \fBthird\fP category of testing (and the \fIonly\fP category for which the word \*(Aqtesting\*(Aq 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 \*(Aq\-t\*(Aq and \*(Aq\-X\*(Aq options can be used to carry out and abort such self-tests; please see below for further details. .Sp Any errors detected in the self testing will be shown in the SMART self-test log, which can be examined using the \*(Aq\-l selftest\*(Aq option. .Sp \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 [ATA] Enables or disables SMART autosave of device vendor-specific Attributes. The valid arguments to this option are \fIon\fP and \fIoff\fP. Note that this feature is preserved across disk power cycles, so you should only need to issue it once. .Sp The ATA standard does not specify a method to check whether SMART autosave is enabled. Unlike SCSI (below), smartctl is unable to print a warning if autosave is disabled. .Sp Note that the ATA commands SMART ENABLE/DISABLE AUTOSAVE were declared obsolete in ATA ACS-4 Revision 10 (Nov 2015). .Sp [SCSI] For SCSI devices this toggles the value of the Global Logging Target Save Disabled (GLTSD) bit in the Control Mode Page. Some disk manufacturers set this bit by default. This prevents error counters, power-up hours and other useful data from being placed in non-volatile storage, so these values may be reset to zero the next time the device is power-cycled. If the GLTSD bit is set then \*(Aqsmartctl \-a\*(Aq 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 \-g NAME, \-\-get=NAME, \-s NAME[,VALUE], \-\-set=NAME[,VALUE] Gets/sets non-SMART device settings. Note that the \*(Aq\-\-set\*(Aq option shares its short option \*(Aq\-s\*(Aq with \*(Aq\-\-smart\*(Aq. Valid arguments are: .Sp .I all \- Gets all values. This is equivalent to .br \*(Aq\-g aam \-g apm \-g lookahead \-g security \-g wcache \-g rcache \-g dsn\*(Aq .Sp .I aam[,N|off] \- [ATA only] Gets/sets the Automatic Acoustic Management (AAM) feature (if supported). A value of 128 sets the most quiet (slowest) mode and 254 the fastest (loudest) mode, \*(Aqoff\*(Aq disables AAM. Devices may support intermediate levels. Values below 128 are defined as vendor specific (0) or retired (1 to 127). Note that the AAM feature was declared obsolete in ATA ACS-2 Revision 4a (Dec 2010). .Sp .I apm[,N|off] \- [ATA only] Gets/sets the Advanced Power Management (APM) feature on device (if supported). If a value between 1 and 254 is provided, it will attempt to enable APM and set the specified value, \*(Aqoff\*(Aq disables APM. Note the actual behavior depends on the drive, for example some drives disable APM if their value is set above 128. Values below 128 are supposed to allow drive spindown, values 128 and above adjust only head-parking frequency, although the actual behavior defined is also vendor-specific. .Sp .I lookahead[,on|off] \- [ATA only] Gets/sets the read look-ahead feature (if supported). Read look-ahead is usually enabled by default. .Sp .I security \- [ATA only] Gets the status of ATA Security feature (if supported). If ATA Security is enabled an ATA user password is set. The drive will be locked on next reset then. .Sp .I security-freeze \- [ATA only] Sets ATA Security feature to frozen mode. This prevents that the drive accepts any security commands until next reset. Note that the frozen mode may already be set by BIOS or OS. .Sp .I standby,[N|off] \- [ATA only] Sets the standby (spindown) timer and places the drive in the IDLE mode. A value of 0 or \*(Aqoff\*(Aq disables the standby timer. Values from 1 to 240 specify timeouts from 5 seconds to 20 minutes in 5 second increments. Values from 241 to 251 specify timeouts from 30 minutes to 330 minutes in 30 minute increments. Value 252 specifies 21 minutes. Value 253 specifies a vendor specific time between 8 and 12 hours. Value 255 specifies 21 minutes and 15 seconds. Some drives may use a vendor specific interpretation for the values. Note that there is no get option because ATA standards do not specify a method to read the standby timer. .br [NEW EXPERIMENTAL SMARTCTL FEATURE] If \*(Aq\-s standby,now\*(Aq is also specified, the drive is immediately placed in the STANDBY mode without temporarily placing it in the IDLE mode. Note that ATA standards do not specify a command to set the standby timer without affecting the power mode. .Sp .I standby,now \- [ATA only] Places the drive in the STANDBY mode. This usually spins down the drive. The setting of the standby timer is not affected unless \*(Aq\-s standby,[N|off]\*(Aq is also specified. .Sp .I wcache[,on|off] \- [ATA] Gets/sets the volatile write cache feature (if supported). The write cache is usually enabled by default. .Sp .I wcache[,on|off] \- [SCSI] Gets/sets the \*(AqWrite Cache Enable\*(Aq (WCE) bit (if supported). The write cache is usually enabled by default. .Sp .I wcache-sct[,ata|on|off[,p]] \- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] Gets/sets the write cache feature through SCT Feature Control (if supported). The state of write cache in SCT Feature Control could be "Controlled by ATA", "Force Enabled", or "Force Disabled". SCT Feature control overwrites the setting by ATA Set Features command (wcache[,on|off] option). If SCT Feature Control sets write cache as "Force Enabled" or "Force Disabled", the setting of wcache[,on|off] is ignored by the drive. SCT Feature Control usually sets write cache as "Controlled by ATA" by default. If \*(Aq,p\*(Aq is specified, the setting is preserved across power cycles. .Sp .I wcreorder[,on|off[,p]] \- [ATA only] Gets/sets Write Cache Reordering. If it is disabled (off), disk write scheduling is executed on a first-in-first-out (FIFO) basis. If Write Cache Reordering is enabled (on), then disk write scheduling may be reordered by the drive. If write cache is disabled, the current Write Cache Reordering state is remembered but has no effect on non-cached writes, which are always written in the order received. The state of Write Cache Reordering has no effect on either NCQ or LCQ queued commands. [NEW EXPERIMENTAL SMARTCTL FEATURE] If \*(Aq,p\*(Aq is specified, the setting is preserved across power cycles. .Sp .I rcache[,on|off] \- [SCSI only] Gets/sets the \*(AqRead Cache Disable\*(Aq (RCE) bit. \*(AqOff\*(Aq value disables read cache (if supported). The read cache is usually enabled by default. .Sp .I dsn[,on|off] \- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] Gets/sets the DSN feature (if supported). The dsn is usually disabled by default. .Sp .TP .B SMART READ AND DISPLAY DATA OPTIONS: .TP .B \-H, \-\-health Prints the health status of the device or pending TapeAlert messages. .Sp 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 \*(Aq\-a\*(Aq option to get more information, and .B get your data off the disk and to someplace safe as soon as you can. .Sp [ATA] Health status is obtained by checking the (boolean) result returned by the SMART RETURN STATUS command. The return value of this ATA command may be unknown due to limitations or bugs in some layer (e.g.\& RAID controller or USB bridge firmware) between disk and operating system. In this case, \fBsmartctl\fP prints a warning and checks whether any Prefailure SMART Attribute value is less than or equal to its threshold (see \*(Aq\-A\*(Aq below). .Sp [SCSI] Health status is obtained by checking the Additional Sense Code (ASC) and Additional Sense Code Qualifier (ASCQ) from Informal Exceptions (IE) log page (if supported) and/or from SCSI sense data. .Sp [SCSI tape drive or changer] TapeAlert status is obtained by reading the TapeAlert log page. Please note that 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. .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp [NVMe] NVMe status is obtained by reading the "Critical Warning" byte from the SMART/Health Information log. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-c, \-\-capabilities [ATA] 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. .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp [NVMe] Prints various NVMe device capabilities obtained from the Identify Controller and the Identify Namespace data structure. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-A, \-\-attributes [ATA] 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. .Sp 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. .Sp 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. .Sp 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. .Sp 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.] .Sp 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. .Sp 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: \*(Aq\-\*(Aq) then this Attribute is OK now (not failing) and has also never failed in the past. .Sp 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". .Sp 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\(en254. 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. .Sp Note that starting with ATA/ATAPI-4, revision 4, the meaning of these Attribute fields has been made entirely vendor-specific. However most newer ATA/SATA disks seem to respect their meaning, so we have retained the option of printing the Attribute values. .Sp Solid-state drives use different meanings for some of the attributes. In this case the attribute name printed by smartctl is incorrect unless the drive is already in the smartmontools drive database. .Sp Note that the ATA command SMART READ DATA was declared obsolete in ATA ACS-4 Revision 10 (Nov 2015). .Sp [SCSI] 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). .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp [NVMe] For NVMe devices the attributes are obtained from the SMART/Health Information log. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-f FORMAT, \-\-format=FORMAT [ATA only] Selects the output format of the attributes: .Sp .I old \- Old smartctl format. This is the default unless the \*(Aq\-x\*(Aq option is specified. .Sp .I brief \- New format which fits into 80 columns (except in some rare cases). This format also decodes four additional attribute flags. This is the default if the \*(Aq\-x\*(Aq option is specified. .Sp .I hex,id \- Print all attribute IDs as hexadecimal numbers. .Sp .I hex,val \- Print all normalized values as hexadecimal numbers. .Sp .I hex \- Same as \*(Aq\-f hex,id \-f hex,val\*(Aq. .TP .B \-l TYPE, \-\-log=TYPE Prints various device logs. The valid arguments to this option are: .Sp .I error \- [ATA] prints the Summary SMART error log. SMART disks maintain a log of the most recent five non-trivial errors. For each of these errors, the disk power-on lifetime at which the error occurred is recorded, as is 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: .Vb 5 \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 .Ve 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 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. .Sp The ATA Specification (ATA ACS-2 Revision 7, Section A.7.1) says: \fB"Error log data structures shall include, but are not limited to, Uncorrectable errors, ID Not Found errors for which the LBA requested was valid, servo errors, and write fault errors. Error log data structures shall not include errors attributed to the receipt of faulty commands."\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. .Sp 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. .Sp 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. .Sp .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. .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I error[,NUM] \- [NVMe] prints the NVMe Error Information log. Only the 16 most recent log entries are printed by default. This number can be changed by the optional parameter NUM. The maximum number of log entries is vendor specific (in the range from 1 to 256 inclusive). .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I xerror[,NUM][,error] \- [ATA only] prints the Extended Comprehensive SMART error log (General Purpose Log address 0x03). Unlike the Summary SMART error log (see \*(Aq\-l error\*(Aq above), it provides sufficient space to log the contents of the 48-bit LBA register set introduced with ATA-6. It also supports logs with more than one sector. Each sector holds up to 4 log entries. The actual number of log sectors is vendor specific. .Sp Only the 8 most recent error log entries are printed by default. This number can be changed by the optional parameter NUM. .Sp If \*(Aq,error\*(Aq is appended and the Extended Comprehensive SMART error log is not supported, the Summary SMART self-test log is printed. .Sp Please note that recent drives may report errors only in the Extended Comprehensive SMART error log. The Summary SMART error log may be reported as supported but is always empty then. .Sp .I selftest \- [ATA] prints the SMART self-test log. The disk maintains a self-test log showing the results of the self tests, which can be run using the \*(Aq\-t\*(Aq 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. [Note: this time stamp wraps after 2^16 hours, or 2730 days and 16 hours, or about 7.5 years.] If any errors were detected, the Logical Block Address (LBA) of the first error is printed in decimal notation. .Sp .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. If provided, the SCSI Sense Key (SK), Additional Sense Code (ASC) and Additional Sense Code Qualifier (ASCQ) are also printed. The self tests can be run using the \*(Aq\-t\*(Aq option described below (using the ATA test terminology). .Sp .I xselftest[,NUM][,selftest] \- [ATA only] prints the Extended SMART self-test log (General Purpose Log address 0x07). Unlike the SMART self-test log (see \*(Aq\-l selftest\*(Aq above), it supports 48-bit LBA and logs with more than one sector. Each sector holds up to 19 log entries. The actual number of log sectors is vendor specific. .Sp Only the 25 most recent log entries are printed by default. This number can be changed by the optional parameter NUM. .Sp If \*(Aq,selftest\*(Aq is appended and the Extended SMART self-test log is not supported, the old SMART self-test log is printed. .Sp .I selective \- [ATA only] Please see the \*(Aq\-t select\*(Aq 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 \*(Aq\-t afterselect\*(Aq option) and the time delay before restarting this read-scan if it is interrupted (see \*(Aq\-t pending\*(Aq option). .Sp .I directory[,gs] \- [ATA only] if the device supports the General Purpose Logging feature set (ATA-6 and above) then this prints the Log Directory (the log at 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. If your version of smartctl supports 48-bit ATA commands, both the General Purpose Log (GPL) and SMART Log (SL) directories are printed in one combined table. The output can be restricted to the GPL directory or SL directory by \*(Aq\-l directory,q\*(Aq or \*(Aq\-l directory,s\*(Aq respectively. .Sp .I background \- [SCSI only] the background scan results log outputs information derived from Background Media Scans (BMS) done after power up and/or periodically (e.g.\& every 24 hours) on recent SCSI disks. If supported, the BMS status is output first, indicating whether a background scan is currently underway (and if so a progress percentage), the amount of time the disk has been powered up and the number of scans already completed. Then there is a header and a line for each background scan "event". These will typically be either recovered or unrecoverable errors. That latter group may need some attention. There is a description of the background scan mechanism in section 4.18 of SBC-3 revision 6 (see www.t10.org ). .Sp .I scttemp, scttempsts, scttemphist \- [ATA only] prints the disk temperature information provided by the SMART Command Transport (SCT) commands. The option \*(Aqscttempsts\*(Aq prints current temperature and temperature ranges returned by the SCT Status command, \*(Aqscttemphist\*(Aq prints temperature limits and the temperature history table returned by the SCT Data Table command, and \*(Aqscttemp\*(Aq prints both. The temperature values are preserved across power cycles. The logging interval can be configured with the \*(Aq\-l scttempint,N[,p]\*(Aq option, see below. The SCT commands were introduced in ATA8-ACS and were also supported by many ATA-7 disks. .Sp .I scttempint,N[,p] \- [ATA only] clears the SCT temperature history table and sets the time interval for temperature logging to N minutes. If \*(Aq,p\*(Aq is specified, the setting is preserved across power cycles. Otherwise, the setting is volatile and will be reverted to the last non-volatile setting by the next hard reset. The default interval is vendor specific, typical values are 1, 2, or 5 minutes. .Sp .I scterc[,READTIME,WRITETIME] \- [ATA only] prints values and descriptions of the SCT Error Recovery Control settings. These are equivalent to TLER (as used by Western Digital), CCTL (as used by Samsung and Hitachi/HGST) and ERC (as used by Seagate). READTIME and WRITETIME arguments (deciseconds) set the specified values. Values of 0 disable the feature, other values less than 65 are probably not supported. For RAID configurations, this is typically set to 70,70 deciseconds. .Sp .I devstat[,PAGE] \- [ATA only] prints values and descriptions of the ATA Device Statistics log pages (General Purpose Log address 0x04). If no PAGE number is specified, entries from all supported pages are printed. If PAGE 0 is specified, the list of supported pages is printed. Device Statistics was introduced in ACS-2 and is only supported by some recent devices. .Sp .I defects[,NUM] \- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints LBA and hours values from the ATA Pending Defects log (General Purpose Log address 0x0c). Only the 31 entries from first log page are printed by default. This number can be changed by the optional parameter NUM. The size of the log and the order of the entries are vendor specific. The Pending Defects log was introduced in ACS-4 Revision 01 (Mar 2014). .Sp .I sataphy[,reset] \- [SATA only] prints values and descriptions of the SATA Phy Event Counters (General Purpose Log address 0x11). If \*(Aq\-l sataphy,reset\*(Aq is specified, all counters are reset after reading the values. This also works for SATA devices with Packet interface like CD/DVD drives. .Sp .I sasphy[,reset] \- [SAS (SCSI) only] prints values and descriptions of the SAS (SSP) Protocol Specific log page (log page 0x18). If \*(Aq\-l sasphy,reset\*(Aq is specified, all counters are reset after reading the values. .Sp .I gplog,ADDR[,FIRST[\-LAST|+SIZE]] \- [ATA only] prints a hex dump of any log accessible via General Purpose Logging (GPL) feature. The log address ADDR is the hex address listed in the log directory (see \*(Aq\-l directory\*(Aq above). The range of log sectors (pages) can be specified by decimal values FIRST\-LAST or FIRST+SIZE. FIRST defaults to 0, SIZE defaults to 1. LAST can be set to \*(Aqmax\*(Aq to specify the last page of the log. .Sp .I smartlog,ADDR[,FIRST[\-LAST|+SIZE]] \- [ATA only] prints a hex dump of any log accessible via SMART Read Log command. See \*(Aq\-l gplog,...\*(Aq above for parameter syntax. .Sp For example, all these commands: .Vb 3 smartctl \-l gplog,0x80,10\-15 /dev/sda smartctl \-l gplog,0x80,10+6 /dev/sda smartctl \-l smartlog,0x80,10\-15 /dev/sda .Ve print pages 10\(en15 of log 0x80 (first host vendor specific log). .Sp The hex dump format is compatible with the \*(Aqxxd \-r\*(Aq command. This command: .Vb 1 smartctl \-l gplog,0x11 /dev/sda | grep ^0 | xxd \-r >log.bin .Ve writes a binary representation of the one sector log 0x11 (SATA Phy Event Counters) to file log.bin. .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I nvmelog,PAGE,SIZE \- [NVMe only] prints a hex dump of the first SIZE bytes from the NVMe log with identifier PAGE. PAGE is a hexadecimal number in the range from 0x1 to 0xff. SIZE is a hexadecimal number in the range from 0x4 to 0x4000 (16 KiB). \fBWARNING: Do not specify the identifier of an unknown log page. Reading a log page may have undesirable side effects.\fP .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I ssd \- [ATA] prints the Solid State Device Statistics log page. This has the same effect as \*(Aq\-l devstat,7\*(Aq, see above. .Sp .I ssd \- [SCSI] prints the Solid State Media percentage used endurance indicator. A value of 0 indicates as new condition while 100 indicates the device is at the end of its lifetime as projected by the manufacturer. The value may reach 255. .TP .B \-v ID,FORMAT[:BYTEORDER][,NAME], \-\-vendorattribute=ID,FORMAT... [ATA only] Sets a vendor-specific raw value print FORMAT, an optional BYTEORDER and an optional NAME for Attribute ID. This option may be used multiple times. .Sp The Attribute ID can be in the range 1 to 255. If \*(AqN\*(Aq is specified as ID, the settings for all Attributes are changed. .Sp The optional BYTEORDER consists of 1 to 8 characters from the set \*(Aq012345rvwz\*(Aq. The characters \*(Aq0\*(Aq to \*(Aq5\*(Aq select the byte 0 to 5 from the 48-bit raw value, \*(Aqr\*(Aq selects the reserved byte of the attribute data block, \*(Aqv\*(Aq selects the normalized value, \*(Aqw\*(Aq selects the worst value and \*(Aqz\*(Aq inserts a zero byte. The default BYTEORDER is \*(Aq543210\*(Aq for all 48-bit formats, \*(Aqr543210\*(Aq for the 54-bit formats, and \*(Aq543210wv\*(Aq for the 64-bit formats. For example, \*(Aq\-v 5,raw48:012345\*(Aq prints the raw value of attribute 5 with big endian instead of little endian byte ordering. .Sp The NAME is a string of letters, digits and underscore. Its length should not exceed 23 characters. The \*(Aq\-P showall\*(Aq option reports an error if this is the case. .Sp .I \-v help \- Prints (to STDOUT) a list of all valid arguments to this option, then exits. .Sp Valid arguments for FORMAT are: .Sp .I raw8 \- Print the Raw value as six 8-bit unsigned base-10 integers. This may be useful for decoding the meaning of the Raw value. .Sp .I raw16 \- Print the Raw value as three 16-bit unsigned base-10 integers. This may be useful for decoding the meaning of the Raw value. .Sp .I raw48 \- Print the Raw value as a 48-bit unsigned base-10 integer. This is the default for most attributes. .Sp .I hex48 \- Print the Raw value as a 12 digit hexadecimal number. This may be useful for decoding the meaning of the Raw value. .Sp .I raw56 \- Print the Raw value as a 54-bit unsigned base-10 integer. This includes the reserved byte which follows the 48-bit raw value. .Sp .I hex56 \- Print the Raw value as a 14 digit hexadecimal number. This includes the reserved byte which follows the 48-bit raw value. .Sp .I raw64 \- Print the Raw value as a 64-bit unsigned base-10 integer. This includes two bytes from the normalized and worst attribute value. This raw format is used by some SSD devices with Indilinx controller. .Sp .I hex64 \- Print the Raw value as a 16 digit hexadecimal number. This includes two bytes from the normalized and worst attribute value. This raw format is used by some SSD devices with Indilinx controller. .Sp .I min2hour \- Raw Attribute is power-on time in minutes. Its raw value will be displayed in the form "Xh+Ym". Here X is hours, and Y is minutes in the range 0\(en59 inclusive. Y is always printed with two digits, for example "06" or "31" or "00". .Sp .I sec2hour \- Raw Attribute is power-on time in seconds. Its raw value will be displayed in the form "Xh+Ym+Zs". Here X is hours, Y is minutes in the range 0\(en59 inclusive, and Z is seconds in the range 0\(en59 inclusive. Y and Z are always printed with two digits, for example "06" or "31" or "00". .Sp .I halfmin2hour \- Raw Attribute is power-on time, measured in units of 30 seconds. This format is used by some Samsung disks. Its raw value will be displayed in the form "Xh+Ym". Here X is hours, and Y is minutes in the range 0\(en59 inclusive. Y is always printed with two digits, for example "06" or "31" or "00". .Sp .I msec24hour32 \- Raw Attribute is power-on time measured in 32-bit hours and 24-bit milliseconds since last hour update. It will be displayed in the form "Xh+Ym+Z.Ms". Here X is hours, Y is minutes, Z is seconds and M is milliseconds. .Sp .I tempminmax \- Raw Attribute is the disk temperature in Celsius. Info about Min/Max temperature is printed if available. This is the default for Attributes 190 and 194. The recording interval (lifetime, last power cycle, last soft reset) of the min/max values is device specific. .Sp .I temp10x \- Raw Attribute is ten times the disk temperature in Celsius. .Sp .I raw16(raw16) \- Print the raw attribute as a 16-bit value and two optional 16-bit values if these words are nonzero. This is the default for Attributes 5 and 196. .Sp .I raw16(avg16) \- Raw attribute is spin-up time. It is printed as a 16-bit value and an optional "Average" 16-bit value if the word is nonzero. This is the default for Attribute 3. .Sp .I raw24(raw8) \- Print the raw attribute as a 24-bit value and three optional 8-bit values if these bytes are nonzero. This is the default for Attribute 9. .Sp .I raw24/raw24 \- Raw Attribute contains two 24-bit values. The first is the number of load cycles. The second is the number of unload cycles. The difference between these two values is the number of times that the drive was unexpectedly powered off (also called an emergency unload). As a rule of thumb, the mechanical stress created by one emergency unload is equivalent to that created by one hundred normal unloads. .Sp .I raw24/raw32 \- Raw attribute is an error rate which consists of a 24-bit error count and a 32-bit total count. .Sp The following old arguments to \*(Aq\-v\*(Aq are also still valid: .Sp .I 9,minutes \- same as: .I 9,min2hour,Power_On_Minutes. .Sp .I 9,seconds \- same as: .I 9,sec2hour,Power_On_Seconds. .Sp .I 9,halfminutes \- same as: .I 9,halfmin2hour,Power_On_Half_Minutes. .Sp .I 9,temp \- same as: .I 9,tempminmax,Temperature_Celsius. .Sp .I 192,emergencyretractcyclect \- same as: .I 192,raw48,Emerg_Retract_Cycle_Ct .Sp .I 193,loadunload \- same as: .I 193,raw24/raw24. .Sp .I 194,10xCelsius \- same as: .I 194,temp10x,Temperature_Celsius_x10. .Sp .I 194,unknown \- same as: .I 194,raw48,Unknown_Attribute. .Sp .I 197,increasing \- same as: .I 197,raw48,Total_Pending_Sectors. Also means that Attribute number 197 (Current Pending Sector Count) is not reset if uncorrectable sectors are reallocated (see \fBsmartd.conf\fP(5) man page). .Sp .I 198,increasing \- same as: .I 198,raw48,Total_Offl_Uncorrectabl. Also means that Attribute number 198 (Offline Uncorrectable Sector Count) is not reset if uncorrectable sectors are reallocated (see \fBsmartd.conf\fP(5) man page). .Sp .I 198,offlinescanuncsectorct \- same as: .I 198,raw48,Offline_Scan_UNC_SectCt. .Sp .I 200,writeerrorcount \- same as: .I 200,raw48,Write_Error_Count. .Sp .I 201,detectedtacount \- same as: .I 201,raw48,Detected_TA_Count. .Sp .I 220,temp \- same as: .I 220,tempminmax,Temperature_Celsius. .TP .B \-F TYPE, \-\-firmwarebug=TYPE [ATA only] Modifies the behavior of \fBsmartctl\fP to compensate for some known and understood device firmware or driver bug. This option may be used multiple times. The valid arguments are: .Sp .I none \- Assume that the device firmware obeys the ATA specifications. This is the default, unless the device has presets for \*(Aq\-F\*(Aq in the drive database. Using this option on the command line will override any preset values. .Sp .I nologdir \- Suppresses read attempts of SMART or GP Log Directory. Support for all standard logs is assumed without an actual check. Some Intel SSDs may freeze if log address 0 is read. .Sp .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. .Sp .I samsung2 \- In some Samsung disks the number of ATA errors reported is byte swapped. Enabling this option tells \fBsmartctl\fP to evaluate this quantity in byte-reversed order. An indication that your Samsung disk needs this option is that the self-test log is printed correctly, but there are a 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). .Sp .I samsung3 \- Some Samsung disks (at least SP2514N with Firmware VF100-37) report a self-test still in progress with 0% remaining when the test was already completed. Enabling this option modifies the output of the self-test execution status (see options \*(Aq\-c\*(Aq or \*(Aq\-a\*(Aq above) accordingly. .Sp .I xerrorlba \- Fixes LBA byte ordering in Extended Comprehensive SMART error log. Some disks use little endian byte ordering instead of ATA register ordering to specify the LBA addresses in the log entries. .Sp .I swapid \- Fixes byte swapped ATA identify strings (device name, serial number, firmware version) returned by some buggy device drivers. .TP .B \-P TYPE, \-\-presets=TYPE [ATA only] 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. .Sp 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. .Sp The valid arguments to this option are: .Sp .I use \- if a drive is recognized, then use the stored presets for it. This is the default. Note that presets will NOT override additional Attribute interpretation (\*(Aq\-v N,something\*(Aq) command-line options or explicit \*(Aq\-F\*(Aq command-line options.. .Sp .I ignore \- do not use presets. .Sp .I show \- show if the drive is recognized in the database, and if so, its presets, then exit. .Sp .I showall \- list all recognized drives, and the presets that are set for them, then exit. This also checks the drive database regular expressions and settings for syntax errors. .Sp The \*(Aq\-P showall\*(Aq option takes up to two optional arguments to match a specific drive type and firmware version. The command: .Vb 1 smartctl \-P showall .Ve lists all entries, the command: .Vb 1 smartctl \-P showall \*(AqMODEL\*(Aq .Ve lists all entries matching MODEL, and the command: .Vb 1 smartctl \-P showall \*(AqMODEL\*(Aq \*(AqFIRMWARE\*(Aq .Ve lists all entries for this MODEL and a specific FIRMWARE version. .TP .B \-B [+]FILE, \-\-drivedb=[+]FILE [ATA only] Read the drive database from FILE. The new database replaces the built in database by default. If \*(Aq+\*(Aq is specified, then the new entries prepend the built in entries. .Sp Optional entries are read from the file .\" %IF NOT OS Windows \fB/usr/local/etc/smart_drivedb.h\fP .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: \fBEXEDIR/drivedb-add.h\fP) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! \fBEXEDIR/drivedb-add.h\fP. .\" %ENDIF OS Windows .\" %IF ENABLE_DRIVEDB if this option is not specified. .Sp If .\" %IF NOT OS Windows \fB/usr/local/share/smartmontools/drivedb.h\fP .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: \fBEXEDIR/drivedb.h\fP) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! \fBEXEDIR/drivedb.h\fP .\" %ENDIF OS Windows is present, the contents of this file is used instead of the built in table. .\" %IF ENABLE_UPDATE_SMART_DRIVEDB .Sp Run .\" %IF NOT OS Windows \fB/usr/local/sbin/update-smart-drivedb\fP .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: \fBEXEDIR/update-smart-drivedb.exe\fP) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! \fBEXEDIR/update-smart-drivedb.exe\fP .\" %ENDIF OS Windows to update this file from the smartmontools SVN repository. .\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB .\" %ENDIF ENABLE_DRIVEDB .Sp The database files use the same C/C++ syntax that is used to initialize the built in database array. C/C++ style comments are allowed. Example: .Sp .Vb 8 /* Full entry: */ { "Model family", // Info about model family/series. "MODEL1.*REGEX", // Regular expression to match model of device. "VERSION.*REGEX", // Regular expression to match firmware version(s). "Some warning", // Warning message. "\-v 9,minutes" // String of preset \-v and \-F options. }, /* Minimal entry: */ { "", // No model family/series info. "MODEL2.*REGEX", // Regular expression to match model of device. "", // All firmware versions. "", // No warning. "" // No options preset. }, /* USB ID entry: */ { "USB: Device; Bridge", // Info about USB device and bridge name. "0x1234:0xabcd", // Regular expression to match vendor:product ID. "0x0101", // Regular expression to match bcdDevice. "", // Not used. "\-d sat" // String with device type option. }, /* ... */ .Ve .Sp .TP .B SMART RUN/ABORT OFFLINE TEST AND self-test OPTIONS: .TP .B \-t TEST, \-\-test=TEST Executes TEST immediately. The \*(Aq\-C\*(Aq 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. .Sp All \*(Aq\-t TEST\*(Aq commands can be given during normal system operation unless captive mode (\*(Aq\-C\*(Aq option) is used. A running self-test can, however, degrade performance of the drive. Frequent I/O requests from the operating system increase the duration of a test. These impacts may vary from device to device. .Sp If a test failure occurs then the device may discontinue the testing and report the result immediately. .Sp [ATA] Note that the ATA command SMART EXECUTE OFF-LINE IMMEDIATE (the command to start a test) was declared obsolete in ATA ACS-4 Revision 10 (Nov 2015). .Sp The valid arguments to this option are: .Sp .I offline \- [ATA] 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 \*(Aq\-l error\*(Aq option. .Sp If the \*(Aq\-c\*(Aq 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 \*(Aq\-c\*(Aq option to \fBsmartctl\fP. If the \*(Aq\-c\*(Aq 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 \*(Aq\-c\*(Aq, as it will abort the test. .Sp .I offline \- [SCSI] runs the default self test in foreground. No entry is placed in the self test log. .Sp .I short \- [ATA] runs SMART Short Self Test (usually under ten minutes). This command can be given during normal system operation (unless run in captive mode \- see the \*(Aq\-C\*(Aq 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 \*(Aq\-l selftest\*(Aq 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 \*(Aq\-c\*(Aq option to monitor progress. .Sp .I short \- [SCSI] runs the "Background short" self-test. .Sp .I long \- [ATA] runs SMART Extended Self Test (tens of minutes to several hours). 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 \*(Aq\-C\*(Aq option below). .Sp .I long \- [SCSI] runs the "Background long" self-test. .Sp .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 \*(Aq\-C\*(Aq option below). .Sp .I select,N\-M, select,N+SIZE \- [ATA only] 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. The range can also be specified as N+SIZE. A span at the end of a disk can be specified by N\-\fBmax\fP. .Sp For example the commands: .Vb 2 smartctl \-t select,10\-20 /dev/sda smartctl \-t select,10+11 /dev/sda .Ve both runs a self test on one span consisting of LBAs ten to twenty (inclusive). The command: .Vb 1 smartctl \-t select,100000000\-max /dev/sda .Ve run a self test from LBA 100000000 up to the end of the disk. The \*(Aq\-t\*(Aq option can be given up to five times, to test up to five spans. For example the command: .Vb 1 smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/sda .Ve 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: .Vb 1 smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/sda .Ve 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 \*(Aq\-l selftest\*(Aq option to smartctl. .Sp 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). .Sp Selective self-tests can be run during normal system operation (unless done in captive mode \- see the \*(Aq\-C\*(Aq option below). .Sp The following variants of the selective self-test command use spans based on the ranges from past tests already stored on the disk: .Sp .I select,redo[+SIZE] \- [ATA only] redo the last SMART Selective Self Test using the same LBA range. The starting LBA is identical to the LBA used by last test, same for ending LBA unless a new span size is specified by optional +SIZE argument. .Sp For example the commands: .Vb 3 smartctl \-t select,10\-20 /dev/sda smartctl \-t select,redo /dev/sda smartctl \-t select,redo+20 /dev/sda .Ve have the same effect as: .Vb 3 smartctl \-t select,10\-20 /dev/sda smartctl \-t select,10\-20 /dev/sda smartctl \-t select,10\-29 /dev/sda .Ve .Sp .I select,next[+SIZE] \- [ATA only] runs a SMART Selective Self Test on the LBA range which follows the range of the last test. The starting LBA is set to (ending LBA +1) of the last test. A new span size may be specified by the optional +SIZE argument. .Sp For example the commands: .Vb 3 smartctl \-t select,0\-999 /dev/sda smartctl \-t select,next /dev/sda smartctl \-t select,next+2000 /dev/sda .Ve have the same effect as: .Vb 3 smartctl \-t select,0\-999 /dev/sda smartctl \-t select,1000\-1999 /dev/sda smartctl \-t select,2000\-3999 /dev/sda .Ve .Sp If the last test ended at the last LBA of the disk, the new range starts at LBA 0. The span size of the last span of a disk is adjusted such that the total number of spans to check the full disk will not be changed by future uses of \*(Aq\-t select,next\*(Aq. .Sp .I select,cont[+SIZE] \- [ATA only] performs a \*(Aqredo\*(Aq (above) if the self test status reports that the last test was aborted by the host. Otherwise it run the \*(Aqnext\*(Aq (above) test. .Sp .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. .Sp .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. .Sp .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. .Sp .I vendor,N \- [ATA only] issues the ATA command SMART EXECUTE OFF-LINE IMMEDIATE with subcommand N in LBA LOW register. The subcommand is specified as a hex value in the range 0x00 to 0xff. Subcommands 0x40\(en0x7e and 0x90\(en0xff are reserved for vendor specific use, see table 61 of T13/1699-D Revision 6a (ATA8-ACS). Note that the subcommands 0x00\(en0x04, 0x7f, 0x81\(en0x84 are supported by other smartctl options (e.g.\& 0x01: \*(Aq\-t short\*(Aq, 0x7f: \*(Aq\-X\*(Aq, 0x82: \*(Aq\-C \-t long\*(Aq). .Sp \fBWARNING: Only run subcommands documented by the vendor of the device.\fP .Sp Example for some Intel SSDs only: The subcommand 0x40 (\*(Aq\-t vendor,0x40\*(Aq) clears the timed workload related SMART attributes (226, 227, 228). Note that the raw values of these attributes are held at 65535 (0xffff) until the workload timer reaches 60 minutes. .Sp .I force \- start new self-test even if another test is already running. By default a running self-test will not be interrupted to begin another test. .TP .B \-C, \-\-captive [ATA] Runs self-tests in captive mode. This has no effect with \*(Aq\-t offline\*(Aq or if the \*(Aq\-t\*(Aq option is not used. .Sp \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 .Sp [SCSI] Runs the self-test in "Foreground" mode. .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. .Sp .SH ATA, SCSI command sets and SAT In the past there has been a clear distinction between storage devices that used the ATA and SCSI command sets. This distinction was often reflected in their device naming and hardware. Now various SCSI transports (e.g.\& SAS, FC and iSCSI) can interconnect to both SCSI disks (e.g.\& FC and SAS) and ATA disks (especially SATA). USB and IEEE 1394 storage devices use the SCSI command set externally but almost always contain ATA or SATA disks (or flash). The storage subsystems in some operating systems have started to remove the distinction between ATA and SCSI in their device naming policies. .PP 99% of operations that an OS performs on a disk involve the SCSI INQUIRY, READ CAPACITY, READ and WRITE commands, or their ATA equivalents. Since the SCSI commands are slightly more general than their ATA equivalents, many OSes are generating SCSI commands (mainly READ and WRITE) and letting a lower level translate them to their ATA equivalents as the need arises. An important note here is that "lower level" may be in external equipment and hence outside the control of an OS. .PP SCSI to ATA Translation (SAT) is a standard (ANSI INCITS 431-2007) that specifies how this translation is done. For the other 1% of operations that an OS performs on a disk, SAT provides two options. First is an optional ATA PASS-THROUGH SCSI command (there are two variants). The second is a translation from the closest SCSI command. Most current interest is in the "pass-through" option. .PP The relevance to smartmontools (and hence smartctl) is that its interactions with disks fall solidly into the "1%" category. So even if the OS can happily treat (and name) a disk as "SCSI", smartmontools needs to detect the native command set and act accordingly. As more storage manufacturers (including external SATA drives) comply with SAT, smartmontools is able to automatically distinguish the native command set of the device. In some cases the \*(Aq\-d sat\*(Aq option is needed on the command line. .PP There are also virtual disks which typically have no useful information to convey to smartmontools, but could conceivably in the future. An example of a virtual disk is the OS's view of a RAID 1 box. There are most likely two SATA disks inside a RAID 1 box. Addressing those SATA disks from a distant OS is a challenge for smartmontools. Another approach is running a tool like smartmontools inside the RAID 1 box (e.g. a Network Attached Storage (NAS) box) and fetching the logs via a browser. .Sp .SH EXAMPLES .B smartctl \-a /dev/sda .br Print a large amount of SMART information for drive /dev/sda. .PP .B smartctl \-s off /dev/sdd .br Disable SMART monitoring and data log collection on drive /dev/sdd. .PP .B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/sda .br Enable SMART on drive /dev/sda, 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 .B smartctl \-t long /dev/sdc .br Begin an extended self-test of drive /dev/sdc. You can issue this command on a running system. The results can be seen in the self-test log visible with the \*(Aq\-l selftest\*(Aq option after it has completed. .PP .B smartctl \-s on \-t offline /dev/sda .br Enable SMART on the disk, and begin an immediate offline test of drive /dev/sda. You can issue this command on a running system. The results are only used to update the SMART Attributes, visible with the \*(Aq\-A\*(Aq option. If any device errors occur, they are logged to the SMART error log, which can be seen with the \*(Aq\-l error\*(Aq option. .PP .B smartctl \-A \-v 9,minutes /dev/sda .br Shows the vendor Attributes, when the disk stores its power-on time internally in minutes rather than hours. .PP .B smartctl \-q errorsonly \-H \-l selftest /dev/sda .br Produces output only if the device returns failing SMART status, or if some of the logged self-tests ended with errors. .PP .B smartctl \-q silent \-a /dev/sda .br Examine all SMART data for device /dev/sda, 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 .B smartctl \-a \-d 3ware,0 /dev/twl0 .br Examine all SMART data for the first SATA (not SAS) disk connected to a 3ware RAID 9750 controller card. .PP .B smartctl \-t long \-d areca,4 /dev/sg2 .br Start a long self-test on the fourth SATA disk connected to an Areca RAID controller addressed by /dev/sg2. .PP .B smartctl \-a \-d hpt,1/3 /dev/sda (under Linux) .br .B smartctl \-a \-d hpt,1/3 /dev/hptrr (under FreeBSD) .br Examine all SMART data for the (S)ATA disk directly connected to the third channel of the first HighPoint RocketRAID controller card. .PP .B smartctl \-t short \-d hpt,1/1/2 /dev/sda (under Linux) .br .B smartctl \-t short \-d hpt,1/1/2 /dev/hptrr (under FreeBSD) .br Start a short self-test on the (S)ATA disk connected to second pmport on the first channel of the first HighPoint RocketRAID controller card. .PP .B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/sda .br 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 .B smartctl \-a \-d cciss,0 /dev/cciss/c0d0 .br Examine all SMART data for the first SCSI disk connected to a cciss RAID controller card. .Sp .SH EXIT STATUS The exit statuses of \fBsmartctl\fP are defined by a bitmask. If all is well with the disk, the exit status (return value) 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 exit status 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, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode (see \*(Aq\-n\*(Aq option above). .TP .B Bit 2: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure (see \*(Aq\-b\*(Aq option above). .TP .B Bit 3: SMART status check returned "DISK FAILING". .TP .B Bit 4: 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. [ATA only] Failed self-tests outdated by a newer successful extended self-test are ignored. .PP To test within the shell for whether or not the different bits are turned on or off, you can use the following type of construction (which should work with any POSIX compatible shell): .br .B smartstat=$(($? & 8)) .br 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 This shell script prints all status bits: .Vb 5 val=$?; mask=1 for i in 0 1 2 3 4 5 6 7; do echo "Bit $i: $(((val & mask) && 1))" mask=$((mask << 1)) done .Ve .Sp .\" %IF NOT OS Windows .SH FILES .TP .B /usr/local/sbin/smartctl full path of this executable. .\" %IF ENABLE_DRIVEDB .TP .B /usr/local/share/smartmontools/drivedb.h drive database (see \*(Aq\-B\*(Aq option). .\" %ENDIF ENABLE_DRIVEDB .TP .B /usr/local/etc/smart_drivedb.h optional local drive database (see \*(Aq\-B\*(Aq option). .Sp .\" %ENDIF NOT OS Windows .SH AUTHORS \fBBruce Allen\fP (project initiator), .br \fBChristian Franke\fP (project manager, Windows port and all sort of things), .br \fBDouglas Gilbert\fP (SCSI subsystem), .br \fBVolker Kuhlmann\fP (moderator of support and database mailing list), .br \fBGabriele Pohl\fP (wiki & development team support), .br \fBAlex Samorukov\fP (FreeBSD port and more, new Trac wiki). .PP Many other individuals have made contributions and corrections, see AUTHORS, ChangeLog and repository files. .PP The first smartmontools code was derived from the smartsuite package, written by Michael Cornwell and Andre Hedrick. .Sp .SH REPORTING BUGS To submit a bug report, create a ticket in smartmontools wiki: .br <\fBhttps://www.smartmontools.org/\fP>. .br Alternatively send the info to the smartmontools support mailing list: .br <\fBhttps://listi.jpberlin.de/mailman/listinfo/smartmontools-support\fB>. .Sp .SH SEE ALSO \fBsmartd\fP(8). .\" %IF ENABLE_UPDATE_SMART_DRIVEDB .br \fBupdate-smart-drivedb\fP(8). .\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB .Sp .SH REFERENCES Please see the following web site for more info: <\fBhttps://www.smartmontools.org/\fP> .PP An introductory article about smartmontools is \fIMonitoring Hard Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004, pages 74\(en77. See <\fBhttps://www.linuxjournal.com/article/6983\fP>. .PP 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 \*(AqAT Attachment with Packet Interface-7\*(Aq (ATA/ATAPI-7) specification Revision 4b. This documents the SMART functionality which the \fBsmartmontools\fP utilities provide access to. .PP 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. .PP Links to these and other documents may be found on the Links page of the \fBsmartmontools\fP Wiki at <\fBhttps://www.smartmontools.org/wiki/Links\fP>. .Sp .SH PACKAGE VERSION CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .br $Id: smartctl.8.in 4882 2018-12-29 21:26:45Z chrfranke $ smartmontools-7.0/smartctl.cpp0000644000175000010010000016101613411230000013531 00000000000000/* * smartctl.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #if defined(__FreeBSD__) #include #endif #include "atacmds.h" #include "dev_interface.h" #include "ataprint.h" #include "knowndrives.h" #include "scsicmds.h" #include "scsiprint.h" #include "nvmeprint.h" #include "smartctl.h" #include "utility.h" #include "svnversion.h" const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4871 2018-12-27 20:03:12Z chrfranke $" CONFIG_H_CVSID SMARTCTL_H_CVSID; // Globals to control printing bool printing_is_switchable = false; bool printing_is_off = false; // Control JSON output json jglb; static bool print_as_json = false; static json::print_options print_as_json_options; static bool print_as_json_output = false; static bool print_as_json_impl = false; static bool print_as_json_unimpl = false; static void printslogan() { jout("%s\n", format_version_info("smartctl").c_str()); } static void UsageSummary() { pout("\nUse smartctl -h to get a usage summary\n\n"); return; } static void js_initialize(int argc, char **argv, bool verbose) { if (jglb.is_enabled()) return; jglb.enable(); if (verbose) jglb.set_verbose(); // Major.minor version of JSON format jglb["json_format_version"][0] = 1; jglb["json_format_version"][1] = 0; // Smartctl version info json::ref jref = jglb["smartctl"]; int ver[3] = { 0, 0, 0 }; sscanf(PACKAGE_VERSION, "%d.%d.%d", ver, ver+1, ver+2); jref["version"][0] = ver[0]; jref["version"][1] = ver[1]; if (ver[2] > 0) jref["version"][2] = ver[2]; #ifdef SMARTMONTOOLS_SVN_REV jref["svn_revision"] = SMARTMONTOOLS_SVN_REV; #endif jref["platform_info"] = smi()->get_os_version_str(); #ifdef BUILD_INFO jref["build_info"] = BUILD_INFO; #endif jref["argv"][0] = "smartctl"; for (int i = 1; i < argc; i++) jref["argv"][i] = argv[i]; } static std::string getvalidarglist(int opt); /* void prints help information for command syntax */ static void Usage() { pout("Usage: smartctl [options] device\n\n"); pout( "============================================ SHOW INFORMATION OPTIONS =====\n\n" " -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" " --identify[=[w][nvb]]\n" " Show words and bits from IDENTIFY DEVICE data (ATA)\n\n" " -g NAME, --get=NAME\n" " Get device setting: all, aam, apm, dsn, lookahead, security,\n" " wcache, rcache, wcreorder, wcache-sct\n\n" " -a, --all\n" " Show all SMART information for device\n\n" " -x, --xall\n" " Show all information for device\n\n" " --scan\n" " Scan for devices\n\n" " --scan-open\n" " Scan for devices and try to open each device\n\n" ); pout( "================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n" " -j, --json[=[cgiosuv]]\n" " Print output in JSON format\n\n" " -q TYPE, --quietmode=TYPE (ATA)\n" " Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n" " -d TYPE, --device=TYPE\n" " Specify device type to one of:\n" " %s\n\n" // TODO: fold this string " -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" " -n MODE[,STATUS], --nocheck=MODE[,STATUS] (ATA)\n" " No check if: never, sleep, standby, idle (see man page)\n\n", getvalidarglist('d').c_str()); // TODO: Use this function also for other options ? pout( "============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n" " -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" " -s NAME[,VALUE], --set=NAME[,VALUE]\n" " Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n" " dsn,[on|off], lookahead,[on|off], security-freeze,\n" " standby,[N|off|now], wcache,[on|off], rcache,[on|off],\n" " wcreorder,[on|off[,p]], wcache-sct,[ata|on|off[,p]]\n\n" ); pout( "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n" " -H, --health\n" " Show device SMART health status\n\n" " -c, --capabilities (ATA, NVMe)\n" " Show device SMART capabilities\n\n" " -A, --attributes\n" " Show device SMART vendor-specific Attributes and values\n\n" " -f FORMAT, --format=FORMAT (ATA)\n" " Set output format for attributes: old, brief, hex[,id|val]\n\n" " -l TYPE, --log=TYPE\n" " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" " xerror[,N][,error], xselftest[,N][,selftest], background,\n" " sasphy[,reset], sataphy[,reset], scttemp[sts,hist],\n" " scttempint,N[,p], scterc[,N,M], devstat[,N], defects[,N], ssd,\n" " gplog,N[,RANGE], smartlog,N[,RANGE], nvmelog,N,SIZE\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:\n" " %s, swapid\n\n" " -P TYPE, --presets=TYPE (ATA)\n" " Drive-specific presets: use, ignore, show, showall\n\n" " -B [+]FILE, --drivedb=[+]FILE (ATA)\n" " Read and replace [add] drive database from FILE\n" " [default is +%s", get_valid_firmwarebug_args(), get_drivedb_path_add() ); #ifdef SMARTMONTOOLS_DRIVEDBDIR pout( "\n" " and then %s", get_drivedb_path_default() ); #endif pout( "]\n\n" "============================================ DEVICE SELF-TEST OPTIONS =====\n\n" " -t TEST, --test=TEST\n" " Run test. TEST: offline, short, long, conveyance, force, vendor,N,\n" " select,M-N, pending,N, afterselect,[on|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" ); std::string examples = smi()->get_app_examples("smartctl"); if (!examples.empty()) pout("%s\n", examples.c_str()); } // Values for --long only options, see parse_options() enum { opt_identify = 1000, opt_scan, opt_scan_open, opt_set, opt_smart }; /* Returns a string containing a formatted list of the valid arguments to the option opt or empty on failure. Note 'v' case different */ static std::string getvalidarglist(int opt) { switch (opt) { case 'q': return "errorsonly, silent, noserial"; case 'd': return smi()->get_valid_dev_types_str() + ", auto, test"; case 'T': return "normal, conservative, permissive, verypermissive"; case 'b': return "warn, exit, ignore"; case 'B': return "[+]"; case 'r': return "ioctl[,N], ataioctl[,N], scsiioctl[,N], nvmeioctl[,N]"; case opt_smart: case 'o': case 'S': return "on, off"; case 'l': return "error, selftest, selective, directory[,g|s], " "xerror[,N][,error], xselftest[,N][,selftest], " "background, sasphy[,reset], sataphy[,reset], " "scttemp[sts,hist], scttempint,N[,p], " "scterc[,N,M], devstat[,N], defects[,N], ssd, " "gplog,N[,RANGE], smartlog,N[,RANGE], " "nvmelog,N,SIZE"; case 'P': return "use, ignore, show, showall"; case 't': return "offline, short, long, conveyance, force, vendor,N, select,M-N, " "pending,N, afterselect,[on|off]"; case 'F': return std::string(get_valid_firmwarebug_args()) + ", swapid"; case 'n': return "never, sleep[,STATUS], standby[,STATUS], idle[,STATUS]"; case 'f': return "old, brief, hex[,id|val]"; case 'g': return "aam, apm, dsn, lookahead, security, wcache, rcache, wcreorder, wcache-sct"; case opt_set: return "aam,[N|off], apm,[N|off], dsn,[on|off], lookahead,[on|off], security-freeze, " "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off[,p]], " "wcache-sct,[ata|on|off[,p]]"; case 's': return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set); case 'j': return "c, g, i, o, s, u, v"; case opt_identify: return "n, wn, w, v, wv, wb"; case 'v': default: return ""; } } /* Prints the message "=======> VALID ARGUMENTS ARE: \n", where is the list of valid arguments for option opt. */ static void printvalidarglistmessage(int opt) { if (opt=='v'){ jerr("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", create_vendor_attribute_arg_list().c_str()); } else { // getvalidarglist() might produce a multiline or single line string. We // need to figure out which to get the formatting right. std::string s = getvalidarglist(opt); char separator = strchr(s.c_str(), '\n') ? '\n' : ' '; jerr("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator); } return; } // Checksum error mode enum checksum_err_mode_t { CHECKSUM_ERR_WARN, CHECKSUM_ERR_EXIT, CHECKSUM_ERR_IGNORE }; static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN; static void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv); /* Takes command options and sets features to be run */ static int parse_options(int argc, char** argv, const char * & type, ata_print_options & ataopts, scsi_print_options & scsiopts, nvme_print_options & nvmeopts, bool & print_type_only) { // Please update getvalidarglist() if you edit shortopts const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:j"; // 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, opt_smart }, { "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' }, { "xall", no_argument, 0, 'x' }, { "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' }, { "nocheck", required_argument, 0, 'n' }, { "drivedb", required_argument, 0, 'B' }, { "format", required_argument, 0, 'f' }, { "get", required_argument, 0, 'g' }, { "json", optional_argument, 0, 'j' }, { "identify", optional_argument, 0, opt_identify }, { "set", required_argument, 0, opt_set }, { "scan", no_argument, 0, opt_scan }, { "scan-open", no_argument, 0, opt_scan_open }, { 0, 0, 0, 0 } }; char extraerror[256]; memset(extraerror, 0, sizeof(extraerror)); opterr=optopt=0; smart_devtype_list scan_types; // multiple -d TYPE options for --scan bool use_default_db = true; // set false on '-B FILE' bool output_format_set = false; // set true on '-f FORMAT' int scan = 0; // set by --scan, --scan-open bool badarg = false, captive = false; int testcnt = 0; // number of self-tests requested int optchar; char *arg; while ((optchar = getopt_long(argc, argv, shortopts, longopts, 0)) != -1) { // Clang analyzer: Workaround for false positive messages // 'Dereference of null pointer' and 'Null pointer argument' bool optarg_is_set = !!optarg; #ifdef __clang_analyzer__ if (!optarg_is_set) optarg = (char *)""; #endif switch (optchar){ case 'V': printing_is_off = false; pout("%s", format_version_info("smartctl", true /*full*/).c_str()); return 0; case 'q': if (!strcmp(optarg,"errorsonly")) { printing_is_switchable = true; printing_is_off = false; } else if (!strcmp(optarg,"silent")) { printing_is_switchable = false; printing_is_off = true; } else if (!strcmp(optarg,"noserial")) { dont_print_serial_number = true; } else { badarg = true; } break; case 'd': if (!strcmp(optarg, "test")) print_type_only = true; else if (!strcmp(optarg, "auto")) { type = 0; scan_types.clear(); } else { type = optarg; scan_types.push_back(optarg); } break; case 'T': if (!strcmp(optarg,"normal")) { failuretest_conservative = false; failuretest_permissive = 0; } else if (!strcmp(optarg,"conservative")) { failuretest_conservative = true; } else if (!strcmp(optarg,"permissive")) { if (failuretest_permissive < 0xff) failuretest_permissive++; } else if (!strcmp(optarg,"verypermissive")) { failuretest_permissive = 0xff; } else { badarg = true; } break; case 'b': if (!strcmp(optarg,"warn")) { checksum_err_mode = CHECKSUM_ERR_WARN; } else if (!strcmp(optarg,"exit")) { checksum_err_mode = CHECKSUM_ERR_EXIT; } else if (!strcmp(optarg,"ignore")) { checksum_err_mode = CHECKSUM_ERR_IGNORE; } else { badarg = true; } break; case 'r': { int n1 = -1, n2 = -1, len = strlen(optarg); char s[9+1]; unsigned i = 1; sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2); if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) { badarg = true; } else if (!strcmp(s,"ioctl")) { ata_debugmode = scsi_debugmode = nvme_debugmode = i; } else if (!strcmp(s,"ataioctl")) { ata_debugmode = i; } else if (!strcmp(s,"scsiioctl")) { scsi_debugmode = i; } else if (!strcmp(s,"nvmeioctl")) { nvme_debugmode = i; } else { badarg = true; } } break; case 's': case opt_smart: // --smart if (!strcmp(optarg,"on")) { ataopts.smart_enable = scsiopts.smart_enable = true; ataopts.smart_disable = scsiopts.smart_disable = false; } else if (!strcmp(optarg,"off")) { ataopts.smart_disable = scsiopts.smart_disable = true; ataopts.smart_enable = scsiopts.smart_enable = false; } else if (optchar == 's') { goto case_s_continued; // --set, see below } else { badarg = true; } break; case 'o': if (!strcmp(optarg,"on")) { ataopts.smart_auto_offl_enable = true; ataopts.smart_auto_offl_disable = false; } else if (!strcmp(optarg,"off")) { ataopts.smart_auto_offl_disable = true; ataopts.smart_auto_offl_enable = false; } else { badarg = true; } break; case 'S': if (!strcmp(optarg,"on")) { ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = true; ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = false; } else if (!strcmp(optarg,"off")) { ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = true; ataopts.smart_auto_save_enable = scsiopts.smart_auto_save_enable = false; } else { badarg = true; } break; case 'H': ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; scsiopts.smart_ss_media_log = true; break; case 'F': if (!strcmp(optarg, "swapid")) ataopts.fix_swapped_id = true; else if (!parse_firmwarebug_def(optarg, ataopts.firmwarebugs)) badarg = true; break; case 'c': ataopts.smart_general_values = nvmeopts.drive_capabilities = true; break; case 'A': ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; break; case 'l': if (str_starts_with(optarg, "error")) { int n1 = -1, n2 = -1, len = strlen(optarg); unsigned val = ~0; sscanf(optarg, "error%n,%u%n", &n1, &val, &n2); ataopts.smart_error_log = scsiopts.smart_error_log = true; if (n1 == len) nvmeopts.error_log_entries = 16; else if (n2 == len && val > 0) nvmeopts.error_log_entries = val; else badarg = true; } else if (!strcmp(optarg,"selftest")) { ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; } else if (!strcmp(optarg, "selective")) { ataopts.smart_selective_selftest_log = true; } else if (!strcmp(optarg,"directory")) { ataopts.smart_logdir = ataopts.gp_logdir = true; // SMART+GPL } else if (!strcmp(optarg,"directory,s")) { ataopts.smart_logdir = true; // SMART } else if (!strcmp(optarg,"directory,g")) { ataopts.gp_logdir = true; // GPL } else if (!strcmp(optarg,"sasphy")) { scsiopts.sasphy = true; } else if (!strcmp(optarg,"sasphy,reset")) { scsiopts.sasphy = scsiopts.sasphy_reset = true; } else if (!strcmp(optarg,"sataphy")) { ataopts.sataphy = true; } else if (!strcmp(optarg,"sataphy,reset")) { ataopts.sataphy = ataopts.sataphy_reset = true; } else if (!strcmp(optarg,"background")) { scsiopts.smart_background_log = true; } else if (!strcmp(optarg,"ssd")) { ataopts.devstat_ssd_page = true; scsiopts.smart_ss_media_log = true; } else if (!strcmp(optarg,"scterc")) { ataopts.sct_erc_get = true; } else if (!strcmp(optarg,"scttemp")) { ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; } else if (!strcmp(optarg,"scttempsts")) { ataopts.sct_temp_sts = true; } else if (!strcmp(optarg,"scttemphist")) { ataopts.sct_temp_hist = true; } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) { unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg); if (!( sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1 && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) { snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n"); badarg = true; } ataopts.sct_temp_int = interval; ataopts.sct_temp_int_pers = (n2 == len); } else if (!strncmp(optarg, "devstat", sizeof("devstat")-1)) { int n1 = -1, n2 = -1, len = strlen(optarg); unsigned val = ~0; sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2); if (n1 == len) ataopts.devstat_all_pages = true; else { if (n2 != len) // retry with hex sscanf(optarg, "devstat,0x%x%n", &val, &n2); if (n2 == len && val <= 0xff) ataopts.devstat_pages.push_back(val); else badarg = true; } } else if (str_starts_with(optarg, "defects")) { int n1 = -1, n2 = -1, len = strlen(optarg); unsigned val = ~0; sscanf(optarg, "defects%n,%u%n", &n1, &val, &n2); if (n1 == len) ataopts.pending_defects_log = 31; // Entries of first page else if (n2 == len && val <= 0xffff * 32 - 1) ataopts.pending_defects_log = val; else badarg = true; } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) { int n1 = -1, n2 = -1, len = strlen(optarg); unsigned val = 8; sscanf(optarg, "xerror%n,error%n", &n1, &n2); if (!(n1 == len || n2 == len)) { n1 = n2 = -1; sscanf(optarg, "xerror,%u%n,error%n", &val, &n1, &n2); } if ((n1 == len || n2 == len) && val > 0) { ataopts.smart_ext_error_log = val; ataopts.retry_error_log = (n2 == len); } else badarg = true; } else if (!strncmp(optarg, "xselftest", sizeof("xselftest")-1)) { int n1 = -1, n2 = -1, len = strlen(optarg); unsigned val = 25; sscanf(optarg, "xselftest%n,selftest%n", &n1, &n2); if (!(n1 == len || n2 == len)) { n1 = n2 = -1; sscanf(optarg, "xselftest,%u%n,selftest%n", &val, &n1, &n2); } if ((n1 == len || n2 == len) && val > 0) { ataopts.smart_ext_selftest_log = val; ataopts.retry_selftest_log = (n2 == len); } else badarg = true; } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { unsigned rt = ~0, wt = ~0; int n = -1; sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { ataopts.sct_erc_set = true; ataopts.sct_erc_readtime = rt; ataopts.sct_erc_writetime = wt; } else { snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); badarg = true; } } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); sscanf(optarg, "%*[a-z],0x%x%n,%u%n%c%u%n", &logaddr, &n1, &page, &n2, &sign, &nsectors, &n3); if (len > n2 && n3 == -1 && !strcmp(optarg+n2, "-max")) { nsectors = ~0U; sign = '+'; n3 = len; } bool gpl = (optarg[0] == 'g'); const char * erropt = (gpl ? "gplog" : "smartlog"); if (!( n1 == len || n2 == len || (n3 == len && (sign == '+' || sign == '-')))) { snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt); badarg = true; } else if (!( logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU) && 0 < nsectors && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U) && (sign != '-' || page <= nsectors) )) { snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt); badarg = true; } else { ata_log_request req; req.gpl = gpl; req.logaddr = logaddr; req.page = page; req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors); ataopts.log_requests.push_back(req); } } else if (str_starts_with(optarg, "nvmelog,")) { int n = -1, len = strlen(optarg); unsigned page = 0, size = 0; sscanf(optarg, "nvmelog,0x%x,0x%x%n", &page, &size, &n); if (n == len && page <= 0xff && 0 < size && size <= 0x4000) { nvmeopts.log_page = page; nvmeopts.log_page_size = size; } else badarg = true; } else { badarg = true; } break; case 'i': ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; break; case opt_identify: ataopts.identify_word_level = ataopts.identify_bit_level = 0; if (optarg_is_set) { for (int i = 0; optarg[i]; i++) { switch (optarg[i]) { case 'w': ataopts.identify_word_level = 1; break; case 'n': ataopts.identify_bit_level = -1; break; case 'v': ataopts.identify_bit_level = 1; break; case 'b': ataopts.identify_bit_level = 2; break; default: badarg = true; } } } break; case 'a': ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; ataopts.smart_general_values = nvmeopts.drive_capabilities = true; ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; ataopts.smart_error_log = scsiopts.smart_error_log = true; nvmeopts.error_log_entries = 16; ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true; ataopts.smart_selective_selftest_log = true; /* scsiopts.smart_background_log = true; */ scsiopts.smart_ss_media_log = true; break; case 'x': ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true; ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true; ataopts.smart_general_values = nvmeopts.drive_capabilities = true; ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true; ataopts.smart_ext_error_log = 8; ataopts.retry_error_log = true; nvmeopts.error_log_entries = 16; ataopts.smart_ext_selftest_log = 25; ataopts.retry_selftest_log = true; scsiopts.smart_error_log = scsiopts.smart_selftest_log = true; ataopts.smart_selective_selftest_log = true; ataopts.smart_logdir = ataopts.gp_logdir = true; ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; ataopts.sct_erc_get = true; ataopts.sct_wcache_reorder_get = true; ataopts.devstat_all_pages = true; ataopts.pending_defects_log = 31; ataopts.sataphy = true; ataopts.get_set_used = true; ataopts.get_aam = ataopts.get_apm = true; ataopts.get_security = true; ataopts.get_lookahead = ataopts.get_wcache = true; ataopts.get_dsn = true; scsiopts.get_rcd = scsiopts.get_wce = true; scsiopts.smart_background_log = true; scsiopts.smart_ss_media_log = true; scsiopts.sasphy = true; if (!output_format_set) ataopts.output_format |= ata_print_options::FMT_BRIEF; break; case 'v': // parse vendor-specific definitions of attributes if (!strcmp(optarg,"help")) { printing_is_off = false; printslogan(); pout("The valid arguments to -v are:\n\thelp\n%s\n", create_vendor_attribute_arg_list().c_str()); return 0; } if (!parse_attribute_def(optarg, ataopts.attribute_defs, PRIOR_USER)) badarg = true; break; case 'P': if (!strcmp(optarg, "use")) { ataopts.ignore_presets = false; } else if (!strcmp(optarg, "ignore")) { ataopts.ignore_presets = true; } else if (!strcmp(optarg, "show")) { ataopts.show_presets = true; } else if (!strcmp(optarg, "showall")) { if (!init_drive_database(use_default_db)) return FAILCMD; if (optind < argc) { // -P showall MODEL [FIRMWARE] int cnt = showmatchingpresets(argv[optind], (optind+1= 0 ? cnt : 0); } if (showallpresets()) return FAILCMD; // report regexp syntax error return 0; } else { badarg = true; } break; case 't': if (!strcmp(optarg,"offline")) { testcnt++; ataopts.smart_selftest_type = OFFLINE_FULL_SCAN; scsiopts.smart_default_selftest = true; } else if (!strcmp(optarg,"short")) { testcnt++; ataopts.smart_selftest_type = SHORT_SELF_TEST; scsiopts.smart_short_selftest = true; } else if (!strcmp(optarg,"long")) { testcnt++; ataopts.smart_selftest_type = EXTEND_SELF_TEST; scsiopts.smart_extend_selftest = true; } else if (!strcmp(optarg,"conveyance")) { testcnt++; ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST; } else if (!strcmp(optarg,"force")) { ataopts.smart_selftest_force = true; scsiopts.smart_selftest_force = true; } else if (!strcmp(optarg,"afterselect,on")) { // scan remainder of disk after doing selected segment ataopts.smart_selective_args.scan_after_select = 2; } else if (!strcmp(optarg,"afterselect,off")) { // don't scan remainder of disk after doing selected segments ataopts.smart_selective_args.scan_after_select = 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') { snprintf(extraerror, sizeof(extraerror), "Option -t pending,N requires N to be a non-negative integer\n"); badarg = true; } else if (i<0 || i>65535) { snprintf(extraerror, sizeof(extraerror), "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i); badarg = true; } else { ataopts.smart_selective_args.pending_time = i+1; } } else if (!strncmp(optarg,"select",strlen("select"))) { if (ataopts.smart_selective_args.num_spans == 0) testcnt++; // parse range of LBAs to test uint64_t start, stop; int mode; if (split_selective_arg(optarg, &start, &stop, &mode)) { snprintf(extraerror, sizeof(extraerror), "Option -t select,M-N must have non-negative integer M and N\n"); badarg = true; } else { if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) { if (start > stop) { snprintf(extraerror, sizeof(extraerror), "ERROR: Start LBA (%" PRIu64 ") > ending LBA (%" PRId64 ") in argument \"%s\"\n", start, stop, optarg); } else { snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be" " defined\n"); } badarg = true; } ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].start = start; ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].end = stop; ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].mode = mode; ataopts.smart_selective_args.num_spans++; ataopts.smart_selftest_type = SELECTIVE_SELF_TEST; } } else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) { snprintf(extraerror, sizeof(extraerror), "-t scttempint is no longer supported, use -l scttempint instead\n"); badarg = true; } else if (!strncmp(optarg, "vendor,", sizeof("vendor,")-1)) { unsigned subcmd = ~0U; int n = -1; if (!( sscanf(optarg, "%*[a-z],0x%x%n", &subcmd, &n) == 1 && subcmd <= 0xff && n == (int)strlen(optarg))) { snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n"); badarg = true; } else ataopts.smart_selftest_type = subcmd; } else { badarg = true; } break; case 'C': captive = true; break; case 'X': testcnt++; scsiopts.smart_selftest_abort = true; ataopts.smart_selftest_type = ABORT_SELF_TEST; break; case 'n': // skip disk check if in low-power mode if (!strcmp(optarg, "never")) { ataopts.powermode = 1; // do not skip, but print mode } else { int n1 = -1, n2 = -1, len = strlen(optarg); char s[7+1]; unsigned i = FAILPOWER; sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2); if (!((n1 == len || n2 == len) && i <= 255)) badarg = true; else if (!strcmp(s, "sleep")) ataopts.powermode = 2; else if (!strcmp(s, "standby")) ataopts.powermode = 3; else if (!strcmp(s, "idle")) ataopts.powermode = 4; else badarg = true; ataopts.powerexit = i; } break; case 'f': if (!strcmp(optarg, "old")) { ataopts.output_format &= ~ata_print_options::FMT_BRIEF; output_format_set = true; } else if (!strcmp(optarg, "brief")) { ataopts.output_format |= ata_print_options::FMT_BRIEF; output_format_set = true; } else if (!strcmp(optarg, "hex")) ataopts.output_format |= ata_print_options::FMT_HEX_ID | ata_print_options::FMT_HEX_VAL; else if (!strcmp(optarg, "hex,id")) ataopts.output_format |= ata_print_options::FMT_HEX_ID; else if (!strcmp(optarg, "hex,val")) ataopts.output_format |= ata_print_options::FMT_HEX_VAL; else badarg = true; break; case 'B': { const char * path = optarg; if (*path == '+' && path[1]) path++; else use_default_db = false; if (!read_drive_database(path)) return FAILCMD; } break; case 'h': printing_is_off = false; printslogan(); Usage(); return 0; case 'g': case_s_continued: // -s, see above case opt_set: // --set { ataopts.get_set_used = true; bool get = (optchar == 'g'); char name[16+1]; unsigned val; int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg); if (sscanf(optarg, "%16[^,=]%n%*[,=]%n%u%n", name, &n1, &n2, &val, &n3) >= 1 && (n1 == len || (!get && n2 > 0))) { bool on = false; bool off = false; bool ata = false; bool persistent = false; if (n2 > 0) { int len2 = strlen(optarg + n2); char * tmp = strstr(optarg+n2, ",p"); // handle ",p" in persistent options like: wcache-sct,[ata|on|off],p if (tmp && (strlen(tmp) == 2)) { persistent = true; len2 = strlen(optarg+n2) - 2; // the ,p option only works for set of SCT Feature Control command if (strcmp(name, "wcache-sct") != 0 && strcmp(name, "wcreorder") != 0) badarg = true; } on = !strncmp(optarg+n2, "on", len2); off = !strncmp(optarg+n2, "off", len2); ata = !strncmp(optarg+n2, "ata", len2); } if (n3 != len) val = ~0U; if (get && !strcmp(name, "all")) { ataopts.get_aam = ataopts.get_apm = true; ataopts.get_security = true; ataopts.get_lookahead = ataopts.get_wcache = true; ataopts.get_dsn = true; scsiopts.get_rcd = scsiopts.get_wce = true; } else if (!strcmp(name, "aam")) { if (get) ataopts.get_aam = true; else if (off) ataopts.set_aam = -1; else if (val <= 254) ataopts.set_aam = val + 1; else { snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n"); badarg = true; } } else if (!strcmp(name, "apm")) { if (get) ataopts.get_apm = true; else if (off) ataopts.set_apm = -1; else if (1 <= val && val <= 254) ataopts.set_apm = val + 1; else { snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n"); badarg = true; } } else if (!strcmp(name, "lookahead")) { if (get) { ataopts.get_lookahead = true; } else if (off) ataopts.set_lookahead = -1; else if (on) ataopts.set_lookahead = 1; else badarg = true; } else if (!strcmp(name, "wcreorder")) { ataopts.sct_wcache_reorder_set_pers = persistent; if (get) { ataopts.sct_wcache_reorder_get = true; } else if (off) ataopts.sct_wcache_reorder_set = -1; else if (on) ataopts.sct_wcache_reorder_set = 1; else badarg = true; } else if (!strcmp(name, "wcache-sct")) { ataopts.sct_wcache_sct_set_pers = persistent; if (get) { ataopts.sct_wcache_sct_get = true; } else if (off) ataopts.sct_wcache_sct_set = 3; else if (on) ataopts.sct_wcache_sct_set = 2; else if (ata) ataopts.sct_wcache_sct_set = 1; else badarg = true; } else if (!strcmp(name, "rcache")) { if (get) scsiopts.get_rcd = true; else if (off) scsiopts.set_rcd = -1; else if (on) scsiopts.set_rcd = 1; else badarg = true; } else if (get && !strcmp(name, "security")) { ataopts.get_security = true; } else if (!get && !strcmp(optarg, "security-freeze")) { ataopts.set_security_freeze = true; } else if (!get && !strcmp(optarg, "standby,now")) { ataopts.set_standby_now = true; } else if (!get && !strcmp(name, "standby")) { if (off) ataopts.set_standby = 0 + 1; else if (val <= 255) ataopts.set_standby = val + 1; else { snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n"); badarg = true; } } else if (!strcmp(name, "wcache")) { if (get) { ataopts.get_wcache = true; scsiopts.get_wce = true; } else if (off) { ataopts.set_wcache = -1; scsiopts.set_wce = -1; } else if (on) { ataopts.set_wcache = 1; scsiopts.set_wce = 1; } else badarg = true; } else if (!strcmp(name, "dsn")) { if (get) { ataopts.get_dsn = true; } else if (off) { ataopts.set_dsn = -1; } else if (on) { ataopts.set_dsn = 1; } else badarg = true; } else badarg = true; } else badarg = true; } break; case opt_scan: case opt_scan_open: scan = optchar; break; case 'j': { print_as_json = true; print_as_json_options.pretty = true; print_as_json_options.sorted = false; print_as_json_options.flat = false; print_as_json_output = false; print_as_json_impl = print_as_json_unimpl = false; bool json_verbose = false; if (optarg_is_set) { for (int i = 0; optarg[i]; i++) { switch (optarg[i]) { case 'c': print_as_json_options.pretty = false; break; case 'g': print_as_json_options.flat = true; break; case 'i': print_as_json_impl = true; break; case 'o': print_as_json_output = true; break; case 's': print_as_json_options.sorted = true; break; case 'u': print_as_json_unimpl = true; break; case 'v': json_verbose = true; break; default: badarg = true; } } } js_initialize(argc, argv, json_verbose); } break; case '?': default: printing_is_off = false; printslogan(); // 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 && (optopt >= opt_scan || strchr(shortopts, optopt))) { jerr("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); printvalidarglistmessage(optopt); } else jerr("=======> UNRECOGNIZED OPTION: %s\n",arg+2); if (extraerror[0]) pout("=======> %s", extraerror); UsageSummary(); return FAILCMD; } if (0 < optopt && 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) { jerr("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt); printvalidarglistmessage(optopt); } else jerr("=======> UNRECOGNIZED OPTION: %c\n",optopt); if (extraerror[0]) pout("=======> %s", extraerror); UsageSummary(); return FAILCMD; } Usage(); return 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. char optstr[] = { (char)optchar, 0 }; jerr("=======> INVALID ARGUMENT TO -%s: %s\n", (optchar == opt_identify ? "-identify" : optchar == opt_set ? "-set" : optchar == opt_smart ? "-smart" : optchar == 'j' ? "-json" : optstr), optarg); printvalidarglistmessage(optchar); if (extraerror[0]) pout("=======> %s", extraerror); UsageSummary(); return FAILCMD; } } // Special handling of --scan, --scanopen if (scan) { // Read or init drive database to allow USB ID check. if (!init_drive_database(use_default_db)) return FAILCMD; scan_devices(scan_types, (scan == opt_scan_open), argv + optind); return 0; } // 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 (printing_is_switchable) printing_is_off = true; // Check for multiple -d TYPE options if (scan_types.size() > 1) { printing_is_off = false; printslogan(); jerr("ERROR: multiple -d TYPE options are only allowed with --scan\n"); UsageSummary(); return FAILCMD; } // error message if user has asked for more than one test if (testcnt > 1) { printing_is_off = false; printslogan(); jerr("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); UsageSummary(); return FAILCMD; } // error message if user has set selective self-test options without // asking for a selective self-test if ( (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select) && !ataopts.smart_selective_args.num_spans) { printing_is_off = false; printslogan(); if (ataopts.smart_selective_args.pending_time) jerr("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); else jerr("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n"); UsageSummary(); return FAILCMD; } // If captive option was used, change test type if appropriate. if (captive) switch (ataopts.smart_selftest_type) { case SHORT_SELF_TEST: ataopts.smart_selftest_type = SHORT_CAPTIVE_SELF_TEST; scsiopts.smart_short_selftest = false; scsiopts.smart_short_cap_selftest = true; break; case EXTEND_SELF_TEST: ataopts.smart_selftest_type = EXTEND_CAPTIVE_SELF_TEST; scsiopts.smart_extend_selftest = false; scsiopts.smart_extend_cap_selftest = true; break; case CONVEYANCE_SELF_TEST: ataopts.smart_selftest_type = CONVEYANCE_CAPTIVE_SELF_TEST; break; case SELECTIVE_SELF_TEST: ataopts.smart_selftest_type = SELECTIVE_CAPTIVE_SELF_TEST; break; } // From here on, normal operations... printslogan(); // Warn if the user has provided no device name if (argc-optind<1){ jerr("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); UsageSummary(); return FAILCMD; } // Warn if the user has provided more than one device name if (argc-optind>1){ int i; jerr("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); pout("You have provided %d device names:\n",argc-optind); for (i=0; i '\0' static int lineno = 0; lineno++; if (print_as_json_output) { // Collect full output in array static int outindex = 0; jglb["smartctl"]["output"][outindex++] = p; } if (!*p) continue; // Skip empty line if (msg_severity) { // Collect non-empty messages in array static int errindex = 0; json::ref jref = jglb["smartctl"]["messages"][errindex++]; jref["string"] = p; jref["severity"] = msg_severity; } if ( ( is_js_impl && print_as_json_impl ) || (!is_js_impl && print_as_json_unimpl)) { // Add (un)implemented non-empty lines to global object jglb[strprintf("smartctl_%04d_%c", lineno, (is_js_impl ? 'i' : 'u')).c_str()] = p; } } } } // Default: print to stdout // --json: ignore // --json=o: append to "output" array // --json=u: add "smartctl_NNNN_u" element(s) void pout(const char *fmt, ...) { if (printing_is_off) return; if (print_as_json && !(print_as_json_output || print_as_json_impl || print_as_json_unimpl)) return; va_list ap; va_start(ap, fmt); vjpout(false, 0, fmt, ap); va_end(ap); } // Default: Print to stdout // --json: ignore // --json=o: append to "output" array // --json=i: add "smartctl_NNNN_i" element(s) void jout(const char *fmt, ...) { if (printing_is_off) return; if (print_as_json && !(print_as_json_output || print_as_json_impl || print_as_json_unimpl)) return; va_list ap; va_start(ap, fmt); vjpout(true, 0, fmt, ap); va_end(ap); } // Default: print to stdout // --json: append to "messages" // --json=o: append to "output" array // --json=i: add "smartctl_NNNN_i" element(s) void jinf(const char *fmt, ...) { if (printing_is_off) return; va_list ap; va_start(ap, fmt); vjpout(true, "information", fmt, ap); va_end(ap); } void jwrn(const char *fmt, ...) { if (printing_is_off) return; va_list ap; va_start(ap, fmt); vjpout(true, "warning", fmt, ap); va_end(ap); } void jerr(const char *fmt, ...) { if (printing_is_off) return; va_list ap; va_start(ap, fmt); vjpout(true, "error", fmt, ap); va_end(ap); } // Globals to set failuretest() policy bool failuretest_conservative = false; unsigned char failuretest_permissive = 0; // Compares failure type to policy in effect, and either exits or // simply returns to the calling routine. // Used in ataprint.cpp and scsiprint.cpp. void failuretest(failure_type type, int returnvalue) { // If this is an error in an "optional" SMART command if (type == OPTIONAL_CMD) { if (!failuretest_conservative) return; pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n"); throw int(returnvalue); } // If this is an error in a "mandatory" SMART command if (type == MANDATORY_CMD) { if (failuretest_permissive--) return; pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n"); throw int(returnvalue); } throw std::logic_error("failuretest: Unknown type"); } // Used to warn users about invalid checksums. Called from atacmds.cpp. // Action to be taken may be altered by the user. void checksumwarning(const char * string) { // user has asked us to ignore checksum errors if (checksum_err_mode == CHECKSUM_ERR_IGNORE) return; pout("Warning! %s error: invalid SMART checksum.\n", string); // user has asked us to fail on checksum errors if (checksum_err_mode == CHECKSUM_ERR_EXIT) throw int(FAILSMART); } // Return info string about device protocol static const char * get_protocol_info(const smart_device * dev) { switch ( (int)dev->is_ata() | ((int)dev->is_scsi() << 1) | ((int)dev->is_nvme() << 2)) { case 0x1: return "ATA"; case 0x2: return "SCSI"; case 0x3: return "ATA+SCSI"; case 0x4: return "NVMe"; default: return "Unknown"; } } // Add JSON device info static void js_device_info(const json::ref & jref, const smart_device * dev) { jref["name"] = dev->get_dev_name(); jref["info_name"] = dev->get_info_name(); jref["type"] = dev->get_dev_type(); jref["protocol"] = get_protocol_info(dev); } // Device scan // smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...] void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv) { bool dont_print = !(ata_debugmode || scsi_debugmode || nvme_debugmode); const char * pattern = 0; int ai = 0; if (argv[ai] && argv[ai][0] != '-') pattern = argv[ai++]; smart_device_list devlist; printing_is_off = dont_print; bool ok = smi()->scan_smart_devices(devlist, types, pattern); printing_is_off = false; if (!ok) { pout("# scan_smart_devices: %s\n", smi()->get_errmsg()); return; } for (unsigned i = 0; i < devlist.size(); i++) { smart_device_auto_ptr dev( devlist.release(i) ); json::ref jref = jglb["devices"][i]; if (with_open) { printing_is_off = dont_print; dev.replace ( dev->autodetect_open() ); printing_is_off = false; } js_device_info(jref, dev.get()); if (with_open && !dev->is_open()) { jout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(), dev->get_dev_type(), dev->get_info_name(), get_protocol_info(dev.get()), dev->get_errmsg()); jref["open_error"] = dev->get_errmsg(); continue; } jout("%s -d %s", dev->get_dev_name(), dev->get_dev_type()); if (!argv[ai]) jout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get())); else { for (int j = ai; argv[j]; j++) jout(" %s", argv[j]); jout("\n"); } if (dev->is_open()) dev->close(); } } // Main program without exception handling static int main_worker(int argc, char **argv) { // Throw if runtime environment does not match compile time test. check_config(); // Initialize interface smart_interface::init(); if (!smi()) return 1; // Parse input arguments const char * type = 0; ata_print_options ataopts; scsi_print_options scsiopts; nvme_print_options nvmeopts; bool print_type_only = false; { int status = parse_options(argc, argv, type, ataopts, scsiopts, nvmeopts, print_type_only); if (status >= 0) return status; } const char * name = argv[argc-1]; smart_device_auto_ptr dev; if (!strcmp(name,"-")) { // Parse "smartctl -r ataioctl,2 ..." output from stdin if (type || print_type_only) { pout("-d option is not allowed in conjunction with device name \"-\".\n"); UsageSummary(); return FAILCMD; } dev = get_parsed_ata_device(smi(), name); } else // get device of appropriate type dev = smi()->get_smart_device(name, type); if (!dev) { jerr("%s: %s\n", name, smi()->get_errmsg()); if (type) printvalidarglistmessage('d'); else pout("Please specify device type with the -d option.\n"); UsageSummary(); return FAILCMD; } if (print_type_only) // Report result of first autodetection pout("%s: Device of type '%s' [%s] detected\n", dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); if (dev->is_ata() && ataopts.powermode>=2 && dev->is_powered_down()) { jinf("Device is in STANDBY (OS) mode, exit(%d)\n", ataopts.powerexit); return ataopts.powerexit; } // Open device { // Save old info smart_device::device_info oldinfo = dev->get_info(); // Open with autodetect support, may return 'better' device dev.replace( dev->autodetect_open() ); // Report if type has changed if ( (ata_debugmode || scsi_debugmode || nvme_debugmode || print_type_only) && oldinfo.dev_type != dev->get_dev_type() ) pout("%s: Device open changed type from '%s' to '%s'\n", dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type()); } if (!dev->is_open()) { jerr("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg()); return FAILDEV; } // Add JSON info similar to --scan output js_device_info(jglb["device"], dev.get()); // now call appropriate ATA or SCSI routine int retval = 0; if (print_type_only) jout("%s: Device of type '%s' [%s] opened\n", dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get())); else if (dev->is_ata()) retval = ataPrintMain(dev->to_ata(), ataopts); else if (dev->is_scsi()) retval = scsiPrintMain(dev->to_scsi(), scsiopts); else if (dev->is_nvme()) retval = nvmePrintMain(dev->to_nvme(), nvmeopts); else // we should never fall into this branch! pout("%s: Neither ATA, SCSI nor NVMe device\n", dev->get_info_name()); dev->close(); return retval; } // Main program int main(int argc, char **argv) { int status; bool badcode = false; try { try { // Do the real work ... status = main_worker(argc, argv); } catch (int ex) { // Exit status from checksumwarning() and failuretest() arrives here status = ex; } // Print JSON if enabled if (jglb.has_uint128_output()) jglb["smartctl"]["uint128_precision_bits"] = uint128_to_str_precision_bits(); jglb["smartctl"]["exit_status"] = status; jglb.print(stdout, print_as_json_options); } catch (const std::bad_alloc & /*ex*/) { // Memory allocation failed (also thrown by std::operator new) printf("Smartctl: Out of memory\n"); status = FAILCMD; } catch (const std::exception & ex) { // Other fatal errors printf("Smartctl: Exception: %s\n", ex.what()); badcode = true; status = FAILCMD; } // Check for remaining device objects if (smart_device::get_num_objects() != 0) { printf("Smartctl: Internal Error: %d device object(s) left at exit.\n", smart_device::get_num_objects()); badcode = true; status = FAILCMD; } if (badcode) printf("Please inform " PACKAGE_BUGREPORT ", including output of smartctl -V.\n"); return status; } smartmontools-7.0/smartctl.h0000644000175000010010000000533213401001476013210 00000000000000/* * smartctl.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-10 Bruce Allen * Copyright (C) 2008-17 Christian Franke * Copyright (C) 2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef SMARTCTL_H_ #define SMARTCTL_H_ #define SMARTCTL_H_CVSID "$Id: smartctl.h 4842 2018-12-02 16:07:26Z chrfranke $\n" // Return codes (bitmask) // command line did not parse, or internal error occurred in smartctl #define FAILCMD (0x01<<0) // device open failed #define FAILDEV (0x01<<1) // device is in low power mode and -n option requests to exit #define FAILPOWER (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. enum failure_type { OPTIONAL_CMD, MANDATORY_CMD, }; // Globals to set failuretest() policy extern bool failuretest_conservative; extern unsigned char failuretest_permissive; // Compares failure type to policy in effect, and either exits or // simply returns to the calling routine. void failuretest(failure_type type, int returnvalue); // Globals to control printing extern bool printing_is_switchable; extern bool printing_is_off; // Printing control functions inline void print_on() { if (printing_is_switchable) printing_is_off = false; } inline void print_off() { if (printing_is_switchable) printing_is_off = true; } // The singleton global JSON object #include "json.h" extern json jglb; #include "utility.h" // __attribute_format_printf() // TODO: move this to a new include file? // Version of pout() for items already included in JSON output void jout(const char *fmt, ...) __attribute_format_printf(1, 2); // Version of pout() for info/warning/error messages void jinf(const char *fmt, ...) __attribute_format_printf(1, 2); void jwrn(const char *fmt, ...) __attribute_format_printf(1, 2); void jerr(const char *fmt, ...) __attribute_format_printf(1, 2); #endif smartmontools-7.0/smartd.8.in0000644000175000010010000007610213405514171013206 00000000000000.ig Copyright (C) 2002-10 Bruce Allen Copyright (C) 2004-18 Christian Franke SPDX-License-Identifier: GPL-2.0-or-later $Id: smartd.8.in 4861 2018-12-16 18:24:57Z chrfranke $ .. .\" Macros borrowed from pages generated with Pod::Man .de Sp \" Vertical space (when we can't use .PP) .if t .sp 0.4v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Use groff extension \(aq (apostrophe quote, ASCII 0x27) if possible .ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH SMARTD 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools" .SH NAME \fBsmartd\fP \- SMART Disk Monitoring Daemon .Sp .SH SYNOPSIS .B smartd [options] .Sp .SH DESCRIPTION .\" %IF NOT OS ALL .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools. .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL \fBsmartd\fP is a daemon that monitors the Self-Monitoring, Analysis and Reporting Technology (SMART) system built into most ATA/SATA and SCSI/SAS hard drives and solid-state drives. The purpose of SMART is to monitor the reliability of the hard drive and predict drive failures, and to carry out different types of drive self-tests. This version of \fBsmartd\fP is compatible with ACS-3, ACS-2, ATA8-ACS, ATA/ATAPI-7 and earlier standards (see \fBREFERENCES\fP below). .PP \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 system-dependent (typically \fB/var/log/messages\fP or \fB/var/log/syslog\fP). To change this default location, please see the \*(Aq\-l\*(Aq command-line option described below. .PP 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. .PP 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 \*(Aq\-i\*(Aq option below for additional details. .PP \fBsmartd\fP can be configured at start-up using the configuration file \fB/usr/local/etc/smartd.conf\fP (Windows: \fBEXEDIR/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: .br \fBkillall \-HUP smartd\fP. .br .\" %IF OS Windows (Windows: See NOTES below.) .\" %ENDIF OS Windows .PP 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. .PP 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. .\" %IF OS Windows (Windows: CONTROL-Break). .\" %ENDIF OS Windows .\" %IF ENABLE_SYSTEMD_NOTIFY .PP [Linux only] [NEW EXPERMIMENTAL SMARTD FEATURE] If \fBsmartd\fP is started as a \fBsystemd\fP(1) service and \*(AqType=Notify\*(Aq is specified in the service file, the service manager is notified after successful startup. Other state changes are reported via systemd notify STATUS messages. Notification of successful reloads (after \fBHUP\fP signal) is not supported. To detect this process start-up type, \fBsmartd\fP checks whether the environment variable \*(AqNOTIFY_SOCKET\*(Aq is set. Note that it is required to set the \*(Aq\-n\*(Aq (\*(Aq\-\-nofork\*(Aq) option in the \*(AqExecStart=/usr/local/sbin/smartd\*(Aq command line if \*(AqType=Notify\*(Aq is used. .\" %ENDIF ENABLE_SYSTEMD_NOTIFY .PP 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: .\" %IF OS Linux .IP \fBLINUX:\fP 9 Examine all entries \fB"/dev/hd[a\-t]"\fP for IDE/ATA devices, and \fB"/dev/sd[a\-z]"\fP, \fB"/dev/sd[a\-c][a\-z]"\fP for ATA/SATA or SCSI/SAS devices. Disks behind RAID controllers are not included. .Sp If directive \*(Aq\-d nvme\*(Aq .\" %IF ENABLE_NVME_DEVICESCAN or no \*(Aq\-d\*(Aq directive .\" %ENDIF ENABLE_NVME_DEVICESCAN is specified, examine all entries \fB"/dev/nvme[0\-99]"\fP for NVMe devices. .\" %ENDIF OS Linux .\" %IF OS FreeBSD .IP \fBFREEBSD:\fP 9 Authoritative list of disk devices is obtained from SCSI (CAM) and ATA subsystems. Disks behind RAID controllers are not included. .\" %ENDIF OS FreeBSD .\" %IF OS NetBSD OpenBSD .IP \fBNETBSD/OPENBSD:\fP 9 Authoritative list of disk devices is obtained from sysctl \*(Aqhw.disknames\*(Aq. .\" %ENDIF OS NetBSD OpenBSD .\" %IF OS Solaris .IP \fBSOLARIS:\fP 9 Examine all entries \fB"/dev/rdsk/*s0"\fP for IDE/ATA and SCSI disk devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices. .\" %ENDIF OS Solaris .\" %IF OS Darwin .IP \fBDARWIN:\fP 9 The IOService plane is scanned for ATA block storage devices. .\" %ENDIF OS Darwin .\" %IF OS Windows Cygwin .IP \fBWINDOWS\fP: 9 Examine all entries \fB"/dev/sd[a\-z]"\fP, \fB"/dev/sd[a\-c][a\-z]"\fP and \fB"/dev/sdd[a\-x]"\fP ("\\\\.\\PhysicalDrive[0\-127]") for IDE/(S)ATA and SCSI disk devices. .Sp If a 3ware 9000 controller is installed, examine all entries \fB"/dev/sdX,N"\fP for the first logical drive (\*(Aqunit\*(Aq \fB"/dev/sdX"\fP) and all physical disks (\*(Aqports\*(Aq \fB",N"\fP) detected behind this controller. Same for a second controller if present. .Sp If directive \*(Aq\-d csmi\*(Aq or no \*(Aq\-d\*(Aq directive is specified, examine all entries \fB"/dev/csmi[0\-9],N"\fP for drives behind an Intel ICHxR controller with RST driver. .Sp Disks behind Areca RAID controllers are not included. .Sp If directive \*(Aq\-d nvme\*(Aq .\" %IF ENABLE_NVME_DEVICESCAN or no \*(Aq\-d\*(Aq directive .\" %ENDIF ENABLE_NVME_DEVICESCAN is specified, examine all entries \fB"/dev/sd[...]"\fP (see above) and all entries \fB"/dev/nvme[0\-9]"\fP for NVMe devices. .\" %ENDIF OS Windows Cygwin .PP \fBsmartd\fP then monitors for \fIall\fP possible SMART errors (corresponding to the \*(Aq\-a\*(Aq Directive in the configuration file; see the \fBsmartd.conf\fP(5) man page). .Sp .SH OPTIONS .TP .B \-A PREFIX, \-\-attributelog=PREFIX Writes \fBsmartd\fP attribute information (normalized and raw attribute values) to files \*(AqPREFIX\*(Aq\*(AqMODEL\-SERIAL.ata.csv\*(Aq or \*(AqPREFIX\*(Aq\*(AqVENDOR\-MODEL\-SERIAL.scsi.csv\*(Aq. At each check cycle attributes are logged as a line of semicolon separated triplets of the form "attribute-ID;attribute-norm-value;attribute-raw-value;". For SCSI devices error counters and temperature recorded in the form "counter-name;counter-value;". Each line is led by a date string of the form "yyyy-mm-dd HH:MM:SS" (in UTC). .Sp .\" %IF ENABLE_ATTRIBUTELOG If this option is not specified, attribute information is written to files \*(Aq/usr/local/var/lib/smartmontools/attrlog.MODEL\-SERIAL.ata.csv\*(Aq. To disable attribute log files, specify this option with an empty string argument: \*(Aq\-A ""\*(Aq. .\" %ENDIF ENABLE_ATTRIBUTELOG MODEL and SERIAL are build from drive identify information, invalid characters are replaced by underline. .Sp If the PREFIX has the form \*(Aq/path/dir/\*(Aq (e.g.\& \*(Aq/var/lib/smartd/\*(Aq), then files \*(AqMODEL\-SERIAL.ata.csv\*(Aq are created in directory \*(Aq/path/dir\*(Aq. If the PREFIX has the form \*(Aq/path/name\*(Aq (e.g.\& \*(Aq/var/lib/misc/attrlog\-\*(Aq), then files \*(AqnameMODEL\-SERIAL.ata.csv\*(Aq are created in directory \*(Aq/path/\*(Aq. The path must be absolute, except if debug mode is enabled. .TP .B \-B [+]FILE, \-\-drivedb=[+]FILE [ATA only] Read the drive database from FILE. The new database replaces the built in database by default. If \*(Aq+\*(Aq is specified, then the new entries prepend the built in entries. Please see the \fBsmartctl\fP(8) man page for further details. .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: \fBEXEDIR/smartd.conf\fP). If FILE does \fBnot\fP exist, then \fBsmartd\fP will print an error message and exit with nonzero status. Thus, \*(Aq\-c /usr/local/etc/smartd.conf\*(Aq can be used to verify the existence of the default configuration file. .Sp By using \*(Aq\-\*(Aq for FILE, the configuration is read from standard input. This is useful for commands like: .br .B echo /dev/sdb \-m user@home \-M test | smartd \-c \- \-q onecheck .br to perform quick and simple checks without a configuration file. .\" %IF ENABLE_CAPABILITIES .TP .B \-C, \-\-capabilities [Linux only] Use libcap-ng to drop unneeded Linux process \fBcapabilities\fP(7). The following capabilities are kept: CAP_SYS_ADMIN, CAP_SYS_RAWIO, CAP_MKNOD. .Sp Warning: Mail notification does not work when used. .\" %ENDIF ENABLE_CAPABILITIES .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\fP(2) 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 \fBINT\fP signal (normally generated from a terminal with CONTROL-C) makes \fBsmartd\fP reload its configuration file. Please use CONTROL-\e to exit .\" %IF OS Windows (Windows: CONTROL-Break). .Sp [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. .\" %ENDIF OS Windows .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 described in the \fBsmartd.conf\fP(5) 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. .Sp 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: .br .B kill \-SIGUSR1 .br where \fB\fP is the process id number of \fBsmartd\fP. One may also use: .br .B killall \-USR1 smartd .br for the same purpose. .br .\" %IF OS Windows (Windows: See NOTES below.) .\" %ENDIF OS Windows .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. .Sp If you would like to have \fBsmartd\fP messages logged somewhere other than the default location, include (for example) \*(Aq\-l local3\*(Aq in its start up argument list. Tell the syslog daemon to log all messages from facility \fBlocal3\fP to (for example) \*(Aq/var/log/smartd.log\*(Aq. .Sp For more detailed information, please refer to the man pages for the local syslog daemon, typically \fBsyslogd\fP(8), \fBsyslog-ng\fP(8) or \fBrsyslogd\fP(8). .\" %IF OS Cygwin .Sp Cygwin: If no \fBsyslogd\fP is running, the \*(Aq\-l\*(Aq option has no effect. In this case, all \fBsyslog\fP messages are written to Windows event log. .\" %ENDIF OS Cygwin .\" %IF OS Windows .Sp Windows: Some \fBsyslog\fP functionality is implemented internally in \fBsmartd\fP as follows: If no \*(Aq\-l\*(Aq option (or \*(Aq\-l daemon\*(Aq) is specified, messages are written to Windows event log or to file \fB./smartd.log\fP if event log is not available (access denied). By specifying other values of FACILITY, log output is redirected as follows: \*(Aq\-l local0\*(Aq to file \fB./smartd.log\fP, \*(Aq\-l local1\*(Aq to standard output (redirect with \*(Aq>\*(Aq to any file), \*(Aq\-l local2\*(Aq to standard error, \*(Aq\-l local[3\-7]\*(Aq: to file \fB./smartd[1\-5].log\fP. .\" %ENDIF OS Windows .TP .B \-n, \-\-no\-fork Do not fork into background; this is useful when executed from modern init methods like initng, minit, supervise or systemd. .\" %IF OS Cygwin .Sp On Cygwin, this allows running \fBsmartd\fP as service via cygrunsrv, see NOTES below. .\" %ENDIF OS Cygwin .\" %IF OS Windows .Sp On Windows, this option is not available, use \*(Aq\-\-service\*(Aq instead. .\" %ENDIF OS Windows .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: .Sp .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. .Sp .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. .Sp .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. .Sp .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. .Sp .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. .Sp This last option is intended for \*(Aqdistribution-writers\*(Aq 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. .Sp .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. .Sp This option is intended to test whether the \*(Aq\-s REGEX\*(Aq 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: .Sp .I ioctl \- report all ioctl() transactions. .Sp .I ataioctl \- report only ioctl() transactions with ATA devices. .Sp .I scsiioctl \- report only ioctl() transactions with SCSI devices. .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I nvmeioctl \- report only ioctl() transactions with NVMe devices. .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin 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 \*(Aq\-r ataioctl,1\*(Aq and \*(Aq\-r ataioctl\*(Aq are equivalent. .TP .B \-s PREFIX, \-\-savestates=PREFIX Reads/writes \fBsmartd\fP state information from/to files \*(AqPREFIX\*(Aq\*(AqMODEL\-SERIAL.ata.state\*(Aq or \*(AqPREFIX\*(Aq\*(AqVENDOR\-MODEL\-SERIAL.scsi.state\*(Aq. This preserves SMART attributes, drive min and max temperatures (\-W directive), info about last sent warning email (\-m directive), and the time of next check of the self-test REGEXP (\-s directive) across boot cycles. .Sp .\" %IF ENABLE_SAVESTATES If this option is not specified, state information is maintained in files \*(Aq/usr/local/var/lib/smartmontools/smartd.MODEL\-SERIAL.ata.state\*(Aq for ATA devices and \*(Aq/usr/local/var/lib/smartmontools/smartd.VENDOR\-MODEL\-SERIAL.scsi.state\*(Aq for SCSI devices. To disable state files, specify this option with an empty string argument: \*(Aq\-s ""\*(Aq. .\" %ENDIF ENABLE_SAVESTATES MODEL and SERIAL are build from drive identify information, invalid characters are replaced by underline. .Sp If the PREFIX has the form \*(Aq/path/dir/\*(Aq (e.g.\& \*(Aq/var/lib/smartd/\*(Aq), then files \*(AqMODEL\-SERIAL.ata.state\*(Aq are created in directory \*(Aq/path/dir\*(Aq. If the PREFIX has the form \*(Aq/path/name\*(Aq (e.g.\& \*(Aq/var/lib/misc/smartd\-\*(Aq), then files \*(AqnameMODEL\-SERIAL.ata.state\*(Aq are created in directory \*(Aq/path/\*(Aq. The path must be absolute, except if debug mode is enabled. .Sp The state information files are read on smartd startup. The files are always (re)written after reading the configuration file, before rereading the configuration file (SIGHUP), before smartd shutdown, and after a check forced by SIGUSR1. After a normal check cycle, a file is only rewritten if an important change (which usually results in a SYSLOG output) occurred. .TP .B \-w PATH, \-\-warnexec=PATH Run the executable PATH instead of the default script when smartd needs to send warning messages. PATH must point to an executable binary file or script. The default script is .\" %IF NOT OS Windows \fB/usr/local/etc/smartd_warning.sh\fP. .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: EXEDIR/smartd_warning.cmd) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! \fBEXEDIR/smartd_warning.cmd\fP. .\" %ENDIF OS Windows .\" %IF OS Windows .TP .B \-\-service [Windows only] Enables \fBsmartd\fP to run as a Windows service. 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. .\" %ENDIF OS Windows .TP .B \-V, \-\-version, \-\-license, \-\-copyright Prints version, copyright, license, home page and SVN revision information for your copy of \fBsmartd\fP to STDOUT and then exits. .Sp .SH EXAMPLES .B smartd .br Runs the daemon in forked mode. This is the normal way to run \fBsmartd\fP. Entries are logged to SYSLOG. .Sp .B smartd \-d \-i 30 .br Run in foreground (debug) mode, checking the disk status every 30 seconds. .Sp .B smartd \-q onecheck .br Registers devices, and checks the status of the devices exactly once. The exit status (the shell .B $? variable) will be zero if all went well, and nonzero if no devices were detected or some other problem was encountered. .\" %IF ENABLE_INITSCRIPT .Sp 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: .br .B /usr/local/etc/rc.d/init.d/smartd start .br and stop it by using the command: .br .B /usr/local/etc/rc.d/init.d/smartd stop .\" %ENDIF ENABLE_INITSCRIPT .Sp .SH CONFIGURATION The syntax of the \fBsmartd.conf\fP(5) file is discussed separately. .Sp .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 \*(Aq\-t\*(Aq, \*(Aq\-p\*(Aq, or .B \*(Aq\-u\*(Aq Directives. For example: .br .B \*(AqDevice: /dev/sda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93\*(Aq .br Note that in this message, the value given is the \*(AqNormalized\*(Aq not the \*(AqRaw\*(Aq Attribute value (the disk temperature in this case is about 22 Celsius). The .B \*(Aq\-R\*(Aq and .B \*(Aq\-r\*(Aq Directives modify this behavior, so that the information is printed with the Raw values as well, for example: .br .B \*(AqDevice: /dev/sda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]\*(Aq .br Here the Raw values are the actual disk temperatures in Celsius. The way in which the Raw values are printed, and the names under which the Attributes are reported, is governed by the various .B \*(Aq\-v Num,Description\*(Aq Directives described previously. .PP Please see the .B smartctl manual page for further explanation of the differences between Normalized and Raw Attribute values. .PP \fBsmartd\fP will make log entries at loglevel .B LOG_CRIT if a SMART Attribute has failed, for example: .br .B \*(AqDevice: /dev/sdc, Failed SMART Attribute: 5 Reallocated_Sector_Ct\*(Aq .br This loglevel is used for reporting enabled by the .B \*(Aq\-H\*(Aq, \-f\*(Aq, \*(Aq\-l\ selftest\*(Aq, and .B \*(Aq\-l\ error\*(Aq 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. .\" %IF OS Solaris .PP 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: .Vb 1 ...;daemon.notice;... /var/adm/messages .Ve to read: .Vb 1 ...;daemon.info;... /var/adm/messages .Ve Alternatively, you can use a local facility to log messages: please see the \fBsmartd\fP \*(Aq\-l\*(Aq command-line option described above. .\" %ENDIF OS Solaris .\" %IF OS Cygwin .PP The Cygwin Version of \fBsmartd\fP can be run as a service via the cygrunsrv tool. .\" %IF ENABLE_INITSCRIPT The start-up script provides Cygwin-specific commands to install and remove the service: .br .B /usr/local/etc/rc.d/init.d/smartd install [options] .br .B /usr/local/etc/rc.d/init.d/smartd remove .br The service can be started and stopped by the start-up script as usual (see \fBEXAMPLES\fP above). .\" %ENDIF ENABLE_INITSCRIPT .\" %ENDIF OS Cygwin .\" %IF OS Windows .PP On Windows, the log messages are written to the event log or to a file. See documentation of the \*(Aq\-l FACILITY\*(Aq option above for details. .PP On Windows, the following built-in commands can be used to control \fBsmartd\fP, if running as a daemon: .PP \*(Aq\fBsmartd status\fP\*(Aq \- check status .br \*(Aq\fBsmartd stop\fP\*(Aq \- stop smartd .br \*(Aq\fBsmartd reload\fP\*(Aq \- reread config file .br \*(Aq\fBsmartd restart\fP\*(Aq \- restart smartd .br \*(Aq\fBsmartd sigusr1\fP\*(Aq \- check disks now .br \*(Aq\fBsmartd sigusr2\fP\*(Aq \- toggle debug mode .PP The Windows Version of \fBsmartd\fP has buildin support for services: .PP \*(Aq\fBsmartd install [options]\fP\*(Aq installs a service named "smartd" (display name "SmartD Service") using the command line \*(Aq/INSTALLPATH/smartd.exe \-\-service [options]\*(Aq. This also installs smartd.exe as a event message file for the Windows event viewer. .PP \*(Aq\fBsmartd remove\fP\*(Aq can later be used to remove the service and event message entries from the registry. .PP 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 \*(Aq\-c\*(Aq option and \*(Aq\-M exec\*(Aq directive is needed. .PP The debug mode (\*(Aq\-d\*(Aq, \*(Aq\-q onecheck\*(Aq) does not work if smartd is running as service. .PP The service can be controlled as usual with Windows commands \*(Aqnet\*(Aq or \*(Aqsc\*(Aq (\*(Aq\fBnet start smartd\fP\*(Aq, \*(Aq\fBnet stop smartd\fP\*(Aq). .PP Pausing the service (\*(Aq\fBnet pause smartd\fP\*(Aq) sets the interval between disk checks (\*(Aq\-i N\*(Aq) to infinite. .PP Continuing the paused service (\*(Aq\fBnet continue smartd\fP\*(Aq) resets the interval and rereads the configuration file immediately (like \fBSIGHUP\fP). The \*(AqPARAMCHANGE\*(Aq service control command (\*(Aq\fBsc control smartd paramchange\fP\*(Aq) has the same effect regardless of paused state. .PP Continuing a still running service (\*(Aq\fBnet continue smartd\fP\*(Aq without preceding \*(Aq\fBnet pause smartd\fP\*(Aq) does not reread configuration but checks disks immediately (like \fBSIGUSR1\fP). .\" %ENDIF OS Windows .Sp .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 \*(Aq\fBTZ\fP\*(Aq 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\fP(3) 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 \*(Aq\fBTZ\fP\*(Aq variable (or a file that it points to). .Sp .SH EXIT STATUS The exit status (return value) 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 \*(Aq\-c\*(Aq option). .TP .B 6: Config file exists, but cannot be read. .TP .B 8: \fBsmartd\fP ran out of memory during startup. .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 developers, see REPORTING BUGS below. .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. .Sp .\" %IF NOT OS Windows .SH FILES .TP .B /usr/local/sbin/smartd full path of this executable. .TP .B /usr/local/etc/smartd.conf configuration file (see \fBsmartd.conf\fP(5) man page). .TP .B /usr/local/etc/smartd_warning.sh script run on warnings (see \*(Aq\-w\*(Aq option above and \*(Aq\-M exec\*(Aq directive on \fBsmartd.conf\fP(5) man page). .\" %IF ENABLE_SMARTDPLUGINDIR .TP .B /usr/local/etc/smartd_warning.d/ plugin directory for smartd warning script (see \*(Aq\-m\*(Aq directive on \fBsmartd.conf\fP(5) man page). .\" %ENDIF ENABLE_SMARTDPLUGINDIR .\" %IF ENABLE_DRIVEDB .TP .B /usr/local/share/smartmontools/drivedb.h drive database (see \*(Aq\-B\*(Aq option). .\" %ENDIF ENABLE_DRIVEDB .TP .B /usr/local/etc/smart_drivedb.h optional local drive database (see \*(Aq\-B\*(Aq option). .Sp .\" %ENDIF NOT OS Windows .SH AUTHORS \fBBruce Allen\fP (project initiator), .br \fBChristian Franke\fP (project manager, Windows port and all sort of things), .br \fBDouglas Gilbert\fP (SCSI subsystem), .br \fBVolker Kuhlmann\fP (moderator of support and database mailing list), .br \fBGabriele Pohl\fP (wiki & development team support), .br \fBAlex Samorukov\fP (FreeBSD port and more, new Trac wiki). .PP Many other individuals have made contributions and corrections, see AUTHORS, ChangeLog and repository files. .PP The first smartmontools code was derived from the smartsuite package, written by Michael Cornwell and Andre Hedrick. .Sp .SH REPORTING BUGS To submit a bug report, create a ticket in smartmontools wiki: .br <\fBhttps://www.smartmontools.org/\fP>. .br Alternatively send the info to the smartmontools support mailing list: .br <\fBhttps://listi.jpberlin.de/mailman/listinfo/smartmontools-support\fB>. .Sp .SH SEE ALSO \fBsmartd.conf\fP(5), \fBsmartctl\fP(8). .\" %IF ENABLE_UPDATE_SMART_DRIVEDB .br \fBupdate-smart-drivedb\fP(8). .\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB .\" %IF ENABLE_SYSTEMD_NOTIFY .br \fBsystemd.exec\fP(5). .\" %ENDIF ENABLE_SYSTEMD_NOTIFY .Sp .SH REFERENCES Please see the following web site for more info: <\fBhttps://www.smartmontools.org/\fP> .PP An introductory article about smartmontools is \fIMonitoring Hard Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004, pages 74\(en77. See <\fBhttps://www.linuxjournal.com/article/6983\fP>. .PP 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 \*(AqAT Attachment with Packet Interface-7\*(Aq (ATA/ATAPI-7) specification Revision 4b. This documents the SMART functionality which the \fBsmartmontools\fP utilities provide access to. .PP 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. .PP Links to these and other documents may be found on the Links page of the \fBsmartmontools\fP Wiki at <\fBhttps://www.smartmontools.org/wiki/Links\fP>. .Sp .SH PACKAGE VERSION CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .br $Id: smartd.8.in 4861 2018-12-16 18:24:57Z chrfranke $ smartmontools-7.0/smartd.conf0000644000175000010010000001505312567633345013372 00000000000000# Sample configuration file for smartd. See man smartd.conf. # Home page is: http://www.smartmontools.org # $Id: smartd.conf 4120 2015-08-27 16:12:21Z samm2 $ # 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 # Alternative setting to ignore temperature and power-on hours reports # in syslog. #DEVICESCAN -I 194 -I 231 -I 9 # Alternative setting to report more useful raw temperature in syslog. #DEVICESCAN -R 194 -R 231 -I 9 # Alternative setting to report raw temperature changes >= 5 Celsius # and min/max temperatures. #DEVICESCAN -I 194 -I 231 -I 9 -W 5 # First ATA/SATA or SCSI/SAS 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/sda -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/sdb -H -l error -l selftest -t -I 194 # Monitor all attributes except normalized Temperature (usually 194), # but track Temperature changes >= 4 Celsius, report Temperatures # >= 45 Celsius and changes in Raw value of Reallocated_Sector_Ct (5). # Send mail on SMART failures or when Temperature is >= 55 Celsius. #/dev/sdc -a -I 194 -W 4,45,55 -R 5 -m admin@example.com # An ATA disk may appear as a SCSI device to the OS. If a SCSI to # ATA Translation (SAT) layer is between the OS and the device then # this can be flagged with the '-d sat' option. This situation may # become common with SATA disks in SAS and FC environments. # /dev/sda -a -d sat # A very silent check. Only report SMART health status if it fails # But send an email in this case #/dev/sdc -H -C 0 -U 0 -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 (Linux, FreeBSD). 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 # Monitor 2 SATA (not SAS) disks connected to a 3ware 9000 controller which # uses the 3w-sas driver (Linux). Start long self-tests Tuesdays # between 1-2 and 3-4 am. # On FreeBSD /dev/tws0 should be used instead #/dev/twl0 -d 3ware,0 -a -s L/../../2/01 #/dev/twl0 -d 3ware,1 -a -s L/../../2/03 # Same as above for Windows. Option '-d 3ware,N' is not necessary, # disk (port) number is specified in device name. # NOTE: On Windows, DEVICESCAN works also for 3ware controllers. #/dev/hdc,0 -a -s L/../../2/01 #/dev/hdc,1 -a -s L/../../2/03 # Monitor 3 ATA disks directly connected to a HighPoint RocketRAID. Start long # self-tests Sundays between 1-2, 2-3, and 3-4 am. #/dev/sdd -d hpt,1/1 -a -s L/../../7/01 #/dev/sdd -d hpt,1/2 -a -s L/../../7/02 #/dev/sdd -d hpt,1/3 -a -s L/../../7/03 # Monitor 2 ATA disks connected to the same PMPort which connected to the # HighPoint RocketRAID. Start long self-tests Tuesdays between 1-2 and 3-4 am #/dev/sdd -d hpt,1/4/1 -a -s L/../../2/01 #/dev/sdd -d hpt,1/4/2 -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, hpt,L/M/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 # -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit # -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. # DEVICESCAN may be followed by any desired Directives. smartmontools-7.0/smartd.conf.5.in0000644000175000010010000020162013404027470014122 00000000000000.ig Copyright (C) 2002-10 Bruce Allen Copyright (C) 2004-18 Christian Franke SPDX-License-Identifier: GPL-2.0-or-later $Id: smartd.conf.5.in 4856 2018-12-11 21:42:16Z chrfranke $ .. .\" Macros borrowed from pages generated with Pod::Man .de Sp \" Vertical space (when we can't use .PP) .if t .sp 0.4v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Use groff extension \(aq (apostrophe quote, ASCII 0x27) if possible .ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH SMARTD.CONF 5 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools" .SH NAME \fBsmartd.conf\fP \- SMART Disk Monitoring Daemon Configuration File\fP .Sp .SH DESCRIPTION .\" %IF NOT OS ALL .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools. .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL \fB/usr/local/etc/smartd.conf\fP is the configuration file for the \fBsmartd\fP daemon. .PP If the configuration file \fB/usr/local/etc/smartd.conf\fP is present, \fBsmartd\fP reads it at startup. 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. .PP In the absence of a configuration file \fBsmartd\fP will try to open all available devices (see \fBsmartd\fP(8) man page). A configuration file with a single line \fB\*(AqDEVICESCAN \-a\*(Aq\fP would have the same effect. .PP 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 devices that can't be opened. .PP 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/\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 \*(Aq#\*(Aq 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 \*(Aq\e\*(Aq 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 \*(Aq#\*(Aq is treated as a white-space blank line, \fBnot\fP as a non-existent line, and will \fBend\fP a continuation line. .PP 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! .PP .Vb 9 ################################################ # This is an example smartd startup config file # /usr/local/etc/smartd.conf # # On the second disk, start a long self-test every # Sunday between 3 and 4 am. # /dev/sda \-a \-m admin@example.com,root@localhost /dev/sdb \-a \-I 194 \-I 5 \-i 12 \-s L/../../7/03 # # Send a TEST warning email to admin on startup. # /dev/sdc \-m admin@example.com \-M test # # Strange device. It's SCSI. Start a scheduled # long self test between 5 and 6 am Monday/Thursday /dev/weird \-d scsi \-s L/../../(1|4)/05 # # An ATA disk may appear as a SCSI device to the # OS. If a SCSI to ATA Translation (SAT) layer # is between the OS and the device then this can be # flagged with the '\-d sat' option. This situation # may become common with SATA disks in SAS and FC # environments. /dev/sda \-a \-d sat .\" %IF OS Linux # # Three disks connected to a MegaRAID controller # Start short self\-tests daily between 1\-2, 2\-3, and # 3\-4 am. /dev/sda \-d megaraid,0 \-a \-s S/../.././01 /dev/sda \-d megaraid,1 \-a \-s S/../.././02 /dev/sda \-d megaraid,2 \-a \-s S/../.././03 /dev/bus/0 \-d megaraid,2 \-a \-s S/../.././03 # # Three disks connected to an AacRaid controller # Start short self\-tests daily between 1\-2, 2\-3, and # 3\-4 am. /dev/sda \-d aacraid,0,0,66 \-a \-s S/../.././01 /dev/sda \-d aacraid,0,0,67 \-a \-s S/../.././02 /dev/sda \-d aacraid,0,0,68 \-a \-s S/../.././03 .\" %ENDIF OS Linux .\" %IF OS FreeBSD Linux # # Two SATA (not SAS) disks on a 3ware 9750 controller. # Start long self\-tests Sundays between midnight and # 1 am and 2\-3 am .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux # under Linux /dev/twl0 \-d 3ware,0 \-a \-s L/../../7/00 /dev/twl0 \-d 3ware,1 \-a \-s L/../../7/02 .\" %ENDIF OS Linux .\" %IF OS FreeBSD # under FreeBSD /dev/tws0 \-d 3ware,0 \-a \-s L/../../7/00 /dev/tws0 \-d 3ware,1 \-a \-s L/../../7/02 .\" %ENDIF OS FreeBSD .\" %IF OS FreeBSD Linux # # Three SATA disks on a HighPoint RocketRAID controller. # Start short self\-tests daily between 1\-2, 2\-3, and # 3\-4 am. .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux # under Linux /dev/sde \-d hpt,1/1 \-a \-s S/../.././01 /dev/sde \-d hpt,1/2 \-a \-s S/../.././02 /dev/sde \-d hpt,1/3 \-a \-s S/../.././03 .\" %ENDIF OS Linux .\" %IF OS FreeBSD # under FreeBSD /dev/hptrr \-d hpt,1/1 \-a \-s S/../.././01 /dev/hptrr \-d hpt,1/2 \-a \-s S/../.././02 /dev/hptrr \-d hpt,1/3 \-a \-s S/../.././03 .\" %ENDIF OS FreeBSD .\" %IF OS FreeBSD Linux # # Two SATA disks connected to a HighPoint RocketRAID # via a pmport device. Start long self\-tests Sundays # between midnight and 1 am and 2\-3 am. .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux # under Linux /dev/sde \-d hpt,1/4/1 \-a \-s L/../../7/00 /dev/sde \-d hpt,1/4/2 \-a \-s L/../../7/02 .\" %ENDIF OS Linux .\" %IF OS FreeBSD # under FreeBSD /dev/hptrr \-d hpt,1/4/1 \-a \-s L/../../7/00 /dev/hptrr \-d hpt,1/4/2 \-a \-s L/../../7/02 .\" %ENDIF OS FreeBSD .\" %IF OS FreeBSD Linux # # Three SATA disks connected to an Areca # RAID controller. Start long self\-tests Sundays # between midnight and 3 am. .\" %ENDIF OS FreeBSD Linux .\" %IF OS Linux # under Linux /dev/sg2 \-d areca,1 \-a \-s L/../../7/00 /dev/sg2 \-d areca,2 \-a \-s L/../../7/01 /dev/sg2 \-d areca,3 \-a \-s L/../../7/02 .\" %ENDIF OS Linux .\" %IF OS FreeBSD # under FreeBSD /dev/arcmsr0 \-d areca,1 \-a \-s L/../../7/00 /dev/arcmsr0 \-d areca,2 \-a \-s L/../../7/01 /dev/arcmsr0 \-d areca,3 \-a \-s L/../../7/02 .\" %ENDIF OS FreeBSD .\" %IF OS Linux # # Two SATA disks on an Intelliprop controller. # Start short self\-tests daily between 1\-2, 2\-3, and # 3\-4 am. /dev/sde \-d intelliprop,0+sat \-a \-s S/../.././01 /dev/sde \-d intelliprop,1+sat \-a \-s S/../.././02 .\" %ENDIF OS Linux # # The following line enables monitoring of the # ATA Error Log and the Self\-Test Error Log. # It also tracks changes in both Prefailure # and Usage Attributes, apart from Attributes # 9, 194, and 231, and shows continued lines: # /dev/sdd\ \-l\ error\ \e \ \ \ \ \ \-l\ selftest\ \e \ \ \ \ \ \-t\ \e\ \ \ \ \ \ \ \ \ # Attributes not tracked: \ \ \ \ \ \-I\ 194\ \e\ \ \ \ \ # temperature \ \ \ \ \ \-I\ 231\ \e\ \ \ \ \ # also temperature \ \ \ \ \ \-I\ 9\ \ \ \ \ \ \ \ \ # power\-on hours # ################################################ .Ve .Sp .SH DEVICESCAN If a 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. If .B DEVICESCAN is not followed by any Directives, then \*(Aq\-a\*(Aq will apply to all devices. .PP .B DEVICESCAN may optionally be followed by Directives that will apply to all devices that are found in the scan. For example .PP .Vb \ \ DEVICESCAN \-m root@example.com .Ve .PP will scan for all devices, and then monitor them. It will send one email warning per device for any problems that are found. .PP .Vb \ \ DEVICESCAN \-H \-m root@example.com .Ve .PP will do the same, but only monitors the SMART health status of the devices, rather than the default \*(Aq\-a\*(Aq. .PP Multiple \*(Aq\-d TYPE\*(Aq options may be specified with DEVICESCAN to combine the scan results of more than one TYPE. .PP Configuration entries for specific devices may precede the \fBDEVICESCAN\fP entry. For example .PP .Vb 4 \ \ DEFAULT \-m root@example.com \ \ /dev/sda \-s S/../.././02 \ \ /dev/sdc \-d ignore \ \ DEVICESCAN \-s L/../.././02 .Ve .PP will scan for all devices except /dev/sda and /dev/sdc, monitor them, and run a long test between 2\(en3 am every morning. Device /dev/sda will also be monitored, but only a short test will be run. Device /dev/sdc will be ignored. Warning emails will be sent for all monitored devices. .PP A device is ignored by DEVICESCAN if a configuration line with the same device name exists. .br [NEW EXPERIMENTAL SMARTD FEATURE] A device name is also ignored if another device with same identify information (vendor, model, firmware version, serial number, WWN) already exists. .Sp .SH DEFAULT SETTINGS If an entry in the configuration file starts with .B DEFAULT instead of a device name, then all directives in this entry are set as defaults for the next device entries. .PP This configuration: .PP .Vb 7 \ \ DEFAULT \-a \-R5! \-W 2,40,45 \-I 194 \-s L/../../7/00 \-m admin@example.com \ \ /dev/sda \ \ /dev/sdb \ \ /dev/sdc \ \ DEFAULT \-H \-m admin@example.com \ \ /dev/sdd \ \ /dev/sde \-d removable .Ve .PP has the same effect as: .PP .Vb 5 \ \ /dev/sda \-a \-R5! \-W 2,40,45 \-I 194 \-s L/../../7/00 \-m admin@example.com \ \ /dev/sdb \-a \-R5! \-W 2,40,45 \-I 194 \-s L/../../7/00 \-m admin@example.com \ \ /dev/sdc \-a \-R5! \-W 2,40,45 \-I 194 \-s L/../../7/00 \-m admin@example.com \ \ /dev/sdd \-H \-m admin@example.com \ \ /dev/sde \-d removable \-H \-m admin@example.com .Ve .Sp .SH CONFIGURATION FILE DIRECTIVES The following are the Directives that may appear following the device name or .B DEVICESCAN or .B DEFAULT 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. .PP .B For an ATA device, if no Directives appear, then the device will be monitored as if the \*(Aq\-a\*(Aq Directive (monitor all SMART properties) had been given. .PP .B If a SCSI disk is listed, it will be monitored at the maximum implemented level: roughly equivalent to using the \*(Aq\-H \-l selftest\*(Aq options for an ATA disk. So with the exception of \*(Aq\-d\*(Aq, \*(Aq\-m\*(Aq, \*(Aq\-l selftest\*(Aq, \*(Aq\-s\*(Aq, and \*(Aq\-M\*(Aq, the Directives below are ignored for SCSI disks. For SCSI disks, the \*(Aq\-m\*(Aq 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. .\" %IF OS FreeBSD Linux .PP .B If a 3ware controller is used then the corresponding SCSI (/dev/sd?) or character device (/dev/twe?, /dev/twa?, /dev/twl? or /dev/tws?) must be listed, along with the \*(Aq\-d 3ware,N\*(Aq 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). .PP .B If an Areca controller is used then the corresponding device (SCSI /dev/sg? on Linux or /dev/arcmsr0 on FreeBSD) must be listed, along with the \*(Aq\-d areca,N\*(Aq Directive (see below). The individual SATA disks hosted by the Areca controller appear to \fBsmartd\fP as normal ATA devices. Hence all the ATA directives can be used for these disks. Areca firmware version 1.46 or later which supports smartmontools must be used; Please see the \fBsmartctl\fP(8) man page for further details. .\" %ENDIF OS FreeBSD Linux .TP .B \-d TYPE Specifies the type of the device. The valid arguments to this directive are: .Sp .I auto \- attempt to guess the device type from the device name or from controller type info provided by the operating system or from a matching USB ID entry in the drive database. This is the default. .Sp .I ata \- the device type is ATA. This prevents \fBsmartd\fP from issuing SCSI commands to an ATA device. .Sp .\" %IF NOT OS Darwin .I scsi \- the device type is SCSI. This prevents \fBsmartd\fP from issuing ATA commands to a SCSI device. .Sp .\" %ENDIF NOT OS Darwin .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I nvme[,NSID] \- the device type is NVM Express (NVMe). The optional parameter NSID specifies the namespace id (in hex) passed to the driver. Use 0xffffffff for the broadcast namespace id. The default for NSID is the namespace id addressed by the device name. .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .\" %IF NOT OS Darwin .I sat[,auto][,N] \- the device type is SCSI to ATA Translation (SAT). This is for ATA disks that have a SCSI to ATA Translation Layer (SATL) between the disk and the operating system. SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and the other 16 bytes long. The default is the 16 byte variant which can be overridden with either \*(Aq\-d sat,12\*(Aq or \*(Aq\-d sat,16\*(Aq. .Sp If \*(Aq\-d sat,auto\*(Aq is specified, device type SAT (for ATA/SATA disks) is only used if the SCSI INQUIRY data reports a SATL (VENDOR: "ATA "). Otherwise device type SCSI (for SCSI/SAS disks) is used. .Sp .I usbcypress \- this device type is for ATA disks that are behind a Cypress USB to PATA bridge. This will use the ATACB proprietary scsi pass through command. The default SCSI operation code is 0x24, but although it can be overridden with \*(Aq\-d usbcypress,0xN\*(Aq, where N is the scsi operation code, you're running the risk of damage to the device or filesystems on it. .Sp .I usbjmicron[,p][,x][,PORT] \- this device type is for SATA disks that are behind a JMicron USB to PATA/SATA bridge. The 48-bit ATA commands (required e.g.\& for \*(Aq\-l xerror\*(Aq, see below) do not work with all of these bridges and are therefore disabled by default. These commands can be enabled by \*(Aq\-d usbjmicron,x\*(Aq. If two disks are connected to a bridge with two ports, an error message is printed if no PORT is specified. The port can be specified by \*(Aq\-d usbjmicron[,x],PORT\*(Aq where PORT is 0 (master) or 1 (slave). This is not necessary if the device uses a port multiplier to connect multiple disks to one port. The disks appear under separate /dev/ice names then. CAUTION: Specifying \*(Aq,x\*(Aq for a device which does not support it results in I/O errors and may disconnect the drive. The same applies if the specified PORT does not exist or is not connected to a disk. .Sp The Prolific PL2507/3507 USB bridges with older firmware support a pass-through command similar to JMicron and work with \*(Aq\-d usbjmicron,0\*(Aq. Newer Prolific firmware requires a modified command which can be selected by \*(Aq\-d usbjmicron,p\*(Aq. Note that this does not yet support the SMART status command. .Sp .I usbprolific \- this device type is for SATA disks that are behind a Prolific PL2571/2771/2773/2775 USB to SATA bridge. .Sp .I usbsunplus \- this device type is for SATA disks that are behind a SunplusIT USB to SATA bridge. .Sp .I sntjmicron[,NSID] \- [NEW EXPERIMENTAL SMARTD FEATURE] this device type is for NVMe disks that are behind a JMicron USB to NVMe bridge. The optional parameter NSID specifies the namespace id (in hex) passed to the driver. The default namespace id is the broadcast namespace id (0xffffffff). .Sp .\" %ENDIF NOT OS Darwin .\" %IF OS Linux .I marvell \- [Linux only] interact with SATA disks behind Marvell chip-set controllers (using the Marvell rather than libata driver). .Sp .I megaraid,N \- [Linux only] the device consists of one or more SCSI/SAS disks connected to a MegaRAID controller. The non-negative integer N (in the range of 0 to 127 inclusive) denotes which disk on the controller is monitored. This interface will also work for Dell PERC controllers. In log files and email messages this disk will be identified as megaraid_disk_XXX with XXX in the range from 000 to 127 inclusive. It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus number. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .\" %ENDIF OS Linux .\" %IF OS Linux Windows Cygwin .I aacraid,H,L,ID \- [Linux, Windows and Cygwin only] the device consists of one or more SCSI/SAS or SATA disks connected to an AacRaid controller. The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk on the controller is monitored. In log files and email messages this disk will be identified as aacraid_disk_HH_LL_ID. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .\" %ENDIF OS Linux Windows Cygwin .\" %IF OS FreeBSD Linux .I 3ware,N \- [FreeBSD and Linux only] 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 127 inclusive) denotes which disk on the controller is monitored. In log files and email messages this disk will be identified as 3ware_disk_XXX with XXX in the range from 000 to 127 inclusive. .Sp Note that while you may use \fBany\fP of the 3ware SCSI logical devices /dev/tw* 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(8) man page for further details. .Sp .\" %ENDIF OS FreeBSD Linux .\" %IF OS FreeBSD Linux Windows Cygwin .I areca,N \- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one or more SATA disks connected to an Areca SATA RAID controller. The positive integer N (in the range from 1 to 24 inclusive) denotes which disk on the controller is monitored. In log files and email messages this disk will be identified as areca_disk_XX with XX in the range from 01 to 24 inclusive. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .I areca,N/E \- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller. The integer N (range 1 to 128) denotes the channel (slot) and E (range 1 to 8) denotes the enclosure. Important: This requires Areca SAS controller firmware version 1.51 or later. .Sp .\" %ENDIF OS FreeBSD Linux Windows Cygwin .\" %IF OS FreeBSD Linux .I cciss,N \- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS or SATA disks connected to a cciss RAID controller. The non-negative integer N (in the range from 0 to 15 inclusive) denotes which disk on the controller is monitored. In log files and email messages this disk will be identified as cciss_disk_XX with XX in the range from 00 to 15 inclusive. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .I hpt,L/M/N \- [FreeBSD and Linux only] the device consists of one or more ATA disks connected to a HighPoint RocketRAID controller. The integer L is the controller id, the integer M is the channel number, and the integer N is the PMPort number if it is available. The allowed values of L are from 1 to 4 inclusive, M are from 1 to 128 inclusive and N from 1 to 4 if PMPort available. And also these values are limited by the model of the HighPoint RocketRAID controller. In log files and email messages this disk will be identified as hpt_X/X/X and X/X/X is the same as L/M/N, note if no N indicated, N set to the default value 1. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .\" %ENDIF OS FreeBSD Linux .I intelliprop,N[+TYPE] \- [NEW EXPERIMENTAL SMARTD FEATURE] the device consists of multiple ATA disks connected to an Intelliprop controller. The integer N is the port number from 0 to 3 of the ATA drive to be targeted. Please see the \fBsmartctl\fP(8) man page for further details. .Sp .I ignore \- the device specified by this configuration entry should be ignored. This allows to ignore specific devices which are detected by a following DEVICESCAN configuration line. It may also be used to temporary disable longer multi-line configuration entries. This Directive may be used in conjunction with the other \*(Aq\-d\*(Aq Directives. .Sp .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 \*(Aq\-d\*(Aq Directives. [NEW EXPERIMENTAL SMARTD FEATURE] This directive also suppresses warning emails and repeated log messages if the device is removed after startup. \fBWARNING: Removing a device and connecting a different one to same interface is not supported and may result in bogus warnings until smartd is restarted.\fP .TP .B \-n POWERMODE[,N][,q] [ATA only] This \*(Aqnocheck\*(Aq Directive is used to prevent a disk from being spun-up when it is periodically polled by \fBsmartd\fP. .Sp ATA disks have five different power states. In order of increasing power consumption they are: \*(AqOFF\*(Aq, \*(AqSLEEP\*(Aq, \*(AqSTANDBY\*(Aq, \*(AqIDLE\*(Aq, and \*(AqACTIVE\*(Aq. 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. .Sp 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. .Sp The \*(Aq\fB\-n\fP\*(Aq (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: .Sp .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. .Sp .I sleep \- check the device unless it is in SLEEP mode. .Sp .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. .Sp .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. .Sp Maximum number of skipped checks (in a row) can be specified by appending positive number \*(Aq,N\*(Aq to POWERMODE (like \*(Aq\-n standby,15\*(Aq). After N checks are skipped in a row, powermode is ignored and the check is performed anyway. .Sp When a periodic test is skipped, \fBsmartd\fP normally writes an informal log message. The message can be suppressed by appending the option \*(Aq,q\*(Aq to POWERMODE (like \*(Aq\-n standby,q\*(Aq). This prevents a laptop disk from spinning up due to this message. .Sp Both \*(Aq,N\*(Aq and \*(Aq,q\*(Aq can be specified together. .TP .B \-T TYPE Specifies how tolerant \fBsmartd\fP should be of SMART command failures. The valid arguments to this Directive are: .Sp .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. .Sp .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. [Please see the \fBsmartctl \-T\fP command-line option.] .TP .B \-o VALUE [ATA only] 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. .Sp The delay between tests is vendor-specific, but is typically four hours. .Sp 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 [ATA] Check the health status of the disk with the SMART RETURN STATUS command. If this command reports a failing health status, then disk failure is predicted in less than 24 hours, and a message at loglevel .B \*(AqLOG_CRIT\*(Aq will be logged to syslog. [Please see the .B smartctl \-H command-line option.] .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp [NVMe] Checks the "Critical Warning" byte from the SMART/Health Information log. If any warning bit is set, a message at loglevel \fB\*(AqLOG_CRIT\*(Aq\fP will be logged to syslog. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-l TYPE Reports increases in the number of errors in one of three SMART logs. The valid arguments to this Directive are: .Sp .I error \- [ATA] report if the number of ATA errors reported in the Summary SMART error log has increased since the last check. .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I error \- [NVMe] report if the "Number of Error Information Log Entries" from the SMART/Health Information log has increased since the last check. .Sp .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I xerror \- [ATA] report if the number of ATA errors reported in the Extended Comprehensive SMART error log has increased since the last check. .Sp If both \*(Aq\-l error\*(Aq and \*(Aq\-l xerror\*(Aq are specified, smartd checks the maximum of both values. .Sp [Please see the \fBsmartctl \-l xerror\fP command-line option.] .Sp .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .I xerror \- [NVMe] same as \*(Aq\-l error\*(Aq. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp .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 \*(Aq\-s\*(Aq Directive below. Self-Tests can also be run manually by using the \*(Aq\-t short\*(Aq and \fB\*(Aq\-t\ long\*(Aq\fP options of \fBsmartctl\fP and the results of the testing can be observed using the \fBsmartctl \*(Aq\-l\ selftest\*(Aq\fP command-line option. [Please see the \fBsmartctl \-l\fP and \fB\-t\fP command-line options.] .Sp [ATA only] Failed self-tests outdated by a newer successful extended self-test are ignored. The warning email counter is reset if the number of failed self tests dropped to 0. This typically happens when an extended self-test is run after all bad sectors have been reallocated. .Sp .I offlinests[,ns] \- [ATA only] report if the Offline Data Collection status has changed since the last check. The report will be logged as LOG_CRIT if the new status indicates an error. With some drives the status often changes, therefore \*(Aq\-l offlinests\*(Aq is not enabled by \*(Aq\-a\*(Aq Directive. .\" %IF NOT OS Cygwin Windows .\"! Appending ',ns' (no standby) to this directive is not implemented .\"! on OS_MAN_FILTER. .\" %ENDIF NOT OS Cygwin Windows .\" %IF OS Cygwin Windows .Sp [Windows and Cygwin only] If \*(Aq,ns\*(Aq (no standby) is appended to this directive, smartd disables system auto standby as long as an Offline Data Collection is in progress. See \*(Aq\-l selfteststs,ns\*(Aq below. .\" %ENDIF OS Cygwin Windows .Sp .I selfteststs[,ns] \- [ATA only] report if the Self-Test execution status has changed since the last check. The report will be logged as LOG_CRIT if the new status indicates an error. .\" %IF NOT OS Cygwin Windows .\"! Appending ',ns' (no standby) to this directive is not implemented .\"! on OS_MAN_FILTER. .\" %ENDIF NOT OS Cygwin Windows .\" %IF OS Cygwin Windows .Sp [Windows and Cygwin only] If \*(Aq,ns\*(Aq (no standby) is appended to this directive, smartd disables system auto standby as long as a Self-Test is in progress. This prevents that a Self-Test is aborted because the OS sets the system to a standby/sleep mode when idle. Smartd check interval (\*(Aq\-i\*(Aq option) should be shorter than the configured idle timeout. Auto standby is not disabled if the system is running on battery. .\" %ENDIF OS Cygwin Windows .Sp .I scterc,READTIME,WRITETIME \- [ATA only] sets the SCT Error Recovery Control settings to the specified values (deciseconds) when \fBsmartd\fP starts up and has no further effect. Values of 0 disable the feature, other values less than 65 are probably not supported. For RAID configurations, this is typically set to 70,70 deciseconds. [Please see the \fBsmartctl \-l scterc\fP command-line option.] .TP .B \-e NAME[,VALUE] Sets non-SMART device settings when \fBsmartd\fP starts up and has no further effect. [Please see the \fBsmartctl \-\-set\fP command-line option.] Valid arguments are: .Sp .I aam,[N|off] \- [ATA only] Sets the Automatic Acoustic Management (AAM) feature. .Sp .I apm,[N|off] \- [ATA only] Sets the Advanced Power Management (APM) feature. .Sp .I lookahead,[on|off] \- [ATA only] Sets the read look-ahead feature. .Sp .I security-freeze \- [ATA only] Sets ATA Security feature to frozen mode. .Sp .I standby,[N|off] \- [ATA only] Sets the standby (spindown) timer and places the drive in the IDLE mode. .Sp .I wcache,[on|off] \- [ATA only] Sets the volatile write cache feature. .Sp .I dsn,[on|off] \- [ATA only] [NEW EXPERIMENTAL SMARTD FEATURE] Sets the DSN feature. .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: \*(AqL\*(Aq for a \fBL\fPong Self-Test, \*(AqS\*(Aq for a \fBS\fPhort Self-Test, \*(AqC\*(Aq for a \fBC\fPonveyance Self-Test (ATA only), and \*(AqO\*(Aq 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. .Sp To run scheduled Selective Self-Tests, use \*(Aqn\*(Aq for \fBn\fPext span, \*(Aqr\*(Aq to \fBr\fPedo last span, or \*(Aqc\*(Aq to \fBc\fPontinue with next span or redo last span based on status of last test. The LBA range is based on the first span from the last test. See the \fBsmartctl \-t select,[next|redo|cont]\fP options for further info. .Sp Some disks (e.g.\& WD) do not preserve the selective self test log across power cycles. If state persistence (\*(Aq\-s\*(Aq option) is enabled, the last test span is preserved by smartd and used if (and only if) the selective self test log is empty. .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 1 am) 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\*(Aq.\*(Aq\fP matches any single character, and a parenthetical expression such as \fB\*(Aq(A|B|C)\*(Aq\fP denotes any one of the three possibilities \fBA\fP, \fBB\fP, or \fBC\fP. .Sp To schedule a short Self-Test between 2\(en3 am every morning, use: .br \fB \-s S/../.././02\fP .br To schedule a long Self-Test between 4\(en5 am every Sunday morning, use: .br \fB \-s L/../../7/04\fP .br To schedule a long Self-Test between 10\(en11 pm on the first and fifteenth day of each month, use: .br \fB \-s L/../(01|15)/./22\fP .br To schedule an Offline Immediate test after every midnight, 6 am, noon, and 6 pm, plus a Short Self-Test daily at 1\(en2 am and a Long Self-Test every Saturday at 3\(en4 am, use: .br \fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP .br If Long Self-Tests of a large disks take longer than the system uptime, a full disk test can be performed by several Selective Self-Tests. To setup a full test of a 1 TB disk within 20 days (one 50 GB span each day), run this command once: .nf smartctl \-t select,0\-99999999 /dev/sda .fi To run the next test spans on Monday\(enFriday between 12\(en13 am, run smartd with this directive: .br \fB \-s n/../../[1\-5]/12\fP .Sp 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 \*(Aq\-i\*(Aq 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. In this case the test will be run following the next device polling. .Sp 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. .Sp \fBsmartd\fP will not attempt to run \fBany\fP type of test if another test was already started or run in the same hour. .Sp To avoid performance problems during system boot, \fBsmartd\fP will not attempt to run any scheduled tests following the very first device polling (unless \*(Aq\-q onecheck\*(Aq is specified). .Sp Each time a test is run, \fBsmartd\fP will log an entry to SYSLOG. You can use these or the \*(Aq\-q showtests\*(Aq 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. .Sp If the scheduled tests are used in conjunction with state persistence (\*(Aq\-s\*(Aq option), smartd will also try to match the hours since last shutdown (or 90 days at most). If any test would have been started during downtime, the longest (see above) of these tests is run after second device polling. .Sp If the \*(Aq\-n\*(Aq directive is used and any test would have been started during disk standby time, the longest of these tests is run when the disk is active again. .Sp Unix users: please beware that the rules for extended regular expressions [\fBregex\fP(7)] are \fBnot\fP the same as the rules for file-name pattern matching by the shell [\fBglob\fP(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 \*(Aq\-H\*(Aq, \*(Aq\-l\*(Aq, \*(Aq\-f\*(Aq, \*(Aq\-C\*(Aq, or \*(Aq\-O\*(Aq 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 \*(Aq\-a\*(Aq Directive). .Sp To prevent your email in-box from getting filled up with warning messages, by default only a single warning and (depending on \*(Aq\-s\*(Aq option) daily reminder emails will be sent for each of the enabled alert types. See the \*(Aq\-M\*(Aq Directive below for details. .Sp 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). .Sp To test that email is being sent correctly, use the \*(Aq\-M test\*(Aq Directive described below to send one test email message on \fBsmartd\fP startup. .Sp By default, email is sent using the system \fBmail\fP(1) command. In order that \fBsmartd\fP find this command (normally /usr/bin/mail) the executable 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 \*(Aq\-M exec\*(Aq Directive below. .Sp .\" %IF OS Windows [Windows only] On Windows, the \*(Aq\fBBlat\fP\*(Aq mailer (<\fBhttp://blat.sourceforge.net/\fP>) is used by default. This mailer uses a different command line syntax, see \*(Aq\-M exec\*(Aq below. .Sp [NEW EXPERIMENTAL SMARTD FEATURE] If the file EXEDIR/smartd_mailer.conf.ps1 is present and \*(Aq\-M exec\*(Aq is not specified, the script smartd_mailer.ps1 is used instead. This script uses the Send-MailMessage cmdlet to send mail. See EXEDIR/smartd_mailer.conf.sample.ps1 for info about the format of the configuration file. .Sp .\" %ENDIF OS Windows Note also that there is a special argument .B which can be given to the \*(Aq\-m\*(Aq Directive in conjunction with the \*(Aq\-M exec\*(Aq Directive. Please see below for an explanation of its effect. .Sp 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 \*(Aq\-d\*(Aq flag, using the \*(Aq\-M test\*(Aq Directive described below. .\" %IF ENABLE_SMARTDPLUGINDIR .\" %IF NOT OS Windows .Sp If a word of the comma separated list has the form \*(Aq@plugin\*(Aq, a custom script /usr/local/etc/smartd_warning.d/plugin is run and the word is removed from the list before sending mail. The string \*(Aqplugin\*(Aq may be any valid name except \*(AqALL\*(Aq. If \*(Aq@ALL\*(Aq is specified, all scripts in /usr/local/etc/smartd_warning.d/* are run instead. This is handled by the script /usr/local/etc/smartd_warning.sh (see also \*(Aq\-M exec\*(Aq below). .\" %ENDIF NOT OS Windows .\" %ENDIF ENABLE_SMARTDPLUGINDIR .\" %IF OS Windows .Sp [Windows only] If one of the following words are used as the first address in the comma separated list, warning messages are sent via WTSSendMessage(). This displays message boxes on the desktops of the selected sessions. Address \*(Aq\fBconsole\fP\*(Aq specifies the console session only, \*(Aq\fBactive\fP\*(Aq specifies the console session and all active remote sessions, and \*(Aq\fBconnected\fP\*(Aq specifies the console session and all connected (active or waiting for login) remote sessions. This is handled by the script EXEDIR/smartd_warning.cmd which runs the tool EXEDIR/wtssendmsg.exe (see also \*(Aq\-M exec\*(Aq below). .\" %ENDIF OS Windows .TP .B \-M TYPE These Directives modify the behavior of the \fBsmartd\fP email warnings enabled with the \*(Aq\-m\*(Aq email Directive described above. These \*(Aq\-M\*(Aq Directives only work in conjunction with the \*(Aq\-m\*(Aq Directive and can not be used without it. .Sp 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. .Sp The valid arguments to the \-M Directive are (one of the following three): .Sp .I once \- send only one warning email for each type of disk problem detected. This is the default unless state persistence (\*(Aq\-s\*(Aq option) is enabled. .Sp .I daily \- send additional warning reminder emails, once per day, for each type of disk problem detected. This is the default if state persistence (\*(Aq\-s\*(Aq option) is enabled. .Sp .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. .Sp If a disk problem is no longer detected, the internal email counter is reset. If the problem reappears a new warning email is sent immediately. .Sp In addition, one may add zero or more of the following Directives: .Sp .I test \- send a single test email immediately upon \fBsmartd\fP startup. This allows one to verify that email is delivered correctly. Note that if this Directive is used, \fBsmartd\fP will also send the normal email warnings that were enabled with the \*(Aq\-m\*(Aq Directive, in addition to the single test email! .Sp .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. .\" %IF OS Windows .Sp [Windows only] The PATH may contain space characters. Then it must be included in double quotes. .\" %ENDIF OS Windows .Sp 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. .\" %IF NOT OS Windows Some sample scripts are included in /usr/local/share/doc/smartmontools/examplescripts/. .\" %ENDIF NOT OS Windows .Sp The exit 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. .Sp 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 \*(Aqmail\*(Aq (examples: /usr/local/bin/mail, mail). .IP \fBSMARTD_DEVICE\fP 4 is set to the device path (example: /dev/sda). .IP \fBSMARTD_DEVICETYPE\fP 4 is set to the device type specified by \*(Aq\-d\*(Aq directive or \*(Aqauto\*(Aq if none. .IP \fBSMARTD_DEVICESTRING\fP 4 is set to the device description. It starts with SMARTD_DEVICE and may be followed by an optional controller identification (example: /dev/sda [SAT]). The string may contain a space and is NOT quoted. .IP \fBSMARTD_DEVICEINFO\fP 4 is set to device identify information. It includes most of the info printed by \fBsmartctl \-i\fP but uses a brief single line format. This device info is also logged when \fBsmartd\fP starts up. The string contains space characters and is NOT quoted. .IP \fBSMARTD_FAILTYPE\fP 4 gives the reason for the warning or message email. The possible values that it takes and their meanings are: .br \fIEmailTest\fP: this is an email test message. .br \fIHealth\fP: the SMART health status indicates imminent failure. .br \fIUsage\fP: a usage Attribute has failed. .br \fISelfTest\fP: the number of self-test failures has increased. .br \fIErrorCount\fP: the number of errors in the ATA error log has increased. .br \fICurrentPendingSector\fP: one of more disk sectors could not be read and are marked to be reallocated (replaced with spare sectors). .br \fIOfflineUncorrectableSector\fP: during off-line testing, or self-testing, one or more disk sectors could not be read. .br \fITemperature\fP: Temperature reached critical limit (see \-W directive). .br \fIFailedHealthCheck\fP: the SMART health status command failed. .br \fIFailedReadSmartData\fP: the command to read SMART Attribute data failed. .br \fIFailedReadSmartErrorLog\fP: the command to read the SMART error log failed. .br \fIFailedReadSmartSelfTestLog\fP: the command to read the SMART self-test log failed. .br \fIFailedOpenDevice\fP: the open() command to the device failed. .IP \fBSMARTD_ADDRESS\fP 4 is determined by the address argument ADD of the \*(Aq\-m\*(Aq 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 shell script you may want to enclose it in double quotes. .\" %IF OS Windows .IP \fBSMARTD_ADDRCSV\fP 4 [Windows only] is set to a comma-separated list of the addresses from SMARTD_ADDRESS. .\" %ENDIF OS Windows .IP \fBSMARTD_MESSAGE\fP 4 is set to the one sentence summary warning email message string from \fBsmartd\fP. This message string contains space characters and is NOT quoted. So to use $SMARTD_MESSAGE in a shell script you should probably enclose it in double quotes. .\" %IF NOT OS Windows .IP \fBSMARTD_FULLMESSAGE\fP 4 is set to the contents of the entire email warning message string from \fBsmartd\fP. This message string contains space and return characters and is NOT quoted. So to use $SMARTD_FULLMESSAGE in a shell script you should probably enclose it in double quotes. .\" %ENDIF NOT OS Windows .\" %IF OS Windows .IP \fBSMARTD_FULLMSGFILE\fP 4 [Windows only] is the path to a temporary file containing the full message. The path may contain space characters and is NOT quoted. The file is created by the smartd_warning.cmd script and removed when the mailer or command exits. .\" %ENDIF OS Windows .IP \fBSMARTD_TFIRST\fP 4 is a text string giving the time and date at which the first problem of this type was reported. This text string contains space characters and no newlines, and is NOT quoted. For example: .br Sun Feb 9 14:58:19 2003 CST .IP \fBSMARTD_TFIRSTEPOCH\fP 4 is an integer, which is the unix epoch (number of seconds since Jan 1, 1970) for \fBSMARTD_TFIRST\fP. .IP \fBSMARTD_PREVCNT\fP 4 is an integer specifying the number of previous messages sent. It is set to \*(Aq0\*(Aq for the first message. .IP \fBSMARTD_NEXTDAYS\fP 4 is an integer specifying the number of days until the next message will be sent. It it set to empty on \*(Aq\-M once\*(Aq and set to \*(Aq1\*(Aq on \*(Aq\-M daily\*(Aq. .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 \& If the \*(Aq\-m ADD\*(Aq 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: .Vb 1 \ \ \-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS .Ve that would normally be provided to \*(Aqmail\*(Aq. Examples include: .br .B \-m user@home \-M exec /usr/bin/mail .br .B \-m admin@work \-M exec /usr/local/bin/mailto .br .B \-m root \-M exec /Example_1/shell/script/below .Sp .\" %IF OS Windows [Windows only] On Windows, the syntax of the \*(Aq\fBBlat\fP\*(Aq mailer is used (except for \*(Aq.ps1\*(Aq scripts): .Vb 1 \ \ \- \-q \-subject "%SMARTD_SUBJECT%" \-to %SMARTD_ADDRCSV% .Ve .Sp .\" %ENDIF OS Windows If the \*(Aq\-m ADD\*(Aq 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: .Vb 1 \ \ \-m \-M exec /Example_2/shell/script/below .Ve .Sp .\" %IF OS Windows [Windows only] [NEW EXPERIMENTAL SMARTD FEATURE] If a PATH with extension \*(Aq.ps1\*(Aq is specified with \*(Aq\-M exec\*(Aq, the script is run as follows with no STDIN, regardless of \*(Aq\-m ADD\*(Aq setting: .Vb 2 \ \ PowerShell -NoProfile -ExecutionPolicy Bypass ^ \ \ \ \ \ \ \ \ \ \ \ \ \ -Command ^& \*(Aq%SMARTD_MAILER%\*(Aq .Ve .Sp .\" %ENDIF OS Windows 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. .Sp Some EXAMPLES of scripts that can be used with the \*(Aq\-M exec\*(Aq Directive are given below. .\" %IF NOT OS Windows Some sample scripts are also included in /usr/local/share/doc/smartmontools/examplescripts/. .\" %ENDIF NOT OS Windows .Sp The executable is run by the script .\" %IF NOT OS Windows /usr/local/etc/smartd_warning.sh. .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: EXEDIR/smartd_warning.cmd) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! EXEDIR/smartd_warning.cmd. .\" %ENDIF OS Windows This script formats subject and full message based on SMARTD_MESSAGE and other environment variables set by \fBsmartd\fP. The environment variables .\" %IF NOT OS Windows SMARTD_SUBJECT and SMARTD_FULLMESSAGE .\" %ENDIF NOT OS Windows .\" %IF OS ALL (Windows: SMARTD_SUBJECT, SMARTD_FULLMSGFILE and SMARTD_ADDRCSV) .\" %ENDIF OS ALL .\" %IF OS Windows .\"! SMARTD_SUBJECT, SMARTD_FULLMSGFILE and SMARTD_ADDRCSV .\" %ENDIF OS Windows are set by the script before running the executable. .TP .B \-f [ATA only] Check for \*(Aqfailure\*(Aq 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 [ATA only] Report anytime that a Prefail Attribute has changed its value since the last check. [Please see the .B smartctl \-A command-line option.] .TP .B \-u [ATA only] Report anytime that a Usage Attribute has changed its value since the last check. [Please see the .B smartctl \-A command-line option.] .TP .B \-t [ATA only] Equivalent to turning on the two previous flags \*(Aq\-p\*(Aq and \*(Aq\-u\*(Aq. Tracks changes in \fIall\fP device Attributes (both Prefailure and Usage). [Please see the \fBsmartctl\fP \-A command-line option.] .TP .B \-i ID [ATA only] 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 \*(Aq\-f\*(Aq Directive and has no effect without it. .Sp 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 [ATA only] 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 \*(Aq\-p\*(Aq, \*(Aq\-u\*(Aq, and \*(Aq\-t\*(Aq tracking Directives and has no effect without one of them. .Sp 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[!] [ATA only] 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 \*(Aq\-p\*(Aq, \*(Aq\-u\*(Aq, and \*(Aq\-t\*(Aq tracking Directives and has no effect without one of them. This Directive may be given multiple times. .Sp A common use of this Directive is to track the device Temperature (often ID=194 or 231). .Sp If the optional flag \*(Aq!\*(Aq is appended, a change of the Normalized value is considered critical. The report will be logged as LOG_CRIT and a warning email will be sent if \*(Aq\-m\*(Aq is specified. .TP .B \-R ID[!] [ATA only] 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 \*(Aq\-p\*(Aq, \*(Aq\-u\*(Aq, and \*(Aq\-t\*(Aq tracking Directives and has no effect without one of them. This Directive may be given multiple times. .Sp If this Directive is given, it automatically implies the \*(Aq\-r\*(Aq Directive for the same Attribute, so that the Raw value of the Attribute is reported. .Sp 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. .Sp If the optional flag \*(Aq!\*(Aq is appended, a change of the Raw value is considered critical. The report will be logged as LOG_CRIT and a warning email will be sent if \*(Aq\-m\*(Aq is specified. An example is \*(Aq\-R 5!\*(Aq to warn when new sectors are reallocated. .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). If the name of this Attribute is changed by a \*(Aq\-v 197,FORMAT,NAME\*(Aq directive, the default is changed to \fB\-C 0\fP. .Sp If \*(Aq+\*(Aq is specified, a report is only printed if the number of sectors has increased between two check cycles. Some disks do not reset this attribute when a bad sector is reallocated. See also \*(Aq\-v 197,increasing\*(Aq below. .Sp The warning email counter is reset if the number of pending sectors dropped to 0. This typically happens when all pending sectors have been reallocated or could be read again. .Sp 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). If the name of this Attribute is changed by a \*(Aq\-v 198,FORMAT,NAME\*(Aq (except \*(Aq\-v 198,FORMAT,Offline_Scan_UNC_SectCt\*(Aq), directive, the default is changed to \fB\-U 0\fP. .Sp If \*(Aq+\*(Aq is specified, a report is only printed if the number of sectors has increased since the last check cycle. Some disks do not reset this attribute when a bad sector is reallocated. See also \*(Aq\-v 198,increasing\*(Aq below. .Sp The warning email counter is reset if the number of offline uncorrectable sectors dropped to 0. This typically happens when all offline uncorrectable sectors have been reallocated or could be read again. .Sp 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 \*(Aq\-C\*(Aq option for more details. .TP .B \-W DIFF[,INFO[,CRIT]] Report if the current temperature had changed by at least \fBDIFF\fP degrees since last report, or if new min or max temperature is detected. Report or Warn if the temperature is greater or equal than one of \fBINFO\fP or \fBCRIT\fP degrees Celsius. If the limit \fBCRIT\fP is reached, a message with loglevel \fB\*(AqLOG_CRIT\*(Aq\fP will be logged to syslog and a warning email will be send if \*(Aq\-m\*(Aq is specified. If only the limit \fBINFO\fP is reached, a message with loglevel \fB\*(AqLOG_INFO\*(Aq\fP will be logged. .Sp The warning email counter is reset if the temperature dropped below \fBINFO\fP or \fBCRIT\fP-5 if \fBINFO\fP is not specified. .Sp If this directive is used in conjunction with state persistence (\*(Aq\-s\*(Aq option), the min and max temperature values are preserved across boot cycles. The minimum temperature value is not updated during the first 30 minutes after startup. .Sp To disable any of the 3 reports, set the corresponding limit to 0. Trailing zero arguments may be omitted. By default, all temperature reports are disabled (\*(Aq\-W 0\*(Aq). .Sp To track temperature changes of at least 2 degrees, use: .br .B \-W 2 .br To log informal messages on temperatures of at least 40 degrees, use: .br .B \-W 0,40 .br For warning messages/mails on temperatures of at least 45 degrees, use: .br .B \-W 0,0,45 .br To combine all of the above reports, use: .br .B \-W 2,40,45 .Sp For ATA devices, smartd interprets Attribute 194 or 190 as Temperature Celsius by default. This can be changed to Attribute 9 or 220 by the drive database or by the \*(Aq\-v 9,temp\*(Aq or \*(Aq\-v 220,temp\*(Aq directive. .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .Sp For NVMe devices, smartd checks the maximum of the Composite Temperature value and all Temperature Sensor values reported by SMART/Health Information log. .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin .TP .B \-F TYPE [ATA only] Modifies the behavior of \fBsmartd\fP to compensate for some known and understood device firmware bug. This directive may be used multiple times. The valid arguments are: .Sp .I none \- Assume that the device firmware obeys the ATA specifications. This is the default, unless the device has presets for \*(Aq\-F\*(Aq in the drive database. Using this directive will override any preset values. .Sp .I nologdir \- Suppresses read attempts of SMART or GP Log Directory. Support for all standard logs is assumed without an actual check. Some Intel SSDs may freeze if log address 0 is read. .Sp .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. .Sp .I samsung2 \- In some Samsung disks the number of ATA errors reported is byte swapped. Enabling this option tells \fBsmartd\fP to evaluate this quantity in byte-reversed order. .Sp .I samsung3 \- Some Samsung disks (at least SP2514N with Firmware VF100-37) report a self-test still in progress with 0% remaining when the test was already completed. If this directive is specified, \fBsmartd\fP will not skip the next scheduled self-test (see Directive \*(Aq\-s\*(Aq above) in this case. .Sp .I xerrorlba \- This only affects \fBsmartctl\fP. .Sp [Please see the \fBsmartctl \-F\fP command-line option.] .TP .B \-v ID,FORMAT[:BYTEORDER][,NAME] [ATA only] Sets a vendor-specific raw value print FORMAT, an optional BYTEORDER and an optional NAME for Attribute ID. This directive may be used multiple times. Please see \fBsmartctl \-v\fP command-line option for further details. .Sp The following arguments affect smartd warning output: .Sp .I 197,increasing \- Raw Attribute number 197 (Current Pending Sector Count) is not reset if uncorrectable sectors are reallocated. This sets \*(Aq\-C 197+\*(Aq if no other \*(Aq\-C\*(Aq directive is specified. .Sp .I 198,increasing \- Raw Attribute number 198 (Offline Uncorrectable Sector Count) is not reset if uncorrectable sectors are reallocated. This sets \*(Aq\-U 198+\*(Aq if no other \*(Aq\-U\*(Aq directive is specified. .TP .B \-P TYPE [ATA only] Specifies whether \fBsmartd\fP should use any preset options that are available for this drive. The valid arguments to this Directive are: .Sp .I use \- use any presets that are available for this drive. This is the default. .Sp .I ignore \- do not use any presets for this drive. .Sp .I show \- show the presets listed for this drive in the database. .Sp .I showall \- show the presets that are available for all drives and then exit. .Sp [Please see the .B smartctl \-P command-line option.] .TP .B \-a Equivalent to turning on all of the following Directives: .B \*(Aq\-H\*(Aq to check the SMART health status, .B \*(Aq\-f\*(Aq to report failures of Usage (rather than Prefail) Attributes, .B \*(Aq\-t\*(Aq to track changes in both Prefailure and Usage Attributes, .B \*(Aq\-l\ error\*(Aq to report increases in the number of ATA errors, .B \*(Aq\-l\ selftest\*(Aq to report increases in the number of Self-Test Log errors, .B \*(Aq\-l\ selfteststs\*(Aq to report changes of Self-Test execution status, .B \*(Aq\-C 197\*(Aq to report nonzero values of the current pending sector count, and .B \*(Aq\-U 198\*(Aq to report nonzero values of the offline pending sector count. .Sp 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: .br \fB\-H \-l selftest \-l error \-f\fP. .br If you want more frequent information, use: \fB\-a\fP. .Sp .TP .B EXAMPLES OF SHELL SCRIPTS FOR \*(Aq\-M exec\*(Aq These are two examples of shell scripts that can be used with the \*(Aq\-M exec PATH\*(Aq Directive described previously. The paths to these scripts and similar executables is the PATH argument to the \*(Aq\-M exec PATH\*(Aq Directive. .Sp Example 1: This script is for use with \*(Aq\-m ADDRESS \-M exec PATH\*(Aq. It appends the output of .B smartctl \-a to the output of the smartd email warning message and sends it to ADDRESS. .Sp .Vb 4 #! /bin/sh # 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 \e $SMARTD_DEVICE >> /root/msg # Now email the message to the user at address ADD: /usr/bin/mail \-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS \e < /root/msg .Ve .Sp Example 2: This script is for use with \*(Aq\-m \-M exec PATH\*(Aq. It warns all users about a disk problem, waits 30 seconds, and then powers down the machine. .Sp .Vb 4 #! /bin/sh # Warn all users of a problem wall < * Copyright (C) 2008 Oliver Bock * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ // unconditionally included files #include #include #include #include // umask #include #include #include #include #include #include #include #include #include #include #include #include #include #include // std::replace() // conditionally included files #ifndef _WIN32 #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef _WIN32 #include "os_win32/popen.h" // popen/pclose() #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__ #include // setmode() #endif // __CYGWIN__ #ifdef HAVE_LIBCAP_NG #include #endif // LIBCAP_NG #ifdef HAVE_LIBSYSTEMD #include #endif // HAVE_LIBSYSTEMD // locally included files #include "atacmds.h" #include "dev_interface.h" #include "knowndrives.h" #include "scsicmds.h" #include "nvmecmds.h" #include "utility.h" #ifdef _WIN32 // fork()/signal()/initd simulation for native Windows #include "os_win32/daemon_win32.h" // daemon_main/detach/signal() #define strsignal daemon_strsignal #define sleep daemon_sleep // SIGQUIT does not exist, CONTROL-Break signals SIGBREAK. #define SIGQUIT SIGBREAK #define SIGQUIT_KEYNAME "CONTROL-Break" #else // _WIN32 #define SIGQUIT_KEYNAME "CONTROL-\\" #endif // _WIN32 const char * smartd_cpp_cvsid = "$Id: smartd.cpp 4864 2018-12-20 13:02:39Z chrfranke $" CONFIG_H_CVSID; extern "C" { typedef void (*signal_handler_type)(int); } static void set_signal_if_not_ignored(int sig, signal_handler_type handler) { #if defined(_WIN32) // signal() emulation daemon_signal(sig, handler); #elif defined(HAVE_SIGACTION) // SVr4, POSIX.1-2001, POSIX.1-2008 struct sigaction sa; sa.sa_handler = SIG_DFL; sigaction(sig, (struct sigaction *)0, &sa); if (sa.sa_handler == SIG_IGN) return; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sa.sa_flags = SA_RESTART; // BSD signal() semantics sigaction(sig, &sa, (struct sigaction *)0); #elif defined(HAVE_SIGSET) // SVr4, POSIX.1-2001, obsoleted in POSIX.1-2008 if (sigset(sig, handler) == SIG_IGN) sigset(sig, SIG_IGN); #else // POSIX.1-2001, POSIX.1-2008, C89, C99, undefined semantics. // Important: BSD semantics is required. Traditional signal() // resets the handler to SIG_DFL after the first signal is caught. if (signal(sig, handler) == SIG_IGN) signal(sig, SIG_IGN); #endif } using namespace smartmontools; // smartd 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 // command-line: 1=debug mode, 2=print presets static unsigned char debugmode = 0; // command-line: how long to sleep between checks #define CHECKTIME 1800 static int checktime=CHECKTIME; // command-line: name of PID file (empty for no pid file) static std::string pid_file; // command-line: path prefix of persistent state file, empty if no persistence. static std::string state_path_prefix #ifdef SMARTMONTOOLS_SAVESTATES = SMARTMONTOOLS_SAVESTATES #endif ; // command-line: path prefix of attribute log file, empty if no logs. static std::string attrlog_path_prefix #ifdef SMARTMONTOOLS_ATTRIBUTELOG = SMARTMONTOOLS_ATTRIBUTELOG #endif ; // configuration file name static const char * configfile; // configuration file "name" if read from stdin static const char * const configfile_stdin = ""; // path of alternate configuration file static std::string configfile_alt; // warning script file static std::string warning_script; // command-line: when should we exit? enum quit_t { QUIT_NODEV, QUIT_NODEVSTARTUP, QUIT_NEVER, QUIT_ONECHECK, QUIT_SHOWTESTS, QUIT_ERRORS }; static quit_t quit = QUIT_NODEV; // command-line; this is the default syslog(3) log facility to use. static int facility=LOG_DAEMON; #ifndef _WIN32 // command-line: fork into background? static bool do_fork=true; #endif // TODO: This smartctl only variable is also used in some os_*.cpp unsigned char failuretest_permissive = 0; // set to one if we catch a USR1 (check devices now) static volatile int caughtsigUSR1=0; #ifdef _WIN32 // set to one if we catch a USR2 (toggle debug mode) static 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). static volatile int caughtsigHUP=0; // set to signal value if we catch INT, QUIT, or TERM static volatile int caughtsigEXIT=0; // This function prints either to stdout or to the syslog as needed. static void PrintOut(int priority, const char *fmt, ...) __attribute_format_printf(2, 3); #ifdef HAVE_LIBSYSTEMD // systemd notify support static bool notify_enabled = false; static inline void notify_init() { if (!getenv("NOTIFY_SOCKET")) return; notify_enabled = true; } static inline bool notify_post_init() { if (!notify_enabled) return true; if (do_fork) { PrintOut(LOG_CRIT, "Option -n (--no-fork) is required if 'Type=notify' is set.\n"); return false; } return true; } static void notify_msg(const char * msg, bool ready = false) { if (!notify_enabled) return; if (debugmode) { pout("sd_notify(0, \"%sSTATUS=%s\")\n", (ready ? "READY=1\\n" : ""), msg); return; } sd_notifyf(0, "%sSTATUS=%s", (ready ? "READY=1\n" : ""), msg); } static void notify_check(int numdev) { if (!notify_enabled) return; char msg[32]; snprintf(msg, sizeof(msg), "Checking %d device%s ...", numdev, (numdev != 1 ? "s" : "")); notify_msg(msg); } static void notify_wait(time_t wakeuptime, int numdev) { if (!notify_enabled) return; char ts[16], msg[64]; strftime(ts, sizeof(ts), "%H:%M:%S", localtime(&wakeuptime)); snprintf(msg, sizeof(msg), "Next check of %d device%s will start at %s", numdev, (numdev != 1 ? "s" : ""), ts); static bool ready = true; // first call notifies READY=1 notify_msg(msg, ready); ready = false; } static void notify_exit(int status) { if (!notify_enabled) return; const char * msg; switch (status) { case 0: msg = "Exiting ..."; break; case EXIT_BADCMD: msg = "Error in command line (see SYSLOG)"; break; case EXIT_BADCONF: case EXIT_NOCONF: case EXIT_READCONF: msg = "Error in config file (see SYSLOG)"; break; case EXIT_BADDEV: msg = "Unable to register a device (see SYSLOG)"; break; case EXIT_NODEV: msg = "No devices to monitor"; break; default: msg = "Error (see SYSLOG)"; break; } notify_msg(msg); } #else // HAVE_LIBSYSTEMD // No systemd notify support static inline bool notify_post_init() { #ifdef __linux__ if (getenv("NOTIFY_SOCKET")) { PrintOut(LOG_CRIT, "This version of smartd was build without 'Type=notify' support.\n"); return false; } #endif return true; } static inline void notify_init() { } static inline void notify_msg(const char *) { } static inline void notify_check(int) { } static inline void notify_wait(time_t, int) { } static inline void notify_exit(int) { } #endif // HAVE_LIBSYSTEMD // Attribute monitoring flags. // See monitor_attr_flags below. enum { MONITOR_IGN_FAILUSE = 0x01, MONITOR_IGNORE = 0x02, MONITOR_RAW_PRINT = 0x04, MONITOR_RAW = 0x08, MONITOR_AS_CRIT = 0x10, MONITOR_RAW_AS_CRIT = 0x20, }; // Array of flags for each attribute. class attribute_flags { public: attribute_flags() { memset(m_flags, 0, sizeof(m_flags)); } bool is_set(int id, unsigned char flag) const { return (0 < id && id < (int)sizeof(m_flags) && (m_flags[id] & flag)); } void set(int id, unsigned char flags) { if (0 < id && id < (int)sizeof(m_flags)) m_flags[id] |= flags; } private: unsigned char m_flags[256]; }; /// Configuration data for a device. Read from smartd.conf. /// Supports copy & assignment and is compatible with STL containers. struct dev_config { int lineno; // Line number of entry in file std::string name; // Device name (with optional extra info) std::string dev_name; // Device name (plain, for SMARTD_DEVICE variable) std::string dev_type; // Device type argument from -d directive, empty if none std::string dev_idinfo; // Device identify info for warning emails std::string state_file; // Path of the persistent state file, empty if none std::string attrlog_file; // Path of the persistent attrlog file, empty if none bool ignore; // Ignore this entry bool id_is_unique; // True if dev_idinfo is unique (includes S/N or WWN) bool smartcheck; // Check SMART status bool usagefailed; // Check for failed Usage Attributes bool prefail; // Track changes in Prefail Attributes bool usage; // Track changes in Usage Attributes bool selftest; // Monitor number of selftest errors bool errorlog; // Monitor number of ATA errors bool xerrorlog; // Monitor number of ATA errors (Extended Comprehensive error log) bool offlinests; // Monitor changes in offline data collection status bool offlinests_ns; // Disable auto standby if in progress bool selfteststs; // Monitor changes in self-test execution status bool selfteststs_ns; // Disable auto standby if in progress bool permissive; // Ignore failed SMART commands char autosave; // 1=disable, 2=enable Autosave Attributes char autoofflinetest; // 1=disable, 2=enable Auto Offline Test firmwarebug_defs firmwarebugs; // -F directives from drivedb or smartd.conf bool ignorepresets; // Ignore database of -v options bool showpresets; // Show database entry for this device bool removable; // Device may disappear (not be present) char powermode; // skip check, if disk in idle or standby mode bool powerquiet; // skip powermode 'skipping checks' message int powerskipmax; // how many times can be check skipped unsigned char tempdiff; // Track Temperature changes >= this limit unsigned char tempinfo, tempcrit; // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail regular_expression test_regex; // Regex for scheduled testing // Configuration of email warning messages std::string emailcmdline; // script to execute, empty if no messages std::string emailaddress; // email address, or empty unsigned char emailfreq; // Emails once (1) daily (2) diminishing (3) bool emailtest; // Send test email? // ATA ONLY int dev_rpm; // rotation rate, 0 = unknown, 1 = SSD, >1 = HDD int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management int set_lookahead; // disable(-1), enable(1) read look-ahead int set_standby; // set(1..255->0..254) standby timer bool set_security_freeze; // Freeze ATA security int set_wcache; // disable(-1), enable(1) write cache int set_dsn; // disable(0x2), enable(0x1) DSN bool sct_erc_set; // set SCT ERC to: unsigned short sct_erc_readtime; // ERC read time (deciseconds) unsigned short sct_erc_writetime; // ERC write time (deciseconds) unsigned char curr_pending_id; // ID of current pending sector count, 0 if none unsigned char offl_pending_id; // ID of offline uncorrectable sector count, 0 if none bool curr_pending_incr, offl_pending_incr; // True if current/offline pending values increase bool curr_pending_set, offl_pending_set; // True if '-C', '-U' set in smartd.conf attribute_flags monitor_attr_flags; // MONITOR_* flags for each attribute ata_vendor_attr_defs attribute_defs; // -v options dev_config(); }; dev_config::dev_config() : lineno(0), ignore(false), id_is_unique(false), smartcheck(false), usagefailed(false), prefail(false), usage(false), selftest(false), errorlog(false), xerrorlog(false), offlinests(false), offlinests_ns(false), selfteststs(false), selfteststs_ns(false), permissive(false), autosave(0), autoofflinetest(0), ignorepresets(false), showpresets(false), removable(false), powermode(0), powerquiet(false), powerskipmax(0), tempdiff(0), tempinfo(0), tempcrit(0), emailfreq(0), emailtest(false), dev_rpm(0), set_aam(0), set_apm(0), set_lookahead(0), set_standby(0), set_security_freeze(false), set_wcache(0), set_dsn(0), sct_erc_set(false), sct_erc_readtime(0), sct_erc_writetime(0), curr_pending_id(0), offl_pending_id(0), curr_pending_incr(false), offl_pending_incr(false), curr_pending_set(false), offl_pending_set(false) { } // Number of allowed mail message types static const int SMARTD_NMAIL = 13; // Type for '-M test' mails (state not persistent) static const int MAILTYPE_TEST = 0; // TODO: Add const or enum for all mail types. struct mailinfo { 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() : logged(0), firstsent(0), lastsent(0) { } }; /// Persistent state data for a device. struct persistent_dev_state { unsigned char tempmin, tempmax; // Min/Max Temperatures unsigned char selflogcount; // total number of self-test errors unsigned short selfloghour; // lifetime hours of last self-test error time_t scheduled_test_next_check; // Time of next check for scheduled self-tests uint64_t selective_test_last_start; // Start LBA of last scheduled selective self-test uint64_t selective_test_last_end; // End LBA of last scheduled selective self-test mailinfo maillog[SMARTD_NMAIL]; // log info on when mail sent // ATA ONLY int ataerrorcount; // Total number of ATA errors // Persistent part of ata_smart_values: struct ata_attribute { unsigned char id; unsigned char val; unsigned char worst; // Byte needed for 'raw64' attribute only. uint64_t raw; unsigned char resvd; ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { } }; ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES]; // SCSI ONLY struct scsi_error_counter_t { struct scsiErrorCounter errCounter; unsigned char found; scsi_error_counter_t() : found(0) { memset(&errCounter, 0, sizeof(errCounter)); } }; scsi_error_counter_t scsi_error_counters[3]; struct scsi_nonmedium_error_t { struct scsiNonMediumError nme; unsigned char found; scsi_nonmedium_error_t() : found(0) { memset(&nme, 0, sizeof(nme)); } }; scsi_nonmedium_error_t scsi_nonmedium_error; // NVMe only uint64_t nvme_err_log_entries; persistent_dev_state(); }; persistent_dev_state::persistent_dev_state() : tempmin(0), tempmax(0), selflogcount(0), selfloghour(0), scheduled_test_next_check(0), selective_test_last_start(0), selective_test_last_end(0), ataerrorcount(0), nvme_err_log_entries(0) { } /// Non-persistent state data for a device. struct temp_dev_state { bool must_write; // true if persistent part should be written bool not_cap_offline; // true == not capable of offline testing bool not_cap_conveyance; bool not_cap_short; bool not_cap_long; bool not_cap_selective; unsigned char temperature; // last recorded Temperature (in Celsius) time_t tempmin_delay; // time where Min Temperature tracking will start bool removed; // true if open() failed for removable device bool powermodefail; // true if power mode check failed int powerskipcnt; // Number of checks skipped due to idle or standby mode int lastpowermodeskipped; // the last power mode that was skipped // SCSI ONLY unsigned char SmartPageSupported; // has log sense IE page (0x2f) unsigned char TempPageSupported; // has log sense temperature page (0xd) unsigned char ReadECounterPageSupported; unsigned char WriteECounterPageSupported; unsigned char VerifyECounterPageSupported; unsigned char NonMediumErrorPageSupported; unsigned char SuppressReport; // minimize nuisance reports unsigned char modese_len; // mode sense/select cmd len: 0 (don't // know yet) 6 or 10 // ATA ONLY uint64_t num_sectors; // Number of sectors ata_smart_values smartval; // SMART data ata_smart_thresholds_pvt smartthres; // SMART thresholds bool offline_started; // true if offline data collection was started bool selftest_started; // true if self-test was started temp_dev_state(); }; temp_dev_state::temp_dev_state() : must_write(false), not_cap_offline(false), not_cap_conveyance(false), not_cap_short(false), not_cap_long(false), not_cap_selective(false), temperature(0), tempmin_delay(0), removed(false), powermodefail(false), powerskipcnt(0), lastpowermodeskipped(0), SmartPageSupported(false), TempPageSupported(false), ReadECounterPageSupported(false), WriteECounterPageSupported(false), VerifyECounterPageSupported(false), NonMediumErrorPageSupported(false), SuppressReport(false), modese_len(0), num_sectors(0), offline_started(false), selftest_started(false) { memset(&smartval, 0, sizeof(smartval)); memset(&smartthres, 0, sizeof(smartthres)); } /// Runtime state data for a device. struct dev_state : public persistent_dev_state, public temp_dev_state { void update_persistent_state(); void update_temp_state(); }; /// Container for configuration info for each device. typedef std::vector dev_config_vector; /// Container for state info for each device. typedef std::vector dev_state_vector; // Copy ATA attributes to persistent state. void dev_state::update_persistent_state() { for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const ata_smart_attribute & ta = smartval.vendor_attributes[i]; ata_attribute & pa = ata_attributes[i]; pa.id = ta.id; if (ta.id == 0) { pa.val = pa.worst = 0; pa.raw = 0; continue; } pa.val = ta.current; pa.worst = ta.worst; pa.raw = ta.raw[0] | ( ta.raw[1] << 8) | ( ta.raw[2] << 16) | ((uint64_t)ta.raw[3] << 24) | ((uint64_t)ta.raw[4] << 32) | ((uint64_t)ta.raw[5] << 40); pa.resvd = ta.reserv; } } // Copy ATA from persistent to temp state. void dev_state::update_temp_state() { for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const ata_attribute & pa = ata_attributes[i]; ata_smart_attribute & ta = smartval.vendor_attributes[i]; ta.id = pa.id; if (pa.id == 0) { ta.current = ta.worst = 0; memset(ta.raw, 0, sizeof(ta.raw)); continue; } ta.current = pa.val; ta.worst = pa.worst; ta.raw[0] = (unsigned char) pa.raw; ta.raw[1] = (unsigned char)(pa.raw >> 8); ta.raw[2] = (unsigned char)(pa.raw >> 16); ta.raw[3] = (unsigned char)(pa.raw >> 24); ta.raw[4] = (unsigned char)(pa.raw >> 32); ta.raw[5] = (unsigned char)(pa.raw >> 40); ta.reserv = pa.resvd; } } // Parse a line from a state file. static bool parse_dev_state_line(const char * line, persistent_dev_state & state) { static const regular_expression regex( "^ *" "((temperature-min)" // (1 (2) "|(temperature-max)" // (3) "|(self-test-errors)" // (4) "|(self-test-last-err-hour)" // (5) "|(scheduled-test-next-check)" // (6) "|(selective-test-last-start)" // (7) "|(selective-test-last-end)" // (8) "|(ata-error-count)" // (9) "|(mail\\.([0-9]+)\\." // (10 (11) "((count)" // (12 (13) "|(first-sent-time)" // (14) "|(last-sent-time)" // (15) ")" // 12) ")" // 10) "|(ata-smart-attribute\\.([0-9]+)\\." // (16 (17) "((id)" // (18 (19) "|(val)" // (20) "|(worst)" // (21) "|(raw)" // (22) "|(resvd)" // (23) ")" // 18) ")" // 16) "|(nvme-err-log-entries)" // (24) ")" // 1) " *= *([0-9]+)[ \n]*$" // (25) ); const int nmatch = 1+25; regular_expression::match_range match[nmatch]; if (!regex.execute(line, nmatch, match)) return false; if (match[nmatch-1].rm_so < 0) return false; uint64_t val = strtoull(line + match[nmatch-1].rm_so, (char **)0, 10); int m = 1; if (match[++m].rm_so >= 0) state.tempmin = (unsigned char)val; else if (match[++m].rm_so >= 0) state.tempmax = (unsigned char)val; else if (match[++m].rm_so >= 0) state.selflogcount = (unsigned char)val; else if (match[++m].rm_so >= 0) state.selfloghour = (unsigned short)val; else if (match[++m].rm_so >= 0) state.scheduled_test_next_check = (time_t)val; else if (match[++m].rm_so >= 0) state.selective_test_last_start = val; else if (match[++m].rm_so >= 0) state.selective_test_last_end = val; else if (match[++m].rm_so >= 0) state.ataerrorcount = (int)val; else if (match[m+=2].rm_so >= 0) { int i = atoi(line+match[m].rm_so); if (!(0 <= i && i < SMARTD_NMAIL)) return false; if (i == MAILTYPE_TEST) // Don't suppress test mails return true; if (match[m+=2].rm_so >= 0) state.maillog[i].logged = (int)val; else if (match[++m].rm_so >= 0) state.maillog[i].firstsent = (time_t)val; else if (match[++m].rm_so >= 0) state.maillog[i].lastsent = (time_t)val; else return false; } else if (match[m+=5+1].rm_so >= 0) { int i = atoi(line+match[m].rm_so); if (!(0 <= i && i < NUMBER_ATA_SMART_ATTRIBUTES)) return false; if (match[m+=2].rm_so >= 0) state.ata_attributes[i].id = (unsigned char)val; else if (match[++m].rm_so >= 0) state.ata_attributes[i].val = (unsigned char)val; else if (match[++m].rm_so >= 0) state.ata_attributes[i].worst = (unsigned char)val; else if (match[++m].rm_so >= 0) state.ata_attributes[i].raw = val; else if (match[++m].rm_so >= 0) state.ata_attributes[i].resvd = (unsigned char)val; else return false; } else if (match[m+7].rm_so >= 0) state.nvme_err_log_entries = val; else return false; return true; } // Read a state file. static bool read_dev_state(const char * path, persistent_dev_state & state) { stdio_file f(path, "r"); if (!f) { if (errno != ENOENT) pout("Cannot read state file \"%s\"\n", path); return false; } #ifdef __CYGWIN__ setmode(fileno(f), O_TEXT); // Allow files with \r\n #endif persistent_dev_state new_state; int good = 0, bad = 0; char line[256]; while (fgets(line, sizeof(line), f)) { const char * s = line + strspn(line, " \t"); if (!*s || *s == '#') continue; if (!parse_dev_state_line(line, new_state)) bad++; else good++; } if (bad) { if (!good) { pout("%s: format error\n", path); return false; } pout("%s: %d invalid line(s) ignored\n", path, bad); } // This sets the values missing in the file to 0. state = new_state; return true; } static void write_dev_state_line(FILE * f, const char * name, uint64_t val) { if (val) fprintf(f, "%s = %" PRIu64 "\n", name, val); } static void write_dev_state_line(FILE * f, const char * name1, int id, const char * name2, uint64_t val) { if (val) fprintf(f, "%s.%d.%s = %" PRIu64 "\n", name1, id, name2, val); } // Write a state file static bool write_dev_state(const char * path, const persistent_dev_state & state) { // Rename old "file" to "file~" std::string pathbak = path; pathbak += '~'; unlink(pathbak.c_str()); rename(path, pathbak.c_str()); stdio_file f(path, "w"); if (!f) { pout("Cannot create state file \"%s\"\n", path); return false; } fprintf(f, "# smartd state file\n"); write_dev_state_line(f, "temperature-min", state.tempmin); write_dev_state_line(f, "temperature-max", state.tempmax); write_dev_state_line(f, "self-test-errors", state.selflogcount); write_dev_state_line(f, "self-test-last-err-hour", state.selfloghour); write_dev_state_line(f, "scheduled-test-next-check", state.scheduled_test_next_check); write_dev_state_line(f, "selective-test-last-start", state.selective_test_last_start); write_dev_state_line(f, "selective-test-last-end", state.selective_test_last_end); int i; for (i = 0; i < SMARTD_NMAIL; i++) { if (i == MAILTYPE_TEST) // Don't suppress test mails continue; const mailinfo & mi = state.maillog[i]; if (!mi.logged) continue; write_dev_state_line(f, "mail", i, "count", mi.logged); write_dev_state_line(f, "mail", i, "first-sent-time", mi.firstsent); write_dev_state_line(f, "mail", i, "last-sent-time", mi.lastsent); } // ATA ONLY write_dev_state_line(f, "ata-error-count", state.ataerrorcount); for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i]; if (!pa.id) continue; write_dev_state_line(f, "ata-smart-attribute", i, "id", pa.id); write_dev_state_line(f, "ata-smart-attribute", i, "val", pa.val); write_dev_state_line(f, "ata-smart-attribute", i, "worst", pa.worst); write_dev_state_line(f, "ata-smart-attribute", i, "raw", pa.raw); write_dev_state_line(f, "ata-smart-attribute", i, "resvd", pa.resvd); } // NVMe only write_dev_state_line(f, "nvme-err-log-entries", state.nvme_err_log_entries); return true; } // Write to the attrlog file static bool write_dev_attrlog(const char * path, const dev_state & state) { stdio_file f(path, "a"); if (!f) { pout("Cannot create attribute log file \"%s\"\n", path); return false; } time_t now = time(0); struct tm * tms = gmtime(&now); fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;", 1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday, tms->tm_hour, tms->tm_min, tms->tm_sec); // ATA ONLY for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i]; if (!pa.id) continue; fprintf(f, "\t%d;%d;%" PRIu64 ";", pa.id, pa.val, pa.raw); } // SCSI ONLY const struct scsiErrorCounter * ecp; const char * pageNames[3] = {"read", "write", "verify"}; for (int k = 0; k < 3; ++k) { if ( !state.scsi_error_counters[k].found ) continue; ecp = &state.scsi_error_counters[k].errCounter; fprintf(f, "\t%s-corr-by-ecc-fast;%" PRIu64 ";" "\t%s-corr-by-ecc-delayed;%" PRIu64 ";" "\t%s-corr-by-retry;%" PRIu64 ";" "\t%s-total-err-corrected;%" PRIu64 ";" "\t%s-corr-algorithm-invocations;%" PRIu64 ";" "\t%s-gb-processed;%.3f;" "\t%s-total-unc-errors;%" PRIu64 ";", pageNames[k], ecp->counter[0], pageNames[k], ecp->counter[1], pageNames[k], ecp->counter[2], pageNames[k], ecp->counter[3], pageNames[k], ecp->counter[4], pageNames[k], (ecp->counter[5] / 1000000000.0), pageNames[k], ecp->counter[6]); } if(state.scsi_nonmedium_error.found && state.scsi_nonmedium_error.nme.gotPC0) { fprintf(f, "\tnon-medium-errors;%" PRIu64 ";", state.scsi_nonmedium_error.nme.counterPC0); } // write SCSI current temperature if it is monitored if (state.temperature) fprintf(f, "\ttemperature;%d;", state.temperature); // end of line fprintf(f, "\n"); return true; } // Write all state files. If write_always is false, don't write // unless must_write is set. static void write_all_dev_states(const dev_config_vector & configs, dev_state_vector & states, bool write_always = true) { for (unsigned i = 0; i < states.size(); i++) { const dev_config & cfg = configs.at(i); if (cfg.state_file.empty()) continue; dev_state & state = states[i]; if (!write_always && !state.must_write) continue; if (!write_dev_state(cfg.state_file.c_str(), state)) continue; state.must_write = false; if (write_always || debugmode) PrintOut(LOG_INFO, "Device: %s, state written to %s\n", cfg.name.c_str(), cfg.state_file.c_str()); } } // Write to all attrlog files static void write_all_dev_attrlogs(const dev_config_vector & configs, dev_state_vector & states) { for (unsigned i = 0; i < states.size(); i++) { const dev_config & cfg = configs.at(i); if (cfg.attrlog_file.empty()) continue; dev_state & state = states[i]; write_dev_attrlog(cfg.attrlog_file.c_str(), state); } } extern "C" { // signal handlers require C-linkage // Note if we catch a SIGUSR1 static void USR1handler(int sig) { if (SIGUSR1==sig) caughtsigUSR1=1; return; } #ifdef _WIN32 // Note if we catch a SIGUSR2 static void USR2handler(int sig) { if (SIGUSR2==sig) caughtsigUSR2=1; return; } #endif // Note if we catch a HUP (or INT in debug mode) static void HUPhandler(int sig) { if (sig==SIGHUP) caughtsigHUP=1; else caughtsigHUP=2; return; } // signal handler for TERM, QUIT, and INT (if not in debug mode) static void sighandler(int sig) { if (!caughtsigEXIT) caughtsigEXIT=sig; return; } } // extern "C" #ifdef HAVE_LIBCAP_NG // capabilities(7) support static bool capabilities_enabled = false; static void capabilities_drop_now() { if (!capabilities_enabled) return; capng_clear(CAPNG_SELECT_BOTH); capng_updatev(CAPNG_ADD, (capng_type_t)(CAPNG_EFFECTIVE|CAPNG_PERMITTED), CAP_SYS_ADMIN, CAP_MKNOD, CAP_SYS_RAWIO, -1); capng_apply(CAPNG_SELECT_BOTH); } static void capabilities_check_config(dev_config_vector & configs) { if (!capabilities_enabled) return; for (unsigned i = 0; i < configs.size(); i++) { dev_config & cfg = configs[i]; if (!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) { PrintOut(LOG_INFO, "Device: %s, --capabilites is set, mail will be suppressed.\n", cfg.name.c_str()); cfg.emailaddress.clear(); cfg.emailcmdline.clear(); } } } #else // HAVE_LIBCAP_NG // No capabilities(7) support static inline void capabilities_drop_now() { } static inline void capabilities_check_config(dev_config_vector &) { } #endif // HAVE_LIBCAP_NG // a replacement for setenv() which is not available on all platforms. // Note that the string passed to putenv must not be freed or made // invalid, since a pointer to it is kept by putenv(). This means that // it must either be a static buffer or allocated off the heap. The // string can be freed if the environment variable is redefined via // another call to putenv(). There is no portable way to unset a variable // with putenv(). So we manage the buffer in a static object. // Using setenv() if available is not considered because some // implementations may produce memory leaks. class env_buffer { public: env_buffer() : m_buf((char *)0) { } void set(const char * name, const char * value); private: char * m_buf; env_buffer(const env_buffer &); void operator=(const env_buffer &); }; void env_buffer::set(const char * name, const char * value) { int size = strlen(name) + 1 + strlen(value) + 1; char * newbuf = new char[size]; snprintf(newbuf, size, "%s=%s", name, value); if (putenv(newbuf)) throw std::runtime_error("putenv() failed"); // This assumes that the same NAME is passed on each call delete [] m_buf; m_buf = newbuf; } #define EBUFLEN 1024 static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) __attribute_format_printf(4, 5); // If either address or executable path is non-null then send and log // a warning email, or execute executable static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) { static const char * const 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 "Temperature" // 12 }; // See if user wants us to send mail if (cfg.emailaddress.empty() && cfg.emailcmdline.empty()) return; std::string address = cfg.emailaddress; const char * executable = cfg.emailcmdline.c_str(); // which type of mail are we sending? mailinfo * mail=(state.maillog)+which; // checks for sanity if (cfg.emailfreq<1 || cfg.emailfreq>3) { PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg.mailwarn->emailfreq=%d\n",cfg.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 ((cfg.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. time_t epoch = time(0); // Return if less than one day has gone by const int day = 24*3600; if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) return; // Return if less than 2^(logged-1) days have gone by if (cfg.emailfreq==3 && mail->logged) { int 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; // print warning string into message char message[256]; va_list ap; va_start(ap, fmt); vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); // replace commas by spaces to separate recipients std::replace(address.begin(), address.end(), ',', ' '); // Export information in environment variables that will be useful // for user scripts static env_buffer env[12]; env[0].set("SMARTD_MAILER", executable); env[1].set("SMARTD_MESSAGE", message); char dates[DATEANDEPOCHLEN]; snprintf(dates, sizeof(dates), "%d", mail->logged); env[2].set("SMARTD_PREVCNT", dates); dateandtimezoneepoch(dates, mail->firstsent); env[3].set("SMARTD_TFIRST", dates); snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); env[4].set("SMARTD_TFIRSTEPOCH", dates); env[5].set("SMARTD_FAILTYPE", whichfail[which]); env[6].set("SMARTD_ADDRESS", address.c_str()); env[7].set("SMARTD_DEVICESTRING", cfg.name.c_str()); // Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE' env[8].set("SMARTD_DEVICETYPE", (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto")); env[9].set("SMARTD_DEVICE", cfg.dev_name.c_str()); env[10].set("SMARTD_DEVICEINFO", cfg.dev_idinfo.c_str()); dates[0] = 0; if (which) switch (cfg.emailfreq) { case 2: dates[0] = '1'; dates[1] = 0; break; case 3: snprintf(dates, sizeof(dates), "%d", (0x01)<logged); } env[11].set("SMARTD_NEXTDAYS", dates); // now construct a command to send this as EMAIL if (!*executable) executable = ""; const char * newadd = (!address.empty()? address.c_str() : ""); const char * newwarn = (which? "Warning via" : "Test of"); char command[256]; #ifdef _WIN32 // Path may contain spaces snprintf(command, sizeof(command), "\"%s\" 2>&1", warning_script.c_str()); #else snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str()); #endif // tell SYSLOG what we are about to do... PrintOut(LOG_INFO,"%s %s to %s ...\n", which?"Sending warning via":"Executing test of", executable, newadd); // issue the command to send mail or to run the user's executable errno=0; FILE * pfp; 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 succeeded! 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))); } } // increment mail sent counter mail->logged++; } static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) __attribute_format_printf(4, 5); static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) { if (!(0 <= which && which < SMARTD_NMAIL)) return; // Return if no mail sent yet mailinfo & mi = state.maillog[which]; if (!mi.logged) return; // Format & print message char msg[256]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); PrintOut(LOG_INFO, "Device: %s, %s, warning condition reset after %d email%s\n", cfg.name.c_str(), msg, mi.logged, (mi.logged==1 ? "" : "s")); // Clear mail counter and timestamps mi = mailinfo(); state.must_write = true; } #ifndef _WIN32 // Output multiple lines via separate syslog(3) calls. __attribute_format_printf(2, 0) static void vsyslog_lines(int priority, const char * fmt, va_list ap) { char buf[512+EBUFLEN]; // enough space for exec cmd output in MailWarning() vsnprintf(buf, sizeof(buf), fmt, ap); for (char * p = buf, * q; p && *p; p = q) { if ((q = strchr(p, '\n'))) *q++ = 0; if (*p) syslog(priority, "%s\n", p); } } #else // _WIN32 // os_win32/syslog_win32.cpp supports multiple lines. #define vsyslog_lines vsyslog #endif // _WIN32 // 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(const char *fmt, ...){ va_list ap; // get the correct time in syslog() FixGlibcTimeZoneBug(); // initialize variable argument list va_start(ap,fmt); // in debugmode==1 mode we will print the output from the ataprint.o functions! if (debugmode && debugmode != 2) { FILE * f = stdout; #ifdef _WIN32 if (facility == LOG_LOCAL1) // logging to stdout f = stderr; #endif vfprintf(f, fmt, ap); fflush(f); } // in debugmode==2 mode we print output from knowndrives.o functions else if (debugmode==2 || ata_debugmode || scsi_debugmode) { openlog("smartd", LOG_PID, facility); vsyslog_lines(LOG_INFO, fmt, ap); closelog(); } va_end(ap); return; } // This function prints either to stdout or to the syslog as needed. static void PrintOut(int priority, const char *fmt, ...){ va_list ap; // get the correct time in syslog() FixGlibcTimeZoneBug(); // initialize variable argument list va_start(ap,fmt); if (debugmode) { FILE * f = stdout; #ifdef _WIN32 if (facility == LOG_LOCAL1) // logging to stdout f = stderr; #endif vfprintf(f, fmt, ap); fflush(f); } else { openlog("smartd", LOG_PID, facility); vsyslog_lines(priority, fmt, ap); closelog(); } va_end(ap); return; } // Used to warn users about invalid checksums. Called from atacmds.cpp. void checksumwarning(const char * string) { pout("Warning! %s error: invalid SMART checksum.\n", string); } #ifndef _WIN32 // Wait for the pid file to show up, this makes sure a calling program knows // that the daemon is really up and running and has a pid to kill it static bool WaitForPidFile() { int waited, max_wait = 10; struct stat stat_buf; if (pid_file.empty() || debugmode) return true; for(waited = 0; waited < max_wait; ++waited) { if (!stat(pid_file.c_str(), &stat_buf)) { return true; } else sleep(1); } return false; } #endif // _WIN32 // Forks new process if needed, closes ALL file descriptors, // redirects stdin, stdout, and stderr. Not quite daemon(). // See https://www.linuxjournal.com/article/2335 // for a good description of why we do things this way. static int daemon_init() { #ifndef _WIN32 // 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 (do_fork) { pid_t pid; if ((pid=fork()) < 0) { // unable to fork! PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n"); return EXIT_STARTUP; } if (pid) { // we are the parent process, wait for pid file, then exit cleanly if(!WaitForPidFile()) { PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file.c_str()); return EXIT_STARTUP; } return 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"); return EXIT_STARTUP; } if (pid) // we are the parent process -- exit cleanly return 0; // Now we are the child's child... } // close any open file descriptors for (int i = getdtablesize(); --i >= 0; ) close(i); // redirect any IO attempts to /dev/null and change to root directory int fd = open("/dev/null", O_RDWR); if (!(fd == 0 && dup(fd) == 1 && dup(fd) == 2 && !chdir("/"))) { PrintOut(LOG_CRIT, "smartd unable to redirect to /dev/null or to chdir to root!\n"); return EXIT_STARTUP; } umask(0022); if (do_fork) 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"); return EXIT_STARTUP; } // stdin/out/err now closed if not redirected #endif // _WIN32 // No error, continue in main_worker() return -1; } // create a PID file containing the current process id static bool write_pid_file() { if (!pid_file.empty()) { pid_t pid = getpid(); mode_t old_umask; #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 stdio_file f(pid_file.c_str(), "w"); umask(old_umask); if (!(f && fprintf(f, "%d\n", (int)pid) > 0 && f.close())) { PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file.c_str()); return false; } PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file.c_str(), (int)pid); } return true; } // Prints header identifying version of code and home static void PrintHead() { PrintOut(LOG_INFO, "%s\n", format_version_info("smartd").c_str()); } // prints help info for configuration file Directives static void Directives() { PrintOut(LOG_INFO, "Configuration file (%s) Directives (after device name):\n" " -d TYPE Set the device type: auto, ignore, removable,\n" " %s\n" " -T TYPE Set the tolerance to one of: normal, permissive\n" " -o VAL Enable/disable automatic offline tests (on/off)\n" " -S VAL Enable/disable attribute autosave (on/off)\n" " -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,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 or self-test status:\n" " error, selftest, xerror, offlinests[,ns], selfteststs[,ns]\n" " -l scterc,R,W Set SCT Error Recovery Control\n" " -e Change device setting: aam,[N|off], apm,[N|off], dsn,[on|off],\n" " lookahead,[on|off], security-freeze, standby,[N|off], wcache,[on|off]\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 [increases of] Current Pending Sectors in Attribute ID\n" " -U ID[+] Monitor [increases of] Offline Uncorrectable Sectors in Attribute ID\n" " -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\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 -l selfteststs -C 197 -U 198\n" " -F TYPE Use firmware bug workaround:\n" " %s\n" " # Comment: text after a hash sign is ignored\n" " \\ Line continuation character\n" "Attribute ID is a decimal integer 1 <= ID <= 255\n" "Use ID = 0 to turn off -C and/or -U Directives\n" "Example: /dev/sda -a\n", configfile, smi()->get_valid_dev_types_str().c_str(), get_valid_firmwarebug_args()); } /* Returns a pointer to a static string containing a formatted list of the valid arguments to the option opt or NULL on failure. */ static const char *GetValidArgList(char opt) { switch (opt) { case 'A': case 's': return ""; case 'B': return "[+]"; case 'c': return ", -"; 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], nvmeioctl[,N]"; case 'p': case 'w': return ""; case 'i': return ""; default: return NULL; } } /* prints help information for command syntax */ static void Usage() { PrintOut(LOG_INFO,"Usage: smartd [options]\n\n"); PrintOut(LOG_INFO," -A PREFIX, --attributelog=PREFIX\n"); PrintOut(LOG_INFO," Log ATA attribute information to {PREFIX}MODEL-SERIAL.ata.csv\n"); #ifdef SMARTMONTOOLS_ATTRIBUTELOG PrintOut(LOG_INFO," [default is " SMARTMONTOOLS_ATTRIBUTELOG "MODEL-SERIAL.ata.csv]\n"); #endif PrintOut(LOG_INFO,"\n"); PrintOut(LOG_INFO," -B [+]FILE, --drivedb=[+]FILE\n"); PrintOut(LOG_INFO," Read and replace [add] drive database from FILE\n"); PrintOut(LOG_INFO," [default is +%s", get_drivedb_path_add()); #ifdef SMARTMONTOOLS_DRIVEDBDIR PrintOut(LOG_INFO,"\n"); PrintOut(LOG_INFO," and then %s", get_drivedb_path_default()); #endif PrintOut(LOG_INFO,"]\n\n"); PrintOut(LOG_INFO," -c NAME|-, --configfile=NAME|-\n"); PrintOut(LOG_INFO," Read configuration file NAME or stdin\n"); PrintOut(LOG_INFO," [default is %s]\n\n", configfile); #ifdef HAVE_LIBCAP_NG PrintOut(LOG_INFO," -C, --capabilities\n"); PrintOut(LOG_INFO," Drop unneeded Linux process capabilities.\n" " Warning: Mail notification does not work when used.\n\n"); #endif 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 #ifndef _WIN32 PrintOut(LOG_INFO," -n, --no-fork\n"); PrintOut(LOG_INFO," Do not fork into background\n"); #ifdef HAVE_LIBSYSTEMD PrintOut(LOG_INFO," (systemd 'Type=notify' is assumed if $NOTIFY_SOCKET is set)\n"); #endif // HAVE_LIBSYSTEMD PrintOut(LOG_INFO,"\n"); #endif // WIN32 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')); PrintOut(LOG_INFO," -s PREFIX, --savestates=PREFIX\n"); PrintOut(LOG_INFO," Save disk states to {PREFIX}MODEL-SERIAL.TYPE.state\n"); #ifdef SMARTMONTOOLS_SAVESTATES PrintOut(LOG_INFO," [default is " SMARTMONTOOLS_SAVESTATES "MODEL-SERIAL.TYPE.state]\n"); #endif PrintOut(LOG_INFO,"\n"); PrintOut(LOG_INFO," -w NAME, --warnexec=NAME\n"); PrintOut(LOG_INFO," Run executable NAME on warnings\n"); #ifndef _WIN32 PrintOut(LOG_INFO," [default is " SMARTMONTOOLS_SMARTDSCRIPTDIR "/smartd_warning.sh]\n\n"); #else PrintOut(LOG_INFO," [default is %s/smartd_warning.cmd]\n\n", get_exe_dir().c_str()); #endif #ifdef _WIN32 PrintOut(LOG_INFO," --service\n"); PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n"); PrintOut(LOG_INFO," smartd install [options]\n"); PrintOut(LOG_INFO," Remove service with:\n"); PrintOut(LOG_INFO," smartd remove\n\n"); #endif // _WIN32 PrintOut(LOG_INFO," -V, --version, --license, --copyright\n"); PrintOut(LOG_INFO," Print License, Copyright, and version information\n"); } static int CloseDevice(smart_device * device, const char * name) { if (!device->close()){ PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg()); return 1; } // device successfully closed return 0; } // return true if a char is not allowed in a state file name static bool not_allowed_in_filename(char c) { return !( ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')); } // Read error count from Summary or Extended Comprehensive SMART error log // Return -1 on error static int read_ata_error_count(ata_device * device, const char * name, firmwarebug_defs firmwarebugs, bool extended) { if (!extended) { ata_smart_errorlog log; if (ataReadErrorLog(device, &log, firmwarebugs)){ PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name); return -1; } return (log.error_log_pointer ? log.ata_error_count : 0); } else { ata_smart_exterrlog logx; if (!ataReadExtErrorLog(device, &logx, 0, 1 /*first sector only*/, firmwarebugs)) { PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name); return -1; } // Some disks use the reserved byte as index, see ataprint.cpp. return (logx.error_log_index || logx.reserved1 ? logx.device_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. static int SelfTestErrorCount(ata_device * device, const char * name, firmwarebug_defs firmwarebugs) { struct ata_smart_selftestlog log; if (ataReadSelfTestLog(device, &log, firmwarebugs)){ PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name); return -1; } if (!log.mostrecenttest) // No tests logged return 0; // Count failed self-tests int errcnt = 0, hours = 0; for (int i = 20; i >= 0; i--) { int j = (i + log.mostrecenttest) % 21; const ata_smart_selftestlog_struct & entry = log.selftest_struct[j]; if (!nonempty(&entry, sizeof(entry))) continue; int status = entry.selfteststatus >> 4; if (status == 0x0 && (entry.selftestnumber & 0x7f) == 0x02) // First successful extended self-test, stop count break; if (0x3 <= status && status <= 0x8) { // Self-test showed an error errcnt++; // Keep track of time of most recent error if (!hours) hours = entry.timestamp; } } return ((hours << 8) | errcnt); } #define SELFTEST_ERRORCOUNT(x) (x & 0xff) #define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff) // Check offline data collection status static inline bool is_offl_coll_in_progress(unsigned char status) { return ((status & 0x7f) == 0x03); } // Check self-test execution status static inline bool is_self_test_in_progress(unsigned char status) { return ((status >> 4) == 0xf); } // Log offline data collection status static void log_offline_data_coll_status(const char * name, unsigned char status) { const char * msg; switch (status & 0x7f) { case 0x00: msg = "was never started"; break; case 0x02: msg = "was completed without error"; break; case 0x03: msg = "is in progress"; break; case 0x04: msg = "was suspended by an interrupting command from host"; break; case 0x05: msg = "was aborted by an interrupting command from host"; break; case 0x06: msg = "was aborted by the device with a fatal error"; break; default: msg = 0; } if (msg) PrintOut(((status & 0x7f) == 0x06 ? LOG_CRIT : LOG_INFO), "Device: %s, offline data collection %s%s\n", name, msg, ((status & 0x80) ? " (auto:on)" : "")); else PrintOut(LOG_INFO, "Device: %s, unknown offline data collection status 0x%02x\n", name, status); } // Log self-test execution status static void log_self_test_exec_status(const char * name, unsigned char status) { const char * msg; switch (status >> 4) { case 0x0: msg = "completed without error"; break; case 0x1: msg = "was aborted by the host"; break; case 0x2: msg = "was interrupted by the host with a reset"; break; case 0x3: msg = "could not complete due to a fatal or unknown error"; break; case 0x4: msg = "completed with error (unknown test element)"; break; case 0x5: msg = "completed with error (electrical test element)"; break; case 0x6: msg = "completed with error (servo/seek test element)"; break; case 0x7: msg = "completed with error (read test element)"; break; case 0x8: msg = "completed with error (handling damage?)"; break; default: msg = 0; } if (msg) PrintOut(((status >> 4) >= 0x4 ? LOG_CRIT : LOG_INFO), "Device: %s, previous self-test %s\n", name, msg); else if ((status >> 4) == 0xf) PrintOut(LOG_INFO, "Device: %s, self-test in progress, %u0%% remaining\n", name, status & 0x0f); else PrintOut(LOG_INFO, "Device: %s, unknown self-test status 0x%02x\n", name, status); } // Check pending sector count id (-C, -U directives). static bool check_pending_id(const dev_config & cfg, const dev_state & state, unsigned char id, const char * msg) { // Check attribute index int i = ata_find_attr_index(id, state.smartval); if (i < 0) { PrintOut(LOG_INFO, "Device: %s, can't monitor %s count - no Attribute %d\n", cfg.name.c_str(), msg, id); return false; } // Check value uint64_t rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs); if (rawval >= (state.num_sectors ? state.num_sectors : 0xffffffffULL)) { PrintOut(LOG_INFO, "Device: %s, ignoring %s count - bogus Attribute %d value %" PRIu64 " (0x%" PRIx64 ")\n", cfg.name.c_str(), msg, id, rawval, rawval); return false; } return true; } // Called by ATA/SCSI/NVMeDeviceScan() after successful device check static void finish_device_scan(dev_config & cfg, dev_state & state) { // Set cfg.emailfreq if user hasn't set it if ((!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) && !cfg.emailfreq) { // Avoid that emails are suppressed forever due to state persistence if (cfg.state_file.empty()) cfg.emailfreq = 1; // '-M once' else cfg.emailfreq = 2; // '-M daily' } // Start self-test regex check now if time was not read from state file if (!cfg.test_regex.empty() && !state.scheduled_test_next_check) state.scheduled_test_next_check = time(0); } // Common function to format result message for ATA setting static void format_set_result_msg(std::string & msg, const char * name, bool ok, int set_option = 0, bool has_value = false) { if (!msg.empty()) msg += ", "; msg += name; if (!ok) msg += ":--"; else if (set_option < 0) msg += ":off"; else if (has_value) msg += strprintf(":%d", set_option-1); else if (set_option > 0) msg += ":on"; } // Return true and print message if CFG.dev_idinfo is already in PREV_CFGS static bool is_duplicate_dev_idinfo(const dev_config & cfg, const dev_config_vector & prev_cfgs) { if (!cfg.id_is_unique) return false; for (unsigned i = 0; i < prev_cfgs.size(); i++) { if (!prev_cfgs[i].id_is_unique) continue; if (cfg.dev_idinfo != prev_cfgs[i].dev_idinfo) continue; PrintOut(LOG_INFO, "Device: %s, same identity as %s, ignored\n", cfg.dev_name.c_str(), prev_cfgs[i].dev_name.c_str()); return true; } return false; } // TODO: Add '-F swapid' directive const bool fix_swapped_id = false; // scan to see what ata devices there are, and if they support SMART static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atadev, const dev_config_vector * prev_cfgs) { int supported=0; struct ata_identify_device drive; const char *name = cfg.name.c_str(); int retid; // Device must be open // Get drive identity structure if ((retid = ata_read_identity(atadev, &drive, fix_swapped_id))) { 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(atadev, name); return 2; } // Get drive identity, size and rotation rate (HDD/SSD) char model[40+1], serial[20+1], firmware[8+1]; ata_format_id_string(model, drive.model, sizeof(model)-1); ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1); ata_format_id_string(firmware, drive.fw_rev, sizeof(firmware)-1); ata_size_info sizes; ata_get_size_info(&drive, sizes); state.num_sectors = sizes.sectors; cfg.dev_rpm = ata_get_rotation_rate(&drive); char wwn[30]; wwn[0] = 0; unsigned oui = 0; uint64_t unique_id = 0; int naa = ata_get_wwn(&drive, oui, unique_id); if (naa >= 0) snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09" PRIx64 ", ", naa, oui, unique_id); // Format device id string for warning emails char cap[32]; cfg.dev_idinfo = strprintf("%s, S/N:%s, %sFW:%s, %s", model, serial, wwn, firmware, format_capacity(cap, sizeof(cap), sizes.capacity, ".")); cfg.id_is_unique = true; // TODO: Check serial? PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str()); // Check for duplicates if (prev_cfgs && is_duplicate_dev_idinfo(cfg, *prev_cfgs)) { CloseDevice(atadev, name); return 1; } // 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 { // Apply vendor specific presets, print warning if present const drive_settings * dbentry = lookup_drive_apply_presets( &drive, cfg.attribute_defs, cfg.firmwarebugs); if (!dbentry) PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); else { PrintOut(LOG_INFO, "Device: %s, found in smartd database%s%s\n", name, (*dbentry->modelfamily ? ": " : "."), (*dbentry->modelfamily ? dbentry->modelfamily : "")); if (*dbentry->warningmsg) PrintOut(LOG_CRIT, "Device: %s, WARNING: %s\n", name, dbentry->warningmsg); } } // Check for ATA Security LOCK unsigned short word128 = drive.words088_255[128-88]; bool locked = ((word128 & 0x0007) == 0x0007); // LOCKED|ENABLED|SUPPORTED if (locked) PrintOut(LOG_INFO, "Device: %s, ATA Security is **LOCKED**\n", name); // Set default '-C 197[+]' if no '-C ID' is specified. if (!cfg.curr_pending_set) cfg.curr_pending_id = get_unc_attr_id(false, cfg.attribute_defs, cfg.curr_pending_incr); // Set default '-U 198[+]' if no '-U ID' is specified. if (!cfg.offl_pending_set) cfg.offl_pending_id = get_unc_attr_id(true, cfg.attribute_defs, cfg.offl_pending_incr); // 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; show_presets(&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(atadev, name); return 2; } } if (ataEnableSmart(atadev)) { // Enable SMART command has failed PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name); if (ataIsSmartEnabled(&drive) <= 0) { if (!cfg.permissive) { PrintOut(LOG_INFO, "Device: %s, to proceed anyway, use '-T permissive' Directive.\n", name); CloseDevice(atadev, name); return 2; } PrintOut(LOG_INFO, "Device: %s, proceeding since '-T permissive' Directive given.\n", name); } else { PrintOut(LOG_INFO, "Device: %s, proceeding since SMART is already enabled\n", name); } } // disable device attribute autosave... if (cfg.autosave==1) { if (ataDisableAutoSave(atadev)) 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(atadev)) 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(atadev) == -1) { PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name); cfg.smartcheck = false; } // 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 get SMART data? bool smart_val_ok = false; if ( cfg.autoofflinetest || cfg.selftest || cfg.errorlog || cfg.xerrorlog || cfg.offlinests || cfg.selfteststs || cfg.usagefailed || cfg.prefail || cfg.usage || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || cfg.curr_pending_id || cfg.offl_pending_id ) { if (ataReadSmartValues(atadev, &state.smartval)) { PrintOut(LOG_INFO, "Device: %s, Read SMART Values failed\n", name); cfg.usagefailed = cfg.prefail = cfg.usage = false; cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; cfg.curr_pending_id = cfg.offl_pending_id = 0; } else { smart_val_ok = true; if (ataReadSmartThresholds(atadev, &state.smartthres)) { PrintOut(LOG_INFO, "Device: %s, Read SMART Thresholds failed%s\n", name, (cfg.usagefailed ? ", ignoring -f Directive" : "")); cfg.usagefailed = false; // Let ata_get_attr_state() return ATTRSTATE_NO_THRESHOLD: memset(&state.smartthres, 0, sizeof(state.smartthres)); } } // see if the necessary Attribute is there to monitor offline or // current pending sectors or temperature if ( cfg.curr_pending_id && !check_pending_id(cfg, state, cfg.curr_pending_id, "Current_Pending_Sector")) cfg.curr_pending_id = 0; if ( cfg.offl_pending_id && !check_pending_id(cfg, state, cfg.offl_pending_id, "Offline_Uncorrectable")) cfg.offl_pending_id = 0; if ( (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) && !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) { PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; } // Report ignored '-r' or '-R' directives for (int id = 1; id <= 255; id++) { if (cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_PRINT)) { char opt = (!cfg.monitor_attr_flags.is_set(id, MONITOR_RAW) ? 'r' : 'R'); const char * excl = (cfg.monitor_attr_flags.is_set(id, (opt == 'r' ? MONITOR_AS_CRIT : MONITOR_RAW_AS_CRIT)) ? "!" : ""); int idx = ata_find_attr_index(id, state.smartval); if (idx < 0) PrintOut(LOG_INFO,"Device: %s, no Attribute %d, ignoring -%c %d%s\n", name, id, opt, id, excl); else { bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(state.smartval.vendor_attributes[idx].flags); if (!((prefail && cfg.prefail) || (!prefail && cfg.usage))) PrintOut(LOG_INFO,"Device: %s, not monitoring %s Attributes, ignoring -%c %d%s\n", name, (prefail ? "Prefailure" : "Usage"), opt, id, excl); } } } } // enable/disable automatic on-line testing if (cfg.autoofflinetest) { // is this an enable or disable request? const char *what=(cfg.autoofflinetest==1)?"disable":"enable"; if (!smart_val_ok) 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(&state.smartval)) PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name); // ... but then try anyway if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev)) 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); } } // Read log directories if required for capability check ata_smart_log_directory smart_logdir, gp_logdir; bool smart_logdir_ok = false, gp_logdir_ok = false; if ( isGeneralPurposeLoggingCapable(&drive) && (cfg.errorlog || cfg.selftest) && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { if (!ataReadLogDirectory(atadev, &smart_logdir, false)) smart_logdir_ok = true; } if (cfg.xerrorlog && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { if (!ataReadLogDirectory(atadev, &gp_logdir, true)) gp_logdir_ok = true; } // capability check: self-test-log state.selflogcount = 0; state.selfloghour = 0; if (cfg.selftest) { int retval; if (!( cfg.permissive || ( smart_logdir_ok && smart_logdir.entry[0x06-1].numsectors) || (!smart_logdir_ok && smart_val_ok && isSmartTestLogCapable(&state.smartval, &drive)))) { PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name); cfg.selftest = false; } else if ((retval = SelfTestErrorCount(atadev, name, cfg.firmwarebugs)) < 0) { PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name); cfg.selftest = false; } else { state.selflogcount=SELFTEST_ERRORCOUNT(retval); state.selfloghour =SELFTEST_ERRORHOURS(retval); } } // capability check: ATA error log state.ataerrorcount = 0; if (cfg.errorlog) { int errcnt1; if (!( cfg.permissive || ( smart_logdir_ok && smart_logdir.entry[0x01-1].numsectors) || (!smart_logdir_ok && smart_val_ok && isSmartErrorLogCapable(&state.smartval, &drive)))) { PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name); cfg.errorlog = false; } else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false)) < 0) { PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name); cfg.errorlog = false; } else state.ataerrorcount = errcnt1; } if (cfg.xerrorlog) { int errcnt2; if (!( cfg.permissive || cfg.firmwarebugs.is_set(BUG_NOLOGDIR) || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors) )) { PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n", name); cfg.xerrorlog = false; } else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true)) < 0) { PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name); cfg.xerrorlog = false; } else if (cfg.errorlog && state.ataerrorcount != errcnt2) { PrintOut(LOG_INFO, "Device: %s, SMART Error Logs report different error counts: %d != %d\n", name, state.ataerrorcount, errcnt2); // Record max error count if (errcnt2 > state.ataerrorcount) state.ataerrorcount = errcnt2; } else state.ataerrorcount = errcnt2; } // capability check: self-test and offline data collection status if (cfg.offlinests || cfg.selfteststs) { if (!(cfg.permissive || (smart_val_ok && state.smartval.offline_data_collection_capability))) { if (cfg.offlinests) PrintOut(LOG_INFO, "Device: %s, no SMART Offline Data Collection capability, ignoring -l offlinests (override with -T permissive)\n", name); if (cfg.selfteststs) PrintOut(LOG_INFO, "Device: %s, no SMART Self-test capability, ignoring -l selfteststs (override with -T permissive)\n", name); cfg.offlinests = cfg.selfteststs = false; } } // capabilities check -- does it support powermode? if (cfg.powermode) { int powermode = ataCheckPowerMode(atadev); 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!=0x00 && powermode!=0x01 && powermode!=0x40 && powermode!=0x41 && powermode!=0x80 && powermode!=0x81 && powermode!=0x82 && powermode!=0x83 && powermode!=0xff) { PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", name, powermode); cfg.powermode=0; } } // Apply ATA settings std::string msg; if (cfg.set_aam) format_set_result_msg(msg, "AAM", (cfg.set_aam > 0 ? ata_set_features(atadev, ATA_ENABLE_AAM, cfg.set_aam-1) : ata_set_features(atadev, ATA_DISABLE_AAM)), cfg.set_aam, true); if (cfg.set_apm) format_set_result_msg(msg, "APM", (cfg.set_apm > 0 ? ata_set_features(atadev, ATA_ENABLE_APM, cfg.set_apm-1) : ata_set_features(atadev, ATA_DISABLE_APM)), cfg.set_apm, true); if (cfg.set_lookahead) format_set_result_msg(msg, "Rd-ahead", ata_set_features(atadev, (cfg.set_lookahead > 0 ? ATA_ENABLE_READ_LOOK_AHEAD : ATA_DISABLE_READ_LOOK_AHEAD)), cfg.set_lookahead); if (cfg.set_wcache) format_set_result_msg(msg, "Wr-cache", ata_set_features(atadev, (cfg.set_wcache > 0? ATA_ENABLE_WRITE_CACHE : ATA_DISABLE_WRITE_CACHE)), cfg.set_wcache); if (cfg.set_dsn) format_set_result_msg(msg, "DSN", ata_set_features(atadev, ATA_ENABLE_DISABLE_DSN, (cfg.set_dsn > 0 ? 0x1 : 0x2))); if (cfg.set_security_freeze) format_set_result_msg(msg, "Security freeze", ata_nodata_command(atadev, ATA_SECURITY_FREEZE_LOCK)); if (cfg.set_standby) format_set_result_msg(msg, "Standby", ata_nodata_command(atadev, ATA_IDLE, cfg.set_standby-1), cfg.set_standby, true); // Report as one log entry if (!msg.empty()) PrintOut(LOG_INFO, "Device: %s, ATA settings applied: %s\n", name, msg.c_str()); // set SCT Error Recovery Control if requested if (cfg.sct_erc_set) { if (!isSCTErrorRecoveryControlCapable(&drive)) PrintOut(LOG_INFO, "Device: %s, no SCT Error Recovery Control support, ignoring -l scterc\n", name); else if (locked) PrintOut(LOG_INFO, "Device: %s, no SCT support if ATA Security is LOCKED, ignoring -l scterc\n", name); else if ( ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime ) || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime)) PrintOut(LOG_INFO, "Device: %s, set of SCT Error Recovery Control failed\n", name); else PrintOut(LOG_INFO, "Device: %s, SCT Error Recovery Control set to: Read: %u, Write: %u\n", name, cfg.sct_erc_readtime, cfg.sct_erc_writetime); } // If no tests available or selected, return if (!( cfg.smartcheck || cfg.selftest || cfg.errorlog || cfg.xerrorlog || cfg.offlinests || cfg.selfteststs || cfg.usagefailed || cfg.prefail || cfg.usage || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)) { CloseDevice(atadev, name); return 3; } // tell user we are registering device PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name); // close file descriptor CloseDevice(atadev, name); if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) { // Build file name for state file std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_'); std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_'); if (!state_path_prefix.empty()) { cfg.state_file = strprintf("%s%s-%s.ata.state", state_path_prefix.c_str(), model, serial); // Read previous state if (read_dev_state(cfg.state_file.c_str(), state)) { PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str()); // Copy ATA attribute values to temp state state.update_temp_state(); } } if (!attrlog_path_prefix.empty()) cfg.attrlog_file = strprintf("%s%s-%s.ata.csv", attrlog_path_prefix.c_str(), model, serial); } finish_device_scan(cfg, state); return 0; } // on success, return 0. On failure, return >0. Never return <0, // please. static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scsidev, const dev_config_vector * prev_cfgs) { int err, req_len, avail_len, version, len; const char *device = cfg.name.c_str(); struct scsi_iec_mode_page iec; uint8_t tBuf[64]; uint8_t inqBuf[96]; uint8_t vpdBuf[252]; char lu_id[64], serial[256], vendor[40], model[40]; // Device must be open memset(inqBuf, 0, 96); req_len = 36; if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) { /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ req_len = 64; if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) { PrintOut(LOG_INFO, "Device: %s, Both 36 and 64 byte INQUIRY failed; " "skip device\n", device); return 2; } } version = (inqBuf[2] & 0x7f); /* Accept old ISO/IEC 9316:1995 variants */ avail_len = inqBuf[4] + 5; len = (avail_len < req_len) ? avail_len : req_len; if (len < 36) { PrintOut(LOG_INFO, "Device: %s, INQUIRY response less than 36 bytes; " "skip device\n", device); return 2; } int pdt = inqBuf[0] & 0x1f; if (! ((0 == pdt) || (4 == pdt) || (5 == pdt) || (7 == pdt) || (0xe == pdt))) { PrintOut(LOG_INFO, "Device: %s, not a disk like device [PDT=0x%x], " "skip\n", device, pdt); return 2; } if (supported_vpd_pages_p) { delete supported_vpd_pages_p; supported_vpd_pages_p = NULL; } supported_vpd_pages_p = new supported_vpd_pages(scsidev); lu_id[0] = '\0'; if ((version >= 0x3) && (version < 0x8)) { /* SPC to SPC-5 */ if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_DEVICE_IDENTIFICATION, vpdBuf, sizeof(vpdBuf))) { len = vpdBuf[3]; scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL); } } serial[0] = '\0'; if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_UNIT_SERIAL_NUMBER, vpdBuf, sizeof(vpdBuf))) { len = vpdBuf[3]; vpdBuf[4 + len] = '\0'; scsi_format_id_string(serial, &vpdBuf[4], len); } char si_str[64]; struct scsi_readcap_resp srr; uint64_t capacity = scsiGetSize(scsidev, scsidev->use_rcap16(), &srr); if (capacity) format_capacity(si_str, sizeof(si_str), capacity, "."); else si_str[0] = '\0'; // Format device id string for warning emails cfg.dev_idinfo = strprintf("[%.8s %.16s %.4s]%s%s%s%s%s%s", (char *)&inqBuf[8], (char *)&inqBuf[16], (char *)&inqBuf[32], (lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""), (serial[0] ? ", S/N: " : ""), (serial[0] ? serial : ""), (si_str[0] ? ", " : ""), (si_str[0] ? si_str : "")); cfg.id_is_unique = (lu_id[0] || serial[0]); // format "model" string scsi_format_id_string(vendor, &inqBuf[8], 8); scsi_format_id_string(model, &inqBuf[16], 16); PrintOut(LOG_INFO, "Device: %s, %s\n", device, cfg.dev_idinfo.c_str()); // Check for duplicates if (prev_cfgs && is_duplicate_dev_idinfo(cfg, *prev_cfgs)) { CloseDevice(scsidev, device); return 1; } // check that device is ready for commands. IE stores its stuff on // the media. if ((err = scsiTestUnitReady(scsidev))) { 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(scsidev, 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(scsidev, &iec, state.modese_len))) state.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(scsidev, 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(scsidev, device); return 3; } // Flag that certain log pages are supported (information may be // available from other sources). if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0) || 0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 68)) /* workaround for the bug #678 on ST8000NM0075/E001. Up to 64 pages + 4b header */ { for (int k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) { switch (tBuf[k]) { case TEMPERATURE_LPAGE: state.TempPageSupported = 1; break; case IE_LPAGE: state.SmartPageSupported = 1; break; case READ_ERROR_COUNTER_LPAGE: state.ReadECounterPageSupported = 1; break; case WRITE_ERROR_COUNTER_LPAGE: state.WriteECounterPageSupported = 1; break; case VERIFY_ERROR_COUNTER_LPAGE: state.VerifyECounterPageSupported = 1; break; case NON_MEDIUM_ERROR_LPAGE: state.NonMediumErrorPageSupported = 1; break; default: break; } } } // Check if scsiCheckIE() is going to work { uint8_t asc = 0; uint8_t ascq = 0; uint8_t currenttemp = 0; uint8_t triptemp = 0; if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported, &asc, &ascq, ¤ttemp, &triptemp)) { PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); state.SuppressReport = 1; } if ( (state.SuppressReport || !currenttemp) && (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)) { PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", device, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; } } // capability check: self-test-log if (cfg.selftest){ int retval = scsiCountFailedSelfTests(scsidev, 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 = false; state.selflogcount = 0; state.selfloghour = 0; } else { // register starting values to watch for changes state.selflogcount=SELFTEST_ERRORCOUNT(retval); state.selfloghour =SELFTEST_ERRORHOURS(retval); } } // disable autosave (set GLTSD bit) if (cfg.autosave==1){ if (scsiSetControlGLTSD(scsidev, 1, state.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(scsidev, 0, state.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); // Make sure that init_standby_check() ignores SCSI devices cfg.offlinests_ns = cfg.selfteststs_ns = false; // close file descriptor CloseDevice(scsidev, device); if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) { // Build file name for state file std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_'); std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_'); if (!state_path_prefix.empty()) { cfg.state_file = strprintf("%s%s-%s-%s.scsi.state", state_path_prefix.c_str(), vendor, model, serial); // Read previous state if (read_dev_state(cfg.state_file.c_str(), state)) { PrintOut(LOG_INFO, "Device: %s, state read from %s\n", device, cfg.state_file.c_str()); // Copy ATA attribute values to temp state state.update_temp_state(); } } if (!attrlog_path_prefix.empty()) cfg.attrlog_file = strprintf("%s%s-%s-%s.scsi.csv", attrlog_path_prefix.c_str(), vendor, model, serial); } finish_device_scan(cfg, state); return 0; } // Convert 128 bit LE integer to uint64_t or its max value on overflow. static uint64_t le128_to_uint64(const unsigned char (& val)[16]) { for (int i = 8; i < 16; i++) { if (val[i]) return ~(uint64_t)0; } uint64_t lo = val[7]; for (int i = 7-1; i >= 0; i--) { lo <<= 8; lo += val[i]; } return lo; } // Get max temperature in Kelvin reported in NVMe SMART/Health log. static int nvme_get_max_temp_kelvin(const nvme_smart_log & smart_log) { int k = (smart_log.temperature[1] << 8) | smart_log.temperature[0]; for (int i = 0; i < 8; i++) { if (smart_log.temp_sensor[i] > k) k = smart_log.temp_sensor[i]; } return k; } static int NVMeDeviceScan(dev_config & cfg, dev_state & state, nvme_device * nvmedev, const dev_config_vector * prev_cfgs) { const char *name = cfg.name.c_str(); // Device must be open // Get ID Controller nvme_id_ctrl id_ctrl; if (!nvme_read_id_ctrl(nvmedev, id_ctrl)) { PrintOut(LOG_INFO, "Device: %s, NVMe Identify Controller failed\n", name); CloseDevice(nvmedev, name); return 2; } // Get drive identity char model[40+1], serial[20+1], firmware[8+1]; format_char_array(model, id_ctrl.mn); format_char_array(serial, id_ctrl.sn); format_char_array(firmware, id_ctrl.fr); // Format device id string for warning emails char nsstr[32] = "", capstr[32] = ""; unsigned nsid = nvmedev->get_nsid(); if (nsid != 0xffffffff) snprintf(nsstr, sizeof(nsstr), ", NSID:%u", nsid); uint64_t capacity = le128_to_uint64(id_ctrl.tnvmcap); if (capacity) format_capacity(capstr, sizeof(capstr), capacity, "."); cfg.dev_idinfo = strprintf("%s, S/N:%s, FW:%s%s%s%s", model, serial, firmware, nsstr, (capstr[0] ? ", " : ""), capstr); cfg.id_is_unique = true; // TODO: Check serial? PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str()); // Check for duplicates if (prev_cfgs && is_duplicate_dev_idinfo(cfg, *prev_cfgs)) { CloseDevice(nvmedev, name); return 1; } // Read SMART/Health log nvme_smart_log smart_log; if (!nvme_read_smart_log(nvmedev, smart_log)) { PrintOut(LOG_INFO, "Device: %s, failed to read NVMe SMART/Health Information\n", name); CloseDevice(nvmedev, name); return 2; } // Check temperature sensor support if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) { if (!nvme_get_max_temp_kelvin(smart_log)) { PrintOut(LOG_INFO, "Device: %s, no Temperature sensors, ignoring -W %d,%d,%d\n", name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; } } // Init total error count if (cfg.errorlog || cfg.xerrorlog) { state.nvme_err_log_entries = le128_to_uint64(smart_log.num_err_log_entries); } // If no supported tests selected, return if (!( cfg.smartcheck || cfg.errorlog || cfg.xerrorlog || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit )) { CloseDevice(nvmedev, name); return 3; } // Tell user we are registering device PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n", name); // Make sure that init_standby_check() ignores NVMe devices cfg.offlinests_ns = cfg.selfteststs_ns = false; CloseDevice(nvmedev, name); if (!state_path_prefix.empty()) { // Build file name for state file std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_'); std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_'); nsstr[0] = 0; if (nsid != 0xffffffff) snprintf(nsstr, sizeof(nsstr), "-n%u", nsid); cfg.state_file = strprintf("%s%s-%s%s.nvme.state", state_path_prefix.c_str(), model, serial, nsstr); // Read previous state if (read_dev_state(cfg.state_file.c_str(), state)) PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str()); } finish_device_scan(cfg, state); return 0; } // Open device for next check, return false on error static bool open_device(const dev_config & cfg, dev_state & state, smart_device * device, const char * type) { const char * name = cfg.name.c_str(); // If user has asked, test the email warning system if (cfg.emailtest) MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name); // User may have requested (with the -n Directive) to leave the disk // alone if it is in idle or standby mode. In this case check the // power mode first before opening the device for full access, // and exit without check if disk is reported in standby. if (device->is_ata() && cfg.powermode && !state.powermodefail && !state.removed) { // Note that 'is_powered_down()' handles opening the device itself, and // can be used before calling 'open()' (that's the whole point of 'is_powered_down()'!). if (device->is_powered_down()) { // skip at most powerskipmax checks if (!cfg.powerskipmax || state.powerskipcntopen()) { // For removable devices, print error message only once and suppress email if (!cfg.removable) { PrintOut(LOG_INFO, "Device: %s, open() of %s device failed: %s\n", name, type, device->get_errmsg()); MailWarning(cfg, state, 9, "Device: %s, unable to open %s device", name, type); } else if (!state.removed) { PrintOut(LOG_INFO, "Device: %s, removed %s device: %s\n", name, type, device->get_errmsg()); state.removed = true; } else if (debugmode) PrintOut(LOG_INFO, "Device: %s, %s device still removed: %s\n", name, type, device->get_errmsg()); return false; } if (debugmode) PrintOut(LOG_INFO,"Device: %s, opened %s device\n", name, type); if (!cfg.removable) reset_warning_mail(cfg, state, 9, "open of %s device worked again", type); else if (state.removed) { PrintOut(LOG_INFO, "Device: %s, reconnected %s device\n", name, type); state.removed = false; } return true; } // If the self-test log has got more self-test errors (or more recent // self-test errors) recorded, then notify user. static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi) { const char * name = cfg.name.c_str(); if (newi<0) // command failed MailWarning(cfg, state, 8, "Device: %s, Read SMART Self-Test Log Failed", name); else { reset_warning_mail(cfg, state, 8, "Read SMART Self-Test Log worked again"); // old and new error counts int oldc=state.selflogcount; int newc=SELFTEST_ERRORCOUNT(newi); // old and new error timestamps in hours int oldh=state.selfloghour; int newh=SELFTEST_ERRORHOURS(newi); if (oldc 0 && oldh != newh) { // more recent error // a 'more recent' error might actually be a smaller hour number, // if the hour number has wrapped. // There's still a bug here. You might just happen to run a new test // exactly 32768 hours after the previous failure, and have run exactly // 20 tests between the two, in which case smartd will miss the // new failure. PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n", name, newh); MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d", name, newh); state.must_write = true; } // Print info if error entries have disappeared // or newer successful successful extended self-test exits if (oldc > newc) { PrintOut(LOG_INFO, "Device: %s, Self-Test Log error count decreased from %d to %d\n", name, oldc, newc); if (newc == 0) reset_warning_mail(cfg, state, 3, "Self-Test Log does no longer report errors"); } // Needed since self-test error count may DECREASE. Hour might // also have changed. state.selflogcount= newc; state.selfloghour = newh; } return; } // Test types, ordered by priority. static const char test_type_chars[] = "LncrSCO"; static const unsigned num_test_types = sizeof(test_type_chars)-1; // returns test type if time to do test of type testtype, // 0 if not time to do test. static char next_scheduled_test(const dev_config & cfg, dev_state & state, bool scsi, time_t usetime = 0) { // check that self-testing has been requested if (cfg.test_regex.empty()) return 0; // Exit if drive not capable of any test if ( state.not_cap_long && state.not_cap_short && (scsi || (state.not_cap_conveyance && state.not_cap_offline))) return 0; // since we are about to call localtime(), be sure glibc is informed // of any timezone changes we make. if (!usetime) FixGlibcTimeZoneBug(); // Is it time for next check? time_t now = (!usetime ? time(0) : usetime); if (now < state.scheduled_test_next_check) return 0; // Limit time check interval to 90 days if (state.scheduled_test_next_check + (3600L*24*90) < now) state.scheduled_test_next_check = now - (3600L*24*90); // Check interval [state.scheduled_test_next_check, now] for scheduled tests char testtype = 0; time_t testtime = 0; int testhour = 0; int maxtest = num_test_types-1; for (time_t t = state.scheduled_test_next_check; ; ) { struct tm * tms = localtime(&t); // tm_wday is 0 (Sunday) to 6 (Saturday). We use 1 (Monday) to 7 (Sunday). int weekday = (tms->tm_wday ? tms->tm_wday : 7); for (int i = 0; i <= maxtest; i++) { // Skip if drive not capable of this test switch (test_type_chars[i]) { case 'L': if (state.not_cap_long) continue; break; case 'S': if (state.not_cap_short) continue; break; case 'C': if (scsi || state.not_cap_conveyance) continue; break; case 'O': if (scsi || state.not_cap_offline) continue; break; case 'c': case 'n': case 'r': if (scsi || state.not_cap_selective) continue; break; default: continue; } // Try match of "T/MM/DD/d/HH" char pattern[16]; snprintf(pattern, sizeof(pattern), "%c/%02d/%02d/%1d/%02d", test_type_chars[i], tms->tm_mon+1, tms->tm_mday, weekday, tms->tm_hour); if (cfg.test_regex.full_match(pattern)) { // Test found testtype = pattern[0]; testtime = t; testhour = tms->tm_hour; // Limit further matches to higher priority self-tests maxtest = i-1; break; } } // Exit if no tests left or current time reached if (maxtest < 0) break; if (t >= now) break; // Check next hour if ((t += 3600) > now) t = now; } // Do next check not before next hour. struct tm * tmnow = localtime(&now); state.scheduled_test_next_check = now + (3600 - tmnow->tm_min*60 - tmnow->tm_sec); if (testtype) { state.must_write = true; // Tell user if an old test was found. if (!usetime && !(testhour == tmnow->tm_hour && testtime + 3600 > now)) { char datebuf[DATEANDEPOCHLEN]; dateandtimezoneepoch(datebuf, testtime); PrintOut(LOG_INFO, "Device: %s, old test of type %c not run at %s, starting now.\n", cfg.name.c_str(), testtype, datebuf); } } return testtype; } // Print a list of future tests. static void PrintTestSchedule(const dev_config_vector & configs, dev_state_vector & states, const smart_device_list & devices) { unsigned numdev = configs.size(); if (!numdev) return; std::vector testcnts(numdev * num_test_types, 0); PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n"); // FixGlibcTimeZoneBug(); // done in PrintOut() time_t now = time(0); char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN]; dateandtimezoneepoch(datenow, now); long seconds; for (seconds=checktime; seconds<3600L*24*90; seconds+=checktime) { // Check for each device whether a test will be run time_t testtime = now + seconds; for (unsigned i = 0; i < numdev; i++) { const dev_config & cfg = configs.at(i); dev_state & state = states.at(i); const char * p; char testtype = next_scheduled_test(cfg, state, devices.at(i)->is_scsi(), testtime); if (testtype && (p = strchr(test_type_chars, testtype))) { unsigned t = (p - test_type_chars); // Report at most 5 tests of each type if (++testcnts[i*num_test_types + t] <= 5) { dateandtimezoneepoch(date, testtime); PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg.name.c_str(), testcnts[i*num_test_types + t], testtype, date); } } } } // Report totals dateandtimezoneepoch(date, now+seconds); PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date); for (unsigned i = 0; i < numdev; i++) { const dev_config & cfg = configs.at(i); bool scsi = devices.at(i)->is_scsi(); for (unsigned t = 0; t < num_test_types; t++) { int cnt = testcnts[i*num_test_types + t]; if (cnt == 0 && !strchr((scsi ? "LS" : "LSCO"), test_type_chars[t])) continue; PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg.name.c_str(), cnt, (cnt==1?"":"s"), test_type_chars[t]); } } } // Return zero on success, nonzero on failure. Perform offline (background) // short or long (extended) self test on given scsi device. static int DoSCSISelfTest(const dev_config & cfg, dev_state & state, scsi_device * device, char testtype) { int retval = 0; const char *testname = 0; const char *name = cfg.name.c_str(); int inProgress; if (scsiSelfTestInProgress(device, &inProgress)) { PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name); state.not_cap_short = state.not_cap_long = true; 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(device); break; case 'L': testname = "Long Self"; retval = scsiSmartExtendSelfTest(device); 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) state.not_cap_long = true; else state.not_cap_short = true; 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. static int DoATASelfTest(const dev_config & cfg, dev_state & state, ata_device * device, char testtype) { const char *name = cfg.name.c_str(); // Read current smart data and check status/capability struct ata_smart_values data; if (ataReadSmartValues(device, &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 int dotest = -1, mode = 0; const char *testname = 0; switch (testtype) { case 'O': testname="Offline Immediate "; if (isSupportExecuteOfflineImmediate(&data)) dotest=OFFLINE_FULL_SCAN; else state.not_cap_offline = true; break; case 'C': testname="Conveyance Self-"; if (isSupportConveyanceSelfTest(&data)) dotest=CONVEYANCE_SELF_TEST; else state.not_cap_conveyance = true; break; case 'S': testname="Short Self-"; if (isSupportSelfTest(&data)) dotest=SHORT_SELF_TEST; else state.not_cap_short = true; break; case 'L': testname="Long Self-"; if (isSupportSelfTest(&data)) dotest=EXTEND_SELF_TEST; else state.not_cap_long = true; break; case 'c': case 'n': case 'r': testname = "Selective Self-"; if (isSupportSelectiveSelfTest(&data)) { dotest = SELECTIVE_SELF_TEST; switch (testtype) { case 'c': mode = SEL_CONT; break; case 'n': mode = SEL_NEXT; break; case 'r': mode = SEL_REDO; break; } } else state.not_cap_selective = true; 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)) { if (cfg.firmwarebugs.is_set(BUG_SAMSUNG3) && data.self_test_exec_status == 0xf0) { PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest " "despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname); } else { 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; } } if (dotest == SELECTIVE_SELF_TEST) { // Set test span ata_selective_selftest_args selargs, prev_args; selargs.num_spans = 1; selargs.span[0].mode = mode; prev_args.num_spans = 1; prev_args.span[0].start = state.selective_test_last_start; prev_args.span[0].end = state.selective_test_last_end; if (ataWriteSelectiveSelfTestLog(device, selargs, &data, state.num_sectors, &prev_args)) { PrintOut(LOG_CRIT, "Device: %s, prepare %sTest failed\n", name, testname); return 1; } uint64_t start = selargs.span[0].start, end = selargs.span[0].end; PrintOut(LOG_INFO, "Device: %s, %s test span at LBA %" PRIu64 " - %" PRIu64 " (%" PRIu64 " sectors, %u%% - %u%% of disk).\n", name, (selargs.span[0].mode == SEL_NEXT ? "next" : "redo"), start, end, end - start + 1, (unsigned)((100 * start + state.num_sectors/2) / state.num_sectors), (unsigned)((100 * end + state.num_sectors/2) / state.num_sectors)); state.selective_test_last_start = start; state.selective_test_last_end = end; } // execute the test, and return status int retval = smartcommandhandler(device, IMMEDIATE_OFFLINE, dotest, NULL); if (retval) { PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname); return retval; } // Report recent test start to do_disable_standby_check() // and force log of next test status if (testtype == 'O') state.offline_started = true; else state.selftest_started = true; PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname); return 0; } // Check pending sector count attribute values (-C, -U directives). static void check_pending(const dev_config & cfg, dev_state & state, unsigned char id, bool increase_only, const ata_smart_values & smartval, int mailtype, const char * msg) { // Find attribute index int i = ata_find_attr_index(id, smartval); if (!(i >= 0 && ata_find_attr_index(id, state.smartval) == i)) return; // No report if no sectors pending. uint64_t rawval = ata_get_attr_raw_value(smartval.vendor_attributes[i], cfg.attribute_defs); if (rawval == 0) { reset_warning_mail(cfg, state, mailtype, "No more %s", msg); return; } // If attribute is not reset, report only sector count increases. uint64_t prev_rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs); if (!(!increase_only || prev_rawval < rawval)) return; // Format message. std::string s = strprintf("Device: %s, %" PRId64 " %s", cfg.name.c_str(), rawval, msg); if (prev_rawval > 0 && rawval != prev_rawval) s += strprintf(" (changed %+" PRId64 ")", rawval - prev_rawval); PrintOut(LOG_CRIT, "%s\n", s.c_str()); MailWarning(cfg, state, mailtype, "%s", s.c_str()); state.must_write = true; } // Format Temperature value static const char * fmt_temp(unsigned char x, char (& buf)[20]) { if (!x) // unset return "??"; snprintf(buf, sizeof(buf), "%u", x); return buf; } // Check Temperature limits static void CheckTemperature(const dev_config & cfg, dev_state & state, unsigned char currtemp, unsigned char triptemp) { if (!(0 < currtemp && currtemp < 255)) { PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg.name.c_str()); return; } // Update Max Temperature const char * minchg = "", * maxchg = ""; if (currtemp > state.tempmax) { if (state.tempmax) maxchg = "!"; state.tempmax = currtemp; state.must_write = true; } char buf[20]; if (!state.temperature) { // First check if (!state.tempmin || currtemp < state.tempmin) // Delay Min Temperature update by ~ 30 minutes. state.tempmin_delay = time(0) + CHECKTIME - 60; PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius (Min/Max %s/%u%s)\n", cfg.name.c_str(), (int)currtemp, fmt_temp(state.tempmin, buf), state.tempmax, maxchg); if (triptemp) PrintOut(LOG_INFO, " [trip Temperature is %d Celsius]\n", (int)triptemp); state.temperature = currtemp; } else { if (state.tempmin_delay) { // End Min Temperature update delay if ... if ( (state.tempmin && currtemp > state.tempmin) // current temp exceeds recorded min, || (state.tempmin_delay <= time(0))) { // or delay time is over. state.tempmin_delay = 0; if (!state.tempmin) state.tempmin = 255; } } // Update Min Temperature if (!state.tempmin_delay && currtemp < state.tempmin) { state.tempmin = currtemp; state.must_write = true; if (currtemp != state.temperature) minchg = "!"; } // Track changes if (cfg.tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)state.temperature) >= cfg.tempdiff)) { PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %s%s/%u%s)\n", cfg.name.c_str(), (int)currtemp-(int)state.temperature, currtemp, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); state.temperature = currtemp; } } // Check limits if (cfg.tempcrit && currtemp >= cfg.tempcrit) { PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)", cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); } else if (cfg.tempinfo && currtemp >= cfg.tempinfo) { PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %s%s/%u%s)\n", cfg.name.c_str(), currtemp, cfg.tempinfo, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); } else if (cfg.tempcrit) { unsigned char limit = (cfg.tempinfo ? cfg.tempinfo : cfg.tempcrit-5); if (currtemp < limit) reset_warning_mail(cfg, state, 12, "Temperature %u Celsius dropped below %u Celsius", currtemp, limit); } } // Check normalized and raw attribute values. static void check_attribute(const dev_config & cfg, dev_state & state, const ata_smart_attribute & attr, const ata_smart_attribute & prev, int attridx, const ata_smart_threshold_entry * thresholds) { // Check attribute and threshold ata_attr_state attrstate = ata_get_attr_state(attr, attridx, thresholds, cfg.attribute_defs); if (attrstate == ATTRSTATE_NON_EXISTING) return; // If requested, check for usage attributes that have failed. if ( cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW && !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) { std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm); PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str()); MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str()); state.must_write = true; } // Return if we're not tracking this type of attribute bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags); if (!( ( prefail && cfg.prefail) || (!prefail && cfg.usage ))) return; // Return if '-I ID' was specified if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGNORE)) return; // Issue warning if they don't have the same ID in all structures. if (attr.id != prev.id) { PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d\n", cfg.name.c_str(), attr.id, prev.id); return; } // Compare normalized values if valid. bool valchanged = false; if (attrstate > ATTRSTATE_NO_NORMVAL) { if (attr.current != prev.current) valchanged = true; } // Compare raw values if requested. bool rawchanged = false; if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW)) { if ( ata_get_attr_raw_value(attr, cfg.attribute_defs) != ata_get_attr_raw_value(prev, cfg.attribute_defs)) rawchanged = true; } // Return if no change if (!(valchanged || rawchanged)) return; // Format value strings std::string currstr, prevstr; if (attrstate == ATTRSTATE_NO_NORMVAL) { // Print raw values only currstr = strprintf("%s (Raw)", ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str()); prevstr = strprintf("%s (Raw)", ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str()); } else if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_PRINT)) { // Print normalized and raw values currstr = strprintf("%d [Raw %s]", attr.current, ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str()); prevstr = strprintf("%d [Raw %s]", prev.current, ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str()); } else { // Print normalized values only currstr = strprintf("%d", attr.current); prevstr = strprintf("%d", prev.current); } // Format message std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s", cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id, ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm).c_str(), prevstr.c_str(), currstr.c_str()); // Report this change as critical ? if ( (valchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_AS_CRIT)) || (rawchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_AS_CRIT))) { PrintOut(LOG_CRIT, "%s\n", msg.c_str()); MailWarning(cfg, state, 2, "%s", msg.c_str()); } else { PrintOut(LOG_INFO, "%s\n", msg.c_str()); } state.must_write = true; } static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device * atadev, bool firstpass, bool allow_selftests) { if (!open_device(cfg, state, atadev, "ATA")) return 1; const char * name = cfg.name.c_str(); // 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 && !state.powermodefail) { int dontcheck=0, powermode=ataCheckPowerMode(atadev); const char * mode = 0; if (0 <= powermode && powermode < 0xff) { // wait for possible spin up and check again int powermode2; sleep(5); powermode2 = ataCheckPowerMode(atadev); 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 0x00: // STANDBY mode="STANDBY"; if (cfg.powermode>=2) dontcheck=1; break; case 0x01: // STANDBY_Y mode="STANDBY_Y"; if (cfg.powermode>=2) dontcheck=1; break; case 0x80: // IDLE mode="IDLE"; if (cfg.powermode>=3) dontcheck=1; break; case 0x81: // IDLE_A mode="IDLE_A"; if (cfg.powermode>=3) dontcheck=1; break; case 0x82: // IDLE_B mode="IDLE_B"; if (cfg.powermode>=3) dontcheck=1; break; case 0x83: // IDLE_C mode="IDLE_C"; if (cfg.powermode>=3) dontcheck=1; break; case 0xff: // ACTIVE/IDLE case 0x40: // ACTIVE case 0x41: // ACTIVE mode="ACTIVE or IDLE"; break; default: // UNKNOWN PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n", name, powermode); state.powermodefail = true; break; } // if we are going to skip a check, return now if (dontcheck){ // skip at most powerskipmax checks if (!cfg.powerskipmax || state.powerskipcnt= errcnt2 ? errcnt1 : errcnt2); // did command fail? if (newc<0) // lack of PrintOut here is INTENTIONAL MailWarning(cfg, state, 7, "Device: %s, Read SMART Error Log Failed", name); // has error count increased? int oldc = state.ataerrorcount; if (newc>oldc){ PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n", name, oldc, newc); MailWarning(cfg, state, 4, "Device: %s, ATA error count increased from %d to %d", name, oldc, newc); state.must_write = true; } if (newc>=0) state.ataerrorcount=newc; } // if the user has asked, and device is capable (or we're not yet // sure) check whether a self test should be done now. if (allow_selftests && !cfg.test_regex.empty()) { char testtype = next_scheduled_test(cfg, state, false/*!scsi*/); if (testtype) DoATASelfTest(cfg, state, atadev, testtype); } // Don't leave device open -- the OS/user may want to access it // before the next smartd cycle! CloseDevice(atadev, name); // Copy ATA attribute values to persistent state state.update_persistent_state(); return 0; } static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_device * scsidev, bool allow_selftests) { if (!open_device(cfg, state, scsidev, "SCSI")) return 1; const char * name = cfg.name.c_str(); uint8_t asc = 0, ascq = 0; uint8_t currenttemp = 0, triptemp = 0; if (!state.SuppressReport) { if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported, &asc, &ascq, ¤ttemp, &triptemp)) { PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n", name); MailWarning(cfg, state, 6, "Device: %s, failed to read SMART values", name); state.SuppressReport = 1; } } if (asc > 0) { const char * cp = scsiGetIEString(asc, ascq); if (cp) { PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp); } else if (asc == 4 && ascq == 9) { PrintOut(LOG_INFO,"Device: %s, self-test in progress\n", name); } else if (debugmode) PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n", name, (int)asc, (int)ascq); } else if (debugmode) PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name); // check temperature limits if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) CheckTemperature(cfg, state, currenttemp, triptemp); // check if number of selftest errors has increased (note: may also DECREASE) if (cfg.selftest) CheckSelfTestLogs(cfg, state, scsiCountFailedSelfTests(scsidev, 0)); if (allow_selftests && !cfg.test_regex.empty()) { char testtype = next_scheduled_test(cfg, state, true/*scsi*/); if (testtype) DoSCSISelfTest(cfg, state, scsidev, testtype); } if (!cfg.attrlog_file.empty()){ // saving error counters to state uint8_t tBuf[252]; if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev, READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter); state.scsi_error_counters[0].found=1; } if (state.WriteECounterPageSupported && (0 == scsiLogSense(scsidev, WRITE_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[1].errCounter); state.scsi_error_counters[1].found=1; } if (state.VerifyECounterPageSupported && (0 == scsiLogSense(scsidev, VERIFY_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[2].errCounter); state.scsi_error_counters[2].found=1; } if (state.NonMediumErrorPageSupported && (0 == scsiLogSense(scsidev, NON_MEDIUM_ERROR_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { scsiDecodeNonMediumErrPage(tBuf, &state.scsi_nonmedium_error.nme); state.scsi_nonmedium_error.found=1; } // store temperature if not done by CheckTemperature() above if (!(cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)) state.temperature = currenttemp; } CloseDevice(scsidev, name); return 0; } static int NVMeCheckDevice(const dev_config & cfg, dev_state & state, nvme_device * nvmedev) { if (!open_device(cfg, state, nvmedev, "NVMe")) return 1; const char * name = cfg.name.c_str(); // Read SMART/Health log nvme_smart_log smart_log; if (!nvme_read_smart_log(nvmedev, smart_log)) { PrintOut(LOG_INFO, "Device: %s, failed to read NVMe SMART/Health Information\n", name); MailWarning(cfg, state, 6, "Device: %s, failed to read NVMe SMART/Health Information", name); state.must_write = true; return 0; } // Check Critical Warning bits if (cfg.smartcheck && smart_log.critical_warning) { unsigned char w = smart_log.critical_warning; std::string msg; static const char * const wnames[] = {"LowSpare", "Temperature", "Reliability", "R/O", "VolMemBackup"}; for (unsigned b = 0, cnt = 0; b < 8 ; b++) { if (!(w & (1 << b))) continue; if (cnt) msg += ", "; if (++cnt > 3) { msg += "..."; break; } if (b >= sizeof(wnames)/sizeof(wnames[0])) { msg += "*Unknown*"; break; } msg += wnames[b]; } PrintOut(LOG_CRIT, "Device: %s, Critical Warning (0x%02x): %s\n", name, w, msg.c_str()); MailWarning(cfg, state, 1, "Device: %s, Critical Warning (0x%02x): %s", name, w, msg.c_str()); state.must_write = true; } // Check temperature limits if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) { int k = nvme_get_max_temp_kelvin(smart_log); // Convert Kelvin to positive Celsius (TODO: Allow negative temperatures) int c = k - 273; if (c < 1) c = 1; else if (c > 0xff) c = 0xff; CheckTemperature(cfg, state, c, 0); } // Check if number of errors has increased if (cfg.errorlog || cfg.xerrorlog) { uint64_t oldcnt = state.nvme_err_log_entries; uint64_t newcnt = le128_to_uint64(smart_log.num_err_log_entries); if (newcnt > oldcnt) { PrintOut(LOG_CRIT, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64 "\n", name, oldcnt, newcnt); MailWarning(cfg, state, 4, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64, name, oldcnt, newcnt); state.must_write = true; } state.nvme_err_log_entries = newcnt; } CloseDevice(nvmedev, name); return 0; } // 0=not used, 1=not disabled, 2=disable rejected by OS, 3=disabled static int standby_disable_state = 0; static void init_disable_standby_check(dev_config_vector & configs) { // Check for '-l offlinests,ns' or '-l selfteststs,ns' directives bool sts1 = false, sts2 = false; for (unsigned i = 0; i < configs.size() && !(sts1 || sts2); i++) { const dev_config & cfg = configs.at(i); if (cfg.offlinests_ns) sts1 = true; if (cfg.selfteststs_ns) sts2 = true; } // Check for support of disable auto standby // Reenable standby if smartd.conf was reread if (sts1 || sts2 || standby_disable_state == 3) { if (!smi()->disable_system_auto_standby(false)) { if (standby_disable_state == 3) PrintOut(LOG_CRIT, "System auto standby enable failed: %s\n", smi()->get_errmsg()); if (sts1 || sts2) { PrintOut(LOG_INFO, "Disable auto standby not supported, ignoring ',ns' from %s%s%s\n", (sts1 ? "-l offlinests,ns" : ""), (sts1 && sts2 ? " and " : ""), (sts2 ? "-l selfteststs,ns" : "")); sts1 = sts2 = false; } } } standby_disable_state = (sts1 || sts2 ? 1 : 0); } static void do_disable_standby_check(const dev_config_vector & configs, const dev_state_vector & states) { if (!standby_disable_state) return; // Check for just started or still running self-tests bool running = false; for (unsigned i = 0; i < configs.size() && !running; i++) { const dev_config & cfg = configs.at(i); const dev_state & state = states.at(i); if ( ( cfg.offlinests_ns && (state.offline_started || is_offl_coll_in_progress(state.smartval.offline_data_collection_status))) || ( cfg.selfteststs_ns && (state.selftest_started || is_self_test_in_progress(state.smartval.self_test_exec_status))) ) running = true; // state.offline/selftest_started will be reset after next logging of test status } // Disable/enable auto standby and log state changes if (!running) { if (standby_disable_state != 1) { if (!smi()->disable_system_auto_standby(false)) PrintOut(LOG_CRIT, "Self-test(s) completed, system auto standby enable failed: %s\n", smi()->get_errmsg()); else PrintOut(LOG_INFO, "Self-test(s) completed, system auto standby enabled\n"); standby_disable_state = 1; } } else if (!smi()->disable_system_auto_standby(true)) { if (standby_disable_state != 2) { PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disable rejected: %s\n", smi()->get_errmsg()); standby_disable_state = 2; } } else { if (standby_disable_state != 3) { PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disabled\n"); standby_disable_state = 3; } } } // Checks the SMART status of all ATA and SCSI devices static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices, bool firstpass, bool allow_selftests) { for (unsigned i = 0; i < configs.size(); i++) { const dev_config & cfg = configs.at(i); dev_state & state = states.at(i); smart_device * dev = devices.at(i); if (dev->is_ata()) ATACheckDevice(cfg, state, dev->to_ata(), firstpass, allow_selftests); else if (dev->is_scsi()) SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests); else if (dev->is_nvme()) NVMeCheckDevice(cfg, state, dev->to_nvme()); } do_disable_standby_check(configs, states); } // Install all signal handlers static void install_signal_handlers() { // normal and abnormal exit set_signal_if_not_ignored(SIGTERM, sighandler); set_signal_if_not_ignored(SIGQUIT, sighandler); // in debug mode, ==> HUP set_signal_if_not_ignored(SIGINT, (debugmode ? HUPhandler : sighandler)); // Catch HUP and USR1 set_signal_if_not_ignored(SIGHUP, HUPhandler); set_signal_if_not_ignored(SIGUSR1, USR1handler); #ifdef _WIN32 set_signal_if_not_ignored(SIGUSR2, USR2handler); #endif } #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 static time_t dosleep(time_t wakeuptime, bool & sigwakeup, int numdev) { // If past wake-up-time, compute next wake-up-time time_t timenow=time(NULL); while (wakeuptime<=timenow){ int intervals=1+(timenow-wakeuptime)/checktime; wakeuptime+=intervals*checktime; } notify_wait(wakeuptime, numdev); // sleep until we catch SIGUSR1 or have completed sleeping int addtime = 0; while (timenow < wakeuptime+addtime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT) { // protect user again system clock being adjusted backwards if (wakeuptime>timenow+checktime){ PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n"); wakeuptime=timenow+checktime; } // Exit sleep when time interval has expired or a signal is received sleep(wakeuptime+addtime-timenow); #ifdef _WIN32 // toggle debug mode? if (caughtsigUSR2) { ToggleDebugMode(); caughtsigUSR2 = 0; } #endif timenow=time(NULL); // Actual sleep time too long? if (!addtime && timenow > wakeuptime+60) { if (debugmode) PrintOut(LOG_INFO, "Sleep time was %d seconds too long, assuming wakeup from standby mode.\n", (int)(timenow-wakeuptime)); // Wait another 20 seconds to avoid I/O errors during disk spin-up addtime = timenow-wakeuptime+20; // Use next wake-up-time if close int nextcheck = checktime - addtime % checktime; if (nextcheck <= 20) addtime += nextcheck; } } // 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; sigwakeup = true; } // return adjusted wakeuptime return wakeuptime; } // Print out a list of valid arguments for the Directive d static void printoutvaliddirectiveargs(int priority, char d) { switch (d) { case 'n': PrintOut(priority, "never[,N][,q], sleep[,N][,q], standby[,N][,q], idle[,N][,q]"); break; case 's': PrintOut(priority, "valid_regular_expression"); break; case 'd': PrintOut(priority, "%s", smi()->get_valid_dev_types_str().c_str()); 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': PrintOut(priority, "\n%s\n", create_vendor_attribute_arg_list().c_str()); break; case 'P': PrintOut(priority, "use, ignore, show, showall"); break; case 'F': PrintOut(priority, "%s", get_valid_firmwarebug_args()); break; case 'e': PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], dsn,[on|off] " "security-freeze, standby,[N|off], wcache,[on|off]"); break; } } // exits with an error message, or returns integer value of token static int GetInteger(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile, int min, int max, char * suffix = 0) { // 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", cfgfile, lineno, name, token, min, max); return -1; } // get argument value (base 10), check that it's integer, and in-range char *endptr; int val = strtol(arg,&endptr,10); // optional suffix present? if (suffix) { if (!strcmp(endptr, suffix)) endptr += strlen(suffix); else *suffix = 0; } if (!(!*endptr && min <= val && val <= max)) { PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n", cfgfile, lineno, name, token, arg, min, max); return -1; } // all is well; return value return val; } // Get 1-3 small integer(s) for '-W' directive static int Get3Integers(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile, unsigned char *val1, unsigned char *val2, unsigned char *val3) { unsigned v1 = 0, v2 = 0, v3 = 0; int n1 = -1, n2 = -1, n3 = -1, len; if (!arg) { PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes 1-3 integer argument(s) from 0 to 255.\n", cfgfile, lineno, name, token); return -1; } len = strlen(arg); if (!( sscanf(arg, "%u%n,%u%n,%u%n", &v1, &n1, &v2, &n2, &v3, &n3) >= 1 && (n1 == len || n2 == len || n3 == len) && v1 <= 255 && v2 <= 255 && v3 <= 255)) { PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs 1-3 integer(s) from 0 to 255.\n", cfgfile, lineno, name, token, arg); return -1; } *val1 = (unsigned char)v1; *val2 = (unsigned char)v2; *val3 = (unsigned char)v3; return 0; } #ifdef _WIN32 // Concatenate strtok() results if quoted with "..." static const char * strtok_dequote(const char * delimiters) { const char * t = strtok(0, delimiters); if (!t || t[0] != '"') return t; static std::string token; token = t+1; for (;;) { t = strtok(0, delimiters); if (!t || !*t) return "\""; token += ' '; int len = strlen(t); if (t[len-1] == '"') { token += std::string(t, len-1); break; } token += t; } return token.c_str(); } #endif // _WIN32 // This function returns 1 if it has correctly parsed one token (and // any arguments), else zero if no tokens remain. It returns -1 if an // error was encountered. static int ParseToken(char * token, dev_config & cfg, smart_devtype_list & scan_types) { char sym; const char * name = cfg.name.c_str(); int lineno=cfg.lineno; const char *delim = " \n\t"; int badarg = 0; int missingarg = 0; const char *arg = 0; // 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]; // parse the token and swallow its argument int val; char plus[] = "+", excl[] = "!"; switch (sym) { case 'C': // monitor current pending sector count (default 197) if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0) return -1; cfg.curr_pending_id = (unsigned char)val; cfg.curr_pending_incr = (*plus == '+'); cfg.curr_pending_set = true; break; case 'U': // monitor offline uncorrectable sectors (default 198) if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0) return -1; cfg.offl_pending_id = (unsigned char)val; cfg.offl_pending_incr = (*plus == '+'); cfg.offl_pending_set = true; 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 = false; } else if (!strcmp(arg, "permissive")) { // Permissive mode; ignore errors from Mandatory SMART commands cfg.permissive = true; } else { badarg = 1; } break; case 'd': // specify the device type if ((arg = strtok(NULL, delim)) == NULL) { missingarg = 1; } else if (!strcmp(arg, "ignore")) { cfg.ignore = true; } else if (!strcmp(arg, "removable")) { cfg.removable = true; } else if (!strcmp(arg, "auto")) { cfg.dev_type = ""; scan_types.clear(); } else { cfg.dev_type = arg; scan_types.push_back(arg); } break; case 'F': // fix firmware bug if (!(arg = strtok(0, delim))) missingarg = 1; else if (!parse_firmwarebug_def(arg, cfg.firmwarebugs)) badarg = 1; break; case 'H': // check SMART status cfg.smartcheck = true; break; case 'f': // check for failure of usage attributes cfg.usagefailed = true; break; case 't': // track changes in all vendor attributes cfg.prefail = true; cfg.usage = true; break; case 'p': // track changes in prefail vendor attributes cfg.prefail = true; break; case 'u': // track changes in usage vendor attributes cfg.usage = true; 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 = true; } else if (!strcmp(arg, "error")) { // track changes in ATA error log cfg.errorlog = true; } else if (!strcmp(arg, "xerror")) { // track changes in Extended Comprehensive SMART error log cfg.xerrorlog = true; } else if (!strcmp(arg, "offlinests")) { // track changes in offline data collection status cfg.offlinests = true; } else if (!strcmp(arg, "offlinests,ns")) { // track changes in offline data collection status, disable auto standby cfg.offlinests = cfg.offlinests_ns = true; } else if (!strcmp(arg, "selfteststs")) { // track changes in self-test execution status cfg.selfteststs = true; } else if (!strcmp(arg, "selfteststs,ns")) { // track changes in self-test execution status, disable auto standby cfg.selfteststs = cfg.selfteststs_ns = true; } else if (!strncmp(arg, "scterc,", sizeof("scterc,")-1)) { // set SCT Error Recovery Control unsigned rt = ~0, wt = ~0; int nc = -1; sscanf(arg,"scterc,%u,%u%n", &rt, &wt, &nc); if (nc == (int)strlen(arg) && rt <= 999 && wt <= 999) { cfg.sct_erc_set = true; cfg.sct_erc_readtime = rt; cfg.sct_erc_writetime = wt; } else badarg = 1; } else { badarg = 1; } break; case 'a': // monitor everything cfg.smartcheck = true; cfg.prefail = true; cfg.usagefailed = true; cfg.usage = true; cfg.selftest = true; cfg.errorlog = true; cfg.selfteststs = true; 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 { char *endptr = NULL; char *next = strchr(const_cast(arg), ','); cfg.powerquiet = false; cfg.powerskipmax = 0; if (next!=NULL) *next='\0'; if (!strcmp(arg, "never")) cfg.powermode = 0; else if (!strcmp(arg, "sleep")) cfg.powermode = 1; else if (!strcmp(arg, "standby")) cfg.powermode = 2; else if (!strcmp(arg, "idle")) cfg.powermode = 3; else badarg = 1; // if optional arguments are present if (!badarg && next!=NULL) { next++; cfg.powerskipmax = strtol(next, &endptr, 10); if (endptr == next) cfg.powerskipmax = 0; else { next = endptr + (*endptr != '\0'); if (cfg.powerskipmax <= 0) badarg = 1; } if (*next != '\0') { if (!strcmp("q", next)) cfg.powerquiet = true; else { badarg = 1; } } } } break; case 'S': // automatic attribute autosave enable/disable if ((arg = strtok(NULL, delim)) == NULL) { missingarg = 1; } else if (!strcmp(arg, "on")) { cfg.autosave = 2; } else if (!strcmp(arg, "off")) { cfg.autosave = 1; } else { badarg = 1; } break; case 's': // warn user, and delete any previously given -s REGEXP Directives if (!cfg.test_regex.empty()){ PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n", configfile, lineno, name, cfg.test_regex.get_pattern()); cfg.test_regex = regular_expression(); } // check for missing argument if (!(arg = strtok(NULL, delim))) { missingarg = 1; } // Compile regex else { if (!cfg.test_regex.compile(arg)) { // not a valid regular expression! PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n", configfile, lineno, name, arg, cfg.test_regex.get_errmsg()); 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 (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))]) 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 (!cfg.emailaddress.empty()) PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", configfile, lineno, name, cfg.emailaddress.c_str()); cfg.emailaddress = arg; } break; case 'M': // email warning options if (!(arg = strtok(NULL, delim))) missingarg = 1; else if (!strcmp(arg, "once")) cfg.emailfreq = 1; else if (!strcmp(arg, "daily")) cfg.emailfreq = 2; else if (!strcmp(arg, "diminishing")) cfg.emailfreq = 3; else if (!strcmp(arg, "test")) cfg.emailtest = 1; else if (!strcmp(arg, "exec")) { // Get the next argument (the command line) #ifdef _WIN32 // Allow "/path name/with spaces/..." on Windows arg = strtok_dequote(delim); if (arg && arg[0] == '"') { PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument: missing closing quote\n", configfile, lineno, name, token); return -1; } #else arg = strtok(0, delim); #endif if (!arg) { PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", configfile, lineno, name, token); return -1; } // Free the last cmd line given if any, and copy new one if (!cfg.emailcmdline.empty()) PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n", configfile, lineno, name, cfg.emailcmdline.c_str()); cfg.emailcmdline = arg; } 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; cfg.monitor_attr_flags.set(val, MONITOR_IGN_FAILUSE); break; case 'I': // ignore attribute for tracking purposes if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0) return -1; cfg.monitor_attr_flags.set(val, MONITOR_IGNORE); break; case 'r': // print raw value when tracking if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0) return -1; cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT); if (*excl == '!') // attribute change is critical cfg.monitor_attr_flags.set(val, MONITOR_AS_CRIT); 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, excl)) < 0) return -1; cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT|MONITOR_RAW); if (*excl == '!') // raw value change is critical cfg.monitor_attr_flags.set(val, MONITOR_RAW_AS_CRIT); break; case 'W': // track Temperature if (Get3Integers(arg=strtok(NULL, delim), name, token, lineno, configfile, &cfg.tempdiff, &cfg.tempinfo, &cfg.tempcrit) < 0) return -1; break; case 'v': // non-default vendor-specific attribute meaning if (!(arg=strtok(NULL,delim))) { missingarg = 1; } else if (!parse_attribute_def(arg, cfg.attribute_defs, PRIOR_USER)) { 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; case 'e': // Various ATA settings if (!(arg = strtok(NULL, delim))) { missingarg = true; } else { char arg2[16+1]; unsigned val; int n1 = -1, n2 = -1, n3 = -1, len = strlen(arg); if (sscanf(arg, "%16[^,=]%n%*[,=]%n%u%n", arg2, &n1, &n2, &val, &n3) >= 1 && (n1 == len || n2 > 0)) { bool on = (n2 > 0 && !strcmp(arg+n2, "on")); bool off = (n2 > 0 && !strcmp(arg+n2, "off")); if (n3 != len) val = ~0U; if (!strcmp(arg2, "aam")) { if (off) cfg.set_aam = -1; else if (val <= 254) cfg.set_aam = val + 1; else badarg = true; } else if (!strcmp(arg2, "apm")) { if (off) cfg.set_apm = -1; else if (1 <= val && val <= 254) cfg.set_apm = val + 1; else badarg = true; } else if (!strcmp(arg2, "lookahead")) { if (off) cfg.set_lookahead = -1; else if (on) cfg.set_lookahead = 1; else badarg = true; } else if (!strcmp(arg, "security-freeze")) { cfg.set_security_freeze = true; } else if (!strcmp(arg2, "standby")) { if (off) cfg.set_standby = 0 + 1; else if (val <= 255) cfg.set_standby = val + 1; else badarg = true; } else if (!strcmp(arg2, "wcache")) { if (off) cfg.set_wcache = -1; else if (on) cfg.set_wcache = 1; else badarg = true; } else if (!strcmp(arg2, "dsn")) { if (off) cfg.set_dsn = -1; else if (on) cfg.set_dsn = 1; else badarg = true; } else badarg = true; } else badarg = true; } 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; } return 1; } // Scan directive for configuration file #define SCANDIRECTIVE "DEVICESCAN" // This is the routine that adds things to the conf_entries list. // // Return values are: // 1: parsed a normal line // 0: found DEFAULT setting or comment or blank line // -1: found SCANDIRECTIVE line // -2: found an error // // Note: this routine modifies *line from the caller! static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & default_conf, smart_devtype_list & scan_types, int lineno, /*const*/ char * line) { const char *delim = " \n\t"; // get first token: device name. If a comment, skip line const char * name = strtok(line, delim); if (!name || *name == '#') return 0; // Check device name for DEFAULT or DEVICESCAN int retval; if (!strcmp("DEFAULT", name)) { retval = 0; // Restart with empty defaults default_conf = dev_config(); } else { retval = (!strcmp(SCANDIRECTIVE, name) ? -1 : 1); // Init new entry with current defaults conf_entries.push_back(default_conf); } dev_config & cfg = (retval ? conf_entries.back() : default_conf); cfg.name = name; // Later replaced by dev->get_info().info_name cfg.dev_name = name; // If DEVICESCAN later replaced by get->dev_info().dev_name cfg.lineno = lineno; // parse tokens one at a time from the file. while (char * token = strtok(0, delim)) { int rc = ParseToken(token, cfg, scan_types); if (rc < 0) // error found on the line return -2; if (rc == 0) // No tokens left break; // PrintOut(LOG_INFO,"Parsed token %s\n",token); } // Check for multiple -d TYPE directives if (retval != -1 && scan_types.size() > 1) { PrintOut(LOG_CRIT, "Drive: %s, invalid multiple -d TYPE Directives on line %d of file %s\n", cfg.name.c_str(), cfg.lineno, configfile); return -2; } // Don't perform checks below for DEFAULT entries if (retval == 0) return retval; // If NO monitoring directives are set, then set all of them. if (!( cfg.smartcheck || cfg.selftest || cfg.errorlog || cfg.xerrorlog || cfg.offlinests || cfg.selfteststs || cfg.usagefailed || cfg.prefail || cfg.usage || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)) { PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n", cfg.name.c_str(), cfg.lineno, configfile); cfg.smartcheck = true; cfg.usagefailed = true; cfg.prefail = true; cfg.usage = true; cfg.selftest = true; cfg.errorlog = true; cfg.selfteststs = true; } // additional sanity check. Has user set -M options without -m? if (cfg.emailaddress.empty() && (!cfg.emailcmdline.empty() || cfg.emailfreq || cfg.emailtest)){ PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n", cfg.name.c_str(), cfg.lineno, configfile); return -2; } // has the user has set ? if (cfg.emailaddress == "") { // check that -M exec is also set if (cfg.emailcmdline.empty()){ PrintOut(LOG_CRIT,"Drive: %s, -m Directive on line %d of file %s needs -M exec Directive\n", cfg.name.c_str(), cfg.lineno, configfile); return -2; } // From here on the sign of is cfg.emailaddress.empty() and !cfg.emailcmdline.empty() cfg.emailaddress.clear(); } return retval; } // 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 // possibilities: // Empty configuration file ==> conf_entries.empty() // No configuration file ==> conf_entries[0].lineno == 0 // SCANDIRECTIVE found ==> conf_entries.back().lineno != 0 (size >= 1) static int ParseConfigFile(dev_config_vector & conf_entries, smart_devtype_list & scan_types) { // maximum line length in configuration file const int MAXLINELEN = 256; // maximum length of a continued line in configuration file const int MAXCONTLINE = 1023; stdio_file f; // Open config file, if it exists and is not if (!(configfile == configfile_stdin)) { // pointer comparison ok here if (!f.open(configfile,"r") && (errno!=ENOENT || !configfile_alt.empty())) { // 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) f.open(stdin); // Start with empty defaults dev_config default_conf; // No configuration file found -- use fake one int entry = 0; if (!f) { char fakeconfig[] = SCANDIRECTIVE " -a"; // TODO: Remove this hack, build cfg_entry. if (ParseConfigLine(conf_entries, default_conf, scan_types, 0, fakeconfig) != -1) throw std::logic_error("Internal error parsing " SCANDIRECTIVE); return 0; } #ifdef __CYGWIN__ setmode(fileno(f), 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 int lineno = 1, cont = 0, contlineno = 0; char line[MAXLINELEN+2]; char fullline[MAXCONTLINE+1]; for (;;) { 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, f); // are we at the end of the file? if (!code){ if (cont) { scandevice = ParseConfigLine(conf_entries, default_conf, scan_types, contlineno, fullline); // See if we found a SCANDIRECTIVE directive if (scandevice==-1) return 0; // did we find a syntax error if (scandevice==-2) return -1; // the final line is part of a continuation line entry+=scandevice; } break; } // input file line number contlineno++; // See if line is too long len=strlen(line); if (len>MAXLINELEN){ const 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); 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); return -1; } // copy string so far into fullline, and increment length snprintf(fullline+cont, sizeof(fullline)-cont, "%s" ,line); cont+=len; // is this a continuation line. If so, replace \ by space and look at next line if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){ *(fullline+(cont-len)+(lastslash-line))=' '; continue; } // Not a continuation line. Parse it scan_types.clear(); scandevice = ParseConfigLine(conf_entries, default_conf, scan_types, contlineno, fullline); // did we find a scandevice directive? if (scandevice==-1) return 0; // did we find a syntax error if (scandevice==-2) return -1; entry+=scandevice; lineno++; cont=0; } // note -- may be zero if syntax of file OK, but no valid entries! return entry; } /* Prints the message "=======> VALID ARGUMENTS ARE: <=======\n", where is the list of valid arguments for option opt. */ static 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, "%s", (char *)s); PrintOut(LOG_CRIT, " <=======\n"); } #ifndef _WIN32 // Report error and return false if specified path is not absolute. static bool check_abs_path(char option, const std::string & path) { if (path.empty() || path[0] == '/') return true; debugmode = 1; PrintHead(); PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <=======\n\n", option, path.c_str()); PrintOut(LOG_CRIT, "Error: relative path names are not allowed\n\n"); return false; } #endif // !_WIN32 // Parses input line, prints usage message and // version/license/copyright messages static int parse_options(int argc, char **argv) { // Init default path names #ifndef _WIN32 configfile = SMARTMONTOOLS_SYSCONFDIR "/smartd.conf"; warning_script = SMARTMONTOOLS_SMARTDSCRIPTDIR "/smartd_warning.sh"; #else std::string exedir = get_exe_dir(); static std::string configfile_str = exedir + "/smartd.conf"; configfile = configfile_str.c_str(); warning_script = exedir + "/smartd_warning.cmd"; #endif // Please update GetValidArgList() if you edit shortopts static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:w:Vh?" #ifdef HAVE_LIBCAP_NG "C" #endif ; // 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' }, #ifndef _WIN32 { "no-fork", no_argument, 0, 'n' }, #else { "service", no_argument, 0, 'n' }, #endif { "pidfile", required_argument, 0, 'p' }, { "report", required_argument, 0, 'r' }, { "savestates", required_argument, 0, 's' }, { "attributelog", required_argument, 0, 'A' }, { "drivedb", required_argument, 0, 'B' }, { "warnexec", required_argument, 0, 'w' }, { "version", no_argument, 0, 'V' }, { "license", no_argument, 0, 'V' }, { "copyright", no_argument, 0, 'V' }, { "help", no_argument, 0, 'h' }, { "usage", no_argument, 0, 'h' }, #ifdef HAVE_LIBCAP_NG { "capabilities", no_argument, 0, 'C' }, #endif { 0, 0, 0, 0 } }; opterr=optopt=0; bool badarg = false; bool use_default_db = true; // set false on '-B FILE' // Parse input options. int optchar; while ((optchar = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { char *arg; char *tailptr; long lchecktime; switch(optchar) { case 'q': // when to quit if (!strcmp(optarg, "nodev")) quit = QUIT_NODEV; else if (!strcmp(optarg, "nodevstartup")) quit = QUIT_NODEVSTARTUP; else if (!strcmp(optarg, "never")) quit = QUIT_NEVER; else if (!strcmp(optarg, "onecheck")) { quit = QUIT_ONECHECK; debugmode = 1; } else if (!strcmp(optarg, "showtests")) { quit = QUIT_SHOWTESTS; debugmode = 1; } else if (!strcmp(optarg, "errors")) quit = QUIT_ERRORS; 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 = 1; break; case 'n': // don't fork() #ifndef _WIN32 // On Windows, --service is already handled by daemon_main() do_fork = false; #endif break; case 'D': // print summary of all valid directives debugmode = 1; Directives(); return 0; 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"); return EXIT_BADCMD; } checktime = (int)lchecktime; break; case 'r': // report IOCTL transactions { int n1 = -1, n2 = -1, len = strlen(optarg); char s[9+1]; unsigned i = 1; sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2); if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) { badarg = true; } else if (!strcmp(s,"ioctl")) { ata_debugmode = scsi_debugmode = nvme_debugmode = i; } else if (!strcmp(s,"ataioctl")) { ata_debugmode = i; } else if (!strcmp(s,"scsiioctl")) { scsi_debugmode = i; } else if (!strcmp(s,"nvmeioctl")) { nvme_debugmode = i; } else { badarg = true; } } break; case 'c': // alternate configuration file if (strcmp(optarg,"-")) configfile = (configfile_alt = optarg).c_str(); else // read from stdin configfile=configfile_stdin; break; case 'p': // output file with PID number pid_file = optarg; break; case 's': // path prefix of persistent state file state_path_prefix = optarg; break; case 'A': // path prefix of attribute log file attrlog_path_prefix = optarg; break; case 'B': { const char * path = optarg; if (*path == '+' && path[1]) path++; else use_default_db = false; unsigned char savedebug = debugmode; debugmode = 1; if (!read_drive_database(path)) return EXIT_BADCMD; debugmode = savedebug; } break; case 'w': warning_script = optarg; break; case 'V': // print version and CVS info debugmode = 1; PrintOut(LOG_INFO, "%s", format_version_info("smartd", true /*full*/).c_str()); return 0; #ifdef HAVE_LIBCAP_NG case 'C': // enable capabilities capabilities_enabled = true; break; #endif case 'h': // help: print summary of command-line options debugmode=1; PrintHead(); Usage(); return 0; case '?': default: // unrecognized option debugmode=1; PrintHead(); // 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"); return EXIT_BADCMD; } 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"); return EXIT_BADCMD; } Usage(); return 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"); return 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"); return EXIT_BADCMD; } // no pidfile in debug mode if (debugmode && !pid_file.empty()) { 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.c_str()); return EXIT_BADCMD; } #ifndef _WIN32 if (!debugmode) { // absolute path names are required due to chdir('/') in daemon_init() if (!( check_abs_path('p', pid_file) && check_abs_path('s', state_path_prefix) && check_abs_path('A', attrlog_path_prefix))) return EXIT_BADCMD; } #endif // Read or init drive database { unsigned char savedebug = debugmode; debugmode = 1; if (!init_drive_database(use_default_db)) return EXIT_BADCMD; debugmode = savedebug; } // Check option compatibility of notify support if (!notify_post_init()) return EXIT_BADCMD; // print header PrintHead(); // No error, continue in main_worker() return -1; } // Function we call if no configuration file was found or if the // SCANDIRECTIVE Directive was found. It makes entries for device // names returned by scan_smart_devices() in os_OSNAME.cpp static int MakeConfigEntries(const dev_config & base_cfg, dev_config_vector & conf_entries, smart_device_list & scanned_devs, const smart_devtype_list & types) { // make list of devices smart_device_list devlist; if (!smi()->scan_smart_devices(devlist, types)) { PrintOut(LOG_CRIT, "DEVICESCAN failed: %s\n", smi()->get_errmsg()); return 0; } // if no devices, return if (devlist.size() <= 0) return 0; // add empty device slots for existing config entries while (scanned_devs.size() < conf_entries.size()) scanned_devs.push_back((smart_device *)0); // loop over entries to create for (unsigned i = 0; i < devlist.size(); i++) { // Move device pointer smart_device * dev = devlist.release(i); scanned_devs.push_back(dev); // Copy configuration, update device and type name conf_entries.push_back(base_cfg); dev_config & cfg = conf_entries.back(); cfg.name = dev->get_info().info_name; cfg.dev_name = dev->get_info().dev_name; cfg.dev_type = dev->get_info().dev_type; } return devlist.size(); } // Returns negative value (see ParseConfigFile()) if config file // had errors, else number of entries which may be zero or positive. static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs) { // parse configuration file configfile (normally /etc/smartd.conf) smart_devtype_list scan_types; int entries = ParseConfigFile(conf_entries, scan_types); if (entries < 0) { // There was an error reading the configuration file. conf_entries.clear(); if (entries == -1) PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile); return entries; } // 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 (!conf_entries.empty()) { // we found a SCANDIRECTIVE or there was no configuration file so // scan. Configuration file's last entry contains all options // that were set dev_config first = conf_entries.back(); conf_entries.pop_back(); if (first.lineno) PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE); else PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile); // make config list of devices to search for MakeConfigEntries(first, conf_entries, scanned_devs, scan_types); // warn user if scan table found no devices if (conf_entries.empty()) PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n"); } else PrintOut(LOG_CRIT, "Configuration file %s parsed but has no entries\n", configfile); return conf_entries.size(); } // Return true if TYPE contains a RAID drive number static bool is_raid_type(const char * type) { if (str_starts_with(type, "sat,")) return false; int i; if (sscanf(type, "%*[^,],%d", &i) != 1) return false; return true; } // Return true if DEV is already in DEVICES[0..NUMDEVS) or IGNORED[*] static bool is_duplicate_device(const smart_device * dev, const smart_device_list & devices, unsigned numdevs, const dev_config_vector & ignored) { const smart_device::device_info & info1 = dev->get_info(); bool is_raid1 = is_raid_type(info1.dev_type.c_str()); for (unsigned i = 0; i < numdevs; i++) { const smart_device::device_info & info2 = devices.at(i)->get_info(); // -d TYPE options must match if RAID drive number is specified if ( info1.dev_name == info2.dev_name && ( info1.dev_type == info2.dev_type || !is_raid1 || !is_raid_type(info2.dev_type.c_str()))) return true; } for (unsigned i = 0; i < ignored.size(); i++) { const dev_config & cfg2 = ignored.at(i); if ( info1.dev_name == cfg2.dev_name && ( info1.dev_type == cfg2.dev_type || !is_raid1 || !is_raid_type(cfg2.dev_type.c_str()))) return true; } return false; } // Register one device, return false on error static bool register_device(dev_config & cfg, dev_state & state, smart_device_auto_ptr & dev, const dev_config_vector * prev_cfgs) { bool scanning; if (!dev) { // Get device of appropriate type dev = smi()->get_smart_device(cfg.name.c_str(), cfg.dev_type.c_str()); if (!dev) { if (cfg.dev_type.empty()) PrintOut(LOG_INFO, "Device: %s, unable to autodetect device type\n", cfg.name.c_str()); else PrintOut(LOG_INFO, "Device: %s, unsupported device type '%s'\n", cfg.name.c_str(), cfg.dev_type.c_str()); return false; } scanning = false; } else { // Use device from device scan scanning = true; } // Save old info smart_device::device_info oldinfo = dev->get_info(); // Open with autodetect support, may return 'better' device dev.replace( dev->autodetect_open() ); // Report if type has changed if (oldinfo.dev_type != dev->get_dev_type()) PrintOut(LOG_INFO, "Device: %s, type changed from '%s' to '%s'\n", cfg.name.c_str(), oldinfo.dev_type.c_str(), dev->get_dev_type()); // Return if autodetect_open() failed if (!dev->is_open()) { if (debugmode || !scanning) PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", dev->get_info_name(), dev->get_errmsg()); return false; } // Update informal name cfg.name = dev->get_info().info_name; PrintOut(LOG_INFO, "Device: %s, opened\n", cfg.name.c_str()); int status; const char * typemsg; // register ATA device if (dev->is_ata()){ typemsg = "ATA"; status = ATADeviceScan(cfg, state, dev->to_ata(), prev_cfgs); } // or register SCSI device else if (dev->is_scsi()){ typemsg = "SCSI"; status = SCSIDeviceScan(cfg, state, dev->to_scsi(), prev_cfgs); } // or register NVMe device else if (dev->is_nvme()) { typemsg = "NVMe"; status = NVMeDeviceScan(cfg, state, dev->to_nvme(), prev_cfgs); } else { PrintOut(LOG_INFO, "Device: %s, neither ATA, SCSI nor NVMe device\n", cfg.name.c_str()); return false; } if (status) { if (!scanning || debugmode) { if (cfg.lineno) PrintOut(scanning ? LOG_INFO : LOG_CRIT, "Unable to register %s device %s at line %d of file %s\n", typemsg, cfg.name.c_str(), cfg.lineno, configfile); else PrintOut(LOG_INFO, "Unable to register %s device %s\n", typemsg, cfg.name.c_str()); } return false; } return true; } // This function tries devices from conf_entries. Each one that can be // registered is moved onto the [ata|scsi]devices lists and removed // from the conf_entries list. static bool register_devices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs, dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices) { // start by clearing lists/memory of ALL existing devices configs.clear(); devices.clear(); states.clear(); // Register entries dev_config_vector ignored_entries; unsigned numnoscan = 0; for (unsigned i = 0; i < conf_entries.size(); i++){ dev_config cfg = conf_entries[i]; if (cfg.ignore) { // Store for is_duplicate_device() check and ignore PrintOut(LOG_INFO, "Device: %s%s%s%s, ignored\n", cfg.name.c_str(), (!cfg.dev_type.empty() ? " [" : ""), cfg.dev_type.c_str(), (!cfg.dev_type.empty() ? "]" : "")); ignored_entries.push_back(cfg); continue; } smart_device_auto_ptr dev; // Device may already be detected during devicescan bool scanning = false; if (i < scanned_devs.size()) { dev = scanned_devs.release(i); if (dev) { // Check for a preceding non-DEVICESCAN entry for the same device if ( (numnoscan || !ignored_entries.empty()) && is_duplicate_device(dev.get(), devices, numnoscan, ignored_entries)) { PrintOut(LOG_INFO, "Device: %s, duplicate, ignored\n", dev->get_info_name()); continue; } scanning = true; } } // Register device // If scanning, pass dev_idinfo of previous devices for duplicate check dev_state state; if (!register_device(cfg, state, dev, (scanning ? &configs : 0))) { // if device is explicitly listed and we can't register it, then // exit unless the user has specified that the device is removable if (!scanning) { if (!(cfg.removable || quit == QUIT_NEVER)) { PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", cfg.name.c_str()); return false; } PrintOut(LOG_INFO, "Device: %s, not available\n", cfg.name.c_str()); // Prevent retry of registration ignored_entries.push_back(cfg); } continue; } // move onto the list of devices configs.push_back(cfg); states.push_back(state); devices.push_back(dev); if (!scanning) numnoscan = devices.size(); } init_disable_standby_check(configs); return true; } // Main program without exception handling static int main_worker(int argc, char **argv) { // Initialize interface smart_interface::init(); if (!smi()) return 1; // Check whether systemd notify is supported and enabled notify_init(); // parse input and print header and usage info if needed int status = parse_options(argc,argv); if (status >= 0) return status; // Configuration for each device dev_config_vector configs; // Device states dev_state_vector states; // Devices to monitor smart_device_list devices; // Drop capabilities if supported and enabled capabilities_drop_now(); notify_msg("Initializing ..."); // the main loop of the code bool firstpass = true, write_states_always = true; time_t wakeuptime = 0; // assert(status < 0); do { // Should we (re)read the config file? if (firstpass || caughtsigHUP){ if (!firstpass) { // Write state files if (!state_path_prefix.empty()) write_all_dev_states(configs, states); 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); notify_msg("Reloading ..."); } { dev_config_vector conf_entries; // Entries read from smartd.conf smart_device_list scanned_devs; // Devices found during scan // (re)reads config file, makes >=0 entries int entries = ReadOrMakeConfigEntries(conf_entries, scanned_devs); if (entries>=0) { // checks devices, then moves onto ata/scsi list or deallocates. if (!register_devices(conf_entries, scanned_devs, configs, states, devices)) { status = EXIT_BADDEV; break; } if (!(configs.size() == devices.size() && configs.size() == states.size())) throw std::logic_error("Invalid result from RegisterDevices"); // Handle limitations if capabilities are dropped capabilities_check_config(configs); } else if ( quit == QUIT_NEVER || ((quit == QUIT_NODEV || quit == QUIT_NODEVSTARTUP) && !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 status = (entries == -3 ? EXIT_READCONF : entries == -2 ? EXIT_NOCONF : EXIT_BADCONF); break; } } if (!( devices.size() > 0 || quit == QUIT_NEVER || (quit == QUIT_NODEVSTARTUP && !firstpass))) { PrintOut(LOG_INFO, "Unable to monitor any SMART enabled devices. %sExiting...\n", (!debugmode ? "Try debug (-d) option. " : "")); status = EXIT_NODEV; break; } // Log number of devices we are monitoring... int numata = 0, numscsi = 0; for (unsigned i = 0; i < devices.size(); i++) { const smart_device * dev = devices.at(i); if (dev->is_ata()) numata++; else if (dev->is_scsi()) numscsi++; } PrintOut(LOG_INFO, "Monitoring %d ATA/SATA, %d SCSI/SAS and %d NVMe devices\n", numata, numscsi, (int)devices.size() - numata - numscsi); if (quit == QUIT_SHOWTESTS) { // user has asked to print test schedule PrintTestSchedule(configs, states, devices); // assert(firstpass); return 0; } // reset signal caughtsigHUP=0; // Always write state files after (re)configuration write_states_always = true; } // check all devices once, // self tests are not started in first pass unless '-q onecheck' is specified notify_check((int)devices.size()); CheckDevicesOnce(configs, states, devices, firstpass, (!firstpass || quit == QUIT_ONECHECK)); // Write state files if (!state_path_prefix.empty()) write_all_dev_states(configs, states, write_states_always); write_states_always = false; // Write attribute logs if (!attrlog_path_prefix.empty()) write_all_dev_attrlogs(configs, states); // user has asked us to exit after first check if (quit == QUIT_ONECHECK) { PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices successfully checked once.\n" "smartd is exiting (exit status 0)\n"); // assert(firstpass); return 0; } if (firstpass) { if (!debugmode) { // fork() into background if needed, close ALL file descriptors, // redirect stdin, stdout, and stderr, chdir to "/". status = daemon_init(); if (status >= 0) return status; // Write PID file if configured if (!write_pid_file()) return EXIT_PID; } // Set exit and signal handlers install_signal_handlers(); // Initialize wakeup time to CURRENT time wakeuptime = time(0); firstpass = false; } // sleep until next check time, or a signal arrives wakeuptime = dosleep(wakeuptime, write_states_always, (int)devices.size()); } while (!caughtsigEXIT); if (caughtsigEXIT && status < 0) { // Loop exited on signal if (caughtsigEXIT == SIGTERM || (debugmode && caughtsigEXIT == SIGQUIT)) { PrintOut(LOG_INFO, "smartd received signal %d: %s\n", caughtsigEXIT, strsignal(caughtsigEXIT)); } else { // Unexpected SIGINT or SIGQUIT PrintOut(LOG_CRIT, "smartd received unexpected signal %d: %s\n", caughtsigEXIT, strsignal(caughtsigEXIT)); status = EXIT_SIGNAL; } } // Status unset above implies success if (status < 0) status = 0; if (!firstpass) { // Loop exited after daemon_init() and write_pid_file() // Write state files only on normal exit if (!status && !state_path_prefix.empty()) write_all_dev_states(configs, states); // Delete PID file, if one was created if (!pid_file.empty() && unlink(pid_file.c_str())) PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", pid_file.c_str(), strerror(errno)); // and this should be the final output from smartd before it exits PrintOut((status ? LOG_CRIT : LOG_INFO), "smartd is exiting (exit status %d)\n", status); } return status; } #ifndef _WIN32 // Main program 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 { int status; try { // Do the real work ... status = main_worker(argc, argv); } catch (const std::bad_alloc & /*ex*/) { // Memory allocation failed (also thrown by std::operator new) PrintOut(LOG_CRIT, "Smartd: Out of memory\n"); status = EXIT_NOMEM; } catch (const std::exception & ex) { // Other fatal errors PrintOut(LOG_CRIT, "Smartd: Exception: %s\n", ex.what()); status = EXIT_BADCODE; } // Check for remaining device objects if (smart_device::get_num_objects() != 0) { PrintOut(LOG_CRIT, "Smartd: Internal Error: %d device object(s) left at exit.\n", smart_device::get_num_objects()); status = EXIT_BADCODE; } if (status == EXIT_BADCODE) PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n"); notify_exit(status); #ifdef _WIN32 daemon_winsvc_exitcode = status; #endif return status; } #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 (SMART) built into " "ATA/SATA and SCSI/SAS hard drives and solid-state drives. " "www.smartmontools.org" }; // 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 smartmontools-7.0/smartd.cygwin.initd.in0000644000175000010010000000762313336335341015452 00000000000000#! /bin/sh # # smartmontools initd file for Cygwin smartd # # Home page of code is: http://www.smartmontools.org # # Copyright (C) 2004-17 Christian Franke # # SPDX-License-Identifier: GPL-2.0-or-later # # $Id: smartd.cygwin.initd.in 4760 2018-08-19 18:45:53Z chrfranke $ # SMARTD_BIN=/usr/local/sbin/smartd # 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 (SMART) built into \ ATA/SATA and SCSI/SAS hard drives and solid-state drives. \ www.smartmontools.org" # Source configuration file. [ -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=; dep2= if cygrunsrv -L 2>/dev/null | grep "^syslogd$" >/dev/null 2>&1; then dep="syslogd" fi if cygrunsrv -L 2>/dev/null | grep "^syslog-ng" >/dev/null 2>&1; then dep2="syslog-ng" fi if [ -z "$dep" ]; then if [ -z "$dep2" ]; then echo "Warning: no syslog service installed, smartd will write to windows event log."; else dep="$dep2" fi else if [ -z "$dep2" ]; then : else dep= echo "Warning: both syslogd and syslog-ng installed, dependency not set." fi fi echo "Installing service ${smartd_svcname}${dep:+ (depending on '$dep')}${smartd_opts:+ with options '$smartd_opts'}:" cygrunsrv -I "$smartd_svcname" -d "$smartd_svcdisp" -f "$smartd_svcdesc" ${dep:+-y} $dep \ -e CYGWIN="$CYGWIN" -p $SMARTD_BIN -a "-n -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 smartmontools-7.0/smartd.freebsd.initd.in0000644000175000010010000000232611725407075015563 00000000000000#!/bin/sh # $FreeBSD: ports/sysutils/smartmontools/files/smartd.in,v 1.4 2012/02/15 08:46:57 dougb Exp $ # # PROVIDE: smartd # REQUIRE: LOGIN # KEYWORD: shutdown nojail # # Define these smartd_* variables in one of these files: # /etc/rc.conf # /etc/rc.conf.local # /etc/rc.conf.d/smartd # # DO NOT CHANGE THESE DEFAULT VALUES HERE . /etc/rc.subr name=smartd rcvar=smartd_enable load_rc_config smartd : ${smartd_enable:="NO"} required_files=${smartd_config:="/usr/local/etc/smartd.conf"} pidfile=${smartd_pidfile:="/var/run/smartd.pid"} command="/usr/local/sbin/smartd" command_args="-c ${required_files} -p ${pidfile}" extra_commands="reload report" reload_cmd="smartd_reload" report_cmd="smartd_report" start_precmd=smartd_prestart smartd_prestart() { case "${smartd_flags}" in -p*|*-p*) err 1 'smartd_flags includes the -p option, use smartd_pidfile instead' ;; esac } smartd_reload() { local status if ! status=`run_rc_command status 2>&1`; then echo $status return 1 fi echo 'Reloading smartd.' kill -HUP $rc_pid } smartd_report() { local status if ! status=`run_rc_command status 2>&1`; then echo $status return 1 fi echo 'Checking SMART devices now.' kill -USR1 $rc_pid } run_rc_command "$1" smartmontools-7.0/smartd.initd.in0000644000175000010010000002760213336335341014152 00000000000000#! /bin/sh # smartmontools init file for smartd # Copyright (C) 2002-8 Bruce Allen # SPDX-License-Identifier: GPL-2.0-or-later # $Id: smartd.initd.in 4760 2018-08-19 18:45:53Z chrfranke $ # 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 $remote_fs # Should-Start: sendmail # Required-Stop: $syslog $remote_fs # Should-Stop: sendmail # 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 # 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@listi.jpberlin.de." 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 pidfile=/var/lock/subsys/smartd config=/etc/smartd.conf start() { [ $UID -eq 0 ] || exit 4 [ -x $SMARTD_BIN ] || exit 5 [ -f $config ] || exit 6 echo -n $"Starting $prog: " daemon $SMARTD_BIN $smartd_opts RETVAL=$? echo [ $RETVAL = 0 ] && touch $pidfile return $RETVAL } stop() { [ $UID -eq 0 ] || exit 4 echo -n $"Shutting down $prog: " killproc $SMARTD_BIN RETVAL=$? echo rm -f $pidfile return $RETVAL } reload() { echo -n $"Reloading $prog daemon configuration: " killproc $SMARTD_BIN -HUP RETVAL=$? echo return $RETVAL } report() { echo -n $"Checking SMART devices now: " killproc $SMARTD_BIN -USR1 RETVAL=$? echo return $RETVAL } case "$1" in start) start ;; stop) stop ;; reload) reload ;; report) report ;; restart) stop start ;; condrestart|try-restart) if [ -f $pidfile ]; then stop start fi ;; force-reload) reload || (stop; start) ;; status) status $prog RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart|try-restart|reload|force-reload|report}" RETVAL=2 [ "$1" = 'usage' ] && RETVAL=0 esac exit $RETVAL # Slackware elif [ -f /etc/slackware-version ] ; then # Source configuration file. This should define the shell variable smartd_opts. # Email smartmontools-support mailing list if there is a better choice # of path for Slackware. [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools RETVAL=0 case "$1" in start) echo -n "Starting smartd: " $SMARTD_BIN $smartd_opts RETVAL=$? echo ;; stop) echo -n "Shutting down smartd: " killall $SMARTD_BIN RETVAL=$? echo ;; restart) $0 stop sleep 1 $0 start RETVAL=$? ;; try-restart) if pidof $SMARTD_BIN >/dev/null; then $0 restart RETVAL=$? fi ;; force-reload) $0 reload || $0 restart RETVAL=$? ;; reload) echo -n "Reloading smartd configuration: " killall -s HUP $SMARTD_BIN RETVAL=$? echo ;; report) echo -n "Checking SMART devices now: " killall -s USR1 $SMARTD_BIN RETVAL=$? echo ;; status) if pidof $SMARTD_BIN >/dev/null; then echo "$SMARTD_BIN is running." else echo "$SMARTD_BIN is not running." RETVAL=1 fi ;; *) echo "Usage: $0 {start|stop|restart|try-restart|force-reload|reload|report|status}" RETVAL=1 esac exit $RETVAL # 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. [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools smartd_opts= if test -n "$SMARTD_CHECK_INTERVAL" -a "$SMARTD_CHECK_INTERVAL" != 1800 ; then smartd_opts=" -i $SMARTD_CHECK_INTERVAL" fi if test -n "$SMARTD_LOG_FACILITY" -a "$SMARTD_LOG_FACILITY" != "daemon" ; then smartd_opts="$smartd_opts -l $SMARTD_LOG_FACILITY" fi if test -n "$SMARTD_DRIVEDB" ; then smartd_opts="$smartd_opts -B $SMARTD_DRIVEDB" fi if test "$SMARTD_SAVESTATES" = "no" ; then smartd_opts="$smartd_opts -s \"\"" fi if test "$SMARTD_ATTRLOG" = "no" ; then smartd_opts="$smartd_opts -A \"\"" fi if test -n "$SMARTD_EXTRA_OPTS" ; then smartd_opts="$smartd_opts $SMARTD_EXTRA_OPTS" fi # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status # rc_status check and set local and overall rc status # rc_status -v be verbose in local rc status and clear it afterwards # rc_status -v -r ditto and clear both the local and overall rc status # rc_status -s display "skipped" and exit with status 3 # rc_status -u display "unused" and exit with status 3 # rc_failed set local and overall rc status to failed # rc_failed set local and overall rc status to # rc_reset clear both the local and overall rc status # rc_exit exit appropriate to overall rc status # rc_active checks whether a service is activated by symlinks . /etc/rc.status # Reset status of this service rc_reset # Return values acc. to LSB for all commands but status: # 0 - success # 1 - generic or unspecified error # 2 - invalid or excess argument(s) # 3 - unimplemented feature (e.g. "reload") # 4 - user had insufficient privileges # 5 - program is not installed # 6 - program is not configured # 7 - program is not running # 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) # # Note that starting an already running service, stopping # or restarting a not-running service as well as the restart # with force-reload (in case signaling is not supported) are # considered a success. case "$1" in start) echo -n "Starting smartd " ## Start daemon with startproc(8). If this fails ## the return value is set appropriately by startproc. # We don't use startproc - we need to check for return code 17. if ! /sbin/checkproc $SMARTD_BIN ; then eval $SMARTD_BIN$smartd_opts # Remember status and be verbose if test $? -ne 17 ; then rc_status -v else rc_status -u fi else rc_reset rc_status -v fi ;; stop) echo -n "Shutting down smartd " /sbin/killproc -TERM $SMARTD_BIN # Remember status and be verbose rc_status -v ;; try-restart) ## Do a restart only if the service was active before. ## Note: try-restart is now part of LSB (as of 1.9). $0 status if test $? = 0; then $0 restart else rc_reset # Not running is not a failure. fi # Remember status and be quiet rc_status ;; restart) $0 stop $0 start # Remember status and be quiet rc_status ;; force-reload|reload) echo -n "Reload service smartd " /sbin/killproc -HUP $SMARTD_BIN rc_status -v ;; report) ## Checking SMART devices now (smartd specific function) echo -n "Checking SMART devices now " /sbin/killproc -USR1 $SMARTD_BIN 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. # Return value is slightly different for the status command: # 0 - service up and running # 1 - service dead, but /var/run/ pid file exists # 2 - service dead, but /var/lock/ lock file exists # 3 - service not running (unused) # 4 - service status unknown :-( # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) # NOTE: checkproc returns LSB compliant status values. /sbin/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.9) test $SMARTD_CONFIG -nt /var/run/smartd.pid && echo reload ;; *) echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|report|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/rcS ] && . /etc/default/rcS [ -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) $0 stop $0 start ;; force-reload) $0 reload || $0 restart ;; reload) echo -n "Reload S.M.A.R.T. daemon: smartd" if start-stop-daemon --stop --quiet --signal 1 \ --pidfile $SMARTDPID; then echo "." else echo " (failed)" RET=1 fi ;; report) echo -n "Checking SMART devices now" if start-stop-daemon --stop --quiet --signal 10 \ --pidfile $SMARTDPID; then echo "." else echo " (failed)" RET=1 fi ;; status) if pidof $SMARTD_BIN >/dev/null; then echo "$SMARTD_BIN is running." else echo "$SMARTD_BIN is not running." RET=1 fi ;; *) echo "Usage: $0 {start|stop|restart|force-reload|reload|report|status}" 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 SunOS > /dev/null 2>&1 ; then # Source configuration file. This should define the shell variable smartd_opts. # Email smartmontools-support mailing list 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 ;; reload) kill -s HUP `cat $PID_FILE` ;; report) kill -s USR1 `cat $PID_FILE` ;; *) echo "Usage: $0 {start|stop|restart|reload|report}" exit 1 esac exit 0 # 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@listi.jpberlin.de" exit 1 smartmontools-7.0/smartd.service.in0000644000175000010010000000053113405510640014465 00000000000000[Unit] Description=Self Monitoring and Reporting Technology (SMART) Daemon Documentation=man:smartd(8) man:smartd.conf(5) [Service] Type=notify EnvironmentFile=-/usr/local/etc/sysconfig/smartmontools ExecStart=/usr/local/sbin/smartd -n $smartd_opts ExecReload=/bin/kill -HUP $MAINPID StandardOutput=syslog [Install] WantedBy=multi-user.target smartmontools-7.0/smartd_warning.sh.in0000644000175000010010000001271713377306100015177 00000000000000#! /bin/sh # # smartd warning script # # Home page of code is: http://www.smartmontools.org # # Copyright (C) 2012-16 Christian Franke # # SPDX-License-Identifier: GPL-2.0-or-later # # $Id: smartd_warning.sh.in 4839 2018-11-27 18:26:08Z chrfranke $ # set -e # Set by config.status @ENABLE_SCRIPTPATH_TRUE@export PATH="@scriptpath@" PACKAGE="@PACKAGE@" VERSION="@VERSION@" prefix="@prefix@" sysconfdir="@sysconfdir@" smartdscriptdir="@smartdscriptdir@" # Default mailer os_mailer="@os_mailer@" # Plugin directory (disabled if empty) plugindir="@smartdplugindir@" # Parse options dryrun= case $1 in --dryrun) dryrun=t; shift ;; esac if [ $# != 0 ]; then cat <&2 exit 1 fi # Get host and domain names for cmd in @os_hostname@ 'echo "[Unknown]"'; do hostname=`eval $cmd 2>/dev/null` || continue test -n "$hostname" || continue break done dnsdomain=${hostname#*.} if [ "$dnsdomain" != "$hostname" ]; then # hostname command printed FQDN hostname=${hostname%%.*} else for cmd in @os_dnsdomainname@ 'echo'; do dnsdomain=`eval $cmd 2>/dev/null` || continue break done test "$dnsdomain" != "(none)" || dnsdomain= fi for cmd in @os_nisdomainname@ 'echo'; do nisdomain=`eval $cmd 2>/dev/null` || continue break done test "$nisdomain" != "(none)" || nisdomain= # Format subject export SMARTD_SUBJECT="SMART error (${SMARTD_FAILTYPE-[SMARTD_FAILTYPE]}) detected on host: $hostname" # Format message fullmessage=` echo "This message was generated by the smartd daemon running on:" echo echo " host name: $hostname" echo " DNS domain: ${dnsdomain:-[Empty]}" test -z "$nisdomain" || echo " NIS domain: $nisdomain" @OS_WIN32_TRUE@test -z "$USERDOMAIN" || @OS_WIN32_TRUE@ echo " Win domain: $USERDOMAIN" echo echo "The following warning/error was logged by the smartd daemon:" echo echo "${SMARTD_MESSAGE-[SMARTD_MESSAGE]}" echo echo "Device info:" echo "${SMARTD_DEVICEINFO-[SMARTD_DEVICEINFO]}" echo echo "For details see host's SYSLOG." if [ "$SMARTD_FAILTYPE" != "EmailTest" ]; then echo echo "You can also use the smartctl utility for further investigation." test "$SMARTD_PREVCNT" = "0" || echo "The original message about this issue was sent at ${SMARTD_TFIRST-[SMARTD_TFIRST]}" case $SMARTD_NEXTDAYS in '') echo "No additional messages about this problem will be sent." ;; 1) echo "Another message will be sent in 24 hours if the problem persists." ;; *) echo "Another message will be sent in $SMARTD_NEXTDAYS days if the problem persists." ;; esac fi ` # Export message with trailing newline export SMARTD_FULLMESSAGE="$fullmessage " # Run plugin scripts if requested if test -n "$plugindir"; then case " $SMARTD_ADDRESS" in *\ @*) if [ -n "$dryrun" ]; then echo "export SMARTD_SUBJECT='$SMARTD_SUBJECT'" echo "export SMARTD_FULLMESSAGE='$SMARTD_FULLMESSAGE'" fi # Run ALL scripts if requested case " $SMARTD_ADDRESS " in *\ @ALL\ *) for cmd in "$plugindir"/*; do if [ -f "$cmd" ] && [ -x "$cmd" ]; then if [ -n "$dryrun" ]; then echo "$cmd
&2 fi ;; *) SMARTD_ADDRESS="${SMARTD_ADDRESS:+ }$ad" ;; esac done # Send email to remaining addresses test -n "$SMARTD_ADDRESS" || exit 0 ;; esac fi # Send mail or run command if [ -n "$SMARTD_ADDRESS" ]; then # Send mail, use platform mailer by default test -n "$SMARTD_MAILER" || SMARTD_MAILER=$os_mailer if [ -n "$dryrun" ]; then echo "exec '$SMARTD_MAILER' -s '$SMARTD_SUBJECT' $SMARTD_ADDRESS < Copyright (C) 2014-18 Christian Franke SPDX-License-Identifier: GPL-2.0-or-later $Id: update-smart-drivedb.8.in 4879 2018-12-28 22:05:12Z chrfranke $ .. .\" Macros borrowed from pages generated with Pod::Man .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Use groff extension \(aq (apostrophe quote, ASCII 0x27) if possible .ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH UPDATE-SMART-DRIVEDB 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools" .SH NAME update-smart-drivedb \- update smartmontools drive database .Sp .SH "SYNOPSIS" .B update-smart-drivedb .RI [ OPTIONS ] .RI [ DESTFILE ] .Sp .SH "DESCRIPTION" .\" %IF NOT OS ALL .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools. .\"! It does not contain info specific to other platforms.] .\"! .PP .\" %ENDIF NOT OS ALL .B update-smart-drivedb updates .B /usr/local/share/smartmontools/drivedb.h or .I DESTFILE from branches/RELEASE_6_0_DRIVEDB of smartmontools SVN repository. .PP The tools used for downloading are either .BR curl (1), .BR wget (1), .BR lynx (1), .\" %IF OS FreeBSD .BR fetch (1) [FreeBSD only], .\" %ENDIF OS FreeBSD .\" %IF OS OpenBSD .BR ftp (1) [OpenBSD only], .\" %ENDIF OS OpenBSD or .BR svn (1). .PP [NEW EXPERIMENTAL UPDATE-SMART-DRIVEDB FEATURE] The downloaded file is verified with OpenPGP/GPG key ID 721042C5. The public key block is included in the script. .PP The old file is kept if the downloaded file is identical (ignoring the differences in Id string) otherwise it is moved to .BR drivedb.h.old . .Sp .SH "OPTIONS" .TP .B \-s SMARTCTL Use the .BR smartctl (8) executable at path SMARTCTL for drive database syntax check. The form \*(Aq\-s \-\*(Aq disables the syntax check. The default is .BR /usr/local/sbin/smartctl . .TP .B \-t TOOL Use TOOL for download. TOOL is one of: .I curl wget lynx .\" %IF OS FreeBSD .I fetch .\" %ENDIF OS FreeBSD .\" %IF OS OpenBSD .I ftp .\" %ENDIF OS OpenBSD .IR svn . The default is the first one found in PATH. .TP .B \-u LOCATION Use URL of LOCATION for download. LOCATION is one of: .br .I github (GitHub mirror of SVN repository), .br .I sf (Sourceforge code browser), .br .I svn (SVN repository), .br .I svni (SVN repository via HTTP instead of HTTPS), .br .I trac (Trac code browser). .br The default is .IR svn . .TP .B \-\-trunk Download from SVN trunk. This requires \*(Aq\-\-no\-verify\*(Aq unless the trunk version is still identical to branches/RELEASE_6_0_DRIVEDB. .TP .B \-\-cacert FILE Use CA certificates from FILE to verify the peer. .TP .B \-\-capath DIR Use CA certificate files from DIR to verify the peer. .TP .B \-\-insecure Don't abort download if certificate verification fails. This option is also required if a HTTP URL is selected with \*(Aq\-u\*(Aq option. .TP .B \-\-no\-verify Don't verify signature with GnuPG. .TP .B \-\-export\-key Print the OpenPGP/GPG public key block. .TP .B \-\-dryrun Print download commands only. .TP .B \-v Verbose output. .Sp .SH "EXAMPLES" .Vb 2 # update-smart-drivedb /usr/local/share/smartmontools/drivedb.h updated from \e branches/RELEASE_6_0_DRIVEDB .Ve .Sp .SH "EXIT STATUS" The exit status is 0 if the database has been successfully updated. If an error occurs the exit status is 1. .Sp .SH FILES .TP .B /usr/local/sbin/update-smart-drivedb full path of this script. .TP .B /usr/local/sbin/smartctl used to check syntax of new drive database. .TP .B /usr/local/share/smartmontools/drivedb.h current drive database. .TP .B /usr/local/share/smartmontools/drivedb.h.raw current drive database with unexpanded SVN Id string. .TP .B /usr/local/share/smartmontools/drivedb.h.raw.asc signature file. .TP .B /usr/local/share/smartmontools/drivedb.h.*old* previous files. .TP .B /usr/local/share/smartmontools/drivedb.h.*error* new files if rejected due to errors. .TP .B /usr/local/share/smartmontools/drivedb.h.lastcheck empty file created if downloaded file was identical. .Sp .SH AUTHORS \fBChristian Franke\fP. .br This manual page was originally written by .BR "Hannes von Haugwitz " . .Sp .SH REPORTING BUGS To submit a bug report, create a ticket in smartmontools wiki: .br <\fBhttps://www.smartmontools.org/\fP>. .br Alternatively send the info to the smartmontools support mailing list: .br <\fBhttps://listi.jpberlin.de/mailman/listinfo/smartmontools-support\fB>. .Sp .SH SEE ALSO \fBsmartctl\fP(8), \fBsmartd\fP(8). .Sp .SH PACKAGE VERSION CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV .br $Id: update-smart-drivedb.8.in 4879 2018-12-28 22:05:12Z chrfranke $ smartmontools-7.0/update-smart-drivedb.in0000644000175000010010000003467613411517030015575 00000000000000#! /bin/sh # # smartmontools drive database update script # # Home page of code is: http://www.smartmontools.org # # Copyright (C) 2010-18 Christian Franke # # SPDX-License-Identifier: GPL-2.0-or-later # # $Id: update-smart-drivedb.in 4879 2018-12-28 22:05:12Z chrfranke $ # set -e # Set by config.status @ENABLE_SCRIPTPATH_TRUE@export PATH="@scriptpath@" PACKAGE="@PACKAGE@" VERSION="@VERSION@" prefix="@prefix@" exec_prefix="@exec_prefix@" sbindir="@sbindir@" datarootdir="@datarootdir@" datadir="@datadir@" drivedbdir="@drivedbdir@" # Download tools os_dltools="@os_dltools@" # drivedb.h update branch BRANCH="@DRIVEDB_BRANCH@" # Default drivedb location DRIVEDB="$drivedbdir/drivedb.h" # GnuPG used to verify signature (disabled if empty) GPG="@gnupg@" # Smartctl used for syntax check SMARTCTL="$sbindir/smartctl" # PATH information for help and error messages @ENABLE_SCRIPTPATH_FALSE@pathinfo='$PATH' @ENABLE_SCRIPTPATH_TRUE@pathinfo="'$PATH'" myname=$0 usage() { @ENABLE_SCRIPTPATH_TRUE@ pathinfo=" @ENABLE_SCRIPTPATH_TRUE@ $pathinfo" cat <&2 exit 1 } err_notfound() { case $1 in */*) error "$1: not found $2" ;; *) error "$1: not found in $pathinfo $2" ;; esac } warning() { echo "$myname: (Warning) $*" >&2 } selecturl() { case $1 in github) # https://github.com/smartmontools/smartmontools/raw/origin/$BRANCH/smartmontools/drivedb.h # https://github.com/smartmontools/smartmontools/raw/master/smartmontools/drivedb.h # redirected to: url='https://raw.githubusercontent.com/smartmontools/smartmontools/master/smartmontools/drivedb.h' ;; sf) url='https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/drivedb.h?format=raw' ;; svn) url='https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;; svni) url='http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;; trac) url='https://www.smartmontools.org/export/HEAD/trunk/smartmontools/drivedb.h' ;; *) usage ;; esac } inpath() { local d rc save rc=1 save=$IFS IFS=':' for d in $PATH; do test -f "$d/$1" || continue test -x "$d/$1" || continue rc=0 break done IFS=$save return $rc } vecho() { test -n "$q" || echo "$*" } # vrun COMMAND ARGS... vrun() { if [ -n "$dryrun" ]; then echo "$*" elif [ -n "$q" ]; then "$@" 2>/dev/null else echo "$*" "$@" fi } # vrun2 OUTFILE COMMAND ARGS... vrun2() { local f err rc f=$1; shift rc=0 if [ -n "$dryrun" ]; then echo "$* > $f" else vecho "$* > $f" err=`"$@" 2>&1 > $f` || rc=$? if [ -n "$err" ]; then vecho "$err" >&2 test $rc != 0 || rc=42 fi fi return $rc } # download URL FILE download() { local f u rc u=$1; f=$2 rc=0 case $tool in curl) vrun curl ${q:+-s} -f --max-redirs 0 \ ${cacert:+--cacert "$cacert"} \ ${capath:+--capath "$capath"} \ ${insecure:+--insecure} \ -o "$f" "$u" || rc=$? ;; wget) vrun wget $q --max-redirect=0 \ ${cacert:+--ca-certificate="$cacert"} \ ${capath:+--ca-directory="$capath"} \ ${insecure:+--no-check-certificate} \ -O "$f" "$u" || rc=$? ;; lynx) test -z "$cacert" || vrun export SSL_CERT_FILE="$cacert" test -z "$capath" || vrun export SSL_CERT_DIR="$capath" # Check also stderr as lynx does not return != 0 on HTTP error vrun2 "$f" lynx -stderr -noredir -source "$u" || rc=$? ;; svn) vrun svn $q export \ --non-interactive --no-auth-cache \ ${cacert:+--config-option "servers:global:ssl-trust-default-ca=no"} \ ${cacert:+--config-option "servers:global:ssl-authority-files=$cacert"} \ ${insecure:+--trust-server-cert} \ "$u" "$f" || rc=$? ;; fetch) # FreeBSD vrun fetch $q --no-redirect \ ${cacert:+--ca-cert "$cacert"} \ ${capath:+--ca-path "$capath"} \ ${insecure:+--no-verify-hostname} \ -o "$f" "$u" || rc=$? ;; ftp) # OpenBSD vrun ftp \ ${cacert:+-S cafile="$cacert"} \ ${capath:+-S capath="$capath"} \ ${insecure:+-S dont} \ -o "$f" "$u" || rc=$? ;; *) error "$tool: unknown (internal error)" ;; esac return $rc } # check_file FILE FIRST_CHAR MIN_SIZE MAX_SIZE check_file() { local firstchar f maxsize minsize size test -z "$dryrun" || return 0 f=$1; firstchar=$2; minsize=$3; maxsize=$4 # Check first chars case `dd if="$f" bs=1 count=1 2>/dev/null` in $firstchar) ;; \<) echo "HTML error message"; return 1 ;; *) echo "unknown file contents"; return 1 ;; esac # Check file size size=`wc -c < "$f"` if test "$size" -lt $minsize; then echo "too small file size $size bytes" return 1 fi if test "$size" -gt $maxsize; then echo "too large file size $size bytes" return 1 fi return 0 } # unexpand_svn_id < INFILE > OUTFILE unexpand_svn_id() { sed 's,\$''Id'': drivedb\.h [0-9][0-9]* 2[-0-9]* [012][:0-9]*Z [a-z][a-z0-9]* \$,$''Id''$,' } # Smartmontools Signing Key (through 2020) # # Key ID 721042C5 public_key="\ -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBFwmhpUBEADRoOZaXq13MrqyAmbGe6FlHi6P9ujsT/SJGhTiAoN3W1X56Dbm KP21nO9ZAjdXnvA2OmzppfCUX7v5Q3/TG3vN3WwfyQIO/dgSaTrGa1E8odbHEGc7 rhzYA8ekAn3TmxhOrEUTcRIogumW0zlQewHOlTe0OYsxat6/N8l3Cqn28HwZUpRH MrJW3RgefFihQGEhXlnfzo+Tltl14IriURbwBZIDeZOk2AWLGweI0+zqTgYSbF5A tI5rXO1QDeoyBYZhSX3MtnncwPdCnxoRasizU5w3KoZWYyKAc5bxJBJgUUp9HDOu ATgNqekc8j28x/cUAWerXe183SBYQp0QkzMPbmE9TCGW3GjtW+Kk/NDbNe8ufj6O hk0r7EbGyBO0qvgzHLzSsQiSsgaMCkLc5Xt4NzB4g2DvnReFU2WwgRh031lHOVLm mvFqRtHzJb20dKufyjOmSMzNKRzURVmobECKARaBlGNP0wHYhq97n4OxM1o0eq7a 4ugaSp2q+6BSaAQhbZN8ULCF/oGA/376Sz7RNuoOmQwl9aFqnfl3YgopBIqKvnSP h4j0QynN45rUFOe/VywTmpWKj+DonGCupxe9VvyZ87NKRgKiHprXGDrhdB0GcNXM wV66WbjKBV7qlpSh/GH3oiHwlcYT8LNyZbxTJXcVF5ODtlZfc9zqRtUBWQARAQAB tFNTbWFydG1vbnRvb2xzIFNpZ25pbmcgS2V5ICh0aHJvdWdoIDIwMjApIDxzbWFy dG1vbnRvb2xzLWRhdGFiYXNlQGxpc3RpLmpwYmVybGluLmRlPokCPgQTAQIAKAUC XCaGlQIbAwUJA8etAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6nSrJXIQ QsWXYQ/+IVHGQxDOg7lMX9iDbg/UDj/zrQfsJR2HQ2j0iI8TmsQLSK4pphwN0r9D g0BuKhQBe3wPphLjwD40HueKatIacE91PgLse/KWmEe4OoQCDxshiIGad3YoIF3X yrJg6pcMLOAnfT55Tg04EmWpT1LzWTJmH8RL2iftTM217Q2JnfQGKicTiD/GiYV1 oyFUvn+H5/u5O7UYhvWKBcccJtal2uhc6h8U2HugMV0SpNM5p83oGDZkV0YYSJ0C 044im1+axbz06Aeq7Uh3JFScCcbjl+SQ7gK0NJF39uI8HbwC7fcfySCj5JDuVeaq KjahWctKa/D6nauKA8+LIGOckkf2oN0sJBrES7Zn8ImHYN/1wLCff9oIDAlux6Jk BZ6+MqIJKHit4SSYPd3QnkdI1ehn+2EdxK9VSBU0W2ZPlZmoUSamWboloumhwYyN 86ohFVJWnN4YWlZiJNJlxj/F6d4GTEJBFqoK9yStdz8Dsg16sAwuNYFVFtCKaesA keuhcS3SfoFXwLsz+8cLfHVdsBHmm9/OCfNtOm3EPJqaD57lL5ocTWQeLaAqUCse rOCDoIUZul5e6kRytjjNIHFNufWTbuw4YlYM3+FU1nkgckmhw4M9kI/xGtVj7bvs tJKKN976kOoRZRIAL+9SlC+3Tqd9a4y4RRjYongvFzqpqRlQfS+JARwEEwECAAYF AlwmhpwACgkQL83sC9OvGqsVOggAqLB5eQrUv8E9ikD6kJCito827bzDWF29yD7P vfhjXaz5in54jOVpwg3o9CsqIjjRW0/1bBVswC8ZL0sAdZ+GDSDMw5F2IpkD77gj nFY79M/e6C9xYyxYzHC7emDPSz9IroOvdkkEgrB+OABKkaOCcS18P4Lk3WNHaPw5 c7aI0z1iJP52EmSfvB8r86mtUFJB+f15eD/4vaRfkZLFjF9FQ3kgEK1U+rV4s1O2 bCFfP3WPDcc83NgwRUvtXmcSOSOIoXnemJzyJr+JnqCWVET4XWF6i20mRFXVEpWt f5AkJYgR3z/jW0djELbBWA/35bAnpXy5pDHv9NbZsTkBZxK/kokBHAQTAQIABgUC XCaGnQAKCRAY7NpGy/a6xn4lB/90tXTnZsgmoftol9uivfQrPdR88WmOZLYmUeQA d1rqSFMxe+KzO/qLuU8s6OF4nznwL2cPfbGZxezM4PiYmAmbbEU/3gTONwjVBBA0 Gfimy/fITEezFtCigo1thkaJ195g/dqY+zE3Vt4rzC03j1vx8mUHRPU6kkvKj8cP 0j+XHX2xQDsTXTstfnom29wBmGnvSZ9HgcdL71e1VXJXwikmnO3P4J/1C2LeCOlW rGqWZ2c0WBLKdJnsYUx7Dm/OvkkB4lF+zWp98zS8jS/5h+1apVgEzrdTMvT8ydTk Ur7ObKGkIhK+L+Xo5BD+V9Qf6xKGYPwhhdj/E5/kyjULrm10iQEcBBMBAgAGBQJc JoadAAoJEPOHY87f0iVZfiUH/3yKS5wGvTeRInse8+W1WzKuto3XzqXLngb9QXWw 7nCwqmNS7PbzDnufQi2ThKrMfcK14WgNYABNZPU75I+6bcb0oCB5tlooIUEV/2Ut /5Hl/83zFFoNA/kQKVz8kIDqgRcxC+zY2VJ4eTKHyQDvXygVk8wnKTBae3gX+CIZ qJHPXiiygHlbl31Mi3G1Iaxu57dP6ocV0vX1dytKSwd4Rbviwwb4L76o/tVT9t3G wFM15uK1SqtnAaiaktEdMi3XI4d01H3VUVz/iR0XQbf13RZoEM6CJWmsQ/qvYlwk bKOdlahjoHrFlkhADSBaO9N1OZp3OYDjziIujMdt2IPKnmM= =0uFV -----END PGP PUBLIC KEY BLOCK----- " # gpg_verify FILE.asc FILE gpg_verify() { local gnupgtmp opts out rc opts="--quiet ${q:+--no-secmem-warnin} --batch --no-tty" # Create temp home dir gnupgtmp="$tmpdir/.gnupg.$$.tmp" rm -f -r "$gnupgtmp" mkdir "$gnupgtmp" || exit 1 chmod 0700 "$gnupgtmp" # Import public key "$GPG" $opts --homedir="$gnupgtmp" --import <&1` || rc=1 vecho "$out" rm -f -r "$gnupgtmp" return $rc } # mv_all PREFIX OLD NEW mv_all() { mv "${1}${2}" "${1}${3}" mv "${1}${2}.raw" "${1}${3}.raw" mv "${1}${2}.raw.asc" "${1}${3}.raw.asc" } # Parse options smtctl=$SMARTCTL tool= url= q="-q" dryrun= trunk= cacert= capath= insecure= no_verify= while true; do case $1 in -s) shift; test -n "$1" || usage smtctl=$1 ;; -t) shift case $1 in *\ *) usage ;; esac case " $os_dltools " in *\ $1\ *) ;; *) usage ;; esac tool=$1 ;; -u) shift; selecturl "$1" ;; -v) q= ;; --dryrun) dryrun=t ;; --trunk) trunk=trunk ;; --cacert) shift; test -n "$1" || usage cacert=$1 ;; --capath) shift; test -n "$1" || usage capath=$1 ;; --insecure) insecure=t ;; --no-verify) no_verify=t ;; --export-key) cat </dev/null 2>&1 \ || err_notfound "$smtctl" "('-s -' to ignore)" fi # Check for GnuPG if [ -z "$no_verify" ]; then test -n "$GPG" \ || error "GnuPG is not available ('--no-verify' to ignore)" "$GPG" --version >/dev/null 2>&1 \ || err_notfound "$GPG" "('--no-verify' to ignore)" fi # Use destination directory as temp directory for gpg tmpdir=`dirname "$DEST"` # Adjust URLs src=`echo "$url" | sed -e "s,/trunk/,/branches/$BRANCH/," \ -e "s,/master/,/origin/$BRANCH/,"` src_asc=`echo "$src" | sed "s,/drivedb\.h,/drivedb.h.raw.asc,"` test -z "$trunk" || src=$url # Download test -n "$dryrun" || rm -f "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc" vecho "Download ${trunk:-branches/$BRANCH}/drivedb.h with $tool" rc=0 download "$src" "$DEST.new" || rc=$? if [ $rc != 0 ]; then rm -f "$DEST.new" error "${trunk:-$BRANCH}/drivedb.h: download failed ($tool: exit $rc)" fi if ! errmsg=`check_file "$DEST.new" '/' 10000 1000000`; then mv "$DEST.new" "$DEST.error" error "$DEST.error: $errmsg" fi vecho "Download branches/$BRANCH/drivedb.h.raw.asc with $tool" rc=0 download "$src_asc" "$DEST.new.raw.asc" || rc=$? if [ $rc != 0 ]; then rm -f "$DEST.new" "$DEST.new.raw.asc" error "$BRANCH/drivedb.h.raw.asc: download failed ($tool: exit $rc)" fi if ! errmsg=`check_file "$DEST.new.raw.asc" '-' 200 2000`; then rm -f "$DEST.new" mv "$DEST.new.raw.asc" "$DEST.error.raw.asc" error "$DEST.error.raw.asc: $errmsg" fi test -z "$dryrun" || exit 0 # Create raw file with unexpanded SVN Id # (This assumes newlines are LF and not CR/LF) unexpand_svn_id < "$DEST.new" > "$DEST.new.raw" # Adjust timestamps and permissions touch "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc" chmod 0644 "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc" if [ -z "$no_verify" ]; then # Verify raw file if ! gpg_verify "$DEST.new.raw.asc" "$DEST.new.raw"; then mv_all "$DEST" ".new" ".error" test -n "$trunk" || error "$DEST.error.raw: *** BAD signature ***" error "$DEST.error.raw: signature from branch no longer valid ('--no-verify' to ignore)" fi fi if [ "$smtctl" != "-" ]; then # Check syntax if ! "$smtctl" -B "$DEST.new" -P showall >/dev/null; then mv_all "$DEST" ".new" ".error" error "$DEST.error: rejected by $smtctl, probably no longer compatible" fi vecho "$smtctl: syntax OK" fi # Keep old file if identical, ignore missing Id keyword expansion in new file rm -f "$DEST.lastcheck" if [ -f "$DEST" ]; then if [ -f "$DEST.raw" ] && [ -f "$DEST.raw.asc" ]; then if cmp "$DEST.raw" "$DEST.new.raw" >/dev/null 2>&1 \ && cmp "$DEST.raw.asc" "$DEST.new.raw.asc" >/dev/null 2>&1 \ && { cmp "$DEST" "$DEST.new" >/dev/null 2>&1 \ || cmp "$DEST.raw" "$DEST.new" >/dev/null 2>&1; } then rm -f "$DEST.new" "$DEST.new.raw" "$DEST.new.raw.asc" touch "$DEST.lastcheck" echo "$DEST is already up to date" exit 0 fi mv_all "$DEST" "" ".old" else mv "$DEST" "$DEST.old" fi fi mv_all "$DEST" ".new" "" echo "$DEST updated from ${trunk:-branches/$BRANCH}${no_verify:+ (NOT VERIFIED)}" smartmontools-7.0/utility.cpp0000644000175000010010000005672413401001476013430 00000000000000/* * utility.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-12 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ // 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 "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #ifdef _WIN32 #include // _mbsinc() #endif #include #include "svnversion.h" #include "utility.h" #include "atacmds.h" #include "dev_interface.h" #include "sg_unaligned.h" const char * utility_cpp_cvsid = "$Id: utility.cpp 4842 2018-12-02 16:07:26Z chrfranke $" 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" }; // BUILD_INFO can be provided by package maintainers #ifndef BUILD_INFO #define BUILD_INFO "(local build)" #endif // Make version information string std::string format_version_info(const char * prog_name, bool full /*= false*/) { std::string info = strprintf( "%s " PACKAGE_VERSION " " #ifdef SMARTMONTOOLS_SVN_REV SMARTMONTOOLS_SVN_DATE " r" SMARTMONTOOLS_SVN_REV #else "(build date " __DATE__ ")" // checkout without expansion of Id keywords #endif " [%s] " BUILD_INFO "\n" "Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org\n", prog_name, smi()->get_os_version_str().c_str() ); if (!full) return info; info += "\n"; info += prog_name; info += " comes with ABSOLUTELY NO WARRANTY. This is free\n" "software, and you are welcome to redistribute it under\n" "the terms of the GNU General Public License; either\n" "version 2, or (at your option) any later version.\n" "See http://www.gnu.org for further details.\n" "\n" "smartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n" #ifdef SMARTMONTOOLS_SVN_REV "smartmontools SVN rev " SMARTMONTOOLS_SVN_REV " dated " SMARTMONTOOLS_SVN_DATE " at " SMARTMONTOOLS_SVN_TIME "\n" #else "smartmontools SVN rev is unknown\n" #endif "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n" "smartmontools build with: " #define N2S_(s) #s #define N2S(s) "(" N2S_(s) ")" #if __cplusplus > 201703 "C++2x" N2S(__cplusplus) #elif __cplusplus == 201703 "C++17" #elif __cplusplus > 201402 "C++14" N2S(__cplusplus) #elif __cplusplus == 201402 "C++14" #elif __cplusplus > 201103 "C++11" N2S(__cplusplus) #elif __cplusplus == 201103 "C++11" #elif __cplusplus > 199711 "C++98" N2S(__cplusplus) #elif __cplusplus == 199711 "C++98" #else "C++" N2S(__cplusplus) #endif #undef N2S #undef N2S_ #if defined(__GNUC__) && defined(__VERSION__) // works also with CLang ", GCC " __VERSION__ #endif "\n" "smartmontools configure arguments:" ; info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ? SMARTMONTOOLS_CONFIGURE_ARGS : " [no arguments given]"); info += '\n'; return info; } // 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 canonical // 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((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)' tzset(); putenv((char *)"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"; } // 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)[DATEANDEPOCHLEN], time_t tval) { struct tm *tmval; const char *timezonename; char datebuffer[DATEANDEPOCHLEN]; int lenm1; FixGlibcTimeZoneBug(); // Get the time structure. We need this to determine if we are in // daylight savings time or not. tmval=localtime(&tval); // Convert to an ASCII string, put in datebuffer // same as: asctime_r(tmval, datebuffer); strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); datebuffer[DATEANDEPOCHLEN-1]='\0'; // Remove newline lenm1=strlen(datebuffer)-1; datebuffer[lenm1>=0?lenm1:0]='\0'; #if defined(_WIN32) && defined(_MSC_VER) // tzname is missing in MSVC14 #define tzname _tzname #endif // 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 // cppcheck-suppress variableScope char tzfixbuf[6+1] = ""; 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; } // A replacement for perror() that sends output to our choice of // printing. If errno not set then just print message. void syserror(const char *message){ if (errno) { // Get the correct system error message: const char *errormessage=strerror(errno); // Check that caller has handed a sensible string, and provide // appropriate output. See perrror(3) man page to understand better. if (message && *message) pout("%s: %s\n",message, errormessage); else pout("%s\n",errormessage); } else if (message && *message) pout("%s\n",message); return; } // Check regular expression for non-portable features. // // POSIX extended regular expressions interpret unmatched ')' ordinary: // "The close-parenthesis shall be considered special in this context // only if matched with a preceding open-parenthesis." // // GNU libc and BSD libc support unmatched ')', Cygwin reports an error. // // POSIX extended regular expressions do not define empty subexpressions: // "A vertical-line appearing first or last in an ERE, or immediately following // a vertical-line or a left-parenthesis, or immediately preceding a // right-parenthesis, produces undefined results." // // GNU libc and Cygwin support empty subexpressions, BSD libc reports an error. // static const char * check_regex(const char * pattern) { int level = 0; char c; for (int i = 0; (c = pattern[i]); i++) { // Skip "\x" if (c == '\\') { if (!pattern[++i]) break; continue; } // Skip "[...]" if (c == '[') { if (pattern[++i] == '^') i++; if (!pattern[i++]) break; while ((c = pattern[i]) && c != ']') i++; if (!c) break; continue; } // Check "(...)" nesting if (c == '(') level++; else if (c == ')' && --level < 0) return "Unmatched ')'"; // Check for leading/trailing '|' or "||", "|)", "|$", "(|", "^|" char c1; if ( (c == '|' && ( i == 0 || !(c1 = pattern[i+1]) || c1 == '|' || c1 == ')' || c1 == '$')) || ((c == '(' || c == '^') && pattern[i+1] == '|') ) return "Empty '|' subexpression"; } return (const char *)0; } // Wrapper class for POSIX regex(3) or std::regex #ifndef WITH_CXX11_REGEX regular_expression::regular_expression() { memset(&m_regex_buf, 0, sizeof(m_regex_buf)); } regular_expression::~regular_expression() { free_buf(); } regular_expression::regular_expression(const regular_expression & x) : m_pattern(x.m_pattern), m_errmsg(x.m_errmsg) { memset(&m_regex_buf, 0, sizeof(m_regex_buf)); copy_buf(x); } regular_expression & regular_expression::operator=(const regular_expression & x) { m_pattern = x.m_pattern; m_errmsg = x.m_errmsg; free_buf(); copy_buf(x); return *this; } void regular_expression::free_buf() { if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) { regfree(&m_regex_buf); memset(&m_regex_buf, 0, sizeof(m_regex_buf)); } } void regular_expression::copy_buf(const regular_expression & x) { if (nonempty(&x.m_regex_buf, sizeof(x.m_regex_buf))) { // There is no POSIX compiled-regex-copy command. if (!compile()) throw std::runtime_error(strprintf( "Unable to recompile regular expression \"%s\": %s", m_pattern.c_str(), m_errmsg.c_str())); } } #endif // !WITH_CXX11_REGEX regular_expression::regular_expression(const char * pattern) : m_pattern(pattern) { if (!compile()) throw std::runtime_error(strprintf( "error in regular expression \"%s\": %s", m_pattern.c_str(), m_errmsg.c_str())); } bool regular_expression::compile(const char * pattern) { #ifndef WITH_CXX11_REGEX free_buf(); #endif m_pattern = pattern; return compile(); } bool regular_expression::compile() { #ifdef WITH_CXX11_REGEX try { m_regex.assign(m_pattern, std::regex_constants::extended); } catch (std::regex_error & ex) { m_errmsg = ex.what(); return false; } #else int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), REG_EXTENDED); if (errcode) { char errmsg[512]; regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg)); m_errmsg = errmsg; free_buf(); return false; } #endif const char * errmsg = check_regex(m_pattern.c_str()); if (errmsg) { m_errmsg = errmsg; #ifdef WITH_CXX11_REGEX m_regex = std::regex(); #else free_buf(); #endif return false; } m_errmsg.clear(); return true; } bool regular_expression::full_match(const char * str) const { #ifdef WITH_CXX11_REGEX return std::regex_match(str, m_regex); #else match_range range; return ( !regexec(&m_regex_buf, str, 1, &range, 0) && range.rm_so == 0 && range.rm_eo == (int)strlen(str)); #endif } bool regular_expression::execute(const char * str, unsigned nmatch, match_range * pmatch) const { #ifdef WITH_CXX11_REGEX std::cmatch m; if (!std::regex_search(str, m, m_regex)) return false; unsigned sz = m.size(); for (unsigned i = 0; i < nmatch; i++) { if (i < sz && *m[i].first) { pmatch[i].rm_so = m[i].first - str; pmatch[i].rm_eo = m[i].second - str; } else pmatch[i].rm_so = pmatch[i].rm_eo = -1; } return true; #else return !regexec(&m_regex_buf, str, nmatch, pmatch, 0); #endif } // 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, int *mode) { char *tailptr; if (!(s = strchr(s, ','))) return 1; bool add = false; if (!isdigit((int)(*++s))) { *start = *stop = 0; if (!strncmp(s, "redo", 4)) *mode = SEL_REDO; else if (!strncmp(s, "next", 4)) *mode = SEL_NEXT; else if (!strncmp(s, "cont", 4)) *mode = SEL_CONT; else return 1; s += 4; if (!*s) return 0; if (*s != '+') return 1; } else { *mode = SEL_RANGE; 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; add = (*s == '+'); if (!(!errno && (add || *s == '-'))) return 1; if (!strcmp(s, "-max")) { *stop = ~(uint64_t)0; // replaced by max LBA later return 0; } } errno = 0; *stop = strtoull(s+1, &tailptr, 0); if (errno || *tailptr != '\0') return 1; if (add) { if (*stop > 0) (*stop)--; *stop += *start; // -t select,N+M => -t select,N,(N+M-1) } return 0; } // Returns true if region of memory contains non-zero entries bool nonempty(const void * data, int size) { for (int i = 0; i < size; i++) if (((const unsigned char *)data)[i]) return true; return false; } // Copy not null terminated char array to null terminated string. // Replace non-ascii characters. Remove leading and trailing blanks. const char * format_char_array(char * str, int strsize, const char * chr, int chrsize) { int b = 0; while (b < chrsize && chr[b] == ' ') b++; int n = 0; while (b+n < chrsize && chr[b+n]) n++; while (n > 0 && chr[b+n-1] == ' ') n--; if (n >= strsize) n = strsize-1; for (int i = 0; i < n; i++) { char c = chr[b+i]; str[i] = (' ' <= c && c <= '~' ? c : '?'); } str[n] = 0; return str; } // Format integer with thousands separator const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, const char * thousands_sep /* = 0 */) { if (!thousands_sep) { thousands_sep = ","; #ifdef HAVE_LOCALE_H setlocale(LC_ALL, ""); const struct lconv * currentlocale = localeconv(); if (*(currentlocale->thousands_sep)) thousands_sep = currentlocale->thousands_sep; #endif } char num[64]; snprintf(num, sizeof(num), "%" PRIu64, val); int numlen = strlen(num); int i = 0, j = 0; do str[j++] = num[i++]; while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1); str[j] = 0; while (i < numlen && j < strsize-1) { j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i); i += 3; } return str; } // Format capacity with SI prefixes const char * format_capacity(char * str, int strsize, uint64_t val, const char * decimal_point /* = 0 */) { if (!decimal_point) { decimal_point = "."; #ifdef HAVE_LOCALE_H setlocale(LC_ALL, ""); const struct lconv * currentlocale = localeconv(); if (*(currentlocale->decimal_point)) decimal_point = currentlocale->decimal_point; #endif } const unsigned factor = 1000; // 1024 for KiB,MiB,... static const char prefixes[] = " KMGTP"; // Find d with val in [d, d*factor) unsigned i = 0; uint64_t d = 1; for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) { d = d2; if (++i >= sizeof(prefixes)-2) break; } // Print 3 digits uint64_t n = val / d; if (i == 0) snprintf(str, strsize, "%u B", (unsigned)n); else if (n >= 100) // "123 xB" snprintf(str, strsize, "%" PRIu64 " %cB", n, prefixes[i]); else if (n >= 10) // "12.3 xB" snprintf(str, strsize, "%" PRIu64 "%s%u %cB", n, decimal_point, (unsigned)(((val % d) * 10) / d), prefixes[i]); else // "1.23 xB" snprintf(str, strsize, "%" PRIu64 "%s%02u %cB", n, decimal_point, (unsigned)(((val % d) * 100) / d), prefixes[i]); return str; } // return (v)sprintf() formatted std::string __attribute_format_printf(1, 0) std::string vstrprintf(const char * fmt, va_list ap) { char buf[512]; vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf)-1] = 0; return buf; } std::string strprintf(const char * fmt, ...) { va_list ap; va_start(ap, fmt); std::string str = vstrprintf(fmt, ap); va_end(ap); return str; } #if defined(HAVE___INT128) // Compiler supports '__int128'. // Recursive 128-bit to string conversion function static int snprint_uint128(char * str, int strsize, unsigned __int128 value) { if (strsize <= 0) return -1; if (value <= 0xffffffffffffffffULL) { // Print leading digits as 64-bit value return snprintf(str, (size_t)strsize, "%" PRIu64, (uint64_t)value); } else { // Recurse to print leading digits const uint64_t e19 = 10000000000000000000ULL; // 2^63 < 10^19 < 2^64 int len1 = snprint_uint128(str, strsize, value / e19); if (len1 < 0) return -1; // Print 19 digits remainder as 64-bit value int len2 = snprintf(str + (len1 < strsize ? len1 : strsize - 1), (size_t)(len1 < strsize ? strsize - len1 : 1), "%019" PRIu64, (uint64_t)(value % e19) ); if (len2 < 0) return -1; return len1 + len2; } } // Convert 128-bit unsigned integer provided as two 64-bit halves to a string. const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) { snprint_uint128(str, strsize, ((unsigned __int128)value_hi << 64) | value_lo); return str; } #elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF) // Compiler and *printf() support 'long double' which is wider than 'double'. const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) { snprintf(str, strsize, "%.0Lf", value_hi * (0xffffffffffffffffULL + 1.0L) + value_lo); return str; } #else // !HAVE_LONG_DOUBLE_WIDER_PRINTF // No '__int128' or 'long double' support, use 'double'. const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo) { snprintf(str, strsize, "%.0f", value_hi * (0xffffffffffffffffULL + 1.0) + value_lo); return str; } #endif // HAVE___INT128 // Runtime check of byte ordering, throws on error. static void check_endianness() { const union { // Force compile error if int type is not 32bit. unsigned char c[sizeof(int) == 4 ? 8 : -1]; uint64_t i; } x = {{1, 2, 3, 4, 5, 6, 7, 8}}; const uint64_t le = 0x0807060504030201ULL; const uint64_t be = 0x0102030405060708ULL; if (!( x.i == (isbigendian() ? be : le) && sg_get_unaligned_le16(x.c) == (uint16_t)le && sg_get_unaligned_be16(x.c+6) == (uint16_t)be && sg_get_unaligned_le32(x.c) == (uint32_t)le && sg_get_unaligned_be32(x.c+4) == (uint32_t)be && sg_get_unaligned_le64(x.c) == le && sg_get_unaligned_be64(x.c) == be )) throw std::logic_error("CPU endianness does not match compile time test"); } #ifndef HAVE_WORKING_SNPRINTF // Some versions of (v)snprintf() don't append null char (MSVCRT.DLL), // and/or return -1 on output truncation (glibc <= 2.0.6). // Below are sane replacements substituted by #define in utility.h. #undef vsnprintf #if defined(_WIN32) && defined(_MSC_VER) #define vsnprintf _vsnprintf #endif int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap) { int i; if (size <= 0) return 0; i = vsnprintf(buf, size, fmt, ap); if (0 <= i && i < size) return i; buf[size-1] = 0; return strlen(buf); // Note: cannot detect for overflow, not necessary here. } int safe_snprintf(char *buf, int size, const char *fmt, ...) { int i; va_list ap; va_start(ap, fmt); i = safe_vsnprintf(buf, size, fmt, ap); va_end(ap); return i; } static void check_snprintf() {} #elif defined(__GNUC__) && (__GNUC__ >= 7) // G++ 7+: Assume sane implementation and avoid -Wformat-truncation warning static void check_snprintf() {} #else static void check_snprintf() { char buf[] = "ABCDEFGHI"; int n1 = snprintf(buf, 8, "123456789"); int n2 = snprintf(buf, 0, "X"); if (!(!strcmp(buf, "1234567") && n1 == 9 && n2 == 1)) throw std::logic_error("Function snprintf() does not conform to C99"); } #endif // HAVE_WORKING_SNPRINTF // Runtime check of ./configure result, throws on error. void check_config() { check_endianness(); check_snprintf(); } smartmontools-7.0/utility.h0000644000175000010010000002201113402014526013054 00000000000000/* * utility.h * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2008-18 Christian Franke * Copyright (C) 2000 Michael Cornwell * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef UTILITY_H_ #define UTILITY_H_ #define UTILITY_H_CVSID "$Id: utility.h 4848 2018-12-05 18:30:46Z chrfranke $" #include // *DBL_MANT_DIG #include #include #include #include #include #include #include // for regex.h (according to POSIX) #ifdef WITH_CXX11_REGEX #include #else #include #endif #ifndef __GNUC__ #define __attribute_format_printf(x, y) /**/ #elif defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO // Check format of __mingw_*printf() instead of MSVCRT.DLL:*printf() #define __attribute_format_printf(x, y) __attribute__((format (gnu_printf, x, y))) #define HAVE_WORKING_SNPRINTF 1 #else #define __attribute_format_printf(x, y) __attribute__((format (printf, x, y))) #endif // Make version information string std::string format_version_info(const char * prog_name, bool full = false); // return (v)sprintf() formatted std::string std::string strprintf(const char * fmt, ...) __attribute_format_printf(1, 2); std::string vstrprintf(const char * fmt, va_list ap); // Return true if STR starts with PREFIX inline bool str_starts_with(const char * str, const char * prefix) { return !strncmp(str, prefix, strlen(prefix)); } inline bool str_starts_with(const std::string & str, const char * prefix) { return !strncmp(str.c_str(), prefix, strlen(prefix)); } #ifndef HAVE_WORKING_SNPRINTF // Substitute by safe replacement functions int safe_snprintf(char *buf, int size, const char *fmt, ...) __attribute_format_printf(3, 4); 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 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 dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval); // 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. void pout(const char *fmt, ...) __attribute_format_printf(1, 2); // replacement for perror() with redirected output. void syserror(const char *message); // Function for processing -t selective... option in smartctl int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode); // Compile time check of byte ordering // (inline const function allows compiler to remove dead code) inline bool isbigendian() { #ifdef WORDS_BIGENDIAN return true; #else return false; #endif } void swap2(char *location); void swap4(char *location); void swap8(char *location); // Typesafe variants using overloading inline void swapx(unsigned short * p) { swap2((char*)p); } inline void swapx(unsigned int * p) { swap4((char*)p); } inline void swapx(uint64_t * p) { swap8((char*)p); } // Runtime check of ./configure result, throws on error. void check_config(); // 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); // returns true if any of the n bytes are nonzero, else zero. bool nonempty(const void * data, int size); // needed to fix glibc bug void FixGlibcTimeZoneBug(); // Copy not null terminated char array to null terminated string. // Replace non-ascii characters. Remove leading and trailing blanks. const char * format_char_array(char * str, int strsize, const char * chr, int chrsize); // Version for fixed size buffers. template inline const char * format_char_array(char (& str)[STRSIZE], const char (& chr)[CHRSIZE]) { return format_char_array(str, (int)STRSIZE, chr, (int)CHRSIZE); } // Format integer with thousands separator const char * format_with_thousands_sep(char * str, int strsize, uint64_t val, const char * thousands_sep = 0); // Format capacity with SI prefixes const char * format_capacity(char * str, int strsize, uint64_t val, const char * decimal_point = 0); // Wrapper class for a raw data buffer class raw_buffer { public: explicit raw_buffer(unsigned sz, unsigned char val = 0) : m_data(new unsigned char[sz]), m_size(sz) { memset(m_data, val, m_size); } ~raw_buffer() { delete [] m_data; } unsigned size() const { return m_size; } unsigned char * data() { return m_data; } const unsigned char * data() const { return m_data; } private: unsigned char * m_data; unsigned m_size; raw_buffer(const raw_buffer &); void operator=(const raw_buffer &); }; /// Wrapper class for FILE *. class stdio_file { public: explicit stdio_file(FILE * f = 0, bool owner = false) : m_file(f), m_owner(owner) { } stdio_file(const char * name, const char * mode) : m_file(fopen(name, mode)), m_owner(true) { } ~stdio_file() { if (m_file && m_owner) fclose(m_file); } bool open(const char * name, const char * mode) { if (m_file && m_owner) fclose(m_file); m_file = fopen(name, mode); m_owner = true; return !!m_file; } void open(FILE * f, bool owner = false) { if (m_file && m_owner) fclose(m_file); m_file = f; m_owner = owner; } bool close() { if (!m_file) return true; bool ok = !ferror(m_file); if (fclose(m_file)) ok = false; m_file = 0; return ok; } operator FILE * () { return m_file; } bool operator!() const { return !m_file; } private: FILE * m_file; bool m_owner; stdio_file(const stdio_file &); void operator=(const stdio_file &); }; /// Wrapper class for POSIX regex(3) or std::regex /// Supports copy & assignment and is compatible with STL containers. class regular_expression { public: // Construction & assignment #ifdef WITH_CXX11_REGEX regular_expression() = default; #else regular_expression(); ~regular_expression(); regular_expression(const regular_expression & x); regular_expression & operator=(const regular_expression & x); #endif /// Construct with pattern, throw on error. explicit regular_expression(const char * pattern); /// Set and compile new pattern, return false on error. bool compile(const char * pattern); // Get pattern from last compile(). const char * get_pattern() const { return m_pattern.c_str(); } /// Get error message from last compile(). const char * get_errmsg() const { return m_errmsg.c_str(); } // Return true if pattern is not set or bad. bool empty() const { return (m_pattern.empty() || !m_errmsg.empty()); } /// Return true if full string matches pattern bool full_match(const char * str) const; #ifdef WITH_CXX11_REGEX struct match_range { int rm_so, rm_eo; }; #else typedef regmatch_t match_range; #endif /// Return true if substring matches pattern, fill match_range array. bool execute(const char * str, unsigned nmatch, match_range * pmatch) const; private: std::string m_pattern; std::string m_errmsg; #ifdef WITH_CXX11_REGEX std::regex m_regex; #else regex_t m_regex_buf; void free_buf(); void copy_buf(const regular_expression & x); #endif bool compile(); }; // 128-bit unsigned integer to string conversion. // Provides full integer precision if compiler supports '__int128'. // Otherwise precision depends on supported floating point data types. #if defined(HAVE_LONG_DOUBLE_WIDER) && \ (!defined(__MINGW32__) || defined(__USE_MINGW_ANSI_STDIO)) // MinGW 'long double' type does not work with MSVCRT *printf() #define HAVE_LONG_DOUBLE_WIDER_PRINTF 1 #else #undef HAVE_LONG_DOUBLE_WIDER_PRINTF #endif // Return #bits precision provided by uint128_hilo_to_str(). inline int uint128_to_str_precision_bits() { #if defined(HAVE___INT128) return 128; #elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF) return LDBL_MANT_DIG; #else return DBL_MANT_DIG; #endif } // Convert 128-bit unsigned integer provided as two 64-bit halves to a string. const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo); // Version for fixed size buffers. template inline const char * uint128_hilo_to_str(char (& str)[SIZE], uint64_t value_hi, uint64_t value_lo) { return uint128_hilo_to_str(str, (int)SIZE, value_hi, value_lo); } #ifdef _WIN32 // Get exe directory //(implemented in os_win32.cpp) std::string get_exe_dir(); #endif #ifdef OLD_INTERFACE // remaining controller types in old interface modules #define CONTROLLER_UNKNOWN 0x00 #define CONTROLLER_ATA 0x01 #define CONTROLLER_SCSI 0x02 #endif #endif