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 <rctl.h> 27 #include <strings.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <glib.h> 33 #include "fen-kernel.h" 34 #include "fen-dump.h" 35 36 #ifdef GIO_COMPILATION 37 #define FK_W if (fk_debug_enabled) g_warning 38 static gboolean fk_debug_enabled = FALSE; 39 #else 40 #include "gam_error.h" 41 #define FK_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__) 42 #endif 43 44 G_GNUC_INTERNAL G_LOCK_DEFINE (fen_lock); 45 #define PE_ALLOC 64 46 #define F_PORT(pfo) (((_f *)(pfo))->port->port) 47 #define F_NAME(pfo) (((_f *)(pfo))->fobj->fo_name) 48 #define FEN_ALL_EVENTS (FILE_MODIFIED | FILE_ATTRIB | FILE_NOFOLLOW) 49 #define FEN_IGNORE_EVENTS (FILE_ACCESS) 50 #define PROCESS_PORT_EVENTS_TIME 400 /* in milliseconds */ 51 52 static GHashTable *_obj_fen_hash = NULL; /* <user_data, port> */ 53 static ulong max_port_events = 512; 54 static GList *pn_vq; /* the queue of ports which don't have the max objs */ 55 static GList *pn_fq; /* the queue of ports which have the max objs */ 56 static GQueue *g_eventq = NULL; 57 static void (*add_event_cb) (gpointer, fnode_event_t*); 58 59 typedef struct pnode 60 { 61 long ref; /* how many fds are associated to this port */ 62 int port; 63 guint port_source_id; 64 } pnode_t; 65 66 typedef struct { 67 pnode_t* port; 68 file_obj_t* fobj; 69 70 gboolean is_active; 71 gpointer user_data; 72 } _f; 73 74 static gboolean port_fetch_event_cb (void *arg); 75 static pnode_t *pnode_new (); 76 static void pnode_delete (pnode_t *pn); 77 78 gboolean 79 is_ported (gpointer f) 80 { 81 _f* fo = g_hash_table_lookup (_obj_fen_hash, f); 82 83 if (fo) { 84 return fo->is_active; 85 } 86 return FALSE; 87 } 88 89 static gchar* 90 printevent (const char *pname, int event, const char *tag) 91 { 92 static gchar *event_string = NULL; 93 GString *str; 94 95 if (event_string) { 96 g_free(event_string); 97 } 98 99 str = g_string_new (""); 100 g_string_printf (str, "[%s] [%-20s]", tag, pname); 101 if (event & FILE_ACCESS) { 102 str = g_string_append (str, " ACCESS"); 103 } 104 if (event & FILE_MODIFIED) { 105 str = g_string_append (str, " MODIFIED"); 106 } 107 if (event & FILE_ATTRIB) { 108 str = g_string_append (str, " ATTRIB"); 109 } 110 if (event & FILE_DELETE) { 111 str = g_string_append (str, " DELETE"); 112 } 113 if (event & FILE_RENAME_TO) { 114 str = g_string_append (str, " RENAME_TO"); 115 } 116 if (event & FILE_RENAME_FROM) { 117 str = g_string_append (str, " RENAME_FROM"); 118 } 119 if (event & UNMOUNTED) { 120 str = g_string_append (str, " UNMOUNTED"); 121 } 122 if (event & MOUNTEDOVER) { 123 str = g_string_append (str, " MOUNTEDOVER"); 124 } 125 event_string = str->str; 126 g_string_free (str, FALSE); 127 return event_string; 128 } 129 130 static void 131 port_add_kevent (int e, gpointer f) 132 { 133 fnode_event_t *ev, *tail; 134 GTimeVal t; 135 gboolean has_twin = FALSE; 136 137 /* 138 * Child FILE_DELETE | FILE_RENAME_FROM will trigger parent FILE_MODIFIED. 139 * FILE_MODIFIED will trigger FILE_ATTRIB. 140 */ 141 142 if ((e & FILE_ATTRIB) && e != FILE_ATTRIB) { 143 e ^= FILE_ATTRIB; 144 has_twin = TRUE; 145 } 146 if (e == FILE_RENAME_FROM) { 147 e = FILE_DELETE; 148 } 149 if (e == FILE_RENAME_TO) { 150 e = FILE_MODIFIED; 151 } 152 153 switch (e) { 154 case FILE_DELETE: 155 case FILE_RENAME_FROM: 156 case FILE_MODIFIED: 157 case FILE_ATTRIB: 158 case UNMOUNTED: 159 case MOUNTEDOVER: 160 break; 161 case FILE_RENAME_TO: 162 case FILE_ACCESS: 163 default: 164 g_assert_not_reached (); 165 return; 166 } 167 168 tail = (fnode_event_t*) g_queue_peek_tail (g_eventq); 169 if (tail) { 170 if (tail->user_data == f) { 171 if (tail->e == e) { 172 tail->has_twin = (has_twin | (tail->has_twin ^ has_twin)); 173 /* skip the current */ 174 return; 175 } else if (e == FILE_MODIFIED && !has_twin 176 && tail->e == FILE_ATTRIB) { 177 tail->e = FILE_MODIFIED; 178 tail->has_twin = TRUE; 179 return; 180 } else if (e == FILE_ATTRIB 181 && tail->e == FILE_MODIFIED && !tail->has_twin) { 182 tail->has_twin = TRUE; 183 return; 184 } 185 } 186 } 187 188 if ((ev = fnode_event_new (e, has_twin, f)) != NULL) { 189 g_queue_push_tail (g_eventq, ev); 190 } 191 } 192 193 static void 194 port_process_kevents () 195 { 196 fnode_event_t *ev; 197 198 while ((ev = (fnode_event_t*)g_queue_pop_head (g_eventq)) != NULL) { 199 FK_W ("[%s] 0x%p %s\n", __func__, ev, _event_string (ev->e)); 200 add_event_cb (ev->user_data, ev); 201 } 202 } 203 204 static gboolean 205 port_fetch_event_cb (void *arg) 206 { 207 pnode_t *pn = (pnode_t *)arg; 208 _f* fo; 209 uint_t nget = 0; 210 port_event_t pe[PE_ALLOC]; 211 timespec_t timeout; 212 gpointer f; 213 gboolean ret = TRUE; 214 215 /* FK_W ("IN <======== %s\n", __func__); */ 216 G_LOCK (fen_lock); 217 218 memset (&timeout, 0, sizeof (timespec_t)); 219 do { 220 nget = 1; 221 if (port_getn (pn->port, pe, PE_ALLOC, &nget, &timeout) == 0) { 222 int i; 223 for (i = 0; i < nget; i++) { 224 fo = (_f*)pe[i].portev_user; 225 /* handle event */ 226 switch (pe[i].portev_source) { 227 case PORT_SOURCE_FILE: 228 /* If got FILE_EXCEPTION or add to port failed, 229 delete the pnode */ 230 fo->is_active = FALSE; 231 if (fo->user_data) { 232 FK_W("%s\n", 233 printevent(F_NAME(fo), pe[i].portev_events, "RAW")); 234 port_add_kevent (pe[i].portev_events, fo->user_data); 235 } else { 236 /* fnode is deleted */ 237 goto L_delete; 238 } 239 if (pe[i].portev_events & FILE_EXCEPTION) { 240 g_hash_table_remove (_obj_fen_hash, fo->user_data); 241 L_delete: 242 FK_W ("[ FREE_FO ] [0x%p]\n", fo); 243 pnode_delete (fo->port); 244 g_free (fo); 245 } 246 break; 247 default: 248 /* case PORT_SOURCE_TIMER: */ 249 FK_W ("[kernel] unknown portev_source %d\n", pe[i].portev_source); 250 } 251 } 252 } else { 253 FK_W ("[kernel] port_getn %s\n", g_strerror (errno)); 254 nget = 0; 255 } 256 } while (nget == PE_ALLOC); 257 258 /* Processing g_eventq */ 259 port_process_kevents (); 260 261 if (pn->ref == 0) { 262 pn->port_source_id = 0; 263 ret = FALSE; 264 } 265 G_UNLOCK (fen_lock); 266 /* FK_W ("OUT ========> %s\n", __func__); */ 267 return ret; 268 } 269 270 /* 271 * ref - 1 if remove a watching file succeeded. 272 */ 273 static void 274 pnode_delete (pnode_t *pn) 275 { 276 g_assert (pn->ref <= max_port_events); 277 278 if (pn->ref == max_port_events) { 279 FK_W ("PORT : move to visible queue - [pn] 0x%p [ref] %d\n", pn, pn->ref); 280 pn_fq = g_list_remove (pn_fq, pn); 281 pn_vq = g_list_prepend (pn_vq, pn); 282 } 283 if ((-- pn->ref) == 0) { 284 /* Should dispatch the source */ 285 } 286 FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref); 287 } 288 289 /* 290 * malloc pnode_t and port_create, start thread at pnode_ref. 291 * if pnode_new succeeded, the pnode_t will never 292 * be freed. So pnode_t can be freed only in pnode_new. 293 * Note pnode_monitor_remove_all can also free pnode_t, but currently no one 294 * invork it. 295 */ 296 static pnode_t * 297 pnode_new () 298 { 299 pnode_t *pn = NULL; 300 301 if (pn_vq) { 302 pn = (pnode_t*)pn_vq->data; 303 g_assert (pn->ref < max_port_events); 304 } else { 305 pn = g_new0 (pnode_t, 1); 306 if (pn != NULL) { 307 if ((pn->port = port_create ()) >= 0) { 308 g_assert (g_list_find (pn_vq, pn) == NULL); 309 pn_vq = g_list_prepend (pn_vq, pn); 310 } else { 311 FK_W ("PORT_CREATE %s\n", g_strerror (errno)); 312 g_free (pn); 313 pn = NULL; 314 } 315 } 316 } 317 if (pn) { 318 FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref); 319 pn->ref++; 320 if (pn->ref == max_port_events) { 321 FK_W ("PORT : move to full queue - [pn] 0x%p [ref] %d\n", pn, pn->ref); 322 pn_vq = g_list_remove (pn_vq, pn); 323 pn_fq = g_list_prepend (pn_fq, pn); 324 g_assert (g_list_find (pn_vq, pn) == NULL); 325 } 326 /* attach the source */ 327 if (pn->port_source_id == 0) { 328 pn->port_source_id = g_timeout_add (PROCESS_PORT_EVENTS_TIME, 329 port_fetch_event_cb, 330 (void *)pn); 331 g_assert (pn->port_source_id > 0); 332 } 333 } 334 335 return pn; 336 } 337 338 /** 339 * port_add_internal 340 * 341 * < private > 342 * Unsafe, need lock fen_lock. 343 */ 344 static gboolean 345 port_add_internal (file_obj_t* fobj, off_t* len, 346 gpointer f, gboolean need_stat) 347 { 348 int ret; 349 struct stat buf; 350 _f* fo = NULL; 351 352 g_assert (f && fobj); 353 FK_W ("%s [0x%p] %s\n", __func__, f, fobj->fo_name); 354 355 if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) == NULL) { 356 fo = g_new0 (_f, 1); 357 fo->fobj = fobj; 358 fo->user_data = f; 359 g_assert (fo); 360 FK_W ("[ NEW_FO ] [0x%p] %s\n", fo, F_NAME(fo)); 361 g_hash_table_insert (_obj_fen_hash, f, fo); 362 } 363 364 if (fo->is_active) { 365 return TRUE; 366 } 367 368 if (fo->port == NULL) { 369 fo->port = pnode_new (); 370 } 371 372 if (need_stat) { 373 if (FN_STAT (F_NAME(fo), &buf) != 0) { 374 FK_W ("LSTAT [%-20s] %s\n", F_NAME(fo), g_strerror (errno)); 375 goto L_exit; 376 } 377 g_assert (len); 378 fo->fobj->fo_atime = buf.st_atim; 379 fo->fobj->fo_mtime = buf.st_mtim; 380 fo->fobj->fo_ctime = buf.st_ctim; 381 *len = buf.st_size; 382 } 383 384 if (port_associate (F_PORT(fo), 385 PORT_SOURCE_FILE, 386 (uintptr_t)fo->fobj, 387 FEN_ALL_EVENTS, 388 (void *)fo) == 0) { 389 fo->is_active = TRUE; 390 FK_W ("%s %s\n", "PORT_ASSOCIATE", F_NAME(fo)); 391 return TRUE; 392 } else { 393 FK_W ("PORT_ASSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno)); 394 L_exit: 395 FK_W ("[ FREE_FO ] [0x%p]\n", fo); 396 g_hash_table_remove (_obj_fen_hash, f); 397 pnode_delete (fo->port); 398 g_free (fo); 399 } 400 return FALSE; 401 } 402 403 gboolean 404 port_add (file_obj_t* fobj, off_t* len, gpointer f) 405 { 406 return port_add_internal (fobj, len, f, TRUE); 407 } 408 409 gboolean 410 port_add_simple (file_obj_t* fobj, gpointer f) 411 { 412 return port_add_internal (fobj, NULL, f, FALSE); 413 } 414 415 /** 416 * port_remove 417 * 418 * < private > 419 * Unsafe, need lock fen_lock. 420 */ 421 void 422 port_remove (gpointer f) 423 { 424 _f* fo = NULL; 425 426 FK_W ("%s\n", __func__); 427 if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) != NULL) { 428 /* Marked */ 429 fo->user_data = NULL; 430 g_hash_table_remove (_obj_fen_hash, f); 431 432 if (port_dissociate (F_PORT(fo), 433 PORT_SOURCE_FILE, 434 (uintptr_t)fo->fobj) == 0) { 435 /* 436 * Note, we can run foode_delete if dissociating is failed, 437 * because there may be some pending events (mostly like 438 * FILE_DELETE) in the port_get. If we delete the foode 439 * the fnode may be deleted, then port_get will run on an invalid 440 * address. 441 */ 442 FK_W ("[ FREE_FO ] [0x%p]\n", fo); 443 pnode_delete (fo->port); 444 g_free (fo); 445 } else { 446 FK_W ("PORT_DISSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno)); 447 } 448 } 449 } 450 451 const gchar * 452 _event_string (int event) 453 { 454 switch (event) { 455 case FILE_DELETE: 456 return "FILE_DELETE"; 457 case FILE_RENAME_FROM: 458 return "FILE_RENAME_FROM"; 459 case FILE_MODIFIED: 460 return "FILE_MODIFIED"; 461 case FILE_RENAME_TO: 462 return "FILE_RENAME_TO"; 463 case MOUNTEDOVER: 464 return "MOUNTEDOVER"; 465 case FILE_ATTRIB: 466 return "FILE_ATTRIB"; 467 case UNMOUNTED: 468 return "UNMOUNTED"; 469 case FILE_ACCESS: 470 return "FILE_ACCESS"; 471 default: 472 return "EVENT_UNKNOWN"; 473 } 474 } 475 476 /** 477 * Get Solaris resouce values. 478 * 479 */ 480 481 extern gboolean 482 port_class_init (void (*user_add_event) (gpointer, fnode_event_t*)) 483 { 484 rctlblk_t *rblk; 485 FK_W ("%s\n", __func__); 486 if ((rblk = malloc (rctlblk_size ())) == NULL) { 487 FK_W ("[kernel] rblk malloc %s\n", g_strerror (errno)); 488 return FALSE; 489 } 490 if (getrctl ("process.max-port-events", NULL, rblk, RCTL_FIRST) == -1) { 491 FK_W ("[kernel] getrctl %s\n", g_strerror (errno)); 492 free (rblk); 493 return FALSE; 494 } else { 495 max_port_events = rctlblk_get_value(rblk); 496 FK_W ("[kernel] max_port_events = %u\n", max_port_events); 497 free (rblk); 498 } 499 if ((_obj_fen_hash = g_hash_table_new(g_direct_hash, 500 g_direct_equal)) == NULL) { 501 FK_W ("[kernel] fobj hash initializing faild\n"); 502 return FALSE; 503 } 504 if ((g_eventq = g_queue_new ()) == NULL) { 505 FK_W ("[kernel] FEN global event queue initializing faild\n"); 506 } 507 if (user_add_event == NULL) { 508 return FALSE; 509 } 510 add_event_cb = user_add_event; 511 return TRUE; 512 } 513 514 fnode_event_t* 515 fnode_event_new (int event, gboolean has_twin, gpointer user_data) 516 { 517 fnode_event_t *ev; 518 519 if ((ev = g_new (fnode_event_t, 1)) != NULL) { 520 g_assert (ev); 521 ev->e = event; 522 ev->user_data = user_data; 523 ev->has_twin = has_twin; 524 /* Default isn't a pending event. */ 525 ev->is_pending = FALSE; 526 } 527 return ev; 528 } 529 530 void 531 fnode_event_delete (fnode_event_t* ev) 532 { 533 g_free (ev); 534 } 535