aboutsummaryrefslogtreecommitdiffstats
path: root/main/stdtime
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-08 05:12:55 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-08 05:12:55 +0000
commit87f076953c8a1623ff38cb08732f56bb68802d3b (patch)
treeaa37a241d65698ce2986f79aa0a59955216c678d /main/stdtime
parent1dbdfb8b2859e8a306cea30b69916c3247a96b6c (diff)
Change needed to make Mac OS X 10.6 happy
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@251262 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/stdtime')
-rw-r--r--main/stdtime/localtime.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c
index 925ef029b..559c6924e 100644
--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -53,6 +53,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"
@@ -152,6 +159,9 @@ struct state {
struct lsinfo lsis[TZ_MAX_LEAPS];
#ifdef HAVE_INOTIFY
int wd[2];
+#elif defined(HAVE_KQUEUE)
+ int fd;
+ DIR *dir;
#else
time_t mtime[2];
#endif
@@ -318,6 +328,129 @@ 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 };
+
+ if ((queue_fd = kqueue()) < 0) {
+ ast_log(LOG_ERROR, "Unable to initialize kqueue(): %s\n", strerror(errno));
+ inotify_thread = AST_PTHREADT_NULL;
+ ast_cond_signal(&initialization);
+ return NULL;
+ }
+
+ for (;/*ever*/;) {
+ if (kevent(queue_fd, NULL, 0, &kev, 1, NULL) < 0) {
+ 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_REMOVE(&zonelist, sp, list);
+
+#ifndef EV_RECEIPT
+#define EV_RECEIPT 0
+#endif
+ /* If the directory event fired, remove the file event */
+ EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_DELETE | EV_RECEIPT, 0, 0, NULL);
+ kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+ close(sp->fd);
+
+ if (sp->dir) {
+ /* If the file event fired, remove the directory event */
+ EV_SET(&kev, dirfd(sp->dir), EVFILT_VNODE, EV_DELETE | EV_RECEIPT, 0, 0, NULL);
+ kevent(queue_fd, &kev, 1, NULL, 0, &no_wait);
+ closedir(sp->dir);
+ }
+ free(sp);
+ }
+}
+
+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;
+ }
+
+ if (readlink(path, watchdir, sizeof(watchdir) - 1) != -1) {
+ /* Special -- watch the directory for changes, because we cannot directly watch a symlink */
+ char *slash;
+ DIR *dir;
+
+ if ((slash = strrchr(watchdir, '/'))) {
+ *slash = '\0';
+ }
+ if (!(sp->dir = opendir(watchdir))) {
+ ast_log(LOG_ERROR, "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_RECEIPT | EV_ONESHOT,
+ NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE, 0, sp);
+ if (kevent(queue_fd, &kev, 1, NULL, 0, &no_wait) < 0 && errno != 0) {
+ ast_log(LOG_ERROR, "Unable to watch '%s': %s\n", watchdir, strerror(errno));
+ closedir(dir);
+ goto watch_file;
+ }
+ }
+
+watch_file:
+ if ((sp->fd = open(path, O_RDONLY)) < 0) {
+ ast_log(LOG_ERROR, "Unable to watch '%s' for changes: %s\n", path, strerror(errno));
+ return;
+ }
+
+ EV_SET(&kev, sp->fd, EVFILT_VNODE, EV_ADD | EV_RECEIPT | EV_ONESHOT, NOTE_DELETE, 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. */
+ ast_log(LOG_ERROR, "Unable to watch '%s': %s\n", path, strerror(errno));
+ close(sp->fd);
+ }
+}
#else
static void *notify_daemon(void *data)
{