2
* Copyright (C) 2003 James Willcox, Corey Bowers
3
* Copyright (C) 2004 Daniel Veillard
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.
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.
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.
20
#include "server_config.h"
23
#include <sys/types.h>
30
#include "gam_error.h"
32
#include "gam_poll_generic.h"
33
#include "gam_event.h"
34
#include "gam_server.h"
35
#include "gam_protocol.h"
36
#include "gam_event.h"
37
#include "gam_excludes.h"
39
//#define VERBOSE_POLL
40
//#define VERBOSE_POLL2
42
#define DEFAULT_POLL_TIMEOUT 1
44
static GamTree * tree = NULL;
45
static GList * missing_resources = NULL;
46
static GList * busy_resources = NULL;
47
static GList * all_resources = NULL;
48
static GList * dead_resources = NULL;
49
static time_t current_time = 0;
52
gam_poll_generic_init()
54
tree = gam_tree_new ();
55
gam_poll_generic_update_time ();
60
gam_poll_debug_node(GamNode * node, gpointer user_data)
65
GAM_DEBUG(DEBUG_INFO, "dir %d flags %d pflags %d nb subs %d : %s\n", node->is_dir, node->flags, node->pflags, g_list_length(node->subs), node->path);
69
gam_poll_generic_debug(void)
71
if (missing_resources != NULL) {
72
GAM_DEBUG(DEBUG_INFO, "Dumping poll missing resources\n");
73
g_list_foreach(missing_resources, (GFunc) gam_poll_debug_node, NULL);
75
GAM_DEBUG(DEBUG_INFO, "No poll missing resources\n");
78
if (busy_resources != NULL) {
79
GAM_DEBUG(DEBUG_INFO, "Dumping poll busy resources\n");
80
g_list_foreach(busy_resources, (GFunc) gam_poll_debug_node, NULL);
82
GAM_DEBUG(DEBUG_INFO, "No poll busy resources\n");
85
if (all_resources != NULL) {
86
GAM_DEBUG(DEBUG_INFO, "Dumping poll all resources\n");
87
g_list_foreach(all_resources, (GFunc) gam_poll_debug_node, NULL);
89
GAM_DEBUG(DEBUG_INFO, "No poll all resources\n");
94
* gam_poll_generic_add_missing:
95
* @node: a missing node
97
* Add a missing node to the list for polling its creation.
100
gam_poll_generic_add_missing(GamNode * node)
102
if (g_list_find(missing_resources, node) == NULL) {
103
missing_resources = g_list_prepend(missing_resources, node);
104
GAM_DEBUG(DEBUG_INFO, "Poll: adding missing node %s\n", gam_node_get_path(node));
111
* gam_poll_generic_remove_missing:
112
* @node: a missing node
114
* Remove a missing node from the list.
117
gam_poll_generic_remove_missing(GamNode * node)
119
if (g_list_find (missing_resources, node))
121
GAM_DEBUG(DEBUG_INFO, "Poll: removing missing node %s\n", gam_node_get_path(node));
122
missing_resources = g_list_remove_all(missing_resources, node);
127
* gam_poll_generic_add_busy:
130
* Add a busy node to the list for polling its creation.
133
gam_poll_generic_add_busy(GamNode * node)
135
if (g_list_find(busy_resources, node) == NULL) {
136
busy_resources = g_list_prepend(busy_resources, node);
137
GAM_DEBUG(DEBUG_INFO, "Poll: adding busy node %s\n", gam_node_get_path(node));
142
* gam_poll_generic_remove_busy:
145
* Remove a busy node from the list.
148
gam_poll_generic_remove_busy(GamNode * node)
150
if (!g_list_find (busy_resources, node))
153
GAM_DEBUG(DEBUG_INFO, "Poll: removing busy node %s\n", gam_node_get_path(node));
154
busy_resources = g_list_remove_all(busy_resources, node);
158
gam_poll_generic_add (GamNode * node)
160
if (g_list_find (all_resources, node) == NULL)
162
all_resources = g_list_prepend(all_resources, node);
163
GAM_DEBUG(DEBUG_INFO, "Poll: Adding node %s\n", gam_node_get_path (node));
168
gam_poll_generic_remove (GamNode * node)
170
g_assert (g_list_find (all_resources, node));
171
GAM_DEBUG(DEBUG_INFO, "Poll: removing node %s\n", gam_node_get_path(node));
172
all_resources = g_list_remove_all(all_resources, node);
176
gam_poll_generic_get_time()
182
gam_poll_generic_update_time()
184
current_time = time (NULL);
188
gam_poll_generic_get_delta_time(time_t pt)
190
if (current_time >= pt)
191
return current_time - pt;
192
/* FIXME: We have wrapped */
197
gam_poll_generic_trigger_file_handler (const char *path, pollHandlerMode mode, GamNode *node)
199
if (node->mon_type != GFS_MT_KERNEL)
202
if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
203
if (gam_node_is_dir(node)) {
204
gam_kernel_file_handler (path, mode);
206
const char *dir = NULL;
207
GamNode *parent = gam_node_parent(node);
215
GAM_DEBUG(DEBUG_INFO, "poll: Activating kernel monitoring on %s\n", dir);
216
gam_kernel_dir_handler (dir, mode);
218
case GAMIN_DEACTIVATE:
219
GAM_DEBUG(DEBUG_INFO, "poll: Deactivating kernel monitoring on %s\n", dir);
220
gam_kernel_dir_handler (dir, mode);
222
case GAMIN_FLOWCONTROLSTART:
223
if (!gam_node_has_pflag (parent, MON_BUSY)) {
224
GAM_DEBUG(DEBUG_INFO, "poll: marking busy on %s\n", dir);
225
gam_kernel_dir_handler (dir, mode);
226
gam_poll_generic_add_busy(parent);
227
gam_node_set_pflag (parent, MON_BUSY);
230
case GAMIN_FLOWCONTROLSTOP:
231
if (gam_node_has_pflag (parent, MON_BUSY)) {
232
GAM_DEBUG(DEBUG_INFO, "poll: unmarking busy on %s\n", dir);
233
gam_kernel_dir_handler (dir, mode);
234
gam_poll_generic_remove_busy(parent);
235
gam_node_unset_pflag (parent, MON_BUSY);
241
gam_kernel_file_handler (path, mode);
247
gam_poll_generic_trigger_dir_handler (const char *path, pollHandlerMode mode, GamNode *node)
249
if (node->mon_type != GFS_MT_KERNEL)
252
if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
253
if (gam_node_is_dir(node)) {
254
gam_kernel_dir_handler (path, mode);
256
gam_poll_generic_trigger_file_handler(path, mode, node);
259
gam_kernel_dir_handler (path, mode);
265
gam_poll_generic_trigger_handler(const char *path, pollHandlerMode mode, GamNode *node)
267
if (gam_node_is_dir(node))
268
gam_poll_generic_trigger_dir_handler(node->path, mode, node);
270
gam_poll_generic_trigger_file_handler(node->path, mode, node);
274
gam_poll_generic_scan_directory_internal (GamNode *dir_node)
277
const char *name = NULL, *dpath = NULL;
279
GamNode *node = NULL;
280
GaminEventType event = 0, fevent;
281
GList *children = NULL, *l = NULL;
282
unsigned int exists = 0;
285
if (dir_node == NULL)
288
dpath = gam_node_get_path(dir_node);
293
if (!gam_node_get_subscriptions(dir_node))
296
if (dir_node->lasttime && gam_poll_generic_get_delta_time (dir_node->lasttime) < dir_node->poll_time)
299
GAM_DEBUG(DEBUG_INFO, "poll-generic: scanning directory %s\n", dpath);
301
event = gam_poll_file(dir_node);
304
gam_node_emit_event (dir_node, event);
306
dir = g_dir_open(dpath, 0, NULL);
310
GAM_DEBUG(DEBUG_INFO, "Poll: directory %s is not readable or missing\n", dpath);
318
GAM_DEBUG(DEBUG_INFO, "Poll: scanning directory %s\n", dpath);
320
while ((name = g_dir_read_name(dir)) != NULL) {
321
path = g_build_filename(gam_node_get_path(dir_node), name, NULL);
322
node = gam_tree_get_at_path(tree, path);
323
GAM_DEBUG(DEBUG_INFO, "poll-generic: scan dir - checking %s\n", dpath);
326
if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
327
node = gam_node_new(path, NULL, FALSE);
328
gam_tree_add(tree, dir_node, node);
329
gam_node_set_flag(node, FLAG_NEW_NODE);
331
node = gam_node_new(path, NULL, TRUE);
332
gam_tree_add(tree, dir_node, node);
333
gam_node_set_flag(node, FLAG_NEW_NODE);
343
/* FIXME: Shouldn't is_dir_node be assigned inside the loop? */
344
children = gam_tree_get_children(tree, dir_node);
345
for (l = children; l; l = l->next) {
346
node = (GamNode *) l->data;
347
is_dir_node = gam_node_is_dir(dir_node);
349
fevent = gam_poll_file(node);
351
if (gam_node_has_flag(node, FLAG_NEW_NODE)) {
352
if (is_dir_node && gam_node_get_subscriptions(node)) {
353
gam_node_unset_flag(node, FLAG_NEW_NODE);
354
gam_poll_generic_scan_directory_internal(node);
356
gam_node_unset_flag(node, FLAG_NEW_NODE);
357
fevent = GAMIN_EVENT_CREATED;
362
gam_node_emit_event (node, fevent);
364
/* just send the EXIST events if the node exists */
366
if (!gam_node_has_pflag (node, MON_MISSING))
368
gam_server_emit_event(gam_node_get_path(node),
369
gam_node_is_dir(node),
370
GAMIN_EVENT_EXISTS, NULL, 0);
375
g_list_free(children);
379
* Scans a directory for changes, and emits events if needed.
381
* @param path the path to the directory to be scanned
384
gam_poll_generic_scan_directory(const char *path)
388
gam_poll_generic_update_time ();
390
node = gam_tree_get_at_path(tree, path);
392
node = gam_tree_add_at_path(tree, path, TRUE);
395
gam_error(DEBUG_INFO, "gam_tree_add_at_path(%s) returned NULL\n", path);
399
gam_poll_generic_scan_directory_internal(node);
403
* First dir scanning on a new subscription, generates the Exists EndExists
407
gam_poll_generic_first_scan_dir (GamSubscription * sub, GamNode * dir_node, const char *dpath)
416
GAM_DEBUG(DEBUG_INFO, "Looking for existing files in: %s\n", dpath);
418
if (gam_subscription_has_option(sub, GAM_OPT_NOEXISTS))
421
GAM_DEBUG(DEBUG_INFO, " Exists not wanted\n");
424
subs = g_list_prepend(NULL, sub);
426
if (!g_file_test(dpath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
427
GAM_DEBUG(DEBUG_INFO, "Monitoring missing dir: %s\n", dpath);
429
gam_server_emit_event(dpath, 1, GAMIN_EVENT_DELETED, subs, 1);
431
stat(dir_node->path, &(dir_node->sbuf));
432
dir_node->lasttime = gam_poll_generic_get_time ();
434
if (g_file_test(dpath, G_FILE_TEST_EXISTS)) {
435
gam_node_set_pflags (dir_node, MON_WRONG_TYPE);
436
dir_node->is_dir = 0;
438
gam_node_set_pflags (dir_node, MON_MISSING);
439
gam_poll_generic_add_missing(dir_node);
444
if (dir_node->lasttime == 0)
445
gam_poll_file(dir_node);
448
gam_server_emit_event(dpath, 1, GAMIN_EVENT_EXISTS, subs, 1);
451
dir = g_dir_open(dpath, 0, NULL);
457
while ((name = g_dir_read_name(dir)) != NULL)
459
path = g_build_filename(dpath, name, NULL);
461
node = gam_tree_get_at_path(tree, path);
465
GAM_DEBUG(DEBUG_INFO, "Unregistered node %s\n", path);
466
if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
467
node = gam_node_new(path, NULL, FALSE);
469
node = gam_node_new(path, NULL, TRUE);
471
stat(node->path, &(node->sbuf));
472
gam_node_set_is_dir(node, (S_ISDIR(node->sbuf.st_mode) != 0));
474
if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
475
gam_node_set_pflag (node, MON_NOKERNEL);
477
node->lasttime = gam_poll_generic_get_time ();
478
gam_tree_add(tree, dir_node, node);
482
gam_server_emit_event(name, 1, GAMIN_EVENT_EXISTS, subs, 1);
491
gam_server_emit_event(dpath, 1, GAMIN_EVENT_ENDEXISTS, subs, 1);
495
GAM_DEBUG(DEBUG_INFO, "Done scanning %s\n", dpath);
499
gam_poll_generic_get_tree()
505
gam_poll_generic_get_missing_list (void)
507
return missing_resources;
511
gam_poll_generic_get_busy_list (void)
513
return busy_resources;
517
gam_poll_generic_get_all_list (void)
519
return all_resources;
523
gam_poll_generic_get_dead_list (void)
525
return dead_resources;
529
gam_poll_generic_unregister_node (GamNode * node)
531
if (missing_resources != NULL) {
532
gam_poll_generic_remove_missing(node);
535
if (busy_resources != NULL) {
536
gam_poll_generic_remove_busy(node);
539
if (all_resources != NULL) {
540
all_resources = g_list_remove(all_resources, node);
545
gam_poll_generic_prune_tree(GamNode * node)
547
/* don't prune the root */
548
if (gam_node_parent(node) == NULL)
551
if (!gam_tree_has_children(tree, node) && !gam_node_get_subscriptions(node))
555
GAM_DEBUG(DEBUG_INFO, "prune_tree: %s\n", gam_node_get_path(node));
557
parent = gam_node_parent(node);
558
gam_poll_generic_unregister_node(node);
559
gam_tree_remove(tree, node);
560
gam_poll_generic_prune_tree(parent);