~ubuntu-branches/debian/jessie/gamin/jessie

« back to all changes in this revision

Viewing changes to server/inotify-kernel.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Banck
  • Date: 2007-03-23 14:43:49 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070323144349-1inpdk22uaneks9h
Tags: 0.1.8-2
* debian/control: Improve long description. (Closes: #405347)
* debian/patches/14_nfs-fix.patch: Fix gam_server startup for Thunar.
  Thanks to Maximiliano Curia. (Closes: #403247)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
        Copyright (C) 2006 John McCutchan <john@johnmccutchan.com>
 
3
 
 
4
        This program is free software; you can redistribute it and/or modify
 
5
        it under the terms of the GNU General Public License as published by
 
6
        the Free Software Foundation; version 2.
 
7
 
 
8
        This program is distributed in the hope that it will be useful,
 
9
        but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
        GNU General Public License version 2 for more details.
 
12
        You should have received a copy of the GNU General Public License
 
13
        along with this program; if not, write to the Free Software Foundation,
 
14
        Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
15
*/
 
16
 
 
17
#include "config.h"
 
18
 
 
19
#include <stdio.h>
 
20
#include <sys/ioctl.h>
 
21
#include <unistd.h>
 
22
#include <errno.h>
 
23
#include <string.h>
 
24
#include <glib.h>
 
25
#include "inotify-kernel.h"
 
26
 
 
27
/* Just include the local headers to stop all the pain */
 
28
#include "local_inotify.h"
 
29
#include "local_inotify_syscalls.h"
 
30
#if 0
 
31
#ifdef HAVE_SYS_INOTIFY_H
 
32
/* We don't actually include the libc header, because there has been
 
33
 * problems with libc versions that was built without inotify support.
 
34
 * Instead we use the local version.
 
35
 */
 
36
#include "local_inotify.h"
 
37
#include "local_inotify_syscalls.h"
 
38
#elif defined (HAVE_LINUX_INOTIFY_H)
 
39
#include <linux/inotify.h>
 
40
#include "local_inotify_syscalls.h"
 
41
#endif
 
42
#endif
 
43
 
 
44
/* Timings for pairing MOVED_TO / MOVED_FROM events */
 
45
#define PROCESS_EVENTS_TIME 1000 /* milliseconds (1 hz) */
 
46
#define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */
 
47
#define MOVE_HOLD_UNTIL_TIME 0 /* 0 milliseconds */
 
48
 
 
49
static int inotify_instance_fd = -1;
 
50
static GQueue *events_to_process = NULL;
 
51
static GQueue *event_queue = NULL;
 
52
static GHashTable * cookie_hash = NULL;
 
53
static GIOChannel *inotify_read_ioc;
 
54
static GPollFD ik_poll_fd;
 
55
static gboolean ik_poll_fd_enabled = TRUE;
 
56
static void (*user_cb)(ik_event_t *event);
 
57
 
 
58
static gboolean ik_read_callback (gpointer user_data);
 
59
static gboolean ik_process_eq_callback (gpointer user_data);
 
60
 
 
61
static guint32 ik_move_matches = 0;
 
62
static guint32 ik_move_misses = 0;
 
63
 
 
64
static gboolean process_eq_running = FALSE;
 
65
 
 
66
/* We use the lock from inotify-helper.c
 
67
 *
 
68
 * There are two places that we take this lock
 
69
 *
 
70
 * 1) In ik_read_callback
 
71
 *
 
72
 * 2) ik_process_eq_callback.
 
73
 *
 
74
 *
 
75
 * The rest of locking is taken care of in inotify-helper.c
 
76
 */
 
77
G_LOCK_EXTERN (inotify_lock);
 
78
 
 
79
typedef struct ik_event_internal {
 
80
        ik_event_t *event;
 
81
        gboolean seen;
 
82
        gboolean sent;
 
83
        GTimeVal hold_until;
 
84
        struct ik_event_internal *pair;
 
85
} ik_event_internal_t;
 
86
 
 
87
/* In order to perform non-sleeping inotify event chunking we need
 
88
 * a custom GSource
 
89
 */
 
90
static gboolean
 
91
ik_source_prepare (GSource *source,
 
92
                                 gint *timeout)
 
93
{
 
94
        return FALSE;
 
95
}
 
96
 
 
97
static gboolean
 
98
ik_source_timeout (gpointer data)
 
99
{
 
100
        GSource *source = (GSource *)data;
 
101
 
 
102
        /* Re-active the PollFD */
 
103
        g_source_add_poll (source, &ik_poll_fd);
 
104
        g_source_unref (source);
 
105
        ik_poll_fd_enabled = TRUE;
 
106
 
 
107
        return FALSE;
 
108
}
 
109
 
 
110
#define MAX_PENDING_COUNT 2
 
111
#define PENDING_THRESHOLD(qsize) ((qsize) >> 1)
 
112
#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p)))
 
113
#define MAX_QUEUED_EVENTS 2048
 
114
#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16
 
115
#define TIMEOUT_MILLISECONDS 10
 
116
static gboolean
 
117
ik_source_check (GSource *source)
 
118
{
 
119
        static int prev_pending = 0, pending_count = 0;
 
120
 
 
121
        /* We already disabled the PollFD or
 
122
         * nothing to be read from inotify */
 
123
        if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN))
 
124
        {
 
125
                return FALSE;
 
126
        }
 
127
 
 
128
        if (pending_count < MAX_PENDING_COUNT) {
 
129
                unsigned int pending;
 
130
 
 
131
                if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1)
 
132
                        goto do_read;
 
133
 
 
134
                pending /= AVERAGE_EVENT_SIZE;
 
135
 
 
136
                /* Don't wait if the number of pending events is too close
 
137
                * to the maximum queue size.
 
138
                */
 
139
                if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS))
 
