Reworked Solaris file event notification for GIO. See
[platform/upstream/glib.git] / gio / fen / fen-node.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /* 
4  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
5  * reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Lin Ma <lin.ma@sun.com>
23  */
24
25 #include "config.h"
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <strings.h>
29 #include <glib.h>
30 #include "fen-kernel.h"
31 #include "fen-node.h"
32 #include "fen-dump.h"
33
34 #ifdef GIO_COMPILATION
35 #include "gfilemonitor.h"
36 #else
37 #include "gam_event.h"
38 #include "gam_server.h"
39 #include "gam_protocol.h"
40 #endif
41
42 #ifdef GIO_COMPILATION
43 #define FN_W if (fn_debug_enabled) g_debug
44 static gboolean fn_debug_enabled = FALSE;
45 #else
46 #include "gam_error.h"
47 #define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
48 #endif
49
50 G_LOCK_EXTERN (fen_lock);
51
52 /* Must continue monitoring if:
53  * 1) I'm subscribed,
54  * 2) The subscribed children (one of the children has subs) are missing,
55  * 3) my parent is subscribed (monitoring directory).
56  */
57 #define NODE_NEED_MONITOR(f)                                            \
58     (NODE_IS_ACTIVE(f) || node_children_num(f) > 0 || NODE_IS_REQUIRED_BY_PARENT(f))
59
60 static int concern_events[] = {
61     FILE_DELETE,
62     FILE_RENAME_FROM,
63     UNMOUNTED,
64     MOUNTEDOVER,
65 #ifdef GIO_COMPILATION
66     FILE_MODIFIED,
67     FILE_ATTRIB,
68 #else
69     FILE_MODIFIED | FILE_ATTRIB,
70 #endif
71     FILE_RENAME_TO,
72 };
73
74 node_t *ROOT = NULL;
75
76 static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event);
77 static void node_emit_events(node_t *f, const node_event_t *ne);
78 static int node_event_translate(int event, gboolean pair);
79 static void node_add_event (node_t *f, node_event_t *ev);
80 static node_t* node_new (node_t* parent, const gchar* basename);
81 static void node_delete (node_t* parent);
82 static node_t* node_get_child (node_t *f, const gchar *basename);
83 static void children_add (node_t *p, node_t *f);
84 static void children_remove (node_t *p, node_t *f);
85 static gboolean children_remove_cb (gpointer key, gpointer value, gpointer user_data);
86 static guint node_children_num (node_t *f);
87
88 gboolean
89 node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2)
90 {
91     if (val1->tv_sec < val2->tv_sec)
92         return TRUE;
93   
94     if (val1->tv_sec > val2->tv_sec)
95         return FALSE;
96   
97     /* val1->tv_sec == val2->tv_sec */
98     if (val1->tv_usec < val2->tv_usec)
99         return TRUE;
100   
101     return FALSE;
102 }
103
104 void
105 node_traverse (node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data)
106 {
107     GHashTableIter iter;
108     gpointer value;
109
110     g_assert(traverse_cb);
111     if (node == NULL) {
112         node = ROOT;
113     }
114
115     if (node) {
116         traverse_cb(node, user_data);
117     }
118
119     g_hash_table_iter_init (&iter, node->children);
120     while (g_hash_table_iter_next (&iter, NULL, &value)) {
121         node_traverse((node_t *)value, traverse_cb, user_data);
122     }
123 }
124
125 node_t*
126 node_find(node_t* node, const gchar* filename, gboolean create_on_missing)
127 {
128     gchar* str;
129     gchar* token;
130     gchar* lasts;
131     node_t* parent;
132     node_t* child;
133     
134     g_assert (filename && filename[0] == '/');
135
136     if (node == NULL) {
137         node = ROOT;
138     }
139     
140     FN_W ("%s %s\n", __func__, filename);
141
142     parent = child = node;
143     str = g_strdup (filename);
144     
145     for (token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts);
146          token != NULL && child != NULL;
147          token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) {
148         FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
149         child = node_get_child(parent, token);
150         if (child) {
151             parent = child;
152         } else if (create_on_missing) {
153             child = node_new (parent, token);
154             if (child) {
155                 children_add (parent, child);
156                 parent = child;
157                 continue;
158             } else {
159                 FN_W ("%s create %s failed", __func__, token);
160             }
161         } else {
162             break;
163         }
164     }
165     
166     g_free (str);
167     return child;
168 }
169
170 node_t*
171 node_find_accessible_ancestor(node_t* node)
172 {
173     for (node = NODE_PARENT(node); node != ROOT; node = NODE_PARENT(node)) {
174         if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED) || node_lstat(node) == 0) {
175             return node;
176         }
177         /* else it isn't existing or not accessible */
178     }
179     g_assert(node);
180     return node;
181 }
182
183 gint
184 node_lstat(node_t *f)
185 {
186     struct stat buf;
187
188     g_assert(!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED));
189
190     if (lstat(NODE_NAME(f), &buf) == 0) {
191         FN_W ("%s %s\n", __func__, NODE_NAME(f));
192         FILE_OBJECT(f)->fo_atime = buf.st_atim;
193         FILE_OBJECT(f)->fo_mtime = buf.st_mtim;
194         FILE_OBJECT(f)->fo_ctime = buf.st_ctim;
195         NODE_SET_FLAG(f, NODE_FLAG_STAT_UPDATED |
196           (S_ISDIR (buf.st_mode) ? NODE_FLAG_DIR : NODE_FLAG_NONE));
197         return 0;
198     } else {
199         FN_W ("%s(lstat) %s %s\n", __func__, NODE_NAME(f), g_strerror (errno));
200     }
201     return errno;
202 }
203
204 void
205 node_create_children_snapshot(node_t *f, gint created_event, gboolean emit)
206 {
207         GDir *dir;
208         GError *err = NULL;
209     
210     FN_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
211
212     dir = g_dir_open (NODE_NAME(f), 0, &err);
213     if (dir) {
214         const char *basename;
215         node_t *child = NULL;
216         
217         while ((basename = g_dir_read_name (dir))) {
218             node_t* data;
219             GList *idx;
220
221             child = node_get_child (f, basename);
222             if (child == NULL) {
223                 gchar *filename;
224             
225                 child = node_new (f, basename);
226                 children_add (f, child);
227             }
228
229             if (f->dir_subs) {
230                 /* We need monitor the new children, or the existed child which
231                  * is in the DELETED mode.
232                  */
233                 if (!NODE_HAS_STATE(child, NODE_STATE_ASSOCIATED) &&
234                   node_lstat(child) == 0 && port_add(child) == 0) {
235                     if (emit) {
236                         /* Emit the whatever event for the new found file. */
237                         node_emit_one_event(child, child->dir_subs, NULL, created_event);
238                         node_emit_one_event(child, child->subs, NULL, created_event);
239                         node_emit_one_event(child, f->dir_subs, NULL, created_event);
240                         node_emit_one_event(child, f->subs, NULL, created_event);
241                     }
242                 }
243                 /* else ignore, because it may be deleted. */
244             }
245         }
246         g_dir_close (dir);
247
248         /* We have finished children snapshot. Any other new added subs should
249          * directory iterate the snapshot instead of scan directory again.
250          */
251         NODE_SET_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED);
252
253     } else {
254         FN_W (err->message);
255         g_error_free (err);
256     }
257 }
258
259 /**
260  * If all active children nodes are ported, then cancel monitor the parent
261  * node. If we know how many children are created, then we can stop accordingly.
262  *
263  * Unsafe, need lock. 
264  */
265 static void
266 foreach_known_children_scan(gpointer key, gpointer value, gpointer user_data)
267 {
268     node_t* f = (node_t*)value;
269     
270     FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
271
272     if (!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) {
273         if (node_lstat(f) == 0 && port_add(f) == 0) {
274             node_emit_one_event(f, f->dir_subs, NULL, FN_EVENT_CREATED);
275             node_emit_one_event(f, f->subs, NULL, FN_EVENT_CREATED);
276             if (NODE_PARENT(f)) {
277                 node_emit_one_event(f, NODE_PARENT(f)->dir_subs, NULL, FN_EVENT_CREATED);
278                 node_emit_one_event(f, NODE_PARENT(f)->subs, NULL, FN_EVENT_CREATED);
279             }
280         }
281     }
282 }
283
284 gboolean
285 node_try_delete(node_t* node)
286 {
287     g_assert (node);
288
289     FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
290
291     /* Try clean children */
292     if (node_children_num (node) > 0) {
293         g_hash_table_foreach_remove(node->children, children_remove_cb, NULL);
294     }
295     if (!NODE_NEED_MONITOR(node)) {
296         /* Clean some flags. */
297         /* NODE_CLE_FLAG(node, NODE_FLAG_HAS_SNAPSHOT | NODE_FLAG_STAT_DONE); */
298         node->flag = 0;
299
300         /* Now we handle the state. */
301         if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED)) {
302             port_remove(node);
303         }
304         /* Actually ignore the ROOT node. */
305         if (node->state == 0 && NODE_PARENT(node)) {
306             children_remove(NODE_PARENT(node), node);
307             /* Do clean instead of returning TRUE. */
308             node_delete (node);
309         }
310         /* else, we have events, clean event queue? */
311     }
312     return FALSE;
313 }
314
315 static node_t*
316 node_new (node_t* parent, const gchar* basename)
317 {
318         node_t *f = NULL;
319
320     g_assert (basename && basename[0]);
321
322     if ((f = g_new0(node_t, 1)) != NULL) {
323         if (parent) {
324             NODE_NAME(f) = g_build_filename(NODE_NAME(parent), basename, NULL);
325         } else {
326             NODE_NAME(f) = g_strdup(G_DIR_SEPARATOR_S);
327         }
328         f->basename = g_strdup (basename);
329         /* f->children = g_hash_table_new_full (g_str_hash, g_str_equal, */
330         /*   NULL, (GDestroyNotify)node_delete); */
331         f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
332           NULL, NULL);
333 #ifdef GIO_COMPILATION
334         f->gfile = g_file_new_for_path (NODE_NAME(f));
335 #endif
336         FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
337     }
338         return f;
339 }
340
341 static void
342 node_delete (node_t *f)
343 {
344     FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
345     g_assert(f->state == 0);
346     g_assert(!NODE_IS_ACTIVE(f));
347     g_assert(g_hash_table_size (f->children) == 0);
348     g_assert(NODE_PARENT(f) == NULL);
349     g_hash_table_unref(f->children);
350 #ifdef GIO_COMPILATION
351     g_object_unref (f->gfile);
352 #endif
353     g_free(f->basename);
354     g_free(NODE_NAME(f));
355     g_free (f);
356 }
357
358 static void
359 children_add (node_t *p, node_t *f)
360 {
361     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
362     g_hash_table_insert (p->children, f->basename, f);
363     NODE_PARENT(f) = p;
364 }
365
366 static void
367 children_remove (node_t *p, node_t *f)
368 {
369     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
370     g_hash_table_steal (p->children, f->basename);
371     NODE_PARENT(f) = NULL;
372 }
373
374 static node_t *
375 node_get_child (node_t *f, const gchar *basename)
376 {
377     if (f->children) {
378         return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
379     }
380     return NULL;
381 }
382
383 static guint
384 node_children_num (node_t *f)
385 {
386     return g_hash_table_size (f->children);
387 }
388
389 /**
390  * depth first delete recursively
391  */
392 static gboolean
393 children_remove_cb (gpointer key, gpointer value, gpointer user_data)
394 {
395     return node_try_delete ((node_t*)value);
396 }
397
398 gboolean
399 node_class_init()
400 {
401     ROOT = node_new (NULL, G_DIR_SEPARATOR_S);
402     if (ROOT == NULL) {
403         FN_W ("[node] Create ROOT node failed.\n");
404         return FALSE;
405     }
406
407     return port_class_init (node_add_event);
408 }
409
410 /**
411  * Adjust self on failing to Port
412  */
413 void
414 node_adjust_deleted(node_t* f)
415 {
416     node_t *ancestor;
417
418     FN_W ("%s %s\n", __func__, NODE_NAME(f));
419
420     for (ancestor = node_find_accessible_ancestor(f);
421          !NODE_HAS_STATE(ancestor, NODE_STATE_ASSOCIATED) && port_add(ancestor) != 0;
422          ancestor = node_find_accessible_ancestor(ancestor)) { /* Empty */ }
423 }
424
425
426 static void
427 node_emit_events(node_t *f, const node_event_t *ne)
428 {
429     gsize num = sizeof(concern_events)/sizeof(int);
430     gint i;
431     int translated_e;
432     node_t *p;
433
434     if (node_timeval_lt(&f->atv, &ne->ctv)) {
435         int event = ne->e;
436
437         /* Emit DELETED on the pair_data */
438         if (ne->pair_data) {
439             node_t *from = ne->pair_data;
440             node_emit_one_event(from, from->dir_subs, NULL, node_event_translate(FILE_DELETE, FALSE));
441             node_emit_one_event(from, from->subs, NULL, node_event_translate(FILE_DELETE, FALSE));
442         }
443
444         for (i = 0; i < num; i++) {
445             if (event & concern_events[i]) {
446                 translated_e = node_event_translate(concern_events[i], FALSE);
447                 /* Neither GIO or gamin cares about modified events on a
448                  * directory.
449                  */
450 #ifdef GIO_COMPILATION
451                 if ((concern_events[i] & FILE_MODIFIED) == 0) {
452                     node_emit_one_event(f, f->dir_subs, NULL, translated_e);
453                 }
454 #else
455                 /* Gamin doesn't care about attrib changed events on a directory
456                  * either.
457                  */
458                 if ((concern_events[i] & (FILE_MODIFIED | FILE_ATTRIB)) == 0) {
459                     node_emit_one_event(f, f->dir_subs, NULL, translated_e);
460                 }
461 #endif
462                 node_emit_one_event(f, f->subs, NULL, translated_e);
463             }
464             event &= ~concern_events[i];
465         }
466     }
467
468     p = NODE_PARENT(f);
469     if (p != NULL && node_timeval_lt(&p->atv, &ne->ctv)) {
470         int event = ne->e;
471         for (i = 0; i < num; i++) {
472             if (event & concern_events[i]) {
473                 translated_e = node_event_translate(concern_events[i], ne->pair_data != NULL);
474                 node_emit_one_event(f, p->dir_subs, ne->pair_data, translated_e);
475                 node_emit_one_event(f, p->subs, ne->pair_data, translated_e);
476             }
477             event &= ~concern_events[i];
478         }
479     }
480 }
481
482 /**
483  * node_add_event:
484  *
485  */
486 static void
487 node_add_event (node_t *f, node_event_t *ev)
488 {
489     FN_W ("%s %d\n", __func__, ev->e);
490
491     /* Clean the events flag early, because all received events need be
492      * processed in this function.
493      */
494     NODE_CLE_STATE(f, NODE_STATE_HAS_EVENTS);
495
496     /*
497      * Node the node has been created, so we can delete create event in
498      * optimizing. To reduce the statings, we add it to Port on discoving
499      * it then emit CREATED event. So we don't need to do anything here.
500      */
501     if (NODE_NEED_MONITOR(f)) {
502         if (HAS_NO_EXCEPTION_EVENTS(ev->e)) {
503             if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || port_add(f) == 0) {
504                 if ((ev->e & FILE_MODIFIED) && NODE_HAS_FLAG(f, NODE_FLAG_DIR)) {
505                     if (f->dir_subs) {
506                         node_create_children_snapshot(f, FN_EVENT_CREATED, TRUE);
507                     } else {
508                         g_hash_table_foreach(f->children, foreach_known_children_scan, NULL);
509                     }
510                 }
511             } else {
512                 /* Emit delete event */
513                 ev->e |= FILE_DELETE;
514
515                 node_adjust_deleted(f);
516             }
517
518         } else {
519             node_adjust_deleted(f);
520         }
521
522         /* Send events to clients. */
523         node_emit_events (f, ev);
524         
525     } else {
526         /* Send events to clients. */
527         node_emit_events (f, ev);
528
529         node_try_delete(f);
530     }
531
532     if (ev->pair_data) {
533         node_t *from = ev->pair_data;
534         g_assert(ev->e == FILE_RENAME_TO);
535
536         if (NODE_NEED_MONITOR(from)) {
537             /* Clean the events flag, since it may block free this node. */
538             NODE_CLE_STATE(from, NODE_STATE_HAS_EVENTS);
539             node_adjust_deleted(from);
540         } else {
541             node_try_delete(from);
542         }
543     }
544
545     node_event_delete (ev);
546 }
547
548 static void
549 node_emit_one_event(node_t *f, GList *subs, node_t *other, int event)
550 {
551     GList* idx;
552     
553     FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event);
554
555 #ifdef GIO_COMPILATION
556     for (idx = subs; idx; idx = idx->next) {
557         g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile,
558           (other == NULL ? NULL : other->gfile), event);
559     }
560 #else
561     for (idx = subs; idx; idx = idx->next) {
562         gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1);
563     }
564 #endif
565 }
566
567 static int
568 node_event_translate(int event, gboolean pair)
569 {
570 #ifdef GIO_COMPILATION
571     switch (event) {
572     case FILE_DELETE:
573     case FILE_RENAME_FROM:
574         return G_FILE_MONITOR_EVENT_DELETED;
575     case UNMOUNTED:
576         return G_FILE_MONITOR_EVENT_UNMOUNTED;
577     case FILE_ATTRIB:
578         return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
579     case MOUNTEDOVER:
580     case FILE_MODIFIED:
581         return G_FILE_MONITOR_EVENT_CHANGED;
582     case FILE_RENAME_TO:
583         if (pair) {
584             return G_FILE_MONITOR_EVENT_MOVED;
585         } else {
586             return G_FILE_MONITOR_EVENT_CREATED;
587         }
588     default:
589         /* case FILE_ACCESS: */
590         g_assert_not_reached ();
591         return -1;
592     }
593 #else
594     switch (event) {
595     case FILE_DELETE:
596     case FILE_RENAME_FROM:
597         return GAMIN_EVENT_DELETED;
598     case MOUNTEDOVER:
599     case UNMOUNTED:
600         return GAMIN_EVENT_CHANGED;
601     case FILE_RENAME_TO:
602         if (pair) {
603             return GAMIN_EVENT_MOVED;
604         } else {
605             return GAMIN_EVENT_CREATED;
606         }
607     default:
608         if (event & (FILE_ATTRIB | FILE_MODIFIED)) {
609             return GAMIN_EVENT_CHANGED;
610         }
611         /* case FILE_ACCESS: */
612         g_assert_not_reached ();
613         return -1;
614     }
615 #endif
616 }
617
618 node_event_t*
619 node_event_new (int event, gpointer user_data)
620 {
621     node_event_t *ev;
622     
623     if ((ev = g_new (node_event_t, 1)) != NULL) {
624         g_assert (ev);
625         ev->e = event;
626         ev->user_data = user_data;
627         ev->pair_data = NULL;   /* For renamed file. */
628         /* Created timestamp */
629         g_get_current_time(&ev->ctv);
630         ev->rename_tv = ev->ctv;
631     }
632     return ev;
633 }
634
635 void
636 node_event_delete (node_event_t* ev)
637 {
638     g_free (ev);
639 }
640