4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * You should have received a copy of the GNU General Public License
15 * (for example COPYING); if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * This code was originally developed as a Senior Thesis by Michael Cornwell
19 * at the Concurrent Systems Laboratory (now part of the Storage Systems
20 * Research Center), Jack Baskin School of Engineering, University of
21 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
25 // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
26 // BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
39 #include <mbstring.h> // _mbsinc()
46 // Any local header files should be represented by a CVSIDX just below.
47 const char* utility_c_cvsid
="$Id: utility.c,v 1.61 2006/04/12 14:54:28 ballen4705 Exp $"
48 CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID
;
50 const char * packet_types
[] = {
51 "Direct-access (disk)",
52 "Sequential-access (tape)",
55 "Write-once (optical disk)",
58 "Optical memory (optical disk)",
61 "Graphic arts pre-press (10)",
62 "Graphic arts pre-press (11)",
65 "Reduced block command (simplified disk)",
66 "Optical card reader/writer"
69 // Whenever exit() status is EXIT_BADCODE, please print this message
70 const char *reportbug
="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT
".\n";
73 // hang on to exit code, so we can make use of more generic 'atexit()'
74 // functionality and still check our exit code
77 // command-line argument: are we running in debug mode?.
78 unsigned char debugmode
= 0;
81 // Solaris only: Get site-default timezone. This is called from
82 // UpdateTimezone() when TZ environment variable is unset at startup.
83 #if defined (__SVR4) && defined (__sun)
84 static const char *TIMEZONE_FILE
= "/etc/TIMEZONE";
86 static char *ReadSiteDefaultTimezone(){
92 fp
= fopen(TIMEZONE_FILE
, "r");
93 if(fp
== NULL
) return NULL
;
94 while(fgets(buf
, sizeof(buf
), fp
)) {
95 if (strncmp(buf
, "TZ=", 3)) // searches last "TZ=" line
98 if (buf
[n
] == '\n') buf
[n
] = 0;
107 // Make sure that this executable is aware if the user has changed the
108 // time-zone since the last time we polled devices. The cannonical
109 // example is a user who starts smartd on a laptop, then flies across
110 // time-zones with a laptop, and then changes the timezone, WITHOUT
111 // restarting smartd. This is a work-around for a bug in
112 // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
113 // thanks to Ian Redfern for posting a workaround.
115 // Please refer to the smartd manual page, in the section labeled LOG
116 // TIMESTAMP TIMEZONE.
117 void FixGlibcTimeZoneBug(){
129 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
132 #elif defined (__SVR4) && defined (__sun)
133 // In Solaris, putenv("TZ=") sets null string and invalid timezone.
134 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
135 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
136 // first tzset() call. Conclusion: Unlike glibc, dynamic
137 // configuration of timezone can be done only by changing actual
138 // value of TZ environment value.
139 enum tzstate
{ NOT_CALLED_YET
, USER_TIMEZONE
, TRACK_TIMEZONE
};
140 static enum tzstate state
= NOT_CALLED_YET
;
142 static struct stat prev_stat
;
143 static char *prev_tz
;
144 struct stat curr_stat
;
147 if(state
== NOT_CALLED_YET
) {
149 state
= USER_TIMEZONE
; // use supplied timezone
151 state
= TRACK_TIMEZONE
;
152 if(stat(TIMEZONE_FILE
, &prev_stat
)) {
153 state
= USER_TIMEZONE
; // no TZ, no timezone file; use GMT forever
155 prev_tz
= ReadSiteDefaultTimezone(); // track timezone file change
156 if(prev_tz
) putenv(prev_tz
);
160 } else if(state
== TRACK_TIMEZONE
) {
161 if(stat(TIMEZONE_FILE
, &curr_stat
) == 0
162 && (curr_stat
.st_ctime
!= prev_stat
.st_ctime
163 || curr_stat
.st_mtime
!= prev_stat
.st_mtime
)) {
164 // timezone file changed
165 curr_tz
= ReadSiteDefaultTimezone();
168 if(prev_tz
) free(prev_tz
);
169 prev_tz
= curr_tz
; prev_stat
= curr_stat
;
175 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
176 // KEEP THEM INDEPENDENT.
181 // Fix strings in tzname[] to avoid long names with non-ascii characters.
182 // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
183 // national language timezone names returned by GetTimezoneInformation().
184 static char * fixtzname(char * dest
, int destsize
, const char * src
)
187 while (src
[i
] && j
< destsize
-1) {
188 int i2
= (const char *)_mbsinc((const unsigned char *)src
+i
) - src
;
190 i
= i2
; // Ignore multibyte chars
192 if ('A' <= src
[i
] && src
[i
] <= 'Z')
193 dest
[j
++] = src
[i
]; // "Pacific Standard Time" => "PST"
204 // This value follows the peripheral device type value as defined in
205 // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
206 // the ATA standard for packet devices to define the device type.
207 const char *packetdevicetype(int type
){
209 return packet_types
[type
];
218 // Returns 1 if machine is big endian, else zero. This is a run-time
219 // rather than a compile-time function. We could do it at
220 // compile-time but in principle there are architectures that can run
221 // with either byte-ordering.
224 char *tmp
=(char *)&i
;
228 // Utility function prints date and time and timezone into a character
229 // buffer of length>=64. All the fuss is needed to get the right
230 // timezone info (sigh).
231 void dateandtimezoneepoch(char *buffer
, time_t tval
){
234 char datebuffer
[DATEANDEPOCHLEN
];
240 FixGlibcTimeZoneBug();
242 // Get the time structure. We need this to determine if we are in
243 // daylight savings time or not.
244 tmval
=localtime(&tval
);
246 // Convert to an ASCII string, put in datebuffer
247 // same as: asctime_r(tmval, datebuffer);
248 strncpy(datebuffer
, asctime(tmval
), DATEANDEPOCHLEN
);
249 datebuffer
[DATEANDEPOCHLEN
-1]='\0';
252 lenm1
=strlen(datebuffer
)-1;
253 datebuffer
[lenm1
>=0?lenm1
:0]='\0';
255 // correct timezone name
256 if (tmval
->tm_isdst
==0)
257 // standard time zone
258 timezonename
=tzname
[0];
259 else if (tmval
->tm_isdst
>0)
260 // daylight savings in effect
261 timezonename
=tzname
[1];
263 // unable to determine if daylight savings in effect
267 // Fix long non-ascii timezone names
269 timezonename
=fixtzname(tzfixbuf
, sizeof(tzfixbuf
), timezonename
);
272 // Finally put the information into the buffer as needed.
273 snprintf(buffer
, DATEANDEPOCHLEN
, "%s %s", datebuffer
, timezonename
);
278 // Date and timezone gets printed into string pointed to by buffer
279 void dateandtimezone(char *buffer
){
281 // Get the epoch (time in seconds since Jan 1 1970)
282 time_t tval
=time(NULL
);
284 dateandtimezoneepoch(buffer
, tval
);
288 // These are two utility functions for printing CVS IDs. Massagecvs()
289 // returns distance that it has moved ahead in the input string
290 int massagecvs(char *out
, const char *cvsid
){
291 char *copy
,*filename
,*date
,*version
;
293 const char delimiters
[] = " ,$";
295 // make a copy on the heap, go to first token,
296 if (!(copy
=strdup(cvsid
)))
299 if (!(filename
=strtok(copy
, delimiters
)))
302 // move to first instance of "Id:"
303 while (strcmp(filename
,"Id:"))
304 if (!(filename
=strtok(NULL
, delimiters
)))
307 // get filename, skip "v", get version and date
308 if (!( filename
=strtok(NULL
, delimiters
) ) ||
309 !( strtok(NULL
, delimiters
) ) ||
310 !( version
=strtok(NULL
, delimiters
) ) ||
311 !( date
=strtok(NULL
, delimiters
) ) )
314 sprintf(out
,"%-16s revision: %-5s date: %-15s", filename
, version
, date
);
315 retVal
= (date
-copy
)+strlen(date
);
322 // prints a single set of CVS ids
323 void printone(char *block
, const char *cvsid
){
324 char strings
[CVSMAXLEN
];
325 const char *here
=cvsid
;
326 int bi
=0, len
=strlen(cvsid
)+1;
328 // check that the size of the output block is sufficient
329 if (len
>=CVSMAXLEN
) {
330 pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN
,len
+1);
334 // loop through the different strings
335 while (bi
<CVSMAXLEN
&& (len
=massagecvs(strings
,here
))){
336 bi
+=snprintf(block
+bi
,CVSMAXLEN
-bi
,"%s %s\n",(bi
==0?"Module:":" uses:"),strings
);
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 // Prints a warning message for a failed regular expression compilation from
366 void printregexwarning(int errcode
, regex_t
*compiled
){
367 size_t length
= regerror(errcode
, compiled
, NULL
, 0);
368 char *buffer
= malloc(length
);
370 pout("Out of memory in printregexwarning()\n");
373 regerror(errcode
, compiled
, buffer
, length
);
374 pout("%s\n", buffer
);
379 // POSIX extended regular expressions interpret unmatched ')' ordinary:
380 // "The close-parenthesis shall be considered special in this context
381 // only if matched with a preceding open-parenthesis."
383 // Actual '(...)' nesting errors remain undetected on strict POSIX
384 // implementations (glibc) but an error is reported on others (Cygwin).
386 // The check below is rather incomplete because it does not handle
388 // But it should work for the regex subset used in drive database.
389 static int check_regex_nesting(const char * pattern
)
392 for (i
= 0; pattern
[i
] && level
>= 0; i
++) {
393 switch (pattern
[i
]) {
394 case '(': level
++; break;
395 case ')': level
--; break;
401 // A wrapper for regcomp(). Returns zero for success, non-zero otherwise.
402 int compileregex(regex_t
*compiled
, const char *pattern
, int cflags
)
406 if ( (errorcode
= regcomp(compiled
, pattern
, cflags
))
407 || check_regex_nesting(pattern
) < 0 ) {
408 pout("Internal error: unable to compile regular expression \"%s\" ", pattern
);
410 printregexwarning(errorcode
, compiled
);
412 pout("Unmatched ')'\n");
413 pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT
"\n");
419 // Splits an argument to the -r option into a name part and an (optional)
420 // positive integer part. s is a pointer to a string containing the
421 // argument. After the call, s will point to the name part and *i the
422 // integer part if there is one or 1 otherwise. Note that the string s may
423 // be changed by this function. Returns zero if successful and non-zero
425 int split_report_arg(char *s
, int *i
)
427 if ((s
= strchr(s
, ','))) {
428 // Looks like there's a name part and an integer part.
432 if (*s
== '0' || !isdigit((int)*s
)) // The integer part must be positive
435 *i
= (int) strtol(s
, &tailptr
, 10);
436 if (errno
|| *tailptr
!= '\0')
439 // There's no integer part.
446 // same as above but sets *i to -1 if missing , argument
447 int split_report_arg2(char *s
, int *i
){
451 if (*s
=='\0' || !isdigit((int)*s
)) {
452 // What's left must be integer
458 *i
= (int) strtol(s
, &tailptr
, 10);
459 if (errno
|| *tailptr
!= '\0') {
467 #ifndef HAVE_STRTOULL
468 // Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0)
469 // Functionality reduced to split_selective_arg()'s requirements.
471 static uint64_t strtoull(const char * p
, char * * endp
, int base
)
473 uint64_t result
, maxres
;
478 if (p
[i
] == 'x' || p
[i
] == 'X') {
489 maxres
= ~(uint64_t)0 / (unsigned)base
;
492 if ('0' <= c
&& c
<= '9')
494 else if ('A' <= c
&& c
<= 'Z')
495 digit
= c
- 'A' + 10;
496 else if ('a' <= c
&& c
<= 'z')
497 digit
= c
- 'a' + 10;
500 if (digit
>= (unsigned)base
)
502 if (!( result
< maxres
503 || (result
== maxres
&& digit
<= ~(uint64_t)0 % (unsigned)base
))) {
504 result
= ~(uint64_t)0; errno
= ERANGE
; // return on overflow
507 result
= result
* (unsigned)base
+ digit
;
510 *endp
= (char *)p
+ i
- 1;
513 #endif // HAVE_STRTOLL
515 // Splits an argument to the -t option that is assumed to be of the form
516 // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
517 // are allowed). The first long long int is assigned to *start and the second
518 // to *stop. Returns zero if successful and non-zero otherwise.
519 int split_selective_arg(char *s
, uint64_t *start
,
524 if (!(s
= strchr(s
, ',')))
526 if (!isdigit((int)(*++s
)))
529 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
530 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
531 *start
= strtoull(s
, &tailptr
, 0);
534 if (errno
|| *s
++ != '-')
536 *stop
= strtoull(s
, &tailptr
, 0);
537 if (errno
|| *tailptr
!= '\0')
543 // Helps debugging. If the second argument is non-negative, then
544 // decrement bytes by that amount. Else decrement bytes by (one plus)
545 // length of null terminated string.
546 void *FreeNonZero(void *address
, int size
, int line
, const char* file
){
549 bytes
-=1+strlen(address
);
552 return CheckFree(address
, line
, file
);
557 // To help with memory checking. Use when it is known that address is
559 void *CheckFree(void *address
, int whatline
, const char* file
){
565 PrintOut(LOG_CRIT
, "Internal error in CheckFree() at line %d of file %s\n%s",
566 whatline
, file
, reportbug
);
570 // A custom version of calloc() that tracks memory use
571 void *Calloc(size_t nmemb
, size_t size
) {
572 void *ptr
=calloc(nmemb
, size
);
580 // A custom version of strdup() that keeps track of how much memory is
581 // being allocated. If mustexist is set, it also throws an error if we
582 // try to duplicate a NULL string.
583 char *CustomStrDup(char *ptr
, int mustexist
, int whatline
, const char* file
){
586 // report error if ptr is NULL and mustexist is set
589 PrintOut(LOG_CRIT
, "Internal error in CustomStrDup() at line %d of file %s\n%s",
590 whatline
, file
, reportbug
);
597 // make a copy of the string...
601 PrintOut(LOG_CRIT
, "No memory to duplicate string %s at line %d of file %s\n", ptr
, whatline
, file
);
605 // and track memory usage
606 bytes
+=1+strlen(ptr
);
611 // Returns nonzero if region of memory contains non-zero entries
612 int nonempty(unsigned char *testarea
,int n
){
621 // This routine converts an integer number of milliseconds into a test
622 // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is
623 // written to the array.
624 void MsecToText(unsigned int msec
, char *txt
){
626 unsigned int days
, hours
, min
, sec
;
628 days
= msec
/86400000U;
629 msec
-= days
*86400000U;
631 hours
= msec
/3600000U;
632 msec
-= hours
*3600000U;
641 txt
+= sprintf(txt
, "%2dd+", (int)days
);
645 sprintf(txt
, "%02d:%02d:%02d.%03d", (int)hours
, (int)min
, (int)sec
, (int)msec
);
650 #ifndef HAVE_WORKING_SNPRINTF
651 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
652 // and/or return -1 on overflow (old Linux).
653 // Below are sane replacements substituted by #define in utility.h.
656 #if defined(_WIN32) && defined(_MSC_VER)
657 #define vsnprintf _vsnprintf
660 int safe_vsnprintf(char *buf
, int size
, const char *fmt
, va_list ap
)
665 i
= vsnprintf(buf
, size
, fmt
, ap
);
666 if (0 <= i
&& i
< size
)
669 return strlen(buf
); // Note: cannot detect for overflow, not necessary here.
672 int safe_snprintf(char *buf
, int size
, const char *fmt
, ...)
676 i
= safe_vsnprintf(buf
, size
, fmt
, ap
);