~ubuntu-branches/debian/jessie/gamin/jessie

« back to all changes in this revision

Viewing changes to server/gam_poll_generic.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Banck
  • Date: 2007-03-23 14:43:49 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070323144349-1inpdk22uaneks9h
Tags: 0.1.8-2
* debian/control: Improve long description. (Closes: #405347)
* debian/patches/14_nfs-fix.patch: Fix gam_server startup for Thunar.
  Thanks to Maximiliano Curia. (Closes: #403247)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <string.h>
 
28
#include <glib.h>
 
29
#include "fam.h"
 
30
#include "gam_error.h"
 
31
#include "gam_tree.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"
 
38
 
 
39
//#define VERBOSE_POLL
 
40
//#define VERBOSE_POLL2
 
41
 
 
42
#define DEFAULT_POLL_TIMEOUT 1
 
43
 
 
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;
 
50
 
 
51
gboolean
 
52
gam_poll_generic_init()
 
53
{
 
54
        tree = gam_tree_new ();
 
55
        gam_poll_generic_update_time ();
 
56
        return TRUE;
 
57
}
 
58
 
 
59
static void
 
60
gam_poll_debug_node(GamNode * node, gpointer user_data)
 
61
{
 
62
    if (node == NULL)
 
63
        return;
 
64
 
 
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);
 
66
}
 
67
 
 
68
void
 
69
gam_poll_generic_debug(void)
 
70
{
 
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);
 
74
    } else {
 
75
        GAM_DEBUG(DEBUG_INFO, "No poll missing resources\n");
 
76
    }
 
77
 
 
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);
 
81
    } else {
 
82
        GAM_DEBUG(DEBUG_INFO, "No poll busy resources\n");
 
83
    }
 
84
 
 
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);
 
88
    } else {
 
89
        GAM_DEBUG(DEBUG_INFO, "No poll all resources\n");
 
90
    }
 
91
}
 
92
 
 
93
/**
 
94
 * gam_poll_generic_add_missing:
 
95
 * @node: a missing node
 
96
 *
 
97
 * Add a missing node to the list for polling its creation.
 
98
 */
 
99
void
 
100
gam_poll_generic_add_missing(GamNode * node)
 
101
{
 
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));
 
105
        }
 
106
}
 
107
 
 
108
 
 
109
 
 
110
/**
 
111
 * gam_poll_generic_remove_missing:
 
112
 * @node: a missing node
 
113
 *
 
114
 * Remove a missing node from the list.
 
115
 */
 
116
void
 
117
gam_poll_generic_remove_missing(GamNode * node)
 
118
{
 
119
        if (g_list_find (missing_resources, node))
 
120
        {
 
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);
 
123
        }
 
124
}
 
125
 
 
126
/**
 
127
 * gam_poll_generic_add_busy:
 
128
 * @node: a busy node
 
129
 *
 
130
 * Add a busy node to the list for polling its creation.
 
131
 */
 
132
void
 
133
gam_poll_generic_add_busy(GamNode * node)
 
134
{
 
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));
 
138
        }
 
139
}
 
140
 
 
141
/**
 
142
 * gam_poll_generic_remove_busy:
 
143
 * @node: a busy node
 
144
 *
 
145
 * Remove a busy node from the list.
 
146
 */
 
147
void
 
148
gam_poll_generic_remove_busy(GamNode * node)
 
149
{
 
150
        if (!g_list_find (busy_resources, node))
 
151
                return;
 
152
 
 
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);
 
155
}
 
156
 
 
157
void
 
158
gam_poll_generic_add (GamNode * node)
 
159
{
 
160
        if (g_list_find (all_resources, node) == NULL)
 
161
        {
 
162
                all_resources = g_list_prepend(all_resources, node);
 
163
                GAM_DEBUG(DEBUG_INFO, "Poll: Adding node %s\n", gam_node_get_path (node));
 
164
        }
 
165
}
 
166
 
 
167
void
 
168
gam_poll_generic_remove (GamNode * node)
 
169
{
 
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);
 
173
}
 
174
 
 
175
time_t
 
176
gam_poll_generic_get_time()
 
