Blame gio/inotify/inotify-kernel.c

Packit ae235b
/*
Packit ae235b
   Copyright (C) 2005 John McCutchan
Packit ae235b
   Copyright © 2015 Canonical Limited
Packit ae235b
Packit ae235b
   This library is free software; you can redistribute it and/or
Packit ae235b
   modify it under the terms of the GNU Lesser General Public
Packit ae235b
   License as published by the Free Software Foundation; either
Packit ae235b
   version 2.1 of the License, or (at your option) any later version.
Packit ae235b
Packit ae235b
   This library is distributed in the hope that it will be useful,
Packit ae235b
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
   Lesser General Public License for more details.
Packit ae235b
Packit ae235b
   You should have received a copy of the GNU Lesser General Public License
Packit ae235b
   along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
Packit ae235b
   Authors:
Packit ae235b
     Ryan Lortie <desrt@desrt.ca>
Packit ae235b
     John McCutchan <john@johnmccutchan.com>
Packit ae235b
*/
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <stdio.h>
Packit ae235b
#include <sys/ioctl.h>
Packit ae235b
#include <unistd.h>
Packit ae235b
#include <errno.h>
Packit ae235b
#include <string.h>
Packit ae235b
#include <glib.h>
Packit ae235b
#include "inotify-kernel.h"
Packit ae235b
#include <sys/inotify.h>
Packit ae235b
#include <glib/glib-unix.h>
Packit ae235b
Packit ae235b
#include "glib-private.h"
Packit ae235b
Packit ae235b
/* From inotify(7) */
Packit ae235b
#define MAX_EVENT_SIZE       (sizeof(struct inotify_event) + NAME_MAX + 1)
Packit ae235b
Packit ae235b
/* Amount of time to sleep on receipt of uninteresting events */
Packit ae235b
#define BOREDOM_SLEEP_TIME   (100 * G_TIME_SPAN_MILLISECOND)
Packit ae235b
Packit ae235b
/* Define limits on the maximum amount of time and maximum amount of
Packit ae235b
 * interceding events between FROM/TO that can be merged.
Packit ae235b
 */
Packit ae235b
#define MOVE_PAIR_DELAY      (10 * G_TIME_SPAN_MILLISECOND)
Packit ae235b
#define MOVE_PAIR_DISTANCE   (100)
Packit ae235b
Packit ae235b
/* We use the lock from inotify-helper.c
Packit ae235b
 *
Packit ae235b
 * We only have to take it on our read callback.
Packit ae235b
 *
Packit ae235b
 * The rest of locking is taken care of in inotify-helper.c
Packit ae235b
 */
Packit ae235b
G_LOCK_EXTERN (inotify_lock);
Packit ae235b
Packit ae235b
static ik_event_t *
Packit ae235b
ik_event_new (struct inotify_event *kevent,
Packit ae235b
              gint64                now)
