Newer
Older
Russell Bryant
committed
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 poll(pfd, 1, ms);
}
/*!
* 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.
* Also note that in the current implementation, the delay is per-write,
* so you still have no guarantees, anyways.
* Fortunately the routine is only used in a few places (cli.c, manager.c,
* res_agi.c) so it is reasonably easy to check how it behaves there.
*
* XXX We either need to fix the code, or fix the documentation.
*/
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
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)) && *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;
}
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
/* !\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
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
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.
*/
const char __ast_string_field_empty[] = ""; /*!< the empty string */
/*! \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;
if (!(pool = ast_calloc(1, sizeof(*pool) + size)))
return -1;
pool->prev = *pool_head;
*pool_head = pool;
mgr->size = size;
mgr->used = 0;
return 0;
}
/*
* 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,
Tilghman Lesher
committed
struct ast_string_field_pool **pool_head, int size)
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
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;
mgr->used = 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 = mgr->size - mgr->used;
if (__builtin_expect(needed > space, 0)) {
new_size *= 2;
if (add_string_pool(mgr, pool_head, new_size))
return NULL;
}
result = (*pool_head)->base + mgr->used;
return result;
}
Kevin P. Fleming
committed
__attribute((format (printf, 4, 0)))
void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head,
const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
Kevin P. Fleming
committed
{
size_t needed;
char *dst = (*pool_head)->base + mgr->used;
const char **p = (const char **)ptr;
size_t space = mgr->size - mgr->used;
Kevin P. Fleming
committed
/* try to write using available space */
needed = vsnprintf(dst, space, format, ap1) + 1;
Kevin P. Fleming
committed
va_end(ap1);
if (needed > space) { /* if it fails, reallocate */
size_t new_size = mgr->size * 2;
while (new_size < needed)
new_size *= 2;
if (add_string_pool(mgr, pool_head, new_size))
dst = (*pool_head)->base + mgr->used;
vsprintf(dst, format, ap2);
__attribute((format (printf, 4, 5)))
void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
struct ast_string_field_pool **pool_head,
const 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
1471
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
/*! \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;
}
/*!
* core handler for dynamic strings.
* This is not meant to be called directly, but rather through the
* various wrapper macros
* ast_str_set(...)
* ast_str_append(...)
* ast_str_set_va(...)
* ast_str_append_va(...)
__attribute__((format (printf, 4, 0)))
int __ast_str_helper(struct ast_str **buf, size_t max_len,
int append, const char *fmt, va_list ap)
int offset = (append && (*buf)->len) ? (*buf)->used : 0;
if (max_len < 0)
max_len = (*buf)->len; /* don't exceed the allocated space */
/*
* Ask vsnprintf how much space we need. Remember that vsnprintf
* does not count the final '\0' so we must add 1.
*/
res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
/*
* If there is not enough space and we are below the max length,
* reallocate the buffer and return a message telling to retry.
*/
if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) {
if (max_len && max_len < need) /* truncate as needed */
else if (max_len == 0) /* if unbounded, give more room for next time */
need += 16 + need/4;
if (0) /* debugging */
ast_verbose("extend from %d to %d\n", (int)(*buf)->len, need);
if (ast_str_make_space(buf, need)) {
ast_verbose("failed to extend from %d to %d\n", (int)(*buf)->len, need);
return AST_DYNSTR_BUILD_FAILED;
(*buf)->str[offset] = '\0'; /* Truncate the partial write. */
Russell Bryant
committed
/* va_end() and va_start() must be done before calling
* vsnprintf() again. */
return AST_DYNSTR_BUILD_RETRY;
}
/* update space used, keep in mind the truncation */
(*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset;
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
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
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, sizeof(utils_cli) / sizeof(utils_cli[0]));