/** * \file * File System Watcher internal calls * * Authors: * Gonzalo Paniagua Javier (gonzalo@ximian.com) * * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_EVENT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #include #include #include #include #include #include #ifdef HOST_WIN32 /* * TODO: * We use the managed watcher on windows, so the code inside this #if is never used */ gint ves_icall_System_IO_FSW_SupportsFSW (void) { return 1; } gboolean ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn, MonoString **filename, gint *code, gint *reqnum) { return FALSE; } #else static int (*FAMNextEvent) (gpointer, gpointer); gint ves_icall_System_IO_FSW_SupportsFSW (void) { #if HAVE_KQUEUE return 3; #else MonoDl *fam_module; int lib_used = 4; /* gamin */ int inotify_instance; char *err; inotify_instance = ves_icall_System_IO_InotifyWatcher_GetInotifyInstance (); if (inotify_instance != -1) { close (inotify_instance); return 5; /* inotify */ } fam_module = mono_dl_open ("libgamin-1.so", MONO_DL_LAZY, NULL); if (fam_module == NULL) { lib_used = 2; /* FAM */ fam_module = mono_dl_open ("libfam.so", MONO_DL_LAZY, NULL); } if (fam_module == NULL) return 0; err = mono_dl_symbol (fam_module, "FAMNextEvent", (gpointer *) &FAMNextEvent); g_free (err); if (FAMNextEvent == NULL) return 0; return lib_used; #endif } /* Almost copied from fam.h. Weird, I know */ typedef struct { gint reqnum; } FAMRequest; typedef struct FAMEvent { gpointer fc; FAMRequest fr; gchar *hostname; gchar filename [PATH_MAX]; gpointer userdata; gint code; } FAMEvent; gboolean ves_icall_System_IO_FAMW_InternalFAMNextEvent (gpointer conn, MonoString **filename, gint *code, gint *reqnum) { MonoError error; FAMEvent ev; if (FAMNextEvent (conn, &ev) == 1) { *filename = mono_string_new_checked (mono_domain_get (), ev.filename, &error); *code = ev.code; *reqnum = ev.fr.reqnum; if (mono_error_set_pending_exception (&error)) return FALSE; return TRUE; } return FALSE; } #endif #ifndef HAVE_SYS_INOTIFY_H int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance () { return -1; } int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *directory, gint32 mask) { return -1; } int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor) { return -1; } #else #include #include int ves_icall_System_IO_InotifyWatcher_GetInotifyInstance () { return inotify_init (); } int ves_icall_System_IO_InotifyWatcher_AddWatch (int fd, MonoString *name, gint32 mask) { MonoError error; char *str, *path; int retval; if (name == NULL) return -1; str = mono_string_to_utf8_checked (name, &error); if (mono_error_set_pending_exception (&error)) return -1; path = mono_portability_find_file (str, TRUE); if (!path) path = str; retval = inotify_add_watch (fd, path, mask); if (retval < 0) { switch (errno) { case EACCES: errno = ERROR_ACCESS_DENIED; break; case EBADF: errno = ERROR_INVALID_HANDLE; break; case EFAULT: errno = ERROR_INVALID_ACCESS; break; case EINVAL: errno = ERROR_INVALID_DATA; break; case ENOMEM: errno = ERROR_NOT_ENOUGH_MEMORY; break; case ENOSPC: errno = ERROR_TOO_MANY_OPEN_FILES; break; default: errno = ERROR_GEN_FAILURE; break; } mono_marshal_set_last_error (); } if (path != str) g_free (path); g_free (str); return retval; } int ves_icall_System_IO_InotifyWatcher_RemoveWatch (int fd, gint32 watch_descriptor) { return inotify_rm_watch (fd, watch_descriptor); } #endif #if HAVE_KQUEUE static void interrupt_kevent (gpointer data) { int *kq_ptr = data; /* Interrupt the kevent () call by closing the fd */ close (*kq_ptr); /* Signal to managed code that the fd is closed */ *kq_ptr = -1; } /* * ves_icall_System_IO_KqueueMonitor_kevent_notimeout: * * Call kevent (), while handling runtime interruptions. */ int ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents) { int res; gboolean interrupted; mono_thread_info_install_interrupt (interrupt_kevent, kq_ptr, &interrupted); if (interrupted) { close (*kq_ptr); *kq_ptr = -1; return -1; } MONO_ENTER_GC_SAFE; res = kevent (*kq_ptr, changelist, nchanges, eventlist, nevents, NULL); MONO_EXIT_GC_SAFE; mono_thread_info_uninstall_interrupt (&interrupted); return res; } #else int ves_icall_System_IO_KqueueMonitor_kevent_notimeout (int *kq_ptr, gpointer changelist, int nchanges, gpointer eventlist, int nevents) { g_assert_not_reached (); return -1; } #endif /* #if HAVE_KQUEUE */