GFileMonitor: Add kqueue(3) support to GIO
[platform/upstream/glib.git] / gio / kqueue / kqueue-helper.c
1 /*******************************************************************************
2   Copyright (c) 2011, 2012 Dmitry Matveev <me@dmitrymatveev.co.uk>
3
4   Permission is hereby granted, free of charge, to any person obtaining a copy
5   of this software and associated documentation files (the "Software"), to deal
6   in the Software without restriction, including without limitation the rights
7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8   copies of the Software, and to permit persons to whom the Software is
9   furnished to do so, subject to the following conditions:
10
11   The above copyright notice and this permission notice shall be included in
12   all copies or substantial portions of the Software.
13
14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   THE SOFTWARE.
21 *******************************************************************************/
22
23 #include "config.h"
24 #include <sys/types.h>
25 #include <sys/event.h>
26 #include <sys/time.h>
27 #include <sys/socket.h>
28 #include <gio/glocalfile.h>
29 #include <gio/gfile.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <pthread.h>
35 #include "kqueue-helper.h"
36 #include "kqueue-utils.h"
37 #include "kqueue-thread.h"
38 #include "kqueue-missing.h"
39 #include "kqueue-exclusions.h"
40
41 #include "gkqueuedirectorymonitor.h"
42
43 static gboolean kh_debug_enabled = FALSE;
44 #define KH_W if (kh_debug_enabled) g_warning
45
46 G_GNUC_INTERNAL G_LOCK_DEFINE (kqueue_lock);
47
48 static GHashTable *subs_hash_table = NULL;
49 G_GNUC_INTERNAL G_LOCK_DEFINE (hash_lock);
50
51 static int kqueue_descriptor = -1;
52 static int kqueue_socket_pair[] = {-1, -1};
53 static pthread_t kqueue_thread;
54
55
56 void _kh_file_appeared_cb (kqueue_sub *sub);
57
58 /**
59  * accessor function for kqueue_descriptor
60  **/
61 int
62 get_kqueue_descriptor()
63 {
64   return kqueue_descriptor;
65 }
66
67 /**
68  * convert_kqueue_events_to_gio:
69  * @flags: a set of kqueue filter flags
70  * @done: a pointer to #gboolean indicating that the
71  *      conversion has been done (out)
72  *
73  * Translates kqueue filter flags into GIO event flags.
74  *
75  * Returns: a #GFileMonitorEvent
76  **/
77 static GFileMonitorEvent
78 convert_kqueue_events_to_gio (uint32_t flags, gboolean *done)
79 {
80   g_assert (done != NULL);
81   *done = FALSE;
82
83   /* TODO: The following notifications should be emulated, if possible:
84    * - G_FILE_MONITOR_EVENT_PRE_UNMOUNT
85    */
86   if (flags & NOTE_DELETE)
87     {    
88       *done = TRUE;
89       return G_FILE_MONITOR_EVENT_DELETED;
90     }
91   if (flags & NOTE_ATTRIB)
92     {
93       *done = TRUE;
94       return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
95     }
96   if (flags & (NOTE_WRITE | NOTE_EXTEND))
97     {
98       *done = TRUE;
99       return G_FILE_MONITOR_EVENT_CHANGED;
100     }
101   if (flags & NOTE_RENAME)
102     {
103       *done = TRUE;
104       return G_FILE_MONITOR_EVENT_MOVED;
105     }
106   if (flags & NOTE_REVOKE)
107     {
108       *done = TRUE;
109       return G_FILE_MONITOR_EVENT_UNMOUNTED;
110     }
111
112   /* done is FALSE */
113   return 0;
114 }
115
116 typedef struct {
117   kqueue_sub *sub;
118   GFileMonitor *monitor;  
119 } handle_ctx;
120
121 /**
122  * handle_created: 
123  * @udata: a pointer to user data (#handle_context).
124  * @path: file name of a new file.
125  * @inode: inode number of a new file.
126  *
127  * A callback function for the directory diff calculation routine,
128  * produces G_FILE_MONITOR_EVENT_CREATED event for a created file.
129  **/
130 static void
131 handle_created (void *udata, const char *path, ino_t inode)
132 {
133   handle_ctx *ctx = NULL;
134   GFile *file = NULL;
135   gchar *fpath = NULL;
136
137   (void) inode;
138   ctx = (handle_ctx *) udata;
139   g_assert (udata != NULL);
140   g_assert (ctx->sub != NULL);
141   g_assert (ctx->monitor != NULL);
142
143   fpath = _ku_path_concat (ctx->sub->filename, path);
144   if (fpath == NULL)
145     {
146       KH_W ("Failed to allocate a string for a new event");
147       return;
148     }
149
150   file = g_file_new_for_path (fpath);
151   g_file_monitor_emit_event (ctx->monitor,
152                              file,
153                              NULL,
154                              G_FILE_MONITOR_EVENT_CREATED);
155   g_free (fpath);
156   g_object_unref (file);
157 }
158
159 /**
160  * handle_deleted:
161  * @udata: a pointer to user data (#handle_context).
162  * @path: file name of the removed file.
163  * @inode: inode number of the removed file.
164  *
165  * A callback function for the directory diff calculation routine,
166  * produces G_FILE_MONITOR_EVENT_DELETED event for a deleted file.
167  **/
168 static void
169 handle_deleted (void *udata, const char *path, ino_t inode)
170 {
171   handle_ctx *ctx = NULL;
172   GFile *file = NULL;
173   gchar *fpath = NULL;
174
175   (void) inode;
176   ctx = (handle_ctx *) udata;
177   g_assert (udata != NULL);
178   g_assert (ctx->sub != NULL);
179   g_assert (ctx->monitor != NULL);
180
181   fpath = _ku_path_concat (ctx->sub->filename, path);
182   if (fpath == NULL)
183     {
184       KH_W ("Failed to allocate a string for a new event");
185       return;
186     }
187
188   file = g_file_new_for_path (fpath);
189   g_file_monitor_emit_event (ctx->monitor,
190                              file,
191                              NULL,
192                              G_FILE_MONITOR_EVENT_DELETED);
193   g_free (fpath);
194   g_object_unref (file);
195 }
196
197 /**
198  * handle_moved:
199  * @udata: a pointer to user data (#handle_context).
200  * @from_path: file name of the source file.
201  * @from_inode: inode number of the source file.
202  * @to_path: file name of the replaced file.
203  * @to_inode: inode number of the replaced file.
204  *
205  * A callback function for the directory diff calculation routine,
206  * produces G_FILE_MONITOR_EVENT_MOVED event on a move.
207  **/
208 static void
209 handle_moved (void       *udata,
210               const char *from_path,
211               ino_t       from_inode,
212               const char *to_path,
213               ino_t       to_inode)
214 {
215   handle_ctx *ctx = NULL;
216   GFile *file = NULL;
217   GFile *other = NULL;
218   gchar *path = NULL;
219   gchar *npath = NULL;
220
221   (void) from_inode;
222   (void) to_inode;
223
224   ctx = (handle_ctx *) udata;
225   g_assert (udata != NULL);
226   g_assert (ctx->sub != NULL);
227   g_assert (ctx->monitor != NULL);
228
229
230   path = _ku_path_concat (ctx->sub->filename, from_path);
231   npath = _ku_path_concat (ctx->sub->filename, to_path);
232   if (path == NULL || npath == NULL)
233     {
234       KH_W ("Failed to allocate strings for event");
235       return;
236     }
237
238   file = g_file_new_for_path (path);
239   other = g_file_new_for_path (npath);
240
241   if (ctx->sub->pair_moves)
242     {
243       g_file_monitor_emit_event (ctx->monitor,
244                                  file,
245                                  other,
246                                  G_FILE_MONITOR_EVENT_MOVED);
247     }
248   else
249     {
250       g_file_monitor_emit_event (ctx->monitor,
251                                  file,
252                                  NULL,
253                                  G_FILE_MONITOR_EVENT_DELETED);
254       g_file_monitor_emit_event (ctx->monitor,
255                                  other,
256                                  NULL,
257                                  G_FILE_MONITOR_EVENT_CREATED);
258     }
259
260   g_free (path);
261   g_free (npath);
262
263   g_object_unref (file);
264   g_object_unref (other);
265 }
266
267
268 /**
269  * handle_overwritten:
270  * @data: a pointer to user data (#handle_context).
271  * @path: file name of the overwritten file.
272  * @node: inode number of the overwritten file.
273  *
274  * A callback function for the directory diff calculation routine,
275  * produces G_FILE_MONITOR_EVENT_DELETED/CREATED event pair when
276  * an overwrite occurs in the directory (see dep-list for details).
277  **/
278 static void
279 handle_overwritten (void *udata, const char *path, ino_t inode)
280 {
281   handle_ctx *ctx = NULL;
282   GFile *file = NULL;
283   gchar *fpath = NULL;
284
285   (void) inode;
286   ctx = (handle_ctx *) udata;
287   g_assert (udata != NULL);
288   g_assert (ctx->sub != NULL);
289   g_assert (ctx->monitor != NULL);
290
291   fpath = _ku_path_concat (ctx->sub->filename, path);
292   if (fpath == NULL)
293     {
294       KH_W ("Failed to allocate a string for a new event");
295       return;
296     }
297
298   file = g_file_new_for_path (fpath);
299   g_file_monitor_emit_event (ctx->monitor,
300                              file,
301                              NULL,
302                              G_FILE_MONITOR_EVENT_DELETED);
303   g_file_monitor_emit_event (ctx->monitor,
304                              file,
305                              NULL,
306                              G_FILE_MONITOR_EVENT_CREATED);
307
308   g_free (fpath);
309   g_object_unref (file);
310 }
311
312 static const traverse_cbs cbs = {
313   handle_created,
314   handle_deleted,
315   handle_moved,
316   handle_overwritten,
317   handle_moved,
318   NULL, /* many added */
319   NULL, /* many removed */
320   NULL, /* names updated */
321 };
322
323
324 void
325 _kh_dir_diff (kqueue_sub *sub, GFileMonitor *monitor)
326 {
327   dep_list *was;
328   handle_ctx ctx;
329
330   g_assert (sub != NULL);
331   g_assert (monitor != NULL);
332
333   memset (&ctx, 0, sizeof (handle_ctx));
334   ctx.sub = sub;
335   ctx.monitor = monitor;
336
337   was = sub->deps;
338   sub->deps = dl_listing (sub->filename);
339  
340   dl_calculate (was, sub->deps, &cbs, &ctx);
341
342   dl_free (was);
343 }
344
345
346 /**
347  * process_kqueue_notifications:
348  * @gioc: unused.
349  * @cond: unused.
350  * @data: unused.
351  *
352  * Processes notifications, coming from the kqueue thread.
353  *
354  * Reads notifications from the command file descriptor, emits the
355  * "changed" event on the appropriate monitor.
356  *
357  * A typical GIO Channel callback function.
358  *
359  * Returns: %TRUE
360  **/
361 static gboolean
362 process_kqueue_notifications (GIOChannel   *gioc,
363                               GIOCondition  cond,
364                               gpointer      data)
365 {
366   struct kqueue_notification n;
367   kqueue_sub *sub = NULL;
368   GFileMonitor *monitor = NULL;
369   GFileMonitorEvent mask = 0;
370   
371   g_assert (kqueue_socket_pair[0] != -1);
372   if (!_ku_read (kqueue_socket_pair[0], &n, sizeof (struct kqueue_notification)))
373     {
374       KH_W ("Failed to read a kqueue notification, error %d", errno);
375       return TRUE;
376     }
377
378   G_LOCK (hash_lock);
379   sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd));
380   G_UNLOCK (hash_lock);
381
382   if (sub == NULL)
383     {
384       KH_W ("Got a notification for a deleted or non-existing subscription %d",
385              n.fd);
386       return TRUE;
387     }
388
389   monitor = G_FILE_MONITOR (sub->user_data);
390   g_assert (monitor != NULL);
391
392   if (n.flags & (NOTE_DELETE | NOTE_REVOKE))
393     {
394       if (sub->deps)
395         {
396           dl_free (sub->deps);
397           sub->deps = NULL;  
398         }  
399       _km_add_missing (sub);
400
401       if (!(n.flags & NOTE_REVOKE))
402         {
403           /* Note that NOTE_REVOKE is issued by the kqueue thread
404            * on EV_ERROR kevent. In this case, a file descriptor is
405            * already closed from the kqueue thread, no need to close
406            * it manually */ 
407           _kh_cancel_sub (sub);
408         }
409     }
410
411   if (sub->is_dir && n.flags & (NOTE_WRITE | NOTE_EXTEND))
412     {
413       _kh_dir_diff (sub, monitor);  
414       n.flags &= ~(NOTE_WRITE | NOTE_EXTEND);
415     }
416
417   if (n.flags)
418     {
419       gboolean done = FALSE;
420       mask = convert_kqueue_events_to_gio (n.flags, &done);
421       if (done == TRUE)
422         {
423           GFile *file = g_file_new_for_path (sub->filename);
424           g_file_monitor_emit_event (monitor, file, NULL, mask);
425           g_object_unref (file);
426         }
427     }
428
429   return TRUE;
430 }
431
432
433 /**
434  * _kh_startup_impl:
435  * @unused: unused
436  *
437  * Kqueue backend startup code. Should be called only once.
438  *
439  * Returns: %TRUE on success, %FALSE otherwise.
440  **/
441 static gpointer
442 _kh_startup_impl (gpointer unused)
443 {
444   GIOChannel *channel = NULL;
445   gboolean result = FALSE;
446
447   kqueue_descriptor = kqueue ();
448   result = (kqueue_descriptor != -1);
449   if (!result)
450     {
451       KH_W ("Failed to initialize kqueue\n!");
452       return GINT_TO_POINTER (FALSE);
453     }
454
455   result = socketpair (AF_UNIX, SOCK_STREAM, 0, kqueue_socket_pair);
456   if (result != 0)
457     {
458       KH_W ("Failed to create socket pair\n!");
459       return GINT_TO_POINTER (FALSE) ;
460     }
461
462   result = pthread_create (&kqueue_thread,
463                            NULL,
464                            _kqueue_thread_func,
465                            &kqueue_socket_pair[1]);
466   if (result != 0)
467     {
468       KH_W ("Failed to run kqueue thread\n!");
469       return GINT_TO_POINTER (FALSE);
470     }
471
472   _km_init (_kh_file_appeared_cb);
473
474   channel = g_io_channel_unix_new (kqueue_socket_pair[0]);
475   g_io_add_watch (channel, G_IO_IN, process_kqueue_notifications, NULL);
476
477   subs_hash_table = g_hash_table_new (g_direct_hash, g_direct_equal);
478
479   KH_W ("started gio kqueue backend\n");
480   return GINT_TO_POINTER (TRUE);
481 }
482
483
484 /**
485  * _kh_startup:
486  * Kqueue backend initialization.
487  *
488  * Returns: %TRUE on success, %FALSE otherwise.
489  **/
490 gboolean
491 _kh_startup (void)
492 {
493   static GOnce init_once = G_ONCE_INIT;
494   g_once (&init_once, _kh_startup_impl, NULL);
495   return GPOINTER_TO_INT (init_once.retval);
496 }
497
498
499 /**
500  * _kh_start_watching:
501  * @sub: a #kqueue_sub
502  *
503  * Starts watching on a subscription.
504  *
505  * Returns: %TRUE on success, %FALSE otherwise.
506  **/
507 gboolean
508 _kh_start_watching (kqueue_sub *sub)
509 {
510   g_assert (kqueue_socket_pair[0] != -1);
511   g_assert (sub != NULL);
512   g_assert (sub->filename != NULL);
513
514   /* kqueue requires a file descriptor to monitor. Sad but true */
515   sub->fd = open (sub->filename, O_RDONLY);
516
517   if (sub->fd == -1)
518     {
519       KH_W ("failed to open file %s (error %d)", sub->filename, errno);
520       return FALSE;
521     }
522
523   _ku_file_information (sub->fd, &sub->is_dir, NULL);
524   if (sub->is_dir)
525     {
526       /* I know, it is very bad to make such decisions in this way and here.
527        * We already do have an user_data at the #kqueue_sub, and it may point to
528        * GKqueueFileMonitor or GKqueueDirectoryMonitor. For a directory case,
529        * we need to scan in contents for the further diffs. Ideally this process
530        * should be delegated to the GKqueueDirectoryMonitor, but for now I will
531        * do it in a dirty way right here. */
532       if (sub->deps)
533         dl_free (sub->deps);
534
535       sub->deps = dl_listing (sub->filename);  
536     }
537
538   G_LOCK (hash_lock);
539   g_hash_table_insert (subs_hash_table, GINT_TO_POINTER (sub->fd), sub);
540   G_UNLOCK (hash_lock);
541
542   _kqueue_thread_push_fd (sub->fd);
543   
544   /* Bump the kqueue thread. It will pick up a new sub entry to monitor */
545   if (!_ku_write (kqueue_socket_pair[0], "A", 1))
546     KH_W ("Failed to bump the kqueue thread (add fd, error %d)", errno);
547   return TRUE;
548 }
549
550
551 /**
552  * _kh_add_sub:
553  * @sub: a #kqueue_sub
554  *
555  * Adds a subscription for monitoring.
556  *
557  * This funciton tries to start watching a subscription with
558  * _kh_start_watching(). On failure, i.e. when a file does not exist yet,
559  * the subscription will be added to a list of missing files to continue
560  * watching when the file will appear.
561  *
562  * Returns: %TRUE
563  **/
564 gboolean
565 _kh_add_sub (kqueue_sub *sub)
566 {
567   g_assert (sub != NULL);
568
569   if (!_kh_start_watching (sub))
570     _km_add_missing (sub);
571
572   return TRUE;
573 }
574
575
576 /**
577  * _kh_cancel_sub:
578  * @sub a #kqueue_sub
579  *
580  * Stops monitoring on a subscription.
581  *
582  * Returns: %TRUE
583  **/
584 gboolean
585 _kh_cancel_sub (kqueue_sub *sub)
586 {
587   gboolean missing = FALSE;
588   g_assert (kqueue_socket_pair[0] != -1);
589   g_assert (sub != NULL);
590
591   G_LOCK (hash_lock);
592   missing = !g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd));
593   G_UNLOCK (hash_lock);
594
595   if (missing)
596     {
597       /* If there were no fd for this subscription, file is still
598        * missing. */
599       KH_W ("Removing subscription from missing");
600       _km_remove (sub);
601     }
602   else
603     {
604       /* fd will be closed in the kqueue thread */
605       _kqueue_thread_remove_fd (sub->fd);
606
607       /* Bump the kqueue thread. It will pick up a new sub entry to remove*/
608       if (!_ku_write (kqueue_socket_pair[0], "R", 1))
609         KH_W ("Failed to bump the kqueue thread (remove fd, error %d)", errno);
610     }
611
612   return TRUE;
613 }
614
615
616 /**
617  * _kh_file_appeared_cb:
618  * @sub: a #kqueue_sub
619  *
620  * A callback function for kqueue-missing subsystem.
621  *
622  * Signals that a missing file has finally appeared in the filesystem.
623  * Emits %G_FILE_MONITOR_EVENT_CREATED.
624  **/
625 void
626 _kh_file_appeared_cb (kqueue_sub *sub)
627 {
628   GFile* child;
629
630   g_assert (sub != NULL);
631   g_assert (sub->filename);
632
633   if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS))
634     return;
635
636   child = g_file_new_for_path (sub->filename);
637
638   g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
639                              child,
640                              NULL,
641                              G_FILE_MONITOR_EVENT_CREATED);
642
643   g_object_unref (child);
644 }