1/* 2 Copyright (C) 2005 John McCutchan 3 4 The Gnome Library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public License as 6 published by the Free Software Foundation; either version 2 of the 7 License, or (at your option) any later version. 8 9 The Gnome Library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public 15 License along with the Gnome Library; see the file COPYING.LIB. If not, 16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 Boston, MA 02111-1307, USA. 18 19 Authors:. 20 John McCutchan <john@johnmccutchan.com> 21*/ 22 23#include "config.h" 24 25#include <stdio.h> 26#include <sys/ioctl.h> 27#include <unistd.h> 28#include <errno.h> 29#include <string.h> 30#include <glib.h> 31#include "inotify-kernel.h" 32#include <sys/inotify.h> 33 34/* Timings for pairing MOVED_TO / MOVED_FROM events */ 35#define PROCESS_EVENTS_TIME 1000 /* milliseconds (1 hz) */ 36#define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */ 37#define MOVE_HOLD_UNTIL_TIME 0 /* 0 milliseconds */ 38 39static int inotify_instance_fd = -1; 40static GQueue *events_to_process = NULL; 41static GQueue *event_queue = NULL; 42static GHashTable * cookie_hash = NULL; 43static GIOChannel *inotify_read_ioc; 44static GPollFD ik_poll_fd; 45static gboolean ik_poll_fd_enabled = TRUE; 46static void (*user_cb)(ik_event_t *event); 47 48static gboolean ik_read_callback (gpointer user_data); 49static gboolean ik_process_eq_callback (gpointer user_data); 50 51static guint32 ik_move_matches = 0; 52static guint32 ik_move_misses = 0; 53 54static gboolean process_eq_running = FALSE; 55 56/* We use the lock from inotify-helper.c 57 * 58 * There are two places that we take this lock 59 * 60 * 1) In ik_read_callback 61 * 62 * 2) ik_process_eq_callback. 63 * 64 * 65 * The rest of locking is taken care of in inotify-helper.c 66 */ 67G_LOCK_EXTERN (inotify_lock); 68 69typedef struct ik_event_internal { 70 ik_event_t *event; 71 gboolean seen; 72 gboolean sent; 73 GTimeVal hold_until; 74 struct ik_event_internal *pair; 75} ik_event_internal_t; 76 77/* In order to perform non-sleeping inotify event chunking we need 78 * a custom GSource 79 */ 80static gboolean 81ik_source_prepare (GSource *source, 82 gint *timeout) 83{ 84 return FALSE; 85} 86 87static gboolean 88ik_source_timeout (gpointer data) 89{ 90 GSource *source = (GSource *)data; 91 92 /* Re-active the PollFD */ 93 g_source_add_poll (source, &ik_poll_fd); 94 g_source_unref (source); 95 ik_poll_fd_enabled = TRUE; 96 97 return FALSE; 98} 99 100#define MAX_PENDING_COUNT 2 101#define PENDING_THRESHOLD(qsize) ((qsize) >> 1) 102#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p))) 103#define MAX_QUEUED_EVENTS 2048 104#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16 105#define TIMEOUT_MILLISECONDS 10 106 107static gboolean 108ik_source_check (GSource *source) 109{ 110 static int prev_pending = 0, pending_count = 0; 111 112 /* We already disabled the PollFD or 113 * nothing to be read from inotify */ 114 if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN)) 115 return FALSE; 116 117 if (pending_count < MAX_PENDING_COUNT) 118 { 119 unsigned int pending; 120 121 if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1) 122 goto do_read; 123 124 pending /= AVERAGE_EVENT_SIZE; 125 126 /* Don't wait if the number of pending events is too close 127 * to the maximum queue size. 128 */ 129 if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS)) 130 goto do_read; 131 132 /* With each successive iteration, the minimum rate for 133 * further sleep doubles. 134 */ 135 if (pending-prev_pending < PENDING_MARGINAL_COST (pending_count)) 136 goto do_read; 137 138 prev_pending = pending; 139 pending_count++; 140 141 /* We are going to wait to read the events: */ 142 143 /* Remove the PollFD from the source */ 144 g_source_remove_poll (source, &ik_poll_fd); 145 /* To avoid threading issues we need to flag that we've done that */ 146 ik_poll_fd_enabled = FALSE; 147 /* Set a timeout to re-add the PollFD to the source */ 148 g_source_ref (source); 149 g_timeout_add (TIMEOUT_MILLISECONDS, ik_source_timeout, source); 150 151 return FALSE; 152 } 153 154do_read: 155 /* We are ready to read events from inotify */ 156 157 prev_pending = 0; 158 pending_count = 0; 159 160 return TRUE; 161} 162 163static gboolean 164ik_source_dispatch (GSource *source, 165 GSourceFunc callback, 166 gpointer user_data) 167{ 168 if (callback) 169 return callback (user_data); 170 return TRUE; 171} 172 173static GSourceFuncs ik_source_funcs = 174{ 175 ik_source_prepare, 176 ik_source_check, 177 ik_source_dispatch, 178 NULL 179}; 180 181gboolean _ik_startup (void (*cb)(ik_event_t *event)) 182{ 183 static gboolean initialized = FALSE; 184 GSource *source; 185 186 user_cb = cb; 187 /* Ignore multi-calls */ 188 if (initialized) 189 return inotify_instance_fd >= 0; 190 191 initialized = TRUE; 192 inotify_instance_fd = inotify_init (); 193 194 if (inotify_instance_fd < 0) 195 return FALSE; 196 197 inotify_read_ioc = g_io_channel_unix_new (inotify_instance_fd); 198 ik_poll_fd.fd = inotify_instance_fd; 199 ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; 200 g_io_channel_set_encoding (inotify_read_ioc, NULL, NULL); 201 g_io_channel_set_flags (inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL); 202 203 source = g_source_new (&ik_source_funcs, sizeof (GSource)); 204 g_source_add_poll (source, &ik_poll_fd); 205 g_source_set_callback (source, ik_read_callback, NULL, NULL); 206 g_source_attach (source, NULL); 207 g_source_unref (source); 208 209 cookie_hash = g_hash_table_new (g_direct_hash, g_direct_equal); 210 event_queue = g_queue_new (); 211 events_to_process = g_queue_new (); 212 213 return TRUE; 214} 215 216static ik_event_internal_t * 217ik_event_internal_new (ik_event_t *event) 218{ 219 ik_event_internal_t *internal_event = g_new0 (ik_event_internal_t, 1); 220 GTimeVal tv; 221 222 g_assert (event); 223 224 g_get_current_time (&tv); 225 g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME); 226 internal_event->event = event; 227 internal_event->hold_until = tv; 228 229 return internal_event; 230} 231 232static ik_event_t * 233ik_event_new (char *buffer) 234{ 235 struct inotify_event *kevent = (struct inotify_event *)buffer; 236 ik_event_t *event = g_new0 (ik_event_t, 1); 237 238 g_assert (buffer); 239 240 event->wd = kevent->wd; 241 event->mask = kevent->mask; 242 event->cookie = kevent->cookie; 243 event->len = kevent->len; 244 if (event->len) 245 event->name = g_strdup (kevent->name); 246 else 247 event->name = g_strdup (""); 248 249 return event; 250} 251 252ik_event_t * 253_ik_event_new_dummy (const char *name, 254 gint32 wd, 255 guint32 mask) 256{ 257 ik_event_t *event = g_new0 (ik_event_t, 1); 258 event->wd = wd; 259 event->mask = mask; 260 event->cookie = 0; 261 if (name) 262 event->name = g_strdup (name); 263 else 264 event->name = g_strdup(""); 265 266 event->len = strlen (event->name); 267 268 return event; 269} 270 271void 272_ik_event_free (ik_event_t *event) 273{ 274 if (event->pair) 275 _ik_event_free (event->pair); 276 g_free (event->name); 277 g_free (event); 278} 279 280gint32 281_ik_watch (const char *path, 282 guint32 mask, 283 int *err) 284{ 285 gint32 wd = -1; 286 287 g_assert (path != NULL); 288 g_assert (inotify_instance_fd >= 0); 289 290 wd = inotify_add_watch (inotify_instance_fd, path, mask); 291 292 if (wd < 0) 293 { 294 int e = errno; 295 /* FIXME: debug msg failed to add watch */ 296 if (err) 297 *err = e; 298 return wd; 299 } 300 301 g_assert (wd >= 0); 302 return wd; 303} 304 305int 306_ik_ignore (const char *path, 307 gint32 wd) 308{ 309 g_assert (wd >= 0); 310 g_assert (inotify_instance_fd >= 0); 311 312 if (inotify_rm_watch (inotify_instance_fd, wd) < 0) 313 { 314 /* int e = errno; */ 315 /* failed to rm watch */ 316 return -1; 317 } 318 319 return 0; 320} 321 322void 323_ik_move_stats (guint32 *matches, 324 guint32 *misses) 325{ 326 if (matches) 327 *matches = ik_move_matches; 328 329 if (misses) 330 *misses = ik_move_misses; 331} 332 333const char * 334_ik_mask_to_string (guint32 mask) 335{ 336 gboolean is_dir = mask & IN_ISDIR; 337 mask &= ~IN_ISDIR; 338 339 if (is_dir) 340 { 341 switch (mask) 342 { 343 case IN_ACCESS: 344 return "ACCESS (dir)"; 345 case IN_MODIFY: 346 return "MODIFY (dir)"; 347 case IN_ATTRIB: 348 return "ATTRIB (dir)"; 349 case IN_CLOSE_WRITE: 350 return "CLOSE_WRITE (dir)"; 351 case IN_CLOSE_NOWRITE: 352 return "CLOSE_NOWRITE (dir)"; 353 case IN_OPEN: 354 return "OPEN (dir)"; 355 case IN_MOVED_FROM: 356 return "MOVED_FROM (dir)"; 357 case IN_MOVED_TO: 358 return "MOVED_TO (dir)"; 359 case IN_DELETE: 360 return "DELETE (dir)"; 361 case IN_CREATE: 362 return "CREATE (dir)"; 363 case IN_DELETE_SELF: 364 return "DELETE_SELF (dir)"; 365 case IN_UNMOUNT: 366 return "UNMOUNT (dir)"; 367 case IN_Q_OVERFLOW: 368 return "Q_OVERFLOW (dir)"; 369 case IN_IGNORED: 370 return "IGNORED (dir)"; 371 default: 372 return "UNKNOWN_EVENT (dir)"; 373 } 374 } 375 else 376 { 377 switch (mask) 378 { 379 case IN_ACCESS: 380 return "ACCESS"; 381 case IN_MODIFY: 382 return "MODIFY"; 383 case IN_ATTRIB: 384 return "ATTRIB"; 385 case IN_CLOSE_WRITE: 386 return "CLOSE_WRITE"; 387 case IN_CLOSE_NOWRITE: 388 return "CLOSE_NOWRITE"; 389 case IN_OPEN: 390 return "OPEN"; 391 case IN_MOVED_FROM: 392 return "MOVED_FROM"; 393 case IN_MOVED_TO: 394 return "MOVED_TO"; 395 case IN_DELETE: 396 return "DELETE"; 397 case IN_CREATE: 398 return "CREATE"; 399 case IN_DELETE_SELF: 400 return "DELETE_SELF"; 401 case IN_UNMOUNT: 402 return "UNMOUNT"; 403 case IN_Q_OVERFLOW: 404 return "Q_OVERFLOW"; 405 case IN_IGNORED: 406 return "IGNORED"; 407 default: 408 return "UNKNOWN_EVENT"; 409 } 410 } 411} 412 413 414static void 415ik_read_events (gsize *buffer_size_out, 416 gchar **buffer_out) 417{ 418 static gchar *buffer = NULL; 419 static gsize buffer_size; 420 421 /* Initialize the buffer on our first call */ 422 if (buffer == NULL) 423 { 424 buffer_size = AVERAGE_EVENT_SIZE; 425 buffer_size *= MAX_QUEUED_EVENTS; 426 buffer = g_malloc (buffer_size); 427 } 428 429 *buffer_size_out = 0; 430 *buffer_out = NULL; 431 432 memset (buffer, 0, buffer_size); 433 434 if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) { 435 /* error reading */ 436 } 437 *buffer_out = buffer; 438} 439 440static gboolean 441ik_read_callback (gpointer user_data) 442{ 443 gchar *buffer; 444 gsize buffer_size, buffer_i, events; 445 446 G_LOCK (inotify_lock); 447 ik_read_events (&buffer_size, &buffer); 448 449 buffer_i = 0; 450 events = 0; 451 while (buffer_i < buffer_size) 452 { 453 struct inotify_event *event; 454 gsize event_size; 455 event = (struct inotify_event *)&buffer[buffer_i]; 456 event_size = sizeof(struct inotify_event) + event->len; 457 g_queue_push_tail (events_to_process, ik_event_internal_new (ik_event_new (&buffer[buffer_i]))); 458 buffer_i += event_size; 459 events++; 460 } 461 462 /* If the event process callback is off, turn it back on */ 463 if (!process_eq_running && events) 464 { 465 process_eq_running = TRUE; 466 g_timeout_add (PROCESS_EVENTS_TIME, ik_process_eq_callback, NULL); 467 } 468 469 G_UNLOCK (inotify_lock); 470 471 return TRUE; 472} 473 474static gboolean 475g_timeval_lt (GTimeVal *val1, 476 GTimeVal *val2) 477{ 478 if (val1->tv_sec < val2->tv_sec) 479 return TRUE; 480 481 if (val1->tv_sec > val2->tv_sec) 482 return FALSE; 483 484 /* val1->tv_sec == val2->tv_sec */ 485 if (val1->tv_usec < val2->tv_usec) 486 return TRUE; 487 488 return FALSE; 489} 490 491static gboolean 492g_timeval_eq (GTimeVal *val1, 493 GTimeVal *val2) 494{ 495 return (val1->tv_sec == val2->tv_sec) && (val1->tv_usec == val2->tv_usec); 496} 497 498static void 499ik_pair_events (ik_event_internal_t *event1, 500 ik_event_internal_t *event2) 501{ 502 g_assert (event1 && event2); 503 /* We should only be pairing events that have the same cookie */ 504 g_assert (event1->event->cookie == event2->event->cookie); 505 /* We shouldn't pair an event that already is paired */ 506 g_assert (event1->pair == NULL && event2->pair == NULL); 507 508 /* Pair the internal structures and the ik_event_t structures */ 509 event1->pair = event2; 510 event1->event->pair = event2->event; 511 512 if (g_timeval_lt (&event1->hold_until, &event2->hold_until)) 513 event1->hold_until = event2->hold_until; 514 515 event2->hold_until = event1->hold_until; 516} 517 518static void 519ik_event_add_microseconds (ik_event_internal_t *event, 520 glong ms) 521{ 522 g_assert (event); 523 g_time_val_add (&event->hold_until, ms); 524} 525 526static gboolean 527ik_event_ready (ik_event_internal_t *event) 528{ 529 GTimeVal tv; 530 g_assert (event); 531 532 g_get_current_time (&tv); 533 534 /* An event is ready if, 535 * 536 * it has no cookie -- there is nothing to be gained by holding it 537 * or, it is already paired -- we don't need to hold it anymore 538 * or, we have held it long enough 539 */ 540 return 541 event->event->cookie == 0 || 542 event->pair != NULL || 543 g_timeval_lt (&event->hold_until, &tv) || 544 g_timeval_eq (&event->hold_until, &tv); 545} 546 547static void 548ik_pair_moves (gpointer data, 549 gpointer user_data) 550{ 551 ik_event_internal_t *event = (ik_event_internal_t *)data; 552 553 if (event->seen == TRUE || event->sent == TRUE) 554 return; 555 556 if (event->event->cookie != 0) 557 { 558 /* When we get a MOVED_FROM event we delay sending the event by 559 * MOVE_HOLD_UNTIL_TIME microseconds. We need to do this because a 560 * MOVED_TO pair _might_ be coming in the near future */ 561 if (event->event->mask & IN_MOVED_FROM) 562 { 563 g_hash_table_insert (cookie_hash, GINT_TO_POINTER (event->event->cookie), event); 564 /* because we don't deliver move events there is no point in waiting for the match right now. */ 565 ik_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME); 566 } 567 else if (event->event->mask & IN_MOVED_TO) 568 { 569 /* We need to check if we are waiting for this MOVED_TO events cookie to pair it with 570 * a MOVED_FROM */ 571 ik_event_internal_t *match = NULL; 572 match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie)); 573 if (match) 574 { 575 g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie)); 576 ik_pair_events (match, event); 577 } 578 } 579 } 580 event->seen = TRUE; 581} 582 583static void 584ik_process_events (void) 585{ 586 g_queue_foreach (events_to_process, ik_pair_moves, NULL); 587 588 while (!g_queue_is_empty (events_to_process)) 589 { 590 ik_event_internal_t *event = g_queue_peek_head (events_to_process); 591 592 /* This must have been sent as part of a MOVED_TO/MOVED_FROM */ 593 if (event->sent) 594 { 595 /* Pop event */ 596 g_queue_pop_head (events_to_process); 597 /* Free the internal event structure */ 598 g_free (event); 599 continue; 600 } 601 602 /* The event isn't ready yet */ 603 if (!ik_event_ready (event)) 604 break; 605 606 /* Pop it */ 607 event = g_queue_pop_head (events_to_process); 608 609 /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */ 610 if (event->event->cookie && event->pair == NULL && 611 g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie))) 612 g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie)); 613 614 if (event->pair) 615 { 616 /* We send out paired MOVED_FROM/MOVED_TO events in the same event buffer */ 617 /* g_assert (event->event->mask == IN_MOVED_FROM && event->pair->event->mask == IN_MOVED_TO); */ 618 /* Copy the paired data */ 619 event->pair->sent = TRUE; 620 event->sent = TRUE; 621 ik_move_matches++; 622 } 623 else if (event->event->cookie) 624 { 625 /* If we couldn't pair a MOVED_FROM and MOVED_TO together, we change 626 * the event masks */ 627 /* Changeing MOVED_FROM to DELETE and MOVED_TO to create lets us make 628 * the gaurantee that you will never see a non-matched MOVE event */ 629 630 if (event->event->mask & IN_MOVED_FROM) 631 { 632 event->event->mask = IN_DELETE|(event->event->mask & IN_ISDIR); 633 ik_move_misses++; /* not super accurate, if we aren't watching the destination it still counts as a miss */ 634 } 635 if (event->event->mask & IN_MOVED_TO) 636 event->event->mask = IN_CREATE|(event->event->mask & IN_ISDIR); 637 } 638 639 /* Push the ik_event_t onto the event queue */ 640 g_queue_push_tail (event_queue, event->event); 641 /* Free the internal event structure */ 642 g_free (event); 643 } 644} 645 646static gboolean 647ik_process_eq_callback (gpointer user_data) 648{ 649 gboolean res; 650 651 /* Try and move as many events to the event queue */ 652 G_LOCK (inotify_lock); 653 ik_process_events (); 654 655 while (!g_queue_is_empty (event_queue)) 656 { 657 ik_event_t *event = g_queue_pop_head (event_queue); 658 659 user_cb (event); 660 } 661 662 res = TRUE; 663 664 if (g_queue_get_length (events_to_process) == 0) 665 { 666 process_eq_running = FALSE; 667 res = FALSE; 668 } 669 670 G_UNLOCK (inotify_lock); 671 672 return res; 673} 674