Runner.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using System;
  2. using System.Threading;
  3. using System.IO;
  4. using System.Collections.Generic;
  5. namespace De.Thekid.INotify
  6. {
  7. // List of possible changes
  8. public enum Change
  9. {
  10. CREATE, MODIFY, DELETE, MOVED_FROM, MOVED_TO
  11. }
  12. /// Main class
  13. public class Runner
  14. {
  15. // Mappings
  16. protected static Dictionary<WatcherChangeTypes, Change> Changes = new Dictionary<WatcherChangeTypes, Change>();
  17. private List<Thread> _threads = new List<Thread>();
  18. private bool _stopMonitoring = false;
  19. private ManualResetEventSlim _stopMonitoringEvent;
  20. private object _notificationReactionLock = new object();
  21. private Arguments _args = null;
  22. static Runner()
  23. {
  24. Changes[WatcherChangeTypes.Created]= Change.CREATE;
  25. Changes[WatcherChangeTypes.Changed]= Change.MODIFY;
  26. Changes[WatcherChangeTypes.Deleted]= Change.DELETE;
  27. }
  28. public Runner(Arguments args)
  29. {
  30. _args = args;
  31. }
  32. /// Callback for errors in watcher
  33. protected void OnWatcherError(object source, ErrorEventArgs e)
  34. {
  35. Console.Error.WriteLine("*** {0}", e.GetException());
  36. }
  37. private void OnWatcherNotification(object sender, FileSystemEventArgs e)
  38. {
  39. FileSystemWatcher w = (FileSystemWatcher)sender;
  40. HandleNotification(w, e, () => Output(Console.Out, _args.Format, w, Changes[e.ChangeType], e.Name));
  41. }
  42. private void OnRenameNotification(object sender, RenamedEventArgs e)
  43. {
  44. FileSystemWatcher w = (FileSystemWatcher)sender;
  45. HandleNotification(w, e, () =>
  46. {
  47. Output(Console.Out, _args.Format, w, Change.MOVED_FROM, e.OldName);
  48. Output(Console.Out, _args.Format, w, Change.MOVED_TO, e.Name);
  49. });
  50. }
  51. private void HandleNotification(FileSystemWatcher sender, FileSystemEventArgs e, Action outputAction)
  52. {
  53. FileSystemWatcher w = (FileSystemWatcher)sender;
  54. // Lock so we don't output more than one change if we were only supposed to watch for one.
  55. // And to serialize access to the console
  56. lock (_notificationReactionLock)
  57. {
  58. // if only looking for one change and another thread beat us to it, return
  59. if (!_args.Monitor && _stopMonitoring)
  60. {
  61. return;
  62. }
  63. if (null != _args.Exclude && _args.Exclude.IsMatch(e.FullPath))
  64. {
  65. return;
  66. }
  67. outputAction();
  68. // If only looking for one change, signal to stop
  69. if (!_args.Monitor)
  70. {
  71. _stopMonitoring = true;
  72. _stopMonitoringEvent.Set();
  73. }
  74. }
  75. }
  76. /// Output method
  77. protected void Output(TextWriter writer, string[] tokens, FileSystemWatcher source, Change type, string name)
  78. {
  79. foreach (var token in tokens)
  80. {
  81. var path = Path.Combine(source.Path, name);
  82. switch (token[0])
  83. {
  84. case 'e':
  85. writer.Write(type);
  86. if (Directory.Exists(path))
  87. {
  88. writer.Write(",ISDIR");
  89. }
  90. break;
  91. case 'f': writer.Write(Path.GetFileName(path)); break;
  92. case 'w': writer.Write(Path.Combine(source.Path, Path.GetDirectoryName(path))); break;
  93. case 'T': writer.Write(DateTime.Now); break;
  94. default: writer.Write(token); break;
  95. }
  96. }
  97. writer.WriteLine();
  98. }
  99. public void Stop(object data) {
  100. string s = Console.ReadLine();
  101. _stopMonitoring = true;
  102. _stopMonitoringEvent.Set();
  103. }
  104. public void Processor(object data) {
  105. string path = (string)data;
  106. using (var w = new FileSystemWatcher {
  107. Path = path,
  108. IncludeSubdirectories = _args.Recursive,
  109. Filter = "*.*"
  110. }) {
  111. w.Error += new ErrorEventHandler(OnWatcherError);
  112. // Parse "events" argument
  113. WatcherChangeTypes changes = 0;
  114. if (_args.Events.Contains("create"))
  115. {
  116. changes |= WatcherChangeTypes.Created;
  117. w.Created += new FileSystemEventHandler(OnWatcherNotification);
  118. }
  119. if (_args.Events.Contains("modify"))
  120. {
  121. changes |= WatcherChangeTypes.Changed;
  122. w.Changed += new FileSystemEventHandler(OnWatcherNotification);
  123. }
  124. if (_args.Events.Contains("delete"))
  125. {
  126. changes |= WatcherChangeTypes.Deleted;
  127. w.Deleted += new FileSystemEventHandler(OnWatcherNotification);
  128. }
  129. if (_args.Events.Contains("move"))
  130. {
  131. changes |= WatcherChangeTypes.Renamed;
  132. w.Renamed += new RenamedEventHandler(OnRenameNotification);
  133. }
  134. // Main loop
  135. if (!_args.Quiet)
  136. {
  137. Console.Error.WriteLine(
  138. "===> {0} for {1} in {2}{3} for {4}",
  139. _args.Monitor ? "Monitoring" : "Watching",
  140. changes,
  141. path,
  142. _args.Recursive ? " -r" : "",
  143. String.Join(", ", _args.Events.ToArray())
  144. );
  145. }
  146. w.EnableRaisingEvents = true;
  147. _stopMonitoringEvent.Wait();
  148. }
  149. }
  150. /// Entry point
  151. public int Run()
  152. {
  153. using (_stopMonitoringEvent = new ManualResetEventSlim(initialState: false))
  154. {
  155. foreach (var path in _args.Paths)
  156. {
  157. Thread t = new Thread(new ParameterizedThreadStart(Processor));
  158. t.Start(path);
  159. _threads.Add(t);
  160. }
  161. Thread stop = new Thread(new ParameterizedThreadStart(Stop));
  162. stop.Start("");
  163. _stopMonitoringEvent.Wait();
  164. foreach (var thread in _threads)
  165. {
  166. if (thread.IsAlive)
  167. thread.Abort();
  168. thread.Join();
  169. }
  170. return 0;
  171. }
  172. }
  173. /// Entry point method
  174. public static int Main(string[] args)
  175. {
  176. var p = new ArgumentParser();
  177. // Show usage if no args or standard "help" args are given
  178. if (0 == args.Length || args[0].Equals("-?") || args[0].Equals("--help"))
  179. {
  180. p.PrintUsage("inotifywait", Console.Error);
  181. return 1;
  182. }
  183. // Run!
  184. return new Runner(p.Parse(args)).Run();
  185. }
  186. }
  187. }