177
{
 
178
        return current_time;
 
179
}
 
180
 
 
181
void
 
182
gam_poll_generic_update_time()
 
183
{
 
184
        current_time = time (NULL);
 
185
}
 
186
 
 
187
time_t
 
188
gam_poll_generic_get_delta_time(time_t pt)
 
189
{
 
190
        if (current_time >= pt)
 
191
                return current_time - pt;
 
192
        /* FIXME: We have wrapped */
 
193
        return 0;
 
194
}
 
195
 
 
196
static void
 
197
gam_poll_generic_trigger_file_handler (const char *path, pollHandlerMode mode, GamNode *node)
 
198
{
 
199
    if (node->mon_type != GFS_MT_KERNEL)
 
200
        return;
 
201
 
 
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);
 
205
                } else {
 
206
                        const char *dir = NULL;
 
207
                        GamNode *parent = gam_node_parent(node);
 
208
 
 
209
                        if (!parent)
 
210
                                return;
 
211
 
 
212
                        dir = parent->path;
 
213
                        switch (mode) {
 
214
                        case GAMIN_ACTIVATE:
 
215
                                GAM_DEBUG(DEBUG_INFO, "poll: Activating kernel monitoring on %s\n", dir);
 
216
                                gam_kernel_dir_handler (dir, mode);
 
217
                        break;
 
218
                        case GAMIN_DEACTIVATE:
 
219
                                GAM_DEBUG(DEBUG_INFO, "poll: Deactivating kernel monitoring on %s\n", dir);
 
220
                                gam_kernel_dir_handler (dir, mode);
 
221
                        break;
 
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);
 
228
                                }
 
229
                        break;
 
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);
 
236
                                }
 
237
                        break;
 
238
                        }
 
239
                }
 
240
        } else {
 
241
                gam_kernel_file_handler (path, mode);
 
242
        }
 
243
}
 
244
 
 
245
 
 
246
static void
 
247
gam_poll_generic_trigger_dir_handler (const char *path, pollHandlerMode mode, GamNode *node)
 
248
{
 
249
        if (node->mon_type != GFS_MT_KERNEL)
 
250
                return;
 
251
 
 
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);
 
255
                } else {
 
256
                        gam_poll_generic_trigger_file_handler(path, mode, node);
 
257
                }
 
258
        } else {
 
259
                gam_kernel_dir_handler (path, mode);
 
260
        }
 
261
}
 
262
 
 
263
 
 
264
void
 
265
gam_poll_generic_trigger_handler(const char *path, pollHandlerMode mode, GamNode *node)
 
266
{
 
267
        if (gam_node_is_dir(node))
 
268
                gam_poll_generic_trigger_dir_handler(node->path, mode, node);
 
269
        else
 
270
                gam_poll_generic_trigger_file_handler(node->path, mode, node);
 
271
}
 
272
 
 
273
void
 
274
gam_poll_generic_scan_directory_internal (GamNode *dir_node)
 
275
{
 
276
        GDir *dir = NULL;
 
277
        const char *name = NULL, *dpath = NULL;
 
278
        char *path = NULL;
 
279
        GamNode *node = NULL;
 
280
        GaminEventType event = 0, fevent;
 
281
        GList *children = NULL, *l = NULL;
 
282
        unsigned int exists = 0;
 
283
        int is_dir_node;
 
284
 
 
285
        if (dir_node == NULL)
 
286
                return;
 
287
 
 
288
        dpath = gam_node_get_path(dir_node);
 
289
 
 
290
        if (dpath == NULL)
 
291
                return;
 
292
 
 
293
        if (!gam_node_get_subscriptions(dir_node))
 
294
                goto scan_files;
 
295
 
 
296
        if (dir_node->lasttime && gam_poll_generic_get_delta_time (dir_node->lasttime) < dir_node->poll_time)
 
297
                return;
 
298
 
 
299
        GAM_DEBUG(DEBUG_INFO, "poll-generic: scanning directory %s\n", dpath);
 
300
 
 
301
        event = gam_poll_file(dir_node);
 
302
 
 
303
        if (event != 0)
 
304
                gam_node_emit_event (dir_node, event);
 
305
 
 
306
        dir = g_dir_open(dpath, 0, NULL);
 
307
 
 
308
        if (dir == NULL) {
 
309
#ifdef VERBOSE_POLL
 
310
                GAM_DEBUG(DEBUG_INFO, "Poll: directory %s is not readable or missing\n", dpath);
 
311
#endif
 
312
                return;
 
313
        }
 
314
 
 
315
        exists = 1;
 
316
 
 
317
#ifdef VERBOSE_POLL
 
318
        GAM_DEBUG(DEBUG_INFO, "Poll: scanning directory %s\n", dpath);
 
319
#endif
 
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);
 
