Skip to content
Snippets Groups Projects
utils.c 44.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (!stacksize)
    		stacksize = AST_STACKSIZE;
    
    
    	if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
    		ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
    
    
    #if !defined(LOW_MEMORY)
    
    	if ((a = ast_malloc(sizeof(*a)))) {
    
    		a->start_routine = start_routine;
    		a->data = data;
    		start_routine = dummy_start;
    
    		if (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;
    		}
    
    #endif /* !LOW_MEMORY */
    
    	return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
    }
    
    
    int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
    			     void *data, size_t stacksize, const char *file, const char *caller,
    			     int line, const char *start_fn)
    {
    	unsigned char attr_destroy = 0;
    	int res;
    
    	if (!attr) {
    		attr = 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 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 = 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;
    		}
    
    /*!
     * Try to write string, but wait no more than ms milliseconds before timing out.
     *
     * \note The code assumes that the file descriptor has NONBLOCK set,
     * so there is only one system call made to do a write, unless we actually
     * have a need to wait.  This way, we get better performance.
     * If the descriptor is blocking, all assumptions on the guaranteed
     * detail do not apply anymore.
     */
    
    int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
    {
    
    	struct timeval start = ast_tvnow();
    
    		if (ast_wait_for_output(fd, timeoutms - elapsed)) {
    
    
    		if (res < 0 && errno != EAGAIN && errno != EINTR) {
    			/* fatal error from write() */
    			ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
    
    
    		if (res < 0) {
    			/* It was an acceptable error */
    
    		}
    
    		/* Update how much data we have left to write */
    
    
    		elapsed = ast_tvdiff_ms(ast_tvnow(), start);
    		if (elapsed >= timeoutms) {
    			/* We've taken too long to write 
    			 * This is only an error condition if we haven't finished writing. */
    			res = len ? -1 : 0;
    			break;
    		}
    
    int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
    {
    	struct timeval start = ast_tvnow();
    	int n = 0;
    
    		if (ast_wait_for_output(fd, timeoutms - elapsed)) {
    
    			/* poll returned a fatal error, so bail out immediately. */
    			return -1;
    		}
    
    		/* Clear any errors from a previous write */
    		clearerr(f);
    
    		n = fwrite(src, 1, len, f);
    
    		if (ferror(f) && errno != EINTR && errno != EAGAIN) {
    			/* fatal error from fwrite() */
    			if (!feof(f)) {
    				/* Don't spam the logs if it was just that the connection is closed. */
    				ast_log(LOG_ERROR, "fwrite() returned error: %s\n", strerror(errno));
    			}
    			n = -1;
    			break;
    		}
    
    		/* Update for data already written to the socket */
    		len -= n;
    		src += n;
    
    		elapsed = ast_tvdiff_ms(ast_tvnow(), start);
    
    		if (elapsed >= timeoutms) {
    
    			/* We've taken too long to write 
    			 * This is only an error condition if we haven't finished writing. */
    			n = len ? -1 : 0;
    			break;
    		}
    	}
    
    	while (fflush(f)) {
    		if (errno == EAGAIN || errno == EINTR) {
    			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;
    
    		} else {
    			work = e + 1;
    
    /* !\brief unescape some C sequences in place, return pointer to the original string.
     */
    char *ast_unescape_c(char *src)
    {
    	char c, *ret, *dst;
    
    	if (src == NULL)
    		return NULL;
    	for (ret = dst = src; (c = *src++); *dst++ = c ) {
    		if (c != '\\')
    			continue;	/* copy char at the end of the loop */
    		switch ((c = *src++)) {
    		case '\0':	/* special, trailing '\' */
    			c = '\\';
    			break;
    		case 'b':	/* backspace */
    			c = '\b';
    			break;
    		case 'f':	/* form feed */
    			c = '\f';
    			break;
    		case 'n':
    			c = '\n';
    			break;
    		case 'r':
    			c = '\r';
    			break;
    		case 't':
    			c = '\t';
    			break;
    		}
    		/* default, use the char literally */
    	}
    	*dst = '\0';
    	return ret;
    }
    
    
    int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
    
    {
    	int result;
    
    	if (!buffer || !*buffer || !space || !*space)
    		return -1;
    
    	result = vsnprintf(*buffer, *space, fmt, ap);
    
    	if (result < 0)
    		return -1;
    	else if (result > *space)
    		result = *space;
    
    	*buffer += result;
    	*space -= result;
    	return 0;
    }
    
    
    int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
    {
    	va_list ap;
    	int result;
    
    	va_start(ap, fmt);
    	result = ast_build_string_va(buffer, space, fmt, ap);
    	va_end(ap);
    
    	return result;
    }
    
    
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    int ast_false(const char *s)
    {
    
    		return 0;
    
    	/* Determine if this is a false value */
    	if (!strcasecmp(s, "no") ||
    	    !strcasecmp(s, "false") ||
    	    !strcasecmp(s, "n") ||
    	    !strcasecmp(s, "f") ||
    	    !strcasecmp(s, "0") ||
    	    !strcasecmp(s, "off"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    
    		ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_sec += a.tv_usec / ONE_MILLION;
    
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    
    			(long)a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    #undef ONE_MILLION
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
    
     * BSD libc (and others) do not. */
    
    AST_MUTEX_DEFINE_STATIC(randomlock);
    
    long int ast_random(void)
    {
    	long int res;
    
    #ifdef HAVE_DEV_URANDOM
    	if (dev_urandom_fd >= 0) {
    		int read_res = read(dev_urandom_fd, &res, sizeof(res));
    
    	ast_mutex_lock(&randomlock);
    	res = random();
    	ast_mutex_unlock(&randomlock);
    
    char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
    {
     	char *dataPut = start;
    	int inEscape = 0;
    	int inQuotes = 0;
    
    	for (; *start; start++) {
    		if (inEscape) {
    			*dataPut++ = *start;       /* Always goes verbatim */
    			inEscape = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else {
    
    			if (*start == '\\') {
    				inEscape = 1;      /* Do not copy \ into the data */
    			} else if (*start == '\'') {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				inQuotes = 1 - inQuotes;   /* Do not copy ' into the data */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
    
    void ast_join(char *s, size_t len, char * const w[])
    
    {
    	int x, ofs = 0;
    	const char *src;
    
    	/* Join words into a string */
    	if (!s)
    		return;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	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';
    }
    
    /*
     * 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;
    
    	mgr->last_alloc = NULL;
    
    /*
     * This is an internal API, code should not use it directly.
     * It initializes all fields as empty, then uses 'size' for 3 functions:
     * size > 0 means initialize the pool list with a pool of given size.
     *	This must be called right after allocating the object.
     * size = 0 means release all pools except the most recent one.
     *	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;
    
    	mgr->last_alloc = NULL;
    
    	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_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
    
    						struct ast_string_field_pool **pool_head, size_t needed)
    
    	size_t space = mgr->size - mgr->used;
    
    	if (__builtin_expect(needed > space, 0)) {
    
    		size_t new_size = mgr->size * 2;
    
    		while (new_size < needed)
    
    		if (add_string_pool(mgr, pool_head, new_size))
    
    	result = (*pool_head)->base + mgr->used;
    
    	mgr->used += needed;
    
    	mgr->last_alloc = result;
    
    int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed,
    				const ast_string_field *ptr)
    {
    	int grow = needed - (strlen(*ptr) + 1);
    	size_t space = mgr->size - mgr->used;
    
    	if (grow <= 0) {
    		return 0;
    	}
    
    	if (*ptr != mgr->last_alloc) {
    		return 1;
    	}
    
    	if (space < grow) {
    		return 1;
    	}
    
    	mgr->used += grow;
    
    	return 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)
    
    	char *dst = (*pool_head)->base + mgr->used;
    
    	const char **p = (const char **) ptr;
    
    	size_t space = mgr->size - mgr->used;
    
    	/* try to write using available space */
    	needed = vsnprintf(dst, space, format, ap1) + 1;
    
    	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);
    
    	mgr->last_alloc = *p = dst;
    
    	mgr->used += needed;
    
    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_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);
    
    /* 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)
    {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	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_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;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief
    
    int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
    
    
    	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) {
    
    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 */
    
    
    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
    
    #if !defined(LOW_MEMORY)
    
    	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