Newer
Older
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 ((a = ast_malloc(sizeof(*a)))) {
a->start_routine = start_routine;
a->data = data;
start_routine = dummy_start;
Kevin P. Fleming
committed
if (asprintf(&a->name, "%-20s started at [%5d] %s %s()",
start_fn, line, file, caller) < 0) {
ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
a->name = NULL;
}
return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
}
Russell Bryant
committed
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
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 = 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);
static int ast_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) {
if (res == 0) {
/* timed out. */
ast_log(LOG_NOTICE, "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;
}
}
return 0;
}
/*!
* 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();
int res = 0;
while (len) {
if (ast_wait_for_output(fd, timeoutms - elapsed)) {
return -1;
res = write(fd, s, len);
if (res < 0 && errno != EAGAIN && errno != EINTR) {
/* fatal error from write() */
ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
return -1;
}
if (res < 0) {
/* It was an acceptable error */
res = 0;
}
/* Update how much data we have left to write */
len -= res;
s += res;
res = 0;
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;
}
return res;
}
int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
{
struct timeval start = ast_tvnow();
int n = 0;
while (len) {
if (ast_wait_for_output(fd, timeoutms - elapsed)) {
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
/* 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);
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
/* 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) {
continue;
}
if (!feof(f)) {
/* Don't spam the logs if it was just that the connection is closed. */
ast_log(LOG_ERROR, "fflush() returned error: %s\n", strerror(errno));
}
n = -1;
break;
}
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_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;
}
}
return s;
}
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
/* !\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_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;
}
Kevin P. Fleming
committed
#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;
Kevin P. Fleming
committed
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);
Kevin P. Fleming
committed
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
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
/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
Joshua Colp
committed
#ifndef linux
Joshua Colp
committed
#endif
long int ast_random(void)
{
long int res;
Joshua Colp
committed
#ifdef HAVE_DEV_URANDOM
if (dev_urandom_fd >= 0) {
int read_res = read(dev_urandom_fd, &res, sizeof(res));
Joshua Colp
committed
if (read_res > 0) {
Joshua Colp
committed
long int rm = RAND_MAX;
Joshua Colp
committed
res = res < 0 ? ~res : res;
Joshua Colp
committed
rm++;
return res % rm;
Joshua Colp
committed
}
Joshua Colp
committed
}
#endif
#ifdef linux
res = random();
#else
ast_mutex_lock(&randomlock);
res = random();
ast_mutex_unlock(&randomlock);
Joshua Colp
committed
#endif
Russell Bryant
committed
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;
Russell Bryant
committed
if (*start == '\\') {
inEscape = 1; /* Do not copy \ into the data */
} else if (*start == '\'') {
inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
Russell Bryant
committed
} else {
/* Replace , with |, unless in quotes */
*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
Russell Bryant
committed
}
}
}
if (start != dataPut)
*dataPut = 0;
return dataPut;
}
Russell Bryant
committed
void ast_join(char *s, size_t len, char * const w[])
Russell Bryant
committed
{
int x, ofs = 0;
const char *src;
/* Join words into a string */
if (!s)
return;
Russell Bryant
committed
if (x > 0)
s[ofs++] = ' ';
for (src = w[x]; *src && ofs < len; src++)
s[ofs++] = *src;
}
if (ofs == len)
ofs--;
s[ofs] = '\0';
}
/*
* stringfields support routines.
*/
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
/* 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)
{
struct ast_string_field_pool *pool;
size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
if (!(pool = ast_calloc(1, alloc_size))) {
pool->prev = *pool_head;
pool->size = alloc_size - sizeof(*pool);
*pool_head = pool;
/*
* 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.
* 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 size)
const char **p = (const char **) pool_head + 1;
struct ast_string_field_pool *cur = *pool_head;
/* clear fields - this is always necessary */
while ((struct ast_string_field_mgr *) p != mgr) {
*p++ = __ast_string_field_empty;
if (size > 0) { /* allocate the initial pool */
*pool_head = NULL;
return add_string_pool(mgr, pool_head, size);
}
if (size < 0) { /* reset all pools */
*pool_head = NULL;
} else { /* preserve the first pool */
if (cur == NULL) {
ast_log(LOG_WARNING, "trying to reset empty pool\n");
return -1;
}
cur = cur->prev;
(*pool_head)->prev = NULL;
(*pool_head)->used = (*pool_head)->active = 0;
while (cur) {
struct ast_string_field_pool *prev = cur->prev;
ast_free(cur);
cur = prev;
}
ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head, size_t needed)
{
char *result = NULL;
size_t space = (*pool_head)->size - (*pool_head)->used;
size_t to_alloc = needed + sizeof(ast_string_field_allocation);
if (__builtin_expect(to_alloc > space, 0)) {
size_t new_size = (*pool_head)->size;
new_size *= 2;
if (add_string_pool(mgr, pool_head, new_size))
return NULL;
}
result = (*pool_head)->base + (*pool_head)->used;
(*pool_head)->used += to_alloc;
(*pool_head)->active += needed;
result += sizeof(ast_string_field_allocation);
AST_STRING_FIELD_ALLOCATION(result) = needed;
return result;
}
Kevin P. Fleming
committed
int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head, size_t needed,
const ast_string_field *ptr)
{
ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
size_t space = (*pool_head)->size - (*pool_head)->used;
if (*ptr != mgr->last_alloc) {
return 1;
}
if (space < grow) {
return 1;
}
(*pool_head)->used += grow;
(*pool_head)->active += grow;
AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
return 0;
}
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
const ast_string_field ptr)
{
struct ast_string_field_pool *pool, *prev;
if (ptr == __ast_string_field_empty) {
return;
}
for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
if ((pool->active == 0) && prev) {
prev->prev = pool->prev;
ast_free(pool);
}
break;
}
}
}
void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head,
ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
Kevin P. Fleming
committed
{
size_t needed;
size_t space = (*pool_head)->size - (*pool_head)->used;
ssize_t grow;
Kevin P. Fleming
committed
/* if the field already has space allocated, try to reuse it;
otherwise, try to use the empty space at the end of the current
if (*ptr != __ast_string_field_empty) {
available = AST_STRING_FIELD_ALLOCATION(*ptr);
if (*ptr == mgr->last_alloc) {
available += space;
}
target = (*pool_head)->base + (*pool_head)->used + sizeof(ast_string_field_allocation);
available = space - sizeof(ast_string_field_allocation);
Kevin P. Fleming
committed
needed = vsnprintf(target, available, format, ap1) + 1;
/* the allocation could not be satisfied using the field's current allocation
(if it has one), or the space available in the pool (if it does not). allocate
space for it, adding a new string pool if necessary.
if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
return;
}
vsprintf(target, format, ap2);
__ast_string_field_release_active(*pool_head, *ptr);
*ptr = target;
} else if (*ptr != target) {
/* the allocation was satisfied using available space in the pool, but not
using the space already allocated to the field
*/
__ast_string_field_release_active(*pool_head, *ptr);
mgr->last_alloc = *ptr = target;
AST_STRING_FIELD_ALLOCATION(target) = needed;
(*pool_head)->used += needed + sizeof(ast_string_field_allocation);
(*pool_head)->active += needed;
} else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
/* the allocation was satisfied by using available space in the pool *and*
the field was the last allocated field from the pool, so it grew
*/
(*pool_head)->used += grow;
(*pool_head)->active += grow;
AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head,
ast_string_field *ptr, const char *format, ...)
{
va_list ap1, ap2;
Kevin P. Fleming
committed
va_start(ap1, format);
va_start(ap2, format); /* va_copy does not exist on FreeBSD */
__ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap1, ap2);
Kevin P. Fleming
committed
va_end(ap2);
}
/* end of stringfields support */
AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
{
int ret;
ast_mutex_lock(&fetchadd_m);
ret = *p;
*p += v;
ast_mutex_unlock(&fetchadd_m);
return ret;
Tilghman Lesher
committed
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
/*! \brief
* get values from config variables.
*/
int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
{
long double dtv = 0.0;
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, "%Lf%n", &dtv, &scanned) > 0) {
dst->tv_sec = dtv;
dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
if (consumed)
*consumed = scanned;
return 0;
} else
return -1;
}
* get values from config variables.
*/
Kevin P. Fleming
committed
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
{
long t;
Kevin P. Fleming
committed
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 */
Kevin P. Fleming
committed
if (sscanf(src, "%ld%n", &t, &scanned) == 1) {
*dst = t;
Kevin P. Fleming
committed
if (consumed)
*consumed = scanned;
return 0;
} else
return -1;
}
void ast_enable_packet_fragmentation(int sock)
{
#if defined(HAVE_IP_MTU_DISCOVER)
int val = IP_PMTUDISC_DONT;
if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
#endif /* HAVE_IP_MTU_DISCOVER */
Tilghman Lesher
committed
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
int ast_mkdir(const char *path, int mode)
{
char *ptr;
int len = strlen(path), count = 0, x, piececount = 0;
char *tmp = ast_strdupa(path);
char **pieces;
char *fullpath = alloca(len + 1);
int res = 0;
for (ptr = tmp; *ptr; ptr++) {
if (*ptr == '/')
count++;
}
/* Count the components to the directory path */
pieces = alloca(count * sizeof(*pieces));
for (ptr = tmp; *ptr; ptr++) {
if (*ptr == '/') {
*ptr = '\0';
pieces[piececount++] = ptr + 1;
}
}
*fullpath = '\0';
for (x = 0; x < piececount; x++) {
/* This looks funky, but the buffer is always ideally-sized, so it's fine. */
strcat(fullpath, "/");
strcat(fullpath, pieces[x]);
res = mkdir(fullpath, mode);
if (res && errno != EEXIST)
return errno;
}
return 0;
}
int ast_utils_init(void)
{
#ifdef HAVE_DEV_URANDOM
dev_urandom_fd = open("/dev/urandom", O_RDONLY);
#endif
base64_init();
#ifdef DEBUG_THREADS
ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
#ifndef __AST_DEBUG_MALLOC
int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
{
int res;
va_list ap;
va_start(ap, fmt);
if ((res = vasprintf(ret, fmt, ap)) == -1) {
MALLOC_FAILURE_MSG;
}
va_end(ap);
return res;
}
#endif