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