4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2008-9 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 2848 2009-07-18 20:14:38Z 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 // Whenever exit() status is EXIT_BADCODE, please print this message
76 const char *reportbug
="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT
".\n";
79 // command-line argument: are we running in debug mode?.
80 unsigned char debugmode
= 0;
82 // BUILD_INFO can be provided by package maintainers
84 #define BUILD_INFO "(local build)"
87 // Make version information string
88 std::string
format_version_info(const char * prog_name
, bool full
/*= false*/)
90 std::string info
= strprintf(
91 "%s "PACKAGE_VERSION
" "SMARTMONTOOLS_SVN_DATE
" r"SMARTMONTOOLS_SVN_REV
92 " [%s] "BUILD_INFO
"\n"
93 "Copyright (C) 2002-9 by Bruce Allen, http://smartmontools.sourceforge.net\n",
94 prog_name
, smi()->get_os_version_str()
101 "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
102 "software, and you are welcome to redistribute it under\n"
103 "the terms of the GNU General Public License Version 2.\n"
104 "See http://www.gnu.org for further details.\n"
106 "smartmontools release "PACKAGE_VERSION
107 " dated "SMARTMONTOOLS_RELEASE_DATE
" at "SMARTMONTOOLS_RELEASE_TIME
"\n"
108 "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
109 " dated "SMARTMONTOOLS_SVN_DATE
" at "SMARTMONTOOLS_SVN_TIME
"\n"
110 "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST
"\n"
111 "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE
"\n"
112 "%s compile dated "__DATE__
" at "__TIME__
"\n",
116 "smartmontools configure arguments: %s\n",
117 (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS
) > 1 ?
118 SMARTMONTOOLS_CONFIGURE_ARGS
: "[no arguments given]")
124 // Solaris only: Get site-default timezone. This is called from
125 // UpdateTimezone() when TZ environment variable is unset at startup.
126 #if defined (__SVR4) && defined (__sun)
127 static const char *TIMEZONE_FILE
= "/etc/TIMEZONE";
129 static char *ReadSiteDefaultTimezone(){
135 fp
= fopen(TIMEZONE_FILE
, "r");
136 if(fp
== NULL
) return NULL
;
137 while(fgets(buf
, sizeof(buf
), fp
)) {
138 if (strncmp(buf
, "TZ=", 3)) // searches last "TZ=" line
141 if (buf
[n
] == '\n') buf
[n
] = 0;
150 // Make sure that this executable is aware if the user has changed the
151 // time-zone since the last time we polled devices. The cannonical
152 // example is a user who starts smartd on a laptop, then flies across
153 // time-zones with a laptop, and then changes the timezone, WITHOUT
154 // restarting smartd. This is a work-around for a bug in
155 // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
156 // thanks to Ian Redfern for posting a workaround.
158 // Please refer to the smartd manual page, in the section labeled LOG
159 // TIMESTAMP TIMEZONE.
160 void FixGlibcTimeZoneBug(){
163 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
165 putenv((char *)"TZ");
172 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
175 #elif defined (__SVR4) && defined (__sun)
176 // In Solaris, putenv("TZ=") sets null string and invalid timezone.
177 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
178 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
179 // first tzset() call. Conclusion: Unlike glibc, dynamic
180 // configuration of timezone can be done only by changing actual
181 // value of TZ environment value.
182 enum tzstate
{ NOT_CALLED_YET
, USER_TIMEZONE
, TRACK_TIMEZONE
};
183 static enum tzstate state
= NOT_CALLED_YET
;
185 static struct stat prev_stat
;
186 static char *prev_tz
;
187 struct stat curr_stat
;
190 if(state
== NOT_CALLED_YET
) {
192 state
= USER_TIMEZONE
; // use supplied timezone
194 state
= TRACK_TIMEZONE
;
195 if(stat(TIMEZONE_FILE
, &prev_stat
)) {
196 state
= USER_TIMEZONE
; // no TZ, no timezone file; use GMT forever
198 prev_tz
= ReadSiteDefaultTimezone(); // track timezone file change
199 if(prev_tz
) putenv(prev_tz
);
203 } else if(state
== TRACK_TIMEZONE
) {
204 if(stat(TIMEZONE_FILE
, &curr_stat
) == 0
205 && (curr_stat
.st_ctime
!= prev_stat
.st_ctime
206 || curr_stat
.st_mtime
!= prev_stat
.st_mtime
)) {
207 // timezone file changed
208 curr_tz
= ReadSiteDefaultTimezone();
211 if(prev_tz
) free(prev_tz
);
212 prev_tz
= curr_tz
; prev_stat
= curr_stat
;
218 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
219 // KEEP THEM INDEPENDENT.
224 // Fix strings in tzname[] to avoid long names with non-ascii characters.
225 // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
226 // national language timezone names returned by GetTimezoneInformation().
227 static char * fixtzname(char * dest
, int destsize
, const char * src
)
230 while (src
[i
] && j
< destsize
-1) {
231 int i2
= (const char *)_mbsinc((const unsigned char *)src
+i
) - src
;
233 i
= i2
; // Ignore multibyte chars
235 if ('A' <= src
[i
] && src
[i
] <= 'Z')
236 dest
[j
++] = src
[i
]; // "Pacific Standard Time" => "PST"
247 // This value follows the peripheral device type value as defined in
248 // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
249 // the ATA standard for packet devices to define the device type.
250 const char *packetdevicetype(int type
){
252 return packet_types
[type
];
261 // Returns 1 if machine is big endian, else zero. This is a run-time
262 // rather than a compile-time function. We could do it at
263 // compile-time but in principle there are architectures that can run
264 // with either byte-ordering.
267 char *tmp
=(char *)&i
;
271 // Utility function prints date and time and timezone into a character
272 // buffer of length>=64. All the fuss is needed to get the right
273 // timezone info (sigh).
274 void dateandtimezoneepoch(char *buffer
, time_t tval
){
276 const char *timezonename
;
277 char datebuffer
[DATEANDEPOCHLEN
];
283 FixGlibcTimeZoneBug();
285 // Get the time structure. We need this to determine if we are in
286 // daylight savings time or not.
287 tmval
=localtime(&tval
);
289 // Convert to an ASCII string, put in datebuffer
290 // same as: asctime_r(tmval, datebuffer);
291 strncpy(datebuffer
, asctime(tmval
), DATEANDEPOCHLEN
);
292 datebuffer
[DATEANDEPOCHLEN
-1]='\0';
295 lenm1
=strlen(datebuffer
)-1;
296 datebuffer
[lenm1
>=0?lenm1
:0]='\0';
298 // correct timezone name
299 if (tmval
->tm_isdst
==0)
300 // standard time zone
301 timezonename
=tzname
[0];
302 else if (tmval
->tm_isdst
>0)
303 // daylight savings in effect
304 timezonename
=tzname
[1];
306 // unable to determine if daylight savings in effect
310 // Fix long non-ascii timezone names
312 timezonename
=fixtzname(tzfixbuf
, sizeof(tzfixbuf
), timezonename
);
315 // Finally put the information into the buffer as needed.
316 snprintf(buffer
, DATEANDEPOCHLEN
, "%s %s", datebuffer
, timezonename
);
321 // Date and timezone gets printed into string pointed to by buffer
322 void dateandtimezone(char *buffer
){
324 // Get the epoch (time in seconds since Jan 1 1970)
325 time_t tval
=time(NULL
);
327 dateandtimezoneepoch(buffer
, tval
);
331 // A replacement for perror() that sends output to our choice of
332 // printing. If errno not set then just print message.
333 void syserror(const char *message
){
336 // Get the correct system error message:
337 const char *errormessage
=strerror(errno
);
339 // Check that caller has handed a sensible string, and provide
340 // appropriate output. See perrror(3) man page to understand better.
341 if (message
&& *message
)
342 pout("%s: %s\n",message
, errormessage
);
344 pout("%s\n",errormessage
);
346 else if (message
&& *message
)
347 pout("%s\n",message
);
352 // POSIX extended regular expressions interpret unmatched ')' ordinary:
353 // "The close-parenthesis shall be considered special in this context
354 // only if matched with a preceding open-parenthesis."
356 // Actual '(...)' nesting errors remain undetected on strict POSIX
357 // implementations (glibc) but an error is reported on others (Cygwin).
359 // The check below is rather incomplete because it does not handle
361 // But it should work for the regex subset used in drive database
362 // and smartd '-s' directives.
363 static int check_regex_nesting(const char * pattern
)
366 for (i
= 0; pattern
[i
] && level
>= 0; i
++) {
367 switch (pattern
[i
]) {
368 case '(': level
++; break;
369 case ')': level
--; break;
375 // Wrapper class for regex(3)
377 regular_expression::regular_expression()
380 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
383 regular_expression::regular_expression(const char * pattern
, int flags
)
385 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
386 compile(pattern
, flags
);
389 regular_expression::~regular_expression()
394 regular_expression::regular_expression(const regular_expression
& x
)
396 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
400 regular_expression
& regular_expression::operator=(const regular_expression
& x
)
407 void regular_expression::free_buf()
409 if (nonempty(&m_regex_buf
, sizeof(m_regex_buf
))) {
410 regfree(&m_regex_buf
);
411 memset(&m_regex_buf
, 0, sizeof(m_regex_buf
));
415 void regular_expression::copy(const regular_expression
& x
)
417 m_pattern
= x
.m_pattern
;
419 m_errmsg
= x
.m_errmsg
;
421 if (!m_pattern
.empty() && m_errmsg
.empty()) {
422 // There is no POSIX compiled-regex-copy command.
424 throw std::runtime_error(strprintf(
425 "Unable to recompile regular expression \"%s\": %s",
426 m_pattern
.c_str(), m_errmsg
.c_str()));
430 bool regular_expression::compile(const char * pattern
, int flags
)
438 bool regular_expression::compile()
440 int errcode
= regcomp(&m_regex_buf
, m_pattern
.c_str(), m_flags
);
443 regerror(errcode
, &m_regex_buf
, errmsg
, sizeof(errmsg
));
449 if (check_regex_nesting(m_pattern
.c_str()) < 0) {
450 m_errmsg
= "Unmatched ')'";
459 // Splits an argument to the -r option into a name part and an (optional)
460 // positive integer part. s is a pointer to a string containing the
461 // argument. After the call, s will point to the name part and *i the
462 // integer part if there is one or 1 otherwise. Note that the string s may
463 // be changed by this function. Returns zero if successful and non-zero
465 int split_report_arg(char *s
, int *i
)
467 if ((s
= strchr(s
, ','))) {
468 // Looks like there's a name part and an integer part.
472 if (*s
== '0' || !isdigit((int)*s
)) // The integer part must be positive
475 *i
= (int) strtol(s
, &tailptr
, 10);
476 if (errno
|| *tailptr
!= '\0')
479 // There's no integer part.
486 // same as above but sets *i to -1 if missing , argument
487 int split_report_arg2(char *s
, int *i
){
491 if (*s
=='\0' || !isdigit((int)*s
)) {
492 // What's left must be integer
498 *i
= (int) strtol(s
, &tailptr
, 10);
499 if (errno
|| *tailptr
!= '\0') {
507 #ifndef HAVE_STRTOULL
508 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
509 // Functionality reduced to requirements of smartd and split_selective_arg().
511 uint64_t strtoull(const char * p
, char * * endp
, int base
)
513 uint64_t result
, maxres
;
519 if (p
[i
] == 'x' || p
[i
] == 'X') {
531 maxres
= ~(uint64_t)0 / (unsigned)base
;
534 if ('0' <= c
&& c
<= '9')
536 else if ('A' <= c
&& c
<= 'Z')
537 digit
= c
- 'A' + 10;
538 else if ('a' <= c
&& c
<= 'z')
539 digit
= c
- 'a' + 10;
542 if (digit
>= (unsigned)base
)
544 if (!( result
< maxres
545 || (result
== maxres
&& digit
<= ~(uint64_t)0 % (unsigned)base
))) {
546 result
= ~(uint64_t)0; errno
= ERANGE
; // return on overflow
549 result
= result
* (unsigned)base
+ digit
;
553 *endp
= (char *)p
+ i
- 1;
556 #endif // HAVE_STRTOLL
558 // Splits an argument to the -t option that is assumed to be of the form
559 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
560 // are allowed). The first long long int is assigned to *start and the second
561 // to *stop. Returns zero if successful and non-zero otherwise.
562 int split_selective_arg(char *s
, uint64_t *start
,
563 uint64_t *stop
, int *mode
)
566 if (!(s
= strchr(s
, ',')))
569 if (!isdigit((int)(*++s
))) {
571 if (!strncmp(s
, "redo", 4))
573 else if (!strncmp(s
, "next", 4))
575 else if (!strncmp(s
, "cont", 4))
588 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
589 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
590 *start
= strtoull(s
, &tailptr
, 0);
593 if (!(!errno
&& (add
|| *s
== '-')))
595 if (!strcmp(s
, "-max")) {
596 *stop
= ~(uint64_t)0; // replaced by max LBA later
600 *stop
= strtoull(s
+1, &tailptr
, 0);
601 if (errno
|| *tailptr
!= '\0')
606 *stop
+= *start
; // -t select,N+M => -t select,N,(N+M-1)
614 #define EXIT_NOMEM 8 // out of memory
615 #define EXIT_BADCODE 10 // internal error - should NEVER happen
619 // Helps debugging. If the second argument is non-negative, then
620 // decrement bytes by that amount. Else decrement bytes by (one plus)
621 // length of null terminated string.
622 void *FreeNonZero1(void *address
, int size
, int line
, const char* file
){
625 bytes
-=1+strlen((char*)address
);
628 return CheckFree1(address
, line
, file
);
633 // To help with memory checking. Use when it is known that address is
635 void *CheckFree1(void *address
, int whatline
, const char* file
){
641 PrintOut(LOG_CRIT
, "Internal error in CheckFree() at line %d of file %s\n%s",
642 whatline
, file
, reportbug
);
646 // A custom version of calloc() that tracks memory use
647 void *Calloc(size_t nmemb
, size_t size
) {
648 void *ptr
=calloc(nmemb
, size
);
656 // A custom version of strdup() that keeps track of how much memory is
657 // being allocated. If mustexist is set, it also throws an error if we
658 // try to duplicate a NULL string.
659 char *CustomStrDup(const char *ptr
, int mustexist
, int whatline
, const char* file
){
662 // report error if ptr is NULL and mustexist is set
665 PrintOut(LOG_CRIT
, "Internal error in CustomStrDup() at line %d of file %s\n%s",
666 whatline
, file
, reportbug
);
673 // make a copy of the string...
677 PrintOut(LOG_CRIT
, "No memory to duplicate string %s at line %d of file %s\n", ptr
, whatline
, file
);
681 // and track memory usage
682 bytes
+=1+strlen(ptr
);
687 #endif // OLD_INTERFACE
690 // Returns true if region of memory contains non-zero entries
691 bool nonempty(const void * data
, int size
)
693 for (int i
= 0; i
< size
; i
++)
694 if (((const unsigned char *)data
)[i
])
700 // This routine converts an integer number of milliseconds into a test
701 // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
702 // written to the array.
703 void MsecToText(unsigned int msec
, char *txt
){
705 unsigned int days
, hours
, min
, sec
;
707 days
= msec
/86400000U;
708 msec
-= days
*86400000U;
710 hours
= msec
/3600000U;
711 msec
-= hours
*3600000U;
720 txt
+= sprintf(txt
, "%2dd+", (int)days
);
724 sprintf(txt
, "%02d:%02d:%02d.%03d", (int)hours
, (int)min
, (int)sec
, (int)msec
);
728 // return (v)sprintf() formatted std::string
730 std::string
vstrprintf(const char * fmt
, va_list ap
)
733 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
734 buf
[sizeof(buf
)-1] = 0;
738 std::string
strprintf(const char * fmt
, ...)
740 va_list ap
; va_start(ap
, fmt
);
741 std::string str
= vstrprintf(fmt
, ap
);
747 #ifndef HAVE_WORKING_SNPRINTF
748 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
749 // and/or return -1 on overflow (old Linux).
750 // Below are sane replacements substituted by #define in utility.h.
753 #if defined(_WIN32) && defined(_MSC_VER)
754 #define vsnprintf _vsnprintf
757 int safe_vsnprintf(char *buf
, int size
, const char *fmt
, va_list ap
)
762 i
= vsnprintf(buf
, size
, fmt
, ap
);
763 if (0 <= i
&& i
< size
)
766 return strlen(buf
); // Note: cannot detect for overflow, not necessary here.
769 int safe_snprintf(char *buf
, int size
, const char *fmt
, ...)
773 i
= safe_vsnprintf(buf
, size
, fmt
, ap
);