324
 
 
325
                if (!node) {
 
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);
 
330
                        } else {
 
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);
 
334
                        }
 
335
                }
 
336
 
 
337
                g_free(path);
 
338
        }
 
339
 
 
340
        g_dir_close(dir);
 
341
 
 
342
scan_files:
 
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);
 
348
 
 
349
                fevent = gam_poll_file(node);
 
350
 
 
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);
 
355
                        } else {
 
356
                                gam_node_unset_flag(node, FLAG_NEW_NODE);
 
357
                                fevent = GAMIN_EVENT_CREATED;
 
358
                        }
 
359
                }
 
360
 
 
361
                if (fevent != 0) {
 
362
                        gam_node_emit_event (node, fevent);
 
363
                } else {
 
364
                        /* just send the EXIST events if the node exists */
 
365
 
 
366
                        if (!gam_node_has_pflag (node, MON_MISSING))
 
367
                        {
 
368
                                gam_server_emit_event(gam_node_get_path(node),
 
369
                                gam_node_is_dir(node),
 
370
                                GAMIN_EVENT_EXISTS, NULL, 0);
 
371
                        }
 
372
                }
 
373
        }
 
374
 
 
375
        g_list_free(children);
 
376
}
 
377
 
 
378
/**
 
379
 * Scans a directory for changes, and emits events if needed.
 
380
 *
 
381
 * @param path the path to the directory to be scanned
 
382
 */
 
383
void
 
384
gam_poll_generic_scan_directory(const char *path)
 
385
{
 
386
        GamNode *node;
 
387
 
 
388
        gam_poll_generic_update_time ();
 
389
 
 
390
        node = gam_tree_get_at_path(tree, path);
 
391
        if (node == NULL)
 
392
                node = gam_tree_add_at_path(tree, path, TRUE);
 
393
 
 
394
        if (node == NULL) {
 
395
                gam_error(DEBUG_INFO, "gam_tree_add_at_path(%s) returned NULL\n", path);
 
396
                return;
 
397
        }
 
398
 
 
399
        gam_poll_generic_scan_directory_internal(node);
 
400
}
 
401
 
 
402
/**
 
403
 * First dir scanning on a new subscription, generates the Exists EndExists
 
404
 * events.
 
405
 */
 
406
void
 
407
gam_poll_generic_first_scan_dir (GamSubscription * sub, GamNode * dir_node, const char *dpath)
 
408
{
 
409
        GDir *dir;
 
410
        char *path;
 
411
        GList *subs;
 
412
        int with_exists = 1;
 
413
        const char *name;
 
414
        GamNode *node;
 
415
 
 
416
        GAM_DEBUG(DEBUG_INFO, "Looking for existing files in: %s\n", dpath);
 
417
 
 
418
        if (gam_subscription_has_option(sub, GAM_OPT_NOEXISTS))
 
419
        {
 
420
                with_exists = 0;
 
421
                GAM_DEBUG(DEBUG_INFO, "   Exists not wanted\n");
 
422
        }
 
423
 
 
424
        subs = g_list_prepend(NULL, sub);
 
425
 
 
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);
 
428
 
 
429
                gam_server_emit_event(dpath, 1, GAMIN_EVENT_DELETED, subs, 1);
 
430
 
 
431
                stat(dir_node->path, &(dir_node->sbuf));
 
432
                dir_node->lasttime = gam_poll_generic_get_time ();
 
433
 
 
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;
 
