diff options
author | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2009-02-25 19:24:44 +0000 |
---|---|---|
committer | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2009-02-25 19:24:44 +0000 |
commit | 82a4dd2ed8d1efc9d6c68f158663beb96aeced65 (patch) | |
tree | cf55319fa2d5403fde6fba72ae8a4fd2b3ac5912 /main | |
parent | a9e3b506a0e9205fea98075119ea77575db78ce6 (diff) |
Use notification when timezone files change and re-scan then.
(closes issue #14300)
Reported by: jamessan
Patches:
20090127__bug14300.diff.txt uploaded by tilghman (license 14)
20090224__bug14300.diff uploaded by jamessan (license 246)
Tested by: jamessan
Review: http://reviewboard.digium.com/r/136/
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@178605 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main')
-rw-r--r-- | main/stdtime/localtime.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c index 389107cb7..e247a4de0 100644 --- a/main/stdtime/localtime.c +++ b/main/stdtime/localtime.c @@ -51,6 +51,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <sys/stat.h> #include <fcntl.h> #include <float.h> +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#endif #include "private.h" #include "tzfile.h" @@ -147,6 +150,11 @@ struct state { char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; +#ifdef HAVE_INOTIFY + int wd[2]; +#else + time_t mtime[2]; +#endif AST_LIST_ENTRY(state) list; }; @@ -217,6 +225,156 @@ static AST_LIST_HEAD_STATIC(zonelist, state); #define TZ_STRLEN_MAX 255 #endif /* !defined TZ_STRLEN_MAX */ +static pthread_t inotify_thread = AST_PTHREADT_NULL; +static ast_cond_t initialization; +static ast_mutex_t initialization_lock; +#ifdef HAVE_INOTIFY +static int inotify_fd = -1; + +static void *inotify_daemon(void *data) +{ + struct { + struct inotify_event iev; + char name[FILENAME_MAX + 1]; + } buf; + ssize_t res; + struct state *cur; + + inotify_fd = inotify_init(); + + ast_mutex_lock(&initialization_lock); + ast_cond_signal(&initialization); + ast_mutex_unlock(&initialization_lock); + + if (inotify_fd < 0) { + ast_log(LOG_ERROR, "Cannot initialize file notification service: %s (%d)\n", strerror(errno), errno); + inotify_thread = AST_PTHREADT_NULL; + return NULL; + } + + for (;/*ever*/;) { + /* This read should block, most of the time. */ + if ((res = read(inotify_fd, &buf, sizeof(buf))) < sizeof(buf.iev) && res > 0) { + /* This should never happen */ + ast_log(LOG_ERROR, "Inotify read less than a full event (%d < %d)?!!\n", res, sizeof(buf.iev)); + break; + } else if (res < 0) { + if (errno == EINTR || errno == EAGAIN) { + /* If read fails, then wait a bit, then continue */ + poll(NULL, 0, 10000); + continue; + } + /* Sanity check -- this should never happen, either */ + ast_log(LOG_ERROR, "Inotify failed: %s\n", strerror(errno)); + break; + } + AST_LIST_LOCK(&zonelist); + AST_LIST_TRAVERSE_SAFE_BEGIN(&zonelist, cur, list) { + if (cur->wd[0] == buf.iev.wd || cur->wd[1] == buf.iev.wd) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(cur); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END + AST_LIST_UNLOCK(&zonelist); + } + close(inotify_fd); + inotify_thread = AST_PTHREADT_NULL; + return NULL; +} + +static void add_notify(struct state *sp, const char *path) +{ + 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, inotify_daemon, NULL))) { + /* Give the thread a chance to initialize */ + ast_cond_wait(&initialization, &initialization_lock); + } else { + ast_log(LOG_ERROR, "Unable to start notification thread\n"); + ast_mutex_unlock(&initialization_lock); + return; + } + ast_mutex_unlock(&initialization_lock); + } + + if (inotify_fd > -1) { + char fullpath[FILENAME_MAX + 1] = ""; + if (readlink(path, fullpath, sizeof(fullpath) - 1) != -1) { + /* If file the symlink points to changes */ + sp->wd[1] = inotify_add_watch(inotify_fd, fullpath, IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_CLOSE_WRITE ); + } else { + sp->wd[1] = -1; + } + /* or if the symlink itself changes (or the real file is here, if path is not a symlink) */ + sp->wd[0] = inotify_add_watch(inotify_fd, path, IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_CLOSE_WRITE | IN_DONT_FOLLOW); + } +} +#else +static void *notify_daemon(void *data) +{ + struct stat st, lst; + struct state *cur; + + ast_mutex_lock(&initialization_lock); + ast_cond_signal(&initialization); + ast_mutex_unlock(&initialization_lock); + + for (;/*ever*/;) { + char fullname[FILENAME_MAX + 1]; + + poll(NULL, 0, 60000); + AST_LIST_LOCK(&zonelist); + AST_LIST_TRAVERSE_SAFE_BEGIN(&zonelist, cur, list) { + char *name = cur->name; + + if (name[0] == ':') + ++name; + if (name[0] != '/') { + (void) strcpy(fullname, TZDIR "/"); + (void) strcat(fullname, name); + name = fullname; + } + stat(name, &st); + lstat(name, &lst); + if (st.st_mtime > cur->mtime[0] || lst.st_mtime > cur->mtime[1]) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(cur); + continue; + } + } + AST_LIST_TRAVERSE_SAFE_END + AST_LIST_UNLOCK(&zonelist); + } + inotify_thread = AST_PTHREADT_NULL; + return NULL; +} + +static void add_notify(struct state *sp, const char *path) +{ + struct stat st; + + 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, notify_daemon, NULL))) { + /* Give the thread a chance to initialize */ + ast_cond_wait(&initialization, &initialization_lock); + } + ast_mutex_unlock(&initialization_lock); + } + + stat(path, &st); + sp->mtime[0] = st.st_mtime; + lstat(path, &st); + sp->mtime[1] = st.st_mtime; +} +#endif + /*! \note ** Section 4.12.3 of X3.159-1989 requires that ** Except for the strftime function, these functions [asctime, @@ -305,6 +463,7 @@ static int tzload(const char *name, struct state * const sp, const int doextend) return -1; if ((fid = open(name, OPEN_MODE)) == -1) return -1; + add_notify(sp, name); } nread = read(fid, u.buf, sizeof u.buf); if (close(fid) < 0 || nread <= 0) |