4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
8 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
15 * You should have received a copy of the GNU General Public License
16 * (for example COPYING); if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * This code was originally developed as a Senior Thesis by Michael Cornwell
20 * at the Concurrent Systems Laboratory (now part of the Storage Systems
21 * Research Center), Jack Baskin School of Engineering, University of
22 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
26 // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
27 // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
44 #include <mbstring.h> // _mbsinc()
49 #include "svnversion.h"
54 #include "dev_interface.h"
56 const char * utility_cpp_cvsid
= "$Id: utility.cpp 3500 2012-01-01 18:03:36Z chrfranke $"
57 UTILITY_H_CVSID INT64_H_CVSID
;
59 const char * packet_types
[] = {
60 "Direct-access (disk)",
61 "Sequential-access (tape)",
64 "Write-once (optical disk)",
67 "Optical memory (optical disk)",
70 "Graphic arts pre-press (10)",
71 "Graphic arts pre-press (11)",
74 "Reduced block command (simplified disk)",
75 "Optical card reader/writer"
78 // BUILD_INFO can be provided by package maintainers
80 #define BUILD_INFO "(local build)"
83 // Make version information string
84 std::string
format_version_info(const char * prog_name
, bool full
/*= false*/)
86 std::string info
= strprintf(
87 "%s "PACKAGE_VERSION
" "
88 #ifdef SMARTMONTOOLS_SVN_REV
89 SMARTMONTOOLS_SVN_DATE
" r"SMARTMONTOOLS_SVN_REV
91 "(build date "__DATE__
")" // checkout without expansion of Id keywords
93 " [%s] "BUILD_INFO
"\n"
94 "Copyright (C) 2002-12 by Bruce Allen, http://smartmontools.sourceforge.net\n",
95 prog_name
, smi()->get_os_version_str().c_str()
102 "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
103 "software, and you are welcome to redistribute it under\n"
104 "the terms of the GNU General Public License; either\n"
105 "version 2, or (at your option) any later version.\n"
106 "See http://www.gnu.org for further details.\n"
111 "smartmontools release "PACKAGE_VERSION
112 " dated "SMARTMONTOOLS_RELEASE_DATE
" at "SMARTMONTOOLS_RELEASE_TIME
"\n"
113 #ifdef SMARTMONTOOLS_SVN_REV
114 "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
115 " dated "SMARTMONTOOLS_SVN_DATE
" at "SMARTMONTOOLS_SVN_TIME
"\n"
117 "smartmontools SVN rev is unknown\n"
119 "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST
"\n"
120 "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE
"\n"
121 "%s compile dated "__DATE__
" at "__TIME__
"\n"
122 "smartmontools configure arguments: ",
125 info
+= (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS
) > 1 ?
126 SMARTMONTOOLS_CONFIGURE_ARGS
: "[no arguments given]");
132 // Solaris only: Get site-default timezone. This is called from
133 // UpdateTimezone() when TZ environment variable is unset at startup.
134 #if defined (__SVR4) && defined (__sun)
135 static const char *TIMEZONE_FILE
= "/etc/TIMEZONE";
137 static char *ReadSiteDefaultTimezone(){
143 fp
= fopen(TIMEZONE_FILE
, "r");
144 if(fp
== NULL
) return NULL
;
145 while(fgets(buf
, sizeof(buf
), fp
)) {
146 if (strncmp(buf
, "TZ=", 3)) // searches last "TZ=" line
149 if (buf
[n
] == '\n') buf
[n
] = 0;
158 // Make sure that this executable is aware if the user has changed the
159 // time-zone since the last time we polled devices. The cannonical
160 // example is a user who starts smartd on a laptop, then flies across
161 // time-zones with a laptop, and then changes the timezone, WITHOUT
162 // restarting smartd. This is a work-around for a bug in
163 // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
164 // thanks to Ian Redfern for posting a workaround.
166 // Please refer to the smartd manual page, in the section labeled LOG
167 // TIMESTAMP TIMEZONE.
168 void FixGlibcTimeZoneBug(){
171 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
173 putenv((char *)"TZ");
180 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
183 #elif defined (__SVR4) && defined (__sun)
184 // In Solaris, putenv("TZ=") sets null string and invalid timezone.
185 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
186 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
187 // first tzset() call. Conclusion: Unlike glibc, dynamic
188 // configuration of timezone can be done only by changing actual
189 // value of TZ environment value.
190 enum tzstate
{ NOT_CALLED_YET
, USER_TIMEZONE
, TRACK_TIMEZONE
};
191 static enum tzstate state
= NOT_CALLED_YET
;
193 static struct stat prev_stat
;
194 static char *prev_tz
;
195 struct stat curr_stat
;
198 if(state
== NOT_CALLED_YET
) {
200 state
= USER_TIMEZONE
; // use supplied timezone
202 state
= TRACK_TIMEZONE
;
203 if(stat(TIMEZONE_FILE
, &prev_stat
)) {
204 state
= USER_TIMEZONE
; // no TZ, no timezone file; use GMT forever
206 prev_tz
= ReadSiteDefaultTimezone(); // track timezone file change
207 if(prev_tz
) putenv(prev_tz
);
211 } else if(state
== TRACK_TIMEZONE
) {
212 if(stat(TIMEZONE_FILE
, &curr_stat
) == 0
213 && (curr_stat
.st_ctime
!= prev_stat
.st_ctime
214 || curr_stat
.st_mtime
!= prev_stat
.st_mtime
)) {
215 // timezone file changed
216 curr_tz
= ReadSiteDefaultTimezone();
219 if(prev_tz
) free(prev_tz
);
220 prev_tz
= curr_tz
; prev_stat
= curr_stat
;
226 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
227 // KEEP THEM INDEPENDENT.
232 // Fix strings in tzname[] to avoid long names with non-ascii characters.
233 // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
234 // national language timezone names returned by GetTimezoneInformation().
235 static char * fixtzname(char * dest
, int destsize
, const char * src
)
238 while (src
[i
] && j
< destsize
-1) {
239 int i2
= (const char *)_mbsinc((const unsigned char *)src
+i
) - src
;
241 i
= i2
; // Ignore multibyte chars
243 if ('A' <= src
[i
] && src
[i
] <= 'Z')
244 dest
[j
++] = src
[i
]; // "Pacific Standard Time" => "PST"
255 // This value follows the peripheral device type value as defined in
256 // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
257 // the ATA standard for packet devices to define the device type.
258 const char *packetdevicetype(int type
){
260 return packet_types
[type
];
268 // Runtime check of byte ordering, throws if different from isbigendian().
269 void check_endianness()
272 // Force compile error if int type is not 32bit.
273 unsigned char c
[sizeof(unsigned) == 4 ? 4 : -1];
279 case 0x01020304: big
= 1; break;
280 case 0x04030201: big
= 0; break;
283 if (big
!= (isbigendian() ? 1 : 0))
284 throw std::logic_error("CPU endianness does not match compile time test");
287 // Utility function prints date and time and timezone into a character
288 // buffer of length>=64. All the fuss is needed to get the right
289 // timezone info (sigh).
290 void dateandtimezoneepoch(char *buffer
, time_t tval
){
292 const char *timezonename
;
293 char datebuffer
[DATEANDEPOCHLEN
];
299 FixGlibcTimeZoneBug();
301 // Get the time structure. We need this to determine if we are in
302 // daylight savings time or not.
303 tmval
=localtime(&tval
);
305 // Convert to an ASCII string, put in datebuffer
306 // same as: asctime_r(tmval, datebuffer);
307 strncpy(datebuffer
, asctime(tmval
), DATEANDEPOCHLEN
);
308 datebuffer
[DATEANDEPOCHLEN
-1]='\0';
311 lenm1
=strlen(datebuffer
)-1;
312 datebuffer
[lenm1
>=0?lenm1
:0]='\0';
314 // correct timezone name
315 if (tmval
->tm_isdst
==0)
316 // standard time zone
317 timezonename
=tzname
[0];
318 else if (tmval
->tm_isdst
>0)
319 // daylight savings in effect
320 timezonename
=tzname
[1];
322 // unable to determine if daylight savings in effect
326 // Fix long non-ascii timezone names
328 timezonename
=fixtzname(tzfixbuf
, sizeof(tzfixbuf
), timezonename
);
331 // Finally put the information into the buffer as needed.
332 snprintf(buffer
, DATEANDEPOCHLEN
, "%s %s", datebuffer
, timezonename
);
337 // Date and timezone gets printed into string pointed to by buffer
338 void dateandtimezone(char *buffer
){
340 // Get the epoch (time in seconds since Jan 1 1970)
341 time_t tval
=time(NULL
);
343 dateandtimezoneepoch(buffer
, tval
);
347 // A replacement for perror() that sends output to our choice of
348 // printing. If errno not set then just print message.
349 void syserror(const char *message
){
352 // Get the correct system error message:
353 const char *errormessage
=strerror(errno
);
355 // Check that caller has handed a sensible string, and provide
356 // appropriate output. See perrror(3) man page to understand better.
357 if (message
&& *message
)
358 pout("%s: %s\n",message
, errormessage
);
360 pout("%s\n",errormessage
);
362 else if (message
&& *message
)
363 pout("%s\n",message
);
368 // POSIX extended regular expressions interpret unmatched ')' ordinary:
369 // "The close-parenthesis shall be considered special in this context
370 // only if matched with a preceding open-parenthesis."
372 // Actual '(...)' nesting errors remain undetected on strict POSIX
373 // implementations (glibc) but an error is reported on others (Cygwin).
375 // The check below is rather incomplete because it does not handle
377 // But it should work for the regex subset used in drive database
378 // and smartd '-s' directives.
379 static int check_regex_nesting(const char * pattern
)
382 for (i
= 0; pattern
[i
] && level
>= 0; i
++) {
383 switch (pattern
[i
]) {
384 case '(': level
++; break;
385 case ')': level
--; break;
391 // Wrapper class for regex(3)
393 regular_expression::regular_expression()
396 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
399 regular_expression::regular_expression(const char * pattern
, int flags
,
400 bool throw_on_error
/*= true*/)
402 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
403 if (!compile(pattern
, flags
) && throw_on_error
)
404 throw std::runtime_error(strprintf(
405 "error in regular expression \"%s\": %s",
406 m_pattern
.c_str(), m_errmsg
.c_str()));
409 regular_expression::~regular_expression()
414 regular_expression::regular_expression(const regular_expression
& x
)
416 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
420 regular_expression
& regular_expression::operator=(const regular_expression
& x
)
427 void regular_expression::free_buf()
429 if (nonempty(&m_regex_buf
, sizeof(m_regex_buf
))) {
430 regfree(&m_regex_buf
);
431 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
435 void regular_expression::copy(const regular_expression
& x
)
437 m_pattern
= x
.m_pattern
;
439 m_errmsg
= x
.m_errmsg
;
441 if (!m_pattern
.empty() && m_errmsg
.empty()) {
442 // There is no POSIX compiled-regex-copy command.
444 throw std::runtime_error(strprintf(
445 "Unable to recompile regular expression \"%s\": %s",
446 m_pattern
.c_str(), m_errmsg
.c_str()));
450 bool regular_expression::compile(const char * pattern
, int flags
)
458 bool regular_expression::compile()
460 int errcode
= regcomp(&m_regex_buf
, m_pattern
.c_str(), m_flags
);
463 regerror(errcode
, &m_regex_buf
, errmsg
, sizeof(errmsg
));
469 if (check_regex_nesting(m_pattern
.c_str()) < 0) {
470 m_errmsg
= "Unmatched ')'";
479 // Splits an argument to the -r option into a name part and an (optional)
480 // positive integer part. s is a pointer to a string containing the
481 // argument. After the call, s will point to the name part and *i the
482 // integer part if there is one or 1 otherwise. Note that the string s may
483 // be changed by this function. Returns zero if successful and non-zero
485 int split_report_arg(char *s
, int *i
)
487 if ((s
= strchr(s
, ','))) {
488 // Looks like there's a name part and an integer part.
492 if (*s
== '0' || !isdigit((int)*s
)) // The integer part must be positive
495 *i
= (int) strtol(s
, &tailptr
, 10);
496 if (errno
|| *tailptr
!= '\0')
499 // There's no integer part.
506 // same as above but sets *i to -1 if missing , argument
507 int split_report_arg2(char *s
, int *i
){
511 if (*s
=='\0' || !isdigit((int)*s
)) {
512 // What's left must be integer
518 *i
= (int) strtol(s
, &tailptr
, 10);
519 if (errno
|| *tailptr
!= '\0') {
527 #ifndef HAVE_STRTOULL
528 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
529 // Functionality reduced to requirements of smartd and split_selective_arg().
531 uint64_t strtoull(const char * p
, char * * endp
, int base
)
533 uint64_t result
, maxres
;
539 if (p
[i
] == 'x' || p
[i
] == 'X') {
551 maxres
= ~(uint64_t)0 / (unsigned)base
;
554 if ('0' <= c
&& c
<= '9')
556 else if ('A' <= c
&& c
<= 'Z')
557 digit
= c
- 'A' + 10;
558 else if ('a' <= c
&& c
<= 'z')
559 digit
= c
- 'a' + 10;
562 if (digit
>= (unsigned)base
)
564 if (!( result
< maxres
565 || (result
== maxres
&& digit
<= ~(uint64_t)0 % (unsigned)base
))) {
566 result
= ~(uint64_t)0; errno
= ERANGE
; // return on overflow
569 result
= result
* (unsigned)base
+ digit
;
573 *endp
= (char *)p
+ i
- 1;
576 #endif // HAVE_STRTOLL
578 // Splits an argument to the -t option that is assumed to be of the form
579 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
580 // are allowed). The first long long int is assigned to *start and the second
581 // to *stop. Returns zero if successful and non-zero otherwise.
582 int split_selective_arg(char *s
, uint64_t *start
,
583 uint64_t *stop
, int *mode
)
586 if (!(s
= strchr(s
, ',')))
589 if (!isdigit((int)(*++s
))) {
591 if (!strncmp(s
, "redo", 4))
593 else if (!strncmp(s
, "next", 4))
595 else if (!strncmp(s
, "cont", 4))
608 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
609 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
610 *start
= strtoull(s
, &tailptr
, 0);
613 if (!(!errno
&& (add
|| *s
== '-')))
615 if (!strcmp(s
, "-max")) {
616 *stop
= ~(uint64_t)0; // replaced by max LBA later
620 *stop
= strtoull(s
+1, &tailptr
, 0);
621 if (errno
|| *tailptr
!= '\0')
626 *stop
+= *start
; // -t select,N+M => -t select,N,(N+M-1)
635 // Helps debugging. If the second argument is non-negative, then
636 // decrement bytes by that amount. Else decrement bytes by (one plus)
637 // length of null terminated string.
638 void *FreeNonZero1(void *address
, int size
, int line
, const char* file
){
641 bytes
-=1+strlen((char*)address
);
644 return CheckFree1(address
, line
, file
);
649 // To help with memory checking. Use when it is known that address is
651 void *CheckFree1(void *address
, int /*whatline*/, const char* /*file*/){
656 throw std::runtime_error("Internal error in CheckFree()");
659 // A custom version of calloc() that tracks memory use
660 void *Calloc(size_t nmemb
, size_t size
) {
661 void *ptr
=calloc(nmemb
, size
);
669 // A custom version of strdup() that keeps track of how much memory is
670 // being allocated. If mustexist is set, it also throws an error if we
671 // try to duplicate a NULL string.
672 char *CustomStrDup(const char *ptr
, int mustexist
, int /*whatline*/, const char* /*file*/){
675 // report error if ptr is NULL and mustexist is set
678 throw std::runtime_error("Internal error in CustomStrDup()");
683 // make a copy of the string...
687 throw std::bad_alloc();
689 // and track memory usage
690 bytes
+=1+strlen(ptr
);
695 #endif // OLD_INTERFACE
698 // Returns true if region of memory contains non-zero entries
699 bool nonempty(const void * data
, int size
)
701 for (int i
= 0; i
< size
; i
++)
702 if (((const unsigned char *)data
)[i
])
708 // This routine converts an integer number of milliseconds into a test
709 // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
710 // written to the array.
711 void MsecToText(unsigned int msec
, char *txt
){
712 unsigned int days
, hours
, min
, sec
;
714 days
= msec
/86400000U;
715 msec
-= days
*86400000U;
717 hours
= msec
/3600000U;
718 msec
-= hours
*3600000U;
727 txt
+= sprintf(txt
, "%2dd+", (int)days
);
730 sprintf(txt
, "%02d:%02d:%02d.%03d", (int)hours
, (int)min
, (int)sec
, (int)msec
);
734 // Format integer with thousands separator
735 const char * format_with_thousands_sep(char * str
, int strsize
, uint64_t val
,
736 const char * thousands_sep
/* = 0 */)
738 if (!thousands_sep
) {
741 setlocale(LC_ALL
, "");
742 const struct lconv
* currentlocale
= localeconv();
743 if (*(currentlocale
->thousands_sep
))
744 thousands_sep
= currentlocale
->thousands_sep
;
749 snprintf(num
, sizeof(num
), "%"PRIu64
, val
);
750 int numlen
= strlen(num
);
755 while (i
< numlen
&& (numlen
- i
) % 3 != 0 && j
< strsize
-1);
758 while (i
< numlen
&& j
< strsize
-1) {
759 j
+= snprintf(str
+j
, strsize
-j
, "%s%.3s", thousands_sep
, num
+i
);
766 // Format capacity with SI prefixes
767 const char * format_capacity(char * str
, int strsize
, uint64_t val
,
768 const char * decimal_point
/* = 0 */)
770 if (!decimal_point
) {
773 setlocale(LC_ALL
, "");
774 const struct lconv
* currentlocale
= localeconv();
775 if (*(currentlocale
->decimal_point
))
776 decimal_point
= currentlocale
->decimal_point
;
780 const unsigned factor
= 1000; // 1024 for KiB,MiB,...
781 static const char prefixes
[] = " KMGTP";
783 // Find d with val in [d, d*factor)
786 for (uint64_t d2
= d
* factor
; val
>= d2
; d2
*= factor
) {
788 if (++i
>= sizeof(prefixes
)-2)
793 uint64_t n
= val
/ d
;
795 snprintf(str
, strsize
, "%u B", (unsigned)n
);
796 else if (n
>= 100) // "123 xB"
797 snprintf(str
, strsize
, "%"PRIu64
" %cB", n
, prefixes
[i
]);
798 else if (n
>= 10) // "12.3 xB"
799 snprintf(str
, strsize
, "%"PRIu64
"%s%u %cB", n
, decimal_point
,
800 (unsigned)(((val
% d
) * 10) / d
), prefixes
[i
]);
802 snprintf(str
, strsize
, "%"PRIu64
"%s%02u %cB", n
, decimal_point
,
803 (unsigned)(((val
% d
) * 100) / d
), prefixes
[i
]);
808 // return (v)sprintf() formatted std::string
810 std::string
vstrprintf(const char * fmt
, va_list ap
)
813 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
814 buf
[sizeof(buf
)-1] = 0;
818 std::string
strprintf(const char * fmt
, ...)
820 va_list ap
; va_start(ap
, fmt
);
821 std::string str
= vstrprintf(fmt
, ap
);
827 #ifndef HAVE_WORKING_SNPRINTF
828 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
829 // and/or return -1 on overflow (old Linux).
830 // Below are sane replacements substituted by #define in utility.h.
833 #if defined(_WIN32) && defined(_MSC_VER)
834 #define vsnprintf _vsnprintf
837 int safe_vsnprintf(char *buf
, int size
, const char *fmt
, va_list ap
)
842 i
= vsnprintf(buf
, size
, fmt
, ap
);
843 if (0 <= i
&& i
< size
)
846 return strlen(buf
); // Note: cannot detect for overflow, not necessary here.
849 int safe_snprintf(char *buf
, int size
, const char *fmt
, ...)
853 i
= safe_vsnprintf(buf
, size
, fmt
, ap
);