blob: d9518a6217defc475396e69f6567149db43e3d76 [file] [log] [blame]
/*
* 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/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include <gio/glocalfilemonitor.h>
#include <gio/giomodule.h>
#include "glib-private.h"
#include <glib-unix.h>
#include <fam.h>
static GMutex fam_lock;
static gboolean fam_initialised;
static FAMConnection fam_connection;
static GSource *fam_source;
#define G_TYPE_FAM_FILE_MONITOR (g_fam_file_monitor_get_type ())
#define G_FAM_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_FAM_FILE_MONITOR, GFamFileMonitor))
typedef GLocalFileMonitorClass GFamFileMonitorClass;
typedef struct
{
GLocalFileMonitor parent_instance;
FAMRequest request;
} GFamFileMonitor;
static GType g_fam_file_monitor_get_type (void);
G_DEFINE_DYNAMIC_TYPE (GFamFileMonitor, g_fam_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
static gboolean
g_fam_file_monitor_callback (gint fd,
GIOCondition condition,
gpointer user_data)
{
gint64 now = g_source_get_time (fam_source);
g_mutex_lock (&fam_lock);
while (FAMPending (&fam_connection))
{
const gchar *child;
FAMEvent ev;
if (FAMNextEvent (&fam_connection, &ev) != 1)
{
/* The daemon died. We're in a really bad situation now
* because we potentially have a bunch of request structures
* outstanding which no longer make any sense to anyone.
*
* The best thing that we can do is do nothing. Notification
* won't work anymore for this process.
*/
g_mutex_unlock (&fam_lock);
g_warning ("Lost connection to FAM (file monitoring) service. Expect no further file monitor events.");
return FALSE;
}
/* We expect ev.filename to be a relative path for children in a
* monitored directory, and an absolute path for a monitored file
* or the directory itself.
*/
if (ev.filename[0] != '/')
child = ev.filename;
else
child = NULL;
switch (ev.code)
{
case FAMAcknowledge:
g_source_unref (ev.userdata);
break;
case FAMChanged:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CHANGED, child, NULL, NULL, now);
break;
case FAMDeleted:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_DELETED, child, NULL, NULL, now);
break;
case FAMCreated:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CREATED, child, NULL, NULL, now);
break;
default:
/* unknown type */
break;
}
}
g_mutex_unlock (&fam_lock);
return TRUE;
}
static gboolean
g_fam_file_monitor_is_supported (void)
{
g_mutex_lock (&fam_lock);
if (!fam_initialised)
{
fam_initialised = FAMOpen2 (&fam_connection, "GLib GIO") == 0;
if (fam_initialised)
{
#ifdef HAVE_FAM_NO_EXISTS
/* This is a gamin extension that avoids sending all the
* Exists event for dir monitors
*/
FAMNoExists (&fam_connection);
#endif
fam_source = g_unix_fd_source_new (FAMCONNECTION_GETFD (&fam_connection), G_IO_IN);
g_source_set_callback (fam_source, (GSourceFunc) g_fam_file_monitor_callback, NULL, NULL);
g_source_attach (fam_source, GLIB_PRIVATE_CALL(g_get_worker_context) ());
}
}
g_mutex_unlock (&fam_lock);
return fam_initialised;
}
static gboolean
g_fam_file_monitor_cancel (GFileMonitor *monitor)
{
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (monitor);
g_mutex_lock (&fam_lock);
g_assert (fam_initialised);
FAMCancelMonitor (&fam_connection, &gffm->request);
g_mutex_unlock (&fam_lock);
return TRUE;
}
static void
g_fam_file_monitor_start (GLocalFileMonitor *local_monitor,
const gchar *dirname,
const gchar *basename,
const gchar *filename,
GFileMonitorSource *source)
{
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (local_monitor);
g_mutex_lock (&fam_lock);
g_assert (fam_initialised);
g_source_ref ((GSource *) source);
if (dirname)
FAMMonitorDirectory (&fam_connection, dirname, &gffm->request, source);
else
FAMMonitorFile (&fam_connection, filename, &gffm->request, source);
g_mutex_unlock (&fam_lock);
}
static void
g_fam_file_monitor_init (GFamFileMonitor* monitor)
{
}
static void
g_fam_file_monitor_class_init (GFamFileMonitorClass *class)
{
GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (class);
class->is_supported = g_fam_file_monitor_is_supported;
class->start = g_fam_file_monitor_start;
file_monitor_class->cancel = g_fam_file_monitor_cancel;
}
static void
g_fam_file_monitor_class_finalize (GFamFileMonitorClass *class)
{
}
void
g_io_module_load (GIOModule *module)
{
g_type_module_use (G_TYPE_MODULE (module));
g_fam_file_monitor_register_type (G_TYPE_MODULE (module));
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
g_io_extension_point_implement (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
}
void
g_io_module_unload (GIOModule *module)
{
g_assert_not_reached ();
}
char **
g_io_module_query (void)
{
char *eps[] = {
G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
NULL
};
return g_strdupv (eps);
}