Skip to content
Snippets Groups Projects
utils.c 68.8 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Asterisk -- An open source telephony toolkit.
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
    
     *
     * This program is free software, distributed under the terms of
    
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
     * \brief Utility functions
    
     * \note These are important for portability and security,
     * so please use them in favour of other routines.
     * Please consult the CODING GUIDELINES for more information.
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    ASTERISK_REGISTER_FILE()
    
    #include <ctype.h>
    
    David M. Lee's avatar
    David M. Lee committed
    #include <fcntl.h>
    
    David M. Lee's avatar
    David M. Lee committed
    #include <unistd.h>
    
    #if defined(__APPLE__)
    #include <mach/mach.h>
    #elif defined(HAVE_SYS_THR_H)
    #include <sys/thr.h>
    #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"
    #include "asterisk/io.h"
    #include "asterisk/md5.h"
    
    #include "asterisk/cli.h"
    #include "asterisk/linkedlists.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/strings.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #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
    #include "asterisk/threadstorage.h"
    
    
    #define AST_API_MODULE
    #include "asterisk/config.h"
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char base64[64];
    static char b2a[256];
    
    
    #if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
    
    Olle Johansson's avatar
    Olle Johansson committed
    #define ERANGE 34	/*!< duh? ERANGE value copied from web... */
    
    AST_MUTEX_DEFINE_STATIC(__mutex);
    
    
    /*! \brief Reentrant replacement for gethostbyname for BSD-based systems.
    
    Olle Johansson's avatar
    Olle Johansson committed
    \note This
    
    routine is derived from code originally written and placed in the public
    
    domain by Enzo Michelangeli <em@em.no-ip.com> */
    
    
    static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
    
    				size_t buflen, struct hostent **result,
    				int *h_errnop)
    
    {
    	int hsave;
    	struct hostent *ph;
    	ast_mutex_lock(&__mutex); /* begin critical area */
    	hsave = h_errno;
    
    	ph = gethostbyname(name);
    	*h_errnop = h_errno; /* copy h_errno to *h_herrnop */
    	if (ph == NULL) {
    		*result = NULL;
    	} else {
    		char **p, **q;
    		char *pbuf;
    
    		int nbytes = 0;
    		int naddr = 0, naliases = 0;
    
    		/* determine if we have enough space in buf */
    
    		/* count how many addresses */
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			nbytes += ph->h_length; /* addresses */
    			nbytes += sizeof(*p); /* pointers */
    			naddr++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* count how many aliases, and total length of strings */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			nbytes += (strlen(*p)+1); /* aliases */
    			nbytes += sizeof(*p);  /* pointers */
    			naliases++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* here nbytes is the number of bytes required in buffer */
    		/* as a terminator must be there, the minimum value is ph->h_length */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (nbytes > buflen) {
    
    			*result = NULL;
    			ast_mutex_unlock(&__mutex); /* end critical area */
    			return ERANGE; /* not enough space in buf!! */
    		}
    
    		/* There is enough space. Now we need to do a deep copy! */
    		/* Allocation in buffer:
    			from [0] to [(naddr-1) * sizeof(*p)]:
    			pointers to addresses
    			at [naddr * sizeof(*p)]:
    			NULL
    			from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
    			pointers to aliases
    			at [(naddr+naliases+1) * sizeof(*p)]:
    			NULL
    			then naddr addresses (fixed length), and naliases aliases (asciiz).
    		*/
    
    		*ret = *ph;   /* copy whole structure (not its address!) */
    
    		/* copy addresses */
    		q = (char **)buf; /* pointer to pointers area (type: char **) */
    		ret->h_addr_list = q; /* update pointer to address list */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		pbuf = buf + ((naddr + naliases + 2) * sizeof(*p)); /* skip that area */
    
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += ph->h_length; /* advance pbuf */
    		}
    		*q++ = NULL; /* address list terminator */
    
    		/* copy aliases */
    		ret->h_aliases = q; /* update pointer to aliases list */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			strcpy(pbuf, *p); /* copy alias strings */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += strlen(*p); /* advance pbuf */
    			*pbuf++ = 0; /* string terminator */
    		}
    		*q++ = NULL; /* terminator */
    
    		strcpy(pbuf, ph->h_name); /* copy alias strings */
    		ret->h_name = pbuf;
    		pbuf += strlen(ph->h_name); /* advance pbuf */
    		*pbuf++ = 0; /* string terminator */
    
    		*result = ret;  /* and let *result point to structure */
    
    	}
    	h_errno = hsave;  /* restore h_errno */
    	ast_mutex_unlock(&__mutex); /* end critical area */
    
    
    	return (*result == NULL); /* return 0 on success, non-zero on error */
    
    /*! \brief Re-entrant (thread safe) version of gethostbyname that replaces the
    
       standard gethostbyname (which is not thread safe)
    
    struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
    {
    	int res;
    	int herrno;
    
    	int dots = 0;
    
    	const char *s;
    
    	struct hostent *result = NULL;
    
    	/* Although it is perfectly legitimate to lookup a pure integer, for
    	   the sake of the sanity of people who like to name their peers as
    	   integers, we break with tradition and refuse to look up a
    	   pure integer */
    	s = host;
    
    		if (*s == '.')
    			dots++;
    		else if (!isdigit(*s))
    
    	if (!s || !*s) {
    		/* Forge a reply for IP's to avoid octal IP's being interpreted as octal */
    		if (dots != 3)
    			return NULL;
    
    		memset(hp, 0, sizeof(struct ast_hostent));
    
    		hp->hp.h_addrtype = AF_INET;
    
    		hp->hp.h_addr_list = (void *) hp->buf;
    
    		hp->hp.h_addr = hp->buf + sizeof(void *);
    
    		/* For AF_INET, this will always be 4 */
    		hp->hp.h_length = 4;
    
    		if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0)
    			return &hp->hp;
    
    #ifdef HAVE_GETHOSTBYNAME_R_5
    
    	result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
    
    	if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    		return NULL;
    #else
    
    	res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
    
    
    	if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Produce 32 char MD5 hash of value. */
    
    void ast_md5_hash(char *output, const char *input)
    
    	struct MD5Context md5;
    	unsigned char digest[16];
    	char *ptr;
    	int x;
    
    	MD5Init(&md5);
    
    	MD5Update(&md5, (const unsigned char *) input, strlen(input));
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	for (x = 0; x < 16; x++)
    
    		ptr += sprintf(ptr, "%02hhx", digest[x]);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Produce 40 char SHA1 hash of value. */
    
    void ast_sha1_hash(char *output, const char *input)
    
    {
    	struct SHA1Context sha;
    	char *ptr;
    	int x;
    	uint8_t Message_Digest[20];
    
    	SHA1Reset(&sha);
    
    	SHA1Input(&sha, (const unsigned char *) input, strlen(input));
    
    	SHA1Result(&sha, Message_Digest);
    	ptr = output;
    	for (x = 0; x < 20; x++)
    
    		ptr += sprintf(ptr, "%02hhx", Message_Digest[x]);
    
    /*! \brief Produce a 20 byte SHA1 hash of value. */
    void ast_sha1_hash_uint(uint8_t *digest, const char *input)
    {
            struct SHA1Context sha;
    
            SHA1Reset(&sha);
    
            SHA1Input(&sha, (const unsigned char *) input, strlen(input));
    
            SHA1Result(&sha, digest);
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief decode BASE64 encoded text */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_base64decode(unsigned char *dst, const char *src, int max)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int cnt = 0;
    	unsigned int byte = 0;
    	unsigned int bits = 0;
    	int incnt = 0;
    
    	while(*src && *src != '=' && (cnt < max)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Shift in 6 bits of input */
    		byte <<= 6;
    		byte |= (b2a[(int)(*src)]) & 0x3f;
    		bits += 6;
    		src++;
    		incnt++;
    
    		/* If we have at least 8 bits left over, take that character
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   off the top */
    		if (bits >= 8)  {
    			bits -= 8;
    			*dst = (byte >> bits) & 0xff;
    			dst++;
    			cnt++;
    		}
    	}
    
    	/* Don't worry about left over bits, they're extra anyway */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return cnt;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief encode text to BASE64 coding */
    
    int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int cnt = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	unsigned int byte = 0;
    	int bits = 0;
    	int cntin = 0;
    
    	/* Reserve space for null byte at end of string */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	max--;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	while ((cntin < srclen) && (cnt < max)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		byte <<= 8;
    		byte |= *(src++);
    		bits += 8;
    		cntin++;
    
    		if ((bits == 24) && (cnt + 4 <= max)) {
    
    			*dst++ = base64[(byte >> 18) & 0x3f];
    			*dst++ = base64[(byte >> 12) & 0x3f];
    			*dst++ = base64[(byte >> 6) & 0x3f];
    			*dst++ = base64[byte & 0x3f];
    			cnt += 4;
    			col += 4;
    			bits = 0;
    			byte = 0;
    		}
    		if (linebreaks && (cnt < max) && (col == 64)) {
    			*dst++ = '\n';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cnt++;
    
    	if (bits && (cnt + 4 <= max)) {
    
    		/* Add one last character for the remaining bits,
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   padding the rest with 0 */
    
    		byte <<= 24 - bits;
    		*dst++ = base64[(byte >> 18) & 0x3f];
    		*dst++ = base64[(byte >> 12) & 0x3f];
    		if (bits == 16)
    			*dst++ = base64[(byte >> 6) & 0x3f];
    		else
    			*dst++ = '=';
    		*dst++ = '=';
    		cnt += 4;
    	}
    	if (linebreaks && (cnt < max)) {
    		*dst++ = '\n';
    
    Mark Spencer's avatar
    Mark Spencer committed
    		cnt++;
    	}
    	*dst = '\0';
    	return cnt;
    }
    
    
    int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
    {
    	return ast_base64encode_full(dst, src, srclen, max, 0);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void base64_init(void)
    {
    	int x;
    	memset(b2a, -1, sizeof(b2a));
    	/* Initialize base-64 Conversion table */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	for (x = 0; x < 26; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* A-Z */
    		base64[x] = 'A' + x;
    		b2a['A' + x] = x;
    		/* a-z */
    		base64[x + 26] = 'a' + x;
    		b2a['a' + x] = x + 26;
    		/* 0-9 */
    		if (x < 10) {
    			base64[x + 52] = '0' + x;
    			b2a['0' + x] = x + 52;
    		}
    	}
    	base64[62] = '+';
    	base64[63] = '/';
    	b2a[(int)'+'] = 62;
    	b2a[(int)'/'] = 63;
    }
    
    
    const struct ast_flags ast_uri_http = {AST_URI_UNRESERVED};
    const struct ast_flags ast_uri_http_legacy = {AST_URI_LEGACY_SPACE | AST_URI_UNRESERVED};
    const struct ast_flags ast_uri_sip_user = {AST_URI_UNRESERVED | AST_URI_SIP_USER_UNRESERVED};
    
    char *ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_flags spec)
    
    	const char *ptr  = string;	/* Start with the string */
    
    	char *out = outbuf;
    
    	const char *mark = "-_.!~*'()"; /* no encode set, RFC 2396 section 2.3, RFC 3261 sec 25 */
    
    	const char *user_unreserved = "&=+$,;?/"; /* user-unreserved set, RFC 3261 sec 25 */
    
    	while (*ptr && out - outbuf < buflen - 1) {
    
    		if (ast_test_flag(&spec, AST_URI_LEGACY_SPACE) && *ptr == ' ') {
    			/* for legacy encoding, encode spaces as '+' */
    			*out = '+';
    			out++;
    		} else if (!(ast_test_flag(&spec, AST_URI_MARK)
    				&& strchr(mark, *ptr))
    			&& !(ast_test_flag(&spec, AST_URI_ALPHANUM)
    				&& ((*ptr >= '0' && *ptr <= '9')
    				|| (*ptr >= 'A' && *ptr <= 'Z')
    				|| (*ptr >= 'a' && *ptr <= 'z')))
    			&& !(ast_test_flag(&spec, AST_URI_SIP_USER_UNRESERVED)
    				&& strchr(user_unreserved, *ptr))) {
    
    
    			if (out - outbuf >= buflen - 3) {
    				break;
    
    			out += sprintf(out, "%%%02hhX", (unsigned char) *ptr);
    
    			*out = *ptr;	/* Continue copying the string */
    			out++;
    
    void ast_uri_decode(char *s, struct ast_flags spec)
    
    {
    	char *o;
    	unsigned int tmp;
    
    	for (o = s; *s; s++, o++) {
    
    		if (ast_test_flag(&spec, AST_URI_LEGACY_SPACE) && *s == '+') {
    			/* legacy mode, decode '+' as space */
    			*o = ' ';
    		} else if (*s == '%' && s[1] != '\0' && s[2] != '\0' && sscanf(s + 1, "%2x", &tmp) == 1) {
    
    			/* have '%', two chars and correct parsing */
    			*o = tmp;
    			s += 2;	/* Will be incremented once more when we break out */
    		} else /* all other cases, just copy */
    			*o = *s;
    	}
    	*o = '\0';
    }
    
    
    char *ast_escape_quoted(const char *string, char *outbuf, int buflen)
    {
    	const char *ptr  = string;
    	char *out = outbuf;
    	char *allow = "\t\v !"; /* allow LWS (minus \r and \n) and "!" */
    
    	while (*ptr && out - outbuf < buflen - 1) {
    		if (!(strchr(allow, *ptr))
    			&& !(*ptr >= '#' && *ptr <= '[') /* %x23 - %x5b */
    			&& !(*ptr >= ']' && *ptr <= '~') /* %x5d - %x7e */
    
    			&& !((unsigned char) *ptr > 0x7f)) {             /* UTF8-nonascii */
    
    
    			if (out - outbuf >= buflen - 2) {
    				break;
    			}
    			out += sprintf(out, "\\%c", (unsigned char) *ptr);
    		} else {
    			*out = *ptr;
    			out++;
    		}
    		ptr++;
    	}
    
    	if (buflen) {
    		*out = '\0';
    	}
    
    	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)
    
    	if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN)))
    		return "";
    
    
    	return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
    
    static int dev_urandom_fd = -1;
    
    #ifndef __linux__
    #undef pthread_create /* For ast_pthread_create function only */
    
    #if !defined(LOW_MEMORY)
    
    
    #ifdef DEBUG_THREADS
    
    /*! \brief A reasonable maximum number of locks a thread would be holding ... */
    
    #define AST_MAX_LOCKS 64
    
    
    /* Allow direct use of pthread_mutex_t and friends */
    #undef pthread_mutex_t
    #undef pthread_mutex_lock
    #undef pthread_mutex_unlock
    #undef pthread_mutex_init
    #undef pthread_mutex_destroy
    
    
    /*!
     * \brief Keep track of which locks a thread holds
    
     *
     * There is an instance of this struct for every active thread
     */
    struct thr_lock_info {
    	/*! The thread's ID */
    	pthread_t thread_id;
    	/*! The thread name which includes where the thread was started */
    	const char *thread_name;
    	/*! This is the actual container of info for what locks this thread holds */
    	struct {
    		const char *file;
    		const char *func;
    		const char *lock_name;
    		void *lock_addr;
    		int times_locked;
    
    		enum ast_lock_type type;
    
    		/*! This thread is waiting on this lock */
    
    		/*! A condition has suspended this lock */
    		int suspended:1;
    
    #ifdef HAVE_BKTR
    		struct ast_bt *backtrace;
    #endif
    
    	} locks[AST_MAX_LOCKS];
    	/*! This is the number of locks currently held by this thread.
    	 *  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;
    	AST_LIST_ENTRY(thr_lock_info) entry;
    };
    
    
    /*!
     * \brief Locked when accessing the lock_infos list
    
     */
    AST_MUTEX_DEFINE_STATIC(lock_infos_lock);
    /*!
    
     * \brief A list of each thread's lock info
    
     */
    static AST_LIST_HEAD_NOLOCK_STATIC(lock_infos, thr_lock_info);
    
    /*!
     * \brief Destroy a thread's lock info
     *
     * This gets called automatically when the thread stops
     */
    static void lock_info_destroy(void *data)
    {
    	struct thr_lock_info *lock_info = data;
    
    
    	pthread_mutex_lock(&lock_infos_lock.mutex);
    	AST_LIST_REMOVE(&lock_infos, lock_info, entry);
    	pthread_mutex_unlock(&lock_infos_lock.mutex);
    
    
    
    	for (i = 0; i < lock_info->num_locks; i++) {
    
    		if (lock_info->locks[i].pending == -1) {
    			/* This just means that the last lock this thread went for was by
    			 * using trylock, and it failed.  This is fine. */
    			break;
    		}
    
    
    		ast_log(LOG_ERROR,
    			"Thread '%s' still has a lock! - '%s' (%p) from '%s' in %s:%d!\n",
    
    			lock_info->thread_name,
    			lock_info->locks[i].lock_name,
    			lock_info->locks[i].lock_addr,
    			lock_info->locks[i].func,
    			lock_info->locks[i].file,
    			lock_info->locks[i].line_num
    		);
    	}
    
    
    	pthread_mutex_destroy(&lock_info->lock);
    
    	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
    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)
    
    {
    	struct thr_lock_info *lock_info;
    	int i;
    
    	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
    		return;
    
    	pthread_mutex_lock(&lock_info->lock);
    
    	for (i = 0; i < lock_info->num_locks; i++) {
    		if (lock_info->locks[i].lock_addr == lock_addr) {
    			lock_info->locks[i].times_locked++;
    
    #ifdef HAVE_BKTR
    			lock_info->locks[i].backtrace = bt;
    #endif
    
    			pthread_mutex_unlock(&lock_info->lock);
    			return;
    		}
    	}
    
    	if (lock_info->num_locks == AST_MAX_LOCKS) {
    		/* Can't use ast_log here, because it will cause infinite recursion */
    		fprintf(stderr, "XXX ERROR XXX A thread holds more locks than '%d'."
    			"  Increase AST_MAX_LOCKS!\n", AST_MAX_LOCKS);
    		pthread_mutex_unlock(&lock_info->lock);
    		return;
    	}
    
    	if (i && lock_info->locks[i - 1].pending == -1) {
    
    		/* The last lock on the list was one that this thread tried to lock but
    		 * failed at doing so.  It has now moved on to something else, so remove
    		 * the old lock from the list. */
    		i--;
    		lock_info->num_locks--;
    		memset(&lock_info->locks[i], 0, sizeof(lock_info->locks[0]));
    	}
    
    
    	lock_info->locks[i].file = filename;
    	lock_info->locks[i].line_num = line_num;
    	lock_info->locks[i].func = func;
    	lock_info->locks[i].lock_name = lock_name;
    	lock_info->locks[i].lock_addr = lock_addr;
    	lock_info->locks[i].times_locked = 1;
    
    	lock_info->locks[i].type = type;
    
    	lock_info->locks[i].pending = 1;
    
    #ifdef HAVE_BKTR
    	lock_info->locks[i].backtrace = bt;
    #endif
    
    	lock_info->num_locks++;
    
    	pthread_mutex_unlock(&lock_info->lock);
    }
    
    
    void ast_mark_lock_acquired(void *lock_addr)
    
    {
    	struct thr_lock_info *lock_info;
    
    	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
    		return;
    
    	pthread_mutex_lock(&lock_info->lock);
    
    	if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
    		lock_info->locks[lock_info->num_locks - 1].pending = 0;
    	}
    
    	pthread_mutex_unlock(&lock_info->lock);
    }
    
    
    void ast_mark_lock_failed(void *lock_addr)
    
    {
    	struct thr_lock_info *lock_info;
    
    	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
    		return;
    
    	pthread_mutex_lock(&lock_info->lock);
    
    	if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
    		lock_info->locks[lock_info->num_locks - 1].pending = -1;
    		lock_info->locks[lock_info->num_locks - 1].times_locked--;
    	}
    
    	pthread_mutex_unlock(&lock_info->lock);
    }
    
    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)
    
    {
    	struct thr_lock_info *lock_info;
    	int i = 0;
    
    	if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
    		return -1;
    
    	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 -1;
    	}
    
    
    	ast_copy_string(filename, lock_info->locks[i].file, filename_size);
    
    	*lineno = lock_info->locks[i].line_num;
    
    	ast_copy_string(func, lock_info->locks[i].func, func_size);
    	ast_copy_string(mutex_name, lock_info->locks[i].lock_name, mutex_name_size);
    
    
    	pthread_mutex_unlock(&lock_info->lock);
    
    
    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
    
    void ast_remove_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;
    	}
    
    	if (lock_info->locks[i].times_locked > 1) {
    		lock_info->locks[i].times_locked--;
    
    #ifdef HAVE_BKTR
    		lock_info->locks[i].backtrace = bt;
    #endif
    
    		pthread_mutex_unlock(&lock_info->lock);
    		return;
    	}
    
    	if (i < lock_info->num_locks - 1) {
    		/* Not the last one ... *should* be rare! */
    
    		memmove(&lock_info->locks[i], &lock_info->locks[i + 1],
    
    			(lock_info->num_locks - (i + 1)) * sizeof(lock_info->locks[0]));
    	}
    
    	lock_info->num_locks--;
    
    	pthread_mutex_unlock(&lock_info->lock);
    }
    
    
    static const char *locktype2str(enum ast_lock_type type)
    {
    	switch (type) {
    	case AST_MUTEX:
    		return "MUTEX";
    	case AST_RDLOCK:
    		return "RDLOCK";
    	case AST_WRLOCK:
    		return "WRLOCK";
    	}
    
    	return "UNKNOWN";
    }
    
    
    #ifdef HAVE_BKTR
    static void append_backtrace_information(struct ast_str **str, struct ast_bt *bt)
    {
    	char **symbols;
    
    
    	if (!bt) {
    		ast_str_append(str, 0, "\tNo backtrace to print\n");
    		return;
    	}
    
    
    	/* store frame count locally to avoid the memory corruption that
    	 * sometimes happens on virtualized CentOS 6.x systems */
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    	num_frames = bt->num_frames;
    
    	if ((symbols = ast_bt_get_symbols(bt->addresses, num_frames))) {
    
    		for (frame_iterator = 0; frame_iterator < num_frames; ++frame_iterator) {
    
    			ast_str_append(str, 0, "\t%s\n", symbols[frame_iterator]);
    		}
    
    	} else {
    		ast_str_append(str, 0, "\tCouldn't retrieve backtrace symbols\n");
    	}
    }
    #endif
    
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    static void append_lock_information(struct ast_str **str, struct thr_lock_info *lock_info, int i)
    {
    	int j;
    	ast_mutex_t *lock;
    
    	ast_str_append(str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d%s)\n",
    
    				   lock_info->locks[i].pending > 0 ? "Waiting for " :
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    				   lock_info->locks[i].pending < 0 ? "Tried and failed to get " : "", i,
    
    				   lock_info->locks[i].file,
    
    Steve Murphy's avatar
     
    Steve Murphy committed
    				   locktype2str(lock_info->locks[i].type),
    				   lock_info->locks[i].line_num,
    				   lock_info->locks[i].func, lock_info->locks[i].lock_name,
    
    				   lock_info->locks[i].lock_addr,