BuildSystem: Remove unused variables.
[asterisk/asterisk.git] / main / utils.c
index 8997305..85228c1 100644 (file)
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include <ctype.h>
-#include <sys/stat.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_DEV_URANDOM
 #include <fcntl.h>
-#endif
-
+#include <sys/stat.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 #if defined(__APPLE__)
 #include <mach/mach.h>
 #elif defined(HAVE_SYS_THR_H)
@@ -47,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 
 #include "asterisk/network.h"
+#include "asterisk/ast_version.h"
 
 #define AST_API_MODULE         /* ensure that inlinable API functions will be built in lock.h if required */
 #include "asterisk/lock.h"
@@ -55,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/sha1.h"
 #include "asterisk/cli.h"
 #include "asterisk/linkedlists.h"
+#include "asterisk/astobj2.h"
 
 #define AST_API_MODULE         /* ensure that inlinable API functions will be built in this module if required */
 #include "asterisk/strings.h"
@@ -63,9 +59,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/time.h"
 
 #define AST_API_MODULE         /* ensure that inlinable API functions will be built in this module if required */
-#include "asterisk/stringfields.h"
-
-#define AST_API_MODULE         /* ensure that inlinable API functions will be built in this module if required */
 #include "asterisk/utils.h"
 
 #define AST_API_MODULE
@@ -74,6 +67,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define AST_API_MODULE
 #include "asterisk/config.h"
 
+#define AST_API_MODULE
+#include "asterisk/alertpipe.h"
+
 static char base64[64];
 static char b2a[256];
 
@@ -253,7 +249,7 @@ void ast_md5_hash(char *output, const char *input)
        MD5Final(digest, &md5);
        ptr = output;
        for (x = 0; x < 16; x++)
-               ptr += sprintf(ptr, "%2.2x", digest[x]);
+               ptr += sprintf(ptr, "%02hhx", digest[x]);
 }
 
 /*! \brief Produce 40 char SHA1 hash of value. */
@@ -271,7 +267,7 @@ void ast_sha1_hash(char *output, const char *input)
        SHA1Result(&sha, Message_Digest);
        ptr = output;
        for (x = 0; x < 20; x++)
-               ptr += sprintf(ptr, "%2.2x", Message_Digest[x]);
+               ptr += sprintf(ptr, "%02hhx", Message_Digest[x]);
 }
 
 /*! \brief Produce a 20 byte SHA1 hash of value. */
@@ -422,7 +418,7 @@ char *ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_fl
                        if (out - outbuf >= buflen - 3) {
                                break;
                        }
-                       out += sprintf(out, "%%%02X", (unsigned char) *ptr);
+                       out += sprintf(out, "%%%02hhX", (unsigned char) *ptr);
                } else {
                        *out = *ptr;    /* Continue copying the string */
                        out++;
@@ -485,6 +481,123 @@ char *ast_escape_quoted(const char *string, char *outbuf, int buflen)
 
        return outbuf;
 }
+
+char *ast_escape_semicolons(const char *string, char *outbuf, int buflen)
+{
+       const char *ptr = string;
+       char *out = outbuf;
+
+       if (string == NULL || outbuf == NULL) {
+               ast_assert(string != NULL && outbuf != NULL);
+               return NULL;
+       }
+
+       while (*ptr && out - outbuf < buflen - 1) {
+               if (*ptr == ';') {
+                       if (out - outbuf >= buflen - 2) {
+                               break;
+                       }
+                       strcpy(out, "\\;");
+                       out += 2;
+               } else {
+                       *out = *ptr;
+                       out++;
+               }
+               ptr++;
+       }
+
+       if (buflen) {
+               *out = '\0';
+       }
+
+       return outbuf;
+}
+
+void ast_unescape_quoted(char *quote_str)
+{
+       int esc_pos;
+       int unesc_pos;
+       int quote_str_len = strlen(quote_str);
+
+       for (esc_pos = 0, unesc_pos = 0;
+               esc_pos < quote_str_len;
+               esc_pos++, unesc_pos++) {
+               if (quote_str[esc_pos] == '\\') {
+                       /* at least one more char and current is \\ */
+                       esc_pos++;
+                       if (esc_pos >= quote_str_len) {
+                               break;
+                       }
+               }
+
+               quote_str[unesc_pos] = quote_str[esc_pos];
+       }
+       quote_str[unesc_pos] = '\0';
+}
+
+int ast_xml_escape(const char *string, char * const outbuf, const size_t buflen)
+{
+       char *dst = outbuf;
+       char *end = outbuf + buflen - 1; /* save one for the null terminator */
+
+       /* Handle the case for the empty output buffer */
+       if (buflen == 0) {
+               return -1;
+       }
+
+       /* Escaping rules from http://www.w3.org/TR/REC-xml/#syntax */
+       /* This also prevents partial entities at the end of a string */
+       while (*string && dst < end) {
+               const char *entity = NULL;
+               int len = 0;
+
+               switch (*string) {
+               case '<':
+                       entity = "&lt;";
+                       len = 4;
+                       break;
+               case '&':
+                       entity = "&amp;";
+                       len = 5;
+                       break;
+               case '>':
+                       /* necessary if ]]> is in the string; easier to escape them all */
+                       entity = "&gt;";
+                       len = 4;
+                       break;
+               case '\'':
+                       /* necessary in single-quoted strings; easier to escape them all */
+                       entity = "&apos;";
+                       len = 6;
+                       break;
+               case '"':
+                       /* necessary in double-quoted strings; easier to escape them all */
+                       entity = "&quot;";
+                       len = 6;
+                       break;
+               default:
+                       *dst++ = *string++;
+                       break;
+               }
+
+               if (entity) {
+                       ast_assert(len == strlen(entity));
+                       if (end - dst < len) {
+                               /* no room for the entity; stop */
+                               break;
+                       }
+                       /* just checked for length; strcpy is fine */
+                       strcpy(dst, entity);
+                       dst += len;
+                       ++string;
+               }
+       }
+       /* Write null terminator */
+       *dst = '\0';
+       /* If any chars are left in string, return failure */
+       return *string == '\0' ? 0 : -1;
+}
+
 /*! \brief  ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
 const char *ast_inet_ntoa(struct in_addr ia)
 {
@@ -496,18 +609,15 @@ const char *ast_inet_ntoa(struct in_addr ia)
        return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
 }
 
-#ifdef HAVE_DEV_URANDOM
-static int dev_urandom_fd;
-#endif
+static int dev_urandom_fd = -1;
 
 #ifndef __linux__
 #undef pthread_create /* For ast_pthread_create function only */
 #endif /* !__linux__ */
 
-#if !defined(LOW_MEMORY)
-
 #ifdef DEBUG_THREADS
 
+#if !defined(LOW_MEMORY)
 /*! \brief A reasonable maximum number of locks a thread would be holding ... */
 #define AST_MAX_LOCKS 64
 
