Skip to content
Snippets Groups Projects
utils.c 68.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				   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
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
    		return;
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	/* We only have further details for mutexes right now */
    	if (lock_info->locks[i].type != AST_MUTEX)
    		return;
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	lock = lock_info->locks[i].lock_addr;
    
    	ast_reentrancy_lock(lt);
    	for (j = 0; *str && j < lt->reentrancy; j++) {
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    		ast_str_append(str, 0, "=== --- ---> Locked Here: %s line %d (%s)\n",
    
    					   lt->file[j], lt->lineno[j], lt->func[j]);
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	}
    
    	ast_reentrancy_unlock(lt);
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    }
    
    
    
    /*! This function can help you find highly temporal locks; locks that happen for a
    
    Steve Murphy's avatar
     
    Steve Murphy committed
        short time, but at unexpected times, usually at times that create a deadlock,
    	Why is this thing locked right then? Who is locking it? Who am I fighting
    
        with for this lock?
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    
    	To answer such questions, just call this routine before you would normally try
    	to aquire a lock. It doesn't do anything if the lock is not acquired. If the
    	lock is taken, it will publish a line or two to the console via ast_log().
    
    	Sometimes, the lock message is pretty uninformative. For instance, you might
    	find that the lock is being aquired deep within the astobj2 code; this tells
    	you little about higher level routines that call the astobj2 routines.
    	But, using gdb, you can set a break at the ast_log below, and for that
    	breakpoint, you can set the commands:
    	  where
    	  cont
    	which will give a stack trace and continue. -- that aught to do the job!
    
    */
    
    void ast_log_show_lock(void *this_lock_addr)
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	struct ast_str *str;
    
    	if (!(str = ast_str_create(4096))) {
    		ast_log(LOG_NOTICE,"Could not create str\n");
    		return;
    	}
    
    
    	pthread_mutex_lock(&lock_infos_lock.mutex);
    	AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
    		int i;
    		pthread_mutex_lock(&lock_info->lock);
    		for (i = 0; str && i < lock_info->num_locks; i++) {
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    			/* ONLY show info about this particular lock, if
    			   it's acquired... */
    
    			if (lock_info->locks[i].lock_addr == this_lock_addr) {
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    				append_lock_information(&str, lock_info, i);
    
    				ast_log(LOG_NOTICE, "%s", ast_str_buffer(str));
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    				break;
    
    			}
    		}
    		pthread_mutex_unlock(&lock_info->lock);
    	}
    	pthread_mutex_unlock(&lock_infos_lock.mutex);
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    	ast_free(str);
    
    struct ast_str *ast_dump_locks(void)
    
    {
    	struct thr_lock_info *lock_info;
    
    	if (!(str = ast_str_create(4096))) {
    
    	ast_str_append(&str, 0, "\n"
    
    	               "=======================================================================\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", ast_get_version());
    
    	pthread_mutex_lock(&lock_infos_lock.mutex);
    	AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
    		int 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;
    
    				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"
    
    		}
    	}
    	pthread_mutex_unlock(&lock_infos_lock.mutex);
    
    
    	ast_str_append(&str, 0, "=======================================================================\n"
    
    	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));
    
    	return CLI_SUCCESS;
    
    }
    
    static struct ast_cli_entry utils_cli[] = {
    
    	AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
    
    /*
     * support for 'show threads'. The start routine is wrapped by
     * dummy_start(), so that ast_register_thread() and
     * ast_unregister_thread() know the thread identifier.
     */
    struct thr_arg {
    	void *(*start_routine)(void *);
    	void *data;
    	char *name;
    };
    
    /*
     * on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
     * are odd macros which start and end a block, so they _must_ be
     * used in pairs (the latter with a '1' argument to call the
     * handler on exit.
    
     * On BSD we don't need this, but we keep it for compatibility.
    
     */
    static void *dummy_start(void *data)
    
    	struct thr_arg a = *((struct thr_arg *) data);	/* make a local copy */
    
    #ifdef DEBUG_THREADS
    	struct thr_lock_info *lock_info;
    
    	pthread_mutexattr_t mutex_attr;
    
    
    	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
    		return NULL;
    
    	lock_info->thread_id = pthread_self();
    
    	lock_info->lwp = ast_get_tid();
    
    	lock_info->thread_name = strdup(a.name);
    
    
    	pthread_mutexattr_init(&mutex_attr);
    	pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
    	pthread_mutex_init(&lock_info->lock, &mutex_attr);
    	pthread_mutexattr_destroy(&mutex_attr);
    
    
    	pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
    	AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
    	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);
    
    #endif /* !LOW_MEMORY */
    
    
    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)
    
    #if !defined(LOW_MEMORY)
    
    	struct thr_arg *a;
    
    	if (!attr) {
    
    		attr = ast_alloca(sizeof(*attr));
    
    		pthread_attr_init(attr);
    
    #ifdef __linux__
    	/* On Linux, 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.
    	   This does mean that callers cannot set a different priority using
    	   PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
    	   the priority afterwards with pthread_setschedparam(). */
    
    	if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
    		ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
    
    	if (!stacksize)
    		stacksize = AST_STACKSIZE;
    
    
    	if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
    		ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
    
    
    #if !defined(LOW_MEMORY)
    
    	if ((a = ast_malloc(sizeof(*a)))) {
    
    		a->start_routine = start_routine;
    		a->data = data;
    		start_routine = dummy_start;
    
    		if (ast_asprintf(&a->name, "%-20s started at [%5d] %s %s()",
    
    			     start_fn, line, file, caller) < 0) {
    			a->name = NULL;
    		}
    
    #endif /* !LOW_MEMORY */
    
    	return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
    }
    
    
    int ast_pthread_create_detached_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)
    {
    	unsigned char attr_destroy = 0;
    	int res;
    
    	if (!attr) {
    
    		attr = ast_alloca(sizeof(*attr));
    
    		pthread_attr_init(attr);
    		attr_destroy = 1;
    	}
    
    	if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
    		ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
    
    
    	res = ast_pthread_create_stack(thread, attr, start_routine, data,
    
    	                               stacksize, file, caller, line, start_fn);
    
    	if (attr_destroy)
    		pthread_attr_destroy(attr);
    
    	return res;
    }
    
    
    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;
    
    	return ast_poll(pfd, 1, ms);
    
    static int wait_for_output(int fd, int timeoutms)
    
    {
    	struct pollfd pfd = {
    		.fd = fd,
    		.events = POLLOUT,
    	};
    	int res;
    
    	struct timeval start = ast_tvnow();
    	int elapsed = 0;
    
    
    	/* poll() until the fd is writable without blocking */
    
    	while ((res = ast_poll(&pfd, 1, timeoutms - elapsed)) <= 0) {
    
    			ast_debug(1, "Timed out trying to write\n");
    
    			return -1;
    		} else if (res == -1) {
    			/* poll() returned an error, check to see if it was fatal */
    
    			if (errno == EINTR || errno == EAGAIN) {
    
    				elapsed = ast_tvdiff_ms(ast_tvnow(), start);
    				if (elapsed >= timeoutms) {
    					return -1;
    				}
    
    				/* This was an acceptable error, go back into poll() */
    				continue;
    			}
    
    			/* Fatal error, bail. */
    			ast_log(LOG_ERROR, "poll returned error: %s\n", strerror(errno));
    
    			return -1;
    		}
    
    		elapsed = ast_tvdiff_ms(ast_tvnow(), start);
    		if (elapsed >= timeoutms) {
    			return -1;
    		}
    
    /*!
     * Try to write string, but wait no more than ms milliseconds before timing out.
     *
     * \note The code assumes that the file descriptor has NONBLOCK set,
     * so there is only one system call made to do a write, unless we actually
     * have a need to wait.  This way, we get better performance.
     * If the descriptor is blocking, all assumptions on the guaranteed
     * detail do not apply anymore.
     */
    
    int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
    
    	struct timeval start = ast_tvnow();
    
    		if (wait_for_output(fd, timeoutms - elapsed)) {
    
    
    		if (res < 0 && errno != EAGAIN && errno != EINTR) {
    			/* fatal error from write() */
    			ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
    
    
    		if (res < 0) {
    			/* It was an acceptable error */
    
    		}
    
    		/* Update how much data we have left to write */
    
    
    		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. */
    			res = len ? -1 : 0;
    			break;
    		}
    
    int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
    {
    	struct timeval start = ast_tvnow();
    	int n = 0;
    
    		if (wait_for_output(fd, timeoutms - elapsed)) {
    
    			/* poll returned a fatal error, so bail out immediately. */
    			return -1;
    		}
    
    		/* Clear any errors from a previous write */
    		clearerr(f);
    
    		n = fwrite(src, 1, len, f);
    
    		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));
    			}
    			n = -1;
    			break;
    		}
    
    		/* Update for data already written to the socket */
    		len -= n;
    		src += n;
    
    		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;
    			break;
    		}
    	}
    
    
    	while (fflush(f)) {
    		if (errno == EAGAIN || errno == EINTR) {
    
    			/* fflush() does not appear to reset errno if it flushes
    			 * and reaches EOF at the same time. It returns EOF with
    			 * the last seen value of errno, causing a possible loop.
    			 * Also usleep() to reduce CPU eating if it does loop */
    			errno = 0;
    			usleep(1);
    
    			/* 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;
    	}
    
    	return n < 0 ? -1 : 0;
    }
    
    
    char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
    {
    	char *e;
    	char *q;
    
    	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;
    }
    
    
    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 (iss == NULL || *iss == '\0') {
    		return NULL;
    	}
    
    	memset(stack, 0, sizeof(stack));
    
    	for(is = st; *is; is++) {
    		if (*is == '\\') {
    			if (*++is != '\0') {
    				is++;
    			} else {
    				break;
    			}
    		}
    
    		if (*is == '\'' || *is == '"') {
    			if (*is == stack[inquote]) {
    				stack[inquote--] = '\0';
    			} else {
    				if (++inquote >= sizeof(stack)) {
    					return NULL;
    				}
    				stack[inquote] = *is;
    			}
    		}
    
    		if (*is == sep && !inquote) {
    			*is = '\0';
    			found = 1;
    			*iss = is + 1;
    			break;
    		}
    	}
    	if (!found) {
    		*iss = NULL;
    	}
    
    	if (flags & AST_STRSEP_STRIP) {
    		st = ast_strip_quoted(st, "'\"", "'\"");
    	}
    
    	if (flags & AST_STRSEP_TRIM) {
    		st = ast_strip(st);
    	}
    
    	if (flags & AST_STRSEP_UNESCAPE) {
    		ast_unescape_quoted(st);
    	}
    
    	return st;
    }
    
    
    char *ast_unescape_semicolon(char *s)
    {
    	char *e;
    	char *work = s;
    
    	while ((e = strchr(work, ';'))) {
    		if ((e > work) && (*(e-1) == '\\')) {
    			memmove(e - 1, e, strlen(e) + 1);
    			work = e;
    
    		} else {
    			work = e + 1;
    
    /* !\brief unescape some C sequences in place, return pointer to the original string.
     */
    char *ast_unescape_c(char *src)
    {
    	char c, *ret, *dst;
    
    	if (src == NULL)
    		return NULL;
    	for (ret = dst = src; (c = *src++); *dst++ = c ) {
    		if (c != '\\')
    			continue;	/* copy char at the end of the loop */
    		switch ((c = *src++)) {
    		case '\0':	/* special, trailing '\' */
    			c = '\\';
    			break;
    		case 'b':	/* backspace */
    			c = '\b';
    			break;
    		case 'f':	/* form feed */
    			c = '\f';
    			break;
    		case 'n':
    			c = '\n';
    			break;
    		case 'r':
    			c = '\r';
    			break;
    		case 't':
    			c = '\t';
    			break;
    		}
    		/* default, use the char literally */
    	}
    	*dst = '\0';
    	return ret;
    }
    
    
    int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
    
    {
    	int result;
    
    	if (!buffer || !*buffer || !space || !*space)
    		return -1;
    
    	result = vsnprintf(*buffer, *space, fmt, ap);
    
    	if (result < 0)
    		return -1;
    	else if (result > *space)
    		result = *space;
    
    	*buffer += result;
    	*space -= result;
    	return 0;
    }
    
    
    int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
    {
    	va_list ap;
    	int result;
    
    	va_start(ap, fmt);
    	result = ast_build_string_va(buffer, space, fmt, ap);
    	va_end(ap);
    
    	return result;
    }
    
    
    int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str **regex_pattern)
    
    {
    	int regex_len = strlen(regex_string);
    	int ret = 3;
    
    	/* Chop off the leading / if there is one */
    	if ((regex_len >= 1) && (regex_string[0] == '/')) {
    
    		ast_str_set(regex_pattern, 0, "%s", regex_string + 1);
    
    		ret -= 2;
    	}
    
    	/* Chop off the ending / if there is one */
    	if ((regex_len > 1) && (regex_string[regex_len - 1] == '/')) {
    
    		ast_str_truncate(*regex_pattern, -1);
    
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    int ast_false(const char *s)
    {
    
    		return 0;
    
    	/* Determine if this is a false value */
    	if (!strcasecmp(s, "no") ||
    	    !strcasecmp(s, "false") ||
    	    !strcasecmp(s, "n") ||
    	    !strcasecmp(s, "f") ||
    	    !strcasecmp(s, "0") ||
    	    !strcasecmp(s, "off"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    
    		ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_sec += a.tv_usec / ONE_MILLION;
    
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    
    
    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);
    }
    
    
    AST_MUTEX_DEFINE_STATIC(randomlock);
    
    long int ast_random(void)
    {
    	long int res;
    
    	if (dev_urandom_fd >= 0) {
    		int read_res = read(dev_urandom_fd, &res, sizeof(res));
    
    	/* 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.
    	 */
    
    	ast_mutex_lock(&randomlock);
    	res = random();
    	ast_mutex_unlock(&randomlock);
    
    void ast_replace_subargument_delimiter(char *s)
    {
    	for (; *s; s++) {
    		if (*s == '^') {
    			*s = ',';
    		}
    	}
    }
    
    
    char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
    {
    
    	char *dataPut = start;
    
    	int inEscape = 0;
    	int inQuotes = 0;
    
    	for (; *start; start++) {
    		if (inEscape) {
    			*dataPut++ = *start;       /* Always goes verbatim */
    			inEscape = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else {
    
    			if (*start == '\\') {
    				inEscape = 1;      /* Do not copy \ into the data */
    			} else if (*start == '\'') {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				inQuotes = 1 - inQuotes;   /* Do not copy ' into the data */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
    
    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;
    
    	/* Join words into a string */
    	if (!s)
    		return;
    
    	for (x = 0; ofs < len && x < size && w[x] ; x++) {
    
    			s[ofs++] = delim;
    
    		for (src = w[x]; *src && ofs < len; src++)
    			s[ofs++] = *src;
    	}
    	if (ofs == len)
    		ofs--;
    	s[ofs] = '\0';
    }
    
    char *ast_to_camel_case_delim(const char *s, const char *delim)
    {
    	char *res = ast_strdup(s);
    	char *front, *back, *buf = res;
    	int size;
    
    	front = strtok_r(buf, delim, &back);
    
    	while (front) {
    		size = strlen(front);
    		*front = toupper(*front);
    		ast_copy_string(buf, front, size + 1);
    		buf += size;
    		front = strtok_r(NULL, delim, &back);
    	}
    
    	return res;
    }
    
    
    /*
     * 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))) {
    
    	pool->prev = *pool_head;
    
    	pool->size = alloc_size - sizeof(*pool);
    
    	mgr->last_alloc = NULL;
    
    /*
     * 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 */
    
    		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;
    	}
    
    
    	} 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) {