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