@@ -531,14 +641,16 @@ struct thr_lock_info {
        /*! This is the actual container of info for what locks this thread holds */
        struct {
                const char *file;
-               int line_num;
                const char *func;
                const char *lock_name;
                void *lock_addr;
                int times_locked;
+               int line_num;
                enum ast_lock_type type;
                /*! This thread is waiting on this lock */
                int pending:2;
+               /*! A condition has suspended this lock */
+               int suspended:1;
 #ifdef HAVE_BKTR
                struct ast_bt *backtrace;
 #endif
@@ -547,6 +659,8 @@ struct thr_lock_info {
         *  The index (num_locks - 1) has the info on the last one in the
         *  locks member */
        unsigned int num_locks;
+       /*! The LWP id (which GDB prints) */
+       int lwp;
        /*! Protects the contents of the locks member
         * Intentionally not ast_mutex_t */
        pthread_mutex_t lock;
@@ -596,23 +710,22 @@ static void lock_info_destroy(void *data)
        }
 
        pthread_mutex_destroy(&lock_info->lock);
-       if (lock_info->thread_name)
-               free((void *) lock_info->thread_name);
-       free(lock_info);
+       if (lock_info->thread_name) {
+               ast_free((void *) lock_info->thread_name);
+       }
+       ast_free(lock_info);
 }
 
 /*!
  * \brief The thread storage key for per-thread lock info
  */
 AST_THREADSTORAGE_CUSTOM(thread_lock_info, NULL, lock_info_destroy);
-#ifdef HAVE_BKTR
+#endif /* ! LOW_MEMORY */
+
 void ast_store_lock_info(enum ast_lock_type type, const char *filename,
        int line_num, const char *func, const char *lock_name, void *lock_addr, struct ast_bt *bt)
-#else
-void ast_store_lock_info(enum ast_lock_type type, const char *filename,
-       int line_num, const char *func, const char *lock_name, void *lock_addr)
-#endif
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
        int i;
 
@@ -663,10 +776,12 @@ void ast_store_lock_info(enum ast_lock_type type, const char *filename,
        lock_info->num_locks++;
 
        pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
 }
 
 void ast_mark_lock_acquired(void *lock_addr)
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
 
        if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
@@ -677,10 +792,12 @@ void ast_mark_lock_acquired(void *lock_addr)
                lock_info->locks[lock_info->num_locks - 1].pending = 0;
        }
        pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
 }
 
 void ast_mark_lock_failed(void *lock_addr)
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
 
        if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
@@ -692,10 +809,12 @@ void ast_mark_lock_failed(void *lock_addr)
                lock_info->locks[lock_info->num_locks - 1].times_locked--;
        }
        pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
 }
 
 int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, int *lineno, char *func, size_t func_size, char *mutex_name, size_t mutex_name_size)
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
        int i = 0;
 
@@ -723,14 +842,72 @@ int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, in
        pthread_mutex_unlock(&lock_info->lock);
 
        return 0;
+#else /* if defined(LOW_MEMORY) */
+       return -1;
+#endif
 }
 
-#ifdef HAVE_BKTR
+void ast_suspend_lock_info(void *lock_addr)
+{
+#if !defined(LOW_MEMORY)
+       struct thr_lock_info *lock_info;
+       int i = 0;
+
+       if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info)))) {
+               return;
+       }
+
+       pthread_mutex_lock(&lock_info->lock);
+
+       for (i = lock_info->num_locks - 1; i >= 0; i--) {
+               if (lock_info->locks[i].lock_addr == lock_addr)
+                       break;
+       }
+
+       if (i == -1) {
+               /* Lock not found :( */
+               pthread_mutex_unlock(&lock_info->lock);
+               return;
+       }
+
+       lock_info->locks[i].suspended = 1;
+
+       pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
+}
+
+void ast_restore_lock_info(void *lock_addr)
+{
+#if !defined(LOW_MEMORY)
+       struct thr_lock_info *lock_info;
+       int i = 0;
+
+       if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+               return;
+
+       pthread_mutex_lock(&lock_info->lock);
+
+       for (i = lock_info->num_locks - 1; i >= 0; i--) {
+               if (lock_info->locks[i].lock_addr == lock_addr)
+                       break;
+       }
+
+       if (i == -1) {
+               /* Lock not found :( */
+               pthread_mutex_unlock(&lock_info->lock);
+               return;
+       }
+
+       lock_info->locks[i].suspended = 0;
+
+       pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
+}
+
+
 void ast_remove_lock_info(void *lock_addr, struct ast_bt *bt)
-#else
-void ast_remove_lock_info(void *lock_addr)
-#endif
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
        int i = 0;
 
@@ -768,8 +945,10 @@ void ast_remove_lock_info(void *lock_addr)
        lock_info->num_locks--;
 
        pthread_mutex_unlock(&lock_info->lock);
+#endif /* ! LOW_MEMORY */
 }
 
+#if !defined(LOW_MEMORY)
 static const char *locktype2str(enum ast_lock_type type)
 {
        switch (type) {
@@ -795,6 +974,8 @@ static void append_backtrace_information(struct ast_str **str, struct ast_bt *bt
                return;
        }
 
+       /* store frame count locally to avoid the memory corruption that
+        * sometimes happens on virtualized CentOS 6.x systems */
        num_frames = bt->num_frames;
        if ((symbols = ast_bt_get_symbols(bt->addresses, num_frames))) {
                int frame_iterator;
@@ -803,7 +984,7 @@ static void append_backtrace_information(struct ast_str **str, struct ast_bt *bt
                        ast_str_append(str, 0, "\t%s\n", symbols[frame_iterator]);
                }
 
-               free(symbols);
+               ast_std_free(symbols);
        } else {
                ast_str_append(str, 0, "\tCouldn't retrieve backtrace symbols\n");
        }
@@ -816,7 +997,7 @@ static void append_lock_information(struct ast_str **str, struct thr_lock_info *
        ast_mutex_t *lock;
        struct ast_lock_track *lt;
 
-       ast_str_append(str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d)\n",
+       ast_str_append(str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d%s)\n",
                                   lock_info->locks[i].pending > 0 ? "Waiting for " :
                                   lock_info->locks[i].pending < 0 ? "Tried and failed to get " : "", i,
                                   lock_info->locks[i].file,
@@ -824,7 +1005,8 @@ static void append_lock_information(struct ast_str **str, struct thr_lock_info *
                                   lock_info->locks[i].line_num,
                                   lock_info->locks[i].func, lock_info->locks[i].lock_name,
                                   lock_info->locks[i].lock_addr,
-                                  lock_info->locks[i].times_locked);
+                                  lock_info->locks[i].times_locked,
+                                  lock_info->locks[i].suspended ? " - suspended" : "");
 #ifdef HAVE_BKTR
        append_backtrace_information(str, lock_info->locks[i].backtrace);
 #endif
@@ -845,7 +1027,7 @@ static void append_lock_information(struct ast_str **str, struct thr_lock_info *
        }
        ast_reentrancy_unlock(lt);
 }
-
+#endif /* ! LOW_MEMORY */
 
 /*! This function can help you find highly temporal locks; locks that happen for a
     short time, but at unexpected times, usually at times that create a deadlock,
@@ -866,8 +1048,9 @@ static void append_lock_information(struct ast_str **str, struct thr_lock_info *
        which will give a stack trace and continue. -- that aught to do the job!
 
 */
-void log_show_lock(void *this_lock_addr)
+void ast_log_show_lock(void *this_lock_addr)
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
        struct ast_str *str;
 
@@ -894,70 +1077,107 @@ void log_show_lock(void *this_lock_addr)
        }
        pthread_mutex_unlock(&lock_infos_lock.mutex);
        ast_free(str);
+#endif /* ! LOW_MEMORY */
 }
 
 
