1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
4 * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights
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.
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.
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.
22 * Authors: Lin Ma <lin.ma@sun.com>
30 #include "fen-kernel.h"
34 #ifdef GIO_COMPILATION
35 #include <gio/gfilemonitor.h>
37 #include "gam_event.h"
38 #include "gam_server.h"
39 #include "gam_protocol.h"
42 #ifdef GIO_COMPILATION
43 #define FN_W if (FALSE) g_debug
45 #include "gam_error.h"
46 #define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
49 G_LOCK_EXTERN (fen_lock);
51 /* Must continue monitoring if:
53 * 2) The subscribed children (one of the children has subs) are missing,
54 * 3) my parent is subscribed (monitoring directory).
56 #define NODE_NEED_MONITOR(f) \
57 (NODE_IS_ACTIVE(f) || node_children_num(f) > 0 || NODE_IS_REQUIRED_BY_PARENT(f))
59 static int concern_events[] = {
64 #ifdef GIO_COMPILATION
68 FILE_MODIFIED | FILE_ATTRIB,
75 static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event);
76 static void node_emit_events(node_t *f, const node_event_t *ne);
77 static int node_event_translate(int event, gboolean pair);
78 static void node_add_event (node_t *f, node_event_t *ev);
79 static node_t* node_new (node_t* parent, const gchar* basename);
80 static void node_delete (node_t* parent);
81 static node_t* node_get_child (node_t *f, const gchar *basename);
82 static void children_add (node_t *p, node_t *f);
83 static void children_remove (node_t *p, node_t *f);
84 static gboolean children_remove_cb (gpointer key, gpointer value, gpointer user_data);
85 static guint node_children_num (node_t *f);
88 node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2)
90 if (val1->tv_sec < val2->tv_sec)
93 if (val1->tv_sec > val2->tv_sec)
96 /* val1->tv_sec == val2->tv_sec */
97 if (val1->tv_usec < val2->tv_usec)
104 node_traverse (node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data)
109 g_assert(traverse_cb);
115 traverse_cb(node, user_data);
118 g_hash_table_iter_init (&iter, node->children);
119 while (g_hash_table_iter_next (&iter, NULL, &value)) {
120 node_traverse((node_t *)value, traverse_cb, user_data);
125 node_find(node_t* node, const gchar* filename, gboolean create_on_missing)
133 g_assert (filename && filename[0] == '/');
139 FN_W ("%s %s\n", __func__, filename);
141 parent = child = node;
142 str = g_strdup (filename);
144 for (token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts);
145 token != NULL && child != NULL;
146 token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) {
147 child = node_get_child(parent, token);
150 } else if (create_on_missing) {
151 child = node_new (parent, token);
153 children_add (parent, child);
157 FN_W ("%s create %s failed", __func__, token);
169 node_lstat(node_t *f)
173 g_assert(!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED));
175 if (lstat(NODE_NAME(f), &buf) == 0) {
176 FN_W ("%s %s\n", __func__, NODE_NAME(f));
177 FILE_OBJECT(f)->fo_atime = buf.st_atim;
178 FILE_OBJECT(f)->fo_mtime = buf.st_mtim;
179 FILE_OBJECT(f)->fo_ctime = buf.st_ctim;
180 NODE_SET_FLAG(f, NODE_FLAG_STAT_UPDATED |
181 (S_ISDIR (buf.st_mode) ? NODE_FLAG_DIR : NODE_FLAG_NONE));
184 FN_W ("%s(lstat) %s %s\n", __func__, NODE_NAME(f), g_strerror (errno));
190 node_create_children_snapshot(node_t *f, gint created_event, gboolean emit)
195 FN_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
197 dir = g_dir_open (NODE_NAME(f), 0, &err);
199 const char *basename;
200 node_t *child = NULL;
202 while ((basename = g_dir_read_name (dir))) {
206 child = node_get_child (f, basename);
210 child = node_new (f, basename);
211 children_add (f, child);
215 /* We need monitor the new children, or the existed child which
216 * is in the DELETED mode.
218 if (!NODE_HAS_STATE(child, NODE_STATE_ASSOCIATED) &&
219 node_lstat(child) == 0 && port_add(child) == 0) {
221 /* Emit the whatever event for the new found file. */
222 node_emit_one_event(child, child->dir_subs, NULL, created_event);
223 node_emit_one_event(child, child->subs, NULL, created_event);
224 node_emit_one_event(child, f->dir_subs, NULL, created_event);
225 node_emit_one_event(child, f->subs, NULL, created_event);
228 /* else ignore, because it may be deleted. */
233 /* We have finished children snapshot. Any other new added subs should
234 * directory iterate the snapshot instead of scan directory again.
236 NODE_SET_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED);
245 * If all active children nodes are ported, then cancel monitor the parent
246 * node. If we know how many children are created, then we can stop accordingly.
251 foreach_known_children_scan(gpointer key, gpointer value, gpointer user_data)
253 node_t* f = (node_t*)value;
255 FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
257 if (!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) {
258 if (node_lstat(f) == 0 && port_add(f) == 0) {
259 node_emit_one_event(f, f->dir_subs, NULL, FN_EVENT_CREATED);
260 node_emit_one_event(f, f->subs, NULL, FN_EVENT_CREATED);
261 if (NODE_PARENT(f)) {
262 node_emit_one_event(f, NODE_PARENT(f)->dir_subs, NULL, FN_EVENT_CREATED);
263 node_emit_one_event(f, NODE_PARENT(f)->subs, NULL, FN_EVENT_CREATED);
270 node_try_delete(node_t* node)
274 FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
276 /* Try clean children */
277 if (node_children_num (node) > 0) {
278 g_hash_table_foreach_remove(node->children, children_remove_cb, NULL);
280 if (!NODE_NEED_MONITOR(node)) {
281 /* Clean some flags. */
282 /* NODE_CLE_FLAG(node, NODE_FLAG_HAS_SNAPSHOT | NODE_FLAG_STAT_DONE); */
284 /* Now we handle the state. */
285 if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED)) {
288 /* Actually ignore the ROOT node. */
289 if (node->state == 0 && NODE_PARENT(node)) {
290 children_remove(NODE_PARENT(node), node);
291 /* Do clean instead of returning TRUE. */
294 /* else, we have events, clean event queue? */
300 node_new (node_t* parent, const gchar* basename)
304 g_assert (basename && basename[0]);
306 if ((f = g_new0(node_t, 1)) != NULL) {
308 NODE_NAME(f) = g_build_filename(NODE_NAME(parent), basename, NULL);
310 NODE_NAME(f) = g_strdup(G_DIR_SEPARATOR_S);
312 f->basename = g_strdup (basename);
313 /* f->children = g_hash_table_new_full (g_str_hash, g_str_equal, */
314 /* NULL, (GDestroyNotify)node_delete); */
315 f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
317 #ifdef GIO_COMPILATION
318 f->gfile = g_file_new_for_path (NODE_NAME(f));
320 FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
326 node_delete (node_t *f)
328 FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f));
331 g_assert(f->state == 0);
332 g_assert(!NODE_IS_ACTIVE(f));
333 g_assert(g_hash_table_size (f->children) == 0);
334 g_assert(NODE_PARENT(f) == NULL);
335 g_hash_table_unref(f->children);
336 #ifdef GIO_COMPILATION
337 g_object_unref (f->gfile);
340 g_free(NODE_NAME(f));
345 children_add (node_t *p, node_t *f)
347 FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
348 g_hash_table_insert (p->children, f->basename, f);
353 children_remove (node_t *p, node_t *f)
355 FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename);
356 g_hash_table_steal (p->children, f->basename);
357 NODE_PARENT(f) = NULL;
361 node_get_child (node_t *f, const gchar *basename)
364 return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
370 node_children_num (node_t *f)
372 return g_hash_table_size (f->children);
376 * depth first delete recursively
379 children_remove_cb (gpointer key, gpointer value, gpointer user_data)
381 return node_try_delete ((node_t*)value);
387 ROOT = node_new (NULL, G_DIR_SEPARATOR_S);
389 FN_W ("[node] Create ROOT node failed.\n");
393 return port_class_init (node_add_event);
397 * Adjust self on failing to Port
400 node_adjust_deleted(node_t* f)
404 FN_W ("%s %s\n", __func__, NODE_NAME(f));
406 for (ancestor = NODE_PARENT(f);
408 ancestor = NODE_PARENT(ancestor)) {
409 /* Stop if we find a node which been already associated or is existed
410 * and can be associated.
412 if (NODE_HAS_STATE(ancestor, NODE_STATE_ASSOCIATED) ||
413 (node_lstat(ancestor) == 0 && port_add(ancestor) == 0)) {
418 /* We assume we shouldn't reach here, because Root is always existed and
419 * associated. But given bugster#6955199, if PORT FS has problems on root,
420 * we may reach here. So just return ROOT and the whole GIO fen backend will
423 /* g_assert(ancestor != NULL); */
428 node_emit_events(node_t *f, const node_event_t *ne)
430 gsize num = sizeof(concern_events)/sizeof(int);
435 if (node_timeval_lt(&f->atv, &ne->ctv)) {
438 /* Emit DELETED on the pair_data */
440 node_t *from = ne->pair_data;
441 node_emit_one_event(from, from->dir_subs, NULL, node_event_translate(FILE_DELETE, FALSE));
442 node_emit_one_event(from, from->subs, NULL, node_event_translate(FILE_DELETE, FALSE));
445 for (i = 0; i < num; i++) {
446 if (event & concern_events[i]) {
447 translated_e = node_event_translate(concern_events[i], FALSE);
448 /* Neither GIO or gamin cares about modified events on a
451 #ifdef GIO_COMPILATION
452 if ((concern_events[i] & FILE_MODIFIED) == 0) {
453 node_emit_one_event(f, f->dir_subs, NULL, translated_e);
456 /* Gamin doesn't care about attrib changed events on a directory
459 if ((concern_events[i] & (FILE_MODIFIED | FILE_ATTRIB)) == 0) {
460 node_emit_one_event(f, f->dir_subs, NULL, translated_e);
463 node_emit_one_event(f, f->subs, NULL, translated_e);
465 event &= ~concern_events[i];
470 if (p != NULL && node_timeval_lt(&p->atv, &ne->ctv)) {
472 for (i = 0; i < num; i++) {
473 if (event & concern_events[i]) {
474 translated_e = node_event_translate(concern_events[i], ne->pair_data != NULL);
475 node_emit_one_event(f, p->dir_subs, ne->pair_data, translated_e);
476 node_emit_one_event(f, p->subs, ne->pair_data, translated_e);
478 event &= ~concern_events[i];
488 node_add_event (node_t *f, node_event_t *ev)
490 FN_W ("%s %d\n", __func__, ev->e);
492 /* Clean the events flag early, because all received events need be
493 * processed in this function.
495 NODE_CLE_STATE(f, NODE_STATE_HAS_EVENTS);
498 * Node the node has been created, so we can delete create event in
499 * optimizing. To reduce the statings, we add it to Port on discoving
500 * it then emit CREATED event. So we don't need to do anything here.
502 if (NODE_NEED_MONITOR(f)) {
503 if (HAS_NO_EXCEPTION_EVENTS(ev->e)) {
504 if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || port_add(f) == 0) {
505 if ((ev->e & FILE_MODIFIED) && NODE_HAS_FLAG(f, NODE_FLAG_DIR)) {
507 node_create_children_snapshot(f, FN_EVENT_CREATED, TRUE);
509 g_hash_table_foreach(f->children, foreach_known_children_scan, NULL);
513 /* Emit delete event */
514 ev->e |= FILE_DELETE;
516 node_adjust_deleted(f);
520 node_adjust_deleted(f);
523 /* Send events to clients. */
524 node_emit_events (f, ev);
527 /* Send events to clients. */
528 node_emit_events (f, ev);
534 node_t *from = ev->pair_data;
535 g_assert(ev->e == FILE_RENAME_TO);
537 if (NODE_NEED_MONITOR(from)) {
538 /* Clean the events flag, since it may block free this node. */
539 NODE_CLE_STATE(from, NODE_STATE_HAS_EVENTS);
540 node_adjust_deleted(from);
542 node_try_delete(from);
546 node_event_delete (ev);
550 node_emit_one_event(node_t *f, GList *subs, node_t *other, int event)
554 FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event);
556 #ifdef GIO_COMPILATION
557 for (idx = subs; idx; idx = idx->next) {
558 g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile,
559 (other == NULL ? NULL : other->gfile), event);
562 for (idx = subs; idx; idx = idx->next) {
563 gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1);
569 node_event_translate(int event, gboolean pair)
571 #ifdef GIO_COMPILATION
574 case FILE_RENAME_FROM:
575 return G_FILE_MONITOR_EVENT_DELETED;
577 return G_FILE_MONITOR_EVENT_UNMOUNTED;
579 return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
582 return G_FILE_MONITOR_EVENT_CHANGED;
585 return G_FILE_MONITOR_EVENT_MOVED;
587 return G_FILE_MONITOR_EVENT_CREATED;
590 /* case FILE_ACCESS: */
591 g_assert_not_reached ();
597 case FILE_RENAME_FROM:
598 return GAMIN_EVENT_DELETED;
601 return GAMIN_EVENT_CHANGED;
604 return GAMIN_EVENT_MOVED;
606 return GAMIN_EVENT_CREATED;
609 if (event & (FILE_ATTRIB | FILE_MODIFIED)) {
610 return GAMIN_EVENT_CHANGED;
612 /* case FILE_ACCESS: */
613 g_assert_not_reached ();
620 node_event_new (int event, gpointer user_data)
624 if ((ev = g_new (node_event_t, 1)) != NULL) {
627 ev->user_data = user_data;
628 ev->pair_data = NULL; /* For renamed file. */
629 /* Created timestamp */
630 g_get_current_time(&ev->ctv);
631 ev->rename_tv = ev->ctv;
637 node_event_delete (node_event_t* ev)