437
                } else {
 
438
                        gam_node_set_pflags (dir_node, MON_MISSING);
 
439
                        gam_poll_generic_add_missing(dir_node);
 
440
                }
 
441
                goto done;
 
442
        }
 
443
 
 
444
        if (dir_node->lasttime == 0)
 
445
                gam_poll_file(dir_node);
 
446
 
 
447
        if (with_exists)
 
448
                gam_server_emit_event(dpath, 1, GAMIN_EVENT_EXISTS, subs, 1);
 
449
 
 
450
 
 
451
        dir = g_dir_open(dpath, 0, NULL);
 
452
 
 
453
        if (dir == NULL) {
 
454
                goto done;
 
455
        }
 
456
 
 
457
        while ((name = g_dir_read_name(dir)) != NULL)
 
458
        {
 
459
                path = g_build_filename(dpath, name, NULL);
 
460
 
 
461
                node = gam_tree_get_at_path(tree, path);
 
462
 
 
463
                if (!node)
 
464
                {
 
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);
 
468
                        } else {
 
469
                                node = gam_node_new(path, NULL, TRUE);
 
470
                        }
 
471
                        stat(node->path, &(node->sbuf));
 
472
                        gam_node_set_is_dir(node, (S_ISDIR(node->sbuf.st_mode) != 0));
 
473
 
 
474
                        if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
 
475
                                gam_node_set_pflag (node, MON_NOKERNEL);
 
476
 
 
477
                        node->lasttime = gam_poll_generic_get_time ();
 
478
                        gam_tree_add(tree, dir_node, node);
 
479
                }
 
480
 
 
481
                if (with_exists)
 
482
                        gam_server_emit_event(name, 1, GAMIN_EVENT_EXISTS, subs, 1);
 
483
 
 
484
                g_free(path);
 
485
        }
 
486
 
 
487
        g_dir_close(dir);
 
488
 
 
489
done:
 
490
        if (with_exists)
 
491
                gam_server_emit_event(dpath, 1, GAMIN_EVENT_ENDEXISTS, subs, 1);
 
492
 
 
493
        g_list_free(subs);
 
494
 
 
495
        GAM_DEBUG(DEBUG_INFO, "Done scanning %s\n", dpath);
 
496
}
 
497
 
 
498
GamTree *
 
499
gam_poll_generic_get_tree()
 
500
{
 
501
        return tree;
 
502
}
 
503
 
 
504
GList *
 
505
gam_poll_generic_get_missing_list (void)
 
506
{
 
507
        return missing_resources;
 
508
}
 
509
 
 
510
GList *
 
511
gam_poll_generic_get_busy_list (void)
 
512
{
 
513
        return busy_resources;
 
514
}
 
515
 
 
516
GList *
 
517
gam_poll_generic_get_all_list (void)
 
518
{
 
519
        return all_resources;
 
520
}
 
521
 
 
522
GList *
 
523
gam_poll_generic_get_dead_list (void)
 
524
{
 
525
        return dead_resources;
 
526
}
 
527
 
 
528
void
 
529
gam_poll_generic_unregister_node (GamNode * node)
 
530
{
 
531
        if (missing_resources != NULL) {
 
532
                gam_poll_generic_remove_missing(node);
 
533
        }
 
534
 
 
535
        if (busy_resources != NULL) {
 
536
                gam_poll_generic_remove_busy(node);
 
537
        }
 
538
 
 
539
        if (all_resources != NULL) {
 
540
                all_resources = g_list_remove(all_resources, node);
 
541
        }
 
542
}
 
543
 
 
544
void
 
545
gam_poll_generic_prune_tree(GamNode * node)
 
546
{
 
547
        /* don't prune the root */
 
548
        if (gam_node_parent(node) == NULL)
 
549
                return;
 
550
 
 
551
        if (!gam_tree_has_children(tree, node) && !gam_node_get_subscriptions(node)) 
 
552
        {
 
553
                GamNode *parent;
 
554
 
 
555
                GAM_DEBUG(DEBUG_INFO, "prune_tree: %s\n", gam_node_get_path(node));
 
556
 
 
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);
 
561
        }
 
562
}
 
563