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
ves_icall_System_IO_FSW_SupportsFSW(void)41 ves_icall_System_IO_FSW_SupportsFSW (void)
42 {
43 	return 1;
44 }
45 
46 gboolean
ves_icall_System_IO_FAMW_InternalFAMNextEvent(gpointer conn,MonoString ** filename,gint * code,gint * reqnum)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
ves_icall_System_IO_FSW_SupportsFSW(void)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
ves_icall_System_IO_FAMW_InternalFAMNextEvent(gpointer conn,MonoString ** filename,gint * code,gint * reqnum)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
ves_icall_System_IO_InotifyWatcher_GetInotifyInstance()131 int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
132 {
133 	return -1;
134 }
135 
ves_icall_System_IO_InotifyWatcher_AddWatch(int fd,MonoString * directory,gint32 mask)136 int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask)
137 {
138 	return -1;
139 }
140 
ves_icall_System_IO_InotifyWatcher_RemoveWatch(int fd,gint32 watch_descriptor)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
ves_icall_System_IO_InotifyWatcher_GetInotifyInstance()150 ves_icall_System_IO_InotifyWatcher_GetInotifyInstance ()
151 {
152 	return inotify_init ();
153 }
154 
155 int
ves_icall_System_IO_InotifyWatcher_AddWatch(int fd,MonoString * name,gint32 mask)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
ves_icall_System_IO_InotifyWatcher_RemoveWatch(int fd,gint32 watch_descriptor)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
interrupt_kevent(gpointer data)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
ves_icall_System_IO_KqueueMonitor_kevent_notimeout(int * kq_ptr,gpointer changelist,int nchanges,gpointer eventlist,int nevents)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
ves_icall_System_IO_KqueueMonitor_kevent_notimeout(int * kq_ptr,gpointer changelist,int nchanges,gpointer eventlist,int nevents)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 
263