atomics: Move atomics/recounting support to separate files
[strongswan.git] / src / libstrongswan / utils / utils.c
index 30084cd..8a52d04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2012 Tobias Brunner
+ * Copyright (C) 2008-2014 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
  * for more details.
  */
 
+#define _GNU_SOURCE /* for memrchr */
+#ifdef WIN32
+/* for GetTickCount64, Windows 7 */
+# define _WIN32_WINNT 0x0601
+#endif
+
 #include "utils.h"
 
 #include <sys/stat.h>
 #include <limits.h>
 #include <dirent.h>
 #include <time.h>
-#include <pthread.h>
+#ifndef WIN32
+# include <signal.h>
+#endif
 
-#include "collections/enumerator.h"
-#include "utils/debug.h"
+#include <library.h>
+#include <utils/debug.h>
+#include <utils/chunk.h>
+#include <collections/enumerator.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
 
 ENUM(status_names, SUCCESS, NEED_MORE,
        "SUCCESS",
@@ -48,6 +60,50 @@ ENUM(status_names, SUCCESS, NEED_MORE,
 /**
  * Described in header.
  */
+void* malloc_align(size_t size, u_int8_t align)
+{
+       u_int8_t pad;
+       void *ptr;
+
+       if (align == 0)
+       {
+               align = 1;
+       }
+       ptr = malloc(align + sizeof(pad) + size);
+       if (!ptr)
+       {
+               return NULL;
+       }
+       /* store padding length just before data, down to the allocation boundary
+        * to do some verification during free_align() */
+       pad = align - ((uintptr_t)ptr % align);
+       memset(ptr, pad, pad);
+       return ptr + pad;
+}
+
+/**
+ * Described in header.
+ */
+void free_align(void *ptr)
+{
+       u_int8_t pad, *pos;
+
+       pos = ptr - 1;
+       /* verify padding to check any corruption */
+       for (pad = *pos; (void*)pos >= ptr - pad; pos--)
+       {
+               if (*pos != pad)
+               {
+                       DBG1(DBG_LIB, "!!!! invalid free_align() !!!!");
+                       return;
+               }
+       }
+       free(ptr - pad);
+}
+
+/**
+ * Described in header.
+ */
 void memxor(u_int8_t dst[], u_int8_t src[], size_t n)
 {
        int m, i;
@@ -99,9 +155,28 @@ void memwipe_noinline(void *ptr, size_t n)
 /**
  * Described in header.
  */
+bool memeq_const(const void *x, const void *y, size_t len)
+{
+       const u_char *a, *b;
+       u_int bad = 0;
+       size_t i;
+
+       a = (const u_char*)x;
+       b = (const u_char*)y;
+
+       for (i = 0; i < len; i++)
+       {
+               bad |= a[i] != b[i];
+       }
+       return !bad;
+}
+
+/**
+ * Described in header.
+ */
 void *memstr(const void *haystack, const char *needle, size_t n)
 {
-       unsigned const char *pos = haystack;
+       const u_char *pos = haystack;
        size_t l;
 
        if (!haystack || !needle || (l = strlen(needle)) == 0)
@@ -121,6 +196,28 @@ void *memstr(const void *haystack, const char *needle, size_t n)
 /**
  * Described in header.
  */
+void *utils_memrchr(const void *s, int c, size_t n)
+{
+       const u_char *pos;
+
+       if (!s || !n)
+       {
+               return NULL;
+       }
+
+       for (pos = s + n - 1; pos >= (u_char*)s; pos--)
+       {
+               if (*pos == (u_char)c)
+               {
+                       return (void*)pos;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * Described in header.
+ */
 char* translate(char *str, const char *from, const char *to)
 {
        char *pos = str;
@@ -143,6 +240,229 @@ char* translate(char *str, const char *from, const char *to)
 /**
  * Described in header.
  */
+char* strreplace(const char *str, const char *search, const char *replace)
+{
+       size_t len, slen, rlen, count = 0;
+       char *res, *pos, *found, *dst;
+
+       if (!str || !*str || !search || !*search || !replace)
+       {
+               return (char*)str;
+       }
+       slen = strlen(search);
+       rlen = strlen(replace);
+       if (slen != rlen)
+       {
+               for (pos = (char*)str; (pos = strstr(pos, search)); pos += slen)
+               {
+                       found = pos;
+                       count++;
+               }
+               if (!count)
+               {
+                       return (char*)str;
+               }
+               len = (found - str) + strlen(found) + count * (rlen - slen);
+       }
+       else
+       {
+               len = strlen(str);
+       }
+       found = strstr(str, search);
+       if (!found)
+       {
+               return (char*)str;
+       }
+       dst = res = malloc(len + 1);
+       pos = (char*)str;
+       do
+       {
+               len = found - pos;
+               memcpy(dst, pos, len);
+               dst += len;
+               memcpy(dst, replace, rlen);
+               dst += rlen;
+               pos = found + slen;
+       }
+       while ((found = strstr(pos, search)));
+       strcpy(dst, pos);
+       return res;
+}
+
+#ifdef WIN32
+
+/**
+ * Flag to indicate signaled wait_sigint()
+ */
+static bool sigint_signaled = FALSE;
+
+/**
+ * Condvar to wait in wait_sigint()
+ */
+static condvar_t *sigint_cond;
+
+/**
+ * Mutex to check signaling()
+ */
+static mutex_t *sigint_mutex;
+
+/**
+ * Control handler to catch ^C
+ */
+static BOOL WINAPI handler(DWORD dwCtrlType)
+{
+       switch (dwCtrlType)
+       {
+               case CTRL_C_EVENT:
+               case CTRL_BREAK_EVENT:
+               case CTRL_CLOSE_EVENT:
+                       sigint_mutex->lock(sigint_mutex);
+                       sigint_signaled = TRUE;
+                       sigint_cond->signal(sigint_cond);
+                       sigint_mutex->unlock(sigint_mutex);
+                       return TRUE;
+               default:
+                       return FALSE;
+       }
+}
+
+/**
+ * Windows variant
+ */
+void wait_sigint()
+{
+       SetConsoleCtrlHandler(handler, TRUE);
+
+       sigint_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+       sigint_cond = condvar_create(CONDVAR_TYPE_DEFAULT);
+
+       sigint_mutex->lock(sigint_mutex);
+       while (!sigint_signaled)
+       {
+               sigint_cond->wait(sigint_cond, sigint_mutex);
+       }
+       sigint_mutex->unlock(sigint_mutex);
+
+       sigint_mutex->destroy(sigint_mutex);
+       sigint_cond->destroy(sigint_cond);
+}
+
+#else /* !WIN32 */
+
+/**
+ * Unix variant
+ */
+void wait_sigint()
+{
+       sigset_t set;
+       int sig;
+
+       sigemptyset(&set);
+       sigaddset(&set, SIGINT);
+       sigaddset(&set, SIGTERM);
+
+       sigprocmask(SIG_BLOCK, &set, NULL);
+       sigwait(&set, &sig);
+}
+
+#endif
+
+/**
+ * Described in header.
+ */
+char* path_dirname(const char *path)
+{
+       char *pos;
+
+       pos = path ? strrchr(path, DIRECTORY_SEPARATOR[0]) : NULL;
+
+       if (pos && !pos[1])
+       {       /* if path ends with slashes we have to look beyond them */
+               while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
+               {       /* skip trailing slashes */
+                       pos--;
+               }
+               pos = memrchr(path, DIRECTORY_SEPARATOR[0], pos - path + 1);
+       }
+       if (!pos)
+       {
+#ifdef WIN32
+               if (path && strlen(path))
+               {
+                       if ((isalpha(path[0]) && path[1] == ':'))
+                       {       /* if just a drive letter given, return that as dirname */
+                               return chunk_clone(chunk_from_chars(path[0], ':', 0)).ptr;
+                       }
+               }
+#endif
+               return strdup(".");
+       }
+       while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
+       {       /* skip superfluous slashes */
+               pos--;
+       }
+       return strndup(path, pos - path + 1);
+}
+
+/**
+ * Described in header.
+ */
+char* path_basename(const char *path)
+{
+       char *pos, *trail = NULL;
+
+       if (!path || !*path)
+       {
+               return strdup(".");
+       }
+       pos = strrchr(path, DIRECTORY_SEPARATOR[0]);
+       if (pos && !pos[1])
+       {       /* if path ends with slashes we have to look beyond them */
+               while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
+               {       /* skip trailing slashes */
+                       pos--;
+               }
+               if (pos == path && *pos == DIRECTORY_SEPARATOR[0])
+               {       /* contains only slashes */
+                       return strdup(DIRECTORY_SEPARATOR);
+               }
+               trail = pos + 1;
+               pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path);
+       }
+       pos = pos ? pos + 1 : (char*)path;
+       return trail ? strndup(pos, trail - pos) : strdup(pos);
+}
+
+/**
+ * Described in header.
+ */
+bool path_absolute(const char *path)
+{
+       if (!path)
+       {
+               return FALSE;
+       }
+#ifdef WIN32
+       if (strpfx(path, "\\\\"))
+       {       /* UNC */
+               return TRUE;
+       }
+       if (strlen(path) && isalpha(path[0]) && path[1] == ':')
+       {       /* drive letter */
+               return TRUE;
+       }
+#else /* !WIN32 */
+       if (path[0] == DIRECTORY_SEPARATOR[0])
+       {
+               return TRUE;
+       }
+#endif
+       return FALSE;
+}
+
+/**
+ * Described in header.
+ */
 bool mkdir_p(const char *path, mode_t mode)
 {
        int len;
@@ -174,7 +494,11 @@ bool mkdir_p(const char *path, mode_t mode)
                *pos = '\0';
                if (access(full, F_OK) < 0)
                {
+#ifdef WIN32
+                       if (_mkdir(full) < 0)
+#else
                        if (mkdir(full, mode) < 0)
+#endif
                        {
                                DBG1(DBG_LIB, "failed to create directory %s", full);
                                return FALSE;
@@ -226,6 +550,9 @@ char* tty_escape_get(int fd, tty_escape_t escape)
                case TTY_BOLD:
                case TTY_UNDERLINE:
                case TTY_BLINKING:
+#ifdef WIN32
+                       return "";
+#endif
                case TTY_FG_BLACK:
                case TTY_FG_RED:
                case TTY_FG_GREEN:
@@ -245,89 +572,11 @@ char* tty_escape_get(int fd, tty_escape_t escape)
                case TTY_BG_WHITE:
                case TTY_BG_DEF:
                        return enum_to_name(tty_color_names, escape);
-               /* warn if a excape code is missing */
+               /* warn if a escape code is missing */
        }
        return "";
 }
 
-/**
- * The size of the thread-specific error buffer
- */
-#define STRERROR_BUF_LEN 256
-
-/**
- * Key to store thread-specific error buffer
- */
-static pthread_key_t strerror_buf_key;
-
-/**
- * Only initialize the key above once
- */
-static pthread_once_t strerror_buf_key_once = PTHREAD_ONCE_INIT;
-
-/**
- * Create the key used for the thread-specific error buffer
- */
-static void create_strerror_buf_key()
-{
-       pthread_key_create(&strerror_buf_key, free);
-}
-
-/**
- * Retrieve the error buffer assigned to the current thread (or create it)
- */
-static inline char *get_strerror_buf()
-{
-       char *buf;
-
-       pthread_once(&strerror_buf_key_once, create_strerror_buf_key);
-       buf = pthread_getspecific(strerror_buf_key);
-       if (!buf)
-       {
-               buf = malloc(STRERROR_BUF_LEN);
-               pthread_setspecific(strerror_buf_key, buf);
-       }
-       return buf;
-}
-
-#ifdef HAVE_STRERROR_R
-/*
- * Described in header.
- */
-const char *safe_strerror(int errnum)
-{
-       char *buf = get_strerror_buf(), *msg;
-
-#ifdef STRERROR_R_CHAR_P
-       /* char* version which may or may not return the original buffer */
-       msg = strerror_r(errnum, buf, STRERROR_BUF_LEN);
-#else
-       /* int version returns 0 on success */
-       msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf;
-#endif
-       return msg;
-}
-#else /* HAVE_STRERROR_R */
-/* we actually wan't to call strerror(3) below */
-#undef strerror
-/*
- * Described in header.
- */
-const char *safe_strerror(int errnum)
-{
-       static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-       char *buf = get_strerror_buf();
-
-       /* use a mutex to ensure calling strerror(3) is thread-safe */
-       pthread_mutex_lock(&mutex);
-       strncpy(buf, strerror(errnum), STRERROR_BUF_LEN);
-       pthread_mutex_unlock(&mutex);
-       buf[STRERROR_BUF_LEN - 1] = '\0';
-       return buf;
-}
-#endif /* HAVE_STRERROR_R */
-
-
 #ifndef HAVE_CLOSEFROM
 /**
  * Described in header.
@@ -359,7 +608,11 @@ void closefrom(int lowfd)
        }
 
        /* ...fall back to closing all fds otherwise */
+#ifdef WIN32
+       maxfd = _getmaxstdio();
+#else
        maxfd = (int)sysconf(_SC_OPEN_MAX);
+#endif
        if (maxfd < 0)
        {
                maxfd = 256;
@@ -376,6 +629,19 @@ void closefrom(int lowfd)
  */
 time_t time_monotonic(timeval_t *tv)
 {
+#ifdef WIN32
+       ULONGLONG ms;
+       time_t s;
+
+       ms = GetTickCount64();
+       s = ms / 1000;
+       if (tv)
+       {
+               tv->tv_sec = s;
+               tv->tv_usec = (ms - (s * 1000)) * 1000;
+       }
+       return s;
+#else /* !WIN32 */
 #if defined(HAVE_CLOCK_GETTIME) && \
        (defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \
         defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
@@ -407,6 +673,7 @@ time_t time_monotonic(timeval_t *tv)
                return -1;
        }
        return tv->tv_sec;
+#endif /* !WIN32 */
 }
 
 /**
@@ -456,63 +723,74 @@ void nop()
 {
 }
 
-#ifndef HAVE_GCC_ATOMIC_OPERATIONS
+#ifdef HAVE_FMEMOPEN_FALLBACK
 
-/**
- * We use a single mutex for all refcount variables.
- */
-static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int fmemread(chunk_t *cookie, char *buf, int size)
+{
+       int len;
 
-/**
- * Increase refcount
- */
-refcount_t ref_get(refcount_t *ref)
+       len = min(size, cookie->len);
+       memcpy(buf, cookie->ptr, len);
+       *cookie = chunk_skip(*cookie, len);
+
+       return len;
+}
+
+static int fmemwrite(chunk_t *cookie, const char *buf, int size)
 {
-       refcount_t current;
+       int len;
 
-       pthread_mutex_lock(&ref_mutex);
-       current = ++(*ref);
-       pthread_mutex_unlock(&ref_mutex);
+       len = min(size, cookie->len);
+       memcpy(cookie->ptr, buf, len);
+       *cookie = chunk_skip(*cookie, len);
 
-       return current;
+       return len;
 }
 
-/**
- * Decrease refcount
- */
-bool ref_put(refcount_t *ref)
+static int fmemclose(void *cookie)
 {
-       bool more_refs;
+       free(cookie);
+       return 0;
+}
 
-       pthread_mutex_lock(&ref_mutex);
-       more_refs = --(*ref) > 0;
-       pthread_mutex_unlock(&ref_mutex);
-       return !more_refs;
+FILE *fmemopen(void *buf, size_t size, const char *mode)
+{
+       chunk_t *cookie;
+
+       INIT(cookie,
+               .ptr = buf,
+               .len = size,
+       );
+
+       return funopen(cookie, (void*)fmemread, (void*)fmemwrite, NULL, fmemclose);
 }
 
+#endif /* FMEMOPEN fallback*/
+
 /**
- * Single mutex for all compare and swap operations.
+ * See header
  */
-static pthread_mutex_t cas_mutex = PTHREAD_MUTEX_INITIALIZER;
+void utils_init()
+{
+#ifdef WIN32
+       windows_init();
+#endif
+       atomics_init();
+       strerror_init();
+}
 
 /**
- * Compare and swap if equal to old value
+ * See header
  */
-#define _cas_impl(name, type) \
-bool cas_##name(type *ptr, type oldval, type newval) \
-{ \
-       bool swapped; \
-       pthread_mutex_lock(&cas_mutex); \
-       if ((swapped = (*ptr == oldval))) { *ptr = newval; } \
-       pthread_mutex_unlock(&cas_mutex); \
-       return swapped; \
+void utils_deinit()
+{
+#ifdef WIN32
+       windows_deinit();
+#endif
+       atomics_deinit();
+       strerror_deinit();
 }
 
-_cas_impl(bool, bool)
-_cas_impl(ptr, void*)
-
-#endif /* HAVE_GCC_ATOMIC_OPERATIONS */
-
 /**
  * Described in header.
  */
@@ -524,21 +802,24 @@ int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };
        time_t *time = *((time_t**)(args[0]));
-       bool utc = *((bool*)(args[1]));;
-       struct tm t;
+       bool utc = *((int*)(args[1]));
+       struct tm t, *ret = NULL;
 
-       if (*time == UNDEFINED_TIME)
-       {
-               return print_in_hook(data, "--- -- --:--:--%s----",
-                                                        utc ? " UTC " : " ");
-       }
-       if (utc)
+       if (*time != UNDEFINED_TIME)
        {
-               gmtime_r(time, &t);
+               if (utc)
+               {
+                       ret = gmtime_r(time, &t);
+               }
+               else
+               {
+                       ret = localtime_r(time, &t);
+               }
        }
-       else
+       if (ret == NULL)
        {
-               localtime_r(time, &t);
+               return print_in_hook(data, "--- -- --:--:--%s----",
+                                                        utc ? " UTC " : " ");
        }
        return print_in_hook(data, "%s %02d %02d:%02d:%02d%s%04d",
                                                 months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,