LCOV - code coverage report
Current view: top level - cmdline - support.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 812 966 84.1 %
Date: 2026-03-15 15:58:19 Functions: 84 85 98.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "support.h"
      21             : 
      22             : /****************************************************************************/
      23             : /* lock */
      24             : 
      25             : /**
      26             :  * Locks used externally.
      27             :  */
      28             : #if HAVE_THREAD
      29             : static thread_mutex_t msg_lock;
      30             : static thread_mutex_t memory_lock;
      31             : static thread_mutex_t random_lock;
      32             : #endif
      33             : 
      34    22797379 : void lock_msg(void)
      35             : {
      36             : #if HAVE_THREAD
      37    22797379 :         thread_mutex_lock(&msg_lock);
      38             : #endif
      39    22797379 : }
      40             : 
      41    22797379 : void unlock_msg(void)
      42             : {
      43             : #if HAVE_THREAD
      44    22797379 :         thread_mutex_unlock(&msg_lock);
      45             : #endif
      46    22797379 : }
      47             : 
      48    21571343 : void lock_memory(void)
      49             : {
      50             : #if HAVE_THREAD
      51    21571343 :         thread_mutex_lock(&memory_lock);
      52             : #endif
      53    21571343 : }
      54             : 
      55    21571343 : void unlock_memory(void)
      56             : {
      57             : #if HAVE_THREAD
      58    21571343 :         thread_mutex_unlock(&memory_lock);
      59             : #endif
      60    21571343 : }
      61             : 
      62          14 : void lock_random(void)
      63             : {
      64             : #if HAVE_THREAD
      65          14 :         thread_mutex_lock(&random_lock);
      66             : #endif
      67          14 : }
      68             : 
      69          14 : void unlock_random(void)
      70             : {
      71             : #if HAVE_THREAD
      72          14 :         thread_mutex_unlock(&random_lock);
      73             : #endif
      74          14 : }
      75             : 
      76             : 
      77         325 : void lock_init(void)
      78             : {
      79             : #if HAVE_THREAD
      80             :         /* initialize the locks as first operation as log_fatal depends on them */
      81         325 :         thread_mutex_init(&msg_lock);
      82         325 :         thread_mutex_init(&memory_lock);
      83         325 :         thread_mutex_init(&random_lock);
      84             : #endif
      85         325 : }
      86             : 
      87         303 : void lock_done(void)
      88             : {
      89             : #if HAVE_THREAD
      90         303 :         thread_mutex_destroy(&msg_lock);
      91         303 :         thread_mutex_destroy(&memory_lock);
      92         303 :         thread_mutex_destroy(&random_lock);
      93             : #endif
      94         303 : }
      95             : 
      96             : /****************************************************************************/
      97             : /* random */
      98             : 
      99             : static uint64_t random_state = 1;
     100             : 
     101           1 : void random_seed(uint64_t seed)
     102             : {
     103           1 :         random_state = seed;
     104           1 : }
     105             : 
     106         297 : void random_reseed(void)
     107             : {
     108         297 :         random_state = os_tick();
     109         297 : }
     110             : 
     111           1 : unsigned char random_u8(void)
     112             : {
     113           1 :         return random_u64() & 0xFF;
     114             : }
     115             : 
     116             : /*
     117             :  * SplitMix64
     118             :  *
     119             :  * - Zero-safe: Any 64-bit seed is valid (even 0).
     120             :  * - Full period: Guaranteed to visit every 2^64 value.
     121             :  * - Logic: Uses a Weyl sequence + MurmurHash3-style finalizer.
     122             :  *
     123             :  * See: https://rosettacode.org/wiki/Pseudo-random_numbers/Splitmix64
     124             :  */
     125          14 : uint64_t random_u64(void)
     126             : {
     127             :         uint64_t z;
     128             : 
     129          14 :         lock_random();
     130             : 
     131          14 :         z = random_state += 0x9E3779B97F4A7C15ULL;
     132             : 
     133          14 :         unlock_random();
     134             : 
     135          14 :         z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
     136          14 :         z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
     137             : 
     138          14 :         return z ^ (z >> 31);
     139             : }
     140             : 
     141             : /****************************************************************************/
     142             : /* error */
     143             : 
     144     1941919 : int is_hw(int err)
     145             : {
     146             :         /* LCOV_EXCL_START */
     147             :         if (err == EIO)
     148             :                 return 1;
     149             : #ifdef EFSCORRUPTED
     150             :         if (err == EFSCORRUPTED) /* used by XFS inside the kernel - never expected in user mode as converted to EUCLEAN */
     151             :                 return 1;
     152             : #endif
     153             : #ifdef EUCLEAN
     154             :         if (err == EUCLEAN) /* used by XFS at user level instead of EFSCORRUPTED */
     155             :                 return 1;
     156             : #endif
     157             : #ifdef EBADCRC
     158             :         if (err == EBADCRC) /* used by BTRFS - never expected in user mode as converted to EIO */
     159             :                 return 1;
     160             : #endif
     161             : #ifdef EBADMSG
     162             :         if (err == EBADMSG) /* used by BTRFS - never expected in user mode as converted to EIO */
     163             :                 return 1;
     164             : #endif
     165             : #ifdef ENOMEDIUM
     166             :         if (err == ENOMEDIUM) /* medium removed */
     167             :                 return 1;
     168             : #endif
     169             : #ifdef EHWPOISON
     170             :         if (err == EHWPOISON) /* hardware memory error */
     171             :                 return 1;
     172             : #endif
     173             :         /* LCOV_EXCL_STOP */
     174     1941907 :         return 0;
     175             : }
     176             : 
     177           0 : void log_fatal_errno(int err, const char* name)
     178             : {
     179             :         /* LCOV_EXCL_START */
     180             :         if (err == EIO) {
     181             :                 log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
     182             : #ifdef EFSCORRUPTED
     183             :         } else if (err == EFSCORRUPTED) {
     184             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
     185             : #endif
     186             : #ifdef EUCLEAN
     187             :         } else if (err == EUCLEAN) {
     188             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
     189             : #endif
     190             : #ifdef EBADCRC
     191             :         } else if (err == EBADCRC) {
     192             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
     193             : #endif
     194             : #ifdef EBADMSG
     195             :         } else if (err == EBADMSG) {
     196             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
     197             : #endif
     198             : #ifdef ENOMEDIUM
     199             :         } else if (err == ENOMEDIUM) {
     200             :                 log_fatal(err, "DANGER! Unexpected medium removed in disk %s. It isn't possible to continue.\n", name);
     201             : #endif
     202             : #ifdef EHWPOISON
     203             :         } else if (err == EHWPOISON) {
     204             :                 log_fatal(err, "DANGER! Unexpected memory error in disk %s. It isn't possible to continue.\n", name);
     205             : #endif
     206             :         } else if (err == EACCES) {
     207             :                 log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
     208             :         } else if (err == ENOSPC) {
     209             :                 log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
     210             :         } else {
     211             :                 log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
     212             :         }
     213             :         /* LCOV_EXCL_STOP */
     214           0 : }
     215             : 
     216          27 : void log_error_errno(int err, const char* name)
     217             : {
     218             :         /* LCOV_EXCL_START */
     219             :         if (err == EIO) {
     220             :                 log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
     221             : #ifdef EFSCORRUPTED
     222             :         } else if (err == EFSCORRUPTED) {
     223             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
     224             : #endif
     225             : #ifdef EUCLEAN
     226             :         } else if (err == EUCLEAN) {
     227             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
     228             : #endif
     229             : #ifdef EBADCRC
     230             :         } else if (err == EBADCRC) {
     231             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
     232             : #endif
     233             : #ifdef EBADMSG
     234             :         } else if (err == EBADMSG) {
     235             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
     236             : #endif
     237             : #ifdef ENOMEDIUM
     238             :         } else if (err == ENOMEDIUM) {
     239             :                 log_fatal(err, "DANGER! Unexpected medium removed in disk %s.\n", name);
     240             : #endif
     241             : #ifdef EHWPOISON
     242             :         } else if (err == EHWPOISON) {
     243             :                 log_fatal(err, "DANGER! Unexpected memory error in disk %s.\n", name);
     244             : #endif
     245             :                 /* LCOV_EXCL_STOP */
     246          27 :         } else if (err == EACCES) {
     247          12 :                 log_error(err, "WARNING! Grant permission in the disk %s.\n", name);
     248          15 :         } else if (err == ENOSPC) {
     249           0 :                 log_error(err, "WARNING! Ensure there is free space on the disk %s.\n", name);
     250          15 :         } else if (err == ENOENT) {
     251          15 :                 log_error(err, "WARNING! You cannot modify files while running.\n");
     252             :         }
     253          27 : }
     254             : 
     255             : /****************************************************************************/
     256             : /* print */
     257             : 
     258             : int msg_level = 0;
     259             : FILE* stdlog = 0;
     260             : int msg_line_prev = 0; /**< Previous line width on stdout */
     261             : int msg_line_curr = 0; /**< Current line width on stdout */
     262             : 
     263             : /*
     264             :  * Note that in the following functions we always flush both
     265             :  * stdout and stderr, because we want to ensure that they mixes
     266             :  * well when redirected to files
     267             :  *
     268             :  * The buffering is similar at the "line buffered" one, that
     269             :  * is not available on Windows, so we emulate it in this way.
     270             :  *
     271             :  * For stdlog flushing is limited. To ensure flushing the
     272             :  * caller should use log_flush().
     273             :  */
     274             : 
     275       41573 : static void vmsg(FILE* out, const char* format, va_list ap)
     276             : {
     277       41573 :         char* dup = strdup_nofail(format);
     278       41573 :         int len = strlen(dup);
     279       41573 :         int written = 0;
     280       41573 :         char control = 0;
     281             : 
     282       41573 :         if (len > 0) {
     283       41573 :                 if (dup[len - 1] == '\n') {
     284       41563 :                         dup[len - 1] = 0;
     285       41563 :                         control = '\n';
     286          10 :                 } else if (dup[len - 1] == '\r') {
     287           0 :                         dup[len - 1] = 0;
     288           0 :                         control = '\r';
     289             :                 }
     290             :         }
     291             : 
     292       41573 :         if (dup[0]) {
     293       41467 :                 written = vfprintf(out, dup, ap);
     294             :         }
     295             : 
     296       41573 :         switch (control) {
     297          10 :         case 0 :
     298          10 :                 msg_line_curr += written;
     299          10 :                 break;
     300       41563 :         case '\n' :
     301       41563 :                 msg_line_curr += written;
     302             :                 /* writes spaces to overwrite any previous char */
     303       41563 :                 while (msg_line_curr < msg_line_prev) {
     304           0 :                         fprintf(out, " ");
     305           0 :                         --msg_line_prev;
     306             :                 }
     307       41563 :                 msg_line_prev = 0;
     308       41563 :                 msg_line_curr = 0;
     309       41563 :                 fprintf(out, "\n");
     310       41563 :                 break;
     311           0 :         case '\r' :
     312           0 :                 msg_line_curr += written;
     313             :                 /* writes spaces to overwrite any previous char */
     314           0 :                 while (msg_line_curr < msg_line_prev) {
     315           0 :                         fprintf(out, " ");
     316           0 :                         --msg_line_prev;
     317             :                 }
     318           0 :                 msg_line_prev = msg_line_curr;
     319           0 :                 msg_line_curr = 0;
     320           0 :                 fprintf(out, "\r");
     321             :         }
     322             : 
     323       41573 :         free(dup);
     324       41573 : }
     325             : 
     326         804 : void log_fatal(int err, const char* format, ...)
     327             : {
     328             :         va_list ap;
     329             : 
     330         804 :         lock_msg();
     331             : 
     332         804 :         if (stdlog) {
     333         106 :                 if (is_hw(err))
     334          12 :                         fprintf(stdlog, "msg:fatal_hardware: ");
     335             :                 else
     336          94 :                         fprintf(stdlog, "msg:fatal: ");
     337             : 
     338         106 :                 va_start(ap, format);
     339         106 :                 vfprintf(stdlog, format, ap);
     340         106 :                 va_end(ap);
     341             : 
     342         106 :                 fflush(stdlog);
     343             :         }
     344             : 
     345         804 :         va_start(ap, format);
     346         804 :         vmsg(stderr, format, ap);
     347         804 :         va_end(ap);
     348             : 
     349         804 :         fflush(stderr);
     350             : 
     351         804 :         unlock_msg();
     352         804 : }
     353             : 
     354      387160 : void log_error(int err, const char* format, ...)
     355             : {
     356             :         va_list ap;
     357             : 
     358      387160 :         lock_msg();
     359             : 
     360      387160 :         if (stdlog) {
     361      385321 :                 if (is_hw(err))
     362           0 :                         fprintf(stdlog, "msg:error_hardware: ");
     363             :                 else
     364      385321 :                         fprintf(stdlog, "msg:error: ");
     365             : 
     366      385321 :                 va_start(ap, format);
     367      385321 :                 vfprintf(stdlog, format, ap);
     368      385321 :                 va_end(ap);
     369             : 
     370      385321 :                 fflush(stdlog);
     371             :         } else {
     372        1839 :                 va_start(ap, format);
     373        1839 :                 vmsg(stderr, format, ap);
     374        1839 :                 va_end(ap);
     375             : 
     376        1839 :                 fflush(stderr);
     377             :         }
     378             : 
     379      387160 :         unlock_msg();
     380      387160 : }
     381             : 
     382        4882 : void log_expected(int err, const char* format, ...)
     383             : {
     384             :         va_list ap;
     385             : 
     386        4882 :         lock_msg();
     387             : 
     388        4882 :         if (stdlog) {
     389        4882 :                 if (is_hw(err))
     390           0 :                         fprintf(stdlog, "msg:error_hardware: "); /* we never expect an hardware error */
     391             :                 else
     392        4882 :                         fprintf(stdlog, "msg:expected: ");
     393             : 
     394        4882 :                 va_start(ap, format);
     395        4882 :                 vfprintf(stdlog, format, ap);
     396        4882 :                 va_end(ap);
     397             : 
     398        4882 :                 fflush(stdlog);
     399             :         }
     400             : 
     401        4882 :         unlock_msg();
     402        4882 : }
     403             : 
     404    21937077 : void log_tag(const char* format, ...)
     405             : {
     406             :         va_list ap;
     407             : 
     408    21937077 :         lock_msg();
     409             : 
     410    21937077 :         if (stdlog) {
     411     2691468 :                 va_start(ap, format);
     412     2691468 :                 vfprintf(stdlog, format, ap);
     413     2691468 :                 va_end(ap);
     414             : 
     415             :                 /* here we intentionally don't flush to make the output faster */
     416             :         }
     417             : 
     418    21937077 :         unlock_msg();
     419    21937077 : }
     420             : 
     421        3694 : void log_flush(void)
     422             : {
     423        3694 :         lock_msg();
     424             : 
     425        3694 :         if (stdlog)
     426         344 :                 fflush(stdlog);
     427        3694 :         fflush(stdout);
     428        3694 :         fflush(stderr);
     429             : 
     430        3694 :         unlock_msg();
     431        3694 : }
     432             : 
     433         799 : void msg_status(const char* format, ...)
     434             : {
     435             :         va_list ap;
     436             : 
     437         799 :         lock_msg();
     438             : 
     439         799 :         if (stdlog) {
     440         426 :                 fprintf(stdlog, "msg:status: ");
     441             : 
     442         426 :                 va_start(ap, format);
     443         426 :                 vfprintf(stdlog, format, ap);
     444         426 :                 va_end(ap);
     445             : 
     446         426 :                 fflush(stdlog);
     447             :         }
     448             : 
     449         799 :         if (msg_level >= MSG_STATUS) {
     450         799 :                 va_start(ap, format);
     451         799 :                 vmsg(stdout, format, ap);
     452         799 :                 va_end(ap);
     453             :         }
     454             : 
     455         799 :         unlock_msg();
     456         799 : }
     457             : 
     458      328246 : void msg_info(const char* format, ...)
     459             : {
     460             :         va_list ap;
     461             : 
     462      328246 :         lock_msg();
     463             : 
     464             :         /* don't output in stdlog as these messages */
     465             :         /* are always paired with a msg_tag() call */
     466             : 
     467      328246 :         if (msg_level >= MSG_INFO) {
     468       37855 :                 va_start(ap, format);
     469       37855 :                 vmsg(stdout, format, ap);
     470       37855 :                 va_end(ap);
     471             : 
     472       37855 :                 fflush(stdout);
     473             :         }
     474             : 
     475      328246 :         unlock_msg();
     476      328246 : }
     477             : 
     478        5224 : void msg_progress(const char* format, ...)
     479             : {
     480             :         va_list ap;
     481             : 
     482        5224 :         lock_msg();
     483             : 
     484        5224 :         if (stdlog) {
     485         935 :                 fprintf(stdlog, "msg:progress: ");
     486             : 
     487         935 :                 va_start(ap, format);
     488         935 :                 vfprintf(stdlog, format, ap);
     489         935 :                 va_end(ap);
     490             : 
     491         935 :                 fflush(stdlog);
     492             :         }
     493             : 
     494        5224 :         if (msg_level >= MSG_PROGRESS) {
     495         176 :                 va_start(ap, format);
     496         176 :                 vmsg(stdout, format, ap);
     497         176 :                 va_end(ap);
     498             : 
     499         176 :                 fflush(stdout);
     500             :         }
     501             : 
     502        5224 :         unlock_msg();
     503        5224 : }
     504             : 
     505        2215 : void msg_bar(const char* format, ...)
     506             : {
     507             :         va_list ap;
     508             : 
     509        2215 :         lock_msg();
     510             : 
     511             :         /* don't output in stdlog as these messages */
     512             :         /* are intended for screen only */
     513             :         /* also don't flush stdout as they are intended to be partial messages */
     514             : 
     515        2215 :         if (msg_level >= MSG_BAR) {
     516           0 :                 va_start(ap, format);
     517           0 :                 vmsg(stdout, format, ap);
     518           0 :                 va_end(ap);
     519             :         }
     520             : 
     521        2215 :         unlock_msg();
     522        2215 : }
     523             : 
     524      126669 : void msg_verbose(const char* format, ...)
     525             : {
     526             :         va_list ap;
     527             : 
     528      126669 :         lock_msg();
     529             : 
     530      126669 :         if (stdlog) {
     531      124409 :                 fprintf(stdlog, "msg:verbose: ");
     532             : 
     533      124409 :                 va_start(ap, format);
     534      124409 :                 vfprintf(stdlog, format, ap);
     535      124409 :                 va_end(ap);
     536             : 
     537      124409 :                 fflush(stdlog);
     538             :         }
     539             : 
     540      126669 :         if (msg_level >= MSG_VERBOSE) {
     541         100 :                 va_start(ap, format);
     542         100 :                 vmsg(stdout, format, ap);
     543         100 :                 va_end(ap);
     544             : 
     545         100 :                 fflush(stdout);
     546             :         }
     547             : 
     548      126669 :         unlock_msg();
     549      126669 : }
     550             : 
     551         609 : void msg_flush(void)
     552             : {
     553         609 :         lock_msg();
     554             : 
     555         609 :         fflush(stdout);
     556         609 :         fflush(stderr);
     557             : 
     558         609 :         unlock_msg();
     559         609 : }
     560             : 
     561          69 : void printc(char c, size_t pad)
     562             : {
     563         125 :         while (pad) {
     564             :                 /* group writes in long pieces */
     565             :                 char buf[128];
     566          56 :                 size_t len = pad;
     567             : 
     568          56 :                 if (len >= sizeof(buf))
     569           0 :                         len = sizeof(buf) - 1;
     570             : 
     571          56 :                 memset(buf, c, len);
     572          56 :                 buf[len] = 0;
     573             : 
     574          56 :                 fputs(buf, stdout);
     575             : 
     576          56 :                 pad -= len;
     577             :         }
     578          69 : }
     579             : 
     580          16 : void printr(const char* str, size_t pad)
     581             : {
     582             :         size_t len;
     583             : 
     584          16 :         len = strlen(str);
     585             : 
     586          16 :         if (len < pad)
     587          16 :                 printc(' ', pad - len);
     588             : 
     589          16 :         fputs(str, stdout);
     590          16 : }
     591             : 
     592         202 : void printl(const char* str, size_t pad)
     593             : {
     594             :         size_t len;
     595             : 
     596         202 :         fputs(str, stdout);
     597             : 
     598         202 :         len = strlen(str);
     599             : 
     600         202 :         if (len < pad)
     601          34 :                 printc(' ', pad - len);
     602         202 : }
     603             : 
     604          18 : void printp(double v, size_t pad)
     605             : {
     606             :         char buf[64];
     607          18 :         const char* s = "%";
     608             : 
     609          18 :         if (v > 0.1)
     610           7 :                 snprintf(buf, sizeof(buf), "%5.2f%s", v, s);
     611          11 :         else if (v > 0.01)
     612           4 :                 snprintf(buf, sizeof(buf), "%6.3f%s", v, s);
     613           7 :         else if (v > 0.001)
     614           2 :                 snprintf(buf, sizeof(buf), "%7.4f%s", v, s);
     615           5 :         else if (v > 0.0001)
     616           1 :                 snprintf(buf, sizeof(buf), "%8.5f%s", v, s);
     617           4 :         else if (v > 0.00001)
     618           2 :                 snprintf(buf, sizeof(buf), "%9.6f%s", v, s);
     619           2 :         else if (v > 0.000001)
     620           0 :                 snprintf(buf, sizeof(buf), "%10.7f%s", v, s);
     621           2 :         else if (v > 0.0000001)
     622           1 :                 snprintf(buf, sizeof(buf), "%11.8f%s", v, s);
     623           1 :         else if (v > 0.00000001)
     624           0 :                 snprintf(buf, sizeof(buf), "%12.9f%s", v, s);
     625           1 :         else if (v > 0.000000001)
     626           1 :                 snprintf(buf, sizeof(buf), "%13.10f%s", v, s);
     627           0 :         else if (v > 0.0000000001)
     628           0 :                 snprintf(buf, sizeof(buf), "%14.11f%s", v, s);
     629           0 :         else if (v > 0.00000000001)
     630           0 :                 snprintf(buf, sizeof(buf), "%15.12f%s", v, s);
     631           0 :         else if (v > 0.000000000001)
     632           0 :                 snprintf(buf, sizeof(buf), "%16.13f%s", v, s);
     633             :         else
     634           0 :                 snprintf(buf, sizeof(buf), "%17.14f%s", v, s);
     635          18 :         printl(buf, pad);
     636          18 : }
     637             : 
     638             : #define charcat(c) \
     639             :         do { \
     640             :                 if (p == end) { \
     641             :                         goto bail; \
     642             :                 } \
     643             :                 *p++ = (c); \
     644             :         } while (0)
     645             : 
     646     2790618 : const char* esc_tag(const char* str, char* buffer)
     647             : {
     648     2790618 :         char* begin = buffer;
     649     2790618 :         char* end = begin + ESC_MAX;
     650     2790618 :         char* p = begin;
     651             : 
     652             :         /* copy string with escaping */
     653    36594094 :         while (*str) {
     654    33803476 :                 char c = *str++;
     655             : 
     656    33803476 :                 switch (c) {
     657           3 :                 case '\n' :
     658           3 :                         charcat('\\');
     659           3 :                         charcat('n');
     660           3 :                         break;
     661           3 :                 case '\r' :
     662           3 :                         charcat('\\');
     663           3 :                         charcat('r');
     664           3 :                         break;
     665           4 :                 case ':' :
     666           4 :                         charcat('\\');
     667           4 :                         charcat('d');
     668           4 :                         break;
     669         143 :                 case '\\' :
     670         143 :                         charcat('\\');
     671         143 :                         charcat('\\');
     672         143 :                         break;
     673    33803323 :                 default :
     674    33803323 :                         charcat(c);
     675    33803323 :                         break;
     676             :                 }
     677             :         }
     678             : 
     679             :         /* put final 0 */
     680     2790618 :         if (p == end)
     681           0 :                 goto bail;
     682     2790618 :         *p = 0;
     683             : 
     684     2790618 :         return begin;
     685             : 
     686           0 : bail:
     687             :         /* LCOV_EXCL_START */
     688             :         log_fatal(EINTERNAL, "Escape for log is too long\n");
     689             :         exit(EXIT_FAILURE);
     690             :         /* LCOV_EXCL_STOP */
     691             : }
     692             : 
     693             : #ifdef _WIN32
     694             : static int needs_quote(const char* arg)
     695             : {
     696             :         while (*arg) {
     697             :                 char c = *arg;
     698             :                 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
     699             :                         c == '&' || c == '|' || c == '(' || c == ')' ||
     700             :                         c == '<' || c == '>' || c == '^' || c == '"' ||
     701             :                         c == '%' || c == '!' || c == '=' || c == ';' ||
     702             :                         (unsigned char)c < 32 || c == 127)
     703             :                         return 1;
     704             :                 ++arg;
     705             :         }
     706             : 
     707             :         return 0;
     708             : }
     709             : #endif
     710             : 
     711             : struct stream {
     712             :         const char* str;
     713             :         unsigned idx;
     714             :         const char** map;
     715             :         unsigned max;
     716             : };
     717             : 
     718     5474491 : static inline char ssget(struct stream* ss)
     719             : {
     720             :         /* get the next char */
     721     5474491 :         char c = *ss->str++;
     722             : 
     723             :         /* if one string is finished, go to the next */
     724     5510586 :         while (c == 0 && ss->idx + 1 < ss->max) {
     725       36095 :                 ss->str = ss->map[++ss->idx];
     726       36095 :                 c = *ss->str++;
     727             :         }
     728             : 
     729     5474491 :         return c;
     730             : }
     731             : 
     732             : #ifdef _WIN32
     733             : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     734             : {
     735             :         char* begin = buffer;
     736             :         char* end = begin + ESC_MAX;
     737             :         char* p = begin;
     738             :         struct stream ss;
     739             :         char c;
     740             :         unsigned i;
     741             :         int has_quote;
     742             : 
     743             :         has_quote = 0;
     744             :         for (i = 0; i < str_max; ++i) {
     745             :                 if (needs_quote(str_map[i]))
     746             :                         has_quote = 1;
     747             :         }
     748             : 
     749             :         ss.idx = 0;
     750             :         ss.str = str_map[0];
     751             :         ss.map = str_map;
     752             :         ss.max = str_max;
     753             : 
     754             :         if (!has_quote) {
     755             :                 c = ssget(&ss);
     756             :                 while (c) {
     757             :                         charcat(c);
     758             :                         c = ssget(&ss);
     759             :                 }
     760             :         } else {
     761             :                 /* starting quote */
     762             :                 charcat('"');
     763             : 
     764             :                 c = ssget(&ss);
     765             :                 while (c) {
     766             :                         int bl = 0;
     767             :                         while (c == '\\') {
     768             :                                 ++bl;
     769             :                                 c = ssget(&ss);
     770             :                         }
     771             : 
     772             :                         if (c == 0) {
     773             :                                 /* double backslashes before closing quote */
     774             :                                 bl = bl * 2;
     775             :                                 while (bl--)
     776             :                                         charcat('\\');
     777             :                                 break;
     778             :                         } else if (c == '"') {
     779             :                                 /* double backslashes + escape the quote */
     780             :                                 bl = bl * 2 + 1;
     781             :                                 while (bl--)
     782             :                                         charcat('\\');
     783             :                                 charcat('"');
     784             :                         } else {
     785             :                                 /* normal backslashes */
     786             :                                 while (bl--)
     787             :                                         charcat('\\');
     788             :                                 charcat(c);
     789             :                         }
     790             : 
     791             :                         c = ssget(&ss);
     792             :                 }
     793             : 
     794             :                 /* ending quote */
     795             :                 charcat('"');
     796             :         }
     797             : 
     798             :         /* put final 0 */
     799             :         charcat(0);
     800             : 
     801             :         return begin;
     802             : 
     803             : bail:
     804             :         /* LCOV_EXCL_START */
     805             :         log_fatal(EINTERNAL, "Escape for shell is too long\n");
     806             :         exit(EXIT_FAILURE);
     807             :         /* LCOV_EXCL_STOP */
     808             : }
     809             : #else
     810      381969 : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     811             : {
     812      381969 :         char* begin = buffer;
     813      381969 :         char* end = begin + ESC_MAX;
     814      381969 :         char* p = begin;
     815             :         struct stream ss;
     816             :         char c;
     817             : 
     818      381969 :         ss.idx = 0;
     819      381969 :         ss.str = str_map[0];
     820      381969 :         ss.map = str_map;
     821      381969 :         ss.max = str_max;
     822             : 
     823      381969 :         c = ssget(&ss);
     824     5474491 :         while (c) {
     825     5092522 :                 switch (c) {
     826             :                 /* special chars that need to be quoted */
     827       84266 :                 case ' ' : /* space */
     828             :                 case '\t' : /* tab */
     829             :                 case '\n' : /* newline */
     830             :                 case '\r' : /* carriage return */
     831             :                 case '~' : /* home */
     832             :                 case '`' : /* command */
     833             :                 case '#' : /* comment */
     834             :                 case '$' : /* variable */
     835             :                 case '&' : /* background job */
     836             :                 case '*' : /* wildcard */
     837             :                 case '(' : /* shell */
     838             :                 case ')' : /* shell */
     839             :                 case '\\' : /* quote */
     840             :                 case '|' : /* pipe */
     841             :                 case '[' : /* wildcard */
     842             :                 case ']' : /* wildcard */
     843             :                 case '{' : /* code */
     844             :                 case '}' : /* code */
     845             :                 case ';' : /* separator */
     846             :                 case '\'' : /* quote */
     847             :                 case '"' : /* quote */
     848             :                 case '<' : /* redirect */
     849             :                 case '>' : /* redirect */
     850             :                 case '?' : /* wildcard */
     851             :                 case '=' : /* assignment */
     852             :                 case '!' : /* history expansion */
     853       84266 :                         charcat('\\');
     854       84266 :                         charcat(c);
     855       84266 :                         break;
     856     5008256 :                 default :
     857             :                         /* check for control characters */
     858     5008256 :                         if ((unsigned char)c < 32 || c == 127) {
     859           6 :                                 charcat('\\');
     860           6 :                                 charcat(c);
     861             :                         } else {
     862     5008250 :                                 charcat(c);
     863             :                         }
     864     5008256 :                         break;
     865             :                 }
     866             : 
     867     5092522 :                 c = ssget(&ss);
     868             :         }
     869             : 
     870             :         /* put final 0 */
     871      381969 :         charcat(0);
     872             : 
     873      381969 :         return begin;
     874             : 
     875           0 : bail:
     876             :         /* LCOV_EXCL_START */
     877             :         log_fatal(EINTERNAL, "Escape for shell is too long\n");
     878             :         exit(EXIT_FAILURE);
     879             :         /* LCOV_EXCL_STOP */
     880             : }
     881             : #endif
     882             : 
     883        4102 : char* strpolish(char* s)
     884             : {
     885        4102 :         char* i = s;
     886             : 
     887      207324 :         while (*i) {
     888      203222 :                 if (isspace(*i) || !isprint(*i))
     889       58814 :                         *i = ' ';
     890      203222 :                 ++i;
     891             :         }
     892             : 
     893        4102 :         return s;
     894             : }
     895             : 
     896        1759 : unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters)
     897             : {
     898        1759 :         unsigned mac = 0;
     899             : 
     900             :         /* skip initial delimiters */
     901        1759 :         str += strspn(str, delimiters);
     902             : 
     903        8951 :         while (*str != 0 || mac == split_max) {
     904             :                 /* start of the token */
     905        7192 :                 split_map[mac] = str;
     906        7192 :                 ++mac;
     907             : 
     908             :                 /* find the first delimiter or the end of the string */
     909        7192 :                 str += strcspn(str, delimiters);
     910             : 
     911             :                 /* put the final terminator if missing */
     912        7192 :                 if (*str != 0)
     913        5625 :                         *str++ = 0;
     914             : 
     915             :                 /* skip trailing delimiters */
     916        7192 :                 str += strspn(str, delimiters);
     917             :         }
     918             : 
     919        1759 :         return mac;
     920             : }
     921             : 
     922         181 : char* strtrim(char* str)
     923             : {
     924             :         char* begin;
     925             :         char* end;
     926             : 
     927         181 :         begin = str;
     928         298 :         while (begin[0] && isspace((unsigned char)begin[0]))
     929         117 :                 ++begin;
     930             : 
     931         181 :         end = begin + strlen(begin);
     932         793 :         while (end > begin && isspace((unsigned char)end[-1]))
     933         612 :                 --end;
     934             : 
     935         181 :         end[0] = 0;
     936             : 
     937         181 :         if (begin != end)
     938         181 :                 memmove(str, begin, end - begin + 1);
     939             : 
     940         181 :         return str;
     941             : }
     942             : 
     943           2 : char* strlwr(char* str)
     944             : {
     945           2 :         char* s = str;
     946             : 
     947          24 :         while (*s) {
     948          22 :                 *s = tolower((unsigned char)*s);
     949          22 :                 ++s;
     950             :         }
     951             : 
     952           2 :         return str;
     953             : }
     954             : 
     955          11 : char* worddigitstr(const char* haystack, const char* needle)
     956             : {
     957          11 :         size_t len = strlen(needle);
     958             :         const char* s;
     959             : 
     960          11 :         if (len == 0)
     961           1 :                 return NULL;
     962             : 
     963          11 :         for (s = haystack; (s = strstr(s, needle)) != NULL; ++s) {
     964             : 
     965             :                 /* left boundary */
     966           8 :                 if (s == haystack || isspace((unsigned char)s[-1]) || isdigit((unsigned char)s[-1])) {
     967             :                         /* right boundary */
     968           7 :                         if (s[len] == '\0' || isspace((unsigned char)s[len]) || isdigit((unsigned char)s[len])) {
     969           7 :                                 return (char*)s;
     970             :                         }
     971             :                 }
     972             :         }
     973             : 
     974           3 :         return NULL;
     975             : }
     976             : 
     977             : /****************************************************************************/
     978             : /* path */
     979             : 
     980     1987265 : void pathcpy(char* dst, size_t size, const char* src)
     981             : {
     982     1987265 :         size_t len = strlen(src);
     983             : 
     984     1987265 :         if (len + 1 > size) {
     985             :                 /* LCOV_EXCL_START */
     986             :                 log_fatal(EINTERNAL, "Path too long '%s'\n", src);
     987             :                 os_abort();
     988             :                 /* LCOV_EXCL_STOP */
     989             :         }
     990             : 
     991     1987265 :         memcpy(dst, src, len + 1);
     992     1987265 : }
     993             : 
     994         293 : void pathcat(char* dst, size_t size, const char* src)
     995             : {
     996         293 :         size_t dst_len = strlen(dst);
     997         293 :         size_t src_len = strlen(src);
     998             : 
     999         293 :         if (dst_len + src_len + 1 > size) {
    1000             :                 /* LCOV_EXCL_START */
    1001             :                 log_fatal(EINTERNAL, "Path too long '%s%s'\n", dst, src);
    1002             :                 os_abort();
    1003             :                 /* LCOV_EXCL_STOP */
    1004             :         }
    1005             : 
    1006         293 :         memcpy(dst + dst_len, src, src_len + 1);
    1007         293 : }
    1008             : 
    1009     3770428 : void pathcatl(char* dst, size_t dst_len, size_t size, const char* src)
    1010             : {
    1011     3770428 :         size_t src_len = strlen(src);
    1012             : 
    1013     3770428 :         if (dst_len + src_len + 1 > size) {
    1014             :                 /* LCOV_EXCL_START */
    1015             :                 log_fatal(EINTERNAL, "Path too long '%s%s'\n", dst, src);
    1016             :                 os_abort();
    1017             :                 /* LCOV_EXCL_STOP */
    1018             :         }
    1019             : 
    1020     3770428 :         memcpy(dst + dst_len, src, src_len + 1);
    1021     3770428 : }
    1022             : 
    1023         812 : void pathcatc(char* dst, size_t size, char c)
    1024             : {
    1025         812 :         size_t dst_len = strlen(dst);
    1026             : 
    1027         812 :         if (dst_len + 2 > size) {
    1028             :                 /* LCOV_EXCL_START */
    1029             :                 log_fatal(EINTERNAL, "Path too long '%s%c'\n", dst, c);
    1030             :                 os_abort();
    1031             :                 /* LCOV_EXCL_STOP */
    1032             :         }
    1033             : 
    1034         812 :         dst[dst_len] = c;
    1035         812 :         dst[dst_len + 1] = 0;
    1036         812 : }
    1037             : 
    1038       18842 : void pathimport(char* dst, size_t size, const char* src)
    1039             : {
    1040       18842 :         pathcpy(dst, size, src);
    1041             : 
    1042             : #ifdef _WIN32
    1043             :         /* convert the Windows dir separator '\' to C '/', */
    1044             :         /* and the Windows escaping char '^' to the fnmatch '\' */
    1045             :         while (*dst) {
    1046             :                 switch (*dst) {
    1047             :                 case '\\' :
    1048             :                         *dst = '/';
    1049             :                         break;
    1050             :                 case '^' :
    1051             :                         *dst = '\\';
    1052             :                         break;
    1053             :                 }
    1054             :                 ++dst;
    1055             :         }
    1056             : #endif
    1057       18842 : }
    1058             : 
    1059       12234 : void pathexport(char* dst, size_t size, const char* src)
    1060             : {
    1061       12234 :         pathcpy(dst, size, src);
    1062             : 
    1063             : #ifdef _WIN32
    1064             :         /* invert the import */
    1065             :         while (*dst) {
    1066             :                 switch (*dst) {
    1067             :                 case '/' :
    1068             :                         *dst = '\\';
    1069             :                         break;
    1070             :                 case '\\' :
    1071             :                         *dst = '^';
    1072             :                         break;
    1073             :                 }
    1074             :                 ++dst;
    1075             :         }
    1076             : #endif
    1077       12234 : }
    1078             : 
    1079    44227830 : void pathprint(char* dst, size_t size, const char* format, ...)
    1080             : {
    1081             :         size_t len;
    1082             :         va_list ap;
    1083             : 
    1084    44227830 :         va_start(ap, format);
    1085    44227830 :         len = vsnprintf(dst, size, format, ap);
    1086    44227830 :         va_end(ap);
    1087             : 
    1088    44227830 :         if (len >= size) {
    1089             :                 /* LCOV_EXCL_START */
    1090             :                 if (size > 0) {
    1091             :                         dst[size - 1] = 0;
    1092             :                         log_fatal(EINTERNAL, "Path too long '%s...'\n", dst);
    1093             :                 } else {
    1094             :                         log_fatal(EINTERNAL, "Path too long for empty size'\n");
    1095             :                 }
    1096             :                 os_abort();
    1097             :                 /* LCOV_EXCL_STOP */
    1098             :         }
    1099    44227830 : }
    1100             : 
    1101        8305 : void pathslash(char* dst, size_t size)
    1102             : {
    1103        8305 :         size_t len = strlen(dst);
    1104             : 
    1105        8305 :         if (len > 0 && dst[len - 1] != '/') {
    1106        6528 :                 if (len + 2 >= size) {
    1107             :                         /* LCOV_EXCL_START */
    1108             :                         log_fatal(EINTERNAL, "Path too long '%s/'\n", dst);
    1109             :                         os_abort();
    1110             :                         /* LCOV_EXCL_STOP */
    1111             :                 }
    1112             : 
    1113        6528 :                 dst[len] = '/';
    1114        6528 :                 dst[len + 1] = 0;
    1115             :         }
    1116        8305 : }
    1117             : 
    1118       58120 : void pathcut(char* dst)
    1119             : {
    1120       58120 :         char* slash = strrchr(dst, '/');
    1121             : 
    1122       58120 :         if (slash)
    1123       58120 :                 slash[1] = 0;
    1124             :         else
    1125           0 :                 dst[0] = 0;
    1126       58120 : }
    1127             : 
    1128    54665945 : int pathcmp(const char* a, const char* b)
    1129             : {
    1130             : #ifdef _WIN32
    1131             :         char ai[PATH_MAX];
    1132             :         char bi[PATH_MAX];
    1133             : 
    1134             :         /* import to convert \ to / */
    1135             :         pathimport(ai, sizeof(ai), a);
    1136             :         pathimport(bi, sizeof(bi), b);
    1137             : 
    1138             :         /* case insensitive compare in Windows */
    1139             :         return stricmp(ai, bi);
    1140             : #else
    1141    54665945 :         return strcmp(a, b);
    1142             : #endif
    1143             : }
    1144             : 
    1145      127913 : int path_is_root_of(const char* root, const char* path)
    1146             : {
    1147      307307 :         while (*root && *path) {
    1148             : #ifdef _WIN32
    1149             :                 /* case insensitive compare in Windows */
    1150             :                 char r = tolower((unsigned char)*root);
    1151             :                 char p = tolower((unsigned char)*path);
    1152             :                 if (r == '\\')
    1153             :                         r = '/';
    1154             :                 if (p == '\\')
    1155             :                         p = '/';
    1156             :                 if (r != p)
    1157             :                         return 0;
    1158             : #else
    1159      248113 :                 if (*root != *path)
    1160       68719 :                         return 0;
    1161             : #endif
    1162      179394 :                 ++root;
    1163      179394 :                 ++path;
    1164             :         }
    1165             : 
    1166       59194 :         return *root == 0 && *path != 0;
    1167             : }
    1168             : 
    1169             : /****************************************************************************/
    1170             : /* file-system */
    1171             : 
    1172      803523 : int mkancestor(const char* file)
    1173             : {
    1174             :         char dir[PATH_MAX];
    1175             :         struct stat st;
    1176             :         char* c;
    1177             : 
    1178      803523 :         pathcpy(dir, sizeof(dir), file);
    1179             : 
    1180      803523 :         c = strrchr(dir, '/');
    1181      803523 :         if (!c) {
    1182             :                 /* no ancestor */
    1183           0 :                 return 0;
    1184             :         }
    1185             : 
    1186             :         /* clear the file */
    1187      803523 :         *c = 0;
    1188             : 
    1189             :         /* if it's the root dir */
    1190      803523 :         if (*dir == 0) {
    1191             :                 /* nothing more to do */
    1192           0 :                 return 0;
    1193             :         }
    1194             : 
    1195             : #ifdef _WIN32
    1196             :         /* if it's a drive specification like "C:" */
    1197             :         if (isalpha(dir[0]) && dir[1] == ':' && dir[2] == 0) {
    1198             :                 /* nothing more to do */
    1199             :                 return 0;
    1200             :         }
    1201             : #endif
    1202             : 
    1203             :         /*
    1204             :          * Check if the dir already exists using lstat().
    1205             :          *
    1206             :          * Note that in Windows when dealing with read-only media
    1207             :          * you cannot try to create the directory, and expecting
    1208             :          * the EEXIST error because the call will fail with ERROR_WRITE_PROTECTED.
    1209             :          *
    1210             :          * Also in Windows it's better to use lstat() than stat() because it
    1211             :          * doesn't need to open the dir with CreateFile().
    1212             :          */
    1213      803523 :         if (lstat(dir, &st) == 0) {
    1214             :                 /* it already exists */
    1215      803438 :                 return 0;
    1216             :         }
    1217             : 
    1218             :         /* recursively create them all */
    1219          85 :         if (mkancestor(dir) != 0) {
    1220             :                 /* LCOV_EXCL_START */
    1221             :                 return -1;
    1222             :                 /* LCOV_EXCL_STOP */
    1223             :         }
    1224             : 
    1225             :         /* create it */
    1226          85 :         if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
    1227             :                 /* LCOV_EXCL_START */
    1228             :                 log_fatal(errno, "Error creating directory '%s'. %s.\n", dir, strerror(errno));
    1229             :                 return -1;
    1230             :                 /* LCOV_EXCL_STOP */
    1231             :         }
    1232             : 
    1233          85 :         return 0;
    1234             : }
    1235             : 
    1236      129224 : int fmtime(int f, int64_t mtime_sec, int mtime_nsec)
    1237             : {
    1238             : #if HAVE_FUTIMENS
    1239             :         struct timespec tv[2];
    1240             : #else
    1241             :         struct timeval tv[2];
    1242             : #endif
    1243             :         int ret;
    1244             : 
    1245             : #if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */
    1246      129224 :         tv[0].tv_sec = mtime_sec;
    1247      129224 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1248      129224 :                 tv[0].tv_nsec = mtime_nsec;
    1249             :         else
    1250           0 :                 tv[0].tv_nsec = 0;
    1251      129224 :         tv[1].tv_sec = tv[0].tv_sec;
    1252      129224 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1253             : 
    1254      129224 :         ret = futimens(f, tv);
    1255             : #elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */
    1256             :         tv[0].tv_sec = mtime_sec;
    1257             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1258             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1259             :         else
    1260             :                 tv[0].tv_usec = 0;
    1261             :         tv[1].tv_sec = tv[0].tv_sec;
    1262             :         tv[1].tv_usec = tv[0].tv_usec;
    1263             : 
    1264             :         ret = futimes(f, tv);
    1265             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1266             :         tv[0].tv_sec = mtime_sec;
    1267             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1268             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1269             :         else
    1270             :                 tv[0].tv_usec = 0;
    1271             :         tv[1].tv_sec = tv[0].tv_sec;
    1272             :         tv[1].tv_usec = tv[0].tv_usec;
    1273             : 
    1274             :         ret = futimesat(f, 0, tv);
    1275             : #else
    1276             : #error No function available to set file timestamps with sub-second precision
    1277             : #endif
    1278             : 
    1279      129224 :         return ret;
    1280             : }
    1281             : 
    1282       11645 : int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec)
    1283             : {
    1284             : #if HAVE_UTIMENSAT
    1285             :         struct timespec tv[2];
    1286             : #else
    1287             :         struct timeval tv[2];
    1288             : #endif
    1289             :         int ret;
    1290             : 
    1291             : #if HAVE_UTIMENSAT /* utimensat() is preferred because it gives nanosecond precision */
    1292       11645 :         tv[0].tv_sec = mtime_sec;
    1293       11645 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1294       11645 :                 tv[0].tv_nsec = mtime_nsec;
    1295             :         else
    1296           0 :                 tv[0].tv_nsec = 0;
    1297       11645 :         tv[1].tv_sec = tv[0].tv_sec;
    1298       11645 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1299             : 
    1300       11645 :         ret = utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW);
    1301             : #elif HAVE_LUTIMES /* fallback to lutimes() if nanosecond precision is not available */
    1302             :         tv[0].tv_sec = mtime_sec;
    1303             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1304             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1305             :         else
    1306             :                 tv[0].tv_usec = 0;
    1307             :         tv[1].tv_sec = tv[0].tv_sec;
    1308             :         tv[1].tv_usec = tv[0].tv_usec;
    1309             : 
    1310             :         ret = lutimes(path, tv);
    1311             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1312             :         tv[0].tv_sec = mtime_sec;
    1313             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1314             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1315             :         else
    1316             :                 tv[0].tv_usec = 0;
    1317             :         tv[1].tv_sec = tv[0].tv_sec;
    1318             :         tv[1].tv_usec = tv[0].tv_usec;
    1319             : 
    1320             :         ret = futimesat(AT_FDCWD, path, tv);
    1321             : #else
    1322             : #error No function available to set file timestamps with sub-second precision
    1323             : #endif
    1324             : 
    1325       11645 :         return ret;
    1326             : }
    1327             : 
    1328             : /****************************************************************************/
    1329             : /* advise */
    1330             : 
    1331     2576942 : void advise_init(struct advise_struct* advise, int mode)
    1332             : {
    1333     2576942 :         advise->mode = mode;
    1334     2576942 :         advise->dirty_begin = 0;
    1335     2576942 :         advise->dirty_end = 0;
    1336     2576942 : }
    1337             : 
    1338     2576942 : int advise_flags(struct advise_struct* advise)
    1339             : {
    1340     2576942 :         int flags = 0;
    1341             : 
    1342     2576942 :         if (advise->mode == ADVISE_SEQUENTIAL
    1343     2565600 :                 || advise->mode == ADVISE_FLUSH
    1344     2554258 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1345     2542916 :                 || advise->mode == ADVISE_DISCARD
    1346     2542916 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1347             :         )
    1348       45368 :                 flags |= O_SEQUENTIAL;
    1349             : 
    1350             : #if HAVE_DIRECT_IO
    1351     2576942 :         if (advise->mode == ADVISE_DIRECT)
    1352       11342 :                 flags |= O_DIRECT;
    1353             : #endif
    1354             : 
    1355     2576942 :         return flags;
    1356             : }
    1357             : 
    1358     2490451 : int advise_open(struct advise_struct* advise, int f)
    1359             : {
    1360             :         (void)advise;
    1361             :         (void)f;
    1362             : 
    1363             : #if HAVE_POSIX_FADVISE
    1364     2490451 :         if (advise->mode == ADVISE_SEQUENTIAL
    1365     2479109 :                 || advise->mode == ADVISE_FLUSH
    1366     2467767 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1367     2456425 :                 || advise->mode == ADVISE_DISCARD
    1368     2456425 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1369             :         ) {
    1370             :                 int ret;
    1371             : 
    1372             :                 /* advise noreuse access, this avoids to pollute the page cache */
    1373             :                 /* supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a */
    1374       45368 :                 ret = posix_fadvise(f, 0, 0, POSIX_FADV_NOREUSE);
    1375       45368 :                 if (ret == ENOSYS) {
    1376             :                         /* call is not supported */
    1377           0 :                         ret = 0;
    1378             :                 }
    1379       45368 :                 if (ret != 0) {
    1380             :                         /* LCOV_EXCL_START */
    1381             :                         errno = ret; /* posix_fadvise return the error code */
    1382             :                         return -1;
    1383             :                         /* LCOV_EXCL_STOP */
    1384             :                 }
    1385             : 
    1386             :                 /* advise sequential access, this doubles the read-ahead window size */
    1387       45368 :                 ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
    1388       45368 :                 if (ret == ENOSYS) {
    1389             :                         /* call is not supported, like in armhf, see posix_fadvise manpage */
    1390           0 :                         ret = 0;
    1391             :                 }
    1392       45368 :                 if (ret != 0) {
    1393             :                         /* LCOV_EXCL_START */
    1394             :                         errno = ret; /* posix_fadvise return the error code */
    1395             :                         return -1;
    1396             :                         /* LCOV_EXCL_STOP */
    1397             :                 }
    1398             :         }
    1399             : #endif
    1400             : 
    1401     2490451 :         return 0;
    1402             : }
    1403             : 
    1404      955744 : int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1405             : {
    1406             :         data_off_t flush_offset;
    1407             :         data_off_t flush_size;
    1408             :         data_off_t discard_offset;
    1409             :         data_off_t discard_size;
    1410             : 
    1411             :         (void)f;
    1412             :         (void)flush_offset;
    1413             :         (void)flush_size;
    1414             :         (void)discard_offset;
    1415             :         (void)discard_size;
    1416             : 
    1417      955744 :         flush_offset = 0;
    1418      955744 :         flush_size = 0;
    1419      955744 :         discard_offset = 0;
    1420      955744 :         discard_size = 0;
    1421             : 
    1422             :         /*
    1423             :          * Follow Linus recommendations about fast writes.
    1424             :          *
    1425             :          * Linus "Unexpected splice "always copy" behavior observed"
    1426             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988070
    1427             :          * ---
    1428             :          * I have had _very_ good experiences with even a rather trivial
    1429             :          * file writer that basically used (iirc) 8MB windows, and the logic was very
    1430             :          * trivial:
    1431             :          *
    1432             :          *  - before writing a new 8M window, do "start writeback"
    1433             :          *    (SYNC_FILE_RANGE_WRITE) on the previous window, and do
    1434             :          *    a wait (SYNC_FILE_RANGE_WAIT_AFTER) on the window before that.
    1435             :          *
    1436             :          * in fact, in its simplest form, you can do it like this (this is from my
    1437             :          * "overwrite disk images" program that I use on old disks):
    1438             :          *
    1439             :          * for (index = 0; index < max_index ;index++) {
    1440             :          *   if (write(fd, buffer, BUFSIZE) != BUFSIZE)
    1441             :          *     break;
    1442             :          *   // This won't block, but will start writeout asynchronously
    1443             :          *   sync_file_range(fd, index*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WRITE);
    1444             :          *   // This does a blocking write-and-wait on any old ranges
    1445             :          *   if (index)
    1446             :          *     sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1447             :          * }
    1448             :          *
    1449             :          * and even if you don't actually do a discard (maybe we should add a
    1450             :          * SYNC_FILE_RANGE_DISCARD bit, right now you'd need to do a separate
    1451             :          * fadvise(FADV_DONTNEED) to throw it out) the system behavior is pretty
    1452             :          * nice, because the heavy writer gets good IO performance _and_ leaves only
    1453             :          * easy-to-free pages around after itself.
    1454             :          * ---
    1455             :          *
    1456             :          * Linus "Unexpected splice "always copy" behavior observed"
    1457             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988176
    1458             :          * ---
    1459             :          * The behavior for dirty page writeback is _not_ well defined, and
    1460             :          * if you do POSIX_FADV_DONTNEED, I would suggest you do it as part of that
    1461             :          * writeback logic, ie you do it only on ranges that you have just waited on.
    1462             :          *
    1463             :          * IOW, in my example, you'd couple the
    1464             :          *
    1465             :          *   sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1466             :          *
    1467             :          * with a
    1468             :          *
    1469             :          *   posix_fadvise(fd, (index-1)*BUFSIZE, BUFSIZE, POSIX_FADV_DONTNEED);
    1470             :          *
    1471             :          * afterwards to throw out the pages that you just waited for.
    1472             :          * ---
    1473             :          */
    1474             : 
    1475      955744 :         switch (advise->mode) {
    1476        4687 :         case ADVISE_FLUSH :
    1477        4687 :                 flush_offset = offset;
    1478        4687 :                 flush_size = size;
    1479        4687 :                 break;
    1480           0 :         case ADVISE_DISCARD :
    1481           0 :                 discard_offset = offset;
    1482           0 :                 discard_size = size;
    1483           0 :                 break;
    1484        4687 :         case ADVISE_FLUSH_WINDOW :
    1485             :                 /* if the dirty range can be extended */
    1486        4687 :                 if (advise->dirty_end == offset) {
    1487             :                         /* extent the dirty range */
    1488        4687 :                         advise->dirty_end += size;
    1489             : 
    1490             :                         /* if we reached the window size */
    1491        4687 :                         if (advise->dirty_end - advise->dirty_begin >= ADVISE_WINDOW_SIZE) {
    1492             :                                 /* flush the window  */
    1493           0 :                                 flush_offset = advise->dirty_begin;
    1494           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1495             : 
    1496             :                                 /* remove it from the dirty range */
    1497           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1498             :                         }
    1499             :                 } else {
    1500             :                         /* otherwise flush the existing dirty */
    1501           0 :                         flush_offset = advise->dirty_begin;
    1502           0 :                         flush_size = advise->dirty_end - advise->dirty_begin;
    1503             : 
    1504             :                         /* and set the new range as dirty */
    1505           0 :                         advise->dirty_begin = offset;
    1506           0 :                         advise->dirty_end = offset + size;
    1507             :                 }
    1508        4687 :                 break;
    1509        4687 :         case ADVISE_DISCARD_WINDOW :
    1510             :                 /* if the dirty range can be extended */
    1511        4687 :                 if (advise->dirty_end == offset) {
    1512             :                         /* extent the dirty range */
    1513        4687 :                         advise->dirty_end += size;
    1514             : 
    1515             :                         /* if we reached the double window size */
    1516        4687 :                         if (advise->dirty_end - advise->dirty_begin >= 2 * ADVISE_WINDOW_SIZE) {
    1517             :                                 /* discard the first window */
    1518           0 :                                 discard_offset = advise->dirty_begin;
    1519           0 :                                 discard_size = ADVISE_WINDOW_SIZE;
    1520             : 
    1521             :                                 /* remove it from the dirty range */
    1522           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1523             : 
    1524             :                                 /* flush the second window */
    1525           0 :                                 flush_offset = advise->dirty_begin;
    1526           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1527             :                         }
    1528             :                 } else {
    1529             :                         /* otherwise discard the existing dirty */
    1530           0 :                         discard_offset = advise->dirty_begin;
    1531           0 :                         discard_size = advise->dirty_end - advise->dirty_begin;
    1532             : 
    1533             :                         /* and set the new range as dirty */
    1534           0 :                         advise->dirty_begin = offset;
    1535           0 :                         advise->dirty_end = offset + size;
    1536             :                 }
    1537        4687 :                 break;
    1538             :         }
    1539             : 
    1540             : #if HAVE_SYNC_FILE_RANGE
    1541      955744 :         if (flush_size != 0) {
    1542             :                 int ret;
    1543             : 
    1544             :                 /* start writing immediately */
    1545        4687 :                 ret = sync_file_range(f, flush_offset, flush_size, SYNC_FILE_RANGE_WRITE);
    1546        4687 :                 if (ret != 0) {
    1547             :                         /* LCOV_EXCL_START */
    1548             :                         return -1;
    1549             :                         /* LCOV_EXCL_STOP */
    1550             :                 }
    1551             :         }
    1552             : #endif
    1553             : 
    1554             : #if HAVE_SYNC_FILE_RANGE && HAVE_POSIX_FADVISE
    1555      955744 :         if (discard_size != 0) {
    1556             :                 int ret;
    1557             : 
    1558             :                 /* send the data to the disk and wait until it's written */
    1559           0 :                 ret = sync_file_range(f, discard_offset, discard_size, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
    1560           0 :                 if (ret != 0) {
    1561             :                         /* LCOV_EXCL_START */
    1562             :                         return -1;
    1563             :                         /* LCOV_EXCL_STOP */
    1564             :                 }
    1565             : 
    1566             :                 /* flush the data from the cache */
    1567           0 :                 ret = posix_fadvise(f, discard_offset, discard_size, POSIX_FADV_DONTNEED);
    1568             :                 /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
    1569           0 :                 if (ret != 0) {
    1570             :                         /* LCOV_EXCL_START */
    1571             :                         errno = ret; /* posix_fadvise return the error code */
    1572             :                         return -1;
    1573             :                         /* LCOV_EXCL_STOP */
    1574             :                 }
    1575             :         }
    1576             : #endif
    1577             : 
    1578      955744 :         return 0;
    1579             : }
    1580             : 
    1581     9119626 : int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1582             : {
    1583             :         (void)advise;
    1584             :         (void)f;
    1585             :         (void)offset;
    1586             :         (void)size;
    1587             : 
    1588             : #if HAVE_POSIX_FADVISE
    1589     9119626 :         if (advise->mode == ADVISE_DISCARD
    1590     9119626 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1591             :         ) {
    1592             :                 int ret;
    1593             : 
    1594             :                 /* flush the data from the cache */
    1595       27825 :                 ret = posix_fadvise(f, offset, size, POSIX_FADV_DONTNEED);
    1596             :                 /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
    1597       27825 :                 if (ret != 0) {
    1598             :                         /* LCOV_EXCL_START */
    1599             :                         errno = ret; /* posix_fadvise return the error code */
    1600             :                         return -1;
    1601             :                         /* LCOV_EXCL_STOP */
    1602             :                 }
    1603             :         }
    1604             : #endif
    1605             : 
    1606             :         /*
    1607             :          * Here we cannot call posix_fadvise(..., POSIX_FADV_WILLNEED) for the next block
    1608             :          * because it may be blocking.
    1609             :          *
    1610             :          * Ted Ts'o "posix_fadvise(POSIX_FADV_WILLNEED) waits before returning?"
    1611             :          * https://lkml.org/lkml/2010/12/6/122
    1612             :          * ---
    1613             :          * readahead and posix_fadvise(POSIX_FADV_WILLNEED) work exactly the same
    1614             :          * way, and in fact share mostly the same code path (see
    1615             :          * force_page_cache_readahead() in mm/readahead.c).
    1616             :          *
    1617             :          * They are asynchronous in that there is no guarantee the pages will be
    1618             :          * in the page cache by the time they return.  But at the same time, they
    1619             :          * are not guaranteed to be non-blocking.  That is, the work of doing the
    1620             :          * readahead does not take place in a kernel thread.  So if you try to
    1621             :          * request I/O than will fit in the request queue, the system call will
    1622             :          * block until some I/O is completed so that more I/O requested cam be
    1623             :          * loaded onto the request queue.
    1624             :          *
    1625             :          * The only way to fix this would be to either put the work on a kernel
    1626             :          * thread (i.e., some kind of workqueue) or in a userspace thread.  For
    1627             :          * ion programmer wondering what to do today, I'd suggest the
    1628             :          * latter since it will be more portable across various kernel versions.
    1629             :          *
    1630             :          * This does leave the question about whether we should change the kernel
    1631             :          * to allow readahead() and posix_fadvise(POSIX_FADV_WILLNEED) to be
    1632             :          * non-blocking and do this work in a workqueue (or via some kind of
    1633             :          * callback/continuation scheme).  My worry is just doing this if a user
    1634             :          * application does something crazy, like request gigabytes and gigabytes
    1635             :          * of readahead, and then repented of their craziness, there should be a
    1636             :          * way of cancelling the readahead request.  Today, the user can just
    1637             :          * kill the application.  But if we simply shove the work to a kernel
    1638             :          * thread, it becomes a lot harder to cancel the readahead request.  We'd
    1639             :          * have to invent a new API, and then have a way to know whether the user
    1640             :          * has access to kill a particular readahead request, etc.
    1641             :          * ---
    1642             :          */
    1643             : 
    1644     9119626 :         return 0;
    1645             : }
    1646             : 
    1647             : /****************************************************************************/
    1648             : /* memory */
    1649             : 
    1650             : /**
    1651             :  * Total amount of memory allocated.
    1652             :  */
    1653             : static size_t mcounter;
    1654             : 
    1655         504 : size_t malloc_counter_get(void)
    1656             : {
    1657             :         size_t ret;
    1658             : 
    1659         504 :         lock_memory();
    1660             : 
    1661         504 :         ret = mcounter;
    1662             : 
    1663         504 :         unlock_memory();
    1664             : 
    1665         504 :         return ret;
    1666             : }
    1667             : 
    1668    21570839 : void malloc_counter_inc(size_t inc)
    1669             : {
    1670    21570839 :         lock_memory();
    1671             : 
    1672    21570839 :         mcounter += inc;
    1673             : 
    1674    21570839 :         unlock_memory();
    1675    21570839 : }
    1676             : 
    1677             : /* LCOV_EXCL_START */
    1678             : static ssize_t malloc_print(int f, const char* str)
    1679             : {
    1680             :         ssize_t len = 0;
    1681             : 
    1682             :         while (str[len])
    1683             :                 ++len;
    1684             :         return write(f, str, len);
    1685             : }
    1686             : /* LCOV_EXCL_STOP */
    1687             : 
    1688             : /* LCOV_EXCL_START */
    1689             : static ssize_t malloc_printn(int f, size_t value)
    1690             : {
    1691             :         char buf[32];
    1692             :         int i;
    1693             : 
    1694             :         if (!value)
    1695             :                 return write(f, "0", 1);
    1696             : 
    1697             :         i = sizeof(buf);
    1698             :         while (value) {
    1699             :                 buf[--i] = (value % 10) + '0';
    1700             :                 value /= 10;
    1701             :         }
    1702             : 
    1703             :         return write(f, buf + i, sizeof(buf) - i);
    1704             : }
    1705             : /* LCOV_EXCL_STOP */
    1706             : 
    1707             : /* LCOV_EXCL_START */
    1708             : void malloc_fail(size_t size)
    1709             : {
    1710             :         /* don't use printf to avoid any possible extra allocation */
    1711             :         int f = 2; /* stderr */
    1712             : 
    1713             :         malloc_print(f, "Failed for Low Memory!\n");
    1714             :         malloc_print(f, "Allocating ");
    1715             :         malloc_printn(f, size);
    1716             :         malloc_print(f, " bytes.\n");
    1717             :         malloc_print(f, "Already allocated ");
    1718             :         malloc_printn(f, malloc_counter_get());
    1719             :         malloc_print(f, " bytes.\n");
    1720             :         if (sizeof(void*) == 4) {
    1721             :                 malloc_print(f, "You are currently using a 32 bits executable.\n");
    1722             :                 malloc_print(f, "If you have more than 4GB of memory, please upgrade to a 64 bits one.\n");
    1723             :         }
    1724             : }
    1725             : /* LCOV_EXCL_STOP */
    1726             : 
    1727    15278120 : void* malloc_nofail(size_t size)
    1728             : {
    1729    15278120 :         void* ptr = malloc(size);
    1730             : 
    1731    15278120 :         if (!ptr) {
    1732             :                 /* LCOV_EXCL_START */
    1733             :                 malloc_fail(size);
    1734             :                 exit(EXIT_FAILURE);
    1735             :                 /* LCOV_EXCL_STOP */
    1736             :         }
    1737             : 
    1738             : #ifndef CHECKER /* Don't preinitialize when running for valgrind */
    1739             :         /* Here we preinitialize the memory to ensure that the OS is really allocating it */
    1740             :         /* and not only reserving the addressable space. */
    1741             :         /* Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process. */
    1742             :         /* Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to */
    1743             :         /* get a real error from malloc() instead than a process killed. */
    1744             :         /* Note that calloc() doesn't have the same effect. */
    1745    15278120 :         memset(ptr, 0xA5, size);
    1746             : #endif
    1747             : 
    1748    15278120 :         malloc_counter_inc(size);
    1749             : 
    1750    15278120 :         return ptr;
    1751             : }
    1752             : 
    1753      156992 : void* calloc_nofail(size_t count, size_t size)
    1754             : {
    1755             :         void* ptr;
    1756             : 
    1757      156992 :         size *= count;
    1758             : 
    1759             :         /* see the note in malloc_nofail() of why we don't use calloc() */
    1760      156992 :         ptr = malloc(size);
    1761             : 
    1762      156992 :         if (!ptr) {
    1763             :                 /* LCOV_EXCL_START */
    1764             :                 malloc_fail(size);
    1765             :                 exit(EXIT_FAILURE);
    1766             :                 /* LCOV_EXCL_STOP */
    1767             :         }
    1768             : 
    1769      156992 :         memset(ptr, 0, size);
    1770             : 
    1771      156992 :         malloc_counter_inc(size);
    1772             : 
    1773      156992 :         return ptr;
    1774             : }
    1775             : 
    1776     6135727 : char* strdup_nofail(const char* str)
    1777             : {
    1778             :         size_t size;
    1779             :         char* ptr;
    1780             : 
    1781     6135727 :         size = strlen(str) + 1;
    1782             : 
    1783     6135727 :         ptr = malloc(size);
    1784             : 
    1785     6135727 :         if (!ptr) {
    1786             :                 /* LCOV_EXCL_START */
    1787             :                 malloc_fail(size);
    1788             :                 exit(EXIT_FAILURE);
    1789             :                 /* LCOV_EXCL_STOP */
    1790             :         }
    1791             : 
    1792     6135727 :         memcpy(ptr, str, size);
    1793             : 
    1794     6135727 :         malloc_counter_inc(size);
    1795             : 
    1796     6135727 :         return ptr;
    1797             : }
    1798             : 
    1799             : /****************************************************************************/
    1800             : /* smartctl */
    1801             : 
    1802             : /**
    1803             :  * Match a string with the specified pattern.
    1804             :  * Like sscanf() a space match any sequence of spaces.
    1805             :  * Return 0 if it matches.
    1806             :  */
    1807       31443 : static int smatch(const char* str, const char* pattern)
    1808             : {
    1809       36291 :         while (*pattern) {
    1810       36078 :                 if (isspace(*pattern)) {
    1811         459 :                         ++pattern;
    1812        1044 :                         while (isspace(*str))
    1813         585 :                                 ++str;
    1814       35619 :                 } else if (*pattern == *str) {
    1815        4389 :                         ++pattern;
    1816        4389 :                         ++str;
    1817             :                 } else
    1818       31230 :                         return -1;
    1819             :         }
    1820             : 
    1821         213 :         return 0;
    1822             : }
    1823             : 
    1824             : /*
    1825             :  * scomma()
    1826             :  *
    1827             :  * Matches a literal prefix (the second argument), then parses the following
    1828             :  * unsigned 64-bit integer, allowing comma thousand separators (U.S. style).
    1829             :  *
    1830             :  * Whitespace is flexible around the prefix and before the number.
    1831             :  * Commas are ignored during number parsing but basic validation is applied.
    1832             :  *
    1833             :  * Returns:
    1834             :  *   1 - successfully parsed a valid uint64_t
    1835             :  *   0 - prefix mismatch, no digits found, invalid format or overflow
    1836             :  */
    1837       40764 : int scomma(const char* str, const char* prefix, uint64_t* value)
    1838             : {
    1839       40764 :         const char* s = str;
    1840       40764 :         const char* f = prefix;
    1841             : 
    1842             :         /* match literal prefix, tolerating whitespace differences */
    1843       41518 :         while (*f) {
    1844             :                 /* skip whitespace in both strings */
    1845       41592 :                 while (isspace((unsigned char)*f))
    1846          74 :                         ++f;
    1847       41592 :                 while (isspace((unsigned char)*s))
    1848          74 :                         ++s;
    1849             : 
    1850       41518 :                 if (*f == 0)
    1851           0 :                         break;
    1852       41518 :                 if (*s != *f)
    1853       40764 :                         return 0; /* prefix mismatch */
    1854             : 
    1855         754 :                 ++s;
    1856         754 :                 ++f;
    1857             :         }
    1858             : 
    1859             :         /* skip whitespace before the number starts */
    1860           0 :         while (isspace((unsigned char)*s))
    1861           0 :                 ++s;
    1862             : 
    1863           0 :         uint64_t num = 0;
    1864           0 :         int digits_seen = 0;
    1865           0 :         int comma_seen = 0;
    1866             : 
    1867             :         /* parse digits, ignoring commas */
    1868           0 :         while (*s) {
    1869           0 :                 if (*s >= '0' && *s <= '9') {
    1870           0 :                         unsigned digit = (unsigned)(*s - '0');
    1871             : 
    1872             :                         /* check for uint64_t overflow before multiplying */
    1873           0 :                         if (num > (UINT64_MAX - digit) / 10ULL)
    1874           0 :                                 return 0;
    1875             : 
    1876           0 :                         num = num * 10ULL + digit;
    1877           0 :                         ++digits_seen;
    1878           0 :                         comma_seen = 0;
    1879           0 :                 } else if (*s == ',') {
    1880             :                         /* disallow leading commas */
    1881           0 :                         if (digits_seen == 0)
    1882           0 :                                 return 0;
    1883             : 
    1884             :                         /* if two consecutive commas, stop parsing */
    1885           0 :                         if (comma_seen)
    1886           0 :                                 break;
    1887             : 
    1888           0 :                         comma_seen = 1;
    1889             :                 } else {
    1890             :                         /* any other character ends the number */
    1891           0 :                         break;
    1892             :                 }
    1893             : 
    1894           0 :                 ++s;
    1895             :         }
    1896             : 
    1897             :         /* require at least one digit */
    1898           0 :         if (digits_seen == 0)
    1899           0 :                 return 0;
    1900             : 
    1901           0 :         *value = num;
    1902           0 :         return 1;
    1903             : }
    1904             : 
    1905          54 : int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* inter)
    1906             : {
    1907             :         unsigned i;
    1908             :         int inside;
    1909             :         struct smart_attr dummy_smart[SMART_COUNT];
    1910             :         uint64_t dummy_info[INFO_COUNT];
    1911             :         char dummy_serial[SMART_MAX];
    1912             :         char dummy_family[SMART_MAX];
    1913             :         char dummy_model[SMART_MAX];
    1914             :         char dummy_interface[SMART_MAX];
    1915             : 
    1916             :         /* dummy attributes */
    1917          54 :         if (!smart)
    1918           0 :                 smart = dummy_smart;
    1919          54 :         if (!info)
    1920           0 :                 info = dummy_info;
    1921          54 :         if (!serial)
    1922           0 :                 serial = dummy_serial;
    1923          54 :         if (!family)
    1924           0 :                 family = dummy_family;
    1925          54 :         if (!model)
    1926           0 :                 model = dummy_model;
    1927          54 :         if (!inter)
    1928           0 :                 inter = dummy_interface;
    1929             : 
    1930             :         /* preclear attributes */
    1931          54 :         *serial = 0;
    1932          54 :         *family = 0;
    1933          54 :         *model = 0;
    1934          54 :         *inter = 0;
    1935          54 :         memset(smart, 0, sizeof(struct smart_attr) * SMART_COUNT);
    1936       14094 :         for (i = 0; i < SMART_COUNT; ++i)
    1937       14040 :                 smart[i].raw = SMART_UNASSIGNED;
    1938         162 :         for (i = 0; i < INFO_COUNT; ++i)
    1939         108 :                 info[i] = SMART_UNASSIGNED;
    1940             : 
    1941             :         /* read the file */
    1942          54 :         inside = 0;
    1943        4033 :         while (1) {
    1944             :                 char buf[256];
    1945             :                 unsigned id;
    1946             :                 uint64_t raw;
    1947             :                 uint64_t norm;
    1948             :                 uint64_t worst;
    1949             :                 uint64_t thresh;
    1950             :                 char* s;
    1951             : 
    1952        4087 :                 s = fgets(buf, sizeof(buf), f);
    1953        4087 :                 if (s == 0)
    1954          54 :                         break;
    1955             : 
    1956             :                 /* remove extraneous chars */
    1957        4033 :                 s = strpolish(buf);
    1958             : 
    1959        4033 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    1960             : 
    1961             :                 /* skip initial spaces */
    1962        9604 :                 while (isspace(*s))
    1963        5571 :                         ++s;
    1964             : 
    1965        4033 :                 if (*s == 0) {
    1966         348 :                         inside = 0;
    1967             :                         /* common */
    1968        3685 :                 } else if (smatch(s, "Rotation Rate: Solid State") == 0) {
    1969           3 :                         info[INFO_ROTATION_RATE] = 0;
    1970        3682 :                 } else if (sscanf(s, "Rotation Rate: %" SCNu64, &info[INFO_ROTATION_RATE]) == 1) {
    1971        3643 :                 } else if (smatch(s, "User Capacity:") == 0) {
    1972          42 :                         char* begin = strchr(s, ':');
    1973          42 :                         char* end = strstr(s, "bytes");
    1974          42 :                         if (begin != 0 && end != 0 && begin < end) {
    1975             :                                 char* p;
    1976          42 :                                 info[INFO_SIZE] = 0;
    1977        1002 :                                 for (p = begin; p != end; ++p) {
    1978         960 :                                         if (isdigit(*p)) {
    1979         543 :                                                 info[INFO_SIZE] *= 10;
    1980         543 :                                                 info[INFO_SIZE] += *p - '0';
    1981             :                                         }
    1982             :                                 }
    1983             :                         }
    1984        3601 :                 } else if (sscanf(s, "Model Family: %63[^\n]", family) == 1) {
    1985          36 :                         strtrim(family);
    1986        3565 :                 } else if (sscanf(s, "Device Model: %63[^\n]", model) == 1) {
    1987          42 :                         strtrim(model);
    1988        3523 :                 } else if (sscanf(s, "Serial number: %63s", serial) == 1) {
    1989           0 :                         strtrim(serial);
    1990             : /* SCSI */
    1991             : /*
    1992             :    Vendor:               WD
    1993             :    Product:              WD4001FYYG-01SL3
    1994             :    Revision:             VR08
    1995             :    Compliance:           SPC-4
    1996             :    User Capacity:        4,000,787,030,016 bytes [4.00 TB]
    1997             :    Logical block size:   512 bytes
    1998             :    Rotation Rate:        7200 rpm
    1999             :    Form Factor:          3.5 inches
    2000             :    Logical Unit id:      0x50000c0f01f55dd0
    2001             :    Serial number:        WMC1F0D41KD5
    2002             :    Device type:          disk
    2003             :    Transport protocol:   SAS (SPL-3)
    2004             :    Local Time is:        Fri Jan 28 14:14:51 2022 CET
    2005             :    SMART support is:     Available - device has SMART capability.
    2006             :    SMART support is:     Enabled
    2007             :    Temperature Warning:  Enabled
    2008             : 
    2009             :    Current Drive Temperature:    38 C
    2010             :    Drive Trip Temperature:              65 C
    2011             : 
    2012             :    Manufactured in week 25 of year 2012
    2013             :    Specified cycle count over device lifetime:  10000
    2014             :    Accumulated start-stop cycles:  103
    2015             :    Specified load-unload count over device lifetime:  300000
    2016             :    Accumulated load-unload cycles:  103
    2017             :    Elements in grown defect list: 5
    2018             : 
    2019             :    Vendor (Seagate) cache information
    2020             :    Blocks sent to initiator = 569127595
    2021             :    Blocks received from initiator = 2633690060
    2022             :    Blocks read from cache and sent to initiator = 1881027254
    2023             :    Number of read and write commands whose size <= segment size = 181284019
    2024             :    Number of read and write commands whose size > segment size = 0
    2025             : 
    2026             :    Vendor (Seagate/Hitachi) factory information
    2027             :    number of hours powered up = 25024.80
    2028             :    number of minutes until next internal SMART test = 42
    2029             : 
    2030             :  */
    2031        3523 :                 } else if (sscanf(s, "Transport protocol: %63[^\n]", inter) == 1) {
    2032           0 :                         if (strcmp(inter, "Fibre channel (FCP-4)") == 0)
    2033           0 :                                 strcpy(inter, "Fibre");
    2034           0 :                         else if (strcmp(inter, "SSA") == 0)
    2035           0 :                                 strcpy(inter, "SSA");
    2036           0 :                         else if (strcmp(inter, "IEEE 1394 (SBP-3)") == 0)
    2037           0 :                                 strcpy(inter, "FireWire");
    2038           0 :                         else if (strcmp(inter, "RDMA (SRP)") == 0)
    2039           0 :                                 strcpy(inter, "SCSI");
    2040           0 :                         else if (strcmp(inter, "iSCSI") == 0)
    2041           0 :                                 strcpy(inter, "iSCSI");
    2042           0 :                         else if (strcmp(inter, "SAS (SPL-4)") == 0)
    2043           0 :                                 strcpy(inter, "SAS");
    2044           0 :                         else if (strcmp(inter, "ADT") == 0)
    2045           0 :                                 strcpy(inter, "SCSI");
    2046           0 :                         else if (strcmp(inter, "ATA (ACS-2)") == 0)
    2047           0 :                                 strcpy(inter, "SATA");
    2048           0 :                         else if (strcmp(inter, "UAS") == 0)
    2049           0 :                                 strcpy(inter, "USB");
    2050           0 :                         else if (strcmp(inter, "SOP") == 0)
    2051           0 :                                 strcpy(inter, "NVMe");
    2052           0 :                         else if (strcmp(inter, "PCIe") == 0)
    2053           0 :                                 strcpy(inter, "PCIe");
    2054             :                         else
    2055           0 :                                 strcpy(inter, "SCSI");
    2056             :                         /* map generic attributes to SMART attr */
    2057        3523 :                 } else if (sscanf(s, "Elements in grown defect list: %" SCNu64, &smart[SMART_REALLOCATED_SECTOR_COUNT].raw) == 1) {
    2058           0 :                         pathcpy(smart[SMART_REALLOCATED_SECTOR_COUNT].name, sizeof(smart[SMART_REALLOCATED_SECTOR_COUNT].name), "Elements_In_Grown_Defect_List");
    2059        3523 :                 } else if (sscanf(s, "Current Drive Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS].raw) == 1) {
    2060           0 :                         pathcpy(smart[SMART_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_TEMPERATURE_CELSIUS].name), "Current_Drive_Temperature");
    2061        3523 :                 } else if (sscanf(s, "Drive Trip Temperature: %" SCNu64, &smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].raw) == 1) {
    2062           0 :                         pathcpy(smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].name), "Drive_Trip_Temperature");
    2063        3523 :                 } else if (sscanf(s, "Accumulated start-stop cycles: %" SCNu64, &smart[SMART_START_STOP_COUNT].raw) == 1) {
    2064           0 :                         pathcpy(smart[SMART_START_STOP_COUNT].name, sizeof(smart[SMART_START_STOP_COUNT].name), "Accumulated_Start-Stop_Cycles");
    2065        3523 :                 } else if (sscanf(s, "Accumulated load-unload cycles: %" SCNu64, &smart[SMART_LOAD_CYCLE_COUNT].raw) == 1) {
    2066           0 :                         pathcpy(smart[SMART_LOAD_CYCLE_COUNT].name, sizeof(smart[SMART_LOAD_CYCLE_COUNT].name), "Accumulated_Load-Unload_Cycles");
    2067        3523 :                 } else if (sscanf(s, "  number of hours powered up = %" SCNu64, &smart[SMART_POWER_ON_HOURS].raw) == 1) { /* note "n" of "number" lower case */
    2068           0 :                         pathcpy(smart[SMART_POWER_ON_HOURS].name, sizeof(smart[SMART_POWER_ON_HOURS].name), "Number_Of_Hours_Powered_Up");
    2069             :                         /* special entries */
    2070        3523 :                 } else if (sscanf(s, "Non-medium error count: %" SCNu64, &raw) == 1) {
    2071           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2072             : /* ATA */
    2073             : /*
    2074             :    ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
    2075             :    1 Raw_Read_Error_Rate     0x002f   200   200   051    Pre-fail  Always       -       0
    2076             :    3 Spin_Up_Time            0x0027   172   169   021    Pre-fail  Always       -       8383
    2077             :    4 Start_Stop_Count        0x0032   100   100   000    Old_age   Always       -       24
    2078             :    5 Reallocated_Sector_Ct   0x0033   200   200   140    Pre-fail  Always       -       0
    2079             :    7 Seek_Error_Rate         0x002e   200   200   000    Old_age   Always       -       0
    2080             :    9 Power_On_Hours          0x0032   083   083   000    Old_age   Always       -       12471
    2081             :    10 Spin_Retry_Count        0x0032   100   253   000    Old_age   Always       -       0
    2082             :    11 Calibration_Retry_Count 0x0032   100   253   000    Old_age   Always       -       0
    2083             :    12 Power_Cycle_Count       0x0032   100   100   000    Old_age   Always       -       24
    2084             :    192 Power-Off_Retract_Count 0x0032   200   200   000    Old_age   Always       -       9
    2085             :    193 Load_Cycle_Count        0x0032   157   157   000    Old_age   Always       -       131565
    2086             :    194 Temperature_Celsius     0x0022   127   119   000    Old_age   Always       -       25
    2087             :    196 Reallocated_Event_Count 0x0032   200   200   000    Old_age   Always       -       0
    2088             :    197 Current_Pending_Sector  0x0032   200   200   000    Old_age   Always       -       0
    2089             :    198 Offline_Uncorrectable   0x0030   100   253   000    Old_age   Offline      -       0
    2090             :    199 UDMA_CRC_Error_Count    0x0032   200   200   000    Old_age   Always       -       0
    2091             :    200 Multi_Zone_Error_Rate   0x0008   200   200   000    Old_age   Offline      -       0
    2092             :  */
    2093        3523 :                 } else if (smatch(s, "ATA Version is:") == 0) {
    2094          42 :                         strcpy(inter, "ATA");
    2095        3481 :                 } else if (smatch(s, "SATA Version is:") == 0) {
    2096          42 :                         strcpy(inter, "SATA");
    2097        3439 :                 } else if (smatch(s, "Transport Type:   PCIe") == 0) {
    2098           0 :                         strcpy(inter, "PCIe");
    2099        3439 :                 } else if (smatch(s, "Transport Type:   Parallel") == 0) {
    2100           0 :                         strcpy(inter, "PATA");
    2101             :                         /* special entries */
    2102        3439 :                 } else if (smatch(s, "No Errors Logged") == 0) {
    2103          42 :                         smart[SMART_ERROR_PROTOCOL].raw = 0;
    2104        3397 :                 } else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) {
    2105           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2106             : /* NVME */
    2107             : /*
    2108             :    Critical Warning:                   0x00
    2109             :    Temperature:                        55 Celsius
    2110             :    Available Spare:                    100%
    2111             :    Available Spare Threshold:          10%
    2112             :    Percentage Used:                    8%
    2113             :    Data Units Read:                    213,006,510 [109 TB]
    2114             :    Data Units Written:                 549,370,112 [281 TB]
    2115             :    Host Read Commands:                 11,210,192,197
    2116             :    Host Write Commands:                20,687,602,229
    2117             :    Controller Busy Time:               14,055
    2118             :    Power Cycles:                       39
    2119             :    Power On Hours:                     4,204
    2120             :    Unsafe Shutdowns:                   9
    2121             :    Media and Data Integrity Errors:    0
    2122             :    Error Information Log Entries:      1,479,242
    2123             :    Warning  Comp. Temperature Time:    0
    2124             :    Critical Comp. Temperature Time:    0
    2125             :    Temperature Sensor 2:               75 Celsius
    2126             :    Thermal Temp. 1 Total Time:         58745
    2127             :  */
    2128        3397 :                 } else if (smatch(s, "NVMe Version:") == 0) {
    2129           0 :                         strcpy(inter, "NVMe");
    2130             :                         /* special entries */
    2131        3397 :                 } else if (scomma(s, "Media and Data Integrity Errors:", &raw) == 1) {
    2132           0 :                         smart[SMART_ERROR_MEDIUM].raw = raw;
    2133        3397 :                 } else if (scomma(s, "Error Information Log Entries:", &raw) == 1) {
    2134           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2135        3397 :                 } else if (sscanf(s, "Percentage Used: %" SCNu64 "%%", &raw) == 1) {
    2136           0 :                         smart[SMART_WEAR_LEVEL].raw = raw; /* take care that it can be greather than 100%, meaning that it's used over the expected lifetime */
    2137             :                         /* map generic attributes to SMART attr */
    2138        3397 :                 } else if (sscanf(s, "Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS].raw) == 1) {
    2139           0 :                         pathcpy(smart[SMART_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_TEMPERATURE_CELSIUS].name), "Temperature");
    2140        3397 :                 } else if (scomma(s, "Power On Hours:", &smart[SMART_POWER_ON_HOURS].raw) == 1) {
    2141           0 :                         pathcpy(smart[SMART_POWER_ON_HOURS].name, sizeof(smart[SMART_POWER_ON_HOURS].name), "Power_On_Hours");
    2142        3397 :                 } else if (scomma(s, "Power Cycles:", &smart[SMART_POWER_CYCLE_COUNT].raw) == 1) {
    2143           0 :                         pathcpy(smart[SMART_POWER_CYCLE_COUNT].name, sizeof(smart[SMART_POWER_CYCLE_COUNT].name), "Power_Cycles");
    2144             :                         /* map specific attributes to UNUSED SMART attr */
    2145        3397 :                 } else if (sscanf(s, "Critical Warning: %" SCNx64, &smart[SMART_NVME_CRITICAL_WARNING].raw) == 1) {
    2146           0 :                         pathcpy(smart[SMART_NVME_CRITICAL_WARNING].name, sizeof(smart[SMART_NVME_CRITICAL_WARNING].name), "Critical_Warning");
    2147        3397 :                 } else if (sscanf(s, "Available Spare: %" SCNu64 "%%", &smart[SMART_NVME_AVAILABLE_SPARE].raw) == 1) {
    2148           0 :                         pathcpy(smart[SMART_NVME_AVAILABLE_SPARE].name, sizeof(smart[SMART_NVME_AVAILABLE_SPARE].name), "Available_Spare");
    2149        3397 :                 } else if (scomma(s, "Data Units Read:", &smart[SMART_NVME_DATA_UNITS_READ].raw) == 1) {
    2150           0 :                         pathcpy(smart[SMART_NVME_DATA_UNITS_READ].name, sizeof(smart[SMART_NVME_DATA_UNITS_READ].name), "Data_Units_Read");
    2151        3397 :                 } else if (scomma(s, "Data Units Written:", &smart[SMART_NVME_DATA_UNITS_WRITTEN].raw) == 1) {
    2152           0 :                         pathcpy(smart[SMART_NVME_DATA_UNITS_WRITTEN].name, sizeof(smart[SMART_NVME_DATA_UNITS_WRITTEN].name), "Data_Units_Written");
    2153        3397 :                 } else if (scomma(s, "Host Read Commands:", &smart[SMART_NVME_HOST_READ_COMMANDS].raw) == 1) {
    2154           0 :                         pathcpy(smart[SMART_NVME_HOST_READ_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_READ_COMMANDS].name), "Host_Read_Commands");
    2155        3397 :                 } else if (scomma(s, "Host Write Commands:", &smart[SMART_NVME_HOST_WRITE_COMMANDS].raw) == 1) {
    2156           0 :                         pathcpy(smart[SMART_NVME_HOST_WRITE_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_WRITE_COMMANDS].name), "Host_Write_Commands");
    2157        3397 :                 } else if (scomma(s, "Controller Busy Time:", &smart[SMART_NVME_CONTROLLER_BUSY_TIME].raw) == 1) {
    2158           0 :                         pathcpy(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name, sizeof(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name), "Controller_Busy_Time");
    2159        3397 :                 } else if (scomma(s, "Unsafe Shutdowns:", &smart[SMART_NVME_UNSAFE_SHUTDOWNS].raw) == 1) {
    2160           0 :                         pathcpy(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name, sizeof(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name), "Unsafe_Shutdowns");
    2161             :                         /* smartctl doesn't print this with command, but %d */
    2162        3397 :                 } else if (scomma(s, "Warning  Comp. Temperature Time:", &smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].raw) == 1) {
    2163           0 :                         pathcpy(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name), "Warning_Comp_Temperature_Time");
    2164             :                         /* smartctl doesn't print this with command, but %d */
    2165        3397 :                 } else if (scomma(s, "Critical Comp. Temperature Time:", &smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].raw) == 1) {
    2166           0 :                         pathcpy(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name), "Critical_Comp_Temperature_Time");
    2167             :                         /* ATA Attributes table */
    2168        3397 :                 } else if (smatch(s, "ID#") == 0) {
    2169          42 :                         inside = 1;
    2170        3355 :                 } else if (inside) {
    2171         759 :                         char id_name[128] = { 0 };
    2172         759 :                         char type[64] = { 0 };
    2173         759 :                         char updated[64] = { 0 };
    2174         759 :                         char when_failed[64] = { 0 };
    2175             :                         uint64_t min, max, avg;
    2176         759 :                         int format_minmax = 0;
    2177         759 :                         int format_avg = 0;
    2178             :                         int flags;
    2179             : 
    2180             :                         /* 194 Temperature_Celsius     0x0002   240   240   000    Old_age   Always       -       27 (Min/Max 16/60) */
    2181         759 :                         if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64 " (Min/Max %" SCNu64 "/%" SCNu64 ")", &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw, &min, &max) == 11) {
    2182          36 :                                 format_minmax = 1;
    2183             :                                 /*  3 Spin_Up_Time            0x0007   149   149   024    Pre-fail  Always       -       442 (Average 441) */
    2184         723 :                         } else if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64 " (Average %" SCNu64 ")", &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw, &avg) == 10) {
    2185          30 :                                 format_avg = 1;
    2186         693 :                         } else if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64, &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw) == 9) {
    2187             :                         } else {
    2188           0 :                                 log_fatal(EEXTERNAL, "Invalid smartctl line '%s'.\n", s);
    2189           0 :                                 return -1;
    2190             :                         }
    2191             : 
    2192         759 :                         if (id >= 256) {
    2193             :                                 /* LCOV_EXCL_START */
    2194             :                                 log_fatal(EEXTERNAL, "Invalid SMART id '%u'.\n", id);
    2195             :                                 return -1;
    2196             :                                 /* LCOV_EXCL_STOP */
    2197             :                         }
    2198             : 
    2199         759 :                         flags = 0;
    2200         759 :                         if (strcmp(type, "Pre-fail") == 0)
    2201         189 :                                 flags |= SMART_ATTR_TYPE_PREFAIL;
    2202         570 :                         else if (strcmp(type, "Old_age") == 0)
    2203         570 :                                 flags |= SMART_ATTR_TYPE_OLDAGE;
    2204         759 :                         if (strcmp(updated, "Always") == 0)
    2205         639 :                                 flags |= SMART_ATTR_UPDATE_ALWAYS;
    2206         120 :                         else if (strcmp(updated, "Offline") == 0)
    2207         120 :                                 flags |= SMART_ATTR_UPDATE_OFFLINE;
    2208         759 :                         if (strcmp(when_failed, "FAILING_NOW") == 0)
    2209           0 :                                 flags |= SMART_ATTR_WHEN_FAILED_NOW;
    2210         759 :                         else if (strcmp(when_failed, "In_the_past") == 0)
    2211           6 :                                 flags |= SMART_ATTR_WHEN_FAILED_PAST;
    2212         753 :                         else if (strcmp(when_failed, "-") == 0)
    2213         753 :                                 flags |= SMART_ATTR_WHEN_FAILED_NEVER;
    2214             : 
    2215             :                         /* revert the min/max decoding done by smartctl */
    2216         759 :                         if (format_minmax
    2217          36 :                                 && raw <= 0xFFFFUL
    2218          36 :                                 && min <= 0xFFFFUL
    2219          36 :                                 && max <= 0xFFFFUL) {
    2220          36 :                                 raw |= min << 16;
    2221          36 :                                 raw |= max << 32;
    2222             :                         }
    2223             : 
    2224             :                         /* revert the avg decoding done by smartctl */
    2225         759 :                         if (format_avg
    2226          30 :                                 && raw <= 0xFFFFUL
    2227          30 :                                 && avg <= 0xFFFFUL) {
    2228          30 :                                 raw |= avg << 16;
    2229             :                         }
    2230             : 
    2231         759 :                         smart[id].raw = raw;
    2232         759 :                         smart[id].norm = norm;
    2233         759 :                         smart[id].worst = worst;
    2234         759 :                         smart[id].thresh = thresh;
    2235         759 :                         smart[id].flags = flags;
    2236         759 :                         pathcpy(smart[id].name, sizeof(smart[id].name), id_name);
    2237             : 
    2238             :                         /*
    2239             :                          * Map normalized health percentage to our unified wear level for SSDs
    2240             :                          * 177: Wear_Leveling_Count (Samsung/Crucial)
    2241             :                          * 231: SSD_Life_Left (Kingston/WD)
    2242             :                          * 233: Media_Wearout_Indicator (Intel)
    2243             :                          */
    2244         759 :                         if (norm <= 100) {
    2245         585 :                                 if (strcmp(id_name, "Wear_Leveling_Count") == 0
    2246         582 :                                         || strcmp(id_name, "SSD_Life_Left") == 0
    2247         582 :                                         || strcmp(id_name, "Media_Wearout_Indicator") == 0) {
    2248           3 :                                         smart[SMART_WEAR_LEVEL].raw = 100 - norm;
    2249             :                                 }
    2250             :                         }
    2251             :                 }
    2252             :         }
    2253             : 
    2254          54 :         return 0;
    2255             : }
    2256             : 
    2257          85 : int smartctl_flush(FILE* f, const char* file, const char* name)
    2258             : {
    2259             :         /* read the file */
    2260          68 :         while (1) {
    2261             :                 char buf[256];
    2262             :                 char* s;
    2263             : 
    2264          85 :                 s = fgets(buf, sizeof(buf), f);
    2265          85 :                 if (s == 0)
    2266          17 :                         break;
    2267             : 
    2268             :                 /* remove extraneous chars */
    2269          68 :                 s = strpolish(buf);
    2270             : 
    2271          68 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    2272             :         }
    2273             : 
    2274          17 :         return 0;
    2275             : }
    2276             : 
    2277      145178 : int smart_temp(devinfo_t* devinfo)
    2278             : {
    2279      145178 :         uint64_t t = devinfo->smart[SMART_TEMPERATURE_CELSIUS].raw;
    2280      145178 :         if (t == SMART_UNASSIGNED)
    2281          16 :                 t = devinfo->smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].raw;
    2282      145178 :         if (t == SMART_UNASSIGNED)
    2283          12 :                 return -1;
    2284             : 
    2285             :         /* mask out min/max values */
    2286      145166 :         t &= 0xFFFFUL;
    2287             : 
    2288      145166 :         if (t == 0)
    2289           0 :                 return -1;
    2290      145166 :         if (t > 100)
    2291           0 :                 return -1;
    2292             : 
    2293      145166 :         return t;
    2294             : }
    2295             : 
    2296             : /****************************************************************************/
    2297             : /* thread */
    2298             : 
    2299             : #if HAVE_THREAD
    2300        3479 : void thread_mutex_init(thread_mutex_t* mutex)
    2301             : {
    2302        3479 :         if (pthread_mutex_init(mutex, 0) != 0) {
    2303             :                 /* LCOV_EXCL_START */
    2304             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_init().\n");
    2305             :                 os_abort();
    2306             :                 /* LCOV_EXCL_STOP */
    2307             :         }
    2308        3479 : }
    2309             : 
    2310        3347 : void thread_mutex_destroy(thread_mutex_t* mutex)
    2311             : {
    2312        3347 :         if (pthread_mutex_destroy(mutex) != 0) {
    2313             :                 /* LCOV_EXCL_START */
    2314             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_destroy().\n");
    2315             :                 os_abort();
    2316             :                 /* LCOV_EXCL_STOP */
    2317             :         }
    2318        3347 : }
    2319             : 
    2320    94671962 : void thread_mutex_lock(thread_mutex_t* mutex)
    2321             : {
    2322    94671962 :         if (pthread_mutex_lock(mutex) != 0) {
    2323             :                 /* LCOV_EXCL_START */
    2324             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_lock().\n");
    2325             :                 os_abort();
    2326             :                 /* LCOV_EXCL_STOP */
    2327             :         }
    2328    94671962 : }
    2329             : 
    2330    94671962 : void thread_mutex_unlock(thread_mutex_t* mutex)
    2331             : {
    2332    94671962 :         if (pthread_mutex_unlock(mutex) != 0) {
    2333             :                 /* LCOV_EXCL_START */
    2334             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_unlock().\n");
    2335             :                 os_abort();
    2336             :                 /* LCOV_EXCL_STOP */
    2337             :         }
    2338    94671962 : }
    2339             : 
    2340         416 : void thread_cond_init(thread_cond_t* cond)
    2341             : {
    2342         416 :         if (pthread_cond_init(cond, 0) != 0) {
    2343             :                 /* LCOV_EXCL_START */
    2344             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_init().\n");
    2345             :                 os_abort();
    2346             :                 /* LCOV_EXCL_STOP */
    2347             :         }
    2348         416 : }
    2349             : 
    2350         416 : void thread_cond_destroy(thread_cond_t* cond)
    2351             : {
    2352         416 :         if (pthread_cond_destroy(cond) != 0) {
    2353             :                 /* LCOV_EXCL_START */
    2354             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_destroy().\n");
    2355             :                 os_abort();
    2356             :                 /* LCOV_EXCL_STOP */
    2357             :         }
    2358         416 : }
    2359             : 
    2360        5264 : void thread_cond_signal(thread_cond_t* cond)
    2361             : {
    2362        5264 :         if (pthread_cond_signal(cond) != 0) {
    2363             :                 /* LCOV_EXCL_START */
    2364             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_signal().\n");
    2365             :                 os_abort();
    2366             :                 /* LCOV_EXCL_STOP */
    2367             :         }
    2368        5264 : }
    2369             : 
    2370      269022 : void thread_cond_broadcast(thread_cond_t* cond)
    2371             : {
    2372      269022 :         if (pthread_cond_broadcast(cond) != 0) {
    2373             :                 /* LCOV_EXCL_START */
    2374             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_broadcast().\n");
    2375             :                 os_abort();
    2376             :                 /* LCOV_EXCL_STOP */
    2377             :         }
    2378      269022 : }
    2379             : 
    2380     1559293 : void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex)
    2381             : {
    2382     1559293 :         if (pthread_cond_wait(cond, mutex) != 0) {
    2383             :                 /* LCOV_EXCL_START */
    2384             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_wait().\n");
    2385             :                 os_abort();
    2386             :                 /* LCOV_EXCL_STOP */
    2387             :         }
    2388     1559293 : }
    2389             : 
    2390             : /**
    2391             :  * Implementation note about conditional variables.
    2392             :  *
    2393             :  * The conditional variables can be signaled inside or outside the mutex,
    2394             :  * what is better it's debatable but in general doing that outside the mutex,
    2395             :  * reduces the number of context switches.
    2396             :  *
    2397             :  * But when testing with helgrind and drd, this disallows such tools to
    2398             :  * to see the dependency between the signal and the wait.
    2399             :  *
    2400             :  * To avoid it we signal everything inside the mutex. And we do this in both
    2401             :  * test mode (with CHECKER defined) and release mode (CHECKER not defined),
    2402             :  * to be on the safe side and avoid any difference in behaviour between test and
    2403             :  * release.
    2404             :  *
    2405             :  * Here some interesting discussion:
    2406             :  *
    2407             :  * Condvars: signal with mutex locked or not?
    2408             :  * http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
    2409             :  *
    2410             :  * Calling pthread_cond_signal without locking mutex
    2411             :  * http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex/4544494#4544494
    2412             :  */
    2413             : 
    2414             : /**
    2415             :  * Control when to signal the condition variables.
    2416             :  */
    2417             : int thread_cond_signal_outside = 0;
    2418             : 
    2419        5264 : void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    2420             : {
    2421        5264 :         if (thread_cond_signal_outside) {
    2422             :                 /* without the thread checker unlock before signaling, */
    2423             :                 /* this reduces the number of context switches */
    2424           0 :                 thread_mutex_unlock(mutex);
    2425             :         }
    2426             : 
    2427        5264 :         thread_cond_signal(cond);
    2428             : 
    2429        5264 :         if (!thread_cond_signal_outside) {
    2430             :                 /* with the thread checker unlock after signaling */
    2431             :                 /* to make explicit the condition and mutex relation */
    2432        5264 :                 thread_mutex_unlock(mutex);
    2433             :         }
    2434        5264 : }
    2435             : 
    2436      268814 : void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    2437             : {
    2438      268814 :         if (thread_cond_signal_outside) {
    2439             :                 /* without the thread checker unlock before signaling, */
    2440             :                 /* this reduces the number of context switches */
    2441           0 :                 thread_mutex_unlock(mutex);
    2442             :         }
    2443             : 
    2444      268814 :         thread_cond_broadcast(cond);
    2445             : 
    2446      268814 :         if (!thread_cond_signal_outside) {
    2447             :                 /* with the thread checker unlock after signaling */
    2448             :                 /* to make explicit the condition and mutex relation */
    2449      268814 :                 thread_mutex_unlock(mutex);
    2450             :         }
    2451      268814 : }
    2452             : 
    2453        3015 : void thread_create(thread_id_t* thread, void* (*func)(void*), void* arg)
    2454             : {
    2455        3015 :         if (pthread_create(thread, 0, func, arg) != 0) {
    2456             :                 /* LCOV_EXCL_START */
    2457             :                 log_fatal(EINTERNAL, "Failed call to pthread_create().\n");
    2458             :                 os_abort();
    2459             :                 /* LCOV_EXCL_STOP */
    2460             :         }
    2461        3015 : }
    2462             : 
    2463        3015 : void thread_join(thread_id_t thread, void** retval)
    2464             : {
    2465        3015 :         if (pthread_join(thread, retval) != 0) {
    2466             :                 /* LCOV_EXCL_START */
    2467             :                 log_fatal(EINTERNAL, "Failed call to pthread_join().\n");
    2468             :                 os_abort();
    2469             :                 /* LCOV_EXCL_STOP */
    2470             :         }
    2471        3015 : }
    2472             : 
    2473      114549 : void thread_yield(void)
    2474             : {
    2475             : #ifdef __MINGW32__
    2476             :         Sleep(0);
    2477             : #else
    2478      114549 :         sched_yield();
    2479             : #endif
    2480      114549 : }
    2481             : #endif
    2482             : 
    2483             : /****************************************************************************/
    2484             : /* wnmatch */
    2485             : 
    2486             : /**
    2487             :  * Helper function for case-insensitive character comparison
    2488             :  */
    2489    89538414 : static inline int char_match(char p, char t)
    2490             : {
    2491             : #ifdef WIN32
    2492             :         return tolower((unsigned char)p) == tolower((unsigned char)t);
    2493             : #else
    2494    89538414 :         return p == t;
    2495             : #endif
    2496             : }
    2497             : 
    2498             : /**
    2499             :  * Match character class [...]
    2500             :  * Return 0 if NOT matched
    2501             :  */
    2502          28 : static const char* match_class(const char* p, char t)
    2503             : {
    2504          28 :         int negate = 0;
    2505          28 :         int matched = 0;
    2506             : 
    2507          28 :         if (*p == '!' || *p == '^') {
    2508           9 :                 negate = 1;
    2509           9 :                 ++p;
    2510             :         }
    2511             : 
    2512          77 :         while (*p && *p != ']') {
    2513          49 :                 if (p[1] == '-' && p[2] != ']' && p[2] != 0) {
    2514             :                         /* range [a-z] */
    2515          22 :                         char start = *p;
    2516          22 :                         char end = p[2];
    2517             : #ifdef WIN32
    2518             :                         start = tolower((unsigned char)start);
    2519             :                         end = tolower((unsigned char)end);
    2520             :                         t = tolower((unsigned char)t);
    2521             : #endif
    2522          22 :                         if (t >= start && t <= end)
    2523          10 :                                 matched = 1;
    2524          22 :                         p += 3;
    2525             :                 } else {
    2526             :                         /* single character */
    2527          27 :                         if (char_match(*p, t))
    2528           5 :                                 matched = 1;
    2529          27 :                         ++p;
    2530             :                 }
    2531             :         }
    2532             : 
    2533          28 :         if (*p == ']')
    2534          28 :                 ++p;
    2535             : 
    2536          28 :         if (negate)
    2537           9 :                 matched = !matched;
    2538          28 :         if (!matched)
    2539          14 :                 return 0;
    2540             : 
    2541          14 :         return p;
    2542             : }
    2543             : 
    2544   105401007 : int wnmatch(const char* p, const char* t)
    2545             : {
    2546   105401007 :         char p1 = 0; /* previous char */
    2547   109158023 :         while (*p) {
    2548   109025220 :                 char p0 = *p;
    2549   109025220 :                 switch (p0) {
    2550          20 :                 case '?' :
    2551             :                         /* ? matches any single character except / */
    2552          20 :                         if (*t == 0 || *t == '/')
    2553           4 :                                 return 1;
    2554          16 :                         ++p;
    2555          16 :                         ++t;
    2556          16 :                         break;
    2557    13066983 :                 case '*' :
    2558             :                         /* check for ** */
    2559    13066983 :                         if (p[1] == '*') {
    2560             :                                 /* skip the ** */
    2561         104 :                                 p += 2;
    2562             : 
    2563             :                                 /* munge all * */
    2564         114 :                                 while (*p == '*')
    2565          10 :                                         ++p;
    2566             : 
    2567             :                                 /* if its not near a slash, it's like a single * */
    2568         104 :                                 if (p1 == '/' || *p == '/') {
    2569             :                                         /* a ** at end matches everything */
    2570          84 :                                         if (*p == 0)
    2571          10 :                                                 return 0;
    2572             : 
    2573             :                                         /*
    2574             :                                          * In between slashes matches to nothing
    2575             :                                          *
    2576             :                                          * Check for /##/ or ^##/ (^ start of string)
    2577             :                                          *
    2578             :                                          * This is required for:
    2579             :                                          * "/##/file.txt" matching "/file.txt"
    2580             :                                          * "##/file.txt" matching "file.txt"
    2581             :                                          * "x##/file.txt" NOT matching "xfile.txt"
    2582             :                                          */
    2583          74 :                                         if (*p == '/' && (p1 == 0 || p1 == '/')) {
    2584             :                                                 /* try reducing to nothing */
    2585          71 :                                                 if (wnmatch(p + 1, t) == 0)
    2586          27 :                                                         return 0;
    2587             :                                                 /* otherwise / should match in the text */
    2588             :                                         }
    2589             : 
    2590             :                                         /* try matching with 0 or more characters */
    2591         275 :                                         while (*t) {
    2592         263 :                                                 if (wnmatch(p, t) == 0)
    2593          35 :                                                         return 0;
    2594         228 :                                                 ++t;
    2595             :                                         }
    2596             : 
    2597             :                                         /* try matching at the end */
    2598          12 :                                         return wnmatch(p, t);
    2599             :                                 }
    2600             :                         } else {
    2601             :                                 /* skip the * */
    2602    13066879 :                                 ++p;
    2603             :                         }
    2604             : 
    2605             :                         /* a * at end matches rest of segment */
    2606    13066899 :                         if (*p == 0) {
    2607        1430 :                                 while (*t && *t != '/')
    2608        1285 :                                         ++t;
    2609         145 :                                 return *t != 0;
    2610             :                         }
    2611             : 
    2612             :                         /* try matching with 0 or more characters */
    2613    92129502 :                         while (*t && *t != '/') {
    2614    79186821 :                                 if (wnmatch(p, t) == 0)
    2615      124073 :                                         return 0;
    2616    79062748 :                                 ++t;
    2617             :                         }
    2618             : 
    2619             :                         /* try matching at the end */
    2620    12942681 :                         return wnmatch(p, t);
    2621          31 :                 case '[' :
    2622             :                         /* character class */
    2623          31 :                         if (*t == 0 || *t == '/')
    2624           3 :                                 return 1;
    2625             : 
    2626          28 :                         p = match_class(p + 1, *t);
    2627          28 :                         if (!p)
    2628          14 :                                 return 1;
    2629             : 
    2630          14 :                         ++t;
    2631          14 :                         break;
    2632    95958186 :                 default :
    2633             :                         /* literal character */
    2634    95958186 :                         if (*t == 0 || !char_match(*p, *t))
    2635    92201200 :                                 return 1;
    2636     3756986 :                         ++p;
    2637     3756986 :                         ++t;
    2638     3756986 :                         break;
    2639             :                 }
    2640     3757016 :                 p1 = p0;
    2641             :         }
    2642             : 
    2643             :         /* match successful if we've consumed all text */
    2644      132803 :         return *t != 0;
    2645             : }
    2646             : 

Generated by: LCOV version 1.0