LCOV - code coverage report
Current view: top level - cmdline - support.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 518 647 80.1 %
Date: 2025-10-28 11:59:11 Functions: 68 70 97.1 %

          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             : #endif
      32             : 
      33     4075441 : void lock_msg(void)
      34             : {
      35             : #if HAVE_THREAD
      36     4075441 :         thread_mutex_lock(&msg_lock);
      37             : #endif
      38     4075441 : }
      39             : 
      40     4075441 : void unlock_msg(void)
      41             : {
      42             : #if HAVE_THREAD
      43     4075441 :         thread_mutex_unlock(&msg_lock);
      44             : #endif
      45     4075441 : }
      46             : 
      47    20386521 : void lock_memory(void)
      48             : {
      49             : #if HAVE_THREAD
      50    20386521 :         thread_mutex_lock(&memory_lock);
      51             : #endif
      52    20386521 : }
      53             : 
      54    20386521 : void unlock_memory(void)
      55             : {
      56             : #if HAVE_THREAD
      57    20386521 :         thread_mutex_unlock(&memory_lock);
      58             : #endif
      59    20386521 : }
      60             : 
      61         301 : void lock_init(void)
      62             : {
      63             : #if HAVE_THREAD
      64             :         /* initialize the locks as first operation as log_fatal depends on them */
      65         301 :         thread_mutex_init(&msg_lock);
      66         301 :         thread_mutex_init(&memory_lock);
      67             : #endif
      68         301 : }
      69             : 
      70         278 : void lock_done(void)
      71             : {
      72             : #if HAVE_THREAD
      73         278 :         thread_mutex_destroy(&msg_lock);
      74         278 :         thread_mutex_destroy(&memory_lock);
      75             : #endif
      76         278 : }
      77             : 
      78             : /****************************************************************************/
      79             : /* print */
      80             : 
      81             : int msg_level = 0;
      82             : FILE* stdlog = 0;
      83             : int msg_line_prev = 0; /**< Previous line width on stdout */
      84             : int msg_line_curr = 0; /**< Current line width on stdout */
      85             : 
      86             : /*
      87             :  * Note that in the following functions we always flush both
      88             :  * stdout and stderr, because we want to ensure that they mixes
      89             :  * well when redirected to files
      90             :  *
      91             :  * The buffering is similar at the "line buffered" one, that
      92             :  * is not available on Windows, so we emulate it in this way.
      93             :  *
      94             :  * For stdlog flushing is limited. To ensure flushing the
      95             :  * caller should use log_flush().
      96             :  */
      97             : 
      98       44350 : static void vmsg(FILE* out, const char* format, va_list ap)
      99             : {
     100       44350 :         char* dup = strdup_nofail(format);
     101       44350 :         int len = strlen(dup);
     102       44350 :         int written = 0;
     103       44350 :         char control = 0;
     104             : 
     105       44350 :         if (len > 0) {
     106       44350 :                 if (dup[len - 1] == '\n') {
     107       44340 :                         dup[len - 1] = 0;
     108       44340 :                         control = '\n';
     109          10 :                 } else if (dup[len - 1] == '\r') {
     110           0 :                         dup[len - 1] = 0;
     111           0 :                         control = '\r';
     112             :                 }
     113             :         }
     114             : 
     115       44350 :         if (dup[0]) {
     116       44247 :                 written = vfprintf(out, dup, ap);
     117             :         }
     118             : 
     119       44350 :         switch (control) {
     120          10 :         case 0 :
     121          10 :                 msg_line_curr += written;
     122          10 :                 break;
     123       44340 :         case '\n' :
     124       44340 :                 msg_line_curr += written;
     125             :                 /* writes spaces to overwrite any previous char */
     126       44340 :                 while (msg_line_curr < msg_line_prev) {
     127           0 :                         fprintf(out, " ");
     128           0 :                         --msg_line_prev;
     129             :                 }
     130       44340 :                 msg_line_prev = 0;
     131       44340 :                 msg_line_curr = 0;
     132       44340 :                 fprintf(out, "\n");
     133       44340 :                 break;
     134           0 :         case '\r' :
     135           0 :                 msg_line_curr += written;
     136             :                 /* writes spaces to overwrite any previous char */
     137           0 :                 while (msg_line_curr < msg_line_prev) {
     138           0 :                         fprintf(out, " ");
     139           0 :                         --msg_line_prev;
     140             :                 }
     141           0 :                 msg_line_prev = msg_line_curr;
     142           0 :                 msg_line_curr = 0;
     143           0 :                 fprintf(out, "\r");
     144             :         }
     145             : 
     146       44350 :         free(dup);
     147       44350 : }
     148             : 
     149         797 : void log_fatal(const char* format, ...)
     150             : {
     151             :         va_list ap;
     152             : 
     153         797 :         lock_msg();
     154             : 
     155         797 :         if (stdlog) {
     156          95 :                 fprintf(stdlog, "msg:fatal: ");
     157             : 
     158          95 :                 va_start(ap, format);
     159          95 :                 vfprintf(stdlog, format, ap);
     160          95 :                 va_end(ap);
     161             : 
     162          95 :                 fflush(stdlog);
     163             :         }
     164             : 
     165         797 :         va_start(ap, format);
     166         797 :         vmsg(stderr, format, ap);
     167         797 :         va_end(ap);
     168             : 
     169         797 :         fflush(stderr);
     170             : 
     171         797 :         unlock_msg();
     172         797 : }
     173             : 
     174      614995 : void log_error(const char* format, ...)
     175             : {
     176             :         va_list ap;
     177             : 
     178      614995 :         lock_msg();
     179             : 
     180      614995 :         if (stdlog) {
     181      613112 :                 fprintf(stdlog, "msg:error: ");
     182             : 
     183      613112 :                 va_start(ap, format);
     184      613112 :                 vfprintf(stdlog, format, ap);
     185      613112 :                 va_end(ap);
     186             : 
     187      613112 :                 fflush(stdlog);
     188             :         } else {
     189        1883 :                 va_start(ap, format);
     190        1883 :                 vmsg(stderr, format, ap);
     191        1883 :                 va_end(ap);
     192             : 
     193        1883 :                 fflush(stderr);
     194             :         }
     195             : 
     196      614995 :         unlock_msg();
     197      614995 : }
     198             : 
     199       11259 : void log_expected(const char* format, ...)
     200             : {
     201             :         va_list ap;
     202             : 
     203       11259 :         lock_msg();
     204             : 
     205       11259 :         if (stdlog) {
     206       11259 :                 fprintf(stdlog, "msg:expected: ");
     207             : 
     208       11259 :                 va_start(ap, format);
     209       11259 :                 vfprintf(stdlog, format, ap);
     210       11259 :                 va_end(ap);
     211             : 
     212       11259 :                 fflush(stdlog);
     213             :         }
     214             : 
     215       11259 :         unlock_msg();
     216       11259 : }
     217             : 
     218     2994267 : void log_tag(const char* format, ...)
     219             : {
     220             :         va_list ap;
     221             : 
     222     2994267 :         lock_msg();
     223             : 
     224     2994267 :         if (stdlog) {
     225     2576356 :                 va_start(ap, format);
     226     2576356 :                 vfprintf(stdlog, format, ap);
     227     2576356 :                 va_end(ap);
     228             : 
     229             :                 /* here we intentionally don't flush to make the output faster */
     230             :         }
     231             : 
     232     2994267 :         unlock_msg();
     233     2994267 : }
     234             : 
     235        3622 : void log_flush(void)
     236             : {
     237        3622 :         lock_msg();
     238             : 
     239        3622 :         if (stdlog)
     240         339 :                 fflush(stdlog);
     241        3622 :         fflush(stdout);
     242        3622 :         fflush(stderr);
     243             : 
     244        3622 :         unlock_msg();
     245        3622 : }
     246             : 
     247         600 : void msg_status(const char* format, ...)
     248             : {
     249             :         va_list ap;
     250             : 
     251         600 :         lock_msg();
     252             : 
     253         600 :         if (stdlog) {
     254         279 :                 fprintf(stdlog, "msg:status: ");
     255             : 
     256         279 :                 va_start(ap, format);
     257         279 :                 vfprintf(stdlog, format, ap);
     258         279 :                 va_end(ap);
     259             : 
     260         279 :                 fflush(stdlog);
     261             :         }
     262             : 
     263         600 :         if (msg_level >= MSG_STATUS) {
     264         600 :                 va_start(ap, format);
     265         600 :                 vmsg(stdout, format, ap);
     266         600 :                 va_end(ap);
     267             :         }
     268             : 
     269         600 :         unlock_msg();
     270         600 : }
     271             : 
     272      315543 : void msg_info(const char* format, ...)
     273             : {
     274             :         va_list ap;
     275             : 
     276      315543 :         lock_msg();
     277             : 
     278             :         /* don't output in stdlog as these messages */
     279             :         /* are always paired with a msg_tag() call */
     280             : 
     281      315543 :         if (msg_level >= MSG_INFO) {
     282       40809 :                 va_start(ap, format);
     283       40809 :                 vmsg(stdout, format, ap);
     284       40809 :                 va_end(ap);
     285             : 
     286       40809 :                 fflush(stdout);
     287             :         }
     288             : 
     289      315543 :         unlock_msg();
     290      315543 : }
     291             : 
     292        4827 : void msg_progress(const char* format, ...)
     293             : {
     294             :         va_list ap;
     295             : 
     296        4827 :         lock_msg();
     297             : 
     298        4827 :         if (stdlog) {
     299         901 :                 fprintf(stdlog, "msg:progress: ");
     300             : 
     301         901 :                 va_start(ap, format);
     302         901 :                 vfprintf(stdlog, format, ap);
     303         901 :                 va_end(ap);
     304             : 
     305         901 :                 fflush(stdlog);
     306             :         }
     307             : 
     308        4827 :         if (msg_level >= MSG_PROGRESS) {
     309         170 :                 va_start(ap, format);
     310         170 :                 vmsg(stdout, format, ap);
     311         170 :                 va_end(ap);
     312             : 
     313         170 :                 fflush(stdout);
     314             :         }
     315             : 
     316        4827 :         unlock_msg();
     317        4827 : }
     318             : 
     319        2007 : void msg_bar(const char* format, ...)
     320             : {
     321             :         va_list ap;
     322             : 
     323        2007 :         lock_msg();
     324             : 
     325             :         /* don't output in stdlog as these messages */
     326             :         /* are intended for screen only */
     327             :         /* also don't flush stdout as they are intended to be partial messages */
     328             : 
     329        2007 :         if (msg_level >= MSG_BAR) {
     330           0 :                 va_start(ap, format);
     331           0 :                 vmsg(stdout, format, ap);
     332           0 :                 va_end(ap);
     333             :         }
     334             : 
     335        2007 :         unlock_msg();
     336        2007 : }
     337             : 
     338      126963 : void msg_verbose(const char* format, ...)
     339             : {
     340             :         va_list ap;
     341             : 
     342      126963 :         lock_msg();
     343             : 
     344      126963 :         if (stdlog) {
     345      125113 :                 fprintf(stdlog, "msg:verbose: ");
     346             : 
     347      125113 :                 va_start(ap, format);
     348      125113 :                 vfprintf(stdlog, format, ap);
     349      125113 :                 va_end(ap);
     350             : 
     351      125113 :                 fflush(stdlog);
     352             :         }
     353             : 
     354      126963 :         if (msg_level >= MSG_VERBOSE) {
     355          91 :                 va_start(ap, format);
     356          91 :                 vmsg(stdout, format, ap);
     357          91 :                 va_end(ap);
     358             : 
     359          91 :                 fflush(stdout);
     360             :         }
     361             : 
     362      126963 :         unlock_msg();
     363      126963 : }
     364             : 
     365         561 : void msg_flush(void)
     366             : {
     367         561 :         lock_msg();
     368             : 
     369         561 :         fflush(stdout);
     370         561 :         fflush(stderr);
     371             : 
     372         561 :         unlock_msg();
     373         561 : }
     374             : 
     375          53 : void printc(char c, size_t pad)
     376             : {
     377          95 :         while (pad) {
     378             :                 /* group writes in long pieces */
     379             :                 char buf[128];
     380          42 :                 size_t len = pad;
     381             : 
     382          42 :                 if (len >= sizeof(buf))
     383           0 :                         len = sizeof(buf) - 1;
     384             : 
     385          42 :                 memset(buf, c, len);
     386          42 :                 buf[len] = 0;
     387             : 
     388          42 :                 fputs(buf, stdout);
     389             : 
     390          42 :                 pad -= len;
     391             :         }
     392          53 : }
     393             : 
     394          16 : void printr(const char* str, size_t pad)
     395             : {
     396             :         size_t len;
     397             : 
     398          16 :         len = strlen(str);
     399             : 
     400          16 :         if (len < pad)
     401          16 :                 printc(' ', pad - len);
     402             : 
     403          16 :         fputs(str, stdout);
     404          16 : }
     405             : 
     406         142 : void printl(const char* str, size_t pad)
     407             : {
     408             :         size_t len;
     409             : 
     410         142 :         fputs(str, stdout);
     411             : 
     412         142 :         len = strlen(str);
     413             : 
     414         142 :         if (len < pad)
     415          18 :                 printc(' ', pad - len);
     416         142 : }
     417             : 
     418          18 : void printp(double v, size_t pad)
     419             : {
     420             :         char buf[64];
     421          18 :         const char* s = "%";
     422             : 
     423          18 :         if (v > 0.1)
     424           7 :                 snprintf(buf, sizeof(buf), "%5.2f%s", v, s);
     425          11 :         else if (v > 0.01)
     426           4 :                 snprintf(buf, sizeof(buf), "%6.3f%s", v, s);
     427           7 :         else if (v > 0.001)
     428           2 :                 snprintf(buf, sizeof(buf), "%7.4f%s", v, s);
     429           5 :         else if (v > 0.0001)
     430           1 :                 snprintf(buf, sizeof(buf), "%8.5f%s", v, s);
     431           4 :         else if (v > 0.00001)
     432           2 :                 snprintf(buf, sizeof(buf), "%9.6f%s", v, s);
     433           2 :         else if (v > 0.000001)
     434           0 :                 snprintf(buf, sizeof(buf), "%10.7f%s", v, s);
     435           2 :         else if (v > 0.0000001)
     436           1 :                 snprintf(buf, sizeof(buf), "%11.8f%s", v, s);
     437           1 :         else if (v > 0.00000001)
     438           0 :                 snprintf(buf, sizeof(buf), "%12.9f%s", v, s);
     439           1 :         else if (v > 0.000000001)
     440           1 :                 snprintf(buf, sizeof(buf), "%13.10f%s", v, s);
     441           0 :         else if (v > 0.0000000001)
     442           0 :                 snprintf(buf, sizeof(buf), "%14.11f%s", v, s);
     443           0 :         else if (v > 0.00000000001)
     444           0 :                 snprintf(buf, sizeof(buf), "%15.12f%s", v, s);
     445           0 :         else if (v > 0.000000000001)
     446           0 :                 snprintf(buf, sizeof(buf), "%16.13f%s", v, s);
     447             :         else
     448           0 :                 snprintf(buf, sizeof(buf), "%17.14f%s", v, s);
     449          18 :         printl(buf, pad);
     450          18 : }
     451             : 
     452             : #define charcat(c) \
     453             :         do { \
     454             :                 if (p == end) \
     455             :                         goto bail; \
     456             :                 *p++ = (c); \
     457             :         } while (0)
     458             : 
     459     2430798 : const char* esc_tag(const char* str, char* buffer)
     460             : {
     461     2430798 :         char* begin = buffer;
     462     2430798 :         char* end = begin + ESC_MAX;
     463     2430798 :         char* p = begin;
     464             : 
     465             :         /* copy string with escaping */
     466    33983048 :         while (*str) {
     467    31552250 :                 char c = *str++;
     468             : 
     469    31552250 :                 switch (c) {
     470           3 :                 case '\n' :
     471           3 :                         charcat('\\');
     472           3 :                         charcat('n');
     473           3 :                         break;
     474           3 :                 case '\r' :
     475           3 :                         charcat('\\');
     476           3 :                         charcat('r');
     477           3 :                         break;
     478           4 :                 case ':' :
     479           4 :                         charcat('\\');
     480           4 :                         charcat('d');
     481           4 :                         break;
     482           6 :                 case '\\' :
     483           6 :                         charcat('\\');
     484           6 :                         charcat('\\');
     485           6 :                         break;
     486    31552234 :                 default :
     487    31552234 :                         charcat(c);
     488    31552234 :                         break;
     489             :                 }
     490             :         }
     491             : 
     492             :         /* put final 0 */
     493     2430798 :         if (p == end)
     494           0 :                 goto bail;
     495     2430798 :         *p = 0;
     496             : 
     497     2430798 :         return begin;
     498             : 
     499           0 : bail:
     500             :         /* LCOV_EXCL_START */
     501             :         log_fatal("Escape for log is too long\n");
     502             :         exit(EXIT_FAILURE);
     503             :         /* LCOV_EXCL_STOP */
     504             : }
     505             : 
     506             : #ifdef _WIN32
     507             : static int needs_quote(const char* arg)
     508             : {
     509             :         while (*arg) {
     510             :                 char c = *arg;
     511             :                 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
     512             :                         c == '&' || c == '|' || c == '(' || c == ')' ||
     513             :                         c == '<' || c == '>' || c == '^' || c == '"' ||
     514             :                         c == '%' || c == '!' || c == '=' || c == ';' ||
     515             :                         (unsigned char)c < 32 || c == 127)
     516             :                         return 1;
     517             :                 ++arg;
     518             :         }
     519             : 
     520             :         return 0;
     521             : }
     522             : #endif
     523             : 
     524             : struct stream {
     525             :         const char* str;
     526             :         unsigned idx;
     527             :         const char** map;
     528             :         unsigned max;
     529             : };
     530             : 
     531     5148999 : static inline char ssget(struct stream* ss)
     532             : {
     533             :         /* get the next char */
     534     5148999 :         char c = *ss->str++;
     535             : 
     536             :         /* if one string is finished, go to the next */
     537     5185089 :         while (c == 0 && ss->idx + 1 < ss->max) {
     538       36090 :                 ss->str = ss->map[++ss->idx];
     539       36090 :                 c = *ss->str++;
     540             :         }
     541             : 
     542     5148999 :         return c;
     543             : }
     544             : 
     545             : #ifdef _WIN32
     546             : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     547             : {
     548             :         char* begin = buffer;
     549             :         char* end = begin + ESC_MAX;
     550             :         char* p = begin;
     551             :         struct stream ss;
     552             :         char c;
     553             :         unsigned i;
     554             :         int has_quote;
     555             : 
     556             :         has_quote = 0;
     557             :         for (i = 0; i < str_max; ++i) {
     558             :                 if (needs_quote(str_map[i]))
     559             :                         has_quote = 1;
     560             :         }
     561             : 
     562             :         ss.idx = 0;
     563             :         ss.str = str_map[0];
     564             :         ss.map = str_map;
     565             :         ss.max = str_max;
     566             : 
     567             :         if (!has_quote) {
     568             :                 c = ssget(&ss);
     569             :                 while (c) {
     570             :                         charcat(c);
     571             :                         c = ssget(&ss);
     572             :                 }
     573             :         } else {
     574             :                 /* starting quote */
     575             :                 charcat('"');
     576             : 
     577             :                 c = ssget(&ss);
     578             :                 while (c) {
     579             :                         int bl = 0;
     580             :                         while (c == '\\') {
     581             :                                 ++bl;
     582             :                                 c = ssget(&ss);
     583             :                         }
     584             : 
     585             :                         if (c == 0) {
     586             :                                 /* double backslashes before closing quote */
     587             :                                 bl = bl * 2;
     588             :                                 while (bl--)
     589             :                                         charcat('\\');
     590             :                                 break;
     591             :                         } else if (c == '"') {
     592             :                                 /* double backslashes + escape the quote */
     593             :                                 bl = bl * 2 + 1;
     594             :                                 while (bl--)
     595             :                                         charcat('\\');
     596             :                                 charcat('"');
     597             :                         } else {
     598             :                                 /* normal backslashes */
     599             :                                 while (bl--)
     600             :                                         charcat('\\');
     601             :                                 charcat(c);
     602             :                         }
     603             : 
     604             :                         c = ssget(&ss);
     605             :                 }
     606             : 
     607             :                 /* ending quote */
     608             :                 charcat('"');
     609             :         }
     610             : 
     611             :         /* put final 0 */
     612             :         charcat(0);
     613             : 
     614             :         return begin;
     615             : 
     616             : bail:
     617             :         /* LCOV_EXCL_START */
     618             :         log_fatal("Escape for shell is too long\n");
     619             :         exit(EXIT_FAILURE);
     620             :         /* LCOV_EXCL_STOP */
     621             : }
     622             : #else
     623      357993 : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     624             : {
     625      357993 :         char* begin = buffer;
     626      357993 :         char* end = begin + ESC_MAX;
     627      357993 :         char* p = begin;
     628             :         struct stream ss;
     629             :         char c;
     630             : 
     631      357993 :         ss.idx = 0;
     632      357993 :         ss.str = str_map[0];
     633      357993 :         ss.map = str_map;
     634      357993 :         ss.max = str_max;
     635             :         
     636      357993 :         c = ssget(&ss);
     637     5148999 :         while (c) {
     638     4791006 :                 switch (c) {
     639             :                 /* special chars that need to be quoted */
     640       79667 :                 case ' ' : /* space */
     641             :                 case '\t' : /* tab */
     642             :                 case '\n' : /* newline */
     643             :                 case '\r' : /* carriage return */
     644             :                 case '~' : /* home */
     645             :                 case '`' : /* command */
     646             :                 case '#' : /* comment */
     647             :                 case '$' : /* variable */
     648             :                 case '&' : /* background job */
     649             :                 case '*' : /* wildcard */
     650             :                 case '(' : /* shell */
     651             :                 case ')' : /* shell */
     652             :                 case '\\' : /* quote */
     653             :                 case '|' : /* pipe */
     654             :                 case '[' : /* wildcard */
     655             :                 case ']' : /* wildcard */
     656             :                 case '{' : /* code */
     657             :                 case '}' : /* code */
     658             :                 case ';' : /* separator */
     659             :                 case '\'' : /* quote */
     660             :                 case '"' : /* quote */
     661             :                 case '<' : /* redirect */
     662             :                 case '>' : /* redirect */
     663             :                 case '?' : /* wildcard */
     664             :                 case '=' : /* assignment */
     665             :                 case '!' : /* history expansion */
     666       79667 :                         charcat('\\');
     667       79667 :                         charcat(c);
     668       79667 :                         break;
     669     4711339 :                 default :
     670             :                         /* check for control characters */
     671     4711339 :                         if ((unsigned char)c < 32 || c == 127) {
     672           6 :                                 charcat('\\');
     673           6 :                                 charcat(c);
     674             :                         } else {
     675     4711333 :                                 charcat(c);
     676             :                         }
     677     4711339 :                         break;
     678             :                 }
     679             : 
     680     4791006 :                 c = ssget(&ss);
     681             :         }
     682             : 
     683             :         /* put final 0 */
     684      357993 :         charcat(0);
     685             : 
     686      357993 :         return begin;
     687             : 
     688           0 : bail:
     689             :         /* LCOV_EXCL_START */
     690             :         log_fatal("Escape for shell is too long\n");
     691             :         exit(EXIT_FAILURE);
     692             :         /* LCOV_EXCL_STOP */
     693             : }
     694             : #endif
     695             : 
     696           1 : char* strpolish(char* s)
     697             : {
     698           1 :         char* i = s;
     699             : 
     700           5 :         while (*i) {
     701           4 :                 if (isspace(*i) || !isprint(*i))
     702           4 :                         *i = ' ';
     703           4 :                 ++i;
     704             :         }
     705             : 
     706           1 :         return s;
     707             : }
     708             : 
     709        1652 : unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters)
     710             : {
     711        1652 :         unsigned mac = 0;
     712             : 
     713             :         /* skip initial delimiters */
     714        1652 :         str += strspn(str, delimiters);
     715             : 
     716        8444 :         while (*str != 0 || mac == split_max) {
     717             :                 /* start of the token */
     718        6792 :                 split_map[mac] = str;
     719        6792 :                 ++mac;
     720             : 
     721             :                 /* find the first delimiter or the end of the string */
     722        6792 :                 str += strcspn(str, delimiters);
     723             : 
     724             :                 /* put the final terminator if missing */
     725        6792 :                 if (*str != 0)
     726        5324 :                         *str++ = 0;
     727             : 
     728             :                 /* skip trailing delimiters */
     729        6792 :                 str += strspn(str, delimiters);
     730             :         }
     731             : 
     732        1652 :         return mac;
     733             : }
     734             : 
     735           9 : char* strtrim(char* str)
     736             : {
     737             :         char* begin;
     738             :         char* end;
     739             : 
     740           9 :         begin = str;
     741          10 :         while (begin[0] && isspace((unsigned char)begin[0]))
     742           1 :                 ++begin;
     743             : 
     744           9 :         end = begin + strlen(begin);
     745          20 :         while (end > begin && isspace((unsigned char)end[-1]))
     746          11 :                 --end;
     747             : 
     748           9 :         end[0] = 0;
     749             : 
     750           9 :         if (begin != end)
     751           9 :                 memmove(str, begin, end - begin + 1);
     752             : 
     753           9 :         return str;
     754             : }
     755             : 
     756           2 : char* strlwr(char* str)
     757             : {
     758           2 :         char* s = str;
     759             : 
     760          24 :         while (*s) {
     761          22 :                 *s = tolower((unsigned char)*s);
     762          22 :                 ++s;
     763             :         }
     764             :         
     765           2 :         return str;
     766             : }
     767             : 
     768          11 : char* worddigitstr(const char* haystack, const char* needle)
     769             : {
     770          11 :         size_t len = strlen(needle);
     771             :         const char* s;
     772             : 
     773          11 :         if (len == 0)
     774           1 :                 return NULL;
     775             : 
     776          11 :         for (s = haystack; (s = strstr(s, needle)) != NULL; ++s) {
     777             : 
     778             :                 /* left boundary */
     779           8 :                 if (s == haystack || isspace((unsigned char)s[-1]) || isdigit((unsigned char)s[-1])) {
     780             :                         /* right boundary */
     781           7 :                         if (s[len] == '\0' || isspace((unsigned char)s[len]) || isdigit((unsigned char)s[len])) {
     782           7 :                                 return (char *)s;
     783             :                         }
     784             :                 }
     785             :         }
     786             : 
     787           3 :         return NULL;
     788             : }
     789             : 
     790             : /****************************************************************************/
     791             : /* path */
     792             : 
     793     8092232 : void pathcpy(char* dst, size_t size, const char* src)
     794             : {
     795     8092232 :         size_t len = strlen(src);
     796             : 
     797     8092232 :         if (len + 1 > size) {
     798             :                 /* LCOV_EXCL_START */
     799             :                 log_fatal("Path too long '%s'\n", src);
     800             :                 os_abort();
     801             :                 /* LCOV_EXCL_STOP */
     802             :         }
     803             : 
     804     8092232 :         memcpy(dst, src, len + 1);
     805     8092232 : }
     806             : 
     807         269 : void pathcat(char* dst, size_t size, const char* src)
     808             : {
     809         269 :         size_t dst_len = strlen(dst);
     810         269 :         size_t src_len = strlen(src);
     811             : 
     812         269 :         if (dst_len + src_len + 1 > size) {
     813             :                 /* LCOV_EXCL_START */
     814             :                 log_fatal("Path too long '%s%s'\n", dst, src);
     815             :                 os_abort();
     816             :                 /* LCOV_EXCL_STOP */
     817             :         }
     818             : 
     819         269 :         memcpy(dst + dst_len, src, src_len + 1);
     820         269 : }
     821             : 
     822     3426856 : void pathcatl(char* dst, size_t dst_len, size_t size, const char* src)
     823             : {
     824     3426856 :         size_t src_len = strlen(src);
     825             : 
     826     3426856 :         if (dst_len + src_len + 1 > size) {
     827             :                 /* LCOV_EXCL_START */
     828             :                 log_fatal("Path too long '%s%s'\n", dst, src);
     829             :                 os_abort();
     830             :                 /* LCOV_EXCL_STOP */
     831             :         }
     832             : 
     833     3426856 :         memcpy(dst + dst_len, src, src_len + 1);
     834     3426856 : }
     835             : 
     836         788 : void pathcatc(char* dst, size_t size, char c)
     837             : {
     838         788 :         size_t dst_len = strlen(dst);
     839             : 
     840         788 :         if (dst_len + 2 > size) {
     841             :                 /* LCOV_EXCL_START */
     842             :                 log_fatal("Path too long '%s%c'\n", dst, c);
     843             :                 os_abort();
     844             :                 /* LCOV_EXCL_STOP */
     845             :         }
     846             : 
     847         788 :         dst[dst_len] = c;
     848         788 :         dst[dst_len + 1] = 0;
     849         788 : }
     850             : 
     851       16750 : void pathimport(char* dst, size_t size, const char* src)
     852             : {
     853       16750 :         pathcpy(dst, size, src);
     854             : 
     855             : #ifdef _WIN32
     856             :         /* convert the  Windows dir separator '\' to C '/', */
     857             :         /* and the Windows escaping  char '^' to the fnmatch '\' */
     858             :         while (*dst) {
     859             :                 switch (*dst) {
     860             :                 case '\\' :
     861             :                         *dst = '/';
     862             :                         break;
     863             :                 case '^' :
     864             :                         *dst = '\\';
     865             :                         break;
     866             :                 }
     867             :                 ++dst;
     868             :         }
     869             : #endif
     870       16750 : }
     871             : 
     872       12234 : void pathexport(char* dst, size_t size, const char* src)
     873             : {
     874       12234 :         pathcpy(dst, size, src);
     875             : 
     876             : #ifdef _WIN32
     877             :         /* invert the import */
     878             :         while (*dst) {
     879             :                 switch (*dst) {
     880             :                 case '/' :
     881             :                         *dst = '\\';
     882             :                         break;
     883             :                 case '\\' :
     884             :                         *dst = '^';
     885             :                         break;
     886             :                 }
     887             :                 ++dst;
     888             :         }
     889             : #endif
     890       12234 : }
     891             : 
     892    41749278 : void pathprint(char* dst, size_t size, const char* format, ...)
     893             : {
     894             :         size_t len;
     895             :         va_list ap;
     896             : 
     897    41749278 :         va_start(ap, format);
     898    41749278 :         len = vsnprintf(dst, size, format, ap);
     899    41749278 :         va_end(ap);
     900             : 
     901    41749278 :         if (len >= size) {
     902             :                 /* LCOV_EXCL_START */
     903             :                 if (size > 0) {
     904             :                         dst[size - 1] = 0;
     905             :                         log_fatal("Path too long '%s...'\n", dst);
     906             :                 } else {
     907             :                         log_fatal("Path too long for empty size'\n");
     908             :                 }
     909             :                 os_abort();
     910             :                 /* LCOV_EXCL_STOP */
     911             :         }
     912    41749278 : }
     913             : 
     914        7719 : void pathslash(char* dst, size_t size)
     915             : {
     916        7719 :         size_t len = strlen(dst);
     917             : 
     918        7719 :         if (len > 0 && dst[len - 1] != '/') {
     919        6116 :                 if (len + 2 >= size) {
     920             :                         /* LCOV_EXCL_START */
     921             :                         log_fatal("Path too long '%s/'\n", dst);
     922             :                         os_abort();
     923             :                         /* LCOV_EXCL_STOP */
     924             :                 }
     925             : 
     926        6116 :                 dst[len] = '/';
     927        6116 :                 dst[len + 1] = 0;
     928             :         }
     929        7719 : }
     930             : 
     931       58108 : void pathcut(char* dst)
     932             : {
     933       58108 :         char* slash = strrchr(dst, '/');
     934             : 
     935       58108 :         if (slash)
     936       58108 :                 slash[1] = 0;
     937             :         else
     938           0 :                 dst[0] = 0;
     939       58108 : }
     940             : 
     941    51890547 : int pathcmp(const char* a, const char* b)
     942             : {
     943             : #ifdef _WIN32
     944             :         char ai[PATH_MAX];
     945             :         char bi[PATH_MAX];
     946             : 
     947             :         /* import to convert \ to / */
     948             :         pathimport(ai, sizeof(ai), a);
     949             :         pathimport(bi, sizeof(bi), b);
     950             : 
     951             :         /* case insensitive compare in Windows */
     952             :         return stricmp(ai, bi);
     953             : #else
     954    51890547 :         return strcmp(a, b);
     955             : #endif
     956             : }
     957             : 
     958             : /****************************************************************************/
     959             : /* file-system */
     960             : 
     961      787754 : int mkancestor(const char* file)
     962             : {
     963             :         char dir[PATH_MAX];
     964             :         struct stat st;
     965             :         char* c;
     966             : 
     967      787754 :         pathcpy(dir, sizeof(dir), file);
     968             : 
     969      787754 :         c = strrchr(dir, '/');
     970      787754 :         if (!c) {
     971             :                 /* no ancestor */
     972           0 :                 return 0;
     973             :         }
     974             : 
     975             :         /* clear the file */
     976      787754 :         *c = 0;
     977             : 
     978             :         /* if it's the root dir */
     979      787754 :         if (*dir == 0) {
     980             :                 /* nothing more to do */
     981           0 :                 return 0;
     982             :         }
     983             : 
     984             : #ifdef _WIN32
     985             :         /* if it's a drive specification like "C:" */
     986             :         if (isalpha(dir[0]) && dir[1] == ':' && dir[2] == 0) {
     987             :                 /* nothing more to do */
     988             :                 return 0;
     989             :         }
     990             : #endif
     991             : 
     992             :         /*
     993             :          * Check if the dir already exists using lstat().
     994             :          *
     995             :          * Note that in Windows when dealing with read-only media
     996             :          * you cannot try to create the directory, and expecting
     997             :          * the EEXIST error because the call will fail with ERROR_WRITE_PROTECTED.
     998             :          *
     999             :          * Also in Windows it's better to use lstat() than stat() because it
    1000             :          * doesn't need to open the dir with CreateFile().
    1001             :          */
    1002      787754 :         if (lstat(dir, &st) == 0) {
    1003             :                 /* it already exists */
    1004      787676 :                 return 0;
    1005             :         }
    1006             : 
    1007             :         /* recursively create them all */
    1008          78 :         if (mkancestor(dir) != 0) {
    1009             :                 /* LCOV_EXCL_START */
    1010             :                 return -1;
    1011             :                 /* LCOV_EXCL_STOP */
    1012             :         }
    1013             : 
    1014             :         /* create it */
    1015          78 :         if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
    1016             :                 /* LCOV_EXCL_START */
    1017             :                 log_fatal("Error creating directory '%s'. %s.\n", dir, strerror(errno));
    1018             :                 return -1;
    1019             :                 /* LCOV_EXCL_STOP */
    1020             :         }
    1021             : 
    1022          78 :         return 0;
    1023             : }
    1024             : 
    1025      121399 : int fmtime(int f, int64_t mtime_sec, int mtime_nsec)
    1026             : {
    1027             : #if HAVE_FUTIMENS
    1028             :         struct timespec tv[2];
    1029             : #else
    1030             :         struct timeval tv[2];
    1031             : #endif
    1032             :         int ret;
    1033             : 
    1034             : #if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */
    1035      121399 :         tv[0].tv_sec = mtime_sec;
    1036      121399 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1037      121399 :                 tv[0].tv_nsec = mtime_nsec;
    1038             :         else
    1039           0 :                 tv[0].tv_nsec = 0;
    1040      121399 :         tv[1].tv_sec = tv[0].tv_sec;
    1041      121399 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1042             : 
    1043      121399 :         ret = futimens(f, tv);
    1044             : #elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */
    1045             :         tv[0].tv_sec = mtime_sec;
    1046             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1047             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1048             :         else
    1049             :                 tv[0].tv_usec = 0;
    1050             :         tv[1].tv_sec = tv[0].tv_sec;
    1051             :         tv[1].tv_usec = tv[0].tv_usec;
    1052             : 
    1053             :         ret = futimes(f, tv);
    1054             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1055             :         tv[0].tv_sec = mtime_sec;
    1056             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1057             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1058             :         else
    1059             :                 tv[0].tv_usec = 0;
    1060             :         tv[1].tv_sec = tv[0].tv_sec;
    1061             :         tv[1].tv_usec = tv[0].tv_usec;
    1062             : 
    1063             :         ret = futimesat(f, 0, tv);
    1064             : #else
    1065             : #error No function available to set file timestamps with sub-second precision
    1066             : #endif
    1067             : 
    1068      121399 :         return ret;
    1069             : }
    1070             : 
    1071       11645 : int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec)
    1072             : {
    1073             : #if HAVE_UTIMENSAT
    1074             :         struct timespec tv[2];
    1075             : #else
    1076             :         struct timeval tv[2];
    1077             : #endif
    1078             :         int ret;
    1079             : 
    1080             : #if HAVE_UTIMENSAT /* utimensat() is preferred because it gives nanosecond precision */
    1081       11645 :         tv[0].tv_sec = mtime_sec;
    1082       11645 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1083       11645 :                 tv[0].tv_nsec = mtime_nsec;
    1084             :         else
    1085           0 :                 tv[0].tv_nsec = 0;
    1086       11645 :         tv[1].tv_sec = tv[0].tv_sec;
    1087       11645 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1088             : 
    1089       11645 :         ret = utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW);
    1090             : #elif HAVE_LUTIMES /* fallback to lutimes() if nanosecond precision is not available */
    1091             :         tv[0].tv_sec = mtime_sec;
    1092             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1093             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1094             :         else
    1095             :                 tv[0].tv_usec = 0;
    1096             :         tv[1].tv_sec = tv[0].tv_sec;
    1097             :         tv[1].tv_usec = tv[0].tv_usec;
    1098             : 
    1099             :         ret = lutimes(path, tv);
    1100             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1101             :         tv[0].tv_sec = mtime_sec;
    1102             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1103             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1104             :         else
    1105             :                 tv[0].tv_usec = 0;
    1106             :         tv[1].tv_sec = tv[0].tv_sec;
    1107             :         tv[1].tv_usec = tv[0].tv_usec;
    1108             : 
    1109             :         ret = futimesat(AT_FDCWD, path, tv);
    1110             : #else
    1111             : #error No function available to set file timestamps with sub-second precision
    1112             : #endif
    1113             : 
    1114       11645 :         return ret;
    1115             : }
    1116             : 
    1117             : /****************************************************************************/
    1118             : /* advise */
    1119             : 
    1120     2492189 : void advise_init(struct advise_struct* advise, int mode)
    1121             : {
    1122     2492189 :         advise->mode = mode;
    1123     2492189 :         advise->dirty_begin = 0;
    1124     2492189 :         advise->dirty_end = 0;
    1125     2492189 : }
    1126             : 
    1127     2492189 : int advise_flags(struct advise_struct* advise)
    1128             : {
    1129     2492189 :         int flags = 0;
    1130             : 
    1131     2492189 :         if (advise->mode == ADVISE_SEQUENTIAL
    1132     2480847 :                 || advise->mode == ADVISE_FLUSH
    1133     2480847 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1134     2469505 :                 || advise->mode == ADVISE_DISCARD
    1135     2469505 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1136             :         )
    1137       34026 :                 flags |= O_SEQUENTIAL;
    1138             : 
    1139             : #if HAVE_DIRECT_IO
    1140     2492189 :         if (advise->mode == ADVISE_DIRECT)
    1141           0 :                 flags |= O_DIRECT;
    1142             : #endif
    1143             : 
    1144     2492189 :         return flags;
    1145             : }
    1146             : 
    1147     2413532 : int advise_open(struct advise_struct* advise, int f)
    1148             : {
    1149             :         (void)advise;
    1150             :         (void)f;
    1151             : 
    1152             : #if HAVE_POSIX_FADVISE
    1153     2413532 :         if (advise->mode == ADVISE_SEQUENTIAL
    1154     2402190 :                 || advise->mode == ADVISE_FLUSH
    1155     2402190 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1156     2390848 :                 || advise->mode == ADVISE_DISCARD
    1157     2390848 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1158             :         ) {
    1159             :                 int ret;
    1160             : 
    1161             :                 /* advise noreuse access, this avoids to pollute the page cache */
    1162             :                 /* supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a */
    1163       34026 :                 ret = posix_fadvise(f, 0, 0, POSIX_FADV_NOREUSE);
    1164       34026 :                 if (ret == ENOSYS) {
    1165             :                         /* call is not supported */
    1166           0 :                         ret = 0;
    1167             :                 }
    1168       34026 :                 if (ret != 0) {
    1169             :                         /* LCOV_EXCL_START */
    1170             :                         errno = ret; /* posix_fadvise return the error code */
    1171             :                         return -1;
    1172             :                         /* LCOV_EXCL_STOP */
    1173             :                 }
    1174             : 
    1175             :                 /* advise sequential access, this doubles the read-ahead window size */
    1176       34026 :                 ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
    1177       34026 :                 if (ret == ENOSYS) {
    1178             :                         /* call is not supported, like in armhf, see posix_fadvise manpage */
    1179           0 :                         ret = 0;
    1180             :                 }
    1181       34026 :                 if (ret != 0) {
    1182             :                         /* LCOV_EXCL_START */
    1183             :                         errno = ret; /* posix_fadvise return the error code */
    1184             :                         return -1;
    1185             :                         /* LCOV_EXCL_STOP */
    1186             :                 }
    1187             :         }
    1188             : #endif
    1189             : 
    1190     2413532 :         return 0;
    1191             : }
    1192             : 
    1193      894657 : int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1194             : {
    1195             :         data_off_t flush_offset;
    1196             :         data_off_t flush_size;
    1197             :         data_off_t discard_offset;
    1198             :         data_off_t discard_size;
    1199             : 
    1200             :         (void)f;
    1201             :         (void)flush_offset;
    1202             :         (void)flush_size;
    1203             :         (void)discard_offset;
    1204             :         (void)discard_size;
    1205             : 
    1206      894657 :         flush_offset = 0;
    1207      894657 :         flush_size = 0;
    1208      894657 :         discard_offset = 0;
    1209      894657 :         discard_size = 0;
    1210             : 
    1211             :         /*
    1212             :          * Follow Linus recommendations about fast writes.
    1213             :          *
    1214             :          * Linus "Unexpected splice "always copy" behavior observed"
    1215             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988070
    1216             :          * ---
    1217             :          * I have had _very_ good experiences with even a rather trivial
    1218             :          * file writer that basically used (iirc) 8MB windows, and the logic was very
    1219             :          * trivial:
    1220             :          *
    1221             :          *  - before writing a new 8M window, do "start writeback"
    1222             :          *    (SYNC_FILE_RANGE_WRITE) on the previous window, and do
    1223             :          *    a wait (SYNC_FILE_RANGE_WAIT_AFTER) on the window before that.
    1224             :          *
    1225             :          * in fact, in its simplest form, you can do it like this (this is from my
    1226             :          * "overwrite disk images" program that I use on old disks):
    1227             :          *
    1228             :          * for (index = 0; index < max_index ;index++) {
    1229             :          *   if (write(fd, buffer, BUFSIZE) != BUFSIZE)
    1230             :          *     break;
    1231             :          *   // This won't block, but will start writeout asynchronously
    1232             :          *   sync_file_range(fd, index*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WRITE);
    1233             :          *   // This does a blocking write-and-wait on any old ranges
    1234             :          *   if (index)
    1235             :          *     sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1236             :          * }
    1237             :          *
    1238             :          * and even if you don't actually do a discard (maybe we should add a
    1239             :          * SYNC_FILE_RANGE_DISCARD bit, right now you'd need to do a separate
    1240             :          * fadvise(FADV_DONTNEED) to throw it out) the system behavior is pretty
    1241             :          * nice, because the heavy writer gets good IO performance _and_ leaves only
    1242             :          * easy-to-free pages around after itself.
    1243             :          * ---
    1244             :          *
    1245             :          * Linus "Unexpected splice "always copy" behavior observed"
    1246             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988176
    1247             :          * ---
    1248             :          * The behavior for dirty page writeback is _not_ well defined, and
    1249             :          * if you do POSIX_FADV_DONTNEED, I would suggest you do it as part of that
    1250             :          * writeback logic, ie you do it only on ranges that you have just waited on.
    1251             :          *
    1252             :          * IOW, in my example, you'd couple the
    1253             :          *
    1254             :          *   sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1255             :          *
    1256             :          * with a
    1257             :          *
    1258             :          *   posix_fadvise(fd, (index-1)*BUFSIZE, BUFSIZE, POSIX_FADV_DONTNEED);
    1259             :          *
    1260             :          * afterwards to throw out the pages that you just waited for.
    1261             :          * ---
    1262             :          */
    1263             : 
    1264      894657 :         switch (advise->mode) {
    1265           0 :         case ADVISE_FLUSH :
    1266           0 :                 flush_offset = offset;
    1267           0 :                 flush_size = size;
    1268           0 :                 break;
    1269           0 :         case ADVISE_DISCARD :
    1270           0 :                 discard_offset = offset;
    1271           0 :                 discard_size = size;
    1272           0 :                 break;
    1273        4687 :         case ADVISE_FLUSH_WINDOW :
    1274             :                 /* if the dirty range can be extended */
    1275        4687 :                 if (advise->dirty_end == offset) {
    1276             :                         /* extent the dirty range */
    1277        4687 :                         advise->dirty_end += size;
    1278             : 
    1279             :                         /* if we reached the window size */
    1280        4687 :                         if (advise->dirty_end - advise->dirty_begin >= ADVISE_WINDOW_SIZE) {
    1281             :                                 /* flush the window  */
    1282           0 :                                 flush_offset = advise->dirty_begin;
    1283           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1284             : 
    1285             :                                 /* remove it from the dirty range */
    1286           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1287             :                         }
    1288             :                 } else {
    1289             :                         /* otherwise flush the existing dirty */
    1290           0 :                         flush_offset = advise->dirty_begin;
    1291           0 :                         flush_size = advise->dirty_end - advise->dirty_begin;
    1292             : 
    1293             :                         /* and set the new range as dirty */
    1294           0 :                         advise->dirty_begin = offset;
    1295           0 :                         advise->dirty_end = offset + size;
    1296             :                 }
    1297        4687 :                 break;
    1298        4687 :         case ADVISE_DISCARD_WINDOW :
    1299             :                 /* if the dirty range can be extended */
    1300        4687 :                 if (advise->dirty_end == offset) {
    1301             :                         /* extent the dirty range */
    1302        4687 :                         advise->dirty_end += size;
    1303             : 
    1304             :                         /* if we reached the double window size */
    1305        4687 :                         if (advise->dirty_end - advise->dirty_begin >= 2 * ADVISE_WINDOW_SIZE) {
    1306             :                                 /* discard the first window */
    1307           0 :                                 discard_offset = advise->dirty_begin;
    1308           0 :                                 discard_size = ADVISE_WINDOW_SIZE;
    1309             : 
    1310             :                                 /* remove it from the dirty range */
    1311           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1312             : 
    1313             :                                 /* flush the second window */
    1314           0 :                                 flush_offset = advise->dirty_begin;
    1315           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1316             :                         }
    1317             :                 } else {
    1318             :                         /* otherwise discard the existing dirty */
    1319           0 :                         discard_offset = advise->dirty_begin;
    1320           0 :                         discard_size = advise->dirty_end - advise->dirty_begin;
    1321             : 
    1322             :                         /* and set the new range as dirty */
    1323           0 :                         advise->dirty_begin = offset;
    1324           0 :                         advise->dirty_end = offset + size;
    1325             :                 }
    1326        4687 :                 break;
    1327             :         }
    1328             : 
    1329             : #if HAVE_SYNC_FILE_RANGE
    1330      894657 :         if (flush_size != 0) {
    1331             :                 int ret;
    1332             : 
    1333             :                 /* start writing immediately */
    1334           0 :                 ret = sync_file_range(f, flush_offset, flush_size, SYNC_FILE_RANGE_WRITE);
    1335           0 :                 if (ret != 0) {
    1336             :                         /* LCOV_EXCL_START */
    1337             :                         return -1;
    1338             :                         /* LCOV_EXCL_STOP */
    1339             :                 }
    1340             :         }
    1341             : #endif
    1342             : 
    1343             : #if HAVE_SYNC_FILE_RANGE && HAVE_POSIX_FADVISE
    1344      894657 :         if (discard_size != 0) {
    1345             :                 int ret;
    1346             : 
    1347             :                 /* send the data to the disk and wait until it's written */
    1348           0 :                 ret = sync_file_range(f, discard_offset, discard_size, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
    1349           0 :                 if (ret != 0) {
    1350             :                         /* LCOV_EXCL_START */
    1351             :                         return -1;
    1352             :                         /* LCOV_EXCL_STOP */
    1353             :                 }
    1354             : 
    1355             :                 /* flush the data from the cache */
    1356           0 :                 ret = posix_fadvise(f, discard_offset, discard_size, POSIX_FADV_DONTNEED);
    1357             :                 /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
    1358           0 :                 if (ret != 0) {
    1359             :                         /* LCOV_EXCL_START */
    1360             :                         errno = ret; /* posix_fadvise return the error code */
    1361             :                         return -1;
    1362             :                         /* LCOV_EXCL_STOP */
    1363             :                 }
    1364             :         }
    1365             : #endif
    1366             : 
    1367      894657 :         return 0;
    1368             : }
    1369             : 
    1370     8845172 : int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1371             : {
    1372             :         (void)advise;
    1373             :         (void)f;
    1374             :         (void)offset;
    1375             :         (void)size;
    1376             : 
    1377             : #if HAVE_POSIX_FADVISE
    1378     8845172 :         if (advise->mode == ADVISE_DISCARD
    1379     8845172 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1380             :         ) {
    1381             :                 int ret;
    1382             : 
    1383             :                 /* flush the data from the cache */
    1384       27825 :                 ret = posix_fadvise(f, offset, size, POSIX_FADV_DONTNEED);
    1385             :                 /* for POSIX_FADV_DONTNEED we don't allow failure with ENOSYS */
    1386       27825 :                 if (ret != 0) {
    1387             :                         /* LCOV_EXCL_START */
    1388             :                         errno = ret; /* posix_fadvise return the error code */
    1389             :                         return -1;
    1390             :                         /* LCOV_EXCL_STOP */
    1391             :                 }
    1392             :         }
    1393             : #endif
    1394             : 
    1395             :         /*
    1396             :          * Here we cannot call posix_fadvise(..., POSIX_FADV_WILLNEED) for the next block
    1397             :          * because it may be blocking.
    1398             :          *
    1399             :          * Ted Ts'o "posix_fadvise(POSIX_FADV_WILLNEED) waits before returning?"
    1400             :          * https://lkml.org/lkml/2010/12/6/122
    1401             :          * ---
    1402             :          * readahead and posix_fadvise(POSIX_FADV_WILLNEED) work exactly the same
    1403             :          * way, and in fact share mostly the same code path (see
    1404             :          * force_page_cache_readahead() in mm/readahead.c).
    1405             :          *
    1406             :          * They are asynchronous in that there is no guarantee the pages will be
    1407             :          * in the page cache by the time they return.  But at the same time, they
    1408             :          * are not guaranteed to be non-blocking.  That is, the work of doing the
    1409             :          * readahead does not take place in a kernel thread.  So if you try to
    1410             :          * request I/O than will fit in the request queue, the system call will
    1411             :          * block until some I/O is completed so that more I/O requested cam be
    1412             :          * loaded onto the request queue.
    1413             :          *
    1414             :          * The only way to fix this would be to either put the work on a kernel
    1415             :          * thread (i.e., some kind of workqueue) or in a userspace thread.  For
    1416             :          * ion programmer wondering what to do today, I'd suggest the
    1417             :          * latter since it will be more portable across various kernel versions.
    1418             :          *
    1419             :          * This does leave the question about whether we should change the kernel
    1420             :          * to allow readahead() and posix_fadvise(POSIX_FADV_WILLNEED) to be
    1421             :          * non-blocking and do this work in a workqueue (or via some kind of
    1422             :          * callback/continuation scheme).  My worry is just doing this if a user
    1423             :          * application does something crazy, like request gigabytes and gigabytes
    1424             :          * of readahead, and then repented of their craziness, there should be a
    1425             :          * way of cancelling the readahead request.  Today, the user can just
    1426             :          * kill the application.  But if we simply shove the work to a kernel
    1427             :          * thread, it becomes a lot harder to cancel the readahead request.  We'd
    1428             :          * have to invent a new API, and then have a way to know whether the user
    1429             :          * has access to kill a particular readahead request, etc.
    1430             :          * ---
    1431             :          */
    1432             : 
    1433     8845172 :         return 0;
    1434             : }
    1435             : 
    1436             : /****************************************************************************/
    1437             : /* memory */
    1438             : 
    1439             : /**
    1440             :  * Total amount of memory allocated.
    1441             :  */
    1442             : static size_t mcounter;
    1443             : 
    1444         478 : size_t malloc_counter_get(void)
    1445             : {
    1446             :         size_t ret;
    1447             : 
    1448         478 :         lock_memory();
    1449             : 
    1450         478 :         ret = mcounter;
    1451             : 
    1452         478 :         unlock_memory();
    1453             : 
    1454         478 :         return ret;
    1455             : }
    1456             : 
    1457    20386043 : void malloc_counter_inc(size_t inc)
    1458             : {
    1459    20386043 :         lock_memory();
    1460             : 
    1461    20386043 :         mcounter += inc;
    1462             : 
    1463    20386043 :         unlock_memory();
    1464    20386043 : }
    1465             : 
    1466             : /* LCOV_EXCL_START */
    1467             : static ssize_t malloc_print(int f, const char* str)
    1468             : {
    1469             :         ssize_t len = 0;
    1470             : 
    1471             :         while (str[len])
    1472             :                 ++len;
    1473             :         return write(f, str, len);
    1474             : }
    1475             : /* LCOV_EXCL_STOP */
    1476             : 
    1477             : /* LCOV_EXCL_START */
    1478             : static ssize_t malloc_printn(int f, size_t value)
    1479             : {
    1480             :         char buf[32];
    1481             :         int i;
    1482             : 
    1483             :         if (!value)
    1484             :                 return write(f, "0", 1);
    1485             : 
    1486             :         i = sizeof(buf);
    1487             :         while (value) {
    1488             :                 buf[--i] = (value % 10) + '0';
    1489             :                 value /= 10;
    1490             :         }
    1491             : 
    1492             :         return write(f, buf + i, sizeof(buf) - i);
    1493             : }
    1494             : /* LCOV_EXCL_STOP */
    1495             : 
    1496             : /* LCOV_EXCL_START */
    1497             : void malloc_fail(size_t size)
    1498             : {
    1499             :         /* don't use printf to avoid any possible extra allocation */
    1500             :         int f = 2; /* stderr */
    1501             : 
    1502             :         malloc_print(f, "Failed for Low Memory!\n");
    1503             :         malloc_print(f, "Allocating ");
    1504             :         malloc_printn(f, size);
    1505             :         malloc_print(f, " bytes.\n");
    1506             :         malloc_print(f, "Already allocated ");
    1507             :         malloc_printn(f, malloc_counter_get());
    1508             :         malloc_print(f, " bytes.\n");
    1509             :         if (sizeof(void*) == 4) {
    1510             :                 malloc_print(f, "You are currently using a 32 bits executable.\n");
    1511             :                 malloc_print(f, "If you have more than 4GB of memory, please upgrade to a 64 bits one.\n");
    1512             :         }
    1513             : }
    1514             : /* LCOV_EXCL_STOP */
    1515             : 
    1516    14382352 : void* malloc_nofail(size_t size)
    1517             : {
    1518    14382352 :         void* ptr = malloc(size);
    1519             : 
    1520    14382352 :         if (!ptr) {
    1521             :                 /* LCOV_EXCL_START */
    1522             :                 malloc_fail(size);
    1523             :                 exit(EXIT_FAILURE);
    1524             :                 /* LCOV_EXCL_STOP */
    1525             :         }
    1526             : 
    1527             : #ifndef CHECKER /* Don't preinitialize when running for valgrind */
    1528             :         /* Here we preinitialize the memory to ensure that the OS is really allocating it */
    1529             :         /* and not only reserving the addressable space. */
    1530             :         /* Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process. */
    1531             :         /* Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to */
    1532             :         /* get a real error from malloc() instead than a process killed. */
    1533             :         /* Note that calloc() doesn't have the same effect. */
    1534    14382352 :         memset(ptr, 0xA5, size);
    1535             : #endif
    1536             : 
    1537    14382352 :         malloc_counter_inc(size);
    1538             : 
    1539    14382352 :         return ptr;
    1540             : }
    1541             : 
    1542      155395 : void* calloc_nofail(size_t count, size_t size)
    1543             : {
    1544             :         void* ptr;
    1545             : 
    1546      155395 :         size *= count;
    1547             : 
    1548             :         /* see the note in malloc_nofail() of why we don't use calloc() */
    1549      155395 :         ptr = malloc(size);
    1550             : 
    1551      155395 :         if (!ptr) {
    1552             :                 /* LCOV_EXCL_START */
    1553             :                 malloc_fail(size);
    1554             :                 exit(EXIT_FAILURE);
    1555             :                 /* LCOV_EXCL_STOP */
    1556             :         }
    1557             : 
    1558      155395 :         memset(ptr, 0, size);
    1559             : 
    1560      155395 :         malloc_counter_inc(size);
    1561             : 
    1562      155395 :         return ptr;
    1563             : }
    1564             : 
    1565     5848296 : char* strdup_nofail(const char* str)
    1566             : {
    1567             :         size_t size;
    1568             :         char* ptr;
    1569             : 
    1570     5848296 :         size = strlen(str) + 1;
    1571             : 
    1572     5848296 :         ptr = malloc(size);
    1573             : 
    1574     5848296 :         if (!ptr) {
    1575             :                 /* LCOV_EXCL_START */
    1576             :                 malloc_fail(size);
    1577             :                 exit(EXIT_FAILURE);
    1578             :                 /* LCOV_EXCL_STOP */
    1579             :         }
    1580             : 
    1581     5848296 :         memcpy(ptr, str, size);
    1582             : 
    1583     5848296 :         malloc_counter_inc(size);
    1584             : 
    1585     5848296 :         return ptr;
    1586             : }
    1587             : 
    1588             : /****************************************************************************/
    1589             : /* smartctl */
    1590             : 
    1591             : /**
    1592             :  * Match a string with the specified pattern.
    1593             :  * Like sscanf() a space match any sequence of spaces.
    1594             :  * Return 0 if it matches.
    1595             :  */
    1596           0 : static int smatch(const char* str, const char* pattern)
    1597             : {
    1598           0 :         while (*pattern) {
    1599           0 :                 if (isspace(*pattern)) {
    1600           0 :                         ++pattern;
    1601           0 :                         while (isspace(*str))
    1602           0 :                                 ++str;
    1603           0 :                 } else if (*pattern == *str) {
    1604           0 :                         ++pattern;
    1605           0 :                         ++str;
    1606             :                 } else
    1607           0 :                         return -1;
    1608             :         }
    1609             : 
    1610           0 :         return 0;
    1611             : }
    1612             : 
    1613           0 : int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* smart, char* serial, char* vendor, char* model)
    1614             : {
    1615             :         unsigned i;
    1616             :         int inside;
    1617             : 
    1618             :         /* preclear attribute */
    1619           0 :         *serial = 0;
    1620           0 :         for (i = 0; i < SMART_COUNT; ++i)
    1621           0 :                 smart[i] = SMART_UNASSIGNED;
    1622             : 
    1623             :         /* read the file */
    1624           0 :         inside = 0;
    1625           0 :         while (1) {
    1626             :                 char buf[256];
    1627             :                 unsigned id;
    1628             :                 uint64_t raw;
    1629             :                 char* s;
    1630             : 
    1631           0 :                 s = fgets(buf, sizeof(buf), f);
    1632           0 :                 if (s == 0)
    1633           0 :                         break;
    1634             : 
    1635             :                 /* remove extraneous chars */
    1636           0 :                 s = strpolish(buf);
    1637             : 
    1638           0 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    1639             : 
    1640             :                 /* skip initial spaces */
    1641           0 :                 while (isspace(*s))
    1642           0 :                         ++s;
    1643             : 
    1644           0 :                 if (*s == 0) {
    1645           0 :                         inside = 0;
    1646             :                 /* common */
    1647           0 :                 } else if (smatch(s, "Rotation Rate: Solid State") == 0) {
    1648           0 :                         smart[SMART_ROTATION_RATE] = 0;
    1649           0 :                 } else if (sscanf(s, "Rotation Rate: %" SCNu64, &smart[SMART_ROTATION_RATE]) == 1) {
    1650           0 :                 } else if (smatch(s, "User Capacity:") == 0) {
    1651           0 :                         char* begin = strchr(s, ':');
    1652           0 :                         char* end = strstr(s, "bytes");
    1653           0 :                         if (begin != 0 && end != 0 && begin < end) {
    1654             :                                 char* p;
    1655           0 :                                 smart[SMART_SIZE] = 0;
    1656           0 :                                 for (p = begin; p != end; ++p) {
    1657           0 :                                         if (isdigit(*p)) {
    1658           0 :                                                 smart[SMART_SIZE] *= 10;
    1659           0 :                                                 smart[SMART_SIZE] += *p - '0';
    1660             :                                         }
    1661             :                                 }
    1662             :                         }
    1663           0 :                 } else if (sscanf(s, "Device Model: %63s %63s", vendor, model) == 2) {
    1664           0 :                 } else if (sscanf(s, "Device Model: %63s", model) == 1) {
    1665             :                 /* SCSI */
    1666           0 :                 } else if (sscanf(s, "Serial number: %63s", serial) == 1) { /* note "n" of "number" lower case */
    1667           0 :                 } else if (sscanf(s, "Elements in grown defect list: %" SCNu64, &smart[SMART_REALLOCATED_SECTOR_COUNT]) == 1) {
    1668           0 :                 } else if (sscanf(s, "Current Drive Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS]) == 1) {
    1669           0 :                 } else if (sscanf(s, "Drive Trip Temperature: %" SCNu64, &smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS]) == 1) {
    1670           0 :                 } else if (sscanf(s, "Accumulated start-stop cycles: %" SCNu64, &smart[SMART_START_STOP_COUNT]) == 1) {
    1671           0 :                 } else if (sscanf(s, "  number of hours powered up = %" SCNu64, &smart[SMART_POWER_ON_HOURS]) == 1) {
    1672             :                 /* ATA */
    1673           0 :                 } else if (sscanf(s, "Serial Number: %63s", serial) == 1) {
    1674           0 :                 } else if (smatch(s, "ID#") == 0) {
    1675           0 :                         inside = 1;
    1676           0 :                 } else if (smatch(s, "No Errors Logged") == 0) {
    1677           0 :                         smart[SMART_ERROR] = 0;
    1678           0 :                 } else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) {
    1679           0 :                         smart[SMART_ERROR] = raw;
    1680           0 :                 } else if (inside) {
    1681           0 :                         if (sscanf(s, "%u %*s %*s %*s %*s %*s %*s %*s %*s %" SCNu64, &id, &raw) != 2) {
    1682             :                                 /* LCOV_EXCL_START */
    1683             :                                 log_fatal("Invalid smartctl line '%s'.\n", s);
    1684             :                                 return -1;
    1685             :                                 /* LCOV_EXCL_STOP */
    1686             :                         }
    1687             : 
    1688           0 :                         if (id >= 256) {
    1689             :                                 /* LCOV_EXCL_START */
    1690             :                                 log_fatal("Invalid SMART id '%u'.\n", id);
    1691             :                                 return -1;
    1692             :                                 /* LCOV_EXCL_STOP */
    1693             :                         }
    1694             : 
    1695           0 :                         smart[id] = raw;
    1696             :                 }
    1697             :         }
    1698             : 
    1699           0 :         return 0;
    1700             : }
    1701             : 
    1702           5 : int smartctl_flush(FILE* f, const char* file, const char* name)
    1703             : {
    1704             :         /* read the file */
    1705           0 :         while (1) {
    1706             :                 char buf[256];
    1707             :                 char* s;
    1708             : 
    1709           5 :                 s = fgets(buf, sizeof(buf), f);
    1710           5 :                 if (s == 0)
    1711           5 :                         break;
    1712             : 
    1713             :                 /* remove extraneous chars */
    1714           0 :                 s = strpolish(buf);
    1715             : 
    1716           0 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    1717             :         }
    1718             : 
    1719           5 :         return 0;
    1720             : }
    1721             : 
    1722             : /****************************************************************************/
    1723             : /* thread */
    1724             : 
    1725             : #if HAVE_THREAD
    1726        2876 : void thread_mutex_init(thread_mutex_t* mutex)
    1727             : {
    1728        2876 :         if (pthread_mutex_init(mutex, 0) != 0) {
    1729             :                 /* LCOV_EXCL_START */
    1730             :                 log_fatal("Failed call to pthread_mutex_init().\n");
    1731             :                 os_abort();
    1732             :                 /* LCOV_EXCL_STOP */
    1733             :         }
    1734        2876 : }
    1735             : 
    1736        2764 : void thread_mutex_destroy(thread_mutex_t* mutex)
    1737             : {
    1738        2764 :         if (pthread_mutex_destroy(mutex) != 0) {
    1739             :                 /* LCOV_EXCL_START */
    1740             :                 log_fatal("Failed call to pthread_mutex_destroy().\n");
    1741             :                 os_abort();
    1742             :                 /* LCOV_EXCL_STOP */
    1743             :         }
    1744        2764 : }
    1745             : 
    1746    34950547 : void thread_mutex_lock(thread_mutex_t* mutex)
    1747             : {
    1748    34950547 :         if (pthread_mutex_lock(mutex) != 0) {
    1749             :                 /* LCOV_EXCL_START */
    1750             :                 log_fatal("Failed call to pthread_mutex_lock().\n");
    1751             :                 os_abort();
    1752             :                 /* LCOV_EXCL_STOP */
    1753             :         }
    1754    34950547 : }
    1755             : 
    1756    34950547 : void thread_mutex_unlock(thread_mutex_t* mutex)
    1757             : {
    1758    34950547 :         if (pthread_mutex_unlock(mutex) != 0) {
    1759             :                 /* LCOV_EXCL_START */
    1760             :                 log_fatal("Failed call to pthread_mutex_unlock().\n");
    1761             :                 os_abort();
    1762             :                 /* LCOV_EXCL_STOP */
    1763             :         }
    1764    34950547 : }
    1765             : 
    1766         384 : void thread_cond_init(thread_cond_t* cond)
    1767             : {
    1768         384 :         if (pthread_cond_init(cond, 0) != 0) {
    1769             :                 /* LCOV_EXCL_START */
    1770             :                 log_fatal("Failed call to pthread_cond_init().\n");
    1771             :                 os_abort();
    1772             :                 /* LCOV_EXCL_STOP */
    1773             :         }
    1774         384 : }
    1775             : 
    1776         384 : void thread_cond_destroy(thread_cond_t* cond)
    1777             : {
    1778         384 :         if (pthread_cond_destroy(cond) != 0) {
    1779             :                 /* LCOV_EXCL_START */
    1780             :                 log_fatal("Failed call to pthread_cond_destroy().\n");
    1781             :                 os_abort();
    1782             :                 /* LCOV_EXCL_STOP */
    1783             :         }
    1784         384 : }
    1785             : 
    1786         930 : void thread_cond_signal(thread_cond_t* cond)
    1787             : {
    1788         930 :         if (pthread_cond_signal(cond) != 0) {
    1789             :                 /* LCOV_EXCL_START */
    1790             :                 log_fatal("Failed call to pthread_cond_signal().\n");
    1791             :                 os_abort();
    1792             :                 /* LCOV_EXCL_STOP */
    1793             :         }
    1794         930 : }
    1795             : 
    1796      251122 : void thread_cond_broadcast(thread_cond_t* cond)
    1797             : {
    1798      251122 :         if (pthread_cond_broadcast(cond) != 0) {
    1799             :                 /* LCOV_EXCL_START */
    1800             :                 log_fatal("Failed call to pthread_cond_broadcast().\n");
    1801             :                 os_abort();
    1802             :                 /* LCOV_EXCL_STOP */
    1803             :         }
    1804      251122 : }
    1805             : 
    1806     1478390 : void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex)
    1807             : {
    1808     1478390 :         if (pthread_cond_wait(cond, mutex) != 0) {
    1809             :                 /* LCOV_EXCL_START */
    1810             :                 log_fatal("Failed call to pthread_cond_wait().\n");
    1811             :                 os_abort();
    1812             :                 /* LCOV_EXCL_STOP */
    1813             :         }
    1814     1478390 : }
    1815             : 
    1816             : /**
    1817             :  * Implementation note about conditional variables.
    1818             :  *
    1819             :  * The conditional variables can be signaled inside or outside the mutex,
    1820             :  * what is better it's debatable but in general doing that outside the mutex,
    1821             :  * reduces the number of context switches.
    1822             :  *
    1823             :  * But when testing with helgrind and drd, this disallows such tools to
    1824             :  * to see the dependency between the signal and the wait.
    1825             :  *
    1826             :  * To avoid it we signal everything inside the mutex. And we do this in both
    1827             :  * test mode (with CHECKER defined) and release mode (CHECKER not defined),
    1828             :  * to be on the safe side and avoid any difference in behaviour between test and
    1829             :  * release.
    1830             :  *
    1831             :  * Here some interesting discussion:
    1832             :  *
    1833             :  * Condvars: signal with mutex locked or not?
    1834             :  * http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
    1835             :  *
    1836             :  * Calling pthread_cond_signal without locking mutex
    1837             :  * http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex/4544494#4544494
    1838             :  */
    1839             : 
    1840             : /**
    1841             :  * Control when to signal the condition variables.
    1842             :  */
    1843             : int thread_cond_signal_outside = 0;
    1844             : 
    1845         930 : void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    1846             : {
    1847         930 :         if (thread_cond_signal_outside) {
    1848             :                 /* without the thread checker unlock before signaling, */
    1849             :                 /* this reduces the number of context switches */
    1850           0 :                 thread_mutex_unlock(mutex);
    1851             :         }
    1852             : 
    1853         930 :         thread_cond_signal(cond);
    1854             : 
    1855         930 :         if (!thread_cond_signal_outside) {
    1856             :                 /* with the thread checker unlock after signaling */
    1857             :                 /* to make explicit the condition and mutex relation */
    1858         930 :                 thread_mutex_unlock(mutex);
    1859             :         }
    1860         930 : }
    1861             : 
    1862      250930 : void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    1863             : {
    1864      250930 :         if (thread_cond_signal_outside) {
    1865             :                 /* without the thread checker unlock before signaling, */
    1866             :                 /* this reduces the number of context switches */
    1867           0 :                 thread_mutex_unlock(mutex);
    1868             :         }
    1869             : 
    1870      250930 :         thread_cond_broadcast(cond);
    1871             : 
    1872      250930 :         if (!thread_cond_signal_outside) {
    1873             :                 /* with the thread checker unlock after signaling */
    1874             :                 /* to make explicit the condition and mutex relation */
    1875      250930 :                 thread_mutex_unlock(mutex);
    1876             :         }
    1877      250930 : }
    1878             : 
    1879        2709 : void thread_create(thread_id_t* thread, void* (*func)(void*), void *arg)
    1880             : {
    1881        2709 :         if (pthread_create(thread, 0, func, arg) != 0) {
    1882             :                 /* LCOV_EXCL_START */
    1883             :                 log_fatal("Failed call to pthread_create().\n");
    1884             :                 os_abort();
    1885             :                 /* LCOV_EXCL_STOP */
    1886             :         }
    1887        2709 : }
    1888             : 
    1889        2709 : void thread_join(thread_id_t thread, void** retval)
    1890             : {
    1891        2709 :         if (pthread_join(thread, retval) != 0) {
    1892             :                 /* LCOV_EXCL_START */
    1893             :                 log_fatal("Failed call to pthread_join().\n");
    1894             :                 os_abort();
    1895             :                 /* LCOV_EXCL_STOP */
    1896             :         }
    1897        2709 : }
    1898             : 
    1899      148506 : void thread_yield(void)
    1900             : {
    1901             : #ifdef __MINGW32__
    1902             :         Sleep(0);
    1903             : #else
    1904      148506 :         sched_yield();
    1905             : #endif
    1906      148506 : }
    1907             : #endif
    1908             : 

Generated by: LCOV version 1.0