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