1 /**
2 * \file
3 * File System Watcher internal calls
4 *
5 * Authors:
6 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 *
8 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 */
11
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #ifdef HAVE_SYS_EVENT_H
20 #include <sys/event.h>
21 #endif
22 #ifdef HAVE_SYS_TIME_H
23 #include <sys/time.h>
24 #endif
25
26 #include <mono/metadata/appdomain.h>
27 #include <mono/metadata/exception.h>
28 #include <mono/metadata/filewatcher.h>
29 #include <mono/metadata/marshal.h>
30 #include <mono/utils/mono-dl.h>
31 #include <mono/utils/mono-io-portability.h>
32 #include <mono/metadata/w32error.h>
33
34 #ifdef HOST_WIN32
35
36 /*
37 * TODO:
38 * We use the managed watcher on windows, so the code inside this #if is never used
39 */
40 gint
ves_icall_System_IO_FSW_SupportsFSW(void)41 ves_icall_System_IO_FSW_SupportsFSW (void)
42 {
43 return 1;
44 }
45
46 gboolean
ves_icall_System_IO_FAMW_InternalFAMNextEvent(gpointer conn,MonoString ** filename,gint * code,gint * reqnum)47 ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn,
48 MonoString **filename,
49 gint *code,
50 gint *reqnum)
51 {
52 return FALSE;
53 }
54
55 #else
56
57 static int (*FAMNextEvent) (gpointer, gpointer);
58
59 gint
ves_icall_System_IO_FSW_SupportsFSW(void)60 ves_icall_System_IO_FSW_SupportsFSW (void)
61 {
62 #if HAVE_KQUEUE
63 return 3;
64 #else
65 MonoDl *fam_module;
66 int lib_used = 4; /* gamin */
67 int inotify_instance;
68 char *err;
69
70 inotify_instance = ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ();
71 if (inotify_instance != -1) {
72 close (inotify_instance);
73 return 5; /* inotify */
74 }
75
76 fam_module = mono_dl_open ("libgamin-1.so", MONO_DL_LAZY, NULL);
77 if (fam_module == NULL) {
78 lib_used = 2; /* FAM */
79 fam_module = mono_dl_open ("libfam.so", MONO_DL_LAZY, NULL);
80 }
81
82 if (fam_module == NULL)
83 return 0;
84
85 err = mono_dl_symbol (fam_module, "FAMNextEvent", (gpointer *) &FAMNextEvent);
86 g_free (err);
87 if (FAMNextEvent == NULL)
88 return 0;
89
90 return lib_used;
91 #endif
92 }
93
94 /* Almost copied from fam.h. Weird, I know */
95 typedef struct {
96 gint reqnum;
97 } FAMRequest;
98
99 typedef struct FAMEvent {
100 gpointer fc;
101 FAMRequest fr;
102 gchar *hostname;
103 gchar filename [PATH_MAX];
104 gpointer userdata;
105 gint code;
106 } FAMEvent;
107
108 gboolean
ves_icall_System_IO_FAMW_InternalFAMNextEvent(gpointer conn,MonoString ** filename,gint * code,gint * reqnum)109 ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn,
110 MonoString **filename,
111 gint *code,
112 gint *reqnum)
113 {
114 MonoError error;
115 FAMEvent ev;
116
117 if (FAMNextEvent (conn, &ev) == 1) {
118 *filename = mono_string_new_checked (mono_domain_get (), ev.filename, &error);
119 *code = ev.code;
120 *reqnum = ev.fr.reqnum;
121 if (mono_error_set_pending_exception (&error))
122 return FALSE;
123 return TRUE;
124 }
125
126 return FALSE;
127 }
128 #endif
129
130 #ifndef HAVE_SYS_INOTIFY_H
ves_icall_System_IO_InotifyWatcher_GetInotifyInstance()131 int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
132 {
133 return -1;
134 }
135
ves_icall_System_IO_InotifyWatcher_AddWatch(int fd,MonoString * directory,gint32 mask)136 int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask)
137 {
138 return -1;
139 }
140
ves_icall_System_IO_InotifyWatcher_RemoveWatch(int fd,gint32 watch_descriptor)141 int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
142 {
143 return -1;
144 }
145 #else
146 #include <sys/inotify.h>
147 #include <errno.h>
148
149 int
ves_icall_System_IO_InotifyWatcher_GetInotifyInstance()150 ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
151 {
152 return inotify_init ();
153 }
154
155 int
ves_icall_System_IO_InotifyWatcher_AddWatch(int fd,MonoString * name,gint32 mask)156 ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *name, gint32 mask)
157 {
158 MonoError error;
159 char *str, *path;
160 int retval;
161
162 if (name == NULL)
163 return -1;
164
165 str = mono_string_to_utf8_checked (name, &error);
166 if (mono_error_set_pending_exception (&error))
167 return -1;
168 path = mono_portability_find_file (str, TRUE);
169 if (!path)
170 path = str;
171
172 retval = inotify_add_watch (fd, path, mask);
173 if (retval < 0) {
174 switch (errno) {
175 case EACCES:
176 errno = ERROR_ACCESS_DENIED;
177 break;
178 case EBADF:
179 errno = ERROR_INVALID_HANDLE;
180 break;
181 case EFAULT:
182 errno = ERROR_INVALID_ACCESS;
183 break;
184 case EINVAL:
185 errno = ERROR_INVALID_DATA;
186 break;
187 case ENOMEM:
188 errno = ERROR_NOT_ENOUGH_MEMORY;
189 break;
190 case ENOSPC:
191 errno = ERROR_TOO_MANY_OPEN_FILES;
192 break;
193 default:
194 errno = ERROR_GEN_FAILURE;
195 break;
196 }
197 mono_marshal_set_last_error ();
198 }
199 if (path != str)
200 g_free (path);
201 g_free (str);
202 return retval;
203 }
204
205 int
ves_icall_System_IO_InotifyWatcher_RemoveWatch(int fd,gint32 watch_descriptor)206 ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
207 {
208 return inotify_rm_watch (fd, watch_descriptor);
209 }
210 #endif
211
212 #if HAVE_KQUEUE
213
214 static void
interrupt_kevent(gpointer data)215 interrupt_kevent (gpointer data)
216 {
217 int *kq_ptr = data;
218
219 /* Interrupt the kevent () call by closing the fd */
220 close (*kq_ptr);
221 /* Signal to managed code that the fd is closed */
222 *kq_ptr = -1;
223 }
224
225 /*
226 * ves_icall_System_IO_KqueueMonitor_kevent_notimeout:
227 *
228 * Call kevent (), while handling runtime interruptions.
229 */
230 int
ves_icall_System_IO_KqueueMonitor_kevent_notimeout(int * kq_ptr,gpointer changelist,int nchanges,gpointer eventlist,int nevents)231 ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents)
232 {
233 int res;
234 gboolean interrupted;
235
236 mono_thread_info_install_interrupt (interrupt_kevent, kq_ptr, &interrupted);
237 if (interrupted) {
238 close (*kq_ptr);
239 *kq_ptr = -1;
240 return -1;
241 }
242
243 MONO_ENTER_GC_SAFE;
244 res = kevent (*kq_ptr, changelist, nchanges, eventlist, nevents, NULL);
245 MONO_EXIT_GC_SAFE;
246
247 mono_thread_info_uninstall_interrupt (&interrupted);
248
249 return res;
250 }
251
252 #else
253
254 int
ves_icall_System_IO_KqueueMonitor_kevent_notimeout(int * kq_ptr,gpointer changelist,int nchanges,gpointer eventlist,int nevents)255 ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents)
256 {
257 g_assert_not_reached ();
258 return -1;
259 }
260
261 #endif /* #if HAVE_KQUEUE */
262
263