-static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+struct ast_str *ast_dump_locks(void)
 {
+#if !defined(LOW_MEMORY)
        struct thr_lock_info *lock_info;
        struct ast_str *str;
 
-       if (!(str = ast_str_create(4096)))
-               return CLI_FAILURE;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show locks";
-               e->usage =
-                       "Usage: core show locks\n"
-                       "       This command is for lock debugging.  It prints out which locks\n"
-                       "are owned by each active thread.\n";
-               return NULL;
-
-       case CLI_GENERATE:
+       if (!(str = ast_str_create(4096))) {
                return NULL;
        }
 
        ast_str_append(&str, 0, "\n"
                       "=======================================================================\n"
-                      "=== Currently Held Locks ==============================================\n"
+                      "=== %s\n"
+                      "=== Currently Held Locks\n"
                       "=======================================================================\n"
                       "===\n"
                       "=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"
-                      "===\n");
+                      "===\n", ast_get_version());
 
-       if (!str)
-               return CLI_FAILURE;
+       if (!str) {
+               return NULL;
+       }
 
        pthread_mutex_lock(&lock_infos_lock.mutex);
        AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
                int i;
-               if (lock_info->num_locks) {
-                       ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n", (long) lock_info->thread_id,
-                               lock_info->thread_name);
-                       pthread_mutex_lock(&lock_info->lock);
-                       for (i = 0; str && i < lock_info->num_locks; i++) {
-                               append_lock_information(&str, lock_info, i);
+               int header_printed = 0;
+               pthread_mutex_lock(&lock_info->lock);
+               for (i = 0; str && i < lock_info->num_locks; i++) {
+                       /* Don't show suspended locks */
+                       if (lock_info->locks[i].suspended) {
+                               continue;
                        }
-                       pthread_mutex_unlock(&lock_info->lock);
-                       if (!str)
-                               break;
+
+                       if (!header_printed) {
+                               if (lock_info->lwp != -1) {
+                                       ast_str_append(&str, 0, "=== Thread ID: 0x%lx LWP:%d (%s)\n",
+                                               (long unsigned) lock_info->thread_id, lock_info->lwp, lock_info->thread_name);
+                               } else {
+                                       ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n",
+                                               (long unsigned) lock_info->thread_id, lock_info->thread_name);
+                               }
+                               header_printed = 1;
+                       }
+
+                       append_lock_information(&str, lock_info, i);
+               }
+               pthread_mutex_unlock(&lock_info->lock);
+               if (!str) {
+                       break;
+               }
+               if (header_printed) {
                        ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
-                                      "===\n");
-                       if (!str)
-                               break;
+                               "===\n");
+               }
+               if (!str) {
+                       break;
                }
        }
        pthread_mutex_unlock(&lock_infos_lock.mutex);
 
-       if (!str)
-               return CLI_FAILURE;
+       if (!str) {
+               return NULL;
+       }
 
        ast_str_append(&str, 0, "=======================================================================\n"
                       "\n");
 
-       if (!str)
+       return str;
+#else /* if defined(LOW_MEMORY) */
+       return NULL;
+#endif
+}
+
+#if !defined(LOW_MEMORY)
+static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_str *str;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core show locks";
+               e->usage =
+                       "Usage: core show locks\n"
+                       "       This command is for lock debugging.  It prints out which locks\n"
+                       "are owned by each active thread.\n";
+               ast_cli_allow_at_shutdown(e);
+               return NULL;
+
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       str = ast_dump_locks();
+       if (!str) {
                return CLI_FAILURE;
+       }
 
        ast_cli(a->fd, "%s", ast_str_buffer(str));
 
@@ -969,9 +1189,10 @@ static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_
 static struct ast_cli_entry utils_cli[] = {
        AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
 };
-
+#endif /* ! LOW_MEMORY */
 #endif /* DEBUG_THREADS */
 
+#if !defined(LOW_MEMORY)
 /*
  * support for 'show threads'. The start routine is wrapped by
  * dummy_start(), so that ast_register_thread() and
@@ -997,23 +1218,13 @@ static void *dummy_start(void *data)
 #ifdef DEBUG_THREADS
        struct thr_lock_info *lock_info;
        pthread_mutexattr_t mutex_attr;
-#endif
-
-       /* note that even though data->name is a pointer to allocated memory,
-          we are not freeing it here because ast_register_thread is going to
-          keep a copy of the pointer and then ast_unregister_thread will
-          free the memory
-       */
-       ast_free(data);
-       ast_register_thread(a.name);
-       pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
 
-#ifdef DEBUG_THREADS
        if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
                return NULL;
 
        lock_info->thread_id = pthread_self();
-       lock_info->thread_name = strdup(a.name);
+       lock_info->lwp = ast_get_tid();
+       lock_info->thread_name = ast_strdup(a.name);
 
        pthread_mutexattr_init(&mutex_attr);
        pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
@@ -1025,6 +1236,15 @@ static void *dummy_start(void *data)
        pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
 #endif /* DEBUG_THREADS */
 
+       /* note that even though data->name is a pointer to allocated memory,
+          we are not freeing it here because ast_register_thread is going to
+          keep a copy of the pointer and then ast_unregister_thread will
+          free the memory
+       */
+       ast_free(data);
+       ast_register_thread(a.name);
+       pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
+
        ret = a.start_routine(a.data);
 
        pthread_cleanup_pop(1);
@@ -1034,6 +1254,15 @@ static void *dummy_start(void *data)
 
 #endif /* !LOW_MEMORY */
 
+int ast_background_stacksize(void)
+{
+#if !defined(LOW_MEMORY)
+       return AST_STACKSIZE;
+#else
+       return AST_STACKSIZE_LOW;
+#endif
+}
+
 int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
                             void *data, size_t stacksize, const char *file, const char *caller,
                             int line, const char *start_fn)
@@ -1047,8 +1276,8 @@ int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*st
                pthread_attr_init(attr);
        }
 
-#ifdef __linux__
-       /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+#if defined(__linux__) || defined(__FreeBSD__)
+       /* On Linux and FreeBSD , pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
           which is kind of useless. Change this here to
           PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
           priority will propagate down to new threads by default.
@@ -1070,9 +1299,8 @@ int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*st
                a->start_routine = start_routine;
                a->data = data;
                start_routine = dummy_start;
-               if (asprintf(&a->name, "%-20s started at [%5d] %s %s()",
+               if (ast_asprintf(&a->name, "%-20s started at [%5d] %s %s()",
                             start_fn, line, file, caller) < 0) {
-                       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
                        a->name = NULL;
                }
                data = a;
@@ -1111,13 +1339,24 @@ int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, v
 int ast_wait_for_input(int fd, int ms)
 {
        struct pollfd pfd[1];
+
+       memset(pfd, 0, sizeof(pfd));
+       pfd[0].fd = fd;
+       pfd[0].events = POLLIN | POLLPRI;
+       return ast_poll(pfd, 1, ms);
+}
+
+int ast_wait_for_output(int fd, int ms)
+{
+       struct pollfd pfd[1];
+
        memset(pfd, 0, sizeof(pfd));
        pfd[0].fd = fd;
-       pfd[0].events = POLLIN|POLLPRI;
+       pfd[0].events = POLLOUT;
        return ast_poll(pfd, 1, ms);
 }
 
-static int ast_wait_for_output(int fd, int timeoutms)
+static int wait_for_output(int fd, int timeoutms)
 {
        struct pollfd pfd = {
                .fd = fd,
@@ -1177,7 +1416,7 @@ int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
        int elapsed = 0;
 
        while (len) {
-               if (ast_wait_for_output(fd, timeoutms - elapsed)) {
+               if (wait_for_output(fd, timeoutms - elapsed)) {
                        return -1;
                }
 
@@ -1185,7 +1424,13 @@ int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
 
                if (res < 0 && errno != EAGAIN && errno != EINTR) {
                        /* fatal error from write() */
