smartmontools SVN Rev 5612
Utility to control and monitor storage systems with "S.M.A.R.T."
utility.cpp
Go to the documentation of this file.
1/*
2 * utility.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2002-12 Bruce Allen
7 * Copyright (C) 2008-24 Christian Franke
8 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
13// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
14// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
15// SMARTCTL, OR BOTH.
16
17#include "config.h"
18#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
19
20#include <inttypes.h>
21#include <stdio.h>
22#include <string.h>
23#include <time.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <ctype.h>
27#include <stdarg.h>
28#include <sys/stat.h>
29#ifdef HAVE_LOCALE_H
30#include <locale.h>
31#endif
32#ifdef _WIN32
33#include <mbstring.h> // _mbsinc()
34#endif
35
36#include <stdexcept>
37
38#include "svnversion.h"
39#include "utility.h"
40
41#include "atacmds.h"
42#include "dev_interface.h"
43#include "sg_unaligned.h"
44
45#ifndef USE_CLOCK_MONOTONIC
46#ifdef __MINGW32__
47// If MinGW-w64 < 9.0.0 or Windows < 8, GetSystemTimeAsFileTime() is used for
48// std::chrono::high_resolution_clock. This provides only 1/64s (>15ms) resolution.
49// CLOCK_MONOTONIC uses QueryPerformanceCounter() which provides <1us resolution.
50#define USE_CLOCK_MONOTONIC 1
51#else
52// Use std::chrono::high_resolution_clock.
53#include <chrono>
54#define USE_CLOCK_MONOTONIC 0
55#endif
56#endif // USE_CLOCK_MONOTONIC
57
58const char * utility_cpp_cvsid = "$Id: utility.cpp 5592 2024-01-17 16:17:10Z chrfranke $"
60
61const char * packet_types[] = {
62 "Direct-access (disk)",
63 "Sequential-access (tape)",
64 "Printer",
65 "Processor",
66 "Write-once (optical disk)",
67 "CD/DVD",
68 "Scanner",
69 "Optical memory (optical disk)",
70 "Medium changer",
71 "Communications",
72 "Graphic arts pre-press (10)",
73 "Graphic arts pre-press (11)",
74 "Array controller",
75 "Enclosure services",
76 "Reduced block command (simplified disk)",
77 "Optical card reader/writer"
78};
79
80// BUILD_INFO can be provided by package maintainers
81#ifndef BUILD_INFO
82#define BUILD_INFO "(local build)"
83#endif
84
85// Make version information string
86// lines: 1: version only, 2: version+copyright, >=3: full information
87std::string format_version_info(const char * prog_name, int lines /* = 2 */)
88{
89 std::string info = strprintf(
90 "%s "
91#ifndef SMARTMONTOOLS_RELEASE_DATE
92 "pre-"
93#endif
94 PACKAGE_VERSION " "
95#ifdef SMARTMONTOOLS_SVN_REV
96 SMARTMONTOOLS_SVN_DATE " r" SMARTMONTOOLS_SVN_REV
97#else
98 "(build date " __DATE__ ")" // checkout without expansion of Id keywords
99#endif
100 " [%s] " BUILD_INFO "\n",
101 prog_name, smi()->get_os_version_str().c_str()
102 );
103 if (lines <= 1)
104 return info;
105
106 info += "Copyright (C) 2002-24, Bruce Allen, Christian Franke, www.smartmontools.org\n";
107 if (lines == 2)
108 return info;
109
110 info += "\n";
111 info += prog_name;
112 info += " comes with ABSOLUTELY NO WARRANTY. This is free\n"
113 "software, and you are welcome to redistribute it under\n"
114 "the terms of the GNU General Public License; either\n"
115 "version 2, or (at your option) any later version.\n"
116 "See https://www.gnu.org for further details.\n"
117 "\n"
118#ifndef SMARTMONTOOLS_RELEASE_DATE
119 "smartmontools pre-release " PACKAGE_VERSION "\n"
120#else
121 "smartmontools release " PACKAGE_VERSION
122 " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"
123#endif
124#ifdef SMARTMONTOOLS_SVN_REV
125 "smartmontools SVN rev " SMARTMONTOOLS_SVN_REV
126#ifdef SMARTMONTOOLS_GIT_HASH
127 " (git " SMARTMONTOOLS_GIT_HASH ")"
128#endif
129 " dated " SMARTMONTOOLS_SVN_DATE " at " SMARTMONTOOLS_SVN_TIME "\n"
130#else
131 "smartmontools SVN rev is unknown\n"
132#endif
133 "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"
134 "smartmontools build with: "
135
136#define N2S_(s) #s
137#define N2S(s) N2S_(s)
138#if __cplusplus == 202002
139 "C++20"
140#elif __cplusplus == 201703
141 "C++17"
142#elif __cplusplus == 201402
143 "C++14"
144#elif __cplusplus == 201103
145 "C++11"
146#else
147 "C++(" N2S(__cplusplus) ")"
148#endif
149#undef N2S
150#undef N2S_
151
152#if defined(__GNUC__) && defined(__VERSION__) // works also with CLang
153 ", GCC " __VERSION__
154#endif
155#ifdef __MINGW64_VERSION_STR
156 ", MinGW-w64 " __MINGW64_VERSION_STR
157#endif
158 "\n"
159 "smartmontools configure arguments:"
160#ifdef SOURCE_DATE_EPOCH
161 " [hidden in reproducible builds]\n"
162 "reproducible build SOURCE_DATE_EPOCH: "
163#endif
164 ;
165#ifdef SOURCE_DATE_EPOCH
166 char ts[32]; struct tm tmbuf;
167 strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", time_to_tm_local(&tmbuf, SOURCE_DATE_EPOCH));
168 info += strprintf("%u (%s)", (unsigned)SOURCE_DATE_EPOCH, ts);
169#else
170 info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
171 SMARTMONTOOLS_CONFIGURE_ARGS : " [no arguments given]");
172#endif
173 info += '\n';
174
175 return info;
176}
177
178// Solaris only: Get site-default timezone. This is called from
179// UpdateTimezone() when TZ environment variable is unset at startup.
180#if defined (__SVR4) && defined (__sun)
181static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
182
183static char *ReadSiteDefaultTimezone(){
184 FILE *fp;
185 char buf[512], *tz;
186 int n;
187
188 tz = NULL;
189 fp = fopen(TIMEZONE_FILE, "r");
190 if(fp == NULL) return NULL;
191 while(fgets(buf, sizeof(buf), fp)) {
192 if (strncmp(buf, "TZ=", 3)) // searches last "TZ=" line
193 continue;
194 n = strlen(buf) - 1;
195 if (buf[n] == '\n') buf[n] = 0;
196 if (tz) free(tz);
197 tz = strdup(buf);
198 }
199 fclose(fp);
200 return tz;
201}
202#endif
203
204// Make sure that this executable is aware if the user has changed the
205// time-zone since the last time we polled devices. The canonical
206// example is a user who starts smartd on a laptop, then flies across
207// time-zones with a laptop, and then changes the timezone, WITHOUT
208// restarting smartd. This is a work-around for a bug in
209// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
210// thanks to Ian Redfern for posting a workaround.
211
212// Please refer to the smartd manual page, in the section labeled LOG
213// TIMESTAMP TIMEZONE.
215#if __GLIBC__
216 if (!getenv("TZ")) {
217 putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
218 tzset();
219 putenv((char *)"TZ");
220 tzset();
221 }
222#elif _WIN32
223 const char * tz = getenv("TZ");
224 if (!tz) {
225 putenv("TZ=GMT");
226 tzset();
227 putenv("TZ="); // empty value removes TZ, putenv("TZ") does nothing
228 tzset();
229 }
230 else {
231 static const regular_expression tzrex("[A-Z]{3}[-+]?[0-9]+([A-Z]{3,4})?");
232 // tzset() from MSVCRT only supports the above basic syntax of TZ.
233 // Otherwise the timezone settings are set to bogus values.
234 // Unset TZ and revert to system default timezone in these cases.
235 if (!tzrex.full_match(tz)) {
236 putenv("TZ=");
237 tzset();
238 }
239 }
240#elif defined (__SVR4) && defined (__sun)
241 // In Solaris, putenv("TZ=") sets null string and invalid timezone.
242 // putenv("TZ") does nothing. With invalid TZ, tzset() do as if
243 // TZ=GMT. With TZ unset, /etc/TIMEZONE will be read only _once_ at
244 // first tzset() call. Conclusion: Unlike glibc, dynamic
245 // configuration of timezone can be done only by changing actual
246 // value of TZ environment value.
247 enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE };
248 static enum tzstate state = NOT_CALLED_YET;
249
250 static struct stat prev_stat;
251 static char *prev_tz;
252 struct stat curr_stat;
253 char *curr_tz;
254
255 if(state == NOT_CALLED_YET) {
256 if(getenv("TZ")) {
257 state = USER_TIMEZONE; // use supplied timezone
258 } else {
259 state = TRACK_TIMEZONE;
260 if(stat(TIMEZONE_FILE, &prev_stat)) {
261 state = USER_TIMEZONE; // no TZ, no timezone file; use GMT forever
262 } else {
263 prev_tz = ReadSiteDefaultTimezone(); // track timezone file change
264 if(prev_tz) putenv(prev_tz);
265 }
266 }
267 tzset();
268 } else if(state == TRACK_TIMEZONE) {
269 if(stat(TIMEZONE_FILE, &curr_stat) == 0
270 && (curr_stat.st_ctime != prev_stat.st_ctime
271 || curr_stat.st_mtime != prev_stat.st_mtime)) {
272 // timezone file changed
273 curr_tz = ReadSiteDefaultTimezone();
274 if(curr_tz) {
275 putenv(curr_tz);
276 if(prev_tz) free(prev_tz);
277 prev_tz = curr_tz; prev_stat = curr_stat;
278 }
279 }
280 tzset();
281 }
282#endif
283 // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED. PLEASE TRY TO
284 // KEEP THEM INDEPENDENT.
285 return;
286}
287
288#ifdef _WIN32
289// Fix strings in tzname[] to avoid long names with non-ascii characters.
290// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
291// national language timezone names returned by GetTimezoneInformation().
292static char * fixtzname(char * dest, int destsize, const char * src)
293{
294 int i = 0, j = 0;
295 while (src[i] && j < destsize-1) {
296 int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src;
297 if (i2 > i+1)
298 i = i2; // Ignore multibyte chars
299 else {
300 if ('A' <= src[i] && src[i] <= 'Z')
301 dest[j++] = src[i]; // "Pacific Standard Time" => "PST"
302 i++;
303 }
304 }
305 if (j < 2)
306 j = 0;
307 dest[j] = 0;
308 return dest;
309}
310#endif // _WIN32
311
312// This value follows the peripheral device type value as defined in
313// SCSI Primary Commands, ANSI INCITS 301:1997. It is also used in
314// the ATA standard for packet devices to define the device type.
315const char *packetdevicetype(int type){
316 if (type<0x10)
317 return packet_types[type];
318
319 if (type<0x20)
320 return "Reserved";
321
322 return "Unknown";
323}
324
325// Convert time to broken-down local time, throw on error.
326struct tm * time_to_tm_local(struct tm * tp, time_t t)
327{
328#ifndef _WIN32
329 // POSIX, C23 - missing in MSVRCT, C++ and older C.
330 if (!localtime_r(&t, tp))
331 throw std::runtime_error("localtime_r() failed");
332#elif 0 // defined(__STDC_LIB_EXT1__)
333 // C11 - requires #define __STDC_WANT_LIB_EXT1__ before <time.h>.
334 // Missing in POSIX and C++, MSVCRT variant differs.
335 if (!localtime_s(&t, tp))
336 throw std::runtime_error("localtime_s() failed");
337#else
338 // MSVCRT - 64-bit variant avoids conflict with the above C11 variant.
339 __time64_t t64 = t;
340 if (_localtime64_s(tp, &t64))
341 throw std::runtime_error("_localtime64_s() failed");
342#endif
343 return tp;
344}
345
346// Utility function prints date and time and timezone into a character
347// buffer of length 64. All the fuss is needed to get the right
348// timezone info (sigh).
349void dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval)
350{
352
353 // Get the time structure. We need this to determine if we are in
354 // daylight savings time or not.
355 struct tm tmbuf, * tmval = time_to_tm_local(&tmbuf, tval);
356
357 // Convert to an ASCII string, put in datebuffer.
358 // Same as: strftime(datebuffer, sizeof(datebuffer), "%a %b %e %H:%M:%S %Y\n"),
359 // but always in "C" locale.
360 char datebuffer[32];
361 STATIC_ASSERT(sizeof(datebuffer) >= 26); // assumed by asctime_r()
362#ifndef _WIN32
363 // POSIX (missing in MSVRCT, C and C++)
364 if (!asctime_r(tmval, datebuffer))
365 throw std::runtime_error("asctime_r() failed");
366#else
367 // MSVCRT, C11 (missing in POSIX)
368 if (asctime_s(datebuffer, sizeof(datebuffer), tmval))
369 throw std::runtime_error("asctime_s() failed");
370#endif
371
372 // Remove newline
373 int lenm1 = strlen(datebuffer) - 1;
374 datebuffer[lenm1>=0?lenm1:0]='\0';
375
376#if defined(_WIN32) && defined(_MSC_VER)
377 // tzname is missing in MSVC14
378 #define tzname _tzname
379#endif
380
381 // correct timezone name
382 const char * timezonename;
383 if (tmval->tm_isdst==0)
384 // standard time zone
385 timezonename=tzname[0];
386 else if (tmval->tm_isdst>0)
387 // daylight savings in effect
388 timezonename=tzname[1];
389 else
390 // unable to determine if daylight savings in effect
391 timezonename="";
392
393#ifdef _WIN32
394 // Fix long non-ascii timezone names
395 // cppcheck-suppress variableScope
396 char tzfixbuf[6+1] = "";
397 if (!getenv("TZ"))
398 timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
399#endif
400
401 // Finally put the information into the buffer as needed.
402 snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename);
403
404 return;
405}
406
407// A replacement for perror() that sends output to our choice of
408// printing. If errno not set then just print message.
409void syserror(const char *message){
410
411 if (errno) {
412 // Get the correct system error message:
413 const char *errormessage=strerror(errno);
414
415 // Check that caller has handed a sensible string, and provide
416 // appropriate output. See perror(3) man page to understand better.
417 if (message && *message)
418 pout("%s: %s\n",message, errormessage);
419 else
420 pout("%s\n",errormessage);
421 }
422 else if (message && *message)
423 pout("%s\n",message);
424
425 return;
426}
427
428// Check regular expression for non-portable features.
429//
430// POSIX extended regular expressions interpret unmatched ')' ordinary:
431// "The close-parenthesis shall be considered special in this context
432// only if matched with a preceding open-parenthesis."
433//
434// GNU libc and BSD libc support unmatched ')', Cygwin reports an error.
435//
436// POSIX extended regular expressions do not define empty subexpressions:
437// "A vertical-line appearing first or last in an ERE, or immediately following
438// a vertical-line or a left-parenthesis, or immediately preceding a
439// right-parenthesis, produces undefined results."
440//
441// GNU libc and Cygwin support empty subexpressions, BSD libc reports an error.
442//
443static const char * check_regex(const char * pattern)
444{
445 int level = 0;
446 char c;
447
448 for (int i = 0; (c = pattern[i]); i++) {
449 // Skip "\x"
450 if (c == '\\') {
451 if (!pattern[++i])
452 break;
453 continue;
454 }
455
456 // Skip "[...]"
457 if (c == '[') {
458 if (pattern[++i] == '^')
459 i++;
460 if (!pattern[i++])
461 break;
462 while ((c = pattern[i]) && c != ']')
463 i++;
464 if (!c)
465 break;
466 continue;
467 }
468
469 // Check "(...)" nesting
470 if (c == '(')
471 level++;
472 else if (c == ')' && --level < 0)
473 return "Unmatched ')'";
474
475 // Check for leading/trailing '|' or "||", "|)", "|$", "(|", "^|"
476 char c1;
477 if ( (c == '|' && ( i == 0 || !(c1 = pattern[i+1])
478 || c1 == '|' || c1 == ')' || c1 == '$'))
479 || ((c == '(' || c == '^') && pattern[i+1] == '|') )
480 return "Empty '|' subexpression";
481 }
482
483 return (const char *)0;
484}
485
486// Wrapper class for POSIX regex(3) or std::regex
487
488#ifndef WITH_CXX11_REGEX
489
491{
492 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
493}
494
496{
497 free_buf();
498}
499
501: m_pattern(x.m_pattern),
502 m_errmsg(x.m_errmsg)
503{
504 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
505 copy_buf(x);
506}
507
509{
511 m_errmsg = x.m_errmsg;
512 free_buf();
513 copy_buf(x);
514 return *this;
515}
516
518{
519 if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) {
520 regfree(&m_regex_buf);
521 memset(&m_regex_buf, 0, sizeof(m_regex_buf));
522 }
523}
524
526{
527 if (nonempty(&x.m_regex_buf, sizeof(x.m_regex_buf))) {
528 // There is no POSIX compiled-regex-copy command.
529 if (!compile())
530 throw std::runtime_error(strprintf(
531 "Unable to recompile regular expression \"%s\": %s",
532 m_pattern.c_str(), m_errmsg.c_str()));
533 }
534}
535
536#endif // !WITH_CXX11_REGEX
537
539: m_pattern(pattern)
540{
541 if (!compile())
542 throw std::runtime_error(strprintf(
543 "error in regular expression \"%s\": %s",
544 m_pattern.c_str(), m_errmsg.c_str()));
545}
546
547bool regular_expression::compile(const char * pattern)
548{
549#ifndef WITH_CXX11_REGEX
550 free_buf();
551#endif
552 m_pattern = pattern;
553 return compile();
554}
555
557{
558#ifdef WITH_CXX11_REGEX
559 try {
560 m_regex.assign(m_pattern, std::regex_constants::extended);
561 }
562 catch (std::regex_error & ex) {
563 m_errmsg = ex.what();
564 return false;
565 }
566
567#else
568 int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), REG_EXTENDED);
569 if (errcode) {
570 char errmsg[512];
571 regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
572 m_errmsg = errmsg;
573 free_buf();
574 return false;
575 }
576#endif
577
578 const char * errmsg = check_regex(m_pattern.c_str());
579 if (errmsg) {
580 m_errmsg = errmsg;
581#ifdef WITH_CXX11_REGEX
582 m_regex = std::regex();
583#else
584 free_buf();
585#endif
586 return false;
587 }
588
589 m_errmsg.clear();
590 return true;
591}
592
593bool regular_expression::full_match(const char * str) const
594{
595#ifdef WITH_CXX11_REGEX
596 return std::regex_match(str, m_regex);
597#else
598 match_range range;
599 return ( !regexec(&m_regex_buf, str, 1, &range, 0)
600 && range.rm_so == 0 && range.rm_eo == (int)strlen(str));
601#endif
602}
603
604bool regular_expression::execute(const char * str, unsigned nmatch, match_range * pmatch) const
605{
606#ifdef WITH_CXX11_REGEX
607 std::cmatch m;
608 if (!std::regex_search(str, m, m_regex))
609 return false;
610 unsigned sz = m.size();
611 for (unsigned i = 0; i < nmatch; i++) {
612 if (i < sz && *m[i].first) {
613 pmatch[i].rm_so = m[i].first - str;
614 pmatch[i].rm_eo = m[i].second - str;
615 }
616 else
617 pmatch[i].rm_so = pmatch[i].rm_eo = -1;
618 }
619 return true;
620
621#else
622 return !regexec(&m_regex_buf, str, nmatch, pmatch, 0);
623#endif
624}
625
626// Splits an argument to the -t option that is assumed to be of the form
627// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
628// are allowed). The first long long int is assigned to *start and the second
629// to *stop. Returns zero if successful and non-zero otherwise.
630int split_selective_arg(char *s, uint64_t *start,
631 uint64_t *stop, int *mode)
632{
633 char *tailptr;
634 if (!(s = strchr(s, ',')))
635 return 1;
636 bool add = false;
637 if (!isdigit((int)(*++s))) {
638 *start = *stop = 0;
639 if (!strncmp(s, "redo", 4))
640 *mode = SEL_REDO;
641 else if (!strncmp(s, "next", 4))
642 *mode = SEL_NEXT;
643 else if (!strncmp(s, "cont", 4))
644 *mode = SEL_CONT;
645 else
646 return 1;
647 s += 4;
648 if (!*s)
649 return 0;
650 if (*s != '+')
651 return 1;
652 }
653 else {
654 *mode = SEL_RANGE;
655 errno = 0;
656 // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
657 // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
658 *start = strtoull(s, &tailptr, 0);
659 s = tailptr;
660 add = (*s == '+');
661 if (!(!errno && (add || *s == '-')))
662 return 1;
663 if (!strcmp(s, "-max")) {
664 *stop = ~(uint64_t)0; // replaced by max LBA later
665 return 0;
666 }
667 }
668
669 errno = 0;
670 *stop = strtoull(s+1, &tailptr, 0);
671 if (errno || *tailptr != '\0')
672 return 1;
673 if (add) {
674 if (*stop > 0)
675 (*stop)--;
676 *stop += *start; // -t select,N+M => -t select,N,(N+M-1)
677 }
678 return 0;
679}
680
681// Returns true if region of memory contains non-zero entries
682bool nonempty(const void * data, int size)
683{
684 for (int i = 0; i < size; i++)
685 if (((const unsigned char *)data)[i])
686 return true;
687 return false;
688}
689
690// Copy not null terminated char array to null terminated string.
691// Replace non-ascii characters. Remove leading and trailing blanks.
692const char * format_char_array(char * str, int strsize, const char * chr, int chrsize)
693{
694 int b = 0;
695 while (b < chrsize && chr[b] == ' ')
696 b++;
697 int n = 0;
698 while (b+n < chrsize && chr[b+n])
699 n++;
700 while (n > 0 && chr[b+n-1] == ' ')
701 n--;
702
703 if (n >= strsize)
704 n = strsize-1;
705
706 for (int i = 0; i < n; i++) {
707 char c = chr[b+i];
708 str[i] = (' ' <= c && c <= '~' ? c : '?');
709 }
710
711 str[n] = 0;
712 return str;
713}
714
715// Format integer with thousands separator
716const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
717 const char * thousands_sep /* = 0 */)
718{
719 if (!thousands_sep) {
720 thousands_sep = ",";
721#ifdef HAVE_LOCALE_H
722 setlocale(LC_ALL, "");
723 const struct lconv * currentlocale = localeconv();
724 if (*(currentlocale->thousands_sep))
725 thousands_sep = currentlocale->thousands_sep;
726#endif
727 }
728
729 char num[64];
730 snprintf(num, sizeof(num), "%" PRIu64, val);
731 int numlen = strlen(num);
732
733 int i = 0, j = 0;
734 do
735 str[j++] = num[i++];
736 while (i < numlen && (numlen - i) % 3 != 0 && j < strsize-1);
737 str[j] = 0;
738
739 while (i < numlen && j < strsize-1) {
740 j += snprintf(str+j, strsize-j, "%s%.3s", thousands_sep, num+i);
741 i += 3;
742 }
743
744 return str;
745}
746
747// Format capacity with SI prefixes
748const char * format_capacity(char * str, int strsize, uint64_t val,
749 const char * decimal_point /* = 0 */)
750{
751 if (!decimal_point) {
752 decimal_point = ".";
753#ifdef HAVE_LOCALE_H
754 setlocale(LC_ALL, "");
755 const struct lconv * currentlocale = localeconv();
756 if (*(currentlocale->decimal_point))
757 decimal_point = currentlocale->decimal_point;
758#endif
759 }
760
761 const unsigned factor = 1000; // 1024 for KiB,MiB,...
762 static const char prefixes[] = " KMGTP";
763
764 // Find d with val in [d, d*factor)
765 unsigned i = 0;
766 uint64_t d = 1;
767 for (uint64_t d2 = d * factor; val >= d2; d2 *= factor) {
768 d = d2;
769 if (++i >= sizeof(prefixes)-2)
770 break;
771 }
772
773 // Print 3 digits
774 uint64_t n = val / d;
775 if (i == 0)
776 snprintf(str, strsize, "%u B", (unsigned)n);
777 else if (n >= 100) // "123 xB"
778 snprintf(str, strsize, "%" PRIu64 " %cB", n, prefixes[i]);
779 else if (n >= 10) // "12.3 xB"
780 snprintf(str, strsize, "%" PRIu64 "%s%u %cB", n, decimal_point,
781 (unsigned)(((val % d) * 10) / d), prefixes[i]);
782 else // "1.23 xB"
783 snprintf(str, strsize, "%" PRIu64 "%s%02u %cB", n, decimal_point,
784 (unsigned)(((val % d) * 100) / d), prefixes[i]);
785
786 return str;
787}
788
789// return (v)sprintf() formatted std::string
791std::string vstrprintf(const char * fmt, va_list ap)
792{
793 char buf[512];
794 vsnprintf(buf, sizeof(buf), fmt, ap);
795 buf[sizeof(buf)-1] = 0;
796 return buf;
797}
798
799std::string strprintf(const char * fmt, ...)
800{
801 va_list ap; va_start(ap, fmt);
802 std::string str = vstrprintf(fmt, ap);
803 va_end(ap);
804 return str;
805}
806
807#if defined(HAVE___INT128)
808// Compiler supports '__int128'.
809
810// Recursive 128-bit to string conversion function
811static int snprint_uint128(char * str, int strsize, unsigned __int128 value)
812{
813 if (strsize <= 0)
814 return -1;
815
816 if (value <= 0xffffffffffffffffULL) {
817 // Print leading digits as 64-bit value
818 return snprintf(str, (size_t)strsize, "%" PRIu64, (uint64_t)value);
819 }
820 else {
821 // Recurse to print leading digits
822 const uint64_t e19 = 10000000000000000000ULL; // 2^63 < 10^19 < 2^64
823 int len1 = snprint_uint128(str, strsize, value / e19);
824 if (len1 < 0)
825 return -1;
826
827 // Print 19 digits remainder as 64-bit value
828 int len2 = snprintf(str + (len1 < strsize ? len1 : strsize - 1),
829 (size_t)(len1 < strsize ? strsize - len1 : 1),
830 "%019" PRIu64, (uint64_t)(value % e19) );
831 if (len2 < 0)
832 return -1;
833 return len1 + len2;
834 }
835}
836
837// Convert 128-bit unsigned integer provided as two 64-bit halves to a string.
838const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
839{
840 snprint_uint128(str, strsize, ((unsigned __int128)value_hi << 64) | value_lo);
841 return str;
842}
843
844#elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF)
845// Compiler and *printf() support 'long double' which is wider than 'double'.
846
847const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
848{
849 snprintf(str, strsize, "%.0Lf", value_hi * (0xffffffffffffffffULL + 1.0L) + value_lo);
850 return str;
851}
852
853#else // !HAVE_LONG_DOUBLE_WIDER_PRINTF
854// No '__int128' or 'long double' support, use 'double'.
855
856const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
857{
858 snprintf(str, strsize, "%.0f", value_hi * (0xffffffffffffffffULL + 1.0) + value_lo);
859 return str;
860}
861
862#endif // HAVE___INT128
863
864// Get microseconds since some unspecified starting point.
865long long get_timer_usec()
866{
867#if USE_CLOCK_MONOTONIC
868 struct timespec ts;
869 if (clock_gettime(CLOCK_MONOTONIC, &ts))
870 return -1;
871 return ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
872#else
873 return std::chrono::duration_cast<std::chrono::microseconds>(
874 std::chrono::high_resolution_clock::now().time_since_epoch()
875 ).count();
876#endif
877}
878
879// Runtime check of byte ordering, throws on error.
880static void check_endianness()
881{
882 const union {
883 // Force compile error if int type is not 32bit.
884 unsigned char c[sizeof(int) == 4 ? 8 : -1];
885 uint64_t i;
886 } x = {{1, 2, 3, 4, 5, 6, 7, 8}};
887 const uint64_t le = 0x0807060504030201ULL;
888 const uint64_t be = 0x0102030405060708ULL;
889
890 if (!( x.i == (isbigendian() ? be : le)
891 && sg_get_unaligned_le16(x.c) == (uint16_t)le
892 && sg_get_unaligned_be16(x.c+6) == (uint16_t)be
893 && sg_get_unaligned_le32(x.c) == (uint32_t)le
894 && sg_get_unaligned_be32(x.c+4) == (uint32_t)be
895 && sg_get_unaligned_le64(x.c) == le
896 && sg_get_unaligned_be64(x.c) == be ))
897 throw std::logic_error("CPU endianness does not match compile time test");
898}
899
900#if defined(__GNUC__) && (__GNUC__ >= 7)
901
902// G++ 7+: Assume sane implementation and avoid -Wformat-truncation warning
903static void check_snprintf() {}
904
905#else
906
907static void check_snprintf()
908{
909 char buf[] = "ABCDEFGHI";
910 int n1 = snprintf(buf, 8, "123456789");
911 int n2 = snprintf(buf, 0, "X");
912 if (!(!strcmp(buf, "1234567") && n1 == 9 && n2 == 1))
913 throw std::logic_error("Function snprintf() does not conform to C99");
914}
915
916#endif
917
918// Runtime check of ./configure result, throws on error.
920{
923}
@ SEL_REDO
Definition: atacmds.h:607
@ SEL_RANGE
Definition: atacmds.h:606
@ SEL_NEXT
Definition: atacmds.h:608
@ SEL_CONT
Definition: atacmds.h:609
Wrapper class for POSIX regex(3) or std::regex Supports copy & assignment and is compatible with STL ...
Definition: utility.h:222
regex_t m_regex_buf
Definition: utility.h:275
bool full_match(const char *str) const
Return true if full string matches pattern.
Definition: utility.cpp:593
std::string m_pattern
Definition: utility.h:269
regular_expression & operator=(const regular_expression &x)
Definition: utility.cpp:508
regmatch_t match_range
Definition: utility.h:262
std::string m_errmsg
Definition: utility.h:270
void copy_buf(const regular_expression &x)
Definition: utility.cpp:525
bool execute(const char *str, unsigned nmatch, match_range *pmatch) const
Return true if substring matches pattern, fill match_range array.
Definition: utility.cpp:604
smart_interface * smi()
Global access to the (usually singleton) smart_interface.
u8 b[12]
Definition: megaraid.h:17
ptr_t buffer
Definition: megaraid.h:3
u16 s[6]
Definition: megaraid.h:18
ptr_t data
Definition: megaraid.h:15
u32 size
Definition: megaraid.h:0
static uint64_t sg_get_unaligned_le64(const void *p)
Definition: sg_unaligned.h:303
static uint32_t sg_get_unaligned_le32(const void *p)
Definition: sg_unaligned.h:297
static uint64_t sg_get_unaligned_be64(const void *p)
Definition: sg_unaligned.h:267
static uint16_t sg_get_unaligned_be16(const void *p)
Definition: sg_unaligned.h:256
static uint16_t sg_get_unaligned_le16(const void *p)
Definition: sg_unaligned.h:292
static uint32_t sg_get_unaligned_be32(const void *p)
Definition: sg_unaligned.h:261
const char const char * fmt
Definition: smartctl.cpp:1324
const char const char va_list ap
Definition: smartctl.cpp:1325
vsnprintf(buf, sizeof(buf), fmt, ap)
void pout(const char *fmt,...)
Definition: smartd.cpp:1338
#define STATIC_ASSERT(x)
Definition: static_assert.h:24
void FixGlibcTimeZoneBug()
Definition: utility.cpp:214
const char * format_char_array(char *str, int strsize, const char *chr, int chrsize)
Definition: utility.cpp:692
std::string format_version_info(const char *prog_name, int lines)
Definition: utility.cpp:87
void dateandtimezoneepoch(char(&buffer)[DATEANDEPOCHLEN], time_t tval)
Definition: utility.cpp:349
long long get_timer_usec()
Get microseconds since some unspecified starting point.
Definition: utility.cpp:865
const char * packet_types[]
Definition: utility.cpp:61
const char * format_capacity(char *str, int strsize, uint64_t val, const char *decimal_point)
Definition: utility.cpp:748
const char * format_with_thousands_sep(char *str, int strsize, uint64_t val, const char *thousands_sep)
Definition: utility.cpp:716
void syserror(const char *message)
Definition: utility.cpp:409
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:799
#define N2S(s)
const char * utility_cpp_cvsid
Definition: utility.cpp:58
static const char * check_regex(const char *pattern)
Definition: utility.cpp:443
bool nonempty(const void *data, int size)
Definition: utility.cpp:682
static void check_endianness()
Definition: utility.cpp:880
int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode)
Definition: utility.cpp:630
const char * uint128_hilo_to_str(char *str, int strsize, uint64_t value_hi, uint64_t value_lo)
Definition: utility.cpp:856
void check_config()
Definition: utility.cpp:919
const char * packetdevicetype(int type)
Definition: utility.cpp:315
#define BUILD_INFO
Definition: utility.cpp:82
struct tm * time_to_tm_local(struct tm *tp, time_t t)
Definition: utility.cpp:326
static void check_snprintf()
Definition: utility.cpp:907
std::string std::string vstrprintf(const char *fmt, va_list ap)
#define UTILITY_H_CVSID
Definition: utility.h:16
#define DATEANDEPOCHLEN
Definition: utility.h:64
bool isbigendian()
Definition: utility.h:82
std::string strprintf(const char *fmt,...) __attribute_format_printf(1
bool nonempty(const void *data, int size)
Definition: utility.cpp:682
#define __attribute_format_printf(x, y)
Definition: utility.h:34