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