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