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