119c6563a88fdc57e6ce163cf7d620f8257170fd
[strongswan.git] / src / libstrongswan / utils / utils.c
1 /*
2 * Copyright (C) 2008-2014 Tobias Brunner
3 * Copyright (C) 2005-2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE /* for memrchr */
18 #ifdef WIN32
19 /* for GetTickCount64, Windows 7 */
20 # define _WIN32_WINNT 0x0601
21 #endif
22
23 #include "utils.h"
24
25 #include <sys/stat.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <inttypes.h>
30 #include <stdint.h>
31 #include <limits.h>
32 #include <dirent.h>
33 #include <time.h>
34 #ifndef WIN32
35 # include <signal.h>
36 #endif
37
38 #include <library.h>
39 #include <utils/debug.h>
40 #include <utils/chunk.h>
41 #include <collections/enumerator.h>
42 #include <threading/spinlock.h>
43 #include <threading/mutex.h>
44 #include <threading/condvar.h>
45
46 ENUM(status_names, SUCCESS, NEED_MORE,
47 "SUCCESS",
48 "FAILED",
49 "OUT_OF_RES",
50 "ALREADY_DONE",
51 "NOT_SUPPORTED",
52 "INVALID_ARG",
53 "NOT_FOUND",
54 "PARSE_ERROR",
55 "VERIFY_ERROR",
56 "INVALID_STATE",
57 "DESTROY_ME",
58 "NEED_MORE",
59 );
60
61 /**
62 * Described in header.
63 */
64 void* malloc_align(size_t size, u_int8_t align)
65 {
66 u_int8_t pad;
67 void *ptr;
68
69 if (align == 0)
70 {
71 align = 1;
72 }
73 ptr = malloc(align + sizeof(pad) + size);
74 if (!ptr)
75 {
76 return NULL;
77 }
78 /* store padding length just before data, down to the allocation boundary
79 * to do some verification during free_align() */
80 pad = align - ((uintptr_t)ptr % align);
81 memset(ptr, pad, pad);
82 return ptr + pad;
83 }
84
85 /**
86 * Described in header.
87 */
88 void free_align(void *ptr)
89 {
90 u_int8_t pad, *pos;
91
92 pos = ptr - 1;
93 /* verify padding to check any corruption */
94 for (pad = *pos; (void*)pos >= ptr - pad; pos--)
95 {
96 if (*pos != pad)
97 {
98 DBG1(DBG_LIB, "!!!! invalid free_align() !!!!");
99 return;
100 }
101 }
102 free(ptr - pad);
103 }
104
105 /**
106 * Described in header.
107 */
108 void memxor(u_int8_t dst[], u_int8_t src[], size_t n)
109 {
110 int m, i;
111
112 /* byte wise XOR until dst aligned */
113 for (i = 0; (uintptr_t)&dst[i] % sizeof(long) && i < n; i++)
114 {
115 dst[i] ^= src[i];
116 }
117 /* try to use words if src shares an aligment with dst */
118 switch (((uintptr_t)&src[i] % sizeof(long)))
119 {
120 case 0:
121 for (m = n - sizeof(long); i <= m; i += sizeof(long))
122 {
123 *(long*)&dst[i] ^= *(long*)&src[i];
124 }
125 break;
126 case sizeof(int):
127 for (m = n - sizeof(int); i <= m; i += sizeof(int))
128 {
129 *(int*)&dst[i] ^= *(int*)&src[i];
130 }
131 break;
132 case sizeof(short):
133 for (m = n - sizeof(short); i <= m; i += sizeof(short))
134 {
135 *(short*)&dst[i] ^= *(short*)&src[i];
136 }
137 break;
138 default:
139 break;
140 }
141 /* byte wise XOR of the rest */
142 for (; i < n; i++)
143 {
144 dst[i] ^= src[i];
145 }
146 }
147
148 /**
149 * Described in header.
150 */
151 void memwipe_noinline(void *ptr, size_t n)
152 {
153 memwipe_inline(ptr, n);
154 }
155
156 /**
157 * Described in header.
158 */
159 bool memeq_const(const void *x, const void *y, size_t len)
160 {
161 const u_char *a, *b;
162 u_int bad = 0;
163 size_t i;
164
165 a = (const u_char*)x;
166 b = (const u_char*)y;
167
168 for (i = 0; i < len; i++)
169 {
170 bad |= a[i] != b[i];
171 }
172 return !bad;
173 }
174
175 /**
176 * Described in header.
177 */
178 void *memstr(const void *haystack, const char *needle, size_t n)
179 {
180 const u_char *pos = haystack;
181 size_t l;
182
183 if (!haystack || !needle || (l = strlen(needle)) == 0)
184 {
185 return NULL;
186 }
187 for (; n >= l; ++pos, --n)
188 {
189 if (memeq(pos, needle, l))
190 {
191 return (void*)pos;
192 }
193 }
194 return NULL;
195 }
196
197 /**
198 * Described in header.
199 */
200 void *utils_memrchr(const void *s, int c, size_t n)
201 {
202 const u_char *pos;
203
204 if (!s || !n)
205 {
206 return NULL;
207 }
208
209 for (pos = s + n - 1; pos >= (u_char*)s; pos--)
210 {
211 if (*pos == (u_char)c)
212 {
213 return (void*)pos;
214 }
215 }
216 return NULL;
217 }
218
219 /**
220 * Described in header.
221 */
222 char* translate(char *str, const char *from, const char *to)
223 {
224 char *pos = str;
225 if (strlen(from) != strlen(to))
226 {
227 return str;
228 }
229 while (pos && *pos)
230 {
231 char *match;
232 if ((match = strchr(from, *pos)) != NULL)
233 {
234 *pos = to[match - from];
235 }
236 pos++;
237 }
238 return str;
239 }
240
241 /**
242 * Described in header.
243 */
244 char* strreplace(const char *str, const char *search, const char *replace)
245 {
246 size_t len, slen, rlen, count = 0;
247 char *res, *pos, *found, *dst;
248
249 if (!str || !*str || !search || !*search || !replace)
250 {
251 return (char*)str;
252 }
253 slen = strlen(search);
254 rlen = strlen(replace);
255 if (slen != rlen)
256 {
257 for (pos = (char*)str; (pos = strstr(pos, search)); pos += slen)
258 {
259 found = pos;
260 count++;
261 }
262 if (!count)
263 {
264 return (char*)str;
265 }
266 len = (found - str) + strlen(found) + count * (rlen - slen);
267 }
268 else
269 {
270 len = strlen(str);
271 }
272 found = strstr(str, search);
273 if (!found)
274 {
275 return (char*)str;
276 }
277 dst = res = malloc(len + 1);
278 pos = (char*)str;
279 do
280 {
281 len = found - pos;
282 memcpy(dst, pos, len);
283 dst += len;
284 memcpy(dst, replace, rlen);
285 dst += rlen;
286 pos = found + slen;
287 }
288 while ((found = strstr(pos, search)));
289 strcpy(dst, pos);
290 return res;
291 }
292
293 #ifdef WIN32
294
295 /**
296 * Flag to indicate signaled wait_sigint()
297 */
298 static bool sigint_signaled = FALSE;
299
300 /**
301 * Condvar to wait in wait_sigint()
302 */
303 static condvar_t *sigint_cond;
304
305 /**
306 * Mutex to check signaling()
307 */
308 static mutex_t *sigint_mutex;
309
310 /**
311 * Control handler to catch ^C
312 */
313 static BOOL WINAPI handler(DWORD dwCtrlType)
314 {
315 switch (dwCtrlType)
316 {
317 case CTRL_C_EVENT:
318 case CTRL_BREAK_EVENT:
319 case CTRL_CLOSE_EVENT:
320 sigint_mutex->lock(sigint_mutex);
321 sigint_signaled = TRUE;
322 sigint_cond->signal(sigint_cond);
323 sigint_mutex->unlock(sigint_mutex);
324 return TRUE;
325 default:
326 return FALSE;
327 }
328 }
329
330 /**
331 * Windows variant
332 */
333 void wait_sigint()
334 {
335 SetConsoleCtrlHandler(handler, TRUE);
336
337 sigint_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
338 sigint_cond = condvar_create(CONDVAR_TYPE_DEFAULT);
339
340 sigint_mutex->lock(sigint_mutex);
341 while (!sigint_signaled)
342 {
343 sigint_cond->wait(sigint_cond, sigint_mutex);
344 }
345 sigint_mutex->unlock(sigint_mutex);
346
347 sigint_mutex->destroy(sigint_mutex);
348 sigint_cond->destroy(sigint_cond);
349 }
350
351 #else /* !WIN32 */
352
353 /**
354 * Unix variant
355 */
356 void wait_sigint()
357 {
358 sigset_t set;
359 int sig;
360
361 sigemptyset(&set);
362 sigaddset(&set, SIGINT);
363 sigaddset(&set, SIGTERM);
364
365 sigprocmask(SIG_BLOCK, &set, NULL);
366 sigwait(&set, &sig);
367 }
368
369 #endif
370
371 /**
372 * Described in header.
373 */
374 char* path_dirname(const char *path)
375 {
376 char *pos;
377
378 pos = path ? strrchr(path, DIRECTORY_SEPARATOR[0]) : NULL;
379
380 if (pos && !pos[1])
381 { /* if path ends with slashes we have to look beyond them */
382 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
383 { /* skip trailing slashes */
384 pos--;
385 }
386 pos = memrchr(path, DIRECTORY_SEPARATOR[0], pos - path + 1);
387 }
388 if (!pos)
389 {
390 #ifdef WIN32
391 if (path && strlen(path))
392 {
393 if ((isalpha(path[0]) && path[1] == ':'))
394 { /* if just a drive letter given, return that as dirname */
395 return chunk_clone(chunk_from_chars(path[0], ':', 0)).ptr;
396 }
397 }
398 #endif
399 return strdup(".");
400 }
401 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
402 { /* skip superfluous slashes */
403 pos--;
404 }
405 return strndup(path, pos - path + 1);
406 }
407
408 /**
409 * Described in header.
410 */
411 char* path_basename(const char *path)
412 {
413 char *pos, *trail = NULL;
414
415 if (!path || !*path)
416 {
417 return strdup(".");
418 }
419 pos = strrchr(path, DIRECTORY_SEPARATOR[0]);
420 if (pos && !pos[1])
421 { /* if path ends with slashes we have to look beyond them */
422 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
423 { /* skip trailing slashes */
424 pos--;
425 }
426 if (pos == path && *pos == DIRECTORY_SEPARATOR[0])
427 { /* contains only slashes */
428 return strdup(DIRECTORY_SEPARATOR);
429 }
430 trail = pos + 1;
431 pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path);
432 }
433 pos = pos ? pos + 1 : (char*)path;
434 return trail ? strndup(pos, trail - pos) : strdup(pos);
435 }
436
437 /**
438 * Described in header.
439 */
440 bool path_absolute(const char *path)
441 {
442 if (!path)
443 {
444 return FALSE;
445 }
446 #ifdef WIN32
447 if (strpfx(path, "\\\\"))
448 { /* UNC */
449 return TRUE;
450 }
451 if (strlen(path) && isalpha(path[0]) && path[1] == ':')
452 { /* drive letter */
453 return TRUE;
454 }
455 #else /* !WIN32 */
456 if (path[0] == DIRECTORY_SEPARATOR[0])
457 {
458 return TRUE;
459 }
460 #endif
461 return FALSE;
462 }
463
464 /**
465 * Described in header.
466 */
467 bool mkdir_p(const char *path, mode_t mode)
468 {
469 int len;
470 char *pos, full[PATH_MAX];
471 pos = full;
472 if (!path || *path == '\0')
473 {
474 return TRUE;
475 }
476 len = snprintf(full, sizeof(full)-1, "%s", path);
477 if (len < 0 || len >= sizeof(full)-1)
478 {
479 DBG1(DBG_LIB, "path string %s too long", path);
480 return FALSE;
481 }
482 /* ensure that the path ends with a '/' */
483 if (full[len-1] != '/')
484 {
485 full[len++] = '/';
486 full[len] = '\0';
487 }
488 /* skip '/' at the beginning */
489 while (*pos == '/')
490 {
491 pos++;
492 }
493 while ((pos = strchr(pos, '/')))
494 {
495 *pos = '\0';
496 if (access(full, F_OK) < 0)
497 {
498 #ifdef WIN32
499 if (_mkdir(full) < 0)
500 #else
501 if (mkdir(full, mode) < 0)
502 #endif
503 {
504 DBG1(DBG_LIB, "failed to create directory %s", full);
505 return FALSE;
506 }
507 }
508 *pos = '/';
509 pos++;
510 }
511 return TRUE;
512 }
513
514 ENUM(tty_color_names, TTY_RESET, TTY_BG_DEF,
515 "\e[0m",
516 "\e[1m",
517 "\e[4m",
518 "\e[5m",
519 "\e[30m",
520 "\e[31m",
521 "\e[32m",
522 "\e[33m",
523 "\e[34m",
524 "\e[35m",
525 "\e[36m",
526 "\e[37m",
527 "\e[39m",
528 "\e[40m",
529 "\e[41m",
530 "\e[42m",
531 "\e[43m",
532 "\e[44m",
533 "\e[45m",
534 "\e[46m",
535 "\e[47m",
536 "\e[49m",
537 );
538
539 /**
540 * Get the escape string for a given TTY color, empty string on non-tty FILE
541 */
542 char* tty_escape_get(int fd, tty_escape_t escape)
543 {
544 if (!isatty(fd))
545 {
546 return "";
547 }
548 switch (escape)
549 {
550 case TTY_RESET:
551 case TTY_BOLD:
552 case TTY_UNDERLINE:
553 case TTY_BLINKING:
554 #ifdef WIN32
555 return "";
556 #endif
557 case TTY_FG_BLACK:
558 case TTY_FG_RED:
559 case TTY_FG_GREEN:
560 case TTY_FG_YELLOW:
561 case TTY_FG_BLUE:
562 case TTY_FG_MAGENTA:
563 case TTY_FG_CYAN:
564 case TTY_FG_WHITE:
565 case TTY_FG_DEF:
566 case TTY_BG_BLACK:
567 case TTY_BG_RED:
568 case TTY_BG_GREEN:
569 case TTY_BG_YELLOW:
570 case TTY_BG_BLUE:
571 case TTY_BG_MAGENTA:
572 case TTY_BG_CYAN:
573 case TTY_BG_WHITE:
574 case TTY_BG_DEF:
575 return enum_to_name(tty_color_names, escape);
576 /* warn if a escape code is missing */
577 }
578 return "";
579 }
580
581 #ifndef HAVE_CLOSEFROM
582 /**
583 * Described in header.
584 */
585 void closefrom(int lowfd)
586 {
587 char fd_dir[PATH_MAX];
588 int maxfd, fd, len;
589
590 /* try to close only open file descriptors on Linux... */
591 len = snprintf(fd_dir, sizeof(fd_dir), "/proc/%u/fd", getpid());
592 if (len > 0 && len < sizeof(fd_dir) && access(fd_dir, F_OK) == 0)
593 {
594 enumerator_t *enumerator = enumerator_create_directory(fd_dir);
595 if (enumerator)
596 {
597 char *rel;
598 while (enumerator->enumerate(enumerator, &rel, NULL, NULL))
599 {
600 fd = atoi(rel);
601 if (fd >= lowfd)
602 {
603 close(fd);
604 }
605 }
606 enumerator->destroy(enumerator);
607 return;
608 }
609 }
610
611 /* ...fall back to closing all fds otherwise */
612 #ifdef WIN32
613 maxfd = _getmaxstdio();
614 #else
615 maxfd = (int)sysconf(_SC_OPEN_MAX);
616 #endif
617 if (maxfd < 0)
618 {
619 maxfd = 256;
620 }
621 for (fd = lowfd; fd < maxfd; fd++)
622 {
623 close(fd);
624 }
625 }
626 #endif /* HAVE_CLOSEFROM */
627
628 /**
629 * Return monotonic time
630 */
631 time_t time_monotonic(timeval_t *tv)
632 {
633 #ifdef WIN32
634 ULONGLONG ms;
635 time_t s;
636
637 ms = GetTickCount64();
638 s = ms / 1000;
639 if (tv)
640 {
641 tv->tv_sec = s;
642 tv->tv_usec = (ms - (s * 1000)) * 1000;
643 }
644 return s;
645 #else /* !WIN32 */
646 #if defined(HAVE_CLOCK_GETTIME) && \
647 (defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \
648 defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
649 /* as we use time_monotonic() for condvar operations, we use the
650 * monotonic time source only if it is also supported by pthread. */
651 timespec_t ts;
652
653 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
654 {
655 if (tv)
656 {
657 tv->tv_sec = ts.tv_sec;
658 tv->tv_usec = ts.tv_nsec / 1000;
659 }
660 return ts.tv_sec;
661 }
662 #endif /* HAVE_CLOCK_GETTIME && (...) */
663 /* Fallback to non-monotonic timestamps:
664 * On MAC OS X, creating monotonic timestamps is rather difficult. We
665 * could use mach_absolute_time() and catch sleep/wakeup notifications.
666 * We stick to the simpler (non-monotonic) gettimeofday() for now.
667 * But keep in mind: we need the same time source here as in condvar! */
668 if (!tv)
669 {
670 return time(NULL);
671 }
672 if (gettimeofday(tv, NULL) != 0)
673 { /* should actually never fail if passed pointers are valid */
674 return -1;
675 }
676 return tv->tv_sec;
677 #endif /* !WIN32 */
678 }
679
680 /**
681 * return null
682 */
683 void *return_null()
684 {
685 return NULL;
686 }
687
688 /**
689 * returns TRUE
690 */
691 bool return_true()
692 {
693 return TRUE;
694 }
695
696 /**
697 * returns FALSE
698 */
699 bool return_false()
700 {
701 return FALSE;
702 }
703
704 /**
705 * returns FAILED
706 */
707 status_t return_failed()
708 {
709 return FAILED;
710 }
711
712 /**
713 * returns SUCCESS
714 */
715 status_t return_success()
716 {
717 return SUCCESS;
718 }
719
720 /**
721 * nop operation
722 */
723 void nop()
724 {
725 }
726
727 #if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
728
729 /**
730 * Spinlock for ref_get/put
731 */
732 static spinlock_t *ref_lock;
733
734 /**
735 * Increase refcount
736 */
737 refcount_t ref_get(refcount_t *ref)
738 {
739 refcount_t current;
740
741 ref_lock->lock(ref_lock);
742 current = ++(*ref);
743 ref_lock->unlock(ref_lock);
744
745 return current;
746 }
747
748 /**
749 * Decrease refcount
750 */
751 bool ref_put(refcount_t *ref)
752 {
753 bool more_refs;
754
755 ref_lock->lock(ref_lock);
756 more_refs = --(*ref) > 0;
757 ref_lock->unlock(ref_lock);
758 return !more_refs;
759 }
760
761 /**
762 * Current refcount
763 */
764 refcount_t ref_cur(refcount_t *ref)
765 {
766 refcount_t current;
767
768 ref_lock->lock(ref_lock);
769 current = *ref;
770 ref_lock->unlock(ref_lock);
771
772 return current;
773 }
774
775 /**
776 * Spinlock for all compare and swap operations.
777 */
778 static spinlock_t *cas_lock;
779
780 /**
781 * Compare and swap if equal to old value
782 */
783 #define _cas_impl(name, type) \
784 bool cas_##name(type *ptr, type oldval, type newval) \
785 { \
786 bool swapped; \
787 cas_lock->lock(cas_lock); \
788 if ((swapped = (*ptr == oldval))) { *ptr = newval; } \
789 cas_lock->unlock(cas_lock); \
790 return swapped; \
791 }
792
793 _cas_impl(bool, bool)
794 _cas_impl(ptr, void*)
795
796 #endif /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
797
798
799 #ifdef HAVE_FMEMOPEN_FALLBACK
800
801 static int fmemread(chunk_t *cookie, char *buf, int size)
802 {
803 int len;
804
805 len = min(size, cookie->len);
806 memcpy(buf, cookie->ptr, len);
807 *cookie = chunk_skip(*cookie, len);
808
809 return len;
810 }
811
812 static int fmemwrite(chunk_t *cookie, const char *buf, int size)
813 {
814 int len;
815
816 len = min(size, cookie->len);
817 memcpy(cookie->ptr, buf, len);
818 *cookie = chunk_skip(*cookie, len);
819
820 return len;
821 }
822
823 static int fmemclose(void *cookie)
824 {
825 free(cookie);
826 return 0;
827 }
828
829 FILE *fmemopen(void *buf, size_t size, const char *mode)
830 {
831 chunk_t *cookie;
832
833 INIT(cookie,
834 .ptr = buf,
835 .len = size,
836 );
837
838 return funopen(cookie, (void*)fmemread, (void*)fmemwrite, NULL, fmemclose);
839 }
840
841 #endif /* FMEMOPEN fallback*/
842
843 /**
844 * See header
845 */
846 void utils_init()
847 {
848 #ifdef WIN32
849 windows_init();
850 #endif
851
852 #if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
853 ref_lock = spinlock_create();
854 cas_lock = spinlock_create();
855 #endif
856
857 strerror_init();
858 }
859
860 /**
861 * See header
862 */
863 void utils_deinit()
864 {
865 #ifdef WIN32
866 windows_deinit();
867 #endif
868
869 #if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
870 ref_lock->destroy(ref_lock);
871 cas_lock->destroy(cas_lock);
872 #endif
873
874 strerror_deinit();
875 }
876
877 /**
878 * Described in header.
879 */
880 int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
881 const void *const *args)
882 {
883 static const char* months[] = {
884 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
885 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
886 };
887 time_t *time = *((time_t**)(args[0]));
888 bool utc = *((int*)(args[1]));
889 struct tm t, *ret = NULL;
890
891 if (*time != UNDEFINED_TIME)
892 {
893 if (utc)
894 {
895 ret = gmtime_r(time, &t);
896 }
897 else
898 {
899 ret = localtime_r(time, &t);
900 }
901 }
902 if (ret == NULL)
903 {
904 return print_in_hook(data, "--- -- --:--:--%s----",
905 utc ? " UTC " : " ");
906 }
907 return print_in_hook(data, "%s %02d %02d:%02d:%02d%s%04d",
908 months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,
909 t.tm_sec, utc ? " UTC " : " ", t.tm_year + 1900);
910 }
911
912 /**
913 * Described in header.
914 */
915 int time_delta_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
916 const void *const *args)
917 {
918 char* unit = "second";
919 time_t *arg1 = *((time_t**)(args[0]));
920 time_t *arg2 = *((time_t**)(args[1]));
921 u_int64_t delta = llabs(*arg1 - *arg2);
922
923 if (delta > 2 * 60 * 60 * 24)
924 {
925 delta /= 60 * 60 * 24;
926 unit = "day";
927 }
928 else if (delta > 2 * 60 * 60)
929 {
930 delta /= 60 * 60;
931 unit = "hour";
932 }
933 else if (delta > 2 * 60)
934 {
935 delta /= 60;
936 unit = "minute";
937 }
938 return print_in_hook(data, "%" PRIu64 " %s%s", delta, unit,
939 (delta == 1) ? "" : "s");
940 }
941
942 /**
943 * Number of bytes per line to dump raw data
944 */
945 #define BYTES_PER_LINE 16
946
947 static char hexdig_upper[] = "0123456789ABCDEF";
948
949 /**
950 * Described in header.
951 */
952 int mem_printf_hook(printf_hook_data_t *data,
953 printf_hook_spec_t *spec, const void *const *args)
954 {
955 char *bytes = *((void**)(args[0]));
956 u_int len = *((int*)(args[1]));
957
958 char buffer[BYTES_PER_LINE * 3];
959 char ascii_buffer[BYTES_PER_LINE + 1];
960 char *buffer_pos = buffer;
961 char *bytes_pos = bytes;
962 char *bytes_roof = bytes + len;
963 int line_start = 0;
964 int i = 0;
965 int written = 0;
966
967 written += print_in_hook(data, "=> %u bytes @ %p", len, bytes);
968
969 while (bytes_pos < bytes_roof)
970 {
971 *buffer_pos++ = hexdig_upper[(*bytes_pos >> 4) & 0xF];
972 *buffer_pos++ = hexdig_upper[ *bytes_pos & 0xF];
973
974 ascii_buffer[i++] =
975 (*bytes_pos > 31 && *bytes_pos < 127) ? *bytes_pos : '.';
976
977 if (++bytes_pos == bytes_roof || i == BYTES_PER_LINE)
978 {
979 int padding = 3 * (BYTES_PER_LINE - i);
980
981 while (padding--)
982 {
983 *buffer_pos++ = ' ';
984 }
985 *buffer_pos++ = '\0';
986 ascii_buffer[i] = '\0';
987
988 written += print_in_hook(data, "\n%4d: %s %s",
989 line_start, buffer, ascii_buffer);
990
991 buffer_pos = buffer;
992 line_start += BYTES_PER_LINE;
993 i = 0;
994 }
995 else
996 {
997 *buffer_pos++ = ' ';
998 }
999 }
1000 return written;
1001 }