kevent[] CreateChangeList(ref List <int> FdList) { if (FdList.Count == 0) { return(emptyEventList); } var changes = new List <kevent> (); foreach (int fd in FdList) { var change = new kevent { ident = (UIntPtr)fd, filter = EventFilter.Vnode, flags = EventFlags.Add | EventFlags.Enable | EventFlags.Clear, fflags = FilterFlags.VNodeDelete | FilterFlags.VNodeExtend | FilterFlags.VNodeRename | FilterFlags.VNodeAttrib | FilterFlags.VNodeLink | FilterFlags.VNodeRevoke | FilterFlags.VNodeWrite, data = IntPtr.Zero, udata = IntPtr.Zero }; changes.Add(change); } FdList.Clear(); return(changes.ToArray()); }
void Setup () { var initialFds = new List<int> (); // fsw.FullPath may end in '/', see https://bugzilla.xamarin.com/show_bug.cgi?id=5747 if (fsw.FullPath != "/" && fsw.FullPath.EndsWith ("/", StringComparison.Ordinal)) fullPathNoLastSlash = fsw.FullPath.Substring (0, fsw.FullPath.Length - 1); else fullPathNoLastSlash = fsw.FullPath; // GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. int fd = open (fullPathNoLastSlash, O_EVTONLY, 0); var resolvedFullPath = GetFilenameFromFd (fd); close (fd); if (resolvedFullPath != fullPathNoLastSlash) fixupPath = resolvedFullPath; else fixupPath = null; Scan (fullPathNoLastSlash, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList (ref initialFds); int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ()); throw new IOException (errMsg); } }
private void Monitor() { while (!KeventWatcher.stop) { kevent ev = default(kevent); ev.udata = IntPtr.Zero; kevent kevent = default(kevent); kevent.udata = IntPtr.Zero; timespec timespec = default(timespec); timespec.tv_sec = 0; timespec.tv_usec = 0; int num; lock (this) { num = KeventWatcher.kevent(KeventWatcher.conn, ref kevent, 0, ref ev, 1, ref timespec); } if (num > 0) { KeventData data = (KeventData)KeventWatcher.requests[ev.ident]; KeventWatcher.StopMonitoringDirectory(data); KeventWatcher.StartMonitoringDirectory(data); this.ProcessEvent(ev); } else { Thread.Sleep(500); } } lock (this) { KeventWatcher.thread = null; KeventWatcher.stop = false; } }
void Monitor() { while (!stop) { kevent ev = new kevent(); ev.udata = IntPtr.Zero; kevent nullev = new kevent(); nullev.udata = IntPtr.Zero; timespec ts = new timespec(); ts.tv_sec = 0; ts.tv_usec = 0; int haveEvents; lock (this) { haveEvents = kevent(conn, ref nullev, 0, ref ev, 1, ref ts); } if (haveEvents > 0) { // Restart monitoring KeventData data = (KeventData)requests [ev.ident]; StopMonitoringDirectory(data); StartMonitoringDirectory(data); ProcessEvent(ev); } else { System.Threading.Thread.Sleep(500); } } lock (this) { thread = null; stop = false; } }
private void Monitor() { while (!stop) { kevent evtlist = default(kevent); evtlist.udata = IntPtr.Zero; kevent ev = default(kevent); ev.udata = IntPtr.Zero; timespec ts = default(timespec); ts.tv_sec = 0; ts.tv_usec = 0; int num; lock (this) { num = kevent(conn, ref ev, 0, ref evtlist, 1, ref ts); } if (num > 0) { KeventData data = (KeventData)requests[evtlist.ident]; StopMonitoringDirectory(data); StartMonitoringDirectory(data); ProcessEvent(evtlist); } else { Thread.Sleep(500); } } lock (this) { thread = null; stop = false; } }
void Setup() { var initialFds = new List <int> (); // fsw.FullPath may end in '/', see https://bugzilla.xamarin.com/show_bug.cgi?id=5747 if (fsw.FullPath != "/" && fsw.FullPath.EndsWith("/", StringComparison.Ordinal)) { fullPathNoLastSlash = fsw.FullPath.Substring(0, fsw.FullPath.Length - 1); } else { fullPathNoLastSlash = fsw.FullPath; } // realpath() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. var sb = new StringBuilder(__DARWIN_MAXPATHLEN); if (realpath(fsw.FullPath, sb) == IntPtr.Zero) { var errMsg = String.Format("realpath({0}) failed, error code = '{1}'", fsw.FullPath, Marshal.GetLastWin32Error()); throw new IOException(errMsg); } var resolvedFullPath = sb.ToString(); if (resolvedFullPath != fullPathNoLastSlash) { fixupPath = resolvedFullPath; } else { fixupPath = null; } Scan(fullPathNoLastSlash, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_nsec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList(ref initialFds); int numEvents; int errno = 0; do { numEvents = kevent(conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { errno = Marshal.GetLastWin32Error(); } } while (numEvents == -1 && errno == EINTR); if (numEvents == -1) { var errMsg = String.Format("kevent() error at initial event registration, error code = '{0}'", errno); throw new IOException(errMsg); } }
static void StartMonitoringDirectory(KeventData data) { DirectoryInfo dir = new DirectoryInfo(data.Directory); if (data.DirEntries == null) { data.DirEntries = new Hashtable(); foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) { data.DirEntries.Add(fsi.FullName, new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime)); } } int fd = open(data.Directory, 0, 0); kevent ev = new kevent(); ev.udata = IntPtr.Zero; timespec nullts = new timespec(); nullts.tv_sec = 0; nullts.tv_usec = 0; if (fd > 0) { ev.ident = fd; ev.filter = EventFilter.Vnode; ev.flags = EventFlags.Add | EventFlags.Enable | EventFlags.OneShot; ev.fflags = // 20 | 2 | 1 | 8; FilterFlags.VNodeDelete | FilterFlags.VNodeWrite | FilterFlags.VNodeAttrib | // The following two values are the equivalent of the original value "20", but we suspect the original author meant // 0x20, we will review later with some test cases FilterFlags.VNodeLink | FilterFlags.VNodeExtend; ev.data = 0; ev.udata = Marshal.StringToHGlobalAuto(data.Directory); kevent outev = new kevent(); outev.udata = IntPtr.Zero; kevent(conn, ref ev, 1, ref outev, 0, ref nullts); data.ev = ev; requests [fd] = data; } if (!data.IncludeSubdirs) { return; } }
static void StartMonitoringDirectory(KeventData data) { DirectoryInfo dir = new DirectoryInfo(data.Directory); if (data.DirEntries == null) { data.DirEntries = new Hashtable(); foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) { data.DirEntries.Add(fsi.FullName, new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime)); } } int fd = open(data.Directory, 0, 0); kevent ev = new kevent(); ev.udata = IntPtr.Zero; timespec nullts = new timespec(); nullts.tv_sec = 0; nullts.tv_usec = 0; if (fd > 0) { ev.ident = fd; ev.filter = -4; ev.flags = 1 | 4 | 20; ev.fflags = 20 | 2 | 1 | 8; ev.data = 0; ev.udata = Marshal.StringToHGlobalAuto(data.Directory); kevent outev = new kevent(); outev.udata = IntPtr.Zero; kevent(conn, ref ev, 1, ref outev, 0, ref nullts); data.ev = ev; requests [fd] = data; } if (!data.IncludeSubdirs) { return; } }
private static void StartMonitoringDirectory(KeventData data) { DirectoryInfo directoryInfo = new DirectoryInfo(data.Directory); if (data.DirEntries == null) { data.DirEntries = new Hashtable(); FileSystemInfo[] fileSystemInfos = directoryInfo.GetFileSystemInfos(); foreach (FileSystemInfo fileSystemInfo in fileSystemInfos) { data.DirEntries.Add(fileSystemInfo.FullName, new KeventFileData(fileSystemInfo, fileSystemInfo.LastAccessTime, fileSystemInfo.LastWriteTime)); } } int num = open(data.Directory, 0, 0); kevent ev = default(kevent); ev.udata = IntPtr.Zero; timespec ts = default(timespec); ts.tv_sec = 0; ts.tv_usec = 0; if (num > 0) { ev.ident = num; ev.filter = -4; ev.flags = 21; ev.fflags = 31u; ev.data = 0; ev.udata = Marshal.StringToHGlobalAuto(data.Directory); kevent evtlist = default(kevent); evtlist.udata = IntPtr.Zero; kevent(conn, ref ev, 1, ref evtlist, 0, ref ts); data.ev = ev; requests[num] = data; } if (data.IncludeSubdirs) { } }
void Setup() { var initialFds = new List <int> (); // GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. int fd = open(fsw.FullPath, O_EVTONLY, 0); var resolvedFullPath = GetFilenameFromFd(fd); close(fd); if (resolvedFullPath != fsw.FullPath) { fixupPath = resolvedFullPath; } else { fixupPath = null; } Scan(fsw.FullPath, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList(ref initialFds); int numEvents = kevent(conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { var errMsg = String.Format("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error()); throw new IOException(errMsg); } }
static void StartMonitoringDirectory (KeventData data) { DirectoryInfo dir = new DirectoryInfo (data.Directory); if(data.DirEntries == null) { data.DirEntries = new Hashtable(); foreach (FileSystemInfo fsi in dir.GetFileSystemInfos() ) data.DirEntries.Add(fsi.FullName, new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime)); } int fd = open(data.Directory, 0, 0); kevent ev = new kevent(); ev.udata = IntPtr.Zero; timespec nullts = new timespec(); nullts.tv_sec = 0; nullts.tv_usec = 0; if (fd > 0) { ev.ident = fd; ev.filter = -4; ev.flags = 1 | 4 | 20; ev.fflags = 20 | 2 | 1 | 8; ev.data = 0; ev.udata = Marshal.StringToHGlobalAuto (data.Directory); kevent outev = new kevent(); outev.udata = IntPtr.Zero; kevent (conn, ref ev, 1, ref outev, 0, ref nullts); data.ev = ev; requests [fd] = data; } if (!data.IncludeSubdirs) return; }
void Monitor() { var timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)500000000 }; var eventBuffer = new kevent[32]; var newFds = new List <int> (); List <PathData> removeQueue = new List <PathData> (); List <string> rescanQueue = new List <string> (); while (!requestStop) { var changes = CreateChangeList(ref newFds); int numEvents = kevent(conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref timeout); if (numEvents == -1) { var errMsg = String.Format("kevent() error, error code = '{0}'", Marshal.GetLastWin32Error()); fsw.OnError(new ErrorEventArgs(new IOException(errMsg))); } if (numEvents == 0) { continue; } for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.OnError(new ErrorEventArgs(new IOException(errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { removeQueue.Add(pathData); } else if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) { rescanQueue.Add(pathData.Path); } else { PostEvent(FileAction.Modified, pathData.Path); } } else if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { var newFilename = GetFilenameFromFd(pathData.Fd); if (newFilename.StartsWith(fsw.FullPath)) { Rename(pathData, newFilename); } else //moved outside of our watched dir so stop watching { RemoveTree(pathData); } } else if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) { PostEvent(FileAction.Modified, pathData.Path); } } removeQueue.ForEach(Remove); removeQueue.Clear(); rescanQueue.ForEach(path => { Scan(path, true, ref newFds); }); rescanQueue.Clear(); } }
void Monitor () { var eventBuffer = new kevent[32]; var newFds = new List<int> (); List<PathData> removeQueue = new List<PathData> (); List<string> rescanQueue = new List<string> (); int retries = 0; while (!requestStop) { var changes = CreateChangeList (ref newFds); // We are calling an icall, so have to marshal manually // Marshal in int ksize = Marshal.SizeOf<kevent> (); var changesNative = Marshal.AllocHGlobal (ksize * changes.Length); for (int i = 0; i < changes.Length; ++i) Marshal.StructureToPtr (changes [i], changesNative + (i * ksize), false); var eventBufferNative = Marshal.AllocHGlobal (ksize * eventBuffer.Length); int numEvents = kevent_notimeout (ref conn, changesNative, changes.Length, eventBufferNative, eventBuffer.Length); // Marshal out Marshal.FreeHGlobal (changesNative); for (int i = 0; i < numEvents; ++i) eventBuffer [i] = Marshal.PtrToStructure<kevent> (eventBufferNative + (i * ksize)); Marshal.FreeHGlobal (eventBufferNative); if (numEvents == -1) { // Stop () signals us to stop by closing the connection if (requestStop) break; if (++retries == 3) throw new IOException (String.Format ( "persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ())); continue; } retries = 0; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; if (!fdsDict.ContainsKey ((int)kevt.ident)) // The event is for a file that was removed continue; var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format ("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.DispatchErrorEvents (new ErrorEventArgs (new IOException (errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { if (pathData.Path == fullPathNoLastSlash) // The root path is deleted; exit silently return; removeQueue.Add (pathData); continue; } if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { UpdatePath (pathData); } if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET rescanQueue.Add (pathData.Path); else PostEvent (FileAction.Modified, pathData.Path); } if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) PostEvent (FileAction.Modified, pathData.Path); } removeQueue.ForEach (Remove); removeQueue.Clear (); rescanQueue.ForEach (path => { Scan (path, true, ref newFds); }); rescanQueue.Clear (); } }
kevent[] CreateChangeList (ref List<int> FdList) { if (FdList.Count == 0) return emptyEventList; var changes = new List<kevent> (); foreach (int fd in FdList) { var change = new kevent { ident = (UIntPtr)fd, filter = EventFilter.Vnode, flags = EventFlags.Add | EventFlags.Enable | EventFlags.Clear, fflags = FilterFlags.VNodeDelete | FilterFlags.VNodeExtend | FilterFlags.VNodeRename | FilterFlags.VNodeAttrib | FilterFlags.VNodeLink | FilterFlags.VNodeRevoke | FilterFlags.VNodeWrite, data = IntPtr.Zero, udata = IntPtr.Zero }; changes.Add (change); } FdList.Clear (); return changes.ToArray (); }
void Setup () { var initialFds = new List<int> (); // fsw.FullPath may end in '/', see https://bugzilla.xamarin.com/show_bug.cgi?id=5747 if (fsw.FullPath != "/" && fsw.FullPath.EndsWith ("/", StringComparison.Ordinal)) fullPathNoLastSlash = fsw.FullPath.Substring (0, fsw.FullPath.Length - 1); else fullPathNoLastSlash = fsw.FullPath; // GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. int fd = open (fullPathNoLastSlash, O_EVTONLY, 0); var resolvedFullPath = GetFilenameFromFd (fd); close (fd); if (resolvedFullPath != fullPathNoLastSlash) fixupPath = resolvedFullPath; else fixupPath = null; Scan (fullPathNoLastSlash, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_nsec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList (ref initialFds); int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ()); throw new IOException (errMsg); } }
void Monitor () { var timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)500000000 }; var eventBuffer = new kevent[32]; var newFds = new List<int> (); List<PathData> removeQueue = new List<PathData> (); List<string> rescanQueue = new List<string> (); int retries = 0; while (!requestStop) { var changes = CreateChangeList (ref newFds); int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref timeout); if (numEvents == -1) { if (++retries == 3) throw new IOException (String.Format ( "persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ())); continue; } retries = 0; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format ("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.DispatchErrorEvents (new ErrorEventArgs (new IOException (errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { removeQueue.Add (pathData); continue; } if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { UpdatePath (pathData); } if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET rescanQueue.Add (pathData.Path); else PostEvent (FileAction.Modified, pathData.Path); } if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) PostEvent (FileAction.Modified, pathData.Path); } removeQueue.ForEach (Remove); removeQueue.Clear (); rescanQueue.ForEach (path => { Scan (path, true, ref newFds); }); rescanQueue.Clear (); } }
void Monitor() { var timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)500000000 }; var eventBuffer = new kevent[32]; var newFds = new List <int> (); List <PathData> removeQueue = new List <PathData> (); List <string> rescanQueue = new List <string> (); int retries = 0; while (!requestStop) { var changes = CreateChangeList(ref newFds); int numEvents = kevent(conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref timeout); if (numEvents == -1) { if (++retries == 3) { throw new IOException(String.Format( "persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error())); } continue; } retries = 0; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.DispatchErrorEvents(new ErrorEventArgs(new IOException(errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { removeQueue.Add(pathData); continue; } if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { UpdatePath(pathData); } if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET { rescanQueue.Add(pathData.Path); } else { PostEvent(FileAction.Modified, pathData.Path); } } if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) { PostEvent(FileAction.Modified, pathData.Path); } } removeQueue.ForEach(Remove); removeQueue.Clear(); rescanQueue.ForEach(path => { Scan(path, true, ref newFds); }); rescanQueue.Clear(); } }
private void ProcessEvent(kevent ev) { lock (this) { KeventData keventData = (KeventData)KeventWatcher.requests[ev.ident]; if (keventData.Enabled) { string text = string.Empty; FileSystemWatcher fsw = keventData.FSW; DirectoryInfo directoryInfo = new DirectoryInfo(keventData.Directory); FileSystemInfo changedFsi = null; try { foreach (FileSystemInfo fileSystemInfo in directoryInfo.GetFileSystemInfos()) { if (keventData.DirEntries.ContainsKey(fileSystemInfo.FullName) && fileSystemInfo is FileInfo) { KeventFileData keventFileData = (KeventFileData)keventData.DirEntries[fileSystemInfo.FullName]; if (keventFileData.LastWriteTime != fileSystemInfo.LastWriteTime) { text = fileSystemInfo.Name; FileAction fa = FileAction.Modified; keventData.DirEntries[fileSystemInfo.FullName] = new KeventFileData(fileSystemInfo, fileSystemInfo.LastAccessTime, fileSystemInfo.LastWriteTime); if (fsw.IncludeSubdirectories && fileSystemInfo is DirectoryInfo) { keventData.Directory = text; KeventWatcher.requests[ev.ident] = keventData; this.ProcessEvent(ev); } this.PostEvent(text, fsw, fa, changedFsi); } } } } catch (Exception) { } try { bool flag = true; while (flag) { foreach (object obj in keventData.DirEntries.Values) { KeventFileData keventFileData2 = (KeventFileData)obj; if (!File.Exists(keventFileData2.fsi.FullName) && !Directory.Exists(keventFileData2.fsi.FullName)) { text = keventFileData2.fsi.Name; FileAction fa = FileAction.Removed; keventData.DirEntries.Remove(keventFileData2.fsi.FullName); this.PostEvent(text, fsw, fa, changedFsi); break; } } flag = false; } } catch (Exception) { } try { foreach (FileSystemInfo fileSystemInfo2 in directoryInfo.GetFileSystemInfos()) { if (!keventData.DirEntries.ContainsKey(fileSystemInfo2.FullName)) { changedFsi = fileSystemInfo2; text = fileSystemInfo2.Name; FileAction fa = FileAction.Added; keventData.DirEntries[fileSystemInfo2.FullName] = new KeventFileData(fileSystemInfo2, fileSystemInfo2.LastAccessTime, fileSystemInfo2.LastWriteTime); this.PostEvent(text, fsw, fa, changedFsi); } } } catch (Exception) { } } } }
void ProcessEvent(kevent ev) { lock (this) { KeventData data = (KeventData)requests [ev.ident]; if (!data.Enabled) { return; } FileSystemWatcher fsw; string filename = ""; fsw = data.FSW; FileAction fa = 0; DirectoryInfo dir = new DirectoryInfo(data.Directory); FileSystemInfo changedFsi = null; try { foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) { if (data.DirEntries.ContainsKey(fsi.FullName) && (fsi is FileInfo)) { KeventFileData entry = (KeventFileData)data.DirEntries [fsi.FullName]; if (entry.LastWriteTime != fsi.LastWriteTime) { filename = fsi.Name; fa = FileAction.Modified; data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); if (fsw.IncludeSubdirectories && fsi is DirectoryInfo) { data.Directory = filename; requests [ev.ident] = data; ProcessEvent(ev); } PostEvent(filename, fsw, fa, changedFsi); } } } } catch (Exception) { // The file system infos were changed while we processed them } // Deleted try { bool deleteMatched = true; while (deleteMatched) { foreach (KeventFileData entry in data.DirEntries.Values) { if (!File.Exists(entry.fsi.FullName) && !Directory.Exists(entry.fsi.FullName)) { filename = entry.fsi.Name; fa = FileAction.Removed; data.DirEntries.Remove(entry.fsi.FullName); PostEvent(filename, fsw, fa, changedFsi); break; } } deleteMatched = false; } } catch (Exception) { // The file system infos were changed while we processed them } // Added try { foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) { if (!data.DirEntries.ContainsKey(fsi.FullName)) { changedFsi = fsi; filename = fsi.Name; fa = FileAction.Added; data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); PostEvent(filename, fsw, fa, changedFsi); } } } catch (Exception) { // The file system infos were changed while we processed them } } }
extern static int kevent(int kqueue, ref kevent ev, int nchanges, ref kevent evtlist, int nevents, ref timespec ts);
void Monitor () { while (!stop) { kevent ev = new kevent(); ev.udata = IntPtr.Zero; kevent nullev = new kevent(); nullev.udata = IntPtr.Zero; timespec ts = new timespec(); ts.tv_sec = 0; ts.tv_usec = 0; int haveEvents; lock (this) { haveEvents = kevent (conn, ref nullev, 0, ref ev, 1, ref ts); } if (haveEvents > 0) { // Restart monitoring KeventData data = (KeventData) requests [ev.ident]; StopMonitoringDirectory (data); StartMonitoringDirectory (data); ProcessEvent (ev); } else { System.Threading.Thread.Sleep (500); } } lock (this) { thread = null; stop = false; } }
static void StartMonitoringDirectory (KeventData data) { DirectoryInfo dir = new DirectoryInfo (data.Directory); if(data.DirEntries == null) { data.DirEntries = new Hashtable(); foreach (FileSystemInfo fsi in dir.GetFileSystemInfos() ) data.DirEntries.Add(fsi.FullName, new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime)); } int fd = open(data.Directory, 0, 0); kevent ev = new kevent(); ev.udata = IntPtr.Zero; timespec nullts = new timespec(); nullts.tv_sec = 0; nullts.tv_usec = 0; if (fd > 0) { ev.ident = fd; ev.filter = EventFilter.Vnode; ev.flags = EventFlags.Add | EventFlags.Enable | EventFlags.OneShot; ev.fflags = // 20 | 2 | 1 | 8; FilterFlags.VNodeDelete | FilterFlags.VNodeWrite | FilterFlags.VNodeAttrib | // The following two values are the equivalent of the original value "20", but we suspect the original author meant // 0x20, we will review later with some test cases FilterFlags.VNodeLink | FilterFlags.VNodeExtend; ev.data = 0; ev.udata = Marshal.StringToHGlobalAuto (data.Directory); kevent outev = new kevent(); outev.udata = IntPtr.Zero; kevent (conn, ref ev, 1, ref outev, 0, ref nullts); data.ev = ev; requests [fd] = data; } if (!data.IncludeSubdirs) return; }
static int kevent(int kqueue, ref kevent ev, int nchanges, ref kevent evtlist, int nevents, ref timespec ts) { throw new System.NotImplementedException(); }
private void ProcessEvent(kevent ev) { lock (this) { KeventData keventData = (KeventData)requests[ev.ident]; if (keventData.Enabled) { string empty = string.Empty; FileSystemWatcher fSW = keventData.FSW; FileAction fileAction = (FileAction)0; DirectoryInfo directoryInfo = new DirectoryInfo(keventData.Directory); FileSystemInfo changedFsi = null; try { FileSystemInfo[] fileSystemInfos = directoryInfo.GetFileSystemInfos(); foreach (FileSystemInfo fileSystemInfo in fileSystemInfos) { if (keventData.DirEntries.ContainsKey(fileSystemInfo.FullName) && fileSystemInfo is FileInfo) { KeventFileData keventFileData = (KeventFileData)keventData.DirEntries[fileSystemInfo.FullName]; if (keventFileData.LastWriteTime != fileSystemInfo.LastWriteTime) { empty = fileSystemInfo.Name; fileAction = FileAction.Modified; keventData.DirEntries[fileSystemInfo.FullName] = new KeventFileData(fileSystemInfo, fileSystemInfo.LastAccessTime, fileSystemInfo.LastWriteTime); if (fSW.IncludeSubdirectories && fileSystemInfo is DirectoryInfo) { keventData.Directory = empty; requests[ev.ident] = keventData; ProcessEvent(ev); } PostEvent(empty, fSW, fileAction, changedFsi); } } } } catch (Exception) { } try { bool flag = true; while (flag) { foreach (KeventFileData value in keventData.DirEntries.Values) { if (!File.Exists(value.fsi.FullName) && !Directory.Exists(value.fsi.FullName)) { empty = value.fsi.Name; fileAction = FileAction.Removed; keventData.DirEntries.Remove(value.fsi.FullName); PostEvent(empty, fSW, fileAction, changedFsi); break; } } flag = false; } } catch (Exception) { } try { FileSystemInfo[] fileSystemInfos2 = directoryInfo.GetFileSystemInfos(); foreach (FileSystemInfo fileSystemInfo2 in fileSystemInfos2) { if (!keventData.DirEntries.ContainsKey(fileSystemInfo2.FullName)) { changedFsi = fileSystemInfo2; empty = fileSystemInfo2.Name; fileAction = FileAction.Added; keventData.DirEntries[fileSystemInfo2.FullName] = new KeventFileData(fileSystemInfo2, fileSystemInfo2.LastAccessTime, fileSystemInfo2.LastWriteTime); PostEvent(empty, fSW, fileAction, changedFsi); } } } catch (Exception) { } } } }
void ProcessEvent (kevent ev) { lock (this) { KeventData data = (KeventData) requests [ev.ident]; if (!data.Enabled) return; FileSystemWatcher fsw; string filename = ""; fsw = data.FSW; FileAction fa = 0; DirectoryInfo dir = new DirectoryInfo (data.Directory); FileSystemInfo changedFsi = null; try { foreach (FileSystemInfo fsi in dir.GetFileSystemInfos() ) if (data.DirEntries.ContainsKey (fsi.FullName) && (fsi is FileInfo)) { KeventFileData entry = (KeventFileData) data.DirEntries [fsi.FullName]; if (entry.LastWriteTime != fsi.LastWriteTime) { filename = fsi.Name; fa = FileAction.Modified; data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); if (fsw.IncludeSubdirectories && fsi is DirectoryInfo) { data.Directory = filename; requests [ev.ident] = data; ProcessEvent(ev); } PostEvent(filename, fsw, fa, changedFsi); } } } catch (Exception) { // The file system infos were changed while we processed them } // Deleted try { bool deleteMatched = true; while(deleteMatched) { foreach (KeventFileData entry in data.DirEntries.Values) { if (!File.Exists (entry.fsi.FullName) && !Directory.Exists (entry.fsi.FullName)) { filename = entry.fsi.Name; fa = FileAction.Removed; data.DirEntries.Remove (entry.fsi.FullName); PostEvent(filename, fsw, fa, changedFsi); break; } } deleteMatched = false; } } catch (Exception) { // The file system infos were changed while we processed them } // Added try { foreach (FileSystemInfo fsi in dir.GetFileSystemInfos()) if (!data.DirEntries.ContainsKey (fsi.FullName)) { changedFsi = fsi; filename = fsi.Name; fa = FileAction.Added; data.DirEntries [fsi.FullName] = new KeventFileData(fsi, fsi.LastAccessTime, fsi.LastWriteTime); PostEvent(filename, fsw, fa, changedFsi); } } catch (Exception) { // The file system infos were changed while we processed them } } }
void Monitor() { var eventBuffer = new kevent[32]; var newFds = new List <int> (); List <PathData> removeQueue = new List <PathData> (); List <string> rescanQueue = new List <string> (); int retries = 0; while (!requestStop) { var changes = CreateChangeList(ref newFds); // We are calling an icall, so have to marshal manually // Marshal in int ksize = Marshal.SizeOf <kevent> (); var changesNative = Marshal.AllocHGlobal(ksize * changes.Length); for (int i = 0; i < changes.Length; ++i) { Marshal.StructureToPtr(changes [i], changesNative + (i * ksize), false); } var eventBufferNative = Marshal.AllocHGlobal(ksize * eventBuffer.Length); int numEvents = kevent_notimeout(ref conn, changesNative, changes.Length, eventBufferNative, eventBuffer.Length); // Marshal out Marshal.FreeHGlobal(changesNative); for (int i = 0; i < numEvents; ++i) { eventBuffer [i] = Marshal.PtrToStructure <kevent> (eventBufferNative + (i * ksize)); } Marshal.FreeHGlobal(eventBufferNative); if (numEvents == -1) { // Stop () signals us to stop by closing the connection if (requestStop) { break; } int errno = Marshal.GetLastWin32Error(); if (errno != EINTR && ++retries == 3) { throw new IOException(String.Format( "persistent kevent() error, error code = '{0}'", errno)); } continue; } retries = 0; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; if (!fdsDict.ContainsKey((int)kevt.ident)) { // The event is for a file that was removed continue; } var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.DispatchErrorEvents(new ErrorEventArgs(new IOException(errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { if (pathData.Path == fullPathNoLastSlash) { // The root path is deleted; exit silently return; } removeQueue.Add(pathData); continue; } if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { UpdatePath(pathData); } if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET { rescanQueue.Add(pathData.Path); } else { PostEvent(FileAction.Modified, pathData.Path); } } if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) { PostEvent(FileAction.Modified, pathData.Path); } } removeQueue.ForEach(Remove); removeQueue.Clear(); rescanQueue.ForEach(path => { Scan(path, true, ref newFds); }); rescanQueue.Clear(); } }
void Setup () { var initialFds = new List<int> (); // fsw.FullPath may end in '/', see https://bugzilla.xamarin.com/show_bug.cgi?id=5747 if (fsw.FullPath != "/" && fsw.FullPath.EndsWith ("/", StringComparison.Ordinal)) fullPathNoLastSlash = fsw.FullPath.Substring (0, fsw.FullPath.Length - 1); else fullPathNoLastSlash = fsw.FullPath; // realpath() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. var sb = new StringBuilder (__DARWIN_MAXPATHLEN); if (realpath(fsw.FullPath, sb) == IntPtr.Zero) { var errMsg = String.Format ("realpath({0}) failed, error code = '{1}'", fsw.FullPath, Marshal.GetLastWin32Error ()); throw new IOException (errMsg); } var resolvedFullPath = sb.ToString(); if (resolvedFullPath != fullPathNoLastSlash) fixupPath = resolvedFullPath; else fixupPath = null; Scan (fullPathNoLastSlash, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_nsec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList (ref initialFds); int numEvents; int errno = 0; do { numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { errno = Marshal.GetLastWin32Error (); } } while (numEvents == -1 && errno == EINTR); if (numEvents == -1) { var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", errno); throw new IOException (errMsg); } }
void Setup () { var initialFds = new List<int> (); // GetFilenameFromFd() returns the *realpath* which can be different than fsw.FullPath because symlinks. // If so, introduce a fixup step. int fd = open (fsw.FullPath, O_EVTONLY, 0); var resolvedFullPath = GetFilenameFromFd (fd); close (fd); if (resolvedFullPath != fsw.FullPath) fixupPath = resolvedFullPath; else fixupPath = null; Scan (fsw.FullPath, false, ref initialFds); var immediate_timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)0 }; var eventBuffer = new kevent[0]; // we don't want to take any events from the queue at this point var changes = CreateChangeList (ref initialFds); int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref immediate_timeout); if (numEvents == -1) { var errMsg = String.Format ("kevent() error at initial event registration, error code = '{0}'", Marshal.GetLastWin32Error ()); throw new IOException (errMsg); } }
void Monitor () { var timeout = new timespec { tv_sec = (IntPtr)0, tv_usec = (IntPtr)500000000 }; var eventBuffer = new kevent[32]; var newFds = new List<int> (); List<PathData> removeQueue = new List<PathData> (); List<string> rescanQueue = new List<string> (); while (!requestStop) { var changes = CreateChangeList (ref newFds); int numEvents = kevent (conn, changes, changes.Length, eventBuffer, eventBuffer.Length, ref timeout); if (numEvents == -1) { var errMsg = String.Format ("kevent() error, error code = '{0}'", Marshal.GetLastWin32Error ()); fsw.OnError (new ErrorEventArgs (new IOException (errMsg))); } if (numEvents == 0) continue; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format ("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.OnError (new ErrorEventArgs (new IOException (errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) removeQueue.Add (pathData); else if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) rescanQueue.Add (pathData.Path); else PostEvent (FileAction.Modified, pathData.Path); } else if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { var newFilename = GetFilenameFromFd (pathData.Fd); if (newFilename.StartsWith (fsw.FullPath)) Rename (pathData, newFilename); else //moved outside of our watched dir so stop watching RemoveTree (pathData); } else if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) PostEvent (FileAction.Modified, pathData.Path); } removeQueue.ForEach (Remove); removeQueue.Clear (); rescanQueue.ForEach (path => { Scan (path, true, ref newFds); }); rescanQueue.Clear (); } }
void Monitor() { var eventBuffer = new kevent[32]; var newFds = new List <int> (); List <PathData> removeQueue = new List <PathData> (); List <string> rescanQueue = new List <string> (); int retries = 0; while (!requestStop) { var changes = CreateChangeList(ref newFds); int numEvents = kevent_notimeout(conn, changes, changes.Length, eventBuffer, eventBuffer.Length, IntPtr.Zero); if (numEvents == -1) { // Stop () signals us to stop by closing the connection if (requestStop) { break; } if (++retries == 3) { throw new IOException(String.Format( "persistent kevent() error, error code = '{0}'", Marshal.GetLastWin32Error())); } continue; } retries = 0; for (var i = 0; i < numEvents; i++) { var kevt = eventBuffer [i]; if (!fdsDict.ContainsKey((int)kevt.ident)) { // The event is for a file that was removed continue; } var pathData = fdsDict [(int)kevt.ident]; if ((kevt.flags & EventFlags.Error) == EventFlags.Error) { var errMsg = String.Format("kevent() error watching path '{0}', error code = '{1}'", pathData.Path, kevt.data); fsw.DispatchErrorEvents(new ErrorEventArgs(new IOException(errMsg))); continue; } if ((kevt.fflags & FilterFlags.VNodeDelete) == FilterFlags.VNodeDelete || (kevt.fflags & FilterFlags.VNodeRevoke) == FilterFlags.VNodeRevoke) { if (pathData.Path == fullPathNoLastSlash) { // The root path is deleted; exit silently return; } removeQueue.Add(pathData); continue; } if ((kevt.fflags & FilterFlags.VNodeRename) == FilterFlags.VNodeRename) { UpdatePath(pathData); } if ((kevt.fflags & FilterFlags.VNodeWrite) == FilterFlags.VNodeWrite) { if (pathData.IsDirectory) //TODO: Check if dirs trigger Changed events on .NET { rescanQueue.Add(pathData.Path); } else { PostEvent(FileAction.Modified, pathData.Path); } } if ((kevt.fflags & FilterFlags.VNodeAttrib) == FilterFlags.VNodeAttrib || (kevt.fflags & FilterFlags.VNodeExtend) == FilterFlags.VNodeExtend) { PostEvent(FileAction.Modified, pathData.Path); } } removeQueue.ForEach(Remove); removeQueue.Clear(); rescanQueue.ForEach(path => { Scan(path, true, ref newFds); }); rescanQueue.Clear(); } }