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