]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * utility.cpp |
832b75ed GG |
3 | * |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
2127e193 GI |
6 | * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net> |
7 | * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net> | |
832b75ed GG |
8 | * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
9 | * | |
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) | |
13 | * any later version. | |
14 | * | |
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. | |
18 | * | |
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/ | |
23 | * | |
24 | */ | |
25 | ||
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, | |
28 | // SMARTCTL, OR BOTH. | |
29 | ||
30 | #include <stdio.h> | |
31 | #include <string.h> | |
32 | #include <time.h> | |
33 | #include <errno.h> | |
34 | #include <stdlib.h> | |
35 | #include <ctype.h> | |
36 | #include <syslog.h> | |
37 | #include <stdarg.h> | |
38 | #include <sys/stat.h> | |
39 | #ifdef _WIN32 | |
40 | #include <mbstring.h> // _mbsinc() | |
41 | #endif | |
42 | ||
2127e193 GI |
43 | #include <stdexcept> |
44 | ||
832b75ed | 45 | #include "config.h" |
2127e193 | 46 | #include "svnversion.h" |
832b75ed GG |
47 | #include "int64.h" |
48 | #include "utility.h" | |
49 | ||
2127e193 GI |
50 | #include "atacmds.h" |
51 | #include "dev_interface.h" | |
52 | ||
bed94269 | 53 | const char * utility_cpp_cvsid = "$Id: utility.cpp 2991 2009-12-03 19:53:21Z chrfranke $" |
2127e193 | 54 | UTILITY_H_CVSID INT64_H_CVSID; |
832b75ed GG |
55 | |
56 | const char * packet_types[] = { | |
57 | "Direct-access (disk)", | |
58 | "Sequential-access (tape)", | |
59 | "Printer", | |
60 | "Processor", | |
61 | "Write-once (optical disk)", | |
62 | "CD/DVD", | |
63 | "Scanner", | |
64 | "Optical memory (optical disk)", | |
65 | "Medium changer", | |
66 | "Communications", | |
67 | "Graphic arts pre-press (10)", | |
68 | "Graphic arts pre-press (11)", | |
69 | "Array controller", | |
70 | "Enclosure services", | |
71 | "Reduced block command (simplified disk)", | |
72 | "Optical card reader/writer" | |
73 | }; | |
74 | ||
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"; | |
77 | ||
78 | ||
832b75ed GG |
79 | // command-line argument: are we running in debug mode?. |
80 | unsigned char debugmode = 0; | |
81 | ||
2127e193 GI |
82 | // BUILD_INFO can be provided by package maintainers |
83 | #ifndef BUILD_INFO | |
84 | #define BUILD_INFO "(local build)" | |
85 | #endif | |
86 | ||
87 | // Make version information string | |
88 | std::string format_version_info(const char * prog_name, bool full /*= false*/) | |
89 | { | |
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", | |
54965743 | 94 | prog_name, smi()->get_os_version_str().c_str() |
2127e193 GI |
95 | ); |
96 | if (!full) | |
97 | return info; | |
98 | ||
99 | info += strprintf( | |
100 | "\n" | |
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" | |
bed94269 GI |
105 | "\n", |
106 | prog_name | |
107 | ); | |
108 | info += strprintf( | |
2127e193 GI |
109 | "smartmontools release "PACKAGE_VERSION |
110 | " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n" | |
111 | "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV | |
112 | " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n" | |
113 | "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n" | |
114 | "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n" | |
bed94269 GI |
115 | "%s compile dated "__DATE__" at "__TIME__"\n" |
116 | "smartmontools configure arguments: ", | |
117 | prog_name | |
2127e193 | 118 | ); |
bed94269 GI |
119 | info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ? |
120 | SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]"); | |
121 | info += '\n'; | |
2127e193 GI |
122 | |
123 | return info; | |
124 | } | |
832b75ed GG |
125 | |
126 | // Solaris only: Get site-default timezone. This is called from | |
127 | // UpdateTimezone() when TZ environment variable is unset at startup. | |
128 | #if defined (__SVR4) && defined (__sun) | |
129 | static const char *TIMEZONE_FILE = "/etc/TIMEZONE"; | |
130 | ||
131 | static char *ReadSiteDefaultTimezone(){ | |
132 | FILE *fp; | |
133 | char buf[512], *tz; | |
134 | int n; | |
135 | ||
136 | tz = NULL; | |
137 | fp = fopen(TIMEZONE_FILE, "r"); | |
138 | if(fp == NULL) return NULL; | |
139 | while(fgets(buf, sizeof(buf), fp)) { | |
140 | if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line | |
141 | continue; | |
142 | n = strlen(buf) - 1; | |
143 | if (buf[n] == '\n') buf[n] = 0; | |
144 | if (tz) free(tz); | |
145 | tz = strdup(buf); | |
146 | } | |
147 | fclose(fp); | |
148 | return tz; | |
149 | } | |
150 | #endif | |
151 | ||
152 | // Make sure that this executable is aware if the user has changed the | |
153 | // time-zone since the last time we polled devices. The cannonical | |
154 | // example is a user who starts smartd on a laptop, then flies across | |
155 | // time-zones with a laptop, and then changes the timezone, WITHOUT | |
156 | // restarting smartd. This is a work-around for a bug in | |
157 | // GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and | |
158 | // thanks to Ian Redfern for posting a workaround. | |
159 | ||
160 | // Please refer to the smartd manual page, in the section labeled LOG | |
161 | // TIMESTAMP TIMEZONE. | |
162 | void FixGlibcTimeZoneBug(){ | |
163 | #if __GLIBC__ | |
164 | if (!getenv("TZ")) { | |
2127e193 | 165 | putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)' |
832b75ed | 166 | tzset(); |
2127e193 | 167 | putenv((char *)"TZ"); |
832b75ed GG |
168 | tzset(); |
169 | } | |
170 | #elif _WIN32 | |
171 | if (!getenv("TZ")) { | |
172 | putenv("TZ=GMT"); | |
173 | tzset(); | |
174 | putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing | |
175 | tzset(); | |
176 | } | |
177 | #elif defined (__SVR4) && defined (__sun) | |
178 | // In Solaris, putenv("TZ=") sets null string and invalid timezone. | |
179 | // putenv("TZ") does nothing. With invalid TZ, tzset() do as if | |
180 | // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at | |
181 | // first tzset() call. Conclusion: Unlike glibc, dynamic | |
182 | // configuration of timezone can be done only by changing actual | |
183 | // value of TZ environment value. | |
184 | enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE }; | |
185 | static enum tzstate state = NOT_CALLED_YET; | |
186 | ||
187 | static struct stat prev_stat; | |
188 | static char *prev_tz; | |
189 | struct stat curr_stat; | |
190 | char *curr_tz; | |
191 | ||
192 | if(state == NOT_CALLED_YET) { | |
193 | if(getenv("TZ")) { | |
194 | state = USER_TIMEZONE; // use supplied timezone | |
195 | } else { | |
196 | state = TRACK_TIMEZONE; | |
197 | if(stat(TIMEZONE_FILE, &prev_stat)) { | |
198 | state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever | |
199 | } else { | |
200 | prev_tz = ReadSiteDefaultTimezone(); // track timezone file change | |
201 | if(prev_tz) putenv(prev_tz); | |
202 | } | |
203 | } | |
204 | tzset(); | |
205 | } else if(state == TRACK_TIMEZONE) { | |
206 | if(stat(TIMEZONE_FILE, &curr_stat) == 0 | |
207 | && (curr_stat.st_ctime != prev_stat.st_ctime | |
208 | || curr_stat.st_mtime != prev_stat.st_mtime)) { | |
209 | // timezone file changed | |
210 | curr_tz = ReadSiteDefaultTimezone(); | |
211 | if(curr_tz) { | |
212 | putenv(curr_tz); | |
213 | if(prev_tz) free(prev_tz); | |
214 | prev_tz = curr_tz; prev_stat = curr_stat; | |
215 | } | |
216 | } | |
217 | tzset(); | |
218 | } | |
219 | #endif | |
220 | // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO | |
221 | // KEEP THEM INDEPENDENT. | |
222 | return; | |
223 | } | |
224 | ||
225 | #ifdef _WIN32 | |
226 | // Fix strings in tzname[] to avoid long names with non-ascii characters. | |
227 | // If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the | |
228 | // national language timezone names returned by GetTimezoneInformation(). | |
229 | static char * fixtzname(char * dest, int destsize, const char * src) | |
230 | { | |
231 | int i = 0, j = 0; | |
232 | while (src[i] && j < destsize-1) { | |
233 | int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src; | |
234 | if (i2 > i+1) | |
235 | i = i2; // Ignore multibyte chars | |
236 | else { | |
237 | if ('A' <= src[i] && src[i] <= 'Z') | |
238 | dest[j++] = src[i]; // "Pacific Standard Time" => "PST" | |
239 | i++; | |
240 | } | |
241 | } | |
242 | if (j < 2) | |
243 | j = 0; | |
244 | dest[j] = 0; | |
245 | return dest; | |
246 | } | |
247 | #endif // _WIN32 | |
248 | ||
249 | // This value follows the peripheral device type value as defined in | |
250 | // SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in | |
251 | // the ATA standard for packet devices to define the device type. | |
252 | const char *packetdevicetype(int type){ | |
253 | if (type<0x10) | |
254 | return packet_types[type]; | |
255 | ||
256 | if (type<0x20) | |
257 | return "Reserved"; | |
258 | ||
259 | return "Unknown"; | |
260 | } | |
261 | ||
262 | ||
263 | // Returns 1 if machine is big endian, else zero. This is a run-time | |
264 | // rather than a compile-time function. We could do it at | |
265 | // compile-time but in principle there are architectures that can run | |
266 | // with either byte-ordering. | |
267 | int isbigendian(){ | |
268 | short i=0x0100; | |
269 | char *tmp=(char *)&i; | |
270 | return *tmp; | |
271 | } | |
272 | ||
273 | // Utility function prints date and time and timezone into a character | |
274 | // buffer of length>=64. All the fuss is needed to get the right | |
275 | // timezone info (sigh). | |
276 | void dateandtimezoneepoch(char *buffer, time_t tval){ | |
277 | struct tm *tmval; | |
2127e193 | 278 | const char *timezonename; |
832b75ed GG |
279 | char datebuffer[DATEANDEPOCHLEN]; |
280 | int lenm1; | |
281 | #ifdef _WIN32 | |
282 | char tzfixbuf[6+1]; | |
283 | #endif | |
284 | ||
285 | FixGlibcTimeZoneBug(); | |
286 | ||
287 | // Get the time structure. We need this to determine if we are in | |
288 | // daylight savings time or not. | |
289 | tmval=localtime(&tval); | |
290 | ||
291 | // Convert to an ASCII string, put in datebuffer | |
292 | // same as: asctime_r(tmval, datebuffer); | |
293 | strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN); | |
294 | datebuffer[DATEANDEPOCHLEN-1]='\0'; | |
295 | ||
296 | // Remove newline | |
297 | lenm1=strlen(datebuffer)-1; | |
298 | datebuffer[lenm1>=0?lenm1:0]='\0'; | |
299 | ||
300 | // correct timezone name | |
301 | if (tmval->tm_isdst==0) | |
302 | // standard time zone | |
303 | timezonename=tzname[0]; | |
304 | else if (tmval->tm_isdst>0) | |
305 | // daylight savings in effect | |
306 | timezonename=tzname[1]; | |
307 | else | |
308 | // unable to determine if daylight savings in effect | |
309 | timezonename=""; | |
310 | ||
311 | #ifdef _WIN32 | |
312 | // Fix long non-ascii timezone names | |
313 | if (!getenv("TZ")) | |
314 | timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename); | |
315 | #endif | |
316 | ||
317 | // Finally put the information into the buffer as needed. | |
318 | snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename); | |
319 | ||
320 | return; | |
321 | } | |
322 | ||
323 | // Date and timezone gets printed into string pointed to by buffer | |
324 | void dateandtimezone(char *buffer){ | |
325 | ||
326 | // Get the epoch (time in seconds since Jan 1 1970) | |
327 | time_t tval=time(NULL); | |
328 | ||
329 | dateandtimezoneepoch(buffer, tval); | |
330 | return; | |
331 | } | |
332 | ||
832b75ed GG |
333 | // A replacement for perror() that sends output to our choice of |
334 | // printing. If errno not set then just print message. | |
335 | void syserror(const char *message){ | |
336 | ||
337 | if (errno) { | |
338 | // Get the correct system error message: | |
339 | const char *errormessage=strerror(errno); | |
340 | ||
341 | // Check that caller has handed a sensible string, and provide | |
342 | // appropriate output. See perrror(3) man page to understand better. | |
343 | if (message && *message) | |
344 | pout("%s: %s\n",message, errormessage); | |
345 | else | |
346 | pout("%s\n",errormessage); | |
347 | } | |
348 | else if (message && *message) | |
349 | pout("%s\n",message); | |
350 | ||
351 | return; | |
352 | } | |
353 | ||
832b75ed GG |
354 | // POSIX extended regular expressions interpret unmatched ')' ordinary: |
355 | // "The close-parenthesis shall be considered special in this context | |
356 | // only if matched with a preceding open-parenthesis." | |
357 | // | |
358 | // Actual '(...)' nesting errors remain undetected on strict POSIX | |
359 | // implementations (glibc) but an error is reported on others (Cygwin). | |
360 | // | |
361 | // The check below is rather incomplete because it does not handle | |
362 | // e.g. '\)' '[)]'. | |
2127e193 GI |
363 | // But it should work for the regex subset used in drive database |
364 | // and smartd '-s' directives. | |
832b75ed GG |
365 | static int check_regex_nesting(const char * pattern) |
366 | { | |
367 | int level = 0, i; | |
368 | for (i = 0; pattern[i] && level >= 0; i++) { | |
369 | switch (pattern[i]) { | |
370 | case '(': level++; break; | |
371 | case ')': level--; break; | |
372 | } | |
373 | } | |
374 | return level; | |
375 | } | |
376 | ||
2127e193 | 377 | // Wrapper class for regex(3) |
832b75ed | 378 | |
2127e193 GI |
379 | regular_expression::regular_expression() |
380 | : m_flags(0) | |
381 | { | |
382 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
383 | } | |
384 | ||
385 | regular_expression::regular_expression(const char * pattern, int flags) | |
386 | { | |
387 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
388 | compile(pattern, flags); | |
389 | } | |
390 | ||
391 | regular_expression::~regular_expression() | |
392 | { | |
393 | free_buf(); | |
394 | } | |
395 | ||
396 | regular_expression::regular_expression(const regular_expression & x) | |
397 | { | |
398 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
399 | copy(x); | |
400 | } | |
401 | ||
402 | regular_expression & regular_expression::operator=(const regular_expression & x) | |
403 | { | |
404 | free_buf(); | |
405 | copy(x); | |
406 | return *this; | |
407 | } | |
408 | ||
409 | void regular_expression::free_buf() | |
410 | { | |
411 | if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) { | |
412 | regfree(&m_regex_buf); | |
413 | memset(&m_regex_buf, 0, sizeof(m_regex_buf)); | |
832b75ed | 414 | } |
2127e193 GI |
415 | } |
416 | ||
417 | void regular_expression::copy(const regular_expression & x) | |
418 | { | |
419 | m_pattern = x.m_pattern; | |
420 | m_flags = x.m_flags; | |
421 | m_errmsg = x.m_errmsg; | |
422 | ||
423 | if (!m_pattern.empty() && m_errmsg.empty()) { | |
424 | // There is no POSIX compiled-regex-copy command. | |
425 | if (!compile()) | |
426 | throw std::runtime_error(strprintf( | |
427 | "Unable to recompile regular expression \"%s\": %s", | |
428 | m_pattern.c_str(), m_errmsg.c_str())); | |
429 | } | |
430 | } | |
431 | ||
432 | bool regular_expression::compile(const char * pattern, int flags) | |
433 | { | |
434 | free_buf(); | |
435 | m_pattern = pattern; | |
436 | m_flags = flags; | |
437 | return compile(); | |
438 | } | |
439 | ||
440 | bool regular_expression::compile() | |
441 | { | |
442 | int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags); | |
443 | if (errcode) { | |
444 | char errmsg[512]; | |
445 | regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg)); | |
446 | m_errmsg = errmsg; | |
447 | free_buf(); | |
448 | return false; | |
449 | } | |
450 | ||
451 | if (check_regex_nesting(m_pattern.c_str()) < 0) { | |
452 | m_errmsg = "Unmatched ')'"; | |
453 | free_buf(); | |
454 | return false; | |
455 | } | |
456 | ||
457 | m_errmsg.clear(); | |
458 | return true; | |
832b75ed GG |
459 | } |
460 | ||
461 | // Splits an argument to the -r option into a name part and an (optional) | |
462 | // positive integer part. s is a pointer to a string containing the | |
463 | // argument. After the call, s will point to the name part and *i the | |
464 | // integer part if there is one or 1 otherwise. Note that the string s may | |
465 | // be changed by this function. Returns zero if successful and non-zero | |
466 | // otherwise. | |
467 | int split_report_arg(char *s, int *i) | |
468 | { | |
469 | if ((s = strchr(s, ','))) { | |
470 | // Looks like there's a name part and an integer part. | |
471 | char *tailptr; | |
472 | ||
473 | *s++ = '\0'; | |
474 | if (*s == '0' || !isdigit((int)*s)) // The integer part must be positive | |
475 | return 1; | |
476 | errno = 0; | |
477 | *i = (int) strtol(s, &tailptr, 10); | |
478 | if (errno || *tailptr != '\0') | |
479 | return 1; | |
480 | } else { | |
481 | // There's no integer part. | |
482 | *i = 1; | |
483 | } | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | // same as above but sets *i to -1 if missing , argument | |
489 | int split_report_arg2(char *s, int *i){ | |
490 | char *tailptr; | |
491 | s+=6; | |
492 | ||
493 | if (*s=='\0' || !isdigit((int)*s)) { | |
494 | // What's left must be integer | |
495 | *i=-1; | |
496 | return 1; | |
497 | } | |
498 | ||
499 | errno = 0; | |
500 | *i = (int) strtol(s, &tailptr, 10); | |
501 | if (errno || *tailptr != '\0') { | |
502 | *i=-1; | |
503 | return 1; | |
504 | } | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
509 | #ifndef HAVE_STRTOULL | |
2127e193 GI |
510 | // Replacement for missing strtoull() (Linux with libc < 6, MSVC) |
511 | // Functionality reduced to requirements of smartd and split_selective_arg(). | |
832b75ed | 512 | |
2127e193 | 513 | uint64_t strtoull(const char * p, char * * endp, int base) |
832b75ed GG |
514 | { |
515 | uint64_t result, maxres; | |
516 | int i = 0; | |
517 | char c = p[i++]; | |
2127e193 GI |
518 | |
519 | if (!base) { | |
520 | if (c == '0') { | |
521 | if (p[i] == 'x' || p[i] == 'X') { | |
522 | base = 16; i++; | |
523 | } | |
524 | else | |
525 | base = 8; | |
526 | c = p[i++]; | |
832b75ed GG |
527 | } |
528 | else | |
2127e193 | 529 | base = 10; |
832b75ed | 530 | } |
832b75ed GG |
531 | |
532 | result = 0; | |
533 | maxres = ~(uint64_t)0 / (unsigned)base; | |
534 | for (;;) { | |
535 | unsigned digit; | |
536 | if ('0' <= c && c <= '9') | |
537 | digit = c - '0'; | |
538 | else if ('A' <= c && c <= 'Z') | |
539 | digit = c - 'A' + 10; | |
540 | else if ('a' <= c && c <= 'z') | |
541 | digit = c - 'a' + 10; | |
542 | else | |
543 | break; | |
544 | if (digit >= (unsigned)base) | |
545 | break; | |
546 | if (!( result < maxres | |
547 | || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) { | |
548 | result = ~(uint64_t)0; errno = ERANGE; // return on overflow | |
549 | break; | |
550 | } | |
551 | result = result * (unsigned)base + digit; | |
552 | c = p[i++]; | |
553 | } | |
2127e193 GI |
554 | if (endp) |
555 | *endp = (char *)p + i - 1; | |
832b75ed GG |
556 | return result; |
557 | } | |
558 | #endif // HAVE_STRTOLL | |
559 | ||
560 | // Splits an argument to the -t option that is assumed to be of the form | |
561 | // "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex) | |
562 | // are allowed). The first long long int is assigned to *start and the second | |
563 | // to *stop. Returns zero if successful and non-zero otherwise. | |
564 | int split_selective_arg(char *s, uint64_t *start, | |
a37e7145 | 565 | uint64_t *stop, int *mode) |
832b75ed GG |
566 | { |
567 | char *tailptr; | |
832b75ed GG |
568 | if (!(s = strchr(s, ','))) |
569 | return 1; | |
a37e7145 GG |
570 | bool add = false; |
571 | if (!isdigit((int)(*++s))) { | |
572 | *start = *stop = 0; | |
573 | if (!strncmp(s, "redo", 4)) | |
574 | *mode = SEL_REDO; | |
575 | else if (!strncmp(s, "next", 4)) | |
576 | *mode = SEL_NEXT; | |
577 | else if (!strncmp(s, "cont", 4)) | |
578 | *mode = SEL_CONT; | |
579 | else | |
580 | return 1; | |
581 | s += 4; | |
582 | if (!*s) | |
583 | return 0; | |
584 | if (*s != '+') | |
585 | return 1; | |
586 | } | |
587 | else { | |
588 | *mode = SEL_RANGE; | |
589 | errno = 0; | |
590 | // Last argument to strtoull (the base) is 0 meaning that decimal is assumed | |
591 | // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used. | |
592 | *start = strtoull(s, &tailptr, 0); | |
593 | s = tailptr; | |
594 | add = (*s == '+'); | |
595 | if (!(!errno && (add || *s == '-'))) | |
596 | return 1; | |
597 | if (!strcmp(s, "-max")) { | |
598 | *stop = ~(uint64_t)0; // replaced by max LBA later | |
599 | return 0; | |
600 | } | |
601 | } | |
602 | *stop = strtoull(s+1, &tailptr, 0); | |
832b75ed GG |
603 | if (errno || *tailptr != '\0') |
604 | return 1; | |
a37e7145 GG |
605 | if (add) { |
606 | if (*stop > 0) | |
607 | (*stop)--; | |
608 | *stop += *start; // -t select,N+M => -t select,N,(N+M-1) | |
609 | } | |
832b75ed GG |
610 | return 0; |
611 | } | |
612 | ||
2127e193 GI |
613 | #ifdef OLD_INTERFACE |
614 | ||
615 | // smartd exit codes | |
616 | #define EXIT_NOMEM 8 // out of memory | |
617 | #define EXIT_BADCODE 10 // internal error - should NEVER happen | |
618 | ||
832b75ed | 619 | int64_t bytes = 0; |
2127e193 | 620 | |
832b75ed GG |
621 | // Helps debugging. If the second argument is non-negative, then |
622 | // decrement bytes by that amount. Else decrement bytes by (one plus) | |
623 | // length of null terminated string. | |
4d59bff9 | 624 | void *FreeNonZero1(void *address, int size, int line, const char* file){ |
832b75ed GG |
625 | if (address) { |
626 | if (size<0) | |
4d59bff9 | 627 | bytes-=1+strlen((char*)address); |
832b75ed GG |
628 | else |
629 | bytes-=size; | |
4d59bff9 | 630 | return CheckFree1(address, line, file); |
832b75ed GG |
631 | } |
632 | return NULL; | |
633 | } | |
634 | ||
635 | // To help with memory checking. Use when it is known that address is | |
636 | // NOT null. | |
4d59bff9 | 637 | void *CheckFree1(void *address, int whatline, const char* file){ |
832b75ed GG |
638 | if (address){ |
639 | free(address); | |
640 | return NULL; | |
641 | } | |
642 | ||
643 | PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", | |
644 | whatline, file, reportbug); | |
645 | EXIT(EXIT_BADCODE); | |
646 | } | |
647 | ||
648 | // A custom version of calloc() that tracks memory use | |
649 | void *Calloc(size_t nmemb, size_t size) { | |
650 | void *ptr=calloc(nmemb, size); | |
651 | ||
652 | if (ptr) | |
653 | bytes+=nmemb*size; | |
654 | ||
655 | return ptr; | |
656 | } | |
657 | ||
658 | // A custom version of strdup() that keeps track of how much memory is | |
659 | // being allocated. If mustexist is set, it also throws an error if we | |
660 | // try to duplicate a NULL string. | |
a37e7145 | 661 | char *CustomStrDup(const char *ptr, int mustexist, int whatline, const char* file){ |
832b75ed GG |
662 | char *tmp; |
663 | ||
664 | // report error if ptr is NULL and mustexist is set | |
665 | if (ptr==NULL){ | |
666 | if (mustexist) { | |
667 | PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", | |
668 | whatline, file, reportbug); | |
669 | EXIT(EXIT_BADCODE); | |
670 | } | |
671 | else | |
672 | return NULL; | |
673 | } | |
674 | ||
675 | // make a copy of the string... | |
676 | tmp=strdup(ptr); | |
677 | ||
678 | if (!tmp) { | |
679 | PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file); | |
680 | EXIT(EXIT_NOMEM); | |
681 | } | |
682 | ||
683 | // and track memory usage | |
684 | bytes+=1+strlen(ptr); | |
685 | ||
686 | return tmp; | |
687 | } | |
688 | ||
2127e193 GI |
689 | #endif // OLD_INTERFACE |
690 | ||
691 | ||
692 | // Returns true if region of memory contains non-zero entries | |
693 | bool nonempty(const void * data, int size) | |
694 | { | |
695 | for (int i = 0; i < size; i++) | |
696 | if (((const unsigned char *)data)[i]) | |
697 | return true; | |
698 | return false; | |
832b75ed GG |
699 | } |
700 | ||
701 | ||
702 | // This routine converts an integer number of milliseconds into a test | |
703 | // string of the form Xd+Yh+Zm+Ts.msec. The resulting text string is | |
704 | // written to the array. | |
705 | void MsecToText(unsigned int msec, char *txt){ | |
706 | int start=0; | |
707 | unsigned int days, hours, min, sec; | |
708 | ||
709 | days = msec/86400000U; | |
710 | msec -= days*86400000U; | |
711 | ||
712 | hours = msec/3600000U; | |
713 | msec -= hours*3600000U; | |
714 | ||
715 | min = msec/60000U; | |
716 | msec -= min*60000U; | |
717 | ||
718 | sec = msec/1000U; | |
719 | msec -= sec*1000U; | |
720 | ||
721 | if (days) { | |
722 | txt += sprintf(txt, "%2dd+", (int)days); | |
723 | start=1; | |
724 | } | |
725 | ||
726 | sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec); | |
727 | return; | |
728 | } | |
729 | ||
2127e193 GI |
730 | // return (v)sprintf() formatted std::string |
731 | ||
732 | std::string vstrprintf(const char * fmt, va_list ap) | |
733 | { | |
734 | char buf[512]; | |
735 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
736 | buf[sizeof(buf)-1] = 0; | |
737 | return buf; | |
738 | } | |
739 | ||
740 | std::string strprintf(const char * fmt, ...) | |
741 | { | |
742 | va_list ap; va_start(ap, fmt); | |
743 | std::string str = vstrprintf(fmt, ap); | |
744 | va_end(ap); | |
745 | return str; | |
746 | } | |
747 | ||
832b75ed GG |
748 | |
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. | |
753 | ||
754 | #undef vsnprintf | |
755 | #if defined(_WIN32) && defined(_MSC_VER) | |
756 | #define vsnprintf _vsnprintf | |
757 | #endif | |
758 | ||
759 | int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap) | |
760 | { | |
761 | int i; | |
762 | if (size <= 0) | |
763 | return 0; | |
764 | i = vsnprintf(buf, size, fmt, ap); | |
765 | if (0 <= i && i < size) | |
766 | return i; | |
767 | buf[size-1] = 0; | |
768 | return strlen(buf); // Note: cannot detect for overflow, not necessary here. | |
769 | } | |
770 | ||
771 | int safe_snprintf(char *buf, int size, const char *fmt, ...) | |
772 | { | |
773 | int i; va_list ap; | |
774 | va_start(ap, fmt); | |
775 | i = safe_vsnprintf(buf, size, fmt, ap); | |
776 | va_end(ap); | |
777 | return i; | |
778 | } | |
779 | ||
780 | #endif |