Home | History | Annotate | Download | only in fam
      1 /* GIO - GLib Input, Output and Streaming Library
      2  *
      3  * Copyright (C) 2006-2007 Red Hat, Inc.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Lesser General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General
     16  * Public License along with this library; if not, write to the
     17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     18  * Boston, MA 02111-1307, USA.
     19  *
     20  * Authors: Alexander Larsson <alexl (at) redhat.com>
     21  *          John McCutchan <john (at) johnmccutchan.com>
     22  *          Sebastian Drge <slomo (at) circular-chaos.org>
     23  */
     24 
     25 #include "config.h"
     26 #include <fam.h>
     27 #include <gio/gfilemonitor.h>
     28 
     29 #include "gfile.h"
     30 #include "fam-helper.h"
     31 
     32 static FAMConnection* fam_connection = NULL;
     33 static gint fam_watch_id = 0;
     34 G_LOCK_DEFINE_STATIC(fam_connection);
     35 
     36 struct _fam_sub
     37 {
     38   gchar *pathname;
     39   gboolean directory;
     40   gpointer user_data;
     41   gboolean cancelled;
     42   FAMRequest request;
     43 };
     44 
     45 /* This uses int as the argument type because the
     46    real type differs between implementations:
     47    gamin has "typedef enum FAMCodes {....} FAMCodes;"
     48    fam has "enum FAMCodes { ... }".
     49 */
     50 static GFileMonitorEvent
     51 fam_event_to_file_monitor_event (int code)
     52 {
     53   switch (code)
     54     {
     55     case FAMChanged:
     56       return G_FILE_MONITOR_EVENT_CHANGED;
     57       break;
     58     case FAMDeleted:
     59       return G_FILE_MONITOR_EVENT_DELETED;
     60       break;
     61     case FAMCreated:
     62       return G_FILE_MONITOR_EVENT_CREATED;
     63       break;
     64     default:
     65       return -1;
     66       break;
     67     }
     68 }
     69 
     70 static gboolean
     71 fam_do_iter_unlocked (void)
     72 {
     73   while (fam_connection != NULL && FAMPending (fam_connection))
     74     {
     75       FAMEvent ev;
     76       fam_sub* sub = NULL;
     77       gboolean cancelled;
     78 
     79       if (FAMNextEvent (fam_connection, &ev) != 1)
     80         {
     81           FAMClose (fam_connection);
     82           g_free (fam_connection);
     83           g_source_remove (fam_watch_id);
     84           fam_watch_id = 0;
     85           fam_connection = NULL;
     86           return FALSE;
     87         }
     88 
     89       sub = (fam_sub*)ev.userdata;
     90       cancelled = sub->cancelled;
     91       if (ev.code == FAMAcknowledge && cancelled)
     92         {
     93 	  _fam_sub_free (sub);
     94 	  continue;
     95         }
     96 
     97       if (cancelled)
     98         continue;
     99 
    100       if (sub->directory)
    101         {
    102 	  GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
    103 	  GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
    104 	  gchar* path = NULL;
    105 	  GFile *child, *parent;
    106 
    107 	  /* unsupported event */
    108 	  if (eflags == -1)
    109 	    continue;
    110 
    111 	  if (ev.filename[0] == '/')
    112 	    path = g_strdup (ev.filename);
    113 	  else
    114 	    path = g_strdup_printf ("%s/%s", sub->pathname, ev.filename);
    115 
    116 	  child = g_file_new_for_path (path);
    117 	  parent = g_file_get_parent (child);
    118 	  g_file_monitor_emit_event (monitor, child, NULL, eflags);
    119 	  g_free (path);
    120 	  g_object_unref (child);
    121 	  g_object_unref (parent);
    122         }
    123       else
    124         {
    125 	  GFile *child;
    126 	  GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
    127 	  GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
    128 	  gchar* path = NULL;
    129 
    130 	  if (eflags == -1)
    131 	    continue;
    132 	  path = g_strdup (ev.filename);
    133 	  child = g_file_new_for_path (path);
    134 	  g_file_monitor_emit_event (monitor, child, NULL, eflags);
    135 	  g_free (path);
    136 	  g_object_unref (child);
    137         }
    138     }
    139 
    140   return TRUE;
    141 }
    142 
    143 static gboolean
    144 fam_callback (GIOChannel   *source,
    145               GIOCondition  condition,
    146               gpointer      data)
    147 {
    148   gboolean res;
    149   G_LOCK (fam_connection);
    150 
    151   res = fam_do_iter_unlocked ();
    152 
    153   G_UNLOCK (fam_connection);
    154   return res;
    155 }
    156 
    157 gboolean
    158 _fam_sub_startup (void)
    159 {
    160   GIOChannel *ioc;
    161 
    162   G_LOCK (fam_connection);
    163 
    164   if (fam_connection == NULL)
    165     {
    166       fam_connection = g_new0 (FAMConnection, 1);
    167       if (FAMOpen2 (fam_connection, "gvfs user") != 0)
    168         {
    169           g_warning ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
    170           g_free (fam_connection);
    171           fam_connection = NULL;
    172           G_UNLOCK (fam_connection);
    173           return FALSE;
    174         }
    175 #ifdef HAVE_FAM_NO_EXISTS
    176     /* This is a gamin extension that avoids sending all the Exists event for dir monitors */
    177       FAMNoExists (fam_connection);
    178 #endif
    179       ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
    180       fam_watch_id = g_io_add_watch (ioc,
    181   				     G_IO_IN | G_IO_HUP | G_IO_ERR,
    182 				     fam_callback, fam_connection);
    183       g_io_channel_unref (ioc);
    184     }
    185 
    186   G_UNLOCK (fam_connection);
    187 
    188   return TRUE;
    189 }
    190 
    191 void
    192 _fam_sub_shutdown (void)
    193 {
    194   G_LOCK (fam_connection);
    195 
    196   if (fam_connection != NULL)
    197     {
    198       FAMClose (fam_connection);
    199       g_free (fam_connection);
    200       g_source_remove (fam_watch_id);
    201       fam_watch_id = 0;
    202       fam_connection = NULL;
    203     }
    204 
    205   G_UNLOCK (fam_connection);
    206 }
    207 
    208 fam_sub*
    209 _fam_sub_add (const gchar *pathname,
    210 	      gboolean     directory,
    211 	      gpointer     user_data)
    212 {
    213   fam_sub *sub;
    214 
    215   if (!_fam_sub_startup ())
    216     return NULL;
    217 
    218   G_LOCK (fam_connection);
    219   /* We need to queue up incoming messages to avoid blocking on write
    220    *  if there are many monitors being canceled */
    221   fam_do_iter_unlocked ();
    222 
    223   if (fam_connection == NULL)
    224     {
    225       G_UNLOCK (fam_connection);
    226       return NULL;
    227     }
    228 
    229   sub = g_new0 (fam_sub, 1);
    230   sub->pathname = g_strdup (pathname);
    231   sub->directory = directory;
    232   sub->user_data = user_data;
    233 
    234   if (directory)
    235     FAMMonitorDirectory (fam_connection, pathname, &sub->request, sub);
    236   else
    237     FAMMonitorFile (fam_connection, pathname, &sub->request, sub);
    238 
    239   G_UNLOCK (fam_connection);
    240 
    241   return sub;
    242 }
    243 
    244 gboolean
    245 _fam_sub_cancel (fam_sub* sub)
    246 {
    247   if (sub->cancelled)
    248     return TRUE;
    249 
    250   sub->cancelled = TRUE;
    251 
    252   G_LOCK (fam_connection);
    253   /* We need to queue up incoming messages to avoid blocking on write
    254    *  if there are many monitors being canceled */
    255   fam_do_iter_unlocked ();
    256 
    257   if (fam_connection == NULL)
    258     {
    259       G_UNLOCK (fam_connection);
    260       return FALSE;
    261     }
    262 
    263   FAMCancelMonitor (fam_connection, &sub->request);
    264 
    265   G_UNLOCK (fam_connection);
    266 
    267   return TRUE;
    268 }
    269 
    270 void
    271 _fam_sub_free (fam_sub* sub)
    272 {
    273   g_free (sub->pathname);
    274   g_free (sub);
    275 }
    276 
    277