-                       ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+                       if (errno == EPIPE) {
+#ifndef STANDALONE
+                               ast_debug(1, "write() failed due to reading end being closed: %s\n", strerror(errno));
+#endif
+                       } else {
+                               ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
+                       }
                        return -1;
                }
 
@@ -1211,76 +1456,81 @@ int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
        return res;
 }
 
-int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
+char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
 {
-       struct timeval start = ast_tvnow();
-       int n = 0;
-       int elapsed = 0;
+       char *e;
+       char *q;
 
-       while (len) {
-               if (ast_wait_for_output(fd, timeoutms - elapsed)) {
-                       /* poll returned a fatal error, so bail out immediately. */
-                       return -1;
+       s = ast_strip(s);
+       if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
+               e = s + strlen(s) - 1;
+               if (*e == *(end_quotes + (q - beg_quotes))) {
+                       s++;
+                       *e = '\0';
                }
+       }
+
+       return s;
+}
 
-               /* Clear any errors from a previous write */
-               clearerr(f);
+char *ast_strsep(char **iss, const char sep, uint32_t flags)
+{
+       char *st = *iss;
+       char *is;
+       int inquote = 0;
+       int found = 0;
+       char stack[8];
+
+       if (ast_strlen_zero(st)) {
+               return NULL;
+       }
 
-               n = fwrite(src, 1, len, f);
+       memset(stack, 0, sizeof(stack));
 
-               if (ferror(f) && errno != EINTR && errno != EAGAIN) {
-                       /* fatal error from fwrite() */
-                       if (!feof(f)) {
-                               /* Don't spam the logs if it was just that the connection is closed. */
-                               ast_log(LOG_ERROR, "fwrite() returned error: %s\n", strerror(errno));
+       for(is = st; *is; is++) {
+               if (*is == '\\') {
+                       if (*++is != '\0') {
+                               is++;
+                       } else {
+                               break;
                        }
-                       n = -1;
-                       break;
                }
 
-               /* Update for data already written to the socket */
-               len -= n;
-               src += n;
+               if (*is == '\'' || *is == '"') {
+                       if (*is == stack[inquote]) {
+                               stack[inquote--] = '\0';
+                       } else {
+                               if (++inquote >= sizeof(stack)) {
+                                       return NULL;
+                               }
+                               stack[inquote] = *is;
+                       }
+               }
 
-               elapsed = ast_tvdiff_ms(ast_tvnow(), start);
-               if (elapsed >= timeoutms) {
-                       /* We've taken too long to write
-                        * This is only an error condition if we haven't finished writing. */
-                       n = len ? -1 : 0;
+               if (*is == sep && !inquote) {
+                       *is = '\0';
+                       found = 1;
+                       *iss = is + 1;
                        break;
                }
        }
-
-       while (fflush(f)) {
-               if (errno == EAGAIN || errno == EINTR) {
-                       continue;
-               }
-               if (!feof(f)) {
-                       /* Don't spam the logs if it was just that the connection is closed. */
-                       ast_log(LOG_ERROR, "fflush() returned error: %s\n", strerror(errno));
-               }
-               n = -1;
-               break;
+       if (!found) {
+               *iss = NULL;
        }
 
-       return n < 0 ? -1 : 0;
-}
+       if (flags & AST_STRSEP_STRIP) {
+               st = ast_strip_quoted(st, "'\"", "'\"");
+       }
 
-char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
-{
-       char *e;
-       char *q;
+       if (flags & AST_STRSEP_TRIM) {
+               st = ast_strip(st);
+       }
 
-       s = ast_strip(s);
-       if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
-               e = s + strlen(s) - 1;
-               if (*e == *(end_quotes + (q - beg_quotes))) {
-                       s++;
-                       *e = '\0';
-               }
+       if (flags & AST_STRSEP_UNESCAPE) {
+               ast_unescape_quoted(st);
        }
 
-       return s;
+       return st;
 }
 
 char *ast_unescape_semicolon(char *s)
@@ -1337,6 +1587,136 @@ char *ast_unescape_c(char *src)
        return ret;
 }
 
+/*
+ * Standard escape sequences - Note, '\0' is not included as a valid character
+ * to escape, but instead is used here as a NULL terminator for the string.
+ */
+char escape_sequences[] = {
+       '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\'', '\"', '\?', '\0'
+};
+
+/*
+ * Standard escape sequences output map (has to maintain matching order with
+ * escape_sequences). '\0' is included here as a NULL terminator for the string.
+ */
+static char escape_sequences_map[] = {
+       'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"', '?', '\0'
+};
+
+char *ast_escape(char *dest, const char *s, size_t size, const char *to_escape)
+{
+       char *p;
+       char *c;
+
+       if (!dest || !size) {
+               return dest;
+       }
+       if (ast_strlen_zero(s)) {
+               *dest = '\0';
+               return dest;
+       }
+
+       if (ast_strlen_zero(to_escape)) {
+               ast_copy_string(dest, s, size);
+               return dest;
+       }
+
+       for (p = dest; *s && --size; ++s, ++p) {
+               /* If in the list of characters to escape then escape it */
+               if (strchr(to_escape, *s)) {
+                       if (!--size) {
+                               /* Not enough room left for the escape sequence. */
+                               break;
+                       }
+
+                       /*
+                        * See if the character to escape is part of the standard escape
+                        * sequences. If so we'll have to use its mapped counterpart
+                        * otherwise just use the current character.
+                        */
+                       c = strchr(escape_sequences, *s);
+                       *p++ = '\\';
+                       *p = c ? escape_sequences_map[c - escape_sequences] : *s;
+               } else {
+                       *p = *s;
+               }
+       }
+       *p = '\0';
+
+       return dest;
+}
+
+char *ast_escape_c(char *dest, const char *s, size_t size)
+{
+       /*
+        * Note - This is an optimized version of ast_escape. When looking only
+        * for escape_sequences a couple of checks used in the generic case can
+        * be left out thus making it slightly more efficient.
+        */
+       char *p;
+       char *c;
+
+       if (!dest || !size) {
+               return dest;
+       }
+       if (ast_strlen_zero(s)) {
+               *dest = '\0';
+               return dest;
+       }
+
+       for (p = dest; *s && --size; ++s, ++p) {
+               /*
+                * See if the character to escape is part of the standard escape
+                * sequences. If so use its mapped counterpart.
+                */
+               c = strchr(escape_sequences, *s);
+               if (c) {
+                       if (!--size) {
+                               /* Not enough room left for the escape sequence. */
+                               break;
+                       }
+
+                       *p++ = '\\';
+                       *p = escape_sequences_map[c - escape_sequences];
+               } else {
+                       *p = *s;
+               }
+       }
+       *p = '\0';
+
+       return dest;
+}
+
+static char *escape_alloc(const char *s, size_t *size)
+{
+       if (!s) {
+               return NULL;
+       }
+
+       /*
+        * The result string needs to be twice the size of the given
+        * string just in case every character in it needs to be escaped.
+        */
+       *size = strlen(s) * 2 + 1;
+       return ast_malloc(*size);
+}
+
+char *ast_escape_alloc(const char *s, const char *to_escape)
+{
+       size_t size = 0;
+       char *dest = escape_alloc(s, &size);
+
+       return ast_escape(dest, s, size, to_escape);
+}
+
+char *ast_escape_c_alloc(const char *s)
+{
+       size_t size = 0;
+       char *dest = escape_alloc(s, &size);
+
+       return ast_escape_c(dest, s, size);
+}
+
 int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
 {
        int result;
@@ -1469,10 +1849,33 @@ struct timeval ast_tvsub(struct timeval a, struct timeval b)
        }
        return a;
 }
-#undef ONE_MILLION
 
-/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
- * BSD libc (and others) do not. */
+int ast_remaining_ms(struct timeval start, int max_ms)
+{
+       int ms;
+
+       if (max_ms < 0) {
+               ms = max_ms;
+       } else {
+               ms = max_ms - ast_tvdiff_ms(ast_tvnow(), start);
+               if (ms < 0) {
+                       ms = 0;
+               }
+       }
+
+       return ms;
+}
+
+void ast_format_duration_hh_mm_ss(int duration, char *buf, size_t length)
+{
+       int durh, durm, durs;
+       durh = duration / 3600;
+       durm = (duration % 3600) / 60;
+       durs = duration % 60;
+       snprintf(buf, length, "%02d:%02d:%02d", durh, durm, durs);
+}
+
+#undef ONE_MILLION
 
 #ifndef linux
 AST_MUTEX_DEFINE_STATIC(randomlock);
@@ -1481,7 +1884,7 @@ AST_MUTEX_DEFINE_STATIC(randomlock);
 long int ast_random(void)
 {
        long int res;
-#ifdef HAVE_DEV_URANDOM
+
        if (dev_urandom_fd >= 0) {
                int read_res = read(dev_urandom_fd, &res, sizeof(res));
                if (read_res > 0) {
@@ -1491,7 +1894,14 @@ long int ast_random(void)
                        return res % rm;
                }
        }
-#endif
+
+       /* XXX - Thread safety really depends on the libc, not the OS.
+        *
+        * But... popular Linux libc's (uClibc, glibc, eglibc), all have a
+        * somewhat thread safe random(3) (results are random, but not
+        * reproducible). The libc's for other systems (BSD, et al.), not so
+        * much.
+        */
 #ifdef linux
        res = random();
 #else
@@ -1537,7 +1947,7 @@ char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
        return dataPut;
 }
 
-void ast_join(char *s, size_t len, const char * const w[])
+void ast_join_delim(char *s, size_t len, const char * const w[], unsigned int size, char delim)
 {
        int x, ofs = 0;
        const char *src;
@@ -1545,9 +1955,9 @@ void ast_join(char *s, size_t len, const char * const w[])
        /* Join words into a string */
        if (!s)
                return;
-       for (x = 0; ofs < len && w[x]; x++) {
+       for (x = 0; ofs < len && x < size && w[x] ; x++) {
                if (x > 0)
-                       s[ofs++] = ' ';
+                       s[ofs++] = delim;
                for (src = w[x]; *src && ofs < len; src++)
                        s[ofs++] = *src;
        }
@@ -1556,353 +1966,25 @@ void ast_join(char *s, size_t len, const char * const w[])
        s[ofs] = '\0';
 }
 
-/*
- * stringfields support routines.
- */
-
-/* this is a little complex... string fields are stored with their
-   allocated size in the bytes preceding the string; even the
-   constant 'empty' string has to be this way, so the code that
-   checks to see if there is enough room for a new string doesn't
-   have to have any special case checks
-*/
-
-static const struct {
-       ast_string_field_allocation allocation;
-       char string[1];
-} __ast_string_field_empty_buffer;
-
-ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
-
-#define ALLOCATOR_OVERHEAD 48
-
-static size_t optimal_alloc_size(size_t size)
-{
-       unsigned int count;
-
-       size += ALLOCATOR_OVERHEAD;
-
-       for (count = 1; size; size >>= 1, count++);
-
-       return (1 << count) - ALLOCATOR_OVERHEAD;
-}
-
-/*! \brief add a new block to the pool.
- * We can only allocate from the topmost pool, so the
- * fields in *mgr reflect the size of that only.
- */
-static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
-                          size_t size, const char *file, int lineno, const char *func)
-{
-       struct ast_string_field_pool *pool;
-       size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
-
-#if defined(__AST_DEBUG_MALLOC)
-       if (!(pool = __ast_calloc(1, alloc_size, file, lineno, func))) {
-               return -1;
-       }
-#else
-       if (!(pool = ast_calloc(1, alloc_size))) {
-               return -1;
-       }
-#endif
-
-       pool->prev = *pool_head;
-       pool->size = alloc_size - sizeof(*pool);
-       *pool_head = pool;
-       mgr->last_alloc = NULL;
-
-       return 0;
-}
-
-/*
- * This is an internal API, code should not use it directly.
- * It initializes all fields as empty, then uses 'size' for 3 functions:
- * size > 0 means initialize the pool list with a pool of given size.
- *     This must be called right after allocating the object.
- * size = 0 means release all pools except the most recent one.
- *      If the first pool was allocated via embedding in another
- *      object, that pool will be preserved instead.
- *     This is useful to e.g. reset an object to the initial value.
- * size < 0 means release all pools.
- *     This must be done before destroying the object.
- */
-int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
-                           int needed, const char *file, int lineno, const char *func)
-{
-       const char **p = (const char **) pool_head + 1;
-       struct ast_string_field_pool *cur = NULL;
-       struct ast_string_field_pool *preserve = NULL;
-
-       /* clear fields - this is always necessary */
-       while ((struct ast_string_field_mgr *) p != mgr) {
-               *p++ = __ast_string_field_empty;
-       }
-
-       mgr->last_alloc = NULL;
-#if defined(__AST_DEBUG_MALLOC)
-       mgr->owner_file = file;
-       mgr->owner_func = func;
-       mgr->owner_line = lineno;
-#endif
-       if (needed > 0) {               /* allocate the initial pool */
-               *pool_head = NULL;
-               mgr->embedded_pool = NULL;
-               return add_string_pool(mgr, pool_head, needed, file, lineno, func);
-       }
-
-       /* if there is an embedded pool, we can't actually release *all*
-        * pools, we must keep the embedded one. if the caller is about
-        * to free the structure that contains the stringfield manager
-        * and embedded pool anyway, it will be freed as part of that
-        * operation.
-        */
-       if ((needed < 0) && mgr->embedded_pool) {
-               needed = 0;
-       }
-
-       if (needed < 0) {               /* reset all pools */
-               cur = *pool_head;
-       } else if (mgr->embedded_pool) { /* preserve the embedded pool */
-               preserve = mgr->embedded_pool;
-               cur = *pool_head;
-       } else {                        /* preserve the last pool */
-               if (*pool_head == NULL) {
-                       ast_log(LOG_WARNING, "trying to reset empty pool\n");
-                       return -1;
-               }
-               preserve = *pool_head;
-               cur = preserve->prev;
-       }
-
-       if (preserve) {
-               preserve->prev = NULL;
-               preserve->used = preserve->active = 0;
-       }
-
-       while (cur) {
-               struct ast_string_field_pool *prev = cur->prev;
-
-               if (cur != preserve) {
-                       ast_free(cur);
-               }
-               cur = prev;
-       }
-
-       *pool_head = preserve;
-
-       return 0;
-}
-
-ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
-                                               struct ast_string_field_pool **pool_head, size_t needed)
+char *ast_to_camel_case_delim(const char *s, const char *delim)
 {
-       char *result = NULL;
-       size_t space = (*pool_head)->size - (*pool_head)->used;
-       size_t to_alloc;
-
-       /* Make room for ast_string_field_allocation and make it a multiple of that. */
-       to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
-       ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
-
-       if (__builtin_expect(to_alloc > space, 0)) {
-               size_t new_size = (*pool_head)->size;
+       char *res = ast_strdup(s);
+       char *front, *back, *buf = res;
+       int size;
 
-               while (new_size < to_alloc) {
-                       new_size *= 2;
-               }
+       front = strtok_r(buf, delim, &back);
 
-#if defined(__AST_DEBUG_MALLOC)
-               if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
-                       return NULL;
-#else
-               if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
-                       return NULL;
-#endif
+       while (front) {
+               size = strlen(front);
+               *front = toupper(*front);
+               ast_copy_string(buf, front, size + 1);
+               buf += size;
+               front = strtok_r(NULL, delim, &back);
        }
 
-       /* pool->base is always aligned (gcc aligned attribute). We ensure that
-        * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
-        * causing result to always be aligned as well; which in turn fixes that
-        * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
-       result = (*pool_head)->base + (*pool_head)->used;
-       (*pool_head)->used += to_alloc;
-       (*pool_head)->active += needed;
-       result += ast_alignof(ast_string_field_allocation);
-       AST_STRING_FIELD_ALLOCATION(result) = needed;
-       mgr->last_alloc = result;
-
-       return result;
-}
-
-int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
-                               struct ast_string_field_pool **pool_head, size_t needed,
-                               const ast_string_field *ptr)
-{
-       ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
-       size_t space = (*pool_head)->size - (*pool_head)->used;
-
-       if (*ptr != mgr->last_alloc) {
-               return 1;
-       }
-
-       if (space < grow) {
-               return 1;
-       }
-
-       (*pool_head)->used += grow;
-       (*pool_head)->active += grow;
-       AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
-
-       return 0;
-}
-
-void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
-                                      const ast_string_field ptr)
-{
-       struct ast_string_field_pool *pool, *prev;
-
-       if (ptr == __ast_string_field_empty) {
-               return;
-       }
-
-       for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
-               if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
-                       pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
-                       if ((pool->active == 0) && prev) {
-                               prev->prev = pool->prev;
-                               ast_free(pool);
-                       }
-                       break;
-               }
-       }
-}
-
-void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
-                                    struct ast_string_field_pool **pool_head,
-                                    ast_string_field *ptr, const char *format, va_list ap)
-{
-       size_t needed;
-       size_t available;
-       size_t space = (*pool_head)->size - (*pool_head)->used;
-       ssize_t grow;
-       char *target;
-       va_list ap2;
-
-       /* if the field already has space allocated, try to reuse it;
-          otherwise, try to use the empty space at the end of the current
-          pool
-       */
-       if (*ptr != __ast_string_field_empty) {
-               target = (char *) *ptr;
-               available = AST_STRING_FIELD_ALLOCATION(*ptr);
-               if (*ptr == mgr->last_alloc) {
-                       available += space;
-               }
-       } else {
-               /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
-                * so we don't need to re-align anything here.
-                */
-               target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
-               available = space - ast_alignof(ast_string_field_allocation);
-       }
-
-       va_copy(ap2, ap);
-       needed = vsnprintf(target, available, format, ap2) + 1;
-       va_end(ap2);
-
-       if (needed > available) {
-               /* the allocation could not be satisfied using the field's current allocation
-                  (if it has one), or the space available in the pool (if it does not). allocate
-                  space for it, adding a new string pool if necessary.
-               */
-               if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
-                       return;
-               }
-               vsprintf(target, format, ap);
-               va_end(ap); /* XXX va_end without va_start? */
-               __ast_string_field_release_active(*pool_head, *ptr);
-               *ptr = target;
-       } else if (*ptr != target) {
-               /* the allocation was satisfied using available space in the pool, but not
-                  using the space already allocated to the field
-               */
-               __ast_string_field_release_active(*pool_head, *ptr);
-               mgr->last_alloc = *ptr = target;
-               AST_STRING_FIELD_ALLOCATION(target) = needed;
-               (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
-               (*pool_head)->active += needed;
-       } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
-               /* the allocation was satisfied by using available space in the pool *and*
-                  the field was the last allocated field from the pool, so it grew
-               */
-               AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
-               (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
-               (*pool_head)->active += grow;
-       }
-}
-
-void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
-                                 struct ast_string_field_pool **pool_head,
-                                 ast_string_field *ptr, const char *format, ...)
-{
-       va_list ap;
-
-       va_start(ap, format);
-       __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
-       va_end(ap);
-}
-
-void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size, size_t field_mgr_offset,
-                                    size_t field_mgr_pool_offset, size_t pool_size, const char *file,
-                                    int lineno, const char *func)
-{
-       struct ast_string_field_mgr *mgr;
-       struct ast_string_field_pool *pool;
-       struct ast_string_field_pool **pool_head;
-       size_t pool_size_needed = sizeof(*pool) + pool_size;
-       size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
-       void *allocation;
-       unsigned int x;
-
-#if defined(__AST_DEBUG_MALLOC)
-       if (!(allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func))) {
-               return NULL;
-       }
-#else
-       if (!(allocation = ast_calloc(num_structs, size_to_alloc))) {
-               return NULL;
-       }
-#endif
-
-       for (x = 0; x < num_structs; x++) {
-               void *base = allocation + (size_to_alloc * x);
-               const char **p;
-
-               mgr = base + field_mgr_offset;
-               pool_head = base + field_mgr_pool_offset;
-               pool = base + struct_size;
-
-               p = (const char **) pool_head + 1;
-               while ((struct ast_string_field_mgr *) p != mgr) {
-                       *p++ = __ast_string_field_empty;
-               }
-
-               mgr->embedded_pool = pool;
-               *pool_head = pool;
-               pool->size = size_to_alloc - struct_size - sizeof(*pool);
-#if defined(__AST_DEBUG_MALLOC)
-               mgr->owner_file = file;
-               mgr->owner_func = func;
-               mgr->owner_line = lineno;
-#endif
-       }
-
-       return allocation;
+       return res;
 }
 
