Home | History | Annotate | Download | only in inotify
      1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
      2 
      3 /* inotify-helper.c - GVFS Monitor based on inotify.
      4 
      5    Copyright (C) 2007 John McCutchan
      6 
      7    The Gnome Library is free software; you can redistribute it and/or
      8    modify it under the terms of the GNU Library General Public License as
      9    published by the Free Software Foundation; either version 2 of the
     10    License, or (at your option) any later version.
     11 
     12    The Gnome Library is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15    Library General Public License for more details.
     16 
     17    You should have received a copy of the GNU Library General Public
     18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
     19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     20    Boston, MA 02111-1307, USA.
     21 
     22    Authors:
     23 		 John McCutchan <john (at) johnmccutchan.com>
     24 */
     25 
     26 #include "config.h"
     27 #include <errno.h>
     28 #include <time.h>
     29 #include <string.h>
     30 #include <sys/ioctl.h>
     31 /* Just include the local header to stop all the pain */
     32 #include <sys/inotify.h>
     33 #include <gio/glocalfile.h>
     34 #include <gio/gfilemonitor.h>
     35 #include <gio/gfile.h>
     36 #include "inotify-helper.h"
     37 #include "inotify-missing.h"
     38 #include "inotify-path.h"
     39 #include "inotify-diag.h"
     40 
     41 #include "gioalias.h"
     42 
     43 
     44 static gboolean ih_debug_enabled = FALSE;
     45 #define IH_W if (ih_debug_enabled) g_warning
     46 
     47 static void ih_event_callback (ik_event_t *event, inotify_sub *sub);
     48 static void ih_not_missing_callback (inotify_sub *sub);
     49 
     50 /* We share this lock with inotify-kernel.c and inotify-missing.c
     51  *
     52  * inotify-kernel.c takes the lock when it reads events from
     53  * the kernel and when it processes those events
     54  *
     55  * inotify-missing.c takes the lock when it is scanning the missing
     56  * list.
     57  *
     58  * We take the lock in all public functions
     59  */
     60 G_GNUC_INTERNAL G_LOCK_DEFINE (inotify_lock);
     61 
     62 static GFileMonitorEvent ih_mask_to_EventFlags (guint32 mask);
     63 
     64 /**
     65  * _ih_startup:
     66  *
     67  * Initializes the inotify backend.  This must be called before
     68  * any other functions in this module.
     69  *
     70  * Return value: #TRUE if initialization succeeded, #FALSE otherwise
     71  */
     72 gboolean
     73 _ih_startup (void)
     74 {
     75   static gboolean initialized = FALSE;
     76   static gboolean result = FALSE;
     77 
     78   G_LOCK (inotify_lock);
     79 
     80   if (initialized == TRUE)
     81     {
     82       G_UNLOCK (inotify_lock);
     83       return result;
     84     }
     85 
     86   result = _ip_startup (ih_event_callback);
     87   if (!result)
     88     {
     89       g_warning ("Could not initialize inotify\n");
     90       G_UNLOCK (inotify_lock);
     91       return FALSE;
     92     }
     93   _im_startup (ih_not_missing_callback);
     94   _id_startup ();
     95 
     96   IH_W ("started gvfs inotify backend\n");
     97 
     98   initialized = TRUE;
     99 
    100   G_UNLOCK (inotify_lock);
    101 
    102   return TRUE;
    103 }
    104 
    105 /**
    106  * Adds a subscription to be monitored.
    107  */
    108 gboolean
    109 _ih_sub_add (inotify_sub *sub)
    110 {
    111   G_LOCK (inotify_lock);
    112 
    113   if (!_ip_start_watching (sub))
    114     _im_add (sub);
    115 
    116   G_UNLOCK (inotify_lock);
    117   return TRUE;
    118 }
    119 
    120 /**
    121  * Cancels a subscription which was being monitored.
    122  */
    123 gboolean
    124 _ih_sub_cancel (inotify_sub *sub)
    125 {
    126   G_LOCK (inotify_lock);
    127 
    128   if (!sub->cancelled)
    129     {
    130       IH_W ("cancelling %s\n", sub->dirname);
    131       sub->cancelled = TRUE;
    132       _im_rm (sub);
    133       _ip_stop_watching (sub);
    134     }
    135 
    136   G_UNLOCK (inotify_lock);
    137 
    138   return TRUE;
    139 }
    140 
    141 
    142 static void
    143 ih_event_callback (ik_event_t  *event,
    144                    inotify_sub *sub)
    145 {
    146   gchar *fullpath;
    147   GFileMonitorEvent eflags;
    148   GFile* parent;
    149   GFile* child;
    150 
    151   eflags = ih_mask_to_EventFlags (event->mask);
    152   parent = g_file_new_for_path (sub->dirname);
    153   if (event->name)
    154     fullpath = g_strdup_printf ("%s/%s", sub->dirname, event->name);
    155   else
    156     fullpath = g_strdup_printf ("%s/", sub->dirname);
    157 
    158   child = g_file_new_for_path (fullpath);
    159   g_free (fullpath);
    160 
    161   g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
    162 			     child, NULL, eflags);
    163 
    164   g_object_unref (child);
    165   g_object_unref (parent);
    166 }
    167 
    168 static void
    169 ih_not_missing_callback (inotify_sub *sub)
    170 {
    171   gchar *fullpath;
    172   GFileMonitorEvent eflags;
    173   guint32 mask;
    174   GFile* parent;
    175   GFile* child;
    176 
    177   parent = g_file_new_for_path (sub->dirname);
    178 
    179   if (sub->filename)
    180     {
    181       fullpath = g_strdup_printf ("%s/%s", sub->dirname, sub->filename);
    182       g_warning ("Missing callback called fullpath = %s\n", fullpath);
    183       if (!g_file_test (fullpath, G_FILE_TEST_EXISTS))
    184 	{
    185 	  g_free (fullpath);
    186 	  return;
    187 	}
    188       mask = IN_CREATE;
    189     }
    190   else
    191     {
    192       fullpath = g_strdup_printf ("%s", sub->dirname);
    193       mask = IN_CREATE|IN_ISDIR;
    194     }
    195 
    196   eflags = ih_mask_to_EventFlags (mask);
    197   child = g_file_new_for_path (fullpath);
    198   g_free (fullpath);
    199 
    200   g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
    201 			     child, NULL, eflags);
    202 
    203   g_object_unref (child);
    204   g_object_unref (parent);
    205 }
    206 
    207 /* Transforms a inotify event to a GVFS event. */
    208 static GFileMonitorEvent
    209 ih_mask_to_EventFlags (guint32 mask)
    210 {
    211   mask &= ~IN_ISDIR;
    212   switch (mask)
    213     {
    214     case IN_MODIFY:
    215       return G_FILE_MONITOR_EVENT_CHANGED;
    216     case IN_CLOSE_WRITE:
    217       return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT;
    218     case IN_ATTRIB:
    219       return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
    220     case IN_MOVE_SELF:
    221     case IN_MOVED_FROM:
    222     case IN_DELETE:
    223     case IN_DELETE_SELF:
    224       return G_FILE_MONITOR_EVENT_DELETED;
    225     case IN_CREATE:
    226     case IN_MOVED_TO:
    227       return G_FILE_MONITOR_EVENT_CREATED;
    228     case IN_UNMOUNT:
    229       return G_FILE_MONITOR_EVENT_UNMOUNTED;
    230     case IN_Q_OVERFLOW:
    231     case IN_OPEN:
    232     case IN_CLOSE_NOWRITE:
    233     case IN_ACCESS:
    234     case IN_IGNORED:
    235     default:
    236       return -1;
    237     }
    238 }
    239