summaryrefslogtreecommitdiff
path: root/pbx
diff options
context:
space:
mode:
Diffstat (limited to 'pbx')
-rw-r--r--pbx/pbx_spool.c60
1 files changed, 58 insertions, 2 deletions
diff --git a/pbx/pbx_spool.c b/pbx/pbx_spool.c
index b45ff6a59..327c995ce 100644
--- a/pbx/pbx_spool.c
+++ b/pbx/pbx_spool.c
@@ -471,6 +471,7 @@ static AST_LIST_HEAD_STATIC(dirlist, direntry);
#if defined(HAVE_INOTIFY)
/* Only one thread is accessing this list, so no lock is necessary */
static AST_LIST_HEAD_NOLOCK_STATIC(createlist, direntry);
+static AST_LIST_HEAD_NOLOCK_STATIC(openlist, direntry);
#endif
static void queue_file(const char *filename, time_t when)
@@ -551,14 +552,47 @@ static void queue_file_create(const char *filename)
return;
}
strcpy(cur->name, filename);
+ /* We'll handle this file unless an IN_OPEN event occurs within 2 seconds */
+ cur->mtime = time(NULL) + 2;
AST_LIST_INSERT_TAIL(&createlist, cur, list);
}
+static void queue_file_open(const char *filename)
+{
+ struct direntry *cur;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&createlist, cur, list) {
+ if (!strcmp(cur->name, filename)) {
+ AST_LIST_REMOVE_CURRENT(list);
+ AST_LIST_INSERT_TAIL(&openlist, cur, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+}
+
+static void queue_created_files(void)
+{
+ struct direntry *cur;
+ time_t now = time(NULL);
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&createlist, cur, list) {
+ if (cur->mtime > now) {
+ break;
+ }
+
+ AST_LIST_REMOVE_CURRENT(list);
+ queue_file(cur->name, 0);
+ ast_free(cur);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+}
+
static void queue_file_write(const char *filename)
{
struct direntry *cur;
/* Only queue entries where an IN_CREATE preceded the IN_CLOSE_WRITE */
- AST_LIST_TRAVERSE_SAFE_BEGIN(&createlist, cur, list) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&openlist, cur, list) {
if (!strcmp(cur->name, filename)) {
AST_LIST_REMOVE_CURRENT(list);
ast_free(cur);
@@ -605,7 +639,7 @@ static void *scan_thread(void *unused)
}
#ifdef HAVE_INOTIFY
- inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO);
+ inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_OPEN | IN_CLOSE_WRITE | IN_MOVED_TO);
#endif
/* First, run through the directory and clear existing entries */
@@ -641,14 +675,35 @@ static void *scan_thread(void *unused)
/* Convert from seconds to milliseconds, unless there's nothing
* in the queue already, in which case, we wait forever. */
int waittime = next == INT_MAX ? -1 : (next - now) * 1000;
+ if (!AST_LIST_EMPTY(&createlist)) {
+ waittime = 1000;
+ }
/* When a file arrives, add it to the queue, in mtime order. */
if ((res = poll(&pfd, 1, waittime)) > 0 && (stage = 1) &&
(res = read(inotify_fd, &buf, sizeof(buf))) >= sizeof(*iev)) {
ssize_t len = 0;
/* File(s) added to directory, add them to my list */
for (iev = (void *) buf; res >= sizeof(*iev); iev = (struct inotify_event *) (((char *) iev) + len)) {
+ /* For an IN_MOVED_TO event, simply process the file. However, if
+ * we get an IN_CREATE event it *might* be an open(O_CREAT) or it
+ * might be a hardlink (like smsq does, since rename() might
+ * overwrite an existing file). So we have to see if we get a
+ * subsequent IN_OPEN event on the same file. If we do, keep it
+ * on the openlist and wait for the corresponding IN_CLOSE_WRITE.
+ * If we *don't* see an IN_OPEN event, then it was a hard link so
+ * it can be processed immediately.
+ *
+ * Unfortunately, although open(O_CREAT) is an atomic file system
+ * operation, the inotify subsystem doesn't give it to us in a
+ * single event with both IN_CREATE|IN_OPEN set. It's two separate
+ * events, and the kernel doesn't even give them to us at the same
+ * time. We can read() from inotify_fd after the IN_CREATE event,
+ * and get *nothing* from it. The IN_OPEN arrives only later! So
+ * we have a very short timeout of 2 seconds. */
if (iev->mask & IN_CREATE) {
queue_file_create(iev->name);
+ } else if (iev->mask & IN_OPEN) {
+ queue_file_open(iev->name);
} else if (iev->mask & IN_CLOSE_WRITE) {
queue_file_write(iev->name);
} else if (iev->mask & IN_MOVED_TO) {
@@ -679,6 +734,7 @@ static void *scan_thread(void *unused)
time(&now);
}
+ queue_created_files();
/* Empty the list of all entries ready to be processed */
AST_LIST_LOCK(&dirlist);
while (!AST_LIST_EMPTY(&dirlist) && AST_LIST_FIRST(&dirlist)->mtime <= now) {