LocalFileEventLog.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. //
  2. // System.Diagnostics.LocalFileEventLog.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. // Gert Driesen <[email protected]>
  7. //
  8. // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
  9. //
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Collections;
  32. using System.ComponentModel;
  33. using System.Diagnostics;
  34. using System.Globalization;
  35. using System.IO;
  36. using System.Runtime.InteropServices;
  37. using System.Security;
  38. using System.Text;
  39. namespace System.Diagnostics
  40. {
  41. internal class LocalFileEventLog : EventLogImpl
  42. {
  43. const string DateFormat = "yyyyMMddHHmmssfff";
  44. static readonly object lockObject = new object ();
  45. public LocalFileEventLog (EventLog coreEventLog) : base (coreEventLog)
  46. {
  47. }
  48. public override void BeginInit () {
  49. }
  50. public override void Clear ()
  51. {
  52. string logDir = FindLogStore (CoreEventLog.Log);
  53. if (!Directory.Exists (logDir))
  54. return;
  55. foreach (string file in Directory.GetFiles (logDir, "*.log"))
  56. File.Delete (file);
  57. }
  58. public override void Close ()
  59. {
  60. // we don't hold any unmanaged resources
  61. }
  62. public override void CreateEventSource (EventSourceCreationData sourceData)
  63. {
  64. // construct path for storing log entries
  65. string logDir = FindLogStore (sourceData.LogName);
  66. // create event log store (if necessary), and modify access
  67. // permissions (unix only)
  68. CreateLogStore (logDir, sourceData.LogName);
  69. // create directory for event source, so we can check if the event
  70. // source already exists
  71. string sourceDir = Path.Combine (logDir, sourceData.Source);
  72. Directory.CreateDirectory (sourceDir);
  73. }
  74. public override void Delete (string logName, string machineName)
  75. {
  76. string logDir = FindLogStore (logName);
  77. if (!Directory.Exists (logDir))
  78. throw new InvalidOperationException (string.Format (
  79. CultureInfo.InvariantCulture, "Event Log '{0}'"
  80. + " does not exist on computer '{1}'.", logName,
  81. machineName));
  82. Directory.Delete (logDir, true);
  83. }
  84. public override void DeleteEventSource (string source, string machineName)
  85. {
  86. if (!Directory.Exists (EventLogStore))
  87. throw new ArgumentException (string.Format (
  88. CultureInfo.InvariantCulture, "The source '{0}' is not"
  89. + " registered on computer '{1}'.", source, machineName));
  90. string sourceDir = FindSourceDirectory (source);
  91. if (sourceDir == null)
  92. throw new ArgumentException (string.Format (
  93. CultureInfo.InvariantCulture, "The source '{0}' is not"
  94. + " registered on computer '{1}'.", source, machineName));
  95. Directory.Delete (sourceDir);
  96. }
  97. public override void Dispose (bool disposing)
  98. {
  99. Close ();
  100. }
  101. public override void EndInit () { }
  102. public override bool Exists (string logName, string machineName)
  103. {
  104. string logDir = FindLogStore (logName);
  105. return Directory.Exists (logDir);
  106. }
  107. [MonoTODO ("Use MessageTable from PE for lookup")]
  108. protected override string FormatMessage (string source, uint eventID, string [] replacementStrings)
  109. {
  110. return string.Join (", ", replacementStrings);
  111. }
  112. protected override int GetEntryCount ()
  113. {
  114. string logDir = FindLogStore (CoreEventLog.Log);
  115. if (!Directory.Exists (logDir))
  116. return 0;
  117. string[] logFiles = Directory.GetFiles (logDir, "*.log");
  118. return logFiles.Length;
  119. }
  120. protected override EventLogEntry GetEntry (int index)
  121. {
  122. string logDir = FindLogStore (CoreEventLog.Log);
  123. // our file names are one-based
  124. string file = Path.Combine (logDir, (index + 1).ToString (
  125. CultureInfo.InvariantCulture) + ".log");
  126. using (TextReader tr = File.OpenText (file)) {
  127. int eventIndex = int.Parse (Path.GetFileNameWithoutExtension (file),
  128. CultureInfo.InvariantCulture);
  129. uint instanceID = uint.Parse (tr.ReadLine ().Substring (12),
  130. CultureInfo.InvariantCulture);
  131. EventLogEntryType type = (EventLogEntryType)
  132. Enum.Parse (typeof (EventLogEntryType), tr.ReadLine ().Substring (11));
  133. string source = tr.ReadLine ().Substring (8);
  134. string category = tr.ReadLine ().Substring (10);
  135. short categoryNumber = short.Parse(category, CultureInfo.InvariantCulture);
  136. string categoryName = "(" + category + ")";
  137. DateTime timeGenerated = DateTime.ParseExact (tr.ReadLine ().Substring (15),
  138. DateFormat, CultureInfo.InvariantCulture);
  139. DateTime timeWritten = File.GetLastWriteTime (file);
  140. int stringNums = int.Parse (tr.ReadLine ().Substring (20));
  141. ArrayList replacementTemp = new ArrayList ();
  142. StringBuilder sb = new StringBuilder ();
  143. while (replacementTemp.Count < stringNums) {
  144. char c = (char) tr.Read ();
  145. if (c == '\0') {
  146. replacementTemp.Add (sb.ToString ());
  147. sb.Length = 0;
  148. } else {
  149. sb.Append (c);
  150. }
  151. }
  152. string [] replacementStrings = new string [replacementTemp.Count];
  153. replacementTemp.CopyTo (replacementStrings, 0);
  154. string message = FormatMessage (source, instanceID, replacementStrings);
  155. int eventID = EventLog.GetEventID (instanceID);
  156. byte [] bin = Convert.FromBase64String (tr.ReadToEnd ());
  157. return new EventLogEntry (categoryName, categoryNumber, eventIndex,
  158. eventID, source, message, null, Environment.MachineName,
  159. type, timeGenerated, timeWritten, bin, replacementStrings,
  160. instanceID);
  161. }
  162. }
  163. public override EventLog [] GetEventLogs (string machineName)
  164. {
  165. if (!Directory.Exists (EventLogStore))
  166. return new EventLog [0];
  167. string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
  168. EventLog [] eventLogs = new EventLog [logDirs.Length];
  169. for (int i = 0; i < logDirs.Length; i++) {
  170. EventLog eventLog = new EventLog (Path.GetFileName (
  171. logDirs [i]), machineName);
  172. eventLogs [i] = eventLog;
  173. }
  174. return eventLogs;
  175. }
  176. [MonoTODO]
  177. protected override string GetLogDisplayName ()
  178. {
  179. return CoreEventLog.Log;
  180. }
  181. public override string LogNameFromSourceName (string source, string machineName)
  182. {
  183. if (!Directory.Exists (EventLogStore))
  184. return string.Empty;
  185. string sourceDir = FindSourceDirectory (source);
  186. if (sourceDir == null)
  187. return string.Empty;
  188. DirectoryInfo info = new DirectoryInfo (sourceDir);
  189. return info.Parent.Name;
  190. }
  191. public override bool SourceExists (string source, string machineName)
  192. {
  193. if (!Directory.Exists (EventLogStore))
  194. return false;
  195. string sourceDir = FindSourceDirectory (source);
  196. return (sourceDir != null);
  197. }
  198. public override void WriteEntry (string [] replacementStrings, EventLogEntryType type, uint instanceID, short category, byte [] rawData)
  199. {
  200. lock (lockObject) {
  201. string logDir = FindLogStore (CoreEventLog.Log);
  202. int index = GetNewIndex ();
  203. string logPath = Path.Combine (logDir, index.ToString (CultureInfo.InvariantCulture) + ".log");
  204. try {
  205. using (TextWriter w = File.CreateText (logPath)) {
  206. #if NET_2_0
  207. w.WriteLine ("InstanceID: {0}", instanceID.ToString (CultureInfo.InvariantCulture));
  208. #else
  209. w.WriteLine ("InstanceID: {0}", instanceID.ToString (CultureInfo.InvariantCulture));
  210. #endif
  211. w.WriteLine ("EntryType: {0}", (int) type);
  212. w.WriteLine ("Source: {0}", CoreEventLog.Source);
  213. w.WriteLine ("Category: {0}", category.ToString (CultureInfo.InvariantCulture));
  214. w.WriteLine ("TimeGenerated: {0}", DateTime.Now.ToString (
  215. DateFormat, CultureInfo.InvariantCulture));
  216. w.WriteLine ("ReplacementStrings: {0}", replacementStrings.
  217. Length.ToString (CultureInfo.InvariantCulture));
  218. StringBuilder sb = new StringBuilder ();
  219. for (int i = 0; i < replacementStrings.Length; i++) {
  220. string replacement = replacementStrings [i];
  221. sb.Append (replacement);
  222. sb.Append ('\0');
  223. }
  224. w.Write (sb.ToString ());
  225. w.Write (Convert.ToBase64String (rawData));
  226. }
  227. } catch (IOException) {
  228. File.Delete (logPath);
  229. }
  230. }
  231. }
  232. private string FindSourceDirectory (string source)
  233. {
  234. string sourceDir = null;
  235. string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
  236. for (int i = 0; i < logDirs.Length; i++) {
  237. string [] sourceDirs = Directory.GetDirectories (logDirs [i], "*");
  238. for (int j = 0; j < sourceDirs.Length; j++) {
  239. string relativeDir = Path.GetFileName (sourceDirs [j]);
  240. // use a case-insensitive comparison
  241. if (string.Compare (relativeDir, source, true, CultureInfo.InvariantCulture) == 0) {
  242. sourceDir = sourceDirs [j];
  243. break;
  244. }
  245. }
  246. }
  247. return sourceDir;
  248. }
  249. private bool RunningOnLinux {
  250. get {
  251. return ((int) Environment.OSVersion.Platform == 4 ||
  252. #if NET_2_0
  253. Environment.OSVersion.Platform == PlatformID.Unix);
  254. #else
  255. (int) Environment.OSVersion.Platform == 128);
  256. #endif
  257. }
  258. }
  259. private string FindLogStore (string logName) {
  260. // when the event log store does not yet exist, there's no need
  261. // to perform a case-insensitive lookup
  262. if (!Directory.Exists (EventLogStore))
  263. return Path.Combine (EventLogStore, logName);
  264. // we'll use a case-insensitive lookup to match the MS behaviour
  265. // while still allowing the original casing of the log name to be
  266. // retained
  267. string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
  268. for (int i = 0; i < logDirs.Length; i++) {
  269. string relativeDir = Path.GetFileName (logDirs [i]);
  270. // use a case-insensitive comparison
  271. if (string.Compare (relativeDir, logName, true, CultureInfo.InvariantCulture) == 0) {
  272. return logDirs [i];
  273. }
  274. }
  275. return Path.Combine (EventLogStore, logName);
  276. }
  277. private string EventLogStore {
  278. get {
  279. // for the local file implementation, the MONO_EVENTLOG_TYPE
  280. // environment variable can contain the path of the event log
  281. // store by using the following syntax: local:<path>
  282. string eventLogType = Environment.GetEnvironmentVariable (EventLog.EVENTLOG_TYPE_VAR);
  283. if (eventLogType != null && eventLogType.Length > EventLog.LOCAL_FILE_IMPL.Length + 1)
  284. return eventLogType.Substring (EventLog.LOCAL_FILE_IMPL.Length + 1);
  285. if (RunningOnLinux) {
  286. return "/var/lib/mono/eventlog";
  287. } else {
  288. return Path.Combine (Environment.GetFolderPath (
  289. Environment.SpecialFolder.CommonApplicationData),
  290. "mono/eventlog");
  291. }
  292. }
  293. }
  294. private void CreateLogStore (string logDir, string logName)
  295. {
  296. if (!Directory.Exists (logDir)) {
  297. Directory.CreateDirectory (logDir);
  298. // MS does not allow an event source to be named after an already
  299. // existing event log. To speed up checking whether a given event
  300. // source already exists (either as a event source or event log)
  301. // we create an event source directory named after the event log.
  302. // This matches what MS does with the registry-based registration.
  303. Directory.CreateDirectory (Path.Combine (logDir, logName));
  304. if (RunningOnLinux) {
  305. ModifyAccessPermissions (logDir, "777");
  306. ModifyAccessPermissions (logDir, "+t");
  307. }
  308. }
  309. }
  310. private int GetNewIndex () {
  311. // our file names are one-based
  312. int maxIndex = 0;
  313. string[] logFiles = Directory.GetFiles (FindLogStore (CoreEventLog.Log), "*.log");
  314. for (int i = 0; i < logFiles.Length; i++) {
  315. try {
  316. string file = logFiles[i];
  317. int index = int.Parse (Path.GetFileNameWithoutExtension (
  318. file), CultureInfo.InvariantCulture);
  319. if (index > maxIndex)
  320. maxIndex = index;
  321. } catch {
  322. }
  323. }
  324. return ++maxIndex;
  325. }
  326. private static void ModifyAccessPermissions (string path, string permissions)
  327. {
  328. ProcessStartInfo pi = new ProcessStartInfo ();
  329. pi.FileName = "chmod";
  330. pi.RedirectStandardOutput = true;
  331. pi.RedirectStandardError = true;
  332. pi.UseShellExecute = false;
  333. pi.Arguments = string.Format ("{0} \"{1}\"", permissions, path);
  334. Process p = null;
  335. try {
  336. p = Process.Start (pi);
  337. } catch (Exception ex) {
  338. throw new SecurityException ("Access permissions could not be modified.", ex);
  339. }
  340. p.WaitForExit ();
  341. if (p.ExitCode != 0) {
  342. p.Close ();
  343. throw new SecurityException ("Access permissions could not be modified.");
  344. }
  345. p.Close ();
  346. }
  347. }
  348. }