140
                        goto do_read;
 
141
 
 
142
                /* With each successive iteration, the minimum rate for
 
143
                * further sleep doubles. */
 
144
                if (pending-prev_pending < PENDING_MARGINAL_COST(pending_count))
 
145
                        goto do_read;
 
146
 
 
147
                prev_pending = pending;
 
148
                pending_count++;
 
149
 
 
150
                /* We are going to wait to read the events: */
 
151
 
 
152
                /* Remove the PollFD from the source */
 
153
                g_source_remove_poll (source, &ik_poll_fd);
 
154
                /* To avoid threading issues we need to flag that we've done that */
 
155
                ik_poll_fd_enabled = FALSE;
 
156
                /* Set a timeout to re-add the PollFD to the source */
 
157
                g_source_ref (source);
 
158
                g_timeout_add (TIMEOUT_MILLISECONDS, ik_source_timeout, source);
 
159
 
 
160
                return FALSE;
 
161
        }
 
162
 
 
163
do_read:
 
164
        /* We are ready to read events from inotify */
 
165
 
 
166
        prev_pending = 0;
 
167
        pending_count = 0;
 
168
 
 
169
        return TRUE;
 
170
}
 
171
 
 
172
static gboolean
 
173
ik_source_dispatch (GSource *source,
 
174
                    GSourceFunc callback,
 
175
                    gpointer user_data)
 
176
{
 
177
        if (callback)
 
178
        {
 
179
                return callback(user_data);
 
180
        }
 
181
        return TRUE;
 
182
}
 
183
 
 
184
GSourceFuncs ik_source_funcs =
 
185
{
 
186
        ik_source_prepare,
 
187
        ik_source_check,
 
188
        ik_source_dispatch,
 
189
        NULL
 
190
};
 
191
 
 
192
gboolean ik_startup (void (*cb)(ik_event_t *event))
 
193
{
 
194
        static gboolean initialized = FALSE;
 
195
        GSource *source;
 
196
 
 
197
        user_cb = cb;
 
198
        /* Ignore multi-calls */
 
199
        if (initialized) {
 
200
                return inotify_instance_fd >= 0;
 
201
        }
 
202
 
 
203
        initialized = TRUE;
 
204
        inotify_instance_fd = inotify_init ();
 
205
 
 
206
        if (inotify_instance_fd < 0) {
 
207
                return FALSE;
 
208
        }
 
209
 
 
210
        inotify_read_ioc = g_io_channel_unix_new(inotify_instance_fd);
 
211
        ik_poll_fd.fd = inotify_instance_fd;
 
212
        ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
 
213
        g_io_channel_set_encoding(inotify_read_ioc, NULL, NULL);
 
214
        g_io_channel_set_flags(inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL);
 
215
 
 
216
        source = g_source_new (&ik_source_funcs, sizeof(GSource));
 
217
        g_source_add_poll (source, &ik_poll_fd);
 
218
        g_source_set_callback(source, ik_read_callback, NULL, NULL);
 
219
        g_source_attach(source, NULL);
 
220
        g_source_unref (source);
 
221
 
 
222
        cookie_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
 
223
        event_queue = g_queue_new ();
 
224
        events_to_process = g_queue_new ();
 
225
 
 
226
        return TRUE;
 
227
}
 
