• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2015 Canonical Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19 
20 #include "config.h"
21 
22 #include <gio/glocalfilemonitor.h>
23 #include <gio/giomodule.h>
24 #include "glib-private.h"
25 #include <glib-unix.h>
26 #include <fam.h>
27 
28 static GMutex         fam_lock;
29 static gboolean       fam_initialised;
30 static FAMConnection  fam_connection;
31 static GSource       *fam_source;
32 
33 #define G_TYPE_FAM_FILE_MONITOR      (g_fam_file_monitor_get_type ())
34 #define G_FAM_FILE_MONITOR(inst)     (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
35                                       G_TYPE_FAM_FILE_MONITOR, GFamFileMonitor))
36 
37 typedef GLocalFileMonitorClass GFamFileMonitorClass;
38 
39 typedef struct
40 {
41   GLocalFileMonitor parent_instance;
42 
43   FAMRequest request;
44 } GFamFileMonitor;
45 
46 static GType g_fam_file_monitor_get_type (void);
G_DEFINE_DYNAMIC_TYPE(GFamFileMonitor,g_fam_file_monitor,G_TYPE_LOCAL_FILE_MONITOR)47 G_DEFINE_DYNAMIC_TYPE (GFamFileMonitor, g_fam_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
48 
49 static gboolean
50 g_fam_file_monitor_callback (gint         fd,
51                              GIOCondition condition,
52                              gpointer     user_data)
53 {
54   gint64 now = g_source_get_time (fam_source);
55 
56   g_mutex_lock (&fam_lock);
57 
58   while (FAMPending (&fam_connection))
59     {
60       const gchar *child;
61       FAMEvent ev;
62 
63       if (FAMNextEvent (&fam_connection, &ev) != 1)
64         {
65           /* The daemon died.  We're in a really bad situation now
66            * because we potentially have a bunch of request structures
67            * outstanding which no longer make any sense to anyone.
68            *
69            * The best thing that we can do is do nothing.  Notification
70            * won't work anymore for this process.
71            */
72           g_mutex_unlock (&fam_lock);
73 
74           g_warning ("Lost connection to FAM (file monitoring) service.  Expect no further file monitor events.");
75 
76           return FALSE;
77         }
78 
79       /* We expect ev.filename to be a relative path for children in a
80        * monitored directory, and an absolute path for a monitored file
81        * or the directory itself.
82        */
83       if (ev.filename[0] != '/')
84         child = ev.filename;
85       else
86         child = NULL;
87 
88       switch (ev.code)
89         {
90         case FAMAcknowledge:
91           g_source_unref (ev.userdata);
92           break;
93 
94         case FAMChanged:
95           g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CHANGED, child, NULL, NULL, now);
96           break;
97 
98         case FAMDeleted:
99           g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_DELETED, child, NULL, NULL, now);
100           break;
101 
102         case FAMCreated:
103           g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CREATED, child, NULL, NULL, now);
104           break;
105 
106         default:
107           /* unknown type */
108           break;
109         }
110     }
111 
112   g_mutex_unlock (&fam_lock);
113 
114   return TRUE;
115 }
116 
117 static gboolean
g_fam_file_monitor_is_supported(void)118 g_fam_file_monitor_is_supported (void)
119 {
120   g_mutex_lock (&fam_lock);
121 
122   if (!fam_initialised)
123     {
124       fam_initialised = FAMOpen2 (&fam_connection, "GLib GIO") == 0;
125 
126       if (fam_initialised)
127         {
128 #ifdef HAVE_FAM_NO_EXISTS
129           /* This is a gamin extension that avoids sending all the
130            * Exists event for dir monitors
131            */
132           FAMNoExists (&fam_connection);
133 #endif
134 
135           fam_source = g_unix_fd_source_new (FAMCONNECTION_GETFD (&fam_connection), G_IO_IN);
136           g_source_set_callback (fam_source, (GSourceFunc) g_fam_file_monitor_callback, NULL, NULL);
137           g_source_attach (fam_source, GLIB_PRIVATE_CALL(g_get_worker_context) ());
138         }
139     }
140 
141   g_mutex_unlock (&fam_lock);
142 
143   return fam_initialised;
144 }
145 
146 static gboolean
g_fam_file_monitor_cancel(GFileMonitor * monitor)147 g_fam_file_monitor_cancel (GFileMonitor *monitor)
148 {
149   GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (monitor);
150 
151   g_mutex_lock (&fam_lock);
152 
153   g_assert (fam_initialised);
154 
155   FAMCancelMonitor (&fam_connection, &gffm->request);
156 
157   g_mutex_unlock (&fam_lock);
158 
159   return TRUE;
160 }
161 
162 static void
g_fam_file_monitor_start(GLocalFileMonitor * local_monitor,const gchar * dirname,const gchar * basename,const gchar * filename,GFileMonitorSource * source)163 g_fam_file_monitor_start (GLocalFileMonitor  *local_monitor,
164                           const gchar        *dirname,
165                           const gchar        *basename,
166                           const gchar        *filename,
167                           GFileMonitorSource *source)
168 {
169   GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (local_monitor);
170 
171   g_mutex_lock (&fam_lock);
172 
173   g_assert (fam_initialised);
174 
175   g_source_ref ((GSource *) source);
176 
177   if (dirname)
178     FAMMonitorDirectory (&fam_connection, dirname, &gffm->request, source);
179   else
180     FAMMonitorFile (&fam_connection, filename, &gffm->request, source);
181 
182   g_mutex_unlock (&fam_lock);
183 }
184 
185 static void
g_fam_file_monitor_init(GFamFileMonitor * monitor)186 g_fam_file_monitor_init (GFamFileMonitor* monitor)
187 {
188 }
189 
190 static void
g_fam_file_monitor_class_init(GFamFileMonitorClass * class)191 g_fam_file_monitor_class_init (GFamFileMonitorClass *class)
192 {
193   GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (class);
194 
195   class->is_supported = g_fam_file_monitor_is_supported;
196   class->start = g_fam_file_monitor_start;
197   file_monitor_class->cancel = g_fam_file_monitor_cancel;
198 }
199 
200 static void
g_fam_file_monitor_class_finalize(GFamFileMonitorClass * class)201 g_fam_file_monitor_class_finalize (GFamFileMonitorClass *class)
202 {
203 }
204 
205 void
g_io_module_load(GIOModule * module)206 g_io_module_load (GIOModule *module)
207 {
208   g_type_module_use (G_TYPE_MODULE (module));
209 
210   g_fam_file_monitor_register_type (G_TYPE_MODULE (module));
211 
212   g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
213                                  G_TYPE_FAM_FILE_MONITOR, "fam", 10);
214 
215   g_io_extension_point_implement (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
216                                  G_TYPE_FAM_FILE_MONITOR, "fam", 10);
217 }
218 
219 void
g_io_module_unload(GIOModule * module)220 g_io_module_unload (GIOModule *module)
221 {
222   g_assert_not_reached ();
223 }
224 
225 char **
g_io_module_query(void)226 g_io_module_query (void)
227 {
228   char *eps[] = {
229     G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
230     G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
231     NULL
232   };
233 
234   return g_strdupv (eps);
235 }
236