Packit ae235b
{
Packit ae235b
  ik_event_t *event = g_new0 (ik_event_t, 1);
Packit ae235b
Packit ae235b
  event->wd = kevent->wd;
Packit ae235b
  event->mask = kevent->mask;
Packit ae235b
  event->cookie = kevent->cookie;
Packit ae235b
  event->len = kevent->len;
Packit ae235b
  event->timestamp = now;
Packit ae235b
  if (event->len)
Packit ae235b
    event->name = g_strdup (kevent->name);
Packit ae235b
  else
Packit ae235b
    event->name = NULL;
Packit ae235b
Packit ae235b
  return event;
Packit ae235b
}
Packit ae235b
Packit ae235b
void
Packit ae235b
_ik_event_free (ik_event_t *event)
Packit ae235b
{
Packit ae235b
  if (event->pair)
Packit ae235b
    {
Packit ae235b
      event->pair->pair = NULL;
Packit ae235b
      _ik_event_free (event->pair);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_free (event->name);
Packit ae235b
  g_free (event);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  GSource     source;
Packit ae235b
Packit ae235b
  GQueue      queue;
Packit ae235b
  gpointer    fd_tag;
Packit ae235b
  gint        fd;
Packit ae235b
Packit ae235b
  GHashTable *unmatched_moves;
Packit ae235b
  gboolean    is_bored;
Packit ae235b
} InotifyKernelSource;
Packit ae235b
Packit ae235b
static InotifyKernelSource *inotify_source;
Packit ae235b
Packit ae235b
static gint64
Packit ae235b
ik_source_get_dispatch_time (InotifyKernelSource *iks)
Packit ae235b
{
Packit ae235b
  ik_event_t *head;
Packit ae235b
Packit ae235b
  head = g_queue_peek_head (&iks->queue);
Packit ae235b
Packit ae235b
  /* nothing in the queue: not ready */
Packit ae235b
  if (!head)
Packit ae235b
    return -1;
Packit ae235b
Packit ae235b
  /* if it's not an unpaired move, it is ready now */
Packit ae235b
  if (~head->mask & IN_MOVED_FROM || head->pair)
Packit ae235b
    return 0;
Packit ae235b
Packit ae235b
  /* if the queue is too long then it's ready now */
Packit ae235b
  if (iks->queue.length > MOVE_PAIR_DISTANCE)
Packit ae235b
    return 0;
Packit ae235b
Packit ae235b
  /* otherwise, it's ready after the delay */
Packit ae235b
  return head->timestamp + MOVE_PAIR_DELAY;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
ik_source_can_dispatch_now (InotifyKernelSource *iks,
Packit ae235b
                            gint64               now)
Packit ae235b
{
Packit ae235b
  gint64 dispatch_time;
Packit ae235b
Packit ae235b
  dispatch_time = ik_source_get_dispatch_time (iks);
Packit ae235b
Packit ae235b
  return 0 <= dispatch_time && dispatch_time <= now;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gsize
Packit ae235b
ik_source_read_some_events (InotifyKernelSource *iks,
Packit ae235b
                            gchar               *buffer,
Packit ae235b
                            gsize                buffer_len)
Packit ae235b
{
Packit ae235b
  gssize result;
Packit ae235b
  int errsv;
Packit ae235b
Packit ae235b
again:
Packit ae235b
  result = read (iks->fd, buffer, buffer_len);
Packit ae235b
  errsv = errno;
Packit ae235b
Packit ae235b
  if (result < 0)
Packit ae235b
    {
Packit ae235b
      if (errsv == EINTR)
Packit ae235b
        goto again;
Packit ae235b
Packit ae235b
      if (errsv == EAGAIN)
Packit ae235b
        return 0;
Packit ae235b
Packit ae235b
      g_error ("inotify read(): %s", g_strerror (errsv));
Packit ae235b
    }
Packit ae235b
  else if (result == 0)
Packit ae235b
    g_error ("inotify unexpectedly hit eof");
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gchar *
Packit ae235b
ik_source_read_all_the_events (InotifyKernelSource *iks,
Packit ae235b
                               gchar               *buffer,
Packit ae235b
                               gsize                buffer_len,
Packit ae235b
                               gsize               *length_out)
Packit ae235b
{
Packit ae235b
  gsize n_read;
Packit ae235b
Packit ae235b
  n_read = ik_source_read_some_events (iks, buffer, buffer_len);
Packit ae235b
Packit ae235b
  /* Check if we might have gotten another event if we had passed in a
Packit ae235b
   * bigger buffer...
Packit ae235b
   */
Packit ae235b
  if (n_read + MAX_EVENT_SIZE > buffer_len)
Packit ae235b
    {
Packit ae235b
      gchar *new_buffer;
Packit ae235b
      guint n_readable;
Packit ae235b
      gint result;
Packit ae235b
      int errsv;
Packit ae235b
Packit ae235b
      /* figure out how many more bytes there are to read */
Packit ae235b
      result = ioctl (iks->fd, FIONREAD, &n_readable);
Packit ae235b
      errsv = errno;
Packit ae235b
      if (result != 0)
Packit ae235b
        g_error ("inotify ioctl(FIONREAD): %s", g_strerror (errsv));
Packit ae235b
Packit ae235b
      if (n_readable != 0)
Packit ae235b
        {
Packit ae235b
          /* there is in fact more data.  allocate a new buffer, copy
Packit ae235b
           * the existing data, and then append the remaining.
Packit ae235b
           */
Packit ae235b
          new_buffer = g_malloc (n_read + n_readable);
Packit ae235b
          memcpy (new_buffer, buffer, n_read);
Packit ae235b
          n_read += ik_source_read_some_events (iks, new_buffer + n_read, n_readable);
Packit ae235b
Packit ae235b
          buffer = new_buffer;
Packit ae235b
Packit ae235b
          /* There may be new events in the buffer that were added after
Packit ae235b
           * the FIONREAD was performed, but we can't risk getting into
Packit ae235b
           * a loop.  We'll get them next time.
Packit ae235b
           */
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  *length_out = n_read;
Packit ae235b
Packit ae235b
  return buffer;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
ik_source_dispatch (GSource     *source,
Packit ae235b
                    GSourceFunc  func,
Packit ae235b
                    gpointer     user_data)
Packit ae235b
{
Packit ae235b
  InotifyKernelSource *iks = (InotifyKernelSource *) source;
Packit ae235b
  gboolean (*user_callback) (ik_event_t *event) = (void *) func;
Packit ae235b
  gboolean interesting = FALSE;
Packit ae235b
  gint64 now;
Packit ae235b
Packit ae235b
  now = g_source_get_time (source);
Packit ae235b
Packit ae235b
  if (iks->is_bored || g_source_query_unix_fd (source, iks->fd_tag))
Packit ae235b
    {
Packit ae235b
      gchar stack_buffer[4096];
Packit ae235b
      gsize buffer_len;
Packit ae235b
      gchar *buffer;
Packit ae235b
      gsize offset;
Packit ae235b
Packit ae235b
      /* We want to read all of the available events.
Packit ae235b
       *
Packit ae235b
       * We need to do it in a finite number of steps so that we don't
Packit ae235b
       * get caught in a loop of read() with another process
Packit ae235b
       * continuously adding events each time we drain them.
Packit ae235b
       *
Packit ae235b
       * In the normal case we will have only a few events in the queue,
Packit ae235b
       * so start out by reading into a small stack-allocated buffer.
Packit ae235b
       * Even though we're on a fresh stack frame, there is no need to
Packit ae235b
       * pointlessly blow up with the size of the worker thread stack
Packit ae235b
       * with a huge buffer here.
Packit ae235b
       *
Packit ae235b
       * If the result is large enough to cause us to suspect that
Packit ae235b
       * another event may be pending then we allocate a buffer on the
Packit ae235b
       * heap that can hold all of the events and read (once!) into that
Packit ae235b
       * buffer.
Packit ae235b
       */
Packit ae235b
      buffer = ik_source_read_all_the_events (iks, stack_buffer, sizeof stack_buffer, &buffer_len);
Packit ae235b
Packit ae235b
      offset = 0;
Packit ae235b
Packit ae235b
      while (offset < buffer_len)
Packit ae235b
        {
Packit ae235b
          struct inotify_event *kevent = (struct inotify_event *) (buffer + offset);
Packit ae235b
          ik_event_t *event;
Packit ae235b
Packit ae235b
          event = ik_event_new (kevent, now);
Packit ae235b
Packit ae235b
          offset += sizeof (struct inotify_event) + event->len;
Packit ae235b
Packit ae235b
          if (event->mask & IN_MOVED_TO)
Packit ae235b
            {
Packit ae235b
              ik_event_t *pair;
Packit ae235b
Packit ae235b
              pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
Packit ae235b
              if (pair != NULL)
Packit ae235b
                {
Packit ae235b
                  g_assert (!pair->pair);
Packit ae235b
Packit ae235b
                  g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
Packit ae235b
                  event->is_second_in_pair = TRUE;
Packit ae235b
                  event->pair = pair;
Packit ae235b
                  pair->pair = event;
Packit ae235b
                  continue;
Packit ae235b
                }
Packit ae235b
Packit ae235b
              interesting = TRUE;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          else if (event->mask & IN_MOVED_FROM)
Packit ae235b
            {
Packit ae235b
              gboolean new;
Packit ae235b
Packit ae235b
              new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
Packit ae235b
              if G_UNLIKELY (!new)
Packit ae235b
                g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event->cookie);
Packit ae235b
Packit ae235b
              interesting = TRUE;
Packit ae235b
            }
Packit ae235b
Packit ae235b
          g_queue_push_tail (&iks->queue, event);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (buffer_len == 0)
Packit ae235b
        {
Packit ae235b
          /* We can end up reading nothing if we arrived here due to a
Packit ae235b
           * boredom timer but the stream of events stopped meanwhile.
Packit ae235b
           *
Packit ae235b
           * In that case, we need to switch back to polling the file
Packit ae235b
           * descriptor in the usual way.
Packit ae235b
           */
Packit ae235b
          g_assert (iks->is_bored);
Packit ae235b
          interesting = TRUE;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      if (buffer != stack_buffer)
Packit ae235b
        g_free (buffer);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  while (ik_source_can_dispatch_now (iks, now))
Packit ae235b
    {
Packit ae235b
      ik_event_t *event;
Packit ae235b
Packit ae235b
      /* callback will free the event */
Packit ae235b
      event = g_queue_pop_head (&iks->queue);
Packit ae235b
Packit ae235b
      if (event->mask & IN_MOVED_FROM && !event->pair)
Packit ae235b
        g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
Packit ae235b
Packit ae235b
      G_LOCK (inotify_lock);
Packit ae235b
Packit ae235b
      interesting |= (* user_callback) (event);
Packit ae235b
Packit ae235b
      G_UNLOCK (inotify_lock);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* The queue gets blocked iff we have unmatched moves */
Packit ae235b
  g_assert ((iks->queue.length > 0) == (g_hash_table_size (iks->unmatched_moves) > 0));
Packit ae235b
Packit ae235b
  /* Here's where we decide what will wake us up next.
Packit ae235b
   *
Packit ae235b
   * If the last event was interesting then we will wake up on the fd or
Packit ae235b
   * when the timeout is reached on an unpaired move (if any).
Packit ae235b
   *
Packit ae235b
   * If the last event was uninteresting then we will wake up after the
Packit ae235b
   * shorter of the boredom sleep or any timeout for a unpaired move.
Packit ae235b
   */
Packit ae235b
  if (interesting)
Packit ae235b
    {
Packit ae235b
      if (iks->is_bored)
Packit ae235b
        {
Packit ae235b
          g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
Packit ae235b
          iks->is_bored = FALSE;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_source_set_ready_time (source, ik_source_get_dispatch_time (iks));
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      guint64 dispatch_time = ik_source_get_dispatch_time (iks);
Packit ae235b
      guint64 boredom_time = now + BOREDOM_SLEEP_TIME;
Packit ae235b
Packit ae235b
      if (!iks->is_bored)
Packit ae235b
        {
Packit ae235b
          g_source_modify_unix_fd (source, iks->fd_tag, 0);
Packit ae235b
          iks->is_bored = TRUE;
Packit ae235b
        }
Packit ae235b
Packit ae235b
      g_source_set_ready_time (source, MIN (dispatch_time, boredom_time));
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static InotifyKernelSource *
Packit ae235b
ik_source_new (gboolean (* callback) (ik_event_t *event))
Packit ae235b
{
Packit ae235b
  static GSourceFuncs source_funcs = {
Packit ae235b
    NULL, NULL,
Packit ae235b
    ik_source_dispatch
Packit ae235b
    /* should have a finalize, but it will never happen */
Packit ae235b
  };
Packit ae235b
  InotifyKernelSource *iks;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  source = g_source_new (&source_funcs, sizeof (InotifyKernelSource));
Packit ae235b
  iks = (InotifyKernelSource *) source;
Packit ae235b
Packit ae235b
  g_source_set_name (source, "inotify kernel source");
Packit ae235b
Packit ae235b
  iks->unmatched_moves = g_hash_table_new (NULL, NULL);
Packit ae235b
  iks->fd = inotify_init1 (IN_CLOEXEC);
Packit ae235b
Packit ae235b
  if (iks->fd < 0)
Packit ae235b
    iks->fd = inotify_init ();
Packit ae235b
Packit ae235b
  if (iks->fd >= 0)
Packit ae235b
    {
Packit ae235b
      GError *error = NULL;
Packit ae235b
Packit ae235b
      g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
Packit ae235b
      g_assert_no_error (error);
Packit ae235b
Packit ae235b
      iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_source_set_callback (source, (GSourceFunc) callback, NULL, NULL);
Packit ae235b
Packit ae235b
  g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
Packit ae235b
Packit ae235b
  return iks;
Packit ae235b
}
Packit ae235b
Packit ae235b
gboolean
Packit ae235b
_ik_startup (gboolean (*cb)(ik_event_t *event))
Packit ae235b
{
Packit ae235b
  if (g_once_init_enter (&inotify_source))
Packit ae235b
    g_once_init_leave (&inotify_source, ik_source_new (cb));
Packit ae235b
Packit ae235b
  return inotify_source->fd >= 0;
Packit ae235b
}
Packit ae235b
Packit ae235b
gint32
Packit ae235b
_ik_watch (const char *path,
Packit ae235b
           guint32     mask,
Packit ae235b
           int        *err)
Packit ae235b
{
Packit ae235b
  gint32 wd = -1;
Packit ae235b
Packit ae235b
  g_assert (path != NULL);
Packit ae235b
  g_assert (inotify_source && inotify_source->fd >= 0);
Packit ae235b
Packit ae235b
  wd = inotify_add_watch (inotify_source->fd, path, mask);
Packit ae235b
Packit ae235b
  if (wd < 0)
Packit ae235b
    {
Packit ae235b
      int e = errno;
Packit ae235b
      /* FIXME: debug msg failed to add watch */
Packit ae235b
      if (err)
Packit ae235b
        *err = e;
Packit ae235b
      return wd;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_assert (wd >= 0);
Packit ae235b
  return wd;
Packit ae235b
}
Packit ae235b
Packit ae235b
int
Packit ae235b
_ik_ignore (const char *path,
Packit ae235b
            gint32      wd)
Packit ae235b
{
Packit ae235b
  g_assert (wd >= 0);
Packit ae235b
  g_assert (inotify_source && inotify_source->fd >= 0);
Packit ae235b
Packit ae235b
  if (inotify_rm_watch (inotify_source->fd, wd) < 0)
Packit ae235b
    {
Packit ae235b
      /* int e = errno; */
Packit ae235b
      /* failed to rm watch */
Packit ae235b
      return -1;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return 0;
Packit ae235b
}