228
 
 
229
static ik_event_internal_t *ik_event_internal_new (ik_event_t *event)
 
230
{
 
231
        ik_event_internal_t *internal_event = g_new0(ik_event_internal_t, 1);
 
232
        GTimeVal tv;
 
233
 
 
234
        g_assert (event);
 
235
        
 
236
        g_get_current_time (&tv);
 
237
        g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME);
 
238
        internal_event->event = event;
 
239
        internal_event->hold_until = tv;
 
240
 
 
241
        return internal_event;
 
242
}
 
243
 
 
244
static ik_event_t *ik_event_new (char *buffer)
 
245
{
 
246
   struct inotify_event *kevent = (struct inotify_event *)buffer;
 
247
   g_assert (buffer);
 
248
   ik_event_t *event = g_new0(ik_event_t,1);
 
249
   event->wd = kevent->wd;
 
250
   event->mask = kevent->mask;
 
251
   event->cookie = kevent->cookie;
 
252
   event->len = kevent->len;
 
253
   if (event->len)
 
254
      event->name = g_strdup(kevent->name);
 
255
   else
 
256
      event->name = g_strdup("");
 
257
 
 
258
   return event;
 
259
}
 
260
 
 
261
ik_event_t *ik_event_new_dummy (const char *name, gint32 wd, guint32 mask)
 
262
{
 
263
        ik_event_t *event = g_new0(ik_event_t,1);
 
264
        event->wd = wd;
 
265
        event->mask = mask;
 
266
        event->cookie = 0;
 
267
        if (name)
 
268
                event->name = g_strdup(name);
 
269
        else
 
270
                event->name = g_strdup("");
 
271
 
 
272
        event->len = strlen (event->name);
 
273
 
 
274
        return event;
 
275
}
 
276
 
 
277
void ik_event_free (ik_event_t *event)
 
278
{
 
279
        if (event->pair)
 
280
                ik_event_free (event->pair);
 
281
        g_free(event->name);
 
282
        g_free(event);
 
283
}
 
284
 
 
285
gint32 ik_watch (const char *path, guint32 mask, int *err)
 
286
{
 
287
   gint32 wd = -1;
 
288
 
 
289
   g_assert (path != NULL);
 
290
   g_assert (inotify_instance_fd >= 0);
 
291
 
 
292
   wd = inotify_add_watch (inotify_instance_fd, path, mask);
 
293
 
 
294
   if (wd < 0)
 
295
   {
 
296
      int e = errno;
 
297
      // FIXME: debug msg failed to add watch
 
298
      if (err)
 
299
         *err = e;
 
300
      return wd;
 
301
   }
 
302
 
 
303
   g_assert (wd >= 0);
 
304
   return wd;
 
305
}
 
306
 
 
307
int ik_ignore(const char *path, gint32 wd)
 
308
{
 
309
        g_assert (wd >= 0);
 
310
        g_assert (inotify_instance_fd >= 0);
 
311
 
 
312
        if (inotify_rm_watch (inotify_instance_fd, wd) < 0)
 
313
        {
 
314
                //int e = errno;
 
315
                // failed to rm watch
 
316
                return -1;
 
317
        }
 
318
 
 
319
        return 0;
 
320
}
 
321
 
 
322
void ik_move_stats (guint32 *matches, guint32 *misses)
 
323
{
 
324
        if (matches)
 
325
                *matches = ik_move_matches;
 
326
 
 
327
        if (misses)
 
328
                *misses = ik_move_misses;
 
329
}
 
330
 
 
331
const char *ik_mask_to_string (guint32 mask)
 
