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