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