Merge pull request #4615 from alexanderkyte/string_error_handling
[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         FAMEvent ev;
115
116         if (FAMNextEvent (conn, &ev) == 1) {
117                 *filename = mono_string_new (mono_domain_get (), ev.filename);
118                 *code = ev.code;
119                 *reqnum = ev.fr.reqnum;
120                 return TRUE;
121         }
122
123         return FALSE;
124 }
125 #endif
126
127 #ifndef HAVE_SYS_INOTIFY_H
128 int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
129 {
130         return -1;
131 }
132
133 int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask)
134 {
135         return -1;
136 }
137
138 int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
139 {
140         return -1;
141 }
142 #else
143 #include <sys/inotify.h>
144 #include <errno.h>
145
146 int
147 ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
148 {
149         return inotify_init ();
150 }
151
152 int
153 ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *name, gint32 mask)
154 {
155         MonoError error;
156         char *str, *path;
157         int retval;
158
159         if (name == NULL)
160                 return -1;
161
162         str = mono_string_to_utf8_checked (name, &error);
163         if (mono_error_set_pending_exception (&error))
164                 return -1;
165         path = mono_portability_find_file (str, TRUE);
166         if (!path)
167                 path = str;
168
169         retval = inotify_add_watch (fd, path, mask);
170         if (retval < 0) {
171                 switch (errno) {
172                 case EACCES:
173                         errno = ERROR_ACCESS_DENIED;
174                         break;
175                 case EBADF:
176                         errno = ERROR_INVALID_HANDLE;
177                         break;
178                 case EFAULT:
179                         errno = ERROR_INVALID_ACCESS;
180                         break;
181                 case EINVAL:
182                         errno = ERROR_INVALID_DATA;
183                         break;
184                 case ENOMEM:
185                         errno = ERROR_NOT_ENOUGH_MEMORY;
186                         break;
187                 case ENOSPC:
188                         errno = ERROR_TOO_MANY_OPEN_FILES;
189                         break;
190                 default:
191                         errno = ERROR_GEN_FAILURE;
192                         break;
193                 }
194                 mono_marshal_set_last_error ();
195         }
196         if (path != str)
197                 g_free (path);
198         g_free (str);
199         return retval;
200 }
201
202 int
203 ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor)
204 {
205         return inotify_rm_watch (fd, watch_descriptor);
206 }
207 #endif
208
209 #if HAVE_KQUEUE
210
211 static void
212 interrupt_kevent (gpointer data)
213 {
214         int *kq_ptr = data;
215
216         /* Interrupt the kevent () call by closing the fd */
217         close (*kq_ptr);
218         /* Signal to managed code that the fd is closed */
219         *kq_ptr = -1;
220 }
221
222 /*
223  * ves_icall_System_IO_KqueueMonitor_kevent_notimeout:
224  *
225  *   Call kevent (), while handling runtime interruptions.
226  */
227 int
228 ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents)
229 {
230         int res;
231         gboolean interrupted;
232
233         mono_thread_info_install_interrupt (interrupt_kevent, kq_ptr, &interrupted);
234         if (interrupted) {
235                 close (*kq_ptr);
236                 *kq_ptr = -1;
237                 return -1;
238         }
239
240         MONO_ENTER_GC_SAFE;
241         res = kevent (*kq_ptr, changelist, nchanges, eventlist, nevents, NULL);
242         MONO_EXIT_GC_SAFE;
243
244         mono_thread_info_uninstall_interrupt (&interrupted);
245
246         return res;
247 }
248
249 #else
250
251 int
252 ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents)
253 {
254         g_assert_not_reached ();
255         return -1;
256 }
257
258 #endif /* #if HAVE_KQUEUE */
259