From 8b0c007ad990aa27d9868da49215fd1076ac77cc Mon Sep 17 00:00:00 2001 From: kpfleming Date: Mon, 21 Aug 2006 02:11:39 +0000 Subject: merge new_loader_completion branch, including (at least): - restructured build tree and makefiles to eliminate recursion problems - support for embedded modules - support for static builds - simpler cross-compilation support - simpler module/loader interface (no exported symbols) git-svn-id: http://svn.digium.com/svn/asterisk/trunk@40722 f38db490-d61c-443f-a65b-d21fe96a405b --- main/utils.c | 1231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1231 insertions(+) create mode 100644 main/utils.c (limited to 'main/utils.c') diff --git a/main/utils.c b/main/utils.c new file mode 100644 index 000000000..0aaa791a5 --- /dev/null +++ b/main/utils.c @@ -0,0 +1,1231 @@ +/* + * 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. + */ + +/*! \file + * + * \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. + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/logger.h" +#include "asterisk/md5.h" +#include "asterisk/sha1.h" +#include "asterisk/options.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" + +static char base64[64]; +static char b2a[256]; + +AST_THREADSTORAGE(inet_ntoa_buf, inet_ntoa_buf_init); + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) || defined(__CYGWIN__) + +#define ERANGE 34 /*!< duh? ERANGE value copied from web... */ +#undef gethostbyname + +AST_MUTEX_DEFINE_STATIC(__mutex); + +/*! \brief Reentrant replacement for gethostbyname for BSD-based systems. +\note This +routine is derived from code originally written and placed in the public +domain by Enzo Michelangeli */ + +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 */ + 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 */ + 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 */ +} + + +#endif + +/*! \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; + res = 0; + while(s && *s) { + if (*s == '.') + dots++; + else if (!isdigit(*s)) + break; + 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_addr_list = (void *) hp->buf; + hp->hp.h_addr = hp->buf + sizeof(void *); + if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0) + return &hp->hp; + return NULL; + + } +#ifdef SOLARIS + 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]) + return NULL; +#endif + return &hp->hp; +} + + + +AST_MUTEX_DEFINE_STATIC(test_lock); +AST_MUTEX_DEFINE_STATIC(test_lock2); +static pthread_t test_thread; +static int lock_count = 0; +static int test_errors = 0; + +/*! \brief This is a regression test for recursive mutexes. + test_for_thread_safety() will return 0 if recursive mutex locks are + working properly, and non-zero if they are not working properly. */ +static void *test_thread_body(void *data) +{ + ast_mutex_lock(&test_lock); + lock_count += 10; + if (lock_count != 10) + test_errors++; + ast_mutex_lock(&test_lock); + lock_count += 10; + if (lock_count != 20) + test_errors++; + ast_mutex_lock(&test_lock2); + ast_mutex_unlock(&test_lock); + lock_count -= 10; + if (lock_count != 10) + test_errors++; + ast_mutex_unlock(&test_lock); + lock_count -= 10; + ast_mutex_unlock(&test_lock2); + if (lock_count != 0) + test_errors++; + return NULL; +} + +int test_for_thread_safety(void) +{ + ast_mutex_lock(&test_lock2); + ast_mutex_lock(&test_lock); + lock_count += 1; + ast_mutex_lock(&test_lock); + lock_count += 1; + ast_pthread_create(&test_thread, NULL, test_thread_body, NULL); + usleep(100); + if (lock_count != 2) + test_errors++; + ast_mutex_unlock(&test_lock); + lock_count -= 1; + usleep(100); + if (lock_count != 1) + test_errors++; + ast_mutex_unlock(&test_lock); + lock_count -= 1; + if (lock_count != 0) + test_errors++; + ast_mutex_unlock(&test_lock2); + usleep(100); + if (lock_count != 0) + test_errors++; + pthread_join(test_thread, NULL); + return(test_errors); /* return 0 on success. */ +} + +/*! \brief Produce 32 char MD5 hash of value. */ +void ast_md5_hash(char *output, char *input) +{ + struct MD5Context md5; + unsigned char digest[16]; + char *ptr; + int x; + + MD5Init(&md5); + MD5Update(&md5, (unsigned char *)input, strlen(input)); + MD5Final(digest, &md5); + ptr = output; + for (x = 0; x < 16; x++) + ptr += sprintf(ptr, "%2.2x", digest[x]); +} + +/*! \brief Produce 40 char SHA1 hash of value. */ +void ast_sha1_hash(char *output, 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, "%2.2x", Message_Digest[x]); +} + +/*! \brief decode BASE64 encoded text */ +int ast_base64decode(unsigned char *dst, const char *src, int max) +{ + int cnt = 0; + unsigned int byte = 0; + unsigned int bits = 0; + int incnt = 0; + while(*src && (cnt < max)) { + /* 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 + off the top */ + if (bits >= 8) { + bits -= 8; + *dst = (byte >> bits) & 0xff; + dst++; + cnt++; + } + } + /* Dont worry about left over bits, they're extra anyway */ + return cnt; +} + +/*! \brief encode text to BASE64 coding */ +int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks) +{ + int cnt = 0; + int col = 0; + unsigned int byte = 0; + int bits = 0; + int cntin = 0; + /* Reserve space for null byte at end of string */ + max--; + while ((cntin < srclen) && (cnt < max)) { + 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'; + cnt++; + col = 0; + } + } + if (bits && (cnt + 4 < max)) { + /* Add one last character for the remaining bits, + 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'; + 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); +} + +static void base64_init(void) +{ + int x; + memset(b2a, -1, sizeof(b2a)); + /* Initialize base-64 Conversion table */ + for (x = 0; x < 26; x++) { + /* 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; +} + +/*! \brief ast_uri_encode: Turn text string to URI-encoded %XX version +\note At this point, we're converting from ISO-8859-x (8-bit), not UTF8 + as in the SIP protocol spec + If doreserved == 1 we will convert reserved characters also. + RFC 2396, section 2.4 + outbuf needs to have more memory allocated than the instring + to have room for the expansion. Every char that is converted + is replaced by three ASCII characters. + + Note: The doreserved option is needed for replaces header in + SIP transfers. +*/ +char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserved) +{ + char *reserved = ";/?:@&=+$, "; /* Reserved chars */ + + const char *ptr = string; /* Start with the string */ + char *out = NULL; + char *buf = NULL; + + strncpy(outbuf, string, buflen); + + /* If there's no characters to convert, just go through and don't do anything */ + while (*ptr) { + if (((unsigned char) *ptr) > 127 || (doreserved && strchr(reserved, *ptr)) ) { + /* Oops, we need to start working here */ + if (!buf) { + buf = outbuf; + out = buf + (ptr - string) ; /* Set output ptr */ + } + out += sprintf(out, "%%%02x", (unsigned char) *ptr); + } else if (buf) { + *out = *ptr; /* Continue copying the string */ + out++; + } + ptr++; + } + if (buf) + *out = '\0'; + return outbuf; +} + +/*! \brief ast_uri_decode: Decode SIP URI, URN, URL (overwrite the string) */ +void ast_uri_decode(char *s) +{ + char *o; + unsigned int tmp; + + for (o = s; *s; s++, o++) { + if (*s == '%' && strlen(s) > 2 && 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'; +} + +/*! \brief ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */ +const char *ast_inet_ntoa(struct in_addr ia) +{ + char *buf; + + if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN))) + return ""; + + return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN); +} + +int ast_utils_init(void) +{ + base64_init(); + return 0; +} + +#ifndef __linux__ +#undef pthread_create /* For ast_pthread_create function only */ +#endif /* !__linux__ */ + +/* + * 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 with the MAC. + */ +static void *dummy_start(void *data) +{ + void *ret; + struct thr_arg a = *((struct thr_arg *)data); /* make a local copy */ + + free(data); + ast_register_thread(a.name); + pthread_cleanup_push(ast_unregister_thread, (void *)pthread_self()); /* on unregister */ + ret = a.start_routine(a.data); + pthread_cleanup_pop(1); + return ret; +} + +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) +{ + struct thr_arg *a; + + pthread_attr_t lattr; + if (!attr) { + pthread_attr_init(&lattr); + attr = &lattr; + } +#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(). */ + errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED); + if (errno) + ast_log(LOG_WARNING, "pthread_attr_setinheritsched returned non-zero: %s\n", strerror(errno)); +#endif + + if (!stacksize) + stacksize = AST_STACKSIZE; + errno = pthread_attr_setstacksize(attr, stacksize); + if (errno) + ast_log(LOG_WARNING, "pthread_attr_setstacksize returned non-zero: %s\n", strerror(errno)); + a = ast_malloc(sizeof(*a)); + if (!a) + ast_log(LOG_WARNING, "no memory, thread %s will not be listed\n", start_fn); + else { /* remap parameters */ + a->start_routine = start_routine; + a->data = data; + start_routine = dummy_start; + asprintf(&a->name, "%-20s started at [%5d] %s %s()", + start_fn, line, file, caller); + data = a; + } + return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */ +} + +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 poll(pfd, 1, ms); +} + +int ast_carefulwrite(int fd, char *s, int len, int timeoutms) +{ + /* Try to write string, but wait no more than ms milliseconds + before timing out */ + int res = 0; + struct pollfd fds[1]; + while (len) { + res = write(fd, s, len); + if ((res < 0) && (errno != EAGAIN)) { + return -1; + } + if (res < 0) + res = 0; + len -= res; + s += res; + res = 0; + if (len) { + fds[0].fd = fd; + fds[0].events = POLLOUT; + /* Wait until writable again */ + res = poll(fds, 1, timeoutms); + if (res < 1) + return -1; + } + } + return res; +} + +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))) { + e = s + strlen(s) - 1; + if (*e == *(end_quotes + (q - beg_quotes))) { + s++; + *e = '\0'; + } + } + + return s; +} + +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_true(const char *s) +{ + if (ast_strlen_zero(s)) + 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) +{ + if (ast_strlen_zero(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", + 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", + 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; +} +#undef ONE_MILLION + +#ifndef HAVE_STRCASESTR +static char *upper(const char *orig, char *buf, int bufsize) +{ + int i = 0; + + while (i < (bufsize - 1) && orig[i]) { + buf[i] = toupper(orig[i]); + i++; + } + + buf[i] = '\0'; + + return buf; +} + +char *strcasestr(const char *haystack, const char *needle) +{ + char *u1, *u2; + int u1len = strlen(haystack) + 1, u2len = strlen(needle) + 1; + + u1 = alloca(u1len); + u2 = alloca(u2len); + if (u1 && u2) { + char *offset; + if (u2len > u1len) { + /* Needle bigger than haystack */ + return NULL; + } + offset = strstr(upper(haystack, u1, u1len), upper(needle, u2, u2len)); + if (offset) { + /* Return the offset into the original string */ + return ((char *)((unsigned long)haystack + (unsigned long)(offset - u1))); + } else { + return NULL; + } + } else { + ast_log(LOG_ERROR, "Out of memory\n"); + return NULL; + } +} +#endif /* !HAVE_STRCASESTR */ + +#ifndef HAVE_STRNLEN +size_t strnlen(const char *s, size_t n) +{ + size_t len; + + for (len = 0; len < n; len++) + if (s[len] == '\0') + break; + + return len; +} +#endif /* !HAVE_STRNLEN */ + +#if !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC) +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = ast_malloc(len + 1); + + if (!new) + return NULL; + + new[len] = '\0'; + return memcpy(new, s, len); +} +#endif /* !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC) */ + +#if !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC) +int vasprintf(char **strp, const char *fmt, va_list ap) +{ + int size; + va_list ap2; + char s; + + *strp = NULL; + va_copy(ap2, ap); + size = vsnprintf(&s, 1, fmt, ap2); + va_end(ap2); + *strp = ast_malloc(size + 1); + if (!*strp) + return -1; + vsnprintf(*strp, size + 1, fmt, ap); + + return size; +} +#endif /* !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC) */ + +/* + * Based on Code from bsd-asprintf from OpenSSH + * Copyright (c) 2004 Darren Tucker. + * + * Based originally on asprintf.c from OpenBSD: + * Copyright (c) 1997 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#if !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC) +int asprintf(char **str, const char *fmt, ...) +{ + va_list ap; + int ret; + + *str = NULL; + va_start(ap, fmt); + ret = vasprintf(str, fmt, ap); + va_end(ap); + + return ret; +} +#endif /* !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC) */ + +#ifndef HAVE_STRTOQ +#ifndef LONG_MIN +#define LONG_MIN (-9223372036854775807L-1L) + /* min value of a "long int" */ +#endif +#ifndef LONG_MAX +#define LONG_MAX 9223372036854775807L + /* max value of a "long int" */ +#endif + +/*! \brief + * Convert a string to a quad integer. + * + * \note Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +uint64_t strtoq(const char *nptr, char **endptr, int base) +{ + const char *s; + uint64_t acc; + unsigned char c; + uint64_t qbase, cutoff; + int neg, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '\0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '\0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for quads is + * [-9223372036854775808..9223372036854775807] and the input base + * is 10, cutoff will be set to 922337203685477580 and cutlim to + * either 7 (neg==0) or 8 (neg==1), meaning that if we have + * accumulated a value > 922337203685477580, or equal but the + * next digit is > 7 (or 8), the number is too big, and we will + * return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + qbase = (unsigned)base; + cutoff = neg ? (uint64_t)-(LONG_MIN + LONG_MAX) + LONG_MAX : LONG_MAX; + cutlim = cutoff % qbase; + cutoff /= qbase; + for (acc = 0, any = 0;; c = *s++) { + if (!isascii(c)) + break; + if (isdigit(c)) + c -= '\0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= qbase; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + } else if (neg) + acc = -acc; + if (endptr != 0) + *((const char **)endptr) = any ? s - 1 : nptr; + return acc; +} +#endif /* !HAVE_STRTOQ */ + +#ifndef HAVE_GETLOADAVG +#ifdef linux +/*! \brief Alternative method of getting load avg on Linux only */ +int getloadavg(double *list, int nelem) +{ + FILE *LOADAVG; + double avg[3] = { 0.0, 0.0, 0.0 }; + int i, res = -1; + + if ((LOADAVG = fopen("/proc/loadavg", "r"))) { + fscanf(LOADAVG, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]); + res = 0; + fclose(LOADAVG); + } + + for (i = 0; (i < nelem) && (i < 3); i++) { + list[i] = avg[i]; + } + + return res; +} +#else /* !linux */ +/*! \brief Return something that won't cancel the call, but still return -1, in case + * we correct the implementation to check return value */ +int getloadavg(double *list, int nelem) +{ + int i; + + for (i = 0; i < nelem; i++) { + list[i] = 0.1; + } + return -1; +} +#endif /* linux */ +#endif /* !defined(_BSD_SOURCE) */ + +/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe. + * BSD libc (and others) do not. */ +#ifndef linux + +AST_MUTEX_DEFINE_STATIC(randomlock); + +long int ast_random(void) +{ + long int res; + ast_mutex_lock(&randomlock); + res = random(); + ast_mutex_unlock(&randomlock); + return res; +} +#endif + +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; + } else { + if (*start == '\\') { + inEscape = 1; /* Do not copy \ into the data */ + } else if (*start == '\'') { + inQuotes = 1 - inQuotes; /* Do not copy ' into the data */ + } else { + /* Replace , with |, unless in quotes */ + *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start); + } + } + } + if (start != dataPut) + *dataPut = 0; + return dataPut; +} + +void ast_join(char *s, size_t len, char * const w[]) +{ + int x, ofs = 0; + const char *src; + + /* Join words into a string */ + if (!s) + return; + for (x = 0; ofs < len && w[x]; x++) { + if (x > 0) + s[ofs++] = ' '; + for (src = w[x]; *src && ofs < len; src++) + s[ofs++] = *src; + } + if (ofs == len) + ofs--; + s[ofs] = '\0'; +} + +const char __ast_string_field_empty[] = ""; + +static int add_string_pool(struct ast_string_field_mgr *mgr, size_t size) +{ + struct ast_string_field_pool *pool; + + if (!(pool = ast_calloc(1, sizeof(*pool) + size))) + return -1; + + pool->prev = mgr->pool; + mgr->pool = pool; + mgr->size = size; + mgr->space = size; + mgr->used = 0; + + return 0; +} + +int __ast_string_field_init(struct ast_string_field_mgr *mgr, size_t size, + ast_string_field *fields, int num_fields) +{ + int index; + + if (add_string_pool(mgr, size)) + return -1; + + for (index = 0; index < num_fields; index++) + fields[index] = __ast_string_field_empty; + + return 0; +} + +ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr, size_t needed, + ast_string_field *fields, int num_fields) +{ + char *result = NULL; + + if (__builtin_expect(needed > mgr->space, 0)) { + size_t new_size = mgr->size * 2; + + while (new_size < needed) + new_size *= 2; + + if (add_string_pool(mgr, new_size)) + return NULL; + } + + result = mgr->pool->base + mgr->used; + mgr->used += needed; + mgr->space -= needed; + return result; +} + +void __ast_string_field_index_build(struct ast_string_field_mgr *mgr, + ast_string_field *fields, int num_fields, + int index, const char *format, ...) +{ + size_t needed; + va_list ap1, ap2; + + va_start(ap1, format); + va_start(ap2, format); /* va_copy does not exist on FreeBSD */ + + needed = vsnprintf(mgr->pool->base + mgr->used, mgr->space, format, ap1) + 1; + + va_end(ap1); + + if (needed > mgr->space) { + size_t new_size = mgr->size * 2; + + while (new_size < needed) + new_size *= 2; + + if (add_string_pool(mgr, new_size)) + return; + + vsprintf(mgr->pool->base + mgr->used, format, ap2); + } + + fields[index] = mgr->pool->base + mgr->used; + mgr->used += needed; + mgr->space -= needed; + + va_end(ap2); +} + +AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */ + +int ast_atomic_fetchadd_int_slow(volatile int *p, int v) +{ + int ret; + ast_mutex_lock(&fetchadd_m); + ret = *p; + *p += v; + ast_mutex_unlock(&fetchadd_m); + return ret; +} + +/*! \brief + * get values from config variables. + */ +int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed) +{ + long t; + int scanned; + + if (dst == NULL) + return -1; + + *dst = _default; + + if (ast_strlen_zero(src)) + return -1; + + /* only integer at the moment, but one day we could accept more formats */ + if (sscanf(src, "%ld%n", &t, &scanned) == 1) { + *dst = t; + if (consumed) + *consumed = scanned; + return 0; + } else + return -1; +} + +int ast_dynamic_str_thread_build_va(struct ast_dynamic_str **buf, size_t max_len, + struct ast_threadstorage *ts, int append, const char *fmt, va_list ap) +{ + int res; + int offset = append ? strlen((*buf)->str) : 0; + + res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap); + + /* Check to see if there was not enough space in the string buffer to prepare + * the string. Also, if a maximum length is present, make sure the current + * length is less than the maximum before increasing the size. */ + if ((res + offset + 1) > (*buf)->len && (max_len ? ((*buf)->len < max_len) : 1)) { + /* Set the new size of the string buffer to be the size needed + * to hold the resulting string (res) plus one byte for the + * terminating '\0'. If this size is greater than the max, set + * the new length to be the maximum allowed. */ + if (max_len) + (*buf)->len = ((res + offset + 1) < max_len) ? (res + offset + 1) : max_len; + else + (*buf)->len = res + offset + 1; + + if (!(*buf = ast_realloc(*buf, (*buf)->len + sizeof(*(*buf))))) + return AST_DYNSTR_BUILD_FAILED; + + if (ts) + pthread_setspecific(ts->key, *buf); + + /* va_end() and va_start() must be done before calling + * vsnprintf() again. */ + return AST_DYNSTR_BUILD_RETRY; + } + + return res; +} -- cgit v1.2.3