-/* end of stringfields support */
-
 AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
 
 int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
@@ -2013,17 +2095,119 @@ int ast_mkdir(const char *path, int mode)
        return 0;
 }
 
+static int safe_mkdir(const char *base_path, char *path, int mode)
+{
+       RAII_VAR(char *, absolute_path, NULL, ast_std_free);
+
+       absolute_path = realpath(path, NULL);
+
+       if (absolute_path) {
+               /* Path exists, but is it in the right place? */
+               if (!ast_begins_with(absolute_path, base_path)) {
+                       return EPERM;
+               }
+
+               /* It is in the right place! */
+               return 0;
+       } else {
+               /* Path doesn't exist. */
+
+               /* The slash terminating the subpath we're checking */
+               char *path_term = strchr(path, '/');
+               /* True indicates the parent path is within base_path */
+               int parent_is_safe = 0;
+               int res;
+
+               while (path_term) {
+                       RAII_VAR(char *, absolute_subpath, NULL, ast_std_free);
+
+                       /* Truncate the path one past the slash */
+                       char c = *(path_term + 1);
+                       *(path_term + 1) = '\0';
+                       absolute_subpath = realpath(path, NULL);
+
+                       if (absolute_subpath) {
+                               /* Subpath exists, but is it safe? */
+                               parent_is_safe = ast_begins_with(
+                                       absolute_subpath, base_path);
+                       } else if (parent_is_safe) {
+                               /* Subpath does not exist, but parent is safe
+                                * Create it */
+                               res = mkdir(path, mode);
+                               if (res != 0) {
+                                       ast_assert(errno != EEXIST);
+                                       return errno;
+                               }
+                       } else {
+                               /* Subpath did not exist, parent was not safe
+                                * Fail! */
+                               errno = EPERM;
+                               return errno;
+                       }
+                       /* Restore the path */
+                       *(path_term + 1) = c;
+                       /* Move on to the next slash */
+                       path_term = strchr(path_term + 1, '/');
+               }
+
+               /* Now to build the final path, but only if it's safe */
+               if (!parent_is_safe) {
+                       errno = EPERM;
+                       return errno;
+               }
+
+               res = mkdir(path, mode);
+               if (res != 0 && errno != EEXIST) {
+                       return errno;
+               }
+
+               return 0;
+       }
+}
+
+int ast_safe_mkdir(const char *base_path, const char *path, int mode)
+{
+       RAII_VAR(char *, absolute_base_path, NULL, ast_std_free);
+       RAII_VAR(char *, p, NULL, ast_free);
+
+       if (base_path == NULL || path == NULL) {
+               errno = EFAULT;
+               return errno;
+       }
+
+       p = ast_strdup(path);
+       if (p == NULL) {
+               errno = ENOMEM;
+               return errno;
+       }
+
+       absolute_base_path = realpath(base_path, NULL);
+       if (absolute_base_path == NULL) {
+               return errno;
+       }
+
+       return safe_mkdir(absolute_base_path, p, mode);
+}
+
+static void utils_shutdown(void)
+{
+       close(dev_urandom_fd);
+       dev_urandom_fd = -1;
+#if defined(DEBUG_THREADS) && !defined(LOW_MEMORY)
+       ast_cli_unregister_multiple(utils_cli, ARRAY_LEN(utils_cli));
+#endif
+}
+
 int ast_utils_init(void)
 {
-#ifdef HAVE_DEV_URANDOM
        dev_urandom_fd = open("/dev/urandom", O_RDONLY);
-#endif
        base64_init();
 #ifdef DEBUG_THREADS
 #if !defined(LOW_MEMORY)
        ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
 #endif
 #endif
+       ast_register_cleanup(utils_shutdown);
        return 0;
 }
 
