Fix DEBUG_THREADS when lock is acquired in __constructor__
[asterisk/asterisk.git] / main / utils.c
index cc9dee3..e2c5e2c 100644 (file)
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <ctype.h>
-#include <sys/stat.h>
-#include <sys/stat.h>
-
 #include <fcntl.h>
-
+#include <sys/stat.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 #if defined(__APPLE__)
 #include <mach/mach.h>
 #elif defined(HAVE_SYS_THR_H)
@@ -45,6 +43,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"
@@ -598,6 +597,8 @@ struct thr_lock_info {
                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
@@ -784,6 +785,60 @@ int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, in
        return 0;
 }
 
+void ast_suspend_lock_info(void *lock_addr)
+{
+       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);
+}
+
+void ast_restore_lock_info(void *lock_addr)
+{
+       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);
+}
+
+
 #ifdef HAVE_BKTR
 void ast_remove_lock_info(void *lock_addr, struct ast_bt *bt)
 #else
@@ -864,7 +919,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");
        }
@@ -877,7 +932,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,
@@ -885,7 +940,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
@@ -927,7 +983,7 @@ 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)
 {
        struct thr_lock_info *lock_info;
        struct ast_str *str;
@@ -958,67 +1014,92 @@ void log_show_lock(void *this_lock_addr)
 }
 
 
-static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+struct ast_str *ast_dump_locks(void)
 {
        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) {
+                               ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n", (long) 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;
+}
+
+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";
+               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));
 
@@ -1544,6 +1625,15 @@ int ast_remaining_ms(struct timeval start, int max_ms)
        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
@@ -2092,6 +2182,100 @@ 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, 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, 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, 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);
+}
+
 int ast_utils_init(void)
 {
        dev_urandom_fd = open("/dev/urandom", O_RDONLY);