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