Home | History | Annotate | Download | only in fen
      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 (at) 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*
     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
     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
     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
    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
    149 node_get_data (node_t* node)
    150 {
    151     g_assert (node);
    152     return node->user_data;
    153 }
    154 
    155 gpointer
    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
    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*
    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*
    230 find_node (const gchar *filename)
    231 {
    232     return find_node_internal (_head, filename, NULL);
    233 }
    234 
    235 node_t*
    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*
    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
    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
    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
    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
    351 remove_node (node_t* node, node_op_t* op)
    352 {
    353     remove_node_internal (node, op);
    354 }
    355 
    356 static node_t*
    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
    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
    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
    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
    408 children_num (node_t *f)
    409 {
    410     return g_hash_table_size (f->children);
    411 }
    412 
    413 node_t *
    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
    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
    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
    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
    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