• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Sun Microsystems, Inc. All rights reserved.
5  * Use is subject to license terms.
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 <errno.h>
27 #include <strings.h>
28 #include <glib.h>
29 #include "fen-node.h"
30 #include "fen-dump.h"
31 
32 #define	NODE_STAT(n)	(((node_t*)(n))->stat)
33 
34 struct _dnode {
35     gchar* filename;
36     node_op_t* op;
37     GTimeVal tv;
38 };
39 
40 #ifdef GIO_COMPILATION
41 #define FN_W if (fn_debug_enabled) g_warning
42 static gboolean fn_debug_enabled = FALSE;
43 #else
44 #include "gam_error.h"
45 #define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
46 #endif
47 
48 G_LOCK_EXTERN (fen_lock);
49 #define	PROCESS_DELETING_INTERVAL	900 /* in second */
50 
51 static node_t* _head = NULL;
52 static GList *deleting_nodes = NULL;
53 static guint deleting_nodes_id = 0;
54 
55 static node_t* node_new (node_t* parent, const gchar* basename);
56 static void node_delete (node_t* parent);
57 static gboolean remove_node_internal (node_t* node, node_op_t* op);
58 static void children_add (node_t *p, node_t *f);
59 static void children_remove (node_t *p, node_t *f);
60 static guint children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data);
61 static void children_foreach (node_t *f, GHFunc func, gpointer user_data);
62 static gboolean children_remove_cb (gpointer key,
63   gpointer value,
64   gpointer user_data);
65 
66 static struct _dnode*
_dnode_new(const gchar * filename,node_op_t * op)67 _dnode_new (const gchar* filename, node_op_t* op)
68 {
69     struct _dnode* d;
70 
71     g_assert (op);
72     if ((d = g_new (struct _dnode, 1)) != NULL) {
73         d->filename = g_strdup (filename);
74         d->op = g_memdup (op, sizeof (node_op_t));
75         g_assert (d->op);
76         g_get_current_time (&d->tv);
77         g_time_val_add (&d->tv, PROCESS_DELETING_INTERVAL);
78     }
79     return d;
80 }
81 
82 static void
_dnode_free(struct _dnode * d)83 _dnode_free (struct _dnode* d)
84 {
85     g_assert (d);
86     g_free (d->filename);
87     g_free (d->op);
88     g_free (d);
89 }
90 
91 static gboolean
g_timeval_lt(GTimeVal * val1,GTimeVal * val2)92 g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
93 {
94     if (val1->tv_sec < val2->tv_sec)
95         return TRUE;
96 
97     if (val1->tv_sec > val2->tv_sec)
98         return FALSE;
99 
100     /* val1->tv_sec == val2->tv_sec */
101     if (val1->tv_usec < val2->tv_usec)
102         return TRUE;
103 
104     return FALSE;
105 }
106 
107 static gboolean
scan_deleting_nodes(gpointer data)108 scan_deleting_nodes (gpointer data)
109 {
110     struct _dnode* d;
111     GTimeVal tv_now;
112     GList* i;
113     GList* deleted_list = NULL;
114     gboolean ret = TRUE;
115     node_t* node;
116 
117     g_get_current_time (&tv_now);
118 
119     if (G_TRYLOCK (fen_lock)) {
120         for (i = deleting_nodes; i; i = i->next) {
121             d = (struct _dnode*)i->data;
122             /* Time to free, try only once */
123             if (g_timeval_lt (&d->tv, &tv_now)) {
124                 if ((node = find_node (d->filename)) != NULL) {
125                     remove_node_internal (node, d->op);
126                 }
127                 _dnode_free (d);
128                 deleted_list = g_list_prepend (deleted_list, i);
129             }
130         }
131 
132         for (i = deleted_list; i; i = i->next) {
133             deleting_nodes = g_list_remove_link (deleting_nodes,
134               (GList *)i->data);
135             g_list_free_1 ((GList *)i->data);
136         }
137         g_list_free (deleted_list);
138 
139         if (deleting_nodes == NULL) {
140             deleting_nodes_id = 0;
141             ret = FALSE;
142         }
143         G_UNLOCK (fen_lock);
144     }
145     return ret;
146 }
147 
148 gpointer
node_get_data(node_t * node)149 node_get_data (node_t* node)
150 {
151     g_assert (node);
152     return node->user_data;
153 }
154 
155 gpointer
node_set_data(node_t * node,gpointer user_data)156 node_set_data (node_t* node, gpointer user_data)
157 {
158     gpointer data = node->user_data;
159     g_assert (node);
160     node->user_data = user_data;
161     return data;
162 }
163 
164 void
travel_nodes(node_t * node,node_op_t * op)165 travel_nodes (node_t* node, node_op_t* op)
166 {
167     GList* children;
168     GList* i;
169 
170     if (node) {
171         if (op && op->hit) {
172             op->hit (node, op->user_data);
173         }
174     }
175     children = g_hash_table_get_values (node->children);
176     if (children) {
177         for (i = children; i; i = i->next) {
178             travel_nodes (i->data, op);
179         }
180         g_list_free (children);
181     }
182 }
183 
184 static node_t*
find_node_internal(node_t * node,const gchar * filename,node_op_t * op)185 find_node_internal (node_t* node, const gchar* filename, node_op_t* op)
186 {
187     gchar* str;
188     gchar* token;
189     gchar* lasts;
190     node_t* parent;
191     node_t* child;
192 
193     g_assert (filename && filename[0] == '/');
194     g_assert (node);
195 
196     parent = node;
197     str = g_strdup (filename + strlen (NODE_NAME(parent)));
198 
199     if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
200         do {
201             FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
202             child = children_find (parent, token);
203             if (child) {
204                 parent = child;
205             } else {
206                 if (op && op->add_missing) {
207                     child = op->add_missing (parent, op->user_data);
208                     goto L_hit;
209                 }
210                 break;
211             }
212         } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
213     } else {
214         /* It's the head */
215         g_assert (parent == _head);
216         child = _head;
217     }
218 
219     if (token == NULL && child) {
220     L_hit:
221         if (op && op->hit) {
222             op->hit (child, op->user_data);
223         }
224     }
225     g_free (str);
226     return child;
227 }
228 
229 node_t*
find_node(const gchar * filename)230 find_node (const gchar *filename)
231 {
232     return find_node_internal (_head, filename, NULL);
233 }
234 
235 node_t*
find_node_full(const gchar * filename,node_op_t * op)236 find_node_full (const gchar* filename, node_op_t* op)
237 {
238     return find_node_internal (_head, filename, op);
239 }
240 
241 node_t*
add_node(node_t * parent,const gchar * filename)242 add_node (node_t* parent, const gchar* filename)
243 {
244     gchar* str;
245     gchar* token;
246     gchar* lasts;
247     node_t* child = NULL;
248 
249     g_assert (_head);
250     g_assert (filename && filename[0] == '/');
251 
252     if (parent == NULL) {
253         parent = _head;
254     }
255 
256     str = g_strdup (filename + strlen (NODE_NAME(parent)));
257 
258     if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
259         do {
260             FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
261             child = node_new (parent, token);
262             if (child) {
263                 children_add (parent, child);
264                 parent = child;
265             } else {
266                 break;
267             }
268         } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
269     }
270     g_free (str);
271     if (token == NULL) {
272         return child;
273     } else {
274         return NULL;
275     }
276 }
277 
278 /**
279  * delete recursively
280  */
281 static gboolean
remove_children(node_t * node,node_op_t * op)282 remove_children (node_t* node, node_op_t* op)
283 {
284     FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
285     if (children_num (node) > 0) {
286         children_foreach_remove (node, children_remove_cb,
287           (gpointer)op);
288     }
289     if (children_num (node) == 0) {
290         return TRUE;
291     }
292     return FALSE;
293 }
294 
295 static gboolean
remove_node_internal(node_t * node,node_op_t * op)296 remove_node_internal (node_t* node, node_op_t* op)
297 {
298     node_t* parent = NULL;
299     /*
300      * If the parent is passive and doesn't have children, delete it.
301      * NOTE node_delete_deep is a depth first delete recursively.
302      * Top node is deleted in node_cancel_sub
303      */
304     g_assert (node);
305     g_assert (op && op->pre_del);
306     if (node != _head) {
307         if (remove_children (node, op)) {
308             if (node->user_data) {
309                 if (!op->pre_del (node, op->user_data)) {
310                     return FALSE;
311                 }
312             }
313             parent = node->parent;
314             children_remove (parent, node);
315             node_delete (node);
316             if (children_num (parent) == 0) {
317                 remove_node_internal (parent, op);
318             }
319             return TRUE;
320         }
321         return FALSE;
322     }
323     return TRUE;
324 }
325 
326 void
pending_remove_node(node_t * node,node_op_t * op)327 pending_remove_node (node_t* node, node_op_t* op)
328 {
329     struct _dnode* d;
330     GList* l;
331 
332     for (l = deleting_nodes; l; l=l->next) {
333         d = (struct _dnode*) l->data;
334         if (g_ascii_strcasecmp (d->filename, NODE_NAME(node)) == 0) {
335             return;
336         }
337     }
338 
339     d = _dnode_new (NODE_NAME(node), op);
340     g_assert (d);
341     deleting_nodes = g_list_prepend (deleting_nodes, d);
342     if (deleting_nodes_id == 0) {
343         deleting_nodes_id = g_timeout_add_seconds (PROCESS_DELETING_INTERVAL,
344           scan_deleting_nodes,
345           NULL);
346         g_assert (deleting_nodes_id > 0);
347     }
348 }
349 
350 void
remove_node(node_t * node,node_op_t * op)351 remove_node (node_t* node, node_op_t* op)
352 {
353     remove_node_internal (node, op);
354 }
355 
356 static node_t*
node_new(node_t * parent,const gchar * basename)357 node_new (node_t* parent, const gchar* basename)
358 {
359 	node_t *f = NULL;
360 
361     g_assert (basename && basename[0]);
362     if ((f = g_new0 (node_t, 1)) != NULL) {
363         if (parent) {
364             f->basename = g_strdup (basename);
365             f->filename = g_build_filename (G_DIR_SEPARATOR_S,
366               NODE_NAME(parent), basename, NULL);
367         } else {
368             f->basename = g_strdup (basename);
369             f->filename = g_strdup (basename);
370         }
371         f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
372           NULL, (GDestroyNotify)node_delete);
373         FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
374     }
375 	return f;
376 }
377 
378 static void
node_delete(node_t * f)379 node_delete (node_t *f)
380 {
381     FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
382     g_assert (g_hash_table_size (f->children) == 0);
383     g_assert (f->user_data == NULL);
384 
385     g_hash_table_unref (f->children);
386     g_free (f->basename);
387     g_free (f->filename);
388     g_free (f);
389 }
390 
391 static void
children_add(node_t * p,node_t * f)392 children_add (node_t *p, node_t *f)
393 {
394     FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
395     g_hash_table_insert (p->children, f->basename, f);
396     f->parent = p;
397 }
398 
399 static void
children_remove(node_t * p,node_t * f)400 children_remove (node_t *p, node_t *f)
401 {
402     FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
403     g_hash_table_steal (p->children, f->basename);
404     f->parent = NULL;
405 }
406 
407 guint
children_num(node_t * f)408 children_num (node_t *f)
409 {
410     return g_hash_table_size (f->children);
411 }
412 
413 node_t *
children_find(node_t * f,const gchar * basename)414 children_find (node_t *f, const gchar *basename)
415 {
416     return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
417 }
418 
419 /**
420  * depth first delete recursively
421  */
422 static gboolean
children_remove_cb(gpointer key,gpointer value,gpointer user_data)423 children_remove_cb (gpointer key,
424   gpointer value,
425   gpointer user_data)
426 {
427     node_t* f = (node_t*)value;
428     node_op_t* op = (node_op_t*) user_data;
429 
430     g_assert (f->parent);
431 
432     FN_W ("%s [p] %8s [c] %8s\n", __func__, f->parent->basename, f->basename);
433     if (remove_children (f, op)) {
434         if (f->user_data != NULL) {
435             return op->pre_del (f, op->user_data);
436         }
437         return TRUE;
438     }
439     return FALSE;
440 }
441 
442 static guint
children_foreach_remove(node_t * f,GHRFunc func,gpointer user_data)443 children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data)
444 {
445     g_assert (f);
446 
447     return g_hash_table_foreach_remove (f->children, func, user_data);
448 }
449 
450 static void
children_foreach(node_t * f,GHFunc func,gpointer user_data)451 children_foreach (node_t *f, GHFunc func, gpointer user_data)
452 {
453     g_assert (f);
454 
455     g_hash_table_foreach (f->children, func, user_data);
456 }
457 
458 gboolean
node_class_init()459 node_class_init ()
460 {
461     FN_W ("%s\n", __func__);
462     if (_head == NULL) {
463         _head = node_new (NULL, G_DIR_SEPARATOR_S);
464     }
465     return _head != NULL;
466 }
467