1 /* Gamin
2 * Copyright (C) 2003 James Willcox, Corey Bowers
3 * Copyright (C) 2004 Daniel Veillard
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "server_config.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <glib.h>
30 #include "fam.h"
31 #include "gam_error.h"
32 #include "gam_tree.h"
33 #include "gam_poll_dnotify.h"
34 #include "gam_event.h"
35 #include "gam_server.h"
36 #include "gam_protocol.h"
37 #include "gam_event.h"
38 #include "gam_excludes.h"
39
40 #define VERBOSE_POLL
41 #define VERBOSE_POLL2
42
43 static gboolean gam_poll_dnotify_add_subscription(GamSubscription * sub);
44 static gboolean gam_poll_dnotify_remove_subscription(GamSubscription * sub);
45 static gboolean gam_poll_dnotify_remove_all_for(GamListener * listener);
46 static GaminEventType gam_poll_dnotify_poll_file(GamNode * node);
47 static gboolean gam_poll_dnotify_scan_callback(gpointer data);
48
49
50 gboolean
gam_poll_dnotify_init()51 gam_poll_dnotify_init ()
52 {
53 gam_poll_generic_init ();
54 gam_server_install_poll_hooks (GAMIN_P_DNOTIFY,
55 gam_poll_dnotify_add_subscription,
56 gam_poll_dnotify_remove_subscription,
57 gam_poll_dnotify_remove_all_for,
58 gam_poll_dnotify_poll_file);
59
60 g_timeout_add(1000, gam_poll_dnotify_scan_callback, NULL);
61 return TRUE;
62 }
63
64 /**
65 * gam_poll_delist_node:
66 * @node: the node to delist
67 *
68 * This function is called when kernel monitoring for a node should
69 * be turned off.
70 */
71 static void
gam_poll_dnotify_delist_node(GamNode * node)72 gam_poll_dnotify_delist_node(GamNode * node)
73 {
74 GList *subs;
75 const char *path;
76
77 path = gam_node_get_path(node);
78
79 if (gam_exclude_check(path) || gam_fs_get_mon_type (path) != GFS_MT_KERNEL)
80 return;
81
82 GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Disabling kernel monitoring for %s\n", path);
83
84 subs = gam_node_get_subscriptions(node);
85 while (subs != NULL) {
86 gam_poll_generic_trigger_handler (path, GAMIN_DEACTIVATE, node);
87 subs = subs->next;
88 }
89 }
90
91 /**
92 * gam_poll_relist_node:
93 * @node: the node to delist
94 *
95 * This function is called when kernel monitoring for a node should
96 * be turned on (again).
97 */
98 static void
gam_poll_dnotify_relist_node(GamNode * node)99 gam_poll_dnotify_relist_node(GamNode * node)
100 {
101 GList *subs;
102 const char *path;
103
104 path = gam_node_get_path(node);
105 GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Enabling kernel monitoring for %s\n", path);
106
107 if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
108 return;
109
110 subs = gam_node_get_subscriptions(node);
111
112 while (subs != NULL) {
113 gam_poll_generic_trigger_handler (path, GAMIN_ACTIVATE, node);
114 subs = subs->next;
115 }
116 }
117
118 /**
119 * gam_poll_flowon_node:
120 * @node: the node to delist
121 *
122 * This function is called when kernel monitoring flow control for a
123 * node should be started
124 */
125 static void
gam_poll_dnotify_flowon_node(GamNode * node)126 gam_poll_dnotify_flowon_node(GamNode * node)
127 {
128 const char *path;
129
130 path = gam_node_get_path(node);
131
132 if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
133 return;
134
135 GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Enabling flow control for %s\n", path);
136
137 gam_poll_generic_trigger_handler (path, GAMIN_FLOWCONTROLSTART, node);
138 }
139
140 /**
141 * gam_poll_flowoff_node:
142 * @node: the node to delist
143 *
144 * This function is called when kernel monitoring flow control for a
145 * node should be started
146 */
147 static void
gam_poll_dnotify_flowoff_node(GamNode * node)148 gam_poll_dnotify_flowoff_node(GamNode * node)
149 {
150 const char *path;
151
152 path = gam_node_get_path(node);
153
154 if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
155 return;
156
157 GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Disabling flow control for %s\n", path);
158
159 gam_poll_generic_trigger_handler (path, GAMIN_FLOWCONTROLSTOP, node);
160 }
161
162 static GaminEventType
gam_poll_dnotify_poll_file(GamNode * node)163 gam_poll_dnotify_poll_file(GamNode * node)
164 {
165 GaminEventType event;
166 struct stat sbuf;
167 int stat_ret;
168 const char *path;
169
170 /* If not enough time has passed since the last time we polled this node, stop here */
171 if (node->lasttime && gam_poll_generic_get_delta_time (node->lasttime) < node->poll_time)
172 return 0;
173
174 path = gam_node_get_path(node);
175 #ifdef VERBOSE_POLL
176 GAM_DEBUG(DEBUG_INFO, "Poll: poll_file for %s called\n", path);
177 #endif
178
179 memset(&sbuf, 0, sizeof(struct stat));
180 if (node->lasttime == 0) {
181 GAM_DEBUG(DEBUG_INFO, "Poll: file is new\n");
182 stat_ret = stat(node->path, &sbuf);
183 if (stat_ret != 0)
184 gam_node_set_pflag (node, MON_MISSING);
185 else
186 gam_node_set_is_dir(node, (S_ISDIR(sbuf.st_mode) != 0));
187
188 if (gam_exclude_check(path) || gam_fs_get_mon_type (path) != GFS_MT_KERNEL)
189 gam_node_set_pflag (node, MON_NOKERNEL);
190
191 memcpy(&(node->sbuf), &(sbuf), sizeof(struct stat));
192 node->lasttime = gam_poll_generic_get_time ();
193
194 if (stat_ret == 0)
195 return 0;
196 else
197 return GAMIN_EVENT_DELETED;
198 }
199
200 #ifdef VERBOSE_POLL
201 GAM_DEBUG(DEBUG_INFO, " at %d delta %d : %d\n", gam_poll_generic_get_time(), gam_poll_generic_get_time() - node->lasttime, node->checks);
202 #endif
203
204 event = 0;
205
206 stat_ret = stat(node->path, &sbuf);
207 if (stat_ret != 0) {
208 if ((gam_errno() == ENOENT) && (!gam_node_has_pflag(node, MON_MISSING))) {
209 /* deleted */
210 gam_node_set_pflags (node, MON_MISSING);
211
212 gam_poll_generic_remove_busy(node);
213 if (gam_node_get_subscriptions(node) != NULL) {
214 gam_poll_dnotify_delist_node(node);
215 gam_poll_generic_add_missing(node);
216 }
217 event = GAMIN_EVENT_DELETED;
218 }
219 } else if (gam_node_has_pflag (node, MON_MISSING)) {
220 /* created */
221 gam_node_unset_pflag (node, MON_MISSING);
222 event = GAMIN_EVENT_CREATED;
223 #ifdef ST_MTIM_NSEC
224 } else if ((node->sbuf.st_mtim.tv_sec != sbuf.st_mtim.tv_sec) ||
225 (node->sbuf.st_mtim.tv_nsec != sbuf.st_mtim.tv_nsec) ||
226 (node->sbuf.st_size != sbuf.st_size) ||
227 (node->sbuf.st_ctim.tv_sec != sbuf.st_ctim.tv_sec) ||
228 (node->sbuf.st_ctim.tv_nsec != sbuf.st_ctim.tv_nsec))
229 {
230 event = GAMIN_EVENT_CHANGED;
231 } else {
232 #ifdef VERBOSE_POLL
233 GAM_DEBUG(DEBUG_INFO, "Poll: poll_file %s unchanged\n", path);
234 GAM_DEBUG(DEBUG_INFO, "%d %d : %d %d\n", node->sbuf.st_mtim.tv_sec, node->sbuf.st_mtim.tv_nsec, sbuf.st_mtim.tv_sec, sbuf.st_mtim.tv_nsec);
235 #endif
236 #else
237 } else if ((node->sbuf.st_mtime != sbuf.st_mtime) ||
238 (node->sbuf.st_size != sbuf.st_size) ||
239 (node->sbuf.st_ctime != sbuf.st_ctime))
240 {
241 event = GAMIN_EVENT_CHANGED;
242 #ifdef VERBOSE_POLL
243 GAM_DEBUG(DEBUG_INFO, "%d : %d\n", node->sbuf.st_mtime, sbuf.st_mtime);
244 #endif
245 #endif
246 }
247
248 /*
249 * TODO: handle the case where a file/dir is removed and replaced by
250 * a dir/file
251 */
252 if (stat_ret == 0)
253 gam_node_set_is_dir(node, (S_ISDIR(sbuf.st_mode) != 0));
254
255 memcpy(&(node->sbuf), &(sbuf), sizeof(struct stat));
256 node->sbuf.st_mtime = sbuf.st_mtime; // VALGRIND!
257
258 /*
259 * if kernel monitoring prohibited, stop here
260 */
261 if (gam_node_has_pflag (node, MON_NOKERNEL))
262 return (event);
263
264 /*
265 * load control, switch back to poll on very busy resources
266 * and back when no update has happened in 5 seconds
267 */
268 if (gam_poll_generic_get_time() == node->lasttime) {
269 if (!gam_node_has_pflag (node, MON_BUSY)) {
270 if (node->sbuf.st_mtime == gam_poll_generic_get_time())
271 node->checks++;
272 }
273 } else {
274 node->lasttime = gam_poll_generic_get_time();
275 if (gam_node_has_pflag (node, MON_BUSY)) {
276 if (event == 0)
277 node->checks++;
278 } else {
279 node->checks = 0;
280 }
281 }
282
283 if ((node->checks >= 4) && (!gam_node_has_pflag (node, MON_BUSY))) {
284 if ((gam_node_get_subscriptions(node) != NULL) &&
285 (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
286 {
287 GAM_DEBUG(DEBUG_INFO, "switching %s back to polling\n", path);
288 gam_node_set_pflag (node, MON_BUSY);
289 node->checks = 0;
290 gam_poll_generic_add_busy(node);
291 gam_poll_dnotify_flowon_node(node);
292 /*
293 * DNotify can be nasty here, we will miss events for parent dir
294 * if we are not careful about it
295 */
296 if (!gam_node_is_dir(node)) {
297 GamNode *parent = gam_node_parent(node);
298
299 if ((parent != NULL) &&
300 (gam_node_get_subscriptions(parent) != NULL))
301 {
302 gam_poll_generic_add_busy(parent);
303 /* gam_poll_flowon_node(parent); */
304 }
305 }
306 }
307 }
308
309 if ((event == 0) && gam_node_has_pflag (node, MON_BUSY) && (node->checks > 5))
310 {
311 if ((gam_node_get_subscriptions(node) != NULL) &&
312 (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
313 {
314 GAM_DEBUG(DEBUG_INFO, "switching %s back to kernel monitoring\n", path);
315 gam_node_unset_pflag (node, MON_BUSY);
316 node->checks = 0;
317 gam_poll_generic_remove_busy(node);
318 gam_poll_dnotify_flowoff_node(node);
319 }
320 }
321
322 return (event);
323 }
324
325 /**
326 * node_add_subscription:
327 * @node: the node tree pointer
328 * @sub: the pointer to the subscription
329 *
330 * register a subscription for this node
331 *
332 * Returns 0 in case of success and -1 in case of failure
333 */
334 static int
node_add_subscription(GamNode * node,GamSubscription * sub)335 node_add_subscription(GamNode * node, GamSubscription * sub)
336 {
337 if ((node == NULL) || (sub == NULL))
338 return (-1);
339
340 if ((node->path == NULL) || (node->path[0] != '/'))
341 return (-1);
342
343 GAM_DEBUG(DEBUG_INFO, "node_add_subscription(%s)\n", node->path);
344 gam_node_add_subscription(node, sub);
345
346 if (gam_exclude_check(node->path) || gam_fs_get_mon_type (node->path) == GFS_MT_POLL) {
347 GAM_DEBUG(DEBUG_INFO, " gam_exclude_check: true\n");
348 if (node->lasttime == 0)
349 gam_poll_dnotify_poll_file(node);
350
351 gam_poll_generic_add_missing(node);
352 return (0);
353 }
354
355 gam_poll_generic_trigger_handler (node->path, GAMIN_ACTIVATE, node);
356
357 return (0);
358 }
359
360 /**
361 * node_remove_subscription:
362 * @node: the node tree pointer
363 * @sub: the pointer to the subscription
364 *
365 * Removes a subscription for this node
366 *
367 * Returns 0 in case of success and -1 in case of failure
368 */
369
370 static int
node_remove_subscription(GamNode * node,GamSubscription * sub)371 node_remove_subscription(GamNode * node, GamSubscription * sub)
372 {
373 const char *path;
374
375 if ((node == NULL) || (sub == NULL))
376 return (-1);
377
378 if ((node->path == NULL) || (node->path[0] != '/'))
379 return (-1);
380
381 GAM_DEBUG(DEBUG_INFO, "node_remove_subscription(%s)\n", node->path);
382
383 gam_node_remove_subscription(node, sub);
384
385 path = node->path;
386 if (gam_exclude_check(path) || gam_fs_get_mon_type (path) == GFS_MT_POLL) {
387 GAM_DEBUG(DEBUG_INFO, " gam_exclude_check: true\n");
388 return (0);
389 }
390
391 if (node->pflags == MON_BUSY) {
392 GAM_DEBUG(DEBUG_INFO, " node is busy\n");
393 } else if (gam_node_has_pflags (node, MON_ALL_PFLAGS)) {
394 GAM_DEBUG(DEBUG_INFO, " node has flag %d\n", node->pflags);
395 return (0);
396 }
397
398 /* DNotify makes our life miserable here */
399 gam_poll_generic_trigger_handler (node->path, GAMIN_DEACTIVATE, node);
400
401 return (0);
402 }
403
404 static gboolean
node_remove_directory_subscription(GamNode * node,GamSubscription * sub)405 node_remove_directory_subscription(GamNode * node, GamSubscription * sub)
406 {
407 GList *children, *l;
408 gboolean remove_dir;
409
410 GAM_DEBUG(DEBUG_INFO, "remove_directory_subscription %s\n",
411 gam_node_get_path(node));
412
413 node_remove_subscription(node, sub);
414
415 remove_dir = (gam_node_get_subscriptions(node) == NULL);
416
417 children = gam_tree_get_children(gam_poll_generic_get_tree(), node);
418 for (l = children; l; l = l->next) {
419 GamNode *child = (GamNode *) l->data;
420
421 if ((!gam_node_get_subscriptions(child)) && (remove_dir) &&
422 (!gam_tree_has_children(gam_poll_generic_get_tree(), child))) {
423 gam_poll_generic_unregister_node (child);
424
425 gam_tree_remove(gam_poll_generic_get_tree(), child);
426 } else {
427 remove_dir = FALSE;
428 }
429 }
430
431 g_list_free(children);
432
433 /*
434 * do not remove the directory if the parent has a directory subscription
435 */
436 remove_dir = ((gam_node_get_subscriptions(node) == NULL) &&
437 (!gam_node_has_dir_subscriptions
438 (gam_node_parent(node))));
439
440 if (remove_dir) {
441 GAM_DEBUG(DEBUG_INFO, " => remove_dir %s\n",
442 gam_node_get_path(node));
443 }
444 return remove_dir;
445 }
446
447
448 /**
449 * Adds a subscription to be polled.
450 *
451 * @param sub a #GamSubscription to be polled
452 * @returns TRUE if adding the subscription succeeded, FALSE otherwise
453 */
454 static gboolean
gam_poll_dnotify_add_subscription(GamSubscription * sub)455 gam_poll_dnotify_add_subscription(GamSubscription * sub)
456 {
457 const char *path = gam_subscription_get_path (sub);
458 GamNode *node = gam_tree_get_at_path (gam_poll_generic_get_tree(), path);
459 int node_is_dir = FALSE;
460
461 gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
462
463 gam_poll_generic_update_time ();
464
465 if (!node)
466 {
467 node = gam_tree_add_at_path(gam_poll_generic_get_tree(), path, gam_subscription_is_dir(sub));
468 }
469
470 if (node_add_subscription(node, sub) < 0)
471 {
472 gam_error(DEBUG_INFO, "Failed to add subscription for: %s\n", path);
473 return FALSE;
474 }
475
476 node_is_dir = gam_node_is_dir(node);
477 if (node_is_dir)
478 {
479 gam_poll_generic_first_scan_dir(sub, node, path);
480 } else {
481 GaminEventType event;
482
483 event = gam_poll_dnotify_poll_file (node);
484 GAM_DEBUG(DEBUG_INFO, "New file subscription: %s event %d\n", path, event);
485
486 if ((event == 0) || (event == GAMIN_EVENT_EXISTS) ||
487 (event == GAMIN_EVENT_CHANGED) ||
488 (event == GAMIN_EVENT_CREATED))
489 {
490 if (gam_subscription_is_dir(sub)) {
491 /* we are watching a file but requested a directory */
492 gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_DELETED, sub, 0);
493 } else {
494 gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_EXISTS, sub, 0);
495 }
496 } else if (event != 0) {
497 gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_DELETED, sub, 0);
498 }
499
500 gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_ENDEXISTS, sub, 0);
501 }
502 if (gam_node_has_pflag (node, MON_MISSING) || gam_node_has_pflag (node, MON_NOKERNEL))
503 gam_poll_generic_add_missing(node);
504
505 if (!node_is_dir) {
506 char *parent;
507 parent = g_path_get_dirname(path);
508 node = gam_tree_get_at_path(gam_poll_generic_get_tree(), parent);
509 if (!node)
510 {
511 node = gam_tree_add_at_path(gam_poll_generic_get_tree(), parent, gam_subscription_is_dir (sub));
512 }
513 g_free(parent);
514 }
515
516 gam_poll_generic_add (node);
517
518 GAM_DEBUG(DEBUG_INFO, "Poll: added subscription\n");
519 return TRUE;
520 }
521
522 /**
523 * gam_poll_remove_subscription_real:
524 * @sub: a subscription
525 *
526 * Implements the removal of a subscription, including
527 * trimming the tree and deactivating the kernel back-end if needed.
528 */
529 static void
gam_poll_dnotify_remove_subscription_real(GamSubscription * sub)530 gam_poll_dnotify_remove_subscription_real(GamSubscription * sub)
531 {
532 GamNode *node;
533
534 node = gam_tree_get_at_path(gam_poll_generic_get_tree(), gam_subscription_get_path(sub));
535
536 if (node != NULL) {
537 if (!gam_node_is_dir(node)) {
538 GAM_DEBUG(DEBUG_INFO, "Removing node sub: %s\n",
539 gam_subscription_get_path(sub));
540 node_remove_subscription(node, sub);
541
542 if (!gam_node_get_subscriptions(node)) {
543 GamNode *parent;
544
545 gam_poll_generic_unregister_node (node);
546 if (gam_tree_has_children(gam_poll_generic_get_tree(), node)) {
547 fprintf(stderr,
548 "node %s is not dir but has children\n",
549 gam_node_get_path(node));
550 } else {
551 parent = gam_node_parent(node);
552 if ((parent != NULL) &&
553 (!gam_node_has_dir_subscriptions(parent))) {
554 gam_tree_remove(gam_poll_generic_get_tree(), node);
555
556 gam_poll_generic_prune_tree(parent);
557 }
558 }
559 }
560 } else {
561 GAM_DEBUG(DEBUG_INFO, "Removing directory sub: %s\n",
562 gam_subscription_get_path(sub));
563 if (node_remove_directory_subscription(node, sub)) {
564 GamNode *parent;
565
566 gam_poll_generic_unregister_node (node);
567 parent = gam_node_parent(node);
568 if (!gam_tree_has_children(gam_poll_generic_get_tree(), node)) {
569 gam_tree_remove(gam_poll_generic_get_tree(), node);
570 }
571
572 gam_poll_generic_prune_tree(parent);
573 }
574 }
575 }
576
577 gam_subscription_free(sub);
578 }
579
580 /**
581 * Removes a subscription which was being polled.
582 *
583 * @param sub a #GamSubscription to remove
584 * @returns TRUE if removing the subscription succeeded, FALSE otherwise
585 */
586 static gboolean
gam_poll_dnotify_remove_subscription(GamSubscription * sub)587 gam_poll_dnotify_remove_subscription(GamSubscription * sub)
588 {
589 GamNode *node;
590
591 node = gam_tree_get_at_path(gam_poll_generic_get_tree(), gam_subscription_get_path(sub));
592 if (node == NULL) {
593 /* free directly */
594 gam_subscription_free(sub);
595 return TRUE;
596 }
597
598 gam_subscription_cancel(sub);
599
600 GAM_DEBUG(DEBUG_INFO, "Tree has %d nodes\n", gam_tree_get_size(gam_poll_generic_get_tree()));
601 gam_poll_dnotify_remove_subscription_real(sub);
602 GAM_DEBUG(DEBUG_INFO, "Tree has %d nodes\n", gam_tree_get_size(gam_poll_generic_get_tree()));
603
604 GAM_DEBUG(DEBUG_INFO, "Poll: removed subscription\n");
605 return TRUE;
606 }
607
608 /**
609 * Stop polling all subscriptions for a given #GamListener.
610 *
611 * @param listener a #GamListener
612 * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
613 */
614 static gboolean
gam_poll_dnotify_remove_all_for(GamListener * listener)615 gam_poll_dnotify_remove_all_for(GamListener * listener)
616 {
617 GList *subs, *l = NULL;
618
619 subs = gam_listener_get_subscriptions(listener);
620
621 for (l = subs; l; l = l->next) {
622 GamSubscription *sub = l->data;
623
624 g_assert(sub != NULL);
625
626 gam_poll_remove_subscription(sub);
627 }
628
629 if (subs) {
630 g_list_free(subs);
631 return TRUE;
632 } else
633 return FALSE;
634 }
635
636 static gboolean
gam_poll_dnotify_scan_callback(gpointer data)637 gam_poll_dnotify_scan_callback(gpointer data)
638 {
639 int idx;
640
641 #ifdef VERBOSE_POLL
642 GAM_DEBUG(DEBUG_INFO, "gam_poll_scan_callback(): %d missing, %d busy\n", g_list_length(gam_poll_generic_get_missing_list()), g_list_length(gam_poll_generic_get_busy_list()));
643 #endif
644
645 gam_poll_generic_update_time ();
646
647
648 /*
649 * do not simply walk the list as it may be modified in the callback
650 */
651 for (idx = 0;; idx++)
652 {
653 GamNode *node = g_list_nth_data(gam_poll_generic_get_missing_list(), idx);
654
655 if (node == NULL)
656 break;
657
658 g_assert (node);
659
660 #ifdef VERBOSE_POLL
661 GAM_DEBUG(DEBUG_INFO, "Checking missing file %s", node->path);
662 #endif
663 if (node->is_dir) {
664 gam_poll_generic_scan_directory_internal(node);
665 } else {
666 GaminEventType event = gam_poll_dnotify_poll_file (node);
667 gam_node_emit_event(node, event);
668 }
669
670 /*
671 * if the resource exists again and is not in a special monitoring
672 * mode then switch back to dnotify for monitoring.
673 */
674 if (!gam_node_has_pflags (node, MON_ALL_PFLAGS) &&
675 !gam_exclude_check(node->path) &&
676 gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL)
677 {
678 gam_poll_generic_remove_missing(node);
679 if (gam_node_get_subscriptions(node) != NULL) {
680 gam_poll_dnotify_relist_node(node);
681 }
682 }
683 }
684
685 for (idx = 0;; idx++)
686 {
687 GamNode *node = (GamNode *) g_list_nth_data(gam_poll_generic_get_busy_list(), idx);
688 /*
689 * do not simply walk the list as it may be modified in the callback
690 */
691 if (node == NULL)
692 break;
693
694 g_assert (node);
695 #ifdef VERBOSE_POLL
696 GAM_DEBUG(DEBUG_INFO, "Checking busy file %s", node->path);
697 #endif
698 if (node->is_dir) {
699 gam_poll_generic_scan_directory_internal(node);
700 } else {
701 GaminEventType event = gam_poll_dnotify_poll_file (node);
702 gam_node_emit_event(node, event);
703 }
704
705 /*
706 * if the resource exists again and is not in a special monitoring
707 * mode then switch back to dnotify for monitoring.
708 */
709 if (!gam_node_has_pflags (node, MON_ALL_PFLAGS) &&
710 !gam_exclude_check(node->path) &&
711 gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL)
712 {
713 gam_poll_generic_remove_busy(node);
714 if (gam_node_get_subscriptions(node) != NULL) {
715 gam_poll_dnotify_flowoff_node(node);
716 }
717 }
718 }
719 return TRUE;
720 }
721