Add kqueue(2) implementation to Asterisk in various places.
This will save a considerable amount of CPU on the BSDs, including Mac OS X, as it eliminates several places in the code that we previously used a busy loop. Additionally, this adds a res_timing interface, using kqueue timers. Review: https://reviewboard.asterisk.org/r/543/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@262852 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
46202599e2
commit
08b5d74894
7
Makefile
7
Makefile
|
@ -157,6 +157,9 @@ ifneq ($(findstring BSD,$(OSARCH)),)
|
|||
else
|
||||
ASTVARLIBDIR=$(localstatedir)/lib/asterisk
|
||||
ASTDBDIR=$(ASTVARLIBDIR)
|
||||
endif
|
||||
ifneq ($(findstring darwin,$(OSARCH)),)
|
||||
ASTVARRUNDIR=/Library/Application Support/Asterisk/Run
|
||||
endif
|
||||
ASTKEYDIR=$(ASTVARLIBDIR)
|
||||
endif
|
||||
|
@ -559,7 +562,7 @@ installdirs:
|
|||
mkdir -p $(DESTDIR)$(ASTSBINDIR)
|
||||
mkdir -p $(DESTDIR)$(ASTETCDIR)
|
||||
mkdir -p $(DESTDIR)$(ASTBINDIR)
|
||||
mkdir -p $(DESTDIR)$(ASTVARRUNDIR)
|
||||
mkdir -p "$(DESTDIR)$(ASTVARRUNDIR)"
|
||||
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail
|
||||
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate
|
||||
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system
|
||||
|
@ -572,7 +575,7 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL)
|
|||
$(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk
|
||||
$(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/
|
||||
$(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/
|
||||
if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \
|
||||
if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk -a ! -f /sbin/launchd ]; then \
|
||||
cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\
|
||||
chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\
|
||||
fi
|
||||
|
|
|
@ -21,6 +21,7 @@ IODBC=@PBX_IODBC@
|
|||
ISDNNET=@PBX_ISDNNET@
|
||||
IXJUSER=@PBX_IXJUSER@
|
||||
JACK=@PBX_JACK@
|
||||
KQUEUE=@PBX_KQUEUE@
|
||||
LDAP=@PBX_LDAP@
|
||||
LIBXML2=@PBX_LIBXML2@
|
||||
LTDL=@PBX_LTDL@
|
||||
|
|
25
configure.ac
25
configure.ac
|
@ -319,6 +319,7 @@ AST_EXT_LIB_SETUP([INOTIFY], [inotify support], [inotify])
|
|||
AST_EXT_LIB_SETUP([IODBC], [iODBC], [iodbc])
|
||||
AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux], [isdnnet])
|
||||
AST_EXT_LIB_SETUP([JACK], [Jack Audio Connection Kit], [jack])
|
||||
AST_EXT_LIB_SETUP([KQUEUE], [kqueue support], [kqueue])
|
||||
AST_EXT_LIB_SETUP([LDAP], [OpenLDAP], [ldap])
|
||||
AST_EXT_LIB_SETUP([LIBXML2], [LibXML2], [libxml2])
|
||||
AST_EXT_LIB_SETUP([LTDL], [libtool], [ltdl])
|
||||
|
@ -389,7 +390,7 @@ AC_FUNC_ALLOCA
|
|||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h arpa/nameser.h sys/io.h])
|
||||
AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h strings.h sys/event.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utime.h arpa/nameser.h sys/io.h])
|
||||
|
||||
AC_CHECK_HEADERS([winsock.h winsock2.h])
|
||||
|
||||
|
@ -540,6 +541,22 @@ then
|
|||
AC_CHECK_FILE(/dev/urandom, AC_DEFINE([HAVE_DEV_URANDOM], 1, [Define to 1 if your system has /dev/urandom.]))
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for O_EVTONLY in fcntl.h)
|
||||
AC_LINK_IFELSE(
|
||||
AC_LANG_PROGRAM([#include <fcntl.h>], [int a = O_EVTONLY;]),
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([HAVE_O_EVTONLY], 1, [Define to 1 if your system defines the file flag O_EVTONLY in fcntl.h]),
|
||||
AC_MSG_RESULT(no)
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for O_SYMLINK in fcntl.h)
|
||||
AC_LINK_IFELSE(
|
||||
AC_LANG_PROGRAM([#include <fcntl.h>], [int a = O_SYMLINK;]),
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE([HAVE_O_SYMLINK], 1, [Define to 1 if your system defines the file flag O_SYMLINK in fcntl.h]),
|
||||
AC_MSG_RESULT(no)
|
||||
)
|
||||
|
||||
AST_C_DEFINE_CHECK([PTHREAD_RWLOCK_INITIALIZER], [PTHREAD_RWLOCK_INITIALIZER], [pthread.h])
|
||||
|
||||
AC_MSG_CHECKING(for PTHREAD_RWLOCK_PREFER_WRITER_NP in pthread.h)
|
||||
|
@ -1435,6 +1452,12 @@ AST_EXT_LIB_CHECK([INOTIFY], [c], [inotify_init], [sys/inotify.h])
|
|||
|
||||
AST_EXT_LIB_CHECK([JACK], [jack], [jack_activate], [jack/jack.h])
|
||||
|
||||
# BSD (and OS X) equivalent of inotify
|
||||
AST_EXT_LIB_CHECK([KQUEUE], [c], [kqueue], [sys/event.h])
|
||||
|
||||
# 64-bit version of kevent (from kqueue) on OS X
|
||||
AC_CHECK_FUNCS([kevent64])
|
||||
|
||||
# Needed by unixodbc
|
||||
AST_EXT_LIB_CHECK([LTDL], [ltdl], [lt_dlinit], [ltdl.h], [])
|
||||
|
||||
|
|
|
@ -342,6 +342,12 @@
|
|||
/* Define to 1 if you have the Jack Audio Connection Kit library. */
|
||||
#undef HAVE_JACK
|
||||
|
||||
/* Define to 1 if you have the `kevent64' function. */
|
||||
#undef HAVE_KEVENT64
|
||||
|
||||
/* Define to 1 if you have the kqueue support library. */
|
||||
#undef HAVE_KQUEUE
|
||||
|
||||
/* Define to 1 if you have the OpenLDAP library. */
|
||||
#undef HAVE_LDAP
|
||||
|
||||
|
@ -490,6 +496,12 @@
|
|||
/* Define to 1 if OSX atomic operations are supported. */
|
||||
#undef HAVE_OSX_ATOMICS
|
||||
|
||||
/* Define to 1 if your system defines the file flag O_EVTONLY in fcntl.h */
|
||||
#undef HAVE_O_EVTONLY
|
||||
|
||||
/* Define to 1 if your system defines the file flag O_SYMLINK in fcntl.h */
|
||||
#undef HAVE_O_SYMLINK
|
||||
|
||||
/* Define to indicate the PostgreSQL library */
|
||||
#undef HAVE_PGSQL
|
||||
|
||||
|
@ -817,6 +829,9 @@
|
|||
*/
|
||||
#undef HAVE_SYS_ENDIAN_SWAP16
|
||||
|
||||
/* Define to 1 if you have the <sys/event.h> header file. */
|
||||
#undef HAVE_SYS_EVENT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/file.h> header file. */
|
||||
#undef HAVE_SYS_FILE_H
|
||||
|
||||
|
@ -1026,6 +1041,9 @@
|
|||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if the C compiler supports function prototypes. */
|
||||
#undef PROTOTYPES
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
|
@ -1045,6 +1063,11 @@
|
|||
/* Define to the type of arg 5 for `select'. */
|
||||
#undef SELECT_TYPE_ARG5
|
||||
|
||||
/* Define to 1 if the `setvbuf' function takes the buffering type as its
|
||||
second argument and the buffer pointer as the third, as on System V before
|
||||
release 3. */
|
||||
#undef SETVBUF_REVERSED
|
||||
|
||||
/* The size of `char *', as computed by sizeof. */
|
||||
#undef SIZEOF_CHAR_P
|
||||
|
||||
|
@ -1074,30 +1097,20 @@
|
|||
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
|
||||
#undef TM_IN_SYS_TIME
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
/* Define to 1 if on AIX 3.
|
||||
System headers sometimes define this.
|
||||
We just want to avoid a redefinition error message. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# undef _ALL_SOURCE
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# undef _POSIX_PTHREAD_SEMANTICS
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# undef _TANDEM_SOURCE
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# undef __EXTENSIONS__
|
||||
#endif
|
||||
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
|
||||
#undef _LARGEFILE_SOURCE
|
||||
|
@ -1115,6 +1128,20 @@
|
|||
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||
#undef _POSIX_SOURCE
|
||||
|
||||
/* Enable extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# undef __EXTENSIONS__
|
||||
#endif
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# undef _POSIX_PTHREAD_SEMANTICS
|
||||
#endif
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# undef _TANDEM_SOURCE
|
||||
#endif
|
||||
|
||||
/* Define like PROTOTYPES; this can be used by system headers. */
|
||||
#undef __PROTOTYPES
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Copyright (C) 1999 - 2010, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
|
@ -54,6 +54,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include <float.h>
|
||||
#ifdef HAVE_INOTIFY
|
||||
#include <sys/inotify.h>
|
||||
#elif HAVE_KQUEUE
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/event.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "private.h"
|
||||
|
@ -153,6 +160,13 @@ struct state {
|
|||
struct lsinfo lsis[TZ_MAX_LEAPS];
|
||||
#ifdef HAVE_INOTIFY
|
||||
int wd[2];
|
||||
#elif defined(HAVE_KQUEUE)
|
||||
int fd;
|
||||
# ifdef HAVE_O_SYMLINK
|
||||
int fds;
|
||||
# else
|
||||
DIR *dir;
|
||||
# endif /* defined(HAVE_O_SYMLINK) */
|
||||
#else
|
||||
time_t mtime[2];
|
||||
#endif
|
||||
|
@ -298,7 +312,7 @@ static void add_notify(struct state *sp, const char *path)
|
|||
/* Give the thread a chance to initialize */
|
||||
ast_cond_wait(&initialization, &initialization_lock);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unable to start notification thread\n");
|
||||
fprintf(stderr, "Unable to start notification thread\n");
|
||||
ast_mutex_unlock(&initialization_lock);
|
||||
return;
|
||||
}
|
||||
|
@ -321,6 +335,180 @@ static void add_notify(struct state *sp, const char *path)
|
|||
);
|
||||
}
|
||||
}
|
||||
#elif HAVE_KQUEUE
|
||||
static int queue_fd = -1;
|
||||
|
||||
static void *kqueue_daemon(void *data)
|
||||
{
|
||||
struct kevent kev;
|
||||
struct state *sp;
|
||||
struct timespec no_wait = { 0, 1 };
|
||||
|
||||
ast_mutex_lock(&initialization_lock);
|
||||
if ((queue_fd = kqueue()) < 0) {
|
||||
/* ast_log uses us to format messages, so if we called ast_log, we'd be
|
||||
* in for a nasty loop (seen already in testing) */
|
||||
fprintf(stderr, "Unable to initialize kqueue(): %s\n", strerror(errno));
|
||||
inotify_thread = AST_PTHREADT_NULL;
|
||||
|
||||
/* Okay to proceed */
|
||||
ast_cond_signal(&initialization);
|
||||
ast_mutex_unlock(&initialization_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_cond_signal(&initialization);
|
||||
ast_mutex_unlock(&initialization_lock);
|
||||
|
||||
for (;/*ever*/;) {
|
||||
if (kevent(queue_fd, NULL, 0, &kev, 1, NULL) < 0) {
|
||||
AST_LIST_LOCK(&zonelist);
|
||||
ast_cond_broadcast(&initialization);
|
||||
AST_LIST_UNLOCK(&zonelist);
|
||||
continue;
|
||||
}
|
||||
|
||||
sp = kev.udata;
|
||||
|
||||
/*!\note
|
||||
* If the file event fired, then the file was removed, so we'll need
|
||||
* to reparse the entry. The directory event is a bit more
|
||||
* interesting. Unfortunately, the queue doesn't contain information
|
||||
* about the file that changed (only the directory itself), so unless
|
||||
* we kept a record of the directory state before, it's not really
|
||||
* possible to know what change occurred. But if we act paranoid and
|
||||
* just purge the associated file, then it will get reparsed, and
|
||||
* everything works fine. It may be more work, but it's a vast
|
||||
* improvement over the alternative implementation, which is to stat
|
||||
* the file repeatedly in what is essentially a busy loop. */
|
||||
AST_LIST_LOCK(&zonelist);
|
||||
AST_LIST_REMOVE(&zonelist, sp, list);
|
||||
AST_LIST_UNLOCK(&zonelist);
|
||||
|
||||
/* If the directory event fired, remove the file event */
|
||||
EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
|
||||
close(sp->fd);
|
||||
|
||||
#ifdef HAVE_O_SYMLINK
|
||||
if (sp->fds > -1) {
|
||||
/* If the file event fired, remove the symlink event */
|
||||
EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
|
||||
close(sp->fds);
|
||||
}
|
||||
#else
|
||||
if (sp->dir) {
|
||||
/* If the file event fired, remove the directory event */
|
||||
EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
|
||||
closedir(sp->dir);
|
||||
}
|
||||
#endif
|
||||
free(sp);
|
||||
|
||||
/* Just in case the signal was sent late */
|
||||
AST_LIST_LOCK(&zonelist);
|
||||
ast_cond_broadcast(&initialization);
|
||||
AST_LIST_UNLOCK(&zonelist);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_notify(struct state *sp, const char *path)
|
||||
{
|
||||
struct kevent kev;
|
||||
struct timespec no_wait = { 0, 1 };
|
||||
char watchdir[PATH_MAX + 1] = "";
|
||||
|
||||
if (inotify_thread == AST_PTHREADT_NULL) {
|
||||
ast_cond_init(&initialization, NULL);
|
||||
ast_mutex_init(&initialization_lock);
|
||||
ast_mutex_lock(&initialization_lock);
|
||||
if (!(ast_pthread_create_background(&inotify_thread, NULL, kqueue_daemon, NULL))) {
|
||||
/* Give the thread a chance to initialize */
|
||||
ast_cond_wait(&initialization, &initialization_lock);
|
||||
}
|
||||
ast_mutex_unlock(&initialization_lock);
|
||||
}
|
||||
|
||||
if (queue_fd < 0) {
|
||||
/* Error already sent */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_O_SYMLINK
|
||||
if (readlink(path, watchdir, sizeof(watchdir) - 1) != -1 && (sp->fds = open(path, O_RDONLY | O_SYMLINK
|
||||
# ifdef HAVE_O_EVTONLY
|
||||
| O_EVTONLY
|
||||
# endif
|
||||
)) >= 0) {
|
||||
EV_SET(&kev, sp->fds, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
|
||||
if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
|
||||
/* According to the API docs, we may get -1 return value, due to the
|
||||
* NULL space for a returned event, but errno should be 0 unless
|
||||
* there's a real error. Otherwise, kevent will return 0 to indicate
|
||||
* that the time limit expired. */
|
||||
fprintf(stderr, "Unable to watch '%s': %s\n", path, strerror(errno));
|
||||
close(sp->fds);
|
||||
sp->fds = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (readlink(path, watchdir, sizeof(watchdir) - 1) != -1) {
|
||||
/* Special -- watch the directory for changes, because we cannot directly watch a symlink */
|
||||
char *slash;
|
||||
|
||||
ast_copy_string(watchdir, path, sizeof(watchdir));
|
||||
|
||||
if ((slash = strrchr(watchdir, '/'))) {
|
||||
*slash = '\0';
|
||||
}
|
||||
if (!(sp->dir = opendir(watchdir))) {
|
||||
fprintf(stderr, "Unable to watch directory with symlink '%s': %s\n", path, strerror(errno));
|
||||
goto watch_file;
|
||||
}
|
||||
|
||||
/*!\note
|
||||
* You may be wondering about whether there is a potential conflict
|
||||
* with the kqueue interface, because we might be watching the same
|
||||
* directory for multiple zones. The answer is no, because kqueue
|
||||
* looks at the descriptor to know if there's a duplicate. Since we
|
||||
* (may) have opened the directory multiple times, each represents a
|
||||
* different event, so no replacement of an existing event will occur.
|
||||
* Likewise, there's no potential leak of a descriptor.
|
||||
*/
|
||||
EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
|
||||
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
|
||||
if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
|
||||
fprintf(stderr, "Unable to watch '%s': %s\n", watchdir, strerror(errno));
|
||||
closedir(sp->dir);
|
||||
sp->dir = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
watch_file:
|
||||
#endif
|
||||
|
||||
if ((sp->fd = open(path, O_RDONLY
|
||||
# ifdef HAVE_O_EVTONLY
|
||||
| O_EVTONLY
|
||||
# endif
|
||||
)) < 0) {
|
||||
fprintf(stderr, "Unable to watch '%s' for changes: %s\n", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, sp);
|
||||
if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
|
||||
/* According to the API docs, we may get -1 return value, due to the
|
||||
* NULL space for a returned event, but errno should be 0 unless
|
||||
* there's a real error. Otherwise, kevent will return 0 to indicate
|
||||
* that the time limit expired. */
|
||||
fprintf(stderr, "Unable to watch '%s': %s\n", path, strerror(errno));
|
||||
close(sp->fd);
|
||||
sp->fd = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void *notify_daemon(void *data)
|
||||
{
|
||||
|
|
|
@ -32,6 +32,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include <dirent.h>
|
||||
#ifdef HAVE_INOTIFY
|
||||
#include <sys/inotify.h>
|
||||
#elif HAVE_KQUEUE
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/event.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
|
||||
|
@ -435,7 +440,7 @@ static int scan_service(const char *fn, time_t now)
|
|||
return res;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
|
||||
struct direntry {
|
||||
AST_LIST_ENTRY(direntry) list;
|
||||
time_t mtime;
|
||||
|
@ -471,6 +476,15 @@ static void queue_file(const char *filename, time_t when)
|
|||
when = st.st_mtime;
|
||||
}
|
||||
|
||||
#ifndef HAVE_INOTIFY
|
||||
/* Need to check the existing list for kqueue(2), in order to avoid duplicates. */
|
||||
AST_LIST_TRAVERSE(&dirlist, cur, list) {
|
||||
if (cur->mtime == when && !strcmp(filename, cur->name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((res = when) > now || (res = scan_service(filename, now)) > 0) {
|
||||
if (!(new = ast_calloc(1, sizeof(*new) + strlen(filename) + 1))) {
|
||||
return;
|
||||
|
@ -501,11 +515,11 @@ static void *scan_thread(void *unused)
|
|||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int res;
|
||||
time_t now;
|
||||
struct timespec ts = { .tv_sec = 1 };
|
||||
#ifdef HAVE_INOTIFY
|
||||
int res;
|
||||
int inotify_fd = inotify_init();
|
||||
struct direntry *cur;
|
||||
struct {
|
||||
struct inotify_event iev;
|
||||
/* It may not look like we're using this element, but when we read
|
||||
|
@ -514,17 +528,31 @@ static void *scan_thread(void *unused)
|
|||
char name[FILENAME_MAX + 1];
|
||||
} buf;
|
||||
struct pollfd pfd = { .fd = inotify_fd, .events = POLLIN };
|
||||
#else
|
||||
struct timespec nowait = { 0, 1 };
|
||||
int inotify_fd = kqueue();
|
||||
struct kevent kev;
|
||||
#endif
|
||||
struct direntry *cur;
|
||||
|
||||
while (!ast_fully_booted) {
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
if (inotify_fd < 0) {
|
||||
ast_log(LOG_ERROR, "Unable to initialize inotify(7)\n");
|
||||
ast_log(LOG_ERROR, "Unable to initialize "
|
||||
#ifdef HAVE_INOTIFY
|
||||
"inotify(7)"
|
||||
#else
|
||||
"kqueue(2)"
|
||||
#endif
|
||||
"\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_ATTRIB | IN_MOVED_TO);
|
||||
#endif
|
||||
|
||||
/* First, run through the directory and clear existing entries */
|
||||
if (!(dir = opendir(qdir))) {
|
||||
|
@ -532,11 +560,21 @@ static void *scan_thread(void *unused)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef HAVE_INOTIFY
|
||||
EV_SET(&kev, dirfd(dir), EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB, 0, NULL);
|
||||
if (kevent(inotify_fd, &kev, 1, NULL, 0, &nowait) < 0 && errno != 0) {
|
||||
ast_log(LOG_ERROR, "Unable to watch directory %s: %s\n", qdir, strerror(errno));
|
||||
}
|
||||
#endif
|
||||
now = time(NULL);
|
||||
while ((de = readdir(dir))) {
|
||||
queue_file(de->d_name, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* Directory needs to remain open for kqueue(2) */
|
||||
closedir(dir);
|
||||
#endif
|
||||
|
||||
/* Wait for either a) next timestamp to occur, or b) a change to happen */
|
||||
for (;/* ever */;) {
|
||||
|
@ -544,6 +582,7 @@ static void *scan_thread(void *unused)
|
|||
|
||||
time(&now);
|
||||
if (next > now) {
|
||||
#ifdef HAVE_INOTIFY
|
||||
int stage = 0;
|
||||
/* Convert from seconds to milliseconds, unless there's nothing
|
||||
* in the queue already, in which case, we wait forever. */
|
||||
|
@ -556,6 +595,19 @@ static void *scan_thread(void *unused)
|
|||
} else if (res < 0 && errno != EINTR && errno != EAGAIN) {
|
||||
ast_debug(1, "Got an error back from %s(2): %s\n", stage ? "read" : "poll", strerror(errno));
|
||||
}
|
||||
#else
|
||||
struct timespec ts2 = { next - now, 0 };
|
||||
if (kevent(inotify_fd, NULL, 0, &kev, 1, &ts2) <= 0) {
|
||||
/* Interrupt or timeout, restart calculations */
|
||||
continue;
|
||||
} else {
|
||||
/* Directory changed, rescan */
|
||||
rewinddir(dir);
|
||||
while ((de = readdir(dir))) {
|
||||
queue_file(de->d_name, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
time(&now);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* Tilghman Lesher <tlesher AT digium DOT com>
|
||||
*
|
||||
* 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
|
||||
* \author Tilghman Lesher <tlesher AT digium DOT com>
|
||||
*
|
||||
* \brief kqueue timing interface
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>kqueue</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/timing.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/time.h"
|
||||
#include "asterisk/test.h"
|
||||
|
||||
static void *timing_funcs_handle;
|
||||
|
||||
static int kqueue_timer_open(void);
|
||||
static void kqueue_timer_close(int handle);
|
||||
static int kqueue_timer_set_rate(int handle, unsigned int rate);
|
||||
static void kqueue_timer_ack(int handle, unsigned int quantity);
|
||||
static int kqueue_timer_enable_continuous(int handle);
|
||||
static int kqueue_timer_disable_continuous(int handle);
|
||||
static enum ast_timer_event kqueue_timer_get_event(int handle);
|
||||
static unsigned int kqueue_timer_get_max_rate(int handle);
|
||||
|
||||
static struct ast_timing_interface kqueue_timing = {
|
||||
.name = "kqueue",
|
||||
.priority = 150,
|
||||
.timer_open = kqueue_timer_open,
|
||||
.timer_close = kqueue_timer_close,
|
||||
.timer_set_rate = kqueue_timer_set_rate,
|
||||
.timer_ack = kqueue_timer_ack,
|
||||
.timer_enable_continuous = kqueue_timer_enable_continuous,
|
||||
.timer_disable_continuous = kqueue_timer_disable_continuous,
|
||||
.timer_get_event = kqueue_timer_get_event,
|
||||
.timer_get_max_rate = kqueue_timer_get_max_rate,
|
||||
};
|
||||
|
||||
static struct ao2_container *kqueue_timers;
|
||||
|
||||
struct kqueue_timer {
|
||||
int handle;
|
||||
uint64_t nsecs;
|
||||
uint64_t unacked;
|
||||
unsigned int is_continuous:1;
|
||||
};
|
||||
|
||||
static int kqueue_timer_hash(const void *obj, const int flags)
|
||||
{
|
||||
const struct kqueue_timer *timer = obj;
|
||||
|
||||
return timer->handle;
|
||||
}
|
||||
|
||||
static int kqueue_timer_cmp(void *obj, void *args, int flags)
|
||||
{
|
||||
struct kqueue_timer *timer1 = obj, *timer2 = args;
|
||||
return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
|
||||
}
|
||||
|
||||
static void timer_destroy(void *obj)
|
||||
{
|
||||
struct kqueue_timer *timer = obj;
|
||||
close(timer->handle);
|
||||
}
|
||||
|
||||
#define lookup_timer(a) _lookup_timer(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
static struct kqueue_timer *_lookup_timer(int handle, const char *file, int line, const char *func)
|
||||
{
|
||||
struct kqueue_timer *our_timer, find_helper = {
|
||||
.handle = handle,
|
||||
};
|
||||
|
||||
if (!(our_timer = ao2_find(kqueue_timers, &find_helper, OBJ_POINTER))) {
|
||||
ast_log(__LOG_ERROR, file, line, func, "Couldn't find timer with handle %d\n", handle);
|
||||
/* API says we set errno */
|
||||
errno = ESRCH;
|
||||
return NULL;
|
||||
}
|
||||
return our_timer;
|
||||
}
|
||||
|
||||
static int kqueue_timer_open(void)
|
||||
{
|
||||
struct kqueue_timer *timer;
|
||||
int handle;
|
||||
|
||||
if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
|
||||
ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
|
||||
return -1;
|
||||
}
|
||||
if ((handle = kqueue()) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
|
||||
ao2_ref(timer, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
timer->handle = handle;
|
||||
ao2_link(kqueue_timers, timer);
|
||||
/* Get rid of the reference from the allocation */
|
||||
ao2_ref(timer, -1);
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void kqueue_timer_close(int handle)
|
||||
{
|
||||
struct kqueue_timer *our_timer;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_unlink(kqueue_timers, our_timer);
|
||||
ao2_ref(our_timer, -1);
|
||||
}
|
||||
|
||||
static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
|
||||
{
|
||||
struct timespec nowait = { 0, 1 };
|
||||
#ifdef HAVE_KEVENT64
|
||||
struct kevent64 kev;
|
||||
|
||||
EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS,
|
||||
nsecs, NULL, 0, 0);
|
||||
kevent64(our_timer->handle, &kev, 1, NULL, 0, &nowait);
|
||||
#else
|
||||
struct kevent kev;
|
||||
|
||||
EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE,
|
||||
#ifdef NOTE_NSECONDS
|
||||
nsecs <= 0xFFffFFff ? NOTE_NSECONDS :
|
||||
#endif
|
||||
#ifdef NOTE_USECONDS
|
||||
NOTE_USECONDS
|
||||
#else /* Milliseconds, if no constants are defined */
|
||||
0
|
||||
#endif
|
||||
,
|
||||
#ifdef NOTE_NSECONDS
|
||||
nsecs <= 0xFFffFFff ? nsecs :
|
||||
#endif
|
||||
#ifdef NOTE_USECONDS
|
||||
nsecs / 1000
|
||||
#else /* Milliseconds, if nothing else is defined */
|
||||
nsecs / 1000000
|
||||
#endif
|
||||
, NULL);
|
||||
kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kqueue_timer_set_rate(int handle, unsigned int rate)
|
||||
{
|
||||
struct kqueue_timer *our_timer;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
kqueue_set_nsecs(our_timer, (our_timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
|
||||
ao2_ref(our_timer, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kqueue_timer_ack(int handle, unsigned int quantity)
|
||||
{
|
||||
struct kqueue_timer *our_timer;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (our_timer->unacked < quantity) {
|
||||
ast_debug(1, "Acking more events than have expired?!!\n");
|
||||
our_timer->unacked = 0;
|
||||
} else {
|
||||
our_timer->unacked -= quantity;
|
||||
}
|
||||
}
|
||||
|
||||
static int kqueue_timer_enable_continuous(int handle)
|
||||
{
|
||||
struct kqueue_timer *our_timer;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
kqueue_set_nsecs(our_timer, 1);
|
||||
our_timer->is_continuous = 1;
|
||||
our_timer->unacked = 0;
|
||||
ao2_ref(our_timer, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kqueue_timer_disable_continuous(int handle)
|
||||
{
|
||||
struct kqueue_timer *our_timer;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
kqueue_set_nsecs(our_timer, our_timer->nsecs);
|
||||
our_timer->is_continuous = 0;
|
||||
our_timer->unacked = 0;
|
||||
ao2_ref(our_timer, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ast_timer_event kqueue_timer_get_event(int handle)
|
||||
{
|
||||
enum ast_timer_event res = -1;
|
||||
struct kqueue_timer *our_timer;
|
||||
struct timespec sixty_seconds = { 60, 0 };
|
||||
struct kevent kev;
|
||||
|
||||
if (!(our_timer = lookup_timer(handle))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If we have non-ACKed events, just return immediately */
|
||||
if (our_timer->unacked == 0) {
|
||||
if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
|
||||
our_timer->unacked += kev.data;
|
||||
}
|
||||
}
|
||||
|
||||
if (our_timer->unacked > 0) {
|
||||
res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
|
||||
}
|
||||
|
||||
ao2_ref(our_timer, -1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned int kqueue_timer_get_max_rate(int handle)
|
||||
{
|
||||
/* Actually, the max rate is 2^64-1 seconds, but that's not representable in a 32-bit integer. */
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_DEFINE(test_kqueue_timing)
|
||||
{
|
||||
int res = AST_TEST_PASS, handle, i;
|
||||
uint64_t diff;
|
||||
struct pollfd pfd = { 0, POLLIN, 0 };
|
||||
struct kqueue_timer *kt;
|
||||
struct timeval start;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "test_kqueue_timing";
|
||||
info->category = "res/res_timing_kqueue";
|
||||
info->summary = "Test KQueue timing interface";
|
||||
info->description = "Verify that the KQueue timing interface correctly generates timing events";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(handle = kqueue_timer_open())) {
|
||||
ast_test_status_update(test, "Cannot open timer!\n");
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
do {
|
||||
pfd.fd = handle;
|
||||
if (kqueue_timer_set_rate(handle, 1000)) {
|
||||
ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (ast_poll(&pfd, 1, 1000) < 1) {
|
||||
ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (pfd.revents != POLLIN) {
|
||||
ast_test_status_update(test, "poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (!(kt = lookup_timer(handle))) {
|
||||
ast_test_status_update(test, "Could not find timer structure in container?!!\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (kqueue_timer_get_event(handle) <= 0) {
|
||||
ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (kt->unacked == 0) {
|
||||
ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
kqueue_timer_enable_continuous(handle);
|
||||
start = ast_tvnow();
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (ast_poll(&pfd, 1, 1000) < 1) {
|
||||
ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
if (kqueue_timer_get_event(handle) <= 0) {
|
||||
ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
diff = ast_tvdiff_us(ast_tvnow(), start);
|
||||
ast_test_status_update(test, "diff is %llu\n", diff);
|
||||
/*
|
||||
if (abs(diff - kt->unacked) == 0) {
|
||||
ast_test_status_update(test, "Unacked events should be around 1000, not %llu\n", kt->unacked);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
*/
|
||||
} while (0);
|
||||
kqueue_timer_close(handle);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
|
||||
ao2_ref(kqueue_timers, -1);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
AST_TEST_REGISTER(test_kqueue_timing);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
AST_TEST_UNREGISTER(test_kqueue_timing);
|
||||
if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
|
||||
ao2_ref(kqueue_timers, -1);
|
||||
kqueue_timers = NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.load_pri = 10,
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2009, Digium, Inc.
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* Tilghman Lesher <tlesher AT digium DOT com>
|
||||
*
|
||||
|
@ -48,10 +48,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
|
||||
AST_TEST_DEFINE(test_timezone_watch)
|
||||
{
|
||||
const char *zones[2] = { "America/Chicago", "America/New_York" };
|
||||
const char *zones[] = { "America/Chicago", "America/New_York" };
|
||||
int type, i, res = AST_TEST_PASS;
|
||||
struct timeval tv = ast_tvnow();
|
||||
struct ast_tm atm[2];
|
||||
struct ast_tm atm[ARRAY_LEN(zones)];
|
||||
char tmpdir[] = "/tmp/timezone.XXXXXX";
|
||||
char tzfile[50], syscmd[256];
|
||||
|
||||
|
@ -73,15 +73,12 @@ AST_TEST_DEFINE(test_timezone_watch)
|
|||
}
|
||||
snprintf(tzfile, sizeof(tzfile), "%s/test", tmpdir);
|
||||
|
||||
/* Allow system(3) to function correctly */
|
||||
ast_replace_sigchld();
|
||||
|
||||
for (type = 0; type < 2; type++) {
|
||||
ast_test_status_update(test, "Executing %s test...\n", type == 0 ? "deletion" : "symlink");
|
||||
for (i = 0; i < ARRAY_LEN(zones); i++) {
|
||||
int system_res;
|
||||
snprintf(syscmd, sizeof(syscmd), "%s " TZDIR "/%s %s", type == 0 ? "cp" : "ln -sf", zones[i], tzfile);
|
||||
if ((system_res = system(syscmd))) {
|
||||
if ((system_res = ast_safe_system(syscmd))) {
|
||||
ast_log(LOG_WARNING, "system(%s) returned non-zero: %d\n", syscmd, system_res);
|
||||
}
|
||||
ast_localtime_wakeup_monitor();
|
||||
|
@ -93,19 +90,18 @@ AST_TEST_DEFINE(test_timezone_watch)
|
|||
}
|
||||
}
|
||||
|
||||
/* stat(2) only has resolution to 1 second - must wait, or the mtime is the same */
|
||||
usleep(1100000);
|
||||
if (i + 1 != ARRAY_LEN(zones)) {
|
||||
/* stat(2) only has resolution to 1 second - must wait, or the mtime is the same */
|
||||
usleep(1100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(syscmd, sizeof(syscmd), "rm -rf %s", tmpdir);
|
||||
if (system(syscmd)) {
|
||||
if (ast_safe_system(syscmd)) {
|
||||
ast_log(LOG_WARNING, "system(%s) returned non-zero.\n", syscmd);
|
||||
}
|
||||
|
||||
/* Restore SIGCHLD handler */
|
||||
ast_unreplace_sigchld();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue