4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2008-11 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,
40 #include <mbstring.h> // _mbsinc()
46 #include "svnversion.h"
51 #include "dev_interface.h"
53 const char * utility_cpp_cvsid
= "$Id: utility.cpp 3285 2011-03-04 22:08:49Z chrfranke $"
54 UTILITY_H_CVSID INT64_H_CVSID
;
56 const char * packet_types
[] = {
57 "Direct-access (disk)",
58 "Sequential-access (tape)",
61 "Write-once (optical disk)",
64 "Optical memory (optical disk)",
67 "Graphic arts pre-press (10)",
68 "Graphic arts pre-press (11)",
71 "Reduced block command (simplified disk)",
72 "Optical card reader/writer"
75 // BUILD_INFO can be provided by package maintainers
77 #define BUILD_INFO "(local build)"
80 // Make version information string
81 std::string
format_version_info(const char * prog_name
, bool full
/*= false*/)
83 std::string info
= strprintf(
84 "%s "PACKAGE_VERSION
" "
85 #ifdef SMARTMONTOOLS_SVN_REV
86 SMARTMONTOOLS_SVN_DATE
" r"SMARTMONTOOLS_SVN_REV
88 "(build date "__DATE__
")" // checkout without expansion of Id keywords
90 " [%s] "BUILD_INFO
"\n"
91 "Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net\n",
92 prog_name
, smi()->get_os_version_str().c_str()
99 "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
100 "software, and you are welcome to redistribute it under\n"
101 "the terms of the GNU General Public License Version 2.\n"
102 "See http://www.gnu.org for further details.\n"
107 "smartmontools release "PACKAGE_VERSION
108 " dated "SMARTMONTOOLS_RELEASE_DATE
" at "SMARTMONTOOLS_RELEASE_TIME
"\n"
109 #ifdef SMARTMONTOOLS_SVN_REV
110 "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
111 " dated "SMARTMONTOOLS_SVN_DATE
" at "SMARTMONTOOLS_SVN_TIME
"\n"
113 "smartmontools SVN rev is unknown\n"
115 "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST
"\n"
116 "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE
"\n"
117 "%s compile dated "__DATE__
" at "__TIME__
"\n"
118 "smartmontools configure arguments: ",
121 info
+= (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS
) > 1 ?
122 SMARTMONTOOLS_CONFIGURE_ARGS
: "[no arguments given]");
128 // Solaris only: Get site-default timezone. This is called from
129 // UpdateTimezone() when TZ environment variable is unset at startup.
130 #if defined (__SVR4) && defined (__sun)
131 static const char *TIMEZONE_FILE
= "/etc/TIMEZONE";
133 static char *ReadSiteDefaultTimezone(){
139 fp
= fopen(TIMEZONE_FILE
, "r");
140 if(fp
== NULL
) return NULL
;
141 while(fgets(buf
, sizeof(buf
), fp
)) {
142 if (strncmp(buf
, "TZ=", 3)) // searches last "TZ=" line
145 if (buf
[n
] == '\n') buf
[n
] = 0;
154 // Make sure that this executable is aware if the user has changed the
155 // time-zone since the last time we polled devices. The cannonical
156 // example is a user who starts smartd on a laptop, then flies across
157 // time-zones with a laptop, and then changes the timezone, WITHOUT
158 // restarting smartd. This is a work-around for a bug in
159 // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
160 // thanks to Ian Redfern for posting a workaround.
162 // Please refer to the smartd manual page, in the section labeled LOG
163 // TIMESTAMP TIMEZONE.
164 void FixGlibcTimeZoneBug(){
167 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
169 putenv((char *)"TZ");
176 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
179 #elif defined (__SVR4) && defined (__sun)
180 // In Solaris, putenv("TZ=") sets null string and invalid timezone.
181 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
182 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
183 // first tzset() call. Conclusion: Unlike glibc, dynamic
184 // configuration of timezone can be done only by changing actual
185 // value of TZ environment value.
186 enum tzstate
{ NOT_CALLED_YET
, USER_TIMEZONE
, TRACK_TIMEZONE
};
187 static enum tzstate state
= NOT_CALLED_YET
;
189 static struct stat prev_stat
;
190 static char *prev_tz
;
191 struct stat curr_stat
;
194 if(state
== NOT_CALLED_YET
) {
196 state
= USER_TIMEZONE
; // use supplied timezone
198 state
= TRACK_TIMEZONE
;
199 if(stat(TIMEZONE_FILE
, &prev_stat
)) {
200 state
= USER_TIMEZONE
; // no TZ, no timezone file; use GMT forever
202 prev_tz
= ReadSiteDefaultTimezone(); // track timezone file change
203 if(prev_tz
) putenv(prev_tz
);
207 } else if(state
== TRACK_TIMEZONE
) {
208 if(stat(TIMEZONE_FILE
, &curr_stat
) == 0
209 && (curr_stat
.st_ctime
!= prev_stat
.st_ctime
210 || curr_stat
.st_mtime
!= prev_stat
.st_mtime
)) {
211 // timezone file changed
212 curr_tz
= ReadSiteDefaultTimezone();
215 if(prev_tz
) free(prev_tz
);
216 prev_tz
= curr_tz
; prev_stat
= curr_stat
;
222 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
223 // KEEP THEM INDEPENDENT.
228 // Fix strings in tzname[] to avoid long names with non-ascii characters.
229 // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
230 // national language timezone names returned by GetTimezoneInformation().
231 static char * fixtzname(char * dest
, int destsize
, const char * src
)
234 while (src
[i
] && j
< destsize
-1) {
235 int i2
= (const char *)_mbsinc((const unsigned char *)src
+i
) - src
;
237 i
= i2
; // Ignore multibyte chars
239 if ('A' <= src
[i
] && src
[i
] <= 'Z')
240 dest
[j
++] = src
[i
]; // "Pacific Standard Time" => "PST"
251 // This value follows the peripheral device type value as defined in
252 // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
253 // the ATA standard for packet devices to define the device type.
254 const char *packetdevicetype(int type
){
256 return packet_types
[type
];
264 // Runtime check of byte ordering, throws if different from isbigendian().
265 void check_endianness()
268 // Force compile error if int type is not 32bit.
269 unsigned char c
[sizeof(unsigned) == 4 ? 4 : -1];
275 case 0x01020304: big
= 1; break;
276 case 0x04030201: big
= 0; break;
279 if (big
!= (isbigendian() ? 1 : 0))
280 throw std::logic_error("CPU endianness does not match compile time test");
283 // Utility function prints date and time and timezone into a character
284 // buffer of length>=64. All the fuss is needed to get the right
285 // timezone info (sigh).
286 void dateandtimezoneepoch(char *buffer
, time_t tval
){
288 const char *timezonename
;
289 char datebuffer
[DATEANDEPOCHLEN
];
295 FixGlibcTimeZoneBug();
297 // Get the time structure. We need this to determine if we are in
298 // daylight savings time or not.
299 tmval
=localtime(&tval
);
301 // Convert to an ASCII string, put in datebuffer
302 // same as: asctime_r(tmval, datebuffer);
303 strncpy(datebuffer
, asctime(tmval
), DATEANDEPOCHLEN
);
304 datebuffer
[DATEANDEPOCHLEN
-1]='\0';
307 lenm1
=strlen(datebuffer
)-1;
308 datebuffer
[lenm1
>=0?lenm1
:0]='\0';
310 // correct timezone name
311 if (tmval
->tm_isdst
==0)
312 // standard time zone
313 timezonename
=tzname
[0];
314 else if (tmval
->tm_isdst
>0)
315 // daylight savings in effect
316 timezonename
=tzname
[1];
318 // unable to determine if daylight savings in effect
322 // Fix long non-ascii timezone names
324 timezonename
=fixtzname(tzfixbuf
, sizeof(tzfixbuf
), timezonename
);
327 // Finally put the information into the buffer as needed.
328 snprintf(buffer
, DATEANDEPOCHLEN
, "%s %s", datebuffer
, timezonename
);
333 // Date and timezone gets printed into string pointed to by buffer
334 void dateandtimezone(char *buffer
){
336 // Get the epoch (time in seconds since Jan 1 1970)
337 time_t tval
=time(NULL
);
339 dateandtimezoneepoch(buffer
, tval
);
343 // A replacement for perror() that sends output to our choice of
344 // printing. If errno not set then just print message.
345 void syserror(const char *message
){
348 // Get the correct system error message:
349 const char *errormessage
=strerror(errno
);
351 // Check that caller has handed a sensible string, and provide
352 // appropriate output. See perrror(3) man page to understand better.
353 if (message
&& *message
)
354 pout("%s: %s\n",message
, errormessage
);
356 pout("%s\n",errormessage
);
358 else if (message
&& *message
)
359 pout("%s\n",message
);
364 // POSIX extended regular expressions interpret unmatched ')' ordinary:
365 // "The close-parenthesis shall be considered special in this context
366 // only if matched with a preceding open-parenthesis."
368 // Actual '(...)' nesting errors remain undetected on strict POSIX
369 // implementations (glibc) but an error is reported on others (Cygwin).
371 // The check below is rather incomplete because it does not handle
373 // But it should work for the regex subset used in drive database
374 // and smartd '-s' directives.
375 static int check_regex_nesting(const char * pattern
)
378 for (i
= 0; pattern
[i
] && level
>= 0; i
++) {
379 switch (pattern
[i
]) {
380 case '(': level
++; break;
381 case ')': level
--; break;
387 // Wrapper class for regex(3)
389 regular_expression::regular_expression()
392 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
395 regular_expression::regular_expression(const char * pattern
, int flags
,
396 bool throw_on_error
/*= true*/)
398 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
399 if (!compile(pattern
, flags
) && throw_on_error
)
400 throw std::runtime_error(strprintf(
401 "error in regular expression \"%s\": %s",
402 m_pattern
.c_str(), m_errmsg
.c_str()));
405 regular_expression::~regular_expression()
410 regular_expression::regular_expression(const regular_expression
& x
)
412 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
416 regular_expression
& regular_expression::operator=(const regular_expression
& x
)
423 void regular_expression::free_buf()
425 if (nonempty(&m_regex_buf
, sizeof(m_regex_buf
))) {
426 regfree(&m_regex_buf
);
427 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
431 void regular_expression::copy(const regular_expression
& x
)
433 m_pattern
= x
.m_pattern
;
435 m_errmsg
= x
.m_errmsg
;
437 if (!m_pattern
.empty() && m_errmsg
.empty()) {
438 // There is no POSIX compiled-regex-copy command.
440 throw std::runtime_error(strprintf(
441 "Unable to recompile regular expression \"%s\": %s",
442 m_pattern
.c_str(), m_errmsg
.c_str()));
446 bool regular_expression::compile(const char * pattern
, int flags
)
454 bool regular_expression::compile()
456 int errcode
= regcomp(&m_regex_buf
, m_pattern
.c_str(), m_flags
);
459 regerror(errcode
, &m_regex_buf
, errmsg
, sizeof(errmsg
));
465 if (check_regex_nesting(m_pattern
.c_str()) < 0) {
466 m_errmsg
= "Unmatched ')'";
475 // Splits an argument to the -r option into a name part and an (optional)
476 // positive integer part. s is a pointer to a string containing the
477 // argument. After the call, s will point to the name part and *i the
478 // integer part if there is one or 1 otherwise. Note that the string s may
479 // be changed by this function. Returns zero if successful and non-zero
481 int split_report_arg(char *s
, int *i
)
483 if ((s
= strchr(s
, ','))) {
484 // Looks like there's a name part and an integer part.
488 if (*s
== '0' || !isdigit((int)*s
)) // The integer part must be positive
491 *i
= (int) strtol(s
, &tailptr
, 10);
492 if (errno
|| *tailptr
!= '\0')
495 // There's no integer part.
502 // same as above but sets *i to -1 if missing , argument
503 int split_report_arg2(char *s
, int *i
){
507 if (*s
=='\0' || !isdigit((int)*s
)) {
508 // What's left must be integer
514 *i
= (int) strtol(s
, &tailptr
, 10);
515 if (errno
|| *tailptr
!= '\0') {
523 #ifndef HAVE_STRTOULL
524 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
525 // Functionality reduced to requirements of smartd and split_selective_arg().
527 uint64_t strtoull(const char * p
, char * * endp
, int base
)
529 uint64_t result
, maxres
;
535 if (p
[i
] == 'x' || p
[i
] == 'X') {
547 maxres
= ~(uint64_t)0 / (unsigned)base
;
550 if ('0' <= c
&& c
<= '9')
552 else if ('A' <= c
&& c
<= 'Z')
553 digit
= c
- 'A' + 10;
554 else if ('a' <= c
&& c
<= 'z')
555 digit
= c
- 'a' + 10;
558 if (digit
>= (unsigned)base
)
560 if (!( result
< maxres
561 || (result
== maxres
&& digit
<= ~(uint64_t)0 % (unsigned)base
))) {
562 result
= ~(uint64_t)0; errno
= ERANGE
; // return on overflow
565 result
= result
* (unsigned)base
+ digit
;
569 *endp
= (char *)p
+ i
- 1;
572 #endif // HAVE_STRTOLL
574 // Splits an argument to the -t option that is assumed to be of the form
575 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
576 // are allowed). The first long long int is assigned to *start and the second
577 // to *stop. Returns zero if successful and non-zero otherwise.
578 int split_selective_arg(char *s
, uint64_t *start
,
579 uint64_t *stop
, int *mode
)
582 if (!(s
= strchr(s
, ',')))
585 if (!isdigit((int)(*++s
))) {
587 if (!strncmp(s
, "redo", 4))
589 else if (!strncmp(s
, "next", 4))
591 else if (!strncmp(s
, "cont", 4))
604 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
605 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
606 *start
= strtoull(s
, &tailptr
, 0);
609 if (!(!errno
&& (add
|| *s
== '-')))
611 if (!strcmp(s
, "-max")) {
612 *stop
= ~(uint64_t)0; // replaced by max LBA later
616 *stop
= strtoull(s
+1, &tailptr
, 0);
617 if (errno
|| *tailptr
!= '\0')
622 *stop
+= *start
; // -t select,N+M => -t select,N,(N+M-1)
631 // Helps debugging. If the second argument is non-negative, then
632 // decrement bytes by that amount. Else decrement bytes by (one plus)
633 // length of null terminated string.
634 void *FreeNonZero1(void *address
, int size
, int line
, const char* file
){
637 bytes
-=1+strlen((char*)address
);
640 return CheckFree1(address
, line
, file
);
645 // To help with memory checking. Use when it is known that address is
647 void *CheckFree1(void *address
, int /*whatline*/, const char* /*file*/){
652 throw std::runtime_error("Internal error in CheckFree()");
655 // A custom version of calloc() that tracks memory use
656 void *Calloc(size_t nmemb
, size_t size
) {
657 void *ptr
=calloc(nmemb
, size
);
665 // A custom version of strdup() that keeps track of how much memory is
666 // being allocated. If mustexist is set, it also throws an error if we
667 // try to duplicate a NULL string.
668 char *CustomStrDup(const char *ptr
, int mustexist
, int /*whatline*/, const char* /*file*/){
671 // report error if ptr is NULL and mustexist is set
674 throw std::runtime_error("Internal error in CustomStrDup()");
679 // make a copy of the string...
683 throw std::bad_alloc();
685 // and track memory usage
686 bytes
+=1+strlen(ptr
);
691 #endif // OLD_INTERFACE
694 // Returns true if region of memory contains non-zero entries
695 bool nonempty(const void * data
, int size
)
697 for (int i
= 0; i
< size
; i
++)
698 if (((const unsigned char *)data
)[i
])
704 // This routine converts an integer number of milliseconds into a test
705 // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
706 // written to the array.
707 void MsecToText(unsigned int msec
, char *txt
){
708 unsigned int days
, hours
, min
, sec
;
710 days
= msec
/86400000U;
711 msec
-= days
*86400000U;
713 hours
= msec
/3600000U;
714 msec
-= hours
*3600000U;
723 txt
+= sprintf(txt
, "%2dd+", (int)days
);
726 sprintf(txt
, "%02d:%02d:%02d.%03d", (int)hours
, (int)min
, (int)sec
, (int)msec
);
730 // return (v)sprintf() formatted std::string
732 std::string
vstrprintf(const char * fmt
, va_list ap
)
735 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
736 buf
[sizeof(buf
)-1] = 0;
740 std::string
strprintf(const char * fmt
, ...)
742 va_list ap
; va_start(ap
, fmt
);
743 std::string str
= vstrprintf(fmt
, ap
);
749 #ifndef HAVE_WORKING_SNPRINTF
750 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
751 // and/or return -1 on overflow (old Linux).
752 // Below are sane replacements substituted by #define in utility.h.
755 #if defined(_WIN32) && defined(_MSC_VER)
756 #define vsnprintf _vsnprintf
759 int safe_vsnprintf(char *buf
, int size
, const char *fmt
, va_list ap
)
764 i
= vsnprintf(buf
, size
, fmt
, ap
);
765 if (0 <= i
&& i
< size
)
768 return strlen(buf
); // Note: cannot detect for overflow, not necessary here.
771 int safe_snprintf(char *buf
, int size
, const char *fmt
, ...)
775 i
= safe_vsnprintf(buf
, size
, fmt
, ap
);