Import Upstream version 2.72.4
This commit is contained in:
commit
4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions
114
gio/inotify/ginotifyfilemonitor.c
Normal file
114
gio/inotify/ginotifyfilemonitor.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
* Copyright (C) 2007 Sebastian Dröge.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Alexander Larsson <alexl@redhat.com>
|
||||
* John McCutchan <john@johnmccutchan.com>
|
||||
* Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ginotifyfilemonitor.h"
|
||||
#include <gio/giomodule.h>
|
||||
|
||||
#define USE_INOTIFY 1
|
||||
#include "inotify-helper.h"
|
||||
|
||||
struct _GInotifyFileMonitor
|
||||
{
|
||||
GLocalFileMonitor parent_instance;
|
||||
|
||||
inotify_sub *sub;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GInotifyFileMonitor, g_inotify_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
|
||||
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
g_define_type_id, "inotify", 20))
|
||||
|
||||
static gboolean
|
||||
g_inotify_file_monitor_is_supported (void)
|
||||
{
|
||||
return _ih_startup ();
|
||||
}
|
||||
|
||||
static void
|
||||
g_inotify_file_monitor_start (GLocalFileMonitor *local_monitor,
|
||||
const gchar *dirname,
|
||||
const gchar *basename,
|
||||
const gchar *filename,
|
||||
GFileMonitorSource *source)
|
||||
{
|
||||
GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (local_monitor);
|
||||
gboolean success G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
|
||||
|
||||
/* should already have been called, from is_supported() */
|
||||
success = _ih_startup ();
|
||||
g_assert (success);
|
||||
|
||||
inotify_monitor->sub = _ih_sub_new (dirname, basename, filename, source);
|
||||
_ih_sub_add (inotify_monitor->sub);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_inotify_file_monitor_cancel (GFileMonitor *monitor)
|
||||
{
|
||||
GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (monitor);
|
||||
|
||||
if (inotify_monitor->sub)
|
||||
{
|
||||
_ih_sub_cancel (inotify_monitor->sub);
|
||||
_ih_sub_free (inotify_monitor->sub);
|
||||
inotify_monitor->sub = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_inotify_file_monitor_finalize (GObject *object)
|
||||
{
|
||||
#ifndef G_DISABLE_ASSERT
|
||||
GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (object);
|
||||
#endif
|
||||
|
||||
/* must surely have been cancelled already */
|
||||
g_assert (!inotify_monitor->sub);
|
||||
|
||||
G_OBJECT_CLASS (g_inotify_file_monitor_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_inotify_file_monitor_init (GInotifyFileMonitor* monitor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
g_inotify_file_monitor_class_init (GInotifyFileMonitorClass* klass)
|
||||
{
|
||||
GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
|
||||
GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
|
||||
GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass);
|
||||
|
||||
local_file_monitor_class->is_supported = g_inotify_file_monitor_is_supported;
|
||||
local_file_monitor_class->start = g_inotify_file_monitor_start;
|
||||
local_file_monitor_class->mount_notify = TRUE;
|
||||
file_monitor_class->cancel = g_inotify_file_monitor_cancel;
|
||||
|
||||
gobject_class->finalize = g_inotify_file_monitor_finalize;
|
||||
}
|
||||
52
gio/inotify/ginotifyfilemonitor.h
Normal file
52
gio/inotify/ginotifyfilemonitor.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
* Copyright (C) 2007 Sebastian Dröge.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Alexander Larsson <alexl@redhat.com>
|
||||
* John McCutchan <john@johnmccutchan.com>
|
||||
* Sebastian Dröge <slomo@circular-chaos.org>
|
||||
*/
|
||||
|
||||
#ifndef __G_INOTIFY_FILE_MONITOR_H__
|
||||
#define __G_INOTIFY_FILE_MONITOR_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <string.h>
|
||||
#include <gio/gfilemonitor.h>
|
||||
#include <gio/glocalfilemonitor.h>
|
||||
#include <gio/giomodule.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_INOTIFY_FILE_MONITOR (g_inotify_file_monitor_get_type ())
|
||||
#define G_INOTIFY_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitor))
|
||||
#define G_INOTIFY_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitorClass))
|
||||
#define G_IS_INOTIFY_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INOTIFY_FILE_MONITOR))
|
||||
#define G_IS_INOTIFY_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_INOTIFY_FILE_MONITOR))
|
||||
|
||||
typedef struct _GInotifyFileMonitor GInotifyFileMonitor;
|
||||
typedef struct _GInotifyFileMonitorClass GInotifyFileMonitorClass;
|
||||
|
||||
struct _GInotifyFileMonitorClass {
|
||||
GLocalFileMonitorClass parent_class;
|
||||
};
|
||||
|
||||
GType g_inotify_file_monitor_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_INOTIFY_FILE_MONITOR_H__ */
|
||||
290
gio/inotify/inotify-helper.c
Normal file
290
gio/inotify/inotify-helper.c
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
|
||||
|
||||
/* inotify-helper.c - GVFS Monitor based on inotify.
|
||||
|
||||
Copyright (C) 2007 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
/* Just include the local header to stop all the pain */
|
||||
#include <sys/inotify.h>
|
||||
#include <gio/glocalfilemonitor.h>
|
||||
#include <gio/gfile.h>
|
||||
#include "inotify-helper.h"
|
||||
#include "inotify-missing.h"
|
||||
#include "inotify-path.h"
|
||||
|
||||
static gboolean ih_debug_enabled = FALSE;
|
||||
#define IH_W if (ih_debug_enabled) g_warning
|
||||
|
||||
static gboolean ih_event_callback (ik_event_t *event,
|
||||
inotify_sub *sub,
|
||||
gboolean file_event);
|
||||
static void ih_not_missing_callback (inotify_sub *sub);
|
||||
|
||||
/* We share this lock with inotify-kernel.c and inotify-missing.c
|
||||
*
|
||||
* inotify-kernel.c takes the lock when it reads events from
|
||||
* the kernel and when it processes those events
|
||||
*
|
||||
* inotify-missing.c takes the lock when it is scanning the missing
|
||||
* list.
|
||||
*
|
||||
* We take the lock in all public functions
|
||||
*/
|
||||
G_LOCK_DEFINE (inotify_lock);
|
||||
|
||||
static GFileMonitorEvent ih_mask_to_EventFlags (guint32 mask);
|
||||
|
||||
/**
|
||||
* _ih_startup:
|
||||
*
|
||||
* Initializes the inotify backend. This must be called before
|
||||
* any other functions in this module.
|
||||
*
|
||||
* Returns: #TRUE if initialization succeeded, #FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
_ih_startup (void)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
static gboolean result = FALSE;
|
||||
|
||||
G_LOCK (inotify_lock);
|
||||
|
||||
if (initialized == TRUE)
|
||||
{
|
||||
G_UNLOCK (inotify_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = _ip_startup (ih_event_callback);
|
||||
if (!result)
|
||||
{
|
||||
G_UNLOCK (inotify_lock);
|
||||
return FALSE;
|
||||
}
|
||||
_im_startup (ih_not_missing_callback);
|
||||
|
||||
IH_W ("started gvfs inotify backend\n");
|
||||
|
||||
initialized = TRUE;
|
||||
|
||||
G_UNLOCK (inotify_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a subscription to be monitored.
|
||||
*/
|
||||
gboolean
|
||||
_ih_sub_add (inotify_sub *sub)
|
||||
{
|
||||
G_LOCK (inotify_lock);
|
||||
|
||||
if (!_ip_start_watching (sub))
|
||||
_im_add (sub);
|
||||
|
||||
G_UNLOCK (inotify_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancels a subscription which was being monitored.
|
||||
*/
|
||||
gboolean
|
||||
_ih_sub_cancel (inotify_sub *sub)
|
||||
{
|
||||
G_LOCK (inotify_lock);
|
||||
|
||||
if (!sub->cancelled)
|
||||
{
|
||||
IH_W ("cancelling %s\n", sub->dirname);
|
||||
sub->cancelled = TRUE;
|
||||
_im_rm (sub);
|
||||
_ip_stop_watching (sub);
|
||||
}
|
||||
|
||||
G_UNLOCK (inotify_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char *
|
||||
_ih_fullpath_from_event (ik_event_t *event,
|
||||
const char *dirname,
|
||||
const char *filename)
|
||||
{
|
||||
char *fullpath;
|
||||
|
||||
if (filename)
|
||||
fullpath = g_strdup_printf ("%s/%s", dirname, filename);
|
||||
else if (event->name)
|
||||
fullpath = g_strdup_printf ("%s/%s", dirname, event->name);
|
||||
else
|
||||
fullpath = g_strdup_printf ("%s/", dirname);
|
||||
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ih_event_callback (ik_event_t *event,
|
||||
inotify_sub *sub,
|
||||
gboolean file_event)
|
||||
{
|
||||
gboolean interesting;
|
||||
GFileMonitorEvent event_flags;
|
||||
|
||||
event_flags = ih_mask_to_EventFlags (event->mask);
|
||||
|
||||
if (event->mask & IN_MOVE)
|
||||
{
|
||||
/* We either have a rename (in the same directory) or a move
|
||||
* (between different directories).
|
||||
*/
|
||||
if (event->pair && event->pair->wd == event->wd)
|
||||
{
|
||||
/* this is a rename */
|
||||
interesting = g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_RENAMED,
|
||||
event->name, event->pair->name, NULL, event->timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
GFile *other;
|
||||
|
||||
if (event->pair)
|
||||
{
|
||||
const char *parent_dir;
|
||||
gchar *fullpath;
|
||||
|
||||
parent_dir = _ip_get_path_for_wd (event->pair->wd);
|
||||
fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL);
|
||||
other = g_file_new_for_path (fullpath);
|
||||
g_free (fullpath);
|
||||
}
|
||||
else
|
||||
other = NULL;
|
||||
|
||||
/* This is either an incoming or outgoing move. Since we checked the
|
||||
* event->mask above, it should have converted to a #GFileMonitorEvent
|
||||
* properly. If not, the assumption we have made about event->mask
|
||||
* only ever having a single bit set (apart from IN_ISDIR) is false.
|
||||
* The kernel documentation is lacking here. */
|
||||
g_assert ((int) event_flags != -1);
|
||||
interesting = g_file_monitor_source_handle_event (sub->user_data, event_flags,
|
||||
event->name, NULL, other, event->timestamp);
|
||||
|
||||
if (other)
|
||||
g_object_unref (other);
|
||||
}
|
||||
}
|
||||
else if ((int) event_flags != -1)
|
||||
/* unpaired event -- no 'other' field */
|
||||
interesting = g_file_monitor_source_handle_event (sub->user_data, event_flags,
|
||||
event->name, NULL, NULL, event->timestamp);
|
||||
else
|
||||
interesting = FALSE;
|
||||
|
||||
if (event->mask & IN_CREATE)
|
||||
{
|
||||
const gchar *parent_dir;
|
||||
gchar *fullname;
|
||||
struct stat buf;
|
||||
gint s;
|
||||
|
||||
/* The kernel reports IN_CREATE for two types of events:
|
||||
*
|
||||
* - creat(), in which case IN_CLOSE_WRITE will come soon; or
|
||||
* - link(), mkdir(), mknod(), etc., in which case it won't
|
||||
*
|
||||
* We can attempt to detect the second case and send the
|
||||
* CHANGES_DONE immediately so that the user isn't left waiting.
|
||||
*
|
||||
* The detection for link() is not 100% reliable since the link
|
||||
* count could be 1 if the original link was deleted or if
|
||||
* O_TMPFILE was being used, but in that case the virtual
|
||||
* CHANGES_DONE will be emitted to close the loop.
|
||||
*/
|
||||
|
||||
parent_dir = _ip_get_path_for_wd (event->wd);
|
||||
fullname = _ih_fullpath_from_event (event, parent_dir, NULL);
|
||||
s = stat (fullname, &buf);
|
||||
g_free (fullname);
|
||||
|
||||
/* if it doesn't look like the result of creat()... */
|
||||
if (s != 0 || !S_ISREG (buf.st_mode) || buf.st_nlink != 1)
|
||||
g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT,
|
||||
event->name, NULL, NULL, event->timestamp);
|
||||
}
|
||||
|
||||
return interesting;
|
||||
}
|
||||
|
||||
static void
|
||||
ih_not_missing_callback (inotify_sub *sub)
|
||||
{
|
||||
gint now = g_get_monotonic_time ();
|
||||
|
||||
g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CREATED,
|
||||
sub->filename, NULL, NULL, now);
|
||||
g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT,
|
||||
sub->filename, NULL, NULL, now);
|
||||
}
|
||||
|
||||
/* Transforms a inotify event to a GVFS event. */
|
||||
static GFileMonitorEvent
|
||||
ih_mask_to_EventFlags (guint32 mask)
|
||||
{
|
||||
mask &= ~IN_ISDIR;
|
||||
switch (mask)
|
||||
{
|
||||
case IN_MODIFY:
|
||||
return G_FILE_MONITOR_EVENT_CHANGED;
|
||||
case IN_CLOSE_WRITE:
|
||||
return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT;
|
||||
case IN_ATTRIB:
|
||||
return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
|
||||
case IN_MOVE_SELF:
|
||||
case IN_DELETE:
|
||||
case IN_DELETE_SELF:
|
||||
return G_FILE_MONITOR_EVENT_DELETED;
|
||||
case IN_CREATE:
|
||||
return G_FILE_MONITOR_EVENT_CREATED;
|
||||
case IN_MOVED_FROM:
|
||||
return G_FILE_MONITOR_EVENT_MOVED_OUT;
|
||||
case IN_MOVED_TO:
|
||||
return G_FILE_MONITOR_EVENT_MOVED_IN;
|
||||
case IN_UNMOUNT:
|
||||
return G_FILE_MONITOR_EVENT_UNMOUNTED;
|
||||
case IN_Q_OVERFLOW:
|
||||
case IN_OPEN:
|
||||
case IN_CLOSE_NOWRITE:
|
||||
case IN_ACCESS:
|
||||
case IN_IGNORED:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
31
gio/inotify/inotify-helper.h
Normal file
31
gio/inotify/inotify-helper.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* inotify-helper.h - GVFS Directory Monitor using inotify
|
||||
|
||||
Copyright (C) 2007 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __INOTIFY_HELPER_H
|
||||
#define __INOTIFY_HELPER_H
|
||||
|
||||
#include "inotify-sub.h"
|
||||
|
||||
gboolean _ih_startup (void);
|
||||
gboolean _ih_sub_add (inotify_sub *sub);
|
||||
gboolean _ih_sub_cancel (inotify_sub *sub);
|
||||
|
||||
#endif /* __INOTIFY_HELPER_H */
|
||||
458
gio/inotify/inotify-kernel.c
Normal file
458
gio/inotify/inotify-kernel.c
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
Copyright (C) 2005 John McCutchan
|
||||
Copyright © 2015 Canonical Limited
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:
|
||||
Ryan Lortie <desrt@desrt.ca>
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include "inotify-kernel.h"
|
||||
#include <sys/inotify.h>
|
||||
#ifdef HAVE_SYS_FILIO_H
|
||||
#include <sys/filio.h>
|
||||
#endif
|
||||
#include <glib/glib-unix.h>
|
||||
|
||||
#include "glib-private.h"
|
||||
|
||||
/* From inotify(7) */
|
||||
#define MAX_EVENT_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1)
|
||||
|
||||
/* Amount of time to sleep on receipt of uninteresting events */
|
||||
#define BOREDOM_SLEEP_TIME (100 * G_TIME_SPAN_MILLISECOND)
|
||||
|
||||
/* Define limits on the maximum amount of time and maximum amount of
|
||||
* interceding events between FROM/TO that can be merged.
|
||||
*/
|
||||
#define MOVE_PAIR_DELAY (10 * G_TIME_SPAN_MILLISECOND)
|
||||
#define MOVE_PAIR_DISTANCE (100)
|
||||
|
||||
/* We use the lock from inotify-helper.c
|
||||
*
|
||||
* We only have to take it on our read callback.
|
||||
*
|
||||
* The rest of locking is taken care of in inotify-helper.c
|
||||
*/
|
||||
G_LOCK_EXTERN (inotify_lock);
|
||||
|
||||
static ik_event_t *
|
||||
ik_event_new (struct inotify_event *kevent,
|
||||
gint64 now)
|
||||
{
|
||||
ik_event_t *event = g_new0 (ik_event_t, 1);
|
||||
|
||||
event->wd = kevent->wd;
|
||||
event->mask = kevent->mask;
|
||||
event->cookie = kevent->cookie;
|
||||
event->len = kevent->len;
|
||||
event->timestamp = now;
|
||||
if (event->len)
|
||||
event->name = g_strdup (kevent->name);
|
||||
else
|
||||
event->name = NULL;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
_ik_event_free (ik_event_t *event)
|
||||
{
|
||||
if (event->pair)
|
||||
{
|
||||
event->pair->pair = NULL;
|
||||
_ik_event_free (event->pair);
|
||||
}
|
||||
|
||||
g_free (event->name);
|
||||
g_free (event);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
|
||||
GQueue queue;
|
||||
gpointer fd_tag;
|
||||
gint fd;
|
||||
|
||||
GHashTable *unmatched_moves;
|
||||
gboolean is_bored;
|
||||
} InotifyKernelSource;
|
||||
|
||||
static InotifyKernelSource *inotify_source;
|
||||
|
||||
static gint64
|
||||
ik_source_get_dispatch_time (InotifyKernelSource *iks)
|
||||
{
|
||||
ik_event_t *head;
|
||||
|
||||
head = g_queue_peek_head (&iks->queue);
|
||||
|
||||
/* nothing in the queue: not ready */
|
||||
if (!head)
|
||||
return -1;
|
||||
|
||||
/* if it's not an unpaired move, it is ready now */
|
||||
if (~head->mask & IN_MOVED_FROM || head->pair)
|
||||
return 0;
|
||||
|
||||
/* if the queue is too long then it's ready now */
|
||||
if (iks->queue.length > MOVE_PAIR_DISTANCE)
|
||||
return 0;
|
||||
|
||||
/* otherwise, it's ready after the delay */
|
||||
return head->timestamp + MOVE_PAIR_DELAY;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ik_source_can_dispatch_now (InotifyKernelSource *iks,
|
||||
gint64 now)
|
||||
{
|
||||
gint64 dispatch_time;
|
||||
|
||||
dispatch_time = ik_source_get_dispatch_time (iks);
|
||||
|
||||
return 0 <= dispatch_time && dispatch_time <= now;
|
||||
}
|
||||
|
||||
static gsize
|
||||
ik_source_read_some_events (InotifyKernelSource *iks,
|
||||
gchar *buffer,
|
||||
gsize buffer_len)
|
||||
{
|
||||
gssize result;
|
||||
int errsv;
|
||||
|
||||
again:
|
||||
result = read (iks->fd, buffer, buffer_len);
|
||||
errsv = errno;
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
if (errsv == EINTR)
|
||||
goto again;
|
||||
|
||||
if (errsv == EAGAIN)
|
||||
return 0;
|
||||
|
||||
g_error ("inotify read(): %s", g_strerror (errsv));
|
||||
}
|
||||
else if (result == 0)
|
||||
g_error ("inotify unexpectedly hit eof");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
ik_source_read_all_the_events (InotifyKernelSource *iks,
|
||||
gchar *buffer,
|
||||
gsize buffer_len,
|
||||
gsize *length_out)
|
||||
{
|
||||
gsize n_read;
|
||||
|
||||
n_read = ik_source_read_some_events (iks, buffer, buffer_len);
|
||||
|
||||
/* Check if we might have gotten another event if we had passed in a
|
||||
* bigger buffer...
|
||||
*/
|
||||
if (n_read + MAX_EVENT_SIZE > buffer_len)
|
||||
{
|
||||
gchar *new_buffer;
|
||||
guint n_readable;
|
||||
gint result;
|
||||
int errsv;
|
||||
|
||||
/* figure out how many more bytes there are to read */
|
||||
result = ioctl (iks->fd, FIONREAD, &n_readable);
|
||||
errsv = errno;
|
||||
if (result != 0)
|
||||
g_error ("inotify ioctl(FIONREAD): %s", g_strerror (errsv));
|
||||
|
||||
if (n_readable != 0)
|
||||
{
|
||||
/* there is in fact more data. allocate a new buffer, copy
|
||||
* the existing data, and then append the remaining.
|
||||
*/
|
||||
new_buffer = g_malloc (n_read + n_readable);
|
||||
memcpy (new_buffer, buffer, n_read);
|
||||
n_read += ik_source_read_some_events (iks, new_buffer + n_read, n_readable);
|
||||
|
||||
buffer = new_buffer;
|
||||
|
||||
/* There may be new events in the buffer that were added after
|
||||
* the FIONREAD was performed, but we can't risk getting into
|
||||
* a loop. We'll get them next time.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
*length_out = n_read;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ik_source_dispatch (GSource *source,
|
||||
GSourceFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
InotifyKernelSource *iks = (InotifyKernelSource *) source;
|
||||
gboolean (*user_callback) (ik_event_t *event) = (void *) func;
|
||||
gboolean interesting = FALSE;
|
||||
gint64 now;
|
||||
|
||||
now = g_source_get_time (source);
|
||||
|
||||
if (iks->is_bored || g_source_query_unix_fd (source, iks->fd_tag))
|
||||
{
|
||||
gchar stack_buffer[4096];
|
||||
gsize buffer_len;
|
||||
gchar *buffer;
|
||||
gsize offset;
|
||||
|
||||
/* We want to read all of the available events.
|
||||
*
|
||||
* We need to do it in a finite number of steps so that we don't
|
||||
* get caught in a loop of read() with another process
|
||||
* continuously adding events each time we drain them.
|
||||
*
|
||||
* In the normal case we will have only a few events in the queue,
|
||||
* so start out by reading into a small stack-allocated buffer.
|
||||
* Even though we're on a fresh stack frame, there is no need to
|
||||
* pointlessly blow up with the size of the worker thread stack
|
||||
* with a huge buffer here.
|
||||
*
|
||||
* If the result is large enough to cause us to suspect that
|
||||
* another event may be pending then we allocate a buffer on the
|
||||
* heap that can hold all of the events and read (once!) into that
|
||||
* buffer.
|
||||
*/
|
||||
buffer = ik_source_read_all_the_events (iks, stack_buffer, sizeof stack_buffer, &buffer_len);
|
||||
|
||||
offset = 0;
|
||||
|
||||
while (offset < buffer_len)
|
||||
{
|
||||
struct inotify_event *kevent = (struct inotify_event *) (buffer + offset);
|
||||
ik_event_t *event;
|
||||
|
||||
event = ik_event_new (kevent, now);
|
||||
|
||||
offset += sizeof (struct inotify_event) + event->len;
|
||||
|
||||
if (event->mask & IN_MOVED_TO)
|
||||
{
|
||||
ik_event_t *pair;
|
||||
|
||||
pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
|
||||
if (pair != NULL)
|
||||
{
|
||||
g_assert (!pair->pair);
|
||||
|
||||
g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
|
||||
event->is_second_in_pair = TRUE;
|
||||
event->pair = pair;
|
||||
pair->pair = event;
|
||||
continue;
|
||||
}
|
||||
|
||||
interesting = TRUE;
|
||||
}
|
||||
|
||||
else if (event->mask & IN_MOVED_FROM)
|
||||
{
|
||||
gboolean new;
|
||||
|
||||
new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
|
||||
if G_UNLIKELY (!new)
|
||||
g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event->cookie);
|
||||
|
||||
interesting = TRUE;
|
||||
}
|
||||
|
||||
g_queue_push_tail (&iks->queue, event);
|
||||
}
|
||||
|
||||
if (buffer_len == 0)
|
||||
{
|
||||
/* We can end up reading nothing if we arrived here due to a
|
||||
* boredom timer but the stream of events stopped meanwhile.
|
||||
*
|
||||
* In that case, we need to switch back to polling the file
|
||||
* descriptor in the usual way.
|
||||
*/
|
||||
g_assert (iks->is_bored);
|
||||
interesting = TRUE;
|
||||
}
|
||||
|
||||
if (buffer != stack_buffer)
|
||||
g_free (buffer);
|
||||
}
|
||||
|
||||
while (ik_source_can_dispatch_now (iks, now))
|
||||
{
|
||||
ik_event_t *event;
|
||||
|
||||
/* callback will free the event */
|
||||
event = g_queue_pop_head (&iks->queue);
|
||||
|
||||
if (event->mask & IN_MOVED_FROM && !event->pair)
|
||||
g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
|
||||
|
||||
G_LOCK (inotify_lock);
|
||||
|
||||
interesting |= (* user_callback) (event);
|
||||
|
||||
G_UNLOCK (inotify_lock);
|
||||
}
|
||||
|
||||
/* The queue gets blocked iff we have unmatched moves */
|
||||
g_assert ((iks->queue.length > 0) == (g_hash_table_size (iks->unmatched_moves) > 0));
|
||||
|
||||
/* Here's where we decide what will wake us up next.
|
||||
*
|
||||
* If the last event was interesting then we will wake up on the fd or
|
||||
* when the timeout is reached on an unpaired move (if any).
|
||||
*
|
||||
* If the last event was uninteresting then we will wake up after the
|
||||
* shorter of the boredom sleep or any timeout for an unpaired move.
|
||||
*/
|
||||
if (interesting)
|
||||
{
|
||||
if (iks->is_bored)
|
||||
{
|
||||
g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
|
||||
iks->is_bored = FALSE;
|
||||
}
|
||||
|
||||
g_source_set_ready_time (source, ik_source_get_dispatch_time (iks));
|
||||
}
|
||||
else
|
||||
{
|
||||
guint64 dispatch_time = ik_source_get_dispatch_time (iks);
|
||||
guint64 boredom_time = now + BOREDOM_SLEEP_TIME;
|
||||
|
||||
if (!iks->is_bored)
|
||||
{
|
||||
g_source_modify_unix_fd (source, iks->fd_tag, 0);
|
||||
iks->is_bored = TRUE;
|
||||
}
|
||||
|
||||
g_source_set_ready_time (source, MIN (dispatch_time, boredom_time));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static InotifyKernelSource *
|
||||
ik_source_new (gboolean (* callback) (ik_event_t *event))
|
||||
{
|
||||
static GSourceFuncs source_funcs = {
|
||||
NULL, NULL,
|
||||
ik_source_dispatch,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
InotifyKernelSource *iks;
|
||||
GSource *source;
|
||||
|
||||
source = g_source_new (&source_funcs, sizeof (InotifyKernelSource));
|
||||
iks = (InotifyKernelSource *) source;
|
||||
|
||||
g_source_set_static_name (source, "inotify kernel source");
|
||||
|
||||
iks->unmatched_moves = g_hash_table_new (NULL, NULL);
|
||||
iks->fd = inotify_init1 (IN_CLOEXEC);
|
||||
|
||||
if (iks->fd < 0)
|
||||
iks->fd = inotify_init ();
|
||||
|
||||
if (iks->fd >= 0)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
|
||||
}
|
||||
|
||||
g_source_set_callback (source, (GSourceFunc) callback, NULL, NULL);
|
||||
|
||||
g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
|
||||
|
||||
return iks;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ik_startup (gboolean (*cb)(ik_event_t *event))
|
||||
{
|
||||
if (g_once_init_enter (&inotify_source))
|
||||
g_once_init_leave (&inotify_source, ik_source_new (cb));
|
||||
|
||||
return inotify_source->fd >= 0;
|
||||
}
|
||||
|
||||
gint32
|
||||
_ik_watch (const char *path,
|
||||
guint32 mask,
|
||||
int *err)
|
||||
{
|
||||
gint32 wd = -1;
|
||||
|
||||
g_assert (path != NULL);
|
||||
g_assert (inotify_source && inotify_source->fd >= 0);
|
||||
|
||||
wd = inotify_add_watch (inotify_source->fd, path, mask);
|
||||
|
||||
if (wd < 0)
|
||||
{
|
||||
int e = errno;
|
||||
/* FIXME: debug msg failed to add watch */
|
||||
if (err)
|
||||
*err = e;
|
||||
return wd;
|
||||
}
|
||||
|
||||
g_assert (wd >= 0);
|
||||
return wd;
|
||||
}
|
||||
|
||||
int
|
||||
_ik_ignore (const char *path,
|
||||
gint32 wd)
|
||||
{
|
||||
g_assert (wd >= 0);
|
||||
g_assert (inotify_source && inotify_source->fd >= 0);
|
||||
|
||||
if (inotify_rm_watch (inotify_source->fd, wd) < 0)
|
||||
{
|
||||
/* int e = errno; */
|
||||
/* failed to rm watch */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
62
gio/inotify/inotify-kernel.h
Normal file
62
gio/inotify/inotify-kernel.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright (C) 2005 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:.
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#ifndef __INOTIFY_KERNEL_H
|
||||
#define __INOTIFY_KERNEL_H
|
||||
|
||||
typedef struct ik_event_s {
|
||||
gint32 wd;
|
||||
guint32 mask;
|
||||
guint32 original_mask;
|
||||
guint32 cookie;
|
||||
guint32 len;
|
||||
char * name;
|
||||
/* TRUE if this event is the last element of a pair
|
||||
* (e.g., MOVE_TO in a pair of MOVE_FROM, MOVE_TO events) */
|
||||
gboolean is_second_in_pair;
|
||||
/* if event1 and event2 are two paired events
|
||||
* (e.g., MOVE_FROM and MOVE_TO events related to the same file move),
|
||||
* then event1->pair == event2 and event2->pair == NULL.
|
||||
* It will result also in event1->pair->is_second_in_pair == TRUE */
|
||||
struct ik_event_s *pair;
|
||||
gint64 timestamp; /* monotonic time that this was created */
|
||||
} ik_event_t;
|
||||
|
||||
gboolean _ik_startup (gboolean (*cb) (ik_event_t *event));
|
||||
|
||||
ik_event_t *_ik_event_new_dummy (const char *name,
|
||||
gint32 wd,
|
||||
guint32 mask);
|
||||
void _ik_event_free (ik_event_t *event);
|
||||
|
||||
gint32 _ik_watch (const char *path,
|
||||
guint32 mask,
|
||||
int *err);
|
||||
int _ik_ignore (const char *path,
|
||||
gint32 wd);
|
||||
|
||||
|
||||
/* The miss count will probably be enflated */
|
||||
void _ik_move_stats (guint32 *matches,
|
||||
guint32 *misses);
|
||||
const char *_ik_mask_to_string (guint32 mask);
|
||||
|
||||
|
||||
#endif
|
||||
156
gio/inotify/inotify-missing.c
Normal file
156
gio/inotify/inotify-missing.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
|
||||
|
||||
/* inotify-missing.c - GVFS Monitor based on inotify.
|
||||
|
||||
Copyright (C) 2005 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <glib.h>
|
||||
#include "inotify-missing.h"
|
||||
#include "inotify-path.h"
|
||||
#include "glib-private.h"
|
||||
|
||||
#define SCAN_MISSING_TIME 4 /* 1/4 Hz */
|
||||
|
||||
static gboolean im_debug_enabled = FALSE;
|
||||
#define IM_W if (im_debug_enabled) g_warning
|
||||
|
||||
/* We put inotify_sub's that are missing on this list */
|
||||
static GList *missing_sub_list = NULL;
|
||||
static gboolean im_scan_missing (gpointer user_data);
|
||||
static gboolean scan_missing_running = FALSE;
|
||||
static void (*missing_cb)(inotify_sub *sub) = NULL;
|
||||
|
||||
G_LOCK_EXTERN (inotify_lock);
|
||||
|
||||
/* inotify_lock must be held before calling */
|
||||
void
|
||||
_im_startup (void (*callback)(inotify_sub *sub))
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
missing_cb = callback;
|
||||
initialized = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* inotify_lock must be held before calling */
|
||||
void
|
||||
_im_add (inotify_sub *sub)
|
||||
{
|
||||
if (g_list_find (missing_sub_list, sub))
|
||||
{
|
||||
IM_W ("asked to add %s to missing list but it's already on the list!\n", sub->dirname);
|
||||
return;
|
||||
}
|
||||
|
||||
IM_W ("adding %s to missing list\n", sub->dirname);
|
||||
missing_sub_list = g_list_prepend (missing_sub_list, sub);
|
||||
|
||||
/* If the timeout is turned off, we turn it back on */
|
||||
if (!scan_missing_running)
|
||||
{
|
||||
GSource *source;
|
||||
|
||||
scan_missing_running = TRUE;
|
||||
source = g_timeout_source_new_seconds (SCAN_MISSING_TIME);
|
||||
g_source_set_callback (source, im_scan_missing, NULL, NULL);
|
||||
g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
|
||||
g_source_unref (source);
|
||||
}
|
||||
}
|
||||
|
||||
/* inotify_lock must be held before calling */
|
||||
void
|
||||
_im_rm (inotify_sub *sub)
|
||||
{
|
||||
GList *link;
|
||||
|
||||
link = g_list_find (missing_sub_list, sub);
|
||||
|
||||
if (!link)
|
||||
{
|
||||
IM_W ("asked to remove %s from missing list but it isn't on the list!\n", sub->dirname);
|
||||
return;
|
||||
}
|
||||
|
||||
IM_W ("removing %s from missing list\n", sub->dirname);
|
||||
|
||||
missing_sub_list = g_list_remove_link (missing_sub_list, link);
|
||||
g_list_free_1 (link);
|
||||
}
|
||||
|
||||
/* Scans the list of missing subscriptions checking if they
|
||||
* are available yet.
|
||||
*/
|
||||
static gboolean
|
||||
im_scan_missing (gpointer user_data)
|
||||
{
|
||||
GList *nolonger_missing = NULL;
|
||||
GList *l;
|
||||
|
||||
G_LOCK (inotify_lock);
|
||||
|
||||
IM_W ("scanning missing list with %d items\n", g_list_length (missing_sub_list));
|
||||
for (l = missing_sub_list; l; l = l->next)
|
||||
{
|
||||
inotify_sub *sub = l->data;
|
||||
gboolean not_m = FALSE;
|
||||
|
||||
IM_W ("checking %p\n", sub);
|
||||
g_assert (sub);
|
||||
g_assert (sub->dirname);
|
||||
not_m = _ip_start_watching (sub);
|
||||
|
||||
if (not_m)
|
||||
{
|
||||
missing_cb (sub);
|
||||
IM_W ("removed %s from missing list\n", sub->dirname);
|
||||
/* We have to build a list of list nodes to remove from the
|
||||
* missing_sub_list. We do the removal outside of this loop.
|
||||
*/
|
||||
nolonger_missing = g_list_prepend (nolonger_missing, l);
|
||||
}
|
||||
}
|
||||
|
||||
for (l = nolonger_missing; l ; l = l->next)
|
||||
{
|
||||
GList *llink = l->data;
|
||||
missing_sub_list = g_list_remove_link (missing_sub_list, llink);
|
||||
g_list_free_1 (llink);
|
||||
}
|
||||
|
||||
g_list_free (nolonger_missing);
|
||||
|
||||
/* If the missing list is now empty, we disable the timeout */
|
||||
if (missing_sub_list == NULL)
|
||||
{
|
||||
scan_missing_running = FALSE;
|
||||
G_UNLOCK (inotify_lock);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_UNLOCK (inotify_lock);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
33
gio/inotify/inotify-missing.h
Normal file
33
gio/inotify/inotify-missing.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/* inotify-helper.h - GNOME VFS Monitor using inotify
|
||||
|
||||
Copyright (C) 2006 John McCutchan <john@johnmccutchan.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: John McCutchan <ttb@tentacle.dhs.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __INOTIFY_MISSING_H
|
||||
#define __INOTIFY_MISSING_H
|
||||
|
||||
#include "inotify-sub.h"
|
||||
|
||||
void _im_startup (void (*missing_cb)(inotify_sub *sub));
|
||||
void _im_add (inotify_sub *sub);
|
||||
void _im_rm (inotify_sub *sub);
|
||||
void _im_diag_dump (GIOChannel *ioc);
|
||||
|
||||
|
||||
#endif /* __INOTIFY_MISSING_H */
|
||||
595
gio/inotify/inotify-path.c
Normal file
595
gio/inotify/inotify-path.c
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
|
||||
|
||||
/* inotify-path.c - GVFS Monitor based on inotify.
|
||||
|
||||
Copyright (C) 2006 John McCutchan
|
||||
Copyright (C) 2009 Codethink Limited
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* Don't put conflicting kernel types in the global namespace: */
|
||||
#define __KERNEL_STRICT_NAMES
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include "inotify-kernel.h"
|
||||
#include "inotify-path.h"
|
||||
#include "inotify-missing.h"
|
||||
|
||||
#define IP_INOTIFY_DIR_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF|IN_CLOSE_WRITE)
|
||||
|
||||
#define IP_INOTIFY_FILE_MASK (IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE)
|
||||
|
||||
/* Older libcs don't have this */
|
||||
#ifndef IN_ONLYDIR
|
||||
#define IN_ONLYDIR 0
|
||||
#endif
|
||||
|
||||
typedef struct ip_watched_file_s {
|
||||
gchar *filename;
|
||||
gchar *path;
|
||||
gint32 wd;
|
||||
|
||||
GList *subs;
|
||||
} ip_watched_file_t;
|
||||
|
||||
typedef struct ip_watched_dir_s {
|
||||
char *path;
|
||||
/* TODO: We need to maintain a tree of watched directories
|
||||
* so that we can deliver move/delete events to sub folders.
|
||||
* Or the application could do it...
|
||||
*/
|
||||
struct ip_watched_dir_s* parent;
|
||||
GList* children;
|
||||
|
||||
/* basename -> ip_watched_file_t
|
||||
* Maps basename to a ip_watched_file_t if the file is currently
|
||||
* being directly watched for changes (ie: 'hardlinks' mode).
|
||||
*/
|
||||
GHashTable *files_hash;
|
||||
|
||||
/* Inotify state */
|
||||
gint32 wd;
|
||||
|
||||
/* List of inotify subscriptions */
|
||||
GList *subs;
|
||||
} ip_watched_dir_t;
|
||||
|
||||
static gboolean ip_debug_enabled = FALSE;
|
||||
#define IP_W if (ip_debug_enabled) g_warning
|
||||
|
||||
/* path -> ip_watched_dir */
|
||||
static GHashTable * path_dir_hash = NULL;
|
||||
/* inotify_sub * -> ip_watched_dir *
|
||||
*
|
||||
* Each subscription is attached to a watched directory or it is on
|
||||
* the missing list
|
||||
*/
|
||||
static GHashTable * sub_dir_hash = NULL;
|
||||
/* This hash holds GLists of ip_watched_dir_t *'s
|
||||
* We need to hold a list because symbolic links can share
|
||||
* the same wd
|
||||
*/
|
||||
static GHashTable * wd_dir_hash = NULL;
|
||||
/* This hash holds GLists of ip_watched_file_t *'s
|
||||
* We need to hold a list because links can share
|
||||
* the same wd
|
||||
*/
|
||||
static GHashTable * wd_file_hash = NULL;
|
||||
|
||||
static ip_watched_dir_t *ip_watched_dir_new (const char *path,
|
||||
int wd);
|
||||
static void ip_watched_dir_free (ip_watched_dir_t *dir);
|
||||
static gboolean ip_event_callback (ik_event_t *event);
|
||||
|
||||
|
||||
static gboolean (*event_callback)(ik_event_t *event, inotify_sub *sub, gboolean file_event);
|
||||
|
||||
gboolean
|
||||
_ip_startup (gboolean (*cb)(ik_event_t *event, inotify_sub *sub, gboolean file_event))
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
static gboolean result = FALSE;
|
||||
|
||||
if (initialized == TRUE)
|
||||
return result;
|
||||
|
||||
event_callback = cb;
|
||||
result = _ik_startup (ip_event_callback);
|
||||
|
||||
if (!result)
|
||||
return FALSE;
|
||||
|
||||
path_dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
sub_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
wd_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
wd_file_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
|
||||
initialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_map_path_dir (const char *path,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
g_assert (path && dir);
|
||||
g_hash_table_insert (path_dir_hash, dir->path, dir);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_map_sub_dir (inotify_sub *sub,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
/* Associate subscription and directory */
|
||||
g_assert (dir && sub);
|
||||
g_hash_table_insert (sub_dir_hash, sub, dir);
|
||||
dir->subs = g_list_prepend (dir->subs, sub);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_map_wd_dir (gint32 wd,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
GList *dir_list;
|
||||
|
||||
g_assert (wd >= 0 && dir);
|
||||
dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
|
||||
dir_list = g_list_prepend (dir_list, dir);
|
||||
g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_map_wd_file (gint32 wd,
|
||||
ip_watched_file_t *file)
|
||||
{
|
||||
GList *file_list;
|
||||
|
||||
g_assert (wd >= 0 && file);
|
||||
file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd));
|
||||
file_list = g_list_prepend (file_list, file);
|
||||
g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_wd_file (gint32 wd,
|
||||
ip_watched_file_t *file)
|
||||
{
|
||||
GList *file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd));
|
||||
|
||||
if (!file_list)
|
||||
return;
|
||||
|
||||
g_assert (wd >= 0 && file);
|
||||
file_list = g_list_remove (file_list, file);
|
||||
if (file_list == NULL)
|
||||
g_hash_table_remove (wd_file_hash, GINT_TO_POINTER (wd));
|
||||
else
|
||||
g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list);
|
||||
}
|
||||
|
||||
|
||||
static ip_watched_file_t *
|
||||
ip_watched_file_new (const gchar *dirname,
|
||||
const gchar *filename)
|
||||
{
|
||||
ip_watched_file_t *file;
|
||||
|
||||
file = g_new0 (ip_watched_file_t, 1);
|
||||
file->path = g_strjoin ("/", dirname, filename, NULL);
|
||||
file->filename = g_strdup (filename);
|
||||
file->wd = -1;
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_watched_file_free (ip_watched_file_t *file)
|
||||
{
|
||||
g_assert (file->subs == NULL);
|
||||
g_free (file->filename);
|
||||
g_free (file->path);
|
||||
g_free (file);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_watched_file_add_sub (ip_watched_file_t *file,
|
||||
inotify_sub *sub)
|
||||
{
|
||||
file->subs = g_list_prepend (file->subs, sub);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_watched_file_start (ip_watched_file_t *file)
|
||||
{
|
||||
if (file->wd < 0)
|
||||
{
|
||||
gint err;
|
||||
|
||||
file->wd = _ik_watch (file->path,
|
||||
IP_INOTIFY_FILE_MASK,
|
||||
&err);
|
||||
|
||||
if (file->wd >= 0)
|
||||
ip_map_wd_file (file->wd, file);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ip_watched_file_stop (ip_watched_file_t *file)
|
||||
{
|
||||
if (file->wd >= 0)
|
||||
{
|
||||
_ik_ignore (file->path, file->wd);
|
||||
ip_unmap_wd_file (file->wd, file);
|
||||
file->wd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ip_start_watching (inotify_sub *sub)
|
||||
{
|
||||
gint32 wd;
|
||||
int err;
|
||||
ip_watched_dir_t *dir;
|
||||
|
||||
g_assert (sub);
|
||||
g_assert (!sub->cancelled);
|
||||
g_assert (sub->dirname);
|
||||
|
||||
IP_W ("Starting to watch %s\n", sub->dirname);
|
||||
dir = g_hash_table_lookup (path_dir_hash, sub->dirname);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
IP_W ("Trying to add inotify watch ");
|
||||
wd = _ik_watch (sub->dirname, IP_INOTIFY_DIR_MASK|IN_ONLYDIR, &err);
|
||||
if (wd < 0)
|
||||
{
|
||||
IP_W ("Failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create new watched directory and associate it with the
|
||||
* wd hash and path hash
|
||||
*/
|
||||
IP_W ("Success\n");
|
||||
dir = ip_watched_dir_new (sub->dirname, wd);
|
||||
ip_map_wd_dir (wd, dir);
|
||||
ip_map_path_dir (sub->dirname, dir);
|
||||
}
|
||||
}
|
||||
else
|
||||
IP_W ("Already watching\n");
|
||||
|
||||
if (sub->hardlinks)
|
||||
{
|
||||
ip_watched_file_t *file;
|
||||
|
||||
file = g_hash_table_lookup (dir->files_hash, sub->filename);
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
file = ip_watched_file_new (sub->dirname, sub->filename);
|
||||
g_hash_table_insert (dir->files_hash, file->filename, file);
|
||||
}
|
||||
|
||||
ip_watched_file_add_sub (file, sub);
|
||||
ip_watched_file_start (file);
|
||||
}
|
||||
|
||||
ip_map_sub_dir (sub, dir);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_path_dir (const char *path,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
g_assert (path && dir);
|
||||
g_hash_table_remove (path_dir_hash, dir->path);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_wd_dir (gint32 wd,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
|
||||
|
||||
if (!dir_list)
|
||||
return;
|
||||
|
||||
g_assert (wd >= 0 && dir);
|
||||
dir_list = g_list_remove (dir_list, dir);
|
||||
if (dir_list == NULL)
|
||||
g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (dir->wd));
|
||||
else
|
||||
g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_wd (gint32 wd)
|
||||
{
|
||||
GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
|
||||
if (!dir_list)
|
||||
return;
|
||||
g_assert (wd >= 0);
|
||||
g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (wd));
|
||||
g_list_free (dir_list);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_sub_dir (inotify_sub *sub,
|
||||
ip_watched_dir_t *dir)
|
||||
{
|
||||
g_assert (sub && dir);
|
||||
g_hash_table_remove (sub_dir_hash, sub);
|
||||
dir->subs = g_list_remove (dir->subs, sub);
|
||||
|
||||
if (sub->hardlinks)
|
||||
{
|
||||
ip_watched_file_t *file;
|
||||
|
||||
file = g_hash_table_lookup (dir->files_hash, sub->filename);
|
||||
file->subs = g_list_remove (file->subs, sub);
|
||||
|
||||
if (file->subs == NULL)
|
||||
{
|
||||
g_hash_table_remove (dir->files_hash, sub->filename);
|
||||
ip_watched_file_stop (file);
|
||||
ip_watched_file_free (file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ip_unmap_all_subs (ip_watched_dir_t *dir)
|
||||
{
|
||||
while (dir->subs != NULL)
|
||||
ip_unmap_sub_dir (dir->subs->data, dir);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ip_stop_watching (inotify_sub *sub)
|
||||
{
|
||||
ip_watched_dir_t *dir = NULL;
|
||||
|
||||
dir = g_hash_table_lookup (sub_dir_hash, sub);
|
||||
if (!dir)
|
||||
return TRUE;
|
||||
|
||||
ip_unmap_sub_dir (sub, dir);
|
||||
|
||||
/* No one is subscribing to this directory any more */
|
||||
if (dir->subs == NULL)
|
||||
{
|
||||
_ik_ignore (dir->path, dir->wd);
|
||||
ip_unmap_wd_dir (dir->wd, dir);
|
||||
ip_unmap_path_dir (dir->path, dir);
|
||||
ip_watched_dir_free (dir);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static ip_watched_dir_t *
|
||||
ip_watched_dir_new (const char *path,
|
||||
gint32 wd)
|
||||
{
|
||||
ip_watched_dir_t *dir = g_new0 (ip_watched_dir_t, 1);
|
||||
|
||||
dir->path = g_strdup (path);
|
||||
dir->files_hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
dir->wd = wd;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_watched_dir_free (ip_watched_dir_t *dir)
|
||||
{
|
||||
g_assert_cmpint (g_hash_table_size (dir->files_hash), ==, 0);
|
||||
g_assert (dir->subs == NULL);
|
||||
g_free (dir->path);
|
||||
g_hash_table_unref (dir->files_hash);
|
||||
g_free (dir);
|
||||
}
|
||||
|
||||
static void
|
||||
ip_wd_delete (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
ip_watched_dir_t *dir = data;
|
||||
GList *l = NULL;
|
||||
|
||||
for (l = dir->subs; l; l = l->next)
|
||||
{
|
||||
inotify_sub *sub = l->data;
|
||||
/* Add subscription to missing list */
|
||||
_im_add (sub);
|
||||
}
|
||||
ip_unmap_all_subs (dir);
|
||||
/* Unassociate the path and the directory */
|
||||
ip_unmap_path_dir (dir->path, dir);
|
||||
ip_watched_dir_free (dir);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip_event_dispatch (GList *dir_list,
|
||||
GList *file_list,
|
||||
ik_event_t *event)
|
||||
{
|
||||
gboolean interesting = FALSE;
|
||||
|
||||
GList *l;
|
||||
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
for (l = dir_list; l; l = l->next)
|
||||
{
|
||||
GList *subl;
|
||||
ip_watched_dir_t *dir = l->data;
|
||||
|
||||
for (subl = dir->subs; subl; subl = subl->next)
|
||||
{
|
||||
inotify_sub *sub = subl->data;
|
||||
|
||||
/* If the subscription and the event
|
||||
* contain a filename and they don't
|
||||
* match, we don't deliver this event.
|
||||
*/
|
||||
if (sub->filename &&
|
||||
event->name &&
|
||||
strcmp (sub->filename, event->name) &&
|
||||
(!event->pair || !event->pair->name || strcmp (sub->filename, event->pair->name)))
|
||||
continue;
|
||||
|
||||
/* If the subscription has a filename
|
||||
* but this event doesn't, we don't
|
||||
* deliver this event.
|
||||
*/
|
||||
if (sub->filename && !event->name)
|
||||
continue;
|
||||
|
||||
/* If we're also watching the file directly
|
||||
* don't report events that will also be
|
||||
* reported on the file itself.
|
||||
*/
|
||||
if (sub->hardlinks)
|
||||
{
|
||||
event->mask &= ~IP_INOTIFY_FILE_MASK;
|
||||
if (!event->mask)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME: We might need to synthesize
|
||||
* DELETE/UNMOUNT events when
|
||||
* the filename doesn't match
|
||||
*/
|
||||
|
||||
interesting |= event_callback (event, sub, FALSE);
|
||||
|
||||
if (sub->hardlinks)
|
||||
{
|
||||
ip_watched_file_t *file;
|
||||
|
||||
file = g_hash_table_lookup (dir->files_hash, sub->filename);
|
||||
|
||||
if (file != NULL)
|
||||
{
|
||||
if (event->mask & (IN_MOVED_FROM | IN_DELETE))
|
||||
ip_watched_file_stop (file);
|
||||
|
||||
if (event->mask & (IN_MOVED_TO | IN_CREATE))
|
||||
ip_watched_file_start (file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (l = file_list; l; l = l->next)
|
||||
{
|
||||
ip_watched_file_t *file = l->data;
|
||||
GList *subl;
|
||||
|
||||
for (subl = file->subs; subl; subl = subl->next)
|
||||
{
|
||||
inotify_sub *sub = subl->data;
|
||||
|
||||
interesting |= event_callback (event, sub, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return interesting;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip_event_callback (ik_event_t *event)
|
||||
{
|
||||
gboolean interesting = FALSE;
|
||||
GList* dir_list = NULL;
|
||||
GList *file_list = NULL;
|
||||
|
||||
/* We can ignore the IGNORED events. Likewise, if the event queue overflowed,
|
||||
* there is not much we can do to recover. */
|
||||
if (event->mask & (IN_IGNORED | IN_Q_OVERFLOW))
|
||||
{
|
||||
_ik_event_free (event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd));
|
||||
file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->wd));
|
||||
|
||||
if (event->mask & IP_INOTIFY_DIR_MASK)
|
||||
interesting |= ip_event_dispatch (dir_list, file_list, event);
|
||||
|
||||
/* Only deliver paired events if the wds are separate */
|
||||
if (event->pair && event->pair->wd != event->wd)
|
||||
{
|
||||
dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd));
|
||||
file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->pair->wd));
|
||||
|
||||
if (event->pair->mask & IP_INOTIFY_DIR_MASK)
|
||||
interesting |= ip_event_dispatch (dir_list, file_list, event->pair);
|
||||
}
|
||||
|
||||
/* We have to manage the missing list
|
||||
* when we get an event that means the
|
||||
* file has been deleted/moved/unmounted.
|
||||
*/
|
||||
if (event->mask & IN_DELETE_SELF ||
|
||||
event->mask & IN_MOVE_SELF ||
|
||||
event->mask & IN_UNMOUNT)
|
||||
{
|
||||
/* Add all subscriptions to missing list */
|
||||
g_list_foreach (dir_list, ip_wd_delete, NULL);
|
||||
/* Unmap all directories attached to this wd */
|
||||
ip_unmap_wd (event->wd);
|
||||
}
|
||||
|
||||
_ik_event_free (event);
|
||||
|
||||
return interesting;
|
||||
}
|
||||
|
||||
const char *
|
||||
_ip_get_path_for_wd (gint32 wd)
|
||||
{
|
||||
GList *dir_list;
|
||||
ip_watched_dir_t *dir;
|
||||
|
||||
g_assert (wd >= 0);
|
||||
dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
|
||||
if (dir_list)
|
||||
{
|
||||
dir = dir_list->data;
|
||||
if (dir)
|
||||
return dir->path;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
31
gio/inotify/inotify-path.h
Normal file
31
gio/inotify/inotify-path.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright (C) 2005 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:.
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#ifndef __INOTIFY_PATH_H
|
||||
#define __INOTIFY_PATH_H
|
||||
|
||||
#include "inotify-kernel.h"
|
||||
#include "inotify-sub.h"
|
||||
|
||||
gboolean _ip_startup (gboolean (*event_cb)(ik_event_t *event, inotify_sub *sub, gboolean file_event));
|
||||
gboolean _ip_start_watching (inotify_sub *sub);
|
||||
gboolean _ip_stop_watching (inotify_sub *sub);
|
||||
const char * _ip_get_path_for_wd (gint32 wd);
|
||||
#endif
|
||||
81
gio/inotify/inotify-sub.c
Normal file
81
gio/inotify/inotify-sub.c
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
|
||||
|
||||
/* inotify-sub.c - GVFS Monitor based on inotify.
|
||||
|
||||
Copyright (C) 2006 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Authors:
|
||||
John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "inotify-sub.h"
|
||||
|
||||
static gboolean is_debug_enabled = FALSE;
|
||||
#define IS_W if (is_debug_enabled) g_warning
|
||||
|
||||
static gchar*
|
||||
dup_dirname (const gchar *dirname)
|
||||
{
|
||||
gchar *d_dirname = g_strdup (dirname);
|
||||
size_t len = strlen (d_dirname);
|
||||
|
||||
if (d_dirname[len - 1] == '/')
|
||||
d_dirname[len - 1] = '\0';
|
||||
|
||||
return d_dirname;
|
||||
}
|
||||
|
||||
inotify_sub*
|
||||
_ih_sub_new (const gchar *dirname,
|
||||
const gchar *basename,
|
||||
const gchar *filename,
|
||||
gpointer user_data)
|
||||
{
|
||||
inotify_sub *sub = NULL;
|
||||
|
||||
sub = g_new0 (inotify_sub, 1);
|
||||
|
||||
if (filename)
|
||||
{
|
||||
sub->dirname = g_path_get_dirname (filename);
|
||||
sub->filename = g_path_get_basename (filename);
|
||||
sub->hardlinks = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
sub->dirname = dup_dirname (dirname);
|
||||
sub->filename = g_strdup (basename);
|
||||
sub->hardlinks = FALSE;
|
||||
}
|
||||
|
||||
sub->user_data = user_data;
|
||||
|
||||
IS_W ("new subscription for %s being setup\n", sub->dirname);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
_ih_sub_free (inotify_sub *sub)
|
||||
{
|
||||
g_free (sub->dirname);
|
||||
g_free (sub->filename);
|
||||
g_free (sub);
|
||||
}
|
||||
41
gio/inotify/inotify-sub.h
Normal file
41
gio/inotify/inotify-sub.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* inotify-sub.h - GVFS Directory Monitor using inotify
|
||||
|
||||
Copyright (C) 2006 John McCutchan
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Author: John McCutchan <john@johnmccutchan.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __INOTIFY_SUB_H
|
||||
#define __INOTIFY_SUB_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar* dirname;
|
||||
gchar* filename;
|
||||
gboolean cancelled;
|
||||
gpointer user_data;
|
||||
gboolean pair_moves;
|
||||
gboolean hardlinks;
|
||||
} inotify_sub;
|
||||
|
||||
inotify_sub *_ih_sub_new (const gchar *dirname,
|
||||
const gchar *basename,
|
||||
const gchar *filename,
|
||||
gpointer user_data);
|
||||
void _ih_sub_free (inotify_sub *sub);
|
||||
|
||||
#endif /* __INOTIFY_SUB_H */
|
||||
15
gio/inotify/meson.build
Normal file
15
gio/inotify/meson.build
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
inotify_sources = [
|
||||
'inotify-kernel.c',
|
||||
'inotify-sub.c',
|
||||
'inotify-path.c',
|
||||
'inotify-missing.c',
|
||||
'inotify-helper.c',
|
||||
'ginotifyfilemonitor.c',
|
||||
]
|
||||
|
||||
inotify_lib = static_library('inotify',
|
||||
sources : inotify_sources,
|
||||
include_directories : [configinc, glibinc, gmoduleinc],
|
||||
dependencies : [gioenumtypes_dep, libglib_dep, libgobject_dep],
|
||||
pic : true,
|
||||
c_args : gio_c_args)
|
||||
Loading…
Add table
Add a link
Reference in a new issue