332
{
 
333
        gboolean is_dir = mask & IN_ISDIR;
 
334
        mask &= ~IN_ISDIR;
 
335
 
 
336
        if (is_dir)
 
337
        {
 
338
                switch (mask)
 
339
                {
 
340
                        case IN_ACCESS:
 
341
                                return "ACCESS (dir)";
 
342
                        break;
 
343
                        case IN_MODIFY:
 
344
                                return "MODIFY (dir)";
 
345
                        break;
 
346
                        case IN_ATTRIB:
 
347
                                return "ATTRIB (dir)";
 
348
                        break;
 
349
                        case IN_CLOSE_WRITE:
 
350
                                return "CLOSE_WRITE (dir)";
 
351
                        break;
 
352
                        case IN_CLOSE_NOWRITE:
 
353
                                return "CLOSE_NOWRITE (dir)"; 
 
354
                        break;
 
355
                        case IN_OPEN:
 
356
                                return "OPEN (dir)";
 
357
                        break;
 
358
                        case IN_MOVED_FROM:
 
359
                                return "MOVED_FROM (dir)";
 
360
                        break;
 
361
                        case IN_MOVED_TO:
 
362
                                return "MOVED_TO (dir)";
 
363
                        break;
 
364
                        case IN_DELETE:
 
365
                                return "DELETE (dir)";
 
366
                        break;
 
367
                        case IN_CREATE:
 
368
                                return "CREATE (dir)";
 
369
                        break;
 
370
                        case IN_DELETE_SELF:
 
371
                                return "DELETE_SELF (dir)";
 
372
                        break;
 
373
                        case IN_UNMOUNT:
 
374
                                return "UNMOUNT (dir)";
 
375
                        break;
 
376
                        case IN_Q_OVERFLOW:
 
377
                                return "Q_OVERFLOW (dir)";
 
378
                        break;
 
379
                        case IN_IGNORED:
 
380
                                return "IGNORED (dir)";
 
381
                        break;
 
382
                        default:
 
383
                                return "UNKNOWN_EVENT (dir)";
 
384
                        break;
 
385
 
 
386
                }
 
387
        } else {
 
388
                switch (mask)
 
389
                {
 
390
                        case IN_ACCESS:
 
391
                                return "ACCESS";
 
392
                        break;
 
393
                        case IN_MODIFY:
 
394
                                return "MODIFY";
 
395
                        break;
 
396
                        case IN_ATTRIB:
 
397
                                return "ATTRIB";
 
398
                        break;
 
399
                        case IN_CLOSE_WRITE:
 
400
                                return "CLOSE_WRITE";
 
401
                        break;
 
402
                        case IN_CLOSE_NOWRITE:
 
403
                                return "CLOSE_NOWRITE";
 
404
                        break;
 
405
                        case IN_OPEN:
 
406
                                return "OPEN";
 
407
                        break;
 
408
                        case IN_MOVED_FROM:
 
409
                                return "MOVED_FROM";
 
410
                        break;
 
411
                        case IN_MOVED_TO:
 
412
                                return "MOVED_TO";
 
413
                        break;
 
414
                        case IN_DELETE:
 
415
                                return "DELETE";
 
416
                        break;
 
417
                        case IN_CREATE:
 
418
                                return "CREATE";
 
419
                        break;
 
420
                        case IN_DELETE_SELF:
 
421
                                return "DELETE_SELF";
 
422
                        break;
 
423
                        case IN_UNMOUNT:
 
424
                                return "UNMOUNT";
 
425
                        break;
 
426
                        case IN_Q_OVERFLOW:
 
427
                                return "Q_OVERFLOW";
 
428
                        break;
 
429
                        case IN_IGNORED:
 
430
                                return "IGNORED";
 
431
                        break;
 
432
                        default:
 
433
                                return "UNKNOWN_EVENT";
 
434
                        break;
 
435
 
 
436
                }
 
437
        }
 
438
}
 
439
 
 
440
 
 
441
static void ik_read_events (gsize *buffer_size_out, gchar **buffer_out)
 
442
{
 
443
        static gchar *buffer = NULL;
 
444
        static gsize buffer_size;
 
445
 
 
446
        /* Initialize the buffer on our first call */
 
447
        if (buffer == NULL)
 
448
        {
 
449
                buffer_size = AVERAGE_EVENT_SIZE;
 
450
                buffer_size *= MAX_QUEUED_EVENTS;
 
451
                buffer = g_malloc (buffer_size);
 
452
 
 
453
                if (!buffer) {
 
454
                        *buffer_size_out = 0;
 
455
                        *buffer_out = NULL;
 
456
                        return;
 
457
                }
 
458
        }
 
459
 
 
460
        *buffer_size_out = 0;
 
461
        *buffer_out = NULL;
 
462
 
 
463
        memset(buffer, 0, buffer_size);
 
464
 
 
465
        if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) {
 
466
                // error reading
 
467
        }
 
468
        *buffer_out = buffer;
 
469
}
 
470
 
 
471
static gboolean ik_read_callback(gpointer user_data)
 