@@ -2150,13 +2334,19 @@ int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request,
 }
 
 #ifndef __AST_DEBUG_MALLOC
-int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
+int __ast_asprintf(const char *file, int lineno, const char *func, char **ret, const char *fmt, ...)
 {
        int res;
        va_list ap;
 
        va_start(ap, fmt);
-       if ((res = vasprintf(ret, fmt, ap)) == -1) {
+       res = vasprintf(ret, fmt, ap);
+       if (res < 0) {
+               /*
+                * *ret is undefined so set to NULL to ensure it is
+                * initialized to something useful.
+                */
+               *ret = NULL;
                MALLOC_FAILURE_MSG;
        }
        va_end(ap);
@@ -2201,3 +2391,395 @@ char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
        return NULL;
 }
 
+int ast_check_ipv6(void)
+{
+       int udp6_socket = socket(AF_INET6, SOCK_DGRAM, 0);
+
+       if (udp6_socket < 0) {
+               return 0;
+       }
+
+       close(udp6_socket);
+       return 1;
+}
+
+void DO_CRASH_NORETURN ast_do_crash(void)
+{
+#if defined(DO_CRASH)
+       abort();
+       /*
+        * Just in case abort() doesn't work or something else super
+        * silly, and for Qwell's amusement.
+        */
+       *((int *) 0) = 0;
+#endif /* defined(DO_CRASH) */
+}
+
+void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
+{
+       /*
+        * Attempt to put it into the logger, but hope that at least
+        * someone saw the message on stderr ...
+        */
+       fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
+               condition_str, condition, line, function, file);
+       ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
+               condition_str, condition);
+
+       /* Generate a backtrace for the assert */
+       ast_log_backtrace();
+
+       /*
+        * Give the logger a chance to get the message out, just in case
+        * we abort(), or Asterisk crashes due to whatever problem just
+        * happened after we exit ast_assert().
+        */
+       usleep(1);
+       ast_do_crash();
+}
+
+char *ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
+{
+       int x;
+       char *os = s;
+       if (maxlen < 18) {
+               if (s && (maxlen > 0)) {
+                       *s = '\0';
+               }
+       } else {
+               for (x = 0; x < 5; x++) {
+                       sprintf(s, "%02hhx:", eid->eid[x]);
+                       s += 3;
+               }
+               sprintf(s, "%02hhx", eid->eid[5]);
+       }
+       return os;
+}
+
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__Darwin__)
+#include <ifaddrs.h>
+#include <net/if_dl.h>
+
+void ast_set_default_eid(struct ast_eid *eid)
+{
+       struct ifaddrs *ifap, *ifaphead;
+       int rtnerr;
+       const struct sockaddr_dl *sdl;
+       int alen;
+       caddr_t ap;
+       char eid_str[20];
+       unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+       unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+       rtnerr = getifaddrs(&ifaphead);
+       if (rtnerr) {
+               ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+                       "You will have to set it manually.\n");
+               return;
+       }
+
+       if (!ifaphead) {
+               ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+                       "You will have to set it manually.\n");
+               return;
+       }
+
+       for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
+               if (ifap->ifa_addr->sa_family != AF_LINK) {
+                       continue;
+               }
+
+               sdl = (const struct sockaddr_dl *) ifap->ifa_addr;
+               ap = ((caddr_t) ((sdl)->sdl_data + (sdl)->sdl_nlen));
+               alen = sdl->sdl_alen;
+               if (alen != 6 || !(memcmp(ap, &empty_mac, 6) && memcmp(ap, &full_mac, 6))) {
+                       continue;
+               }
+
+               memcpy(eid, ap, sizeof(*eid));
+               ast_debug(1, "Seeding global EID '%s'\n",
+                               ast_eid_to_str(eid_str, sizeof(eid_str), eid));
+               freeifaddrs(ifaphead);
+               return;
+       }
+
+       ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+               "You will have to set it manually.\n");
+       freeifaddrs(ifaphead);
+
+       return;
+}
+
+#elif defined(SOLARIS)
+#include <sys/sockio.h>
+#include <net/if_arp.h>
+
+void ast_set_default_eid(struct ast_eid *eid)
+{
+       int s;
+       int x;
+       int res = 0;
+       struct lifreq *ifr = NULL;
+       struct lifnum ifn;
+       struct lifconf ifc;
+       struct arpreq ar;
+       struct sockaddr_in *sa, *sa2;
+       char *buf = NULL;
+       char eid_str[20];
+       int bufsz;
+       unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+       unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s <= 0) {
+               ast_log(LOG_WARNING, "Unable to open a socket for seeding global EID. "
+                       " You will have to set it manually.\n");
+               return;
+       }
+
+       /* Get a count of interfaces on the machine */
+       ifn.lifn_family = AF_UNSPEC;
+       ifn.lifn_flags = 0;
+       ifn.lifn_count = 0;
+       if (ioctl(s, SIOCGLIFNUM, &ifn) < 0) {
+               ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+                       " You will have to set it manually.\n");
+               close(s);
+               return;
+       }
+
+       bufsz = ifn.lifn_count * sizeof(struct lifreq);
+       if (!(buf = ast_malloc(bufsz))) {
+               ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
+                       "You will have to set it manually.\n");
+               close(s);
+               return;
+       }
+       memset(buf, 0, bufsz);
+
+       /* Get a list of interfaces on the machine */
+       ifc.lifc_len = bufsz;
+       ifc.lifc_buf = buf;
+       ifc.lifc_family = AF_UNSPEC;
+       ifc.lifc_flags = 0;
+       if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
+               ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+                       "You will have to set it manually.\n");
+               ast_free(buf);
+               close(s);
+               return;
+       }
+
+       for (ifr = (struct lifreq *)buf, x = 0; x < ifn.lifn_count; ifr++, x++) {
+               unsigned char *p;
+
+               sa = (struct sockaddr_in *)&(ifr->lifr_addr);
+               sa2 = (struct sockaddr_in *)&(ar.arp_pa);
+               *sa2 = *sa;
+
+               if(ioctl(s, SIOCGARP, &ar) >= 0) {
+                       p = (unsigned char *)&(ar.arp_ha.sa_data);
+                       if (!(memcmp(p, &empty_mac, 6) && memcmp(p, &full_mac, 6))) {
+                               continue;
+                       }
+
+                       memcpy(eid, p, sizeof(*eid));
+                       ast_debug(1, "Seeding global EID '%s'\n",
+                               ast_eid_to_str(eid_str, sizeof(eid_str), eid));
+                       ast_free(buf);
+                       close(s);
+                       return;
+               }
+       }
+
+       ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+               "You will have to set it manually.\n");
+       ast_free(buf);
+       close(s);
+
+       return;
+}
+
+#else
+void ast_set_default_eid(struct ast_eid *eid)
+{
+       int s;
+       int i;
+       struct ifreq *ifr;
+       struct ifreq *ifrp;
+       struct ifconf ifc;
+       char *buf = NULL;
+       char eid_str[20];
+       int bufsz, num_interfaces;
+       unsigned char empty_mac[6] = {0, 0, 0, 0, 0, 0};
+       unsigned char full_mac[6]  = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0) {
+               ast_log(LOG_WARNING, "Unable to open socket for seeding global EID. "
+                       "You will have to set it manually.\n");
+               return;
+       }
+
+       ifc.ifc_len = 0;
+       ifc.ifc_buf = NULL;
+       if (ioctl(s, SIOCGIFCONF, &ifc) || ifc.ifc_len <= 0) {
+               ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+                       "You will have to set it manually.\n");
+               close(s);
+               return;
+       }
+       bufsz = ifc.ifc_len;
+
+       if (!(buf = ast_malloc(bufsz))) {
+               ast_log(LOG_WARNING, "Unable to allocate memory for seeding global EID. "
+                       "You will have to set it manually.\n");
+               close(s);
+               return;
+       }
+
+       ifc.ifc_buf = buf;
+       if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+               ast_log(LOG_WARNING, "Unable to retrieve ethernet interfaces for seeding global EID. "
+                       "You will have to set it manually.\n");
+               ast_free(buf);
+               close(s);
+               return;
+       }
+
+       ifrp = ifc.ifc_req;
+       num_interfaces = ifc.ifc_len / sizeof(*ifr);
+
+       for (i = 0; i < num_interfaces; i++) {
+               ifr = &ifrp[i];
+               if (!ioctl(s, SIOCGIFHWADDR, ifr)) {
+                       unsigned char *hwaddr = (unsigned char *) ifr->ifr_hwaddr.sa_data;
+
+                       if (!(memcmp(hwaddr, &empty_mac, 6) && memcmp(hwaddr, &full_mac, 6))) {
+                               continue;
+                       }
+
+                       memcpy(eid, hwaddr, sizeof(*eid));
+                       ast_debug(1, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n",
+                               ast_eid_to_str(eid_str, sizeof(eid_str), eid), ifr->ifr_name);
+                       ast_free(buf);
+                       close(s);
+                       return;
+               }
+       }
+
+       ast_log(LOG_WARNING, "No ethernet interface found for seeding global EID. "
+               "You will have to set it manually.\n");
+       ast_free(buf);
+       close(s);
+
+       return;
+}
+#endif /* LINUX */
+
+int ast_str_to_eid(struct ast_eid *eid, const char *s)
+{
+       unsigned int eid_int[6];
+       int x;
+
+       if (sscanf(s, "%2x:%2x:%2x:%2x:%2x:%2x", &eid_int[0], &eid_int[1], &eid_int[2],
+                &eid_int[3], &eid_int[4], &eid_int[5]) != 6) {
+                       return -1;
+       }
+
+       for (x = 0; x < 6; x++) {
+               eid->eid[x] = eid_int[x];
+       }
+
+       return 0;
+}
+
+int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
+{
+       return memcmp(eid1, eid2, sizeof(*eid1));
+}
+
+int ast_eid_is_empty(const struct ast_eid *eid)
+{
+       struct ast_eid empty_eid;
+
+       memset(&empty_eid, 0, sizeof(empty_eid));
+       return memcmp(eid, &empty_eid, sizeof(empty_eid)) ? 0 : 1;
+}
+
+int ast_file_is_readable(const char *filename)
+{
+#if defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS)
+#if defined(HAVE_EUIDACCESS) && !defined(HAVE_EACCESS)
+#define eaccess euidaccess
+#endif
+       return eaccess(filename, R_OK) == 0;
+#else
+       int fd = open(filename, O_RDONLY |  O_NONBLOCK);
+       if (fd < 0) {
+               return 0;
+       }
+       close(fd);
+       return 1;
+#endif
+}
+
+int ast_compare_versions(const char *version1, const char *version2)
+{
+       unsigned int major[2] = { 0 };
+       unsigned int minor[2] = { 0 };
+       unsigned int patch[2] = { 0 };
+       unsigned int extra[2] = { 0 };
+       int res;
+
+       sscanf(version1, "%u.%u.%u.%u", &major[0], &minor[0], &patch[0], &extra[0]);
+       sscanf(version2, "%u.%u.%u.%u", &major[1], &minor[1], &patch[1], &extra[1]);
+
+       res = major[0] - major[1];
+       if (res) {
+               return res;
+       }
+       res = minor[0] - minor[1];
+       if (res) {
+               return res;
+       }
+       res = patch[0] - patch[1];
+       if (res) {
+               return res;
+       }
+       return extra[0] - extra[1];
+}
+
+int __ast_fd_set_flags(int fd, int flags, enum ast_fd_flag_operation op,
+       const char *file, int lineno, const char *function)
+{
+       int f;
+
+       f = fcntl(fd, F_GETFL);
+       if (f == -1) {
+               ast_log(__LOG_ERROR, file, lineno, function,
+                       "Failed to get fcntl() flags for file descriptor: %s\n", strerror(errno));
+               return -1;
+       }
+
+       switch (op) {
+       case AST_FD_FLAG_SET:
+               f |= flags;
+               break;
+       case AST_FD_FLAG_CLEAR:
+               f &= ~flags;
+               break;
+       default:
+               ast_assert(0);
+               break;
+       }
+
+       f = fcntl(fd, F_SETFL, f);
+       if (f == -1) {
+               ast_log(__LOG_ERROR, file, lineno, function,
+                       "Failed to set fcntl() flags for file descriptor: %s\n", strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}