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