472
{
 
473
        gchar *buffer;
 
474
        gsize buffer_size, buffer_i, events;
 
475
 
 
476
        G_LOCK(inotify_lock);
 
477
        ik_read_events (&buffer_size, &buffer);
 
478
 
 
479
        buffer_i = 0;
 
480
        events = 0;
 
481
        while (buffer_i < buffer_size)
 
482
        {
 
483
                struct inotify_event *event;
 
484
                gsize event_size;
 
485
                event = (struct inotify_event *)&buffer[buffer_i];
 
486
                event_size = sizeof(struct inotify_event) + event->len;
 
487
                g_queue_push_tail (events_to_process, ik_event_internal_new (ik_event_new (&buffer[buffer_i])));
 
488
                buffer_i += event_size;
 
489
                events++;
 
490
        }
 
491
 
 
492
        /* If the event process callback is off, turn it back on */
 
493
        if (!process_eq_running && events)
 
494
        {
 
495
                process_eq_running = TRUE;
 
496
                g_timeout_add (PROCESS_EVENTS_TIME, ik_process_eq_callback, NULL);
 
497
        }
 
498
 
 
499
        G_UNLOCK(inotify_lock);
 
500
        return TRUE;
 
501
}
 
502
 
 
503
static gboolean
 
504
g_timeval_lt(GTimeVal *val1, GTimeVal *val2)
 
505
{
 
506
        if (val1->tv_sec < val2->tv_sec)
 
507
                return TRUE;
 
508
 
 
509
        if (val1->tv_sec > val2->tv_sec)
 
510
                return FALSE;
 
511
 
 
512
        /* val1->tv_sec == val2->tv_sec */
 
513
        if (val1->tv_usec < val2->tv_usec)
 
514
                return TRUE;
 
515
 
 
516
        return FALSE;
 
517
}
 
518
 
 
519
static gboolean
 
520
g_timeval_eq(GTimeVal *val1, GTimeVal *val2)
 
521
{
 
522
        return (val1->tv_sec == val2->tv_sec) && (val1->tv_usec == val2->tv_usec);
 
523
}
 
524
 
 
525
static void
 
526
ik_pair_events (ik_event_internal_t *event1, ik_event_internal_t *event2)
 
527
{
 
528
        g_assert (event1 && event2);
 
529
        /* We should only be pairing events that have the same cookie */
 
530
        g_assert (event1->event->cookie == event2->event->cookie);
 
531
        /* We shouldn't pair an event that already is paired */
 
532
        g_assert (event1->pair == NULL && event2->pair == NULL);
 
533
 
 
534
        /* Pair the internal structures and the ik_event_t structures */
 
535
        event1->pair = event2;
 
536
        event1->event->pair = event2->event;
 
537
 
 
538
        if (g_timeval_lt (&event1->hold_until, &event2->hold_until))
 
539
                event1->hold_until = event2->hold_until;
 
540
 
 
541
        event2->hold_until = event1->hold_until;
 
542
}
 
543
 
 
544
static void
 
545
ik_event_add_microseconds (ik_event_internal_t *event, glong ms)
 
546
{
 
547
        g_assert (event);
 
548
        g_time_val_add (&event->hold_until, ms);
 
549
}
 
550
 
 
551
static gboolean
 
552
ik_event_ready (ik_event_internal_t *event)
 
553
{
 
554
        GTimeVal tv;
 
555
        g_assert (event);
 
556
 
 
557
        g_get_current_time (&tv);
 
558
 
 
559
        /* An event is ready if,
 
560
        *
 
561
        * it has no cookie -- there is nothing to be gained by holding it
 
562
        * or, it is already paired -- we don't need to hold it anymore
 
563
        * or, we have held it long enough
 
564
        */
 
565
        return event->event->cookie == 0 ||
 
566
               event->pair != NULL ||
 
567
               g_timeval_lt(&event->hold_until, &tv) || g_timeval_eq(&event->hold_until, &tv);
 
568
}
 
569
 
 
570
static void
 
571
ik_pair_moves (gpointer data, gpointer user_data)
 
572
{
 
573
        ik_event_internal_t *event = (ik_event_internal_t *)data;
 
574
 
 
575
        if (event->seen == TRUE || event->sent == TRUE)
 
576
        return;
 
577
 
 
578
        if (event->event->cookie != 0)
 
579
        {
 
580
                /* When we get a MOVED_FROM event we delay sending the event by
 
581
                * MOVE_HOLD_UNTIL_TIME microseconds. We need to do this because a
 
582
                * MOVED_TO pair _might_ be coming in the near future */
 
583
                if (event->event->mask & IN_MOVED_FROM) {
 
584
                        g_hash_table_insert (cookie_hash, GINT_TO_POINTER(event->event->cookie), event);
 
585
                        // because we don't deliver move events there is no point in waiting for the match right now.
 
586
                        ik_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME);
 
587
                } else if (event->event->mask & IN_MOVED_TO) {
 
588
                        /* We need to check if we are waiting for this MOVED_TO events cookie to pair it with
 
589
                        * a MOVED_FROM */
 
590
                        ik_event_internal_t *match = NULL;
 
591
                        match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->event->cookie));
 
592
                        if (match) {
 
593
                                g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->event->cookie));
 
594
                                ik_pair_events (match, event);
 
595
                        }
 
596
                }
 
597
        }
 
598
        event->seen = TRUE;
 
599
}
 
600
 
 
601
static void
 
602
ik_process_events ()
 
603
{
 
604
        g_queue_foreach (events_to_process, ik_pair_moves, NULL);
 
605
 
 
606
        while (!g_queue_is_empty (events_to_process))
 
607
        {
 
608
                ik_event_internal_t *event = g_queue_peek_head (events_to_process);
 
609
 
 
610
                /* This must have been sent as part of a MOVED_TO/MOVED_FROM */
 
611
                if (event->sent)
 
612
                {
 
613
                        /* Pop event */
 
614
                        g_queue_pop_head (events_to_process);
 
615
                        /* Free the internal event structure */
 
616
                        g_free (event);
 
617
                        continue;
 
618
                }
 
619
 
 
620
                /* The event isn't ready yet */
 
621
                if (!ik_event_ready (event)) {
 
622
                        break;
 
623
                }
 
624
 
 
625
                /* Pop it */
 
626
                event = g_queue_pop_head (events_to_process);
 
627
 
 
628
                /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */
 
629
                if (event->event->cookie && event->pair == NULL &&
 
630
                    g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->event->cookie)))
 
631
                {
 
632
                        g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->event->cookie));
 
633
                }
 
634
 
 
635
                if (event->pair) {
 
636
                        /* We send out paired MOVED_FROM/MOVED_TO events in the same event buffer */
 
637
                        //g_assert (event->event->mask == IN_MOVED_FROM && event->pair->event->mask == IN_MOVED_TO);
 
638
                        /* Copy the paired data */
 
639
                        event->pair->sent = TRUE;
 
640
                        event->sent = TRUE;
 
641
                        ik_move_matches++;
 
642
                } else if (event->event->cookie) {
 
643
                        /* If we couldn't pair a MOVED_FROM and MOVED_TO together, we change
 
644
                        * the event masks */
 
645
                        /* Changeing MOVED_FROM to DELETE and MOVED_TO to create lets us make
 
646
                        * the gaurantee that you will never see a non-matched MOVE event */
 
647
 
 
648
                        if (event->event->mask & IN_MOVED_FROM) {
 
649
                                event->event->mask = IN_DELETE|(event->event->mask & IN_ISDIR);
 
650
                                ik_move_misses++; // not super accurate, if we aren't watching the destination it still counts as a miss
 
651
                        }
 
652
                        if (event->event->mask & IN_MOVED_TO)
 
653
                                event->event->mask = IN_CREATE|(event->event->mask & IN_ISDIR);
 
654
                }
 
655
 
 
656
                /* Push the ik_event_t onto the event queue */
 
657
                g_queue_push_tail (event_queue, event->event);
 
658
                /* Free the internal event structure */
 
659
                g_free (event);
 
660
        }
 
661
}
 
662
 
 
663
gboolean ik_process_eq_callback (gpointer user_data)
 
664
{
 
665
    /* Try and move as many events to the event queue */
 
666
        G_LOCK(inotify_lock);
 
667
        ik_process_events ();
 
668
 
 
669
        while (!g_queue_is_empty (event_queue))
 
670
        {
 
671
                ik_event_t *event = g_queue_pop_head (event_queue);
 
672
 
 
673
                user_cb (event);
 
674
        }
 
675
 
 
676
        if (g_queue_get_length (events_to_process) == 0)
 
677
        {
 
678
                process_eq_running = FALSE;
 
679
                G_UNLOCK(inotify_lock);
 
680
                return FALSE;
 
681
        } else {
 
682
                G_UNLOCK(inotify_lock);
 
683
                return TRUE;
 
684
        }
 
685
}