Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / filewatcher.c
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
41 ves_icall_System_IO_FSW_SupportsFSW (void)
42 {
43         return 1;
44 }
45
46 gboolean
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
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
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
131 int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
132 {
133         return -1;
134 }
135
136 int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask)
137 {
138         return -1;
139 }
140
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
150 ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
151 {
152         return inotify_init ();
153 }
154
155 int
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
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
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
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
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