2 // System.Diagnostics.LocalFileEventLog.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Gert Driesen <drieseng@users.sourceforge.net>
8 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Diagnostics;
35 using System.Globalization;
37 using System.Runtime.InteropServices;
38 using System.Security;
41 namespace System.Diagnostics
43 internal class LocalFileEventLog : EventLogImpl
45 const string DateFormat = "yyyyMMddHHmmssfff";
46 static readonly object lockObject = new object ();
48 public LocalFileEventLog (EventLog coreEventLog) : base (coreEventLog)
52 public override void BeginInit () {
55 public override void Clear ()
57 string logDir = FindLogStore (CoreEventLog.Log);
58 if (!Directory.Exists (logDir))
61 foreach (string file in Directory.GetFiles (logDir, "*.log"))
65 public override void Close ()
67 // we don't hold any unmanaged resources
70 public override void CreateEventSource (EventSourceCreationData sourceData)
72 // construct path for storing log entries
73 string logDir = FindLogStore (sourceData.LogName);
74 // create event log store (if necessary), and modify access
75 // permissions (unix only)
76 if (!Directory.Exists (logDir)) {
77 // ensure the log name is valid for customer logs
78 ValidateCustomerLogName (sourceData.LogName, sourceData.MachineName);
80 Directory.CreateDirectory (logDir);
81 // MS does not allow an event source to be named after an already
82 // existing event log. To speed up checking whether a given event
83 // source already exists (either as a event source or event log)
84 // we create an event source directory named after the event log.
85 // This matches what MS does with the registry-based registration.
86 Directory.CreateDirectory (Path.Combine (logDir, sourceData.LogName));
88 ModifyAccessPermissions (logDir, "777");
89 ModifyAccessPermissions (logDir, "+t");
92 // create directory for event source, so we can check if the event
93 // source already exists
94 string sourceDir = Path.Combine (logDir, sourceData.Source);
95 Directory.CreateDirectory (sourceDir);
98 public override void Delete (string logName, string machineName)
100 string logDir = FindLogStore (logName);
101 if (!Directory.Exists (logDir))
102 throw new InvalidOperationException (string.Format (
103 CultureInfo.InvariantCulture, "Event Log '{0}'"
104 + " does not exist on computer '{1}'.", logName,
107 Directory.Delete (logDir, true);
110 public override void DeleteEventSource (string source, string machineName)
112 if (!Directory.Exists (EventLogStore))
113 throw new ArgumentException (string.Format (
114 CultureInfo.InvariantCulture, "The source '{0}' is not"
115 + " registered on computer '{1}'.", source, machineName));
117 string sourceDir = FindSourceDirectory (source);
118 if (sourceDir == null)
119 throw new ArgumentException (string.Format (
120 CultureInfo.InvariantCulture, "The source '{0}' is not"
121 + " registered on computer '{1}'.", source, machineName));
122 Directory.Delete (sourceDir);
125 public override void Dispose (bool disposing)
130 public override void EndInit () { }
132 public override bool Exists (string logName, string machineName)
134 string logDir = FindLogStore (logName);
135 return Directory.Exists (logDir);
138 [MonoTODO ("Use MessageTable from PE for lookup")]
139 protected override string FormatMessage (string source, uint eventID, string [] replacementStrings)
141 return string.Join (", ", replacementStrings);
144 protected override int GetEntryCount ()
146 string logDir = FindLogStore (CoreEventLog.Log);
147 if (!Directory.Exists (logDir))
150 string[] logFiles = Directory.GetFiles (logDir, "*.log");
151 return logFiles.Length;
154 protected override EventLogEntry GetEntry (int index)
156 string logDir = FindLogStore (CoreEventLog.Log);
158 // our file names are one-based
159 string file = Path.Combine (logDir, (index + 1).ToString (
160 CultureInfo.InvariantCulture) + ".log");
162 using (TextReader tr = File.OpenText (file)) {
163 int eventIndex = int.Parse (Path.GetFileNameWithoutExtension (file),
164 CultureInfo.InvariantCulture);
165 uint instanceID = uint.Parse (tr.ReadLine ().Substring (12),
166 CultureInfo.InvariantCulture);
167 EventLogEntryType type = (EventLogEntryType)
168 Enum.Parse (typeof (EventLogEntryType), tr.ReadLine ().Substring (11));
169 string source = tr.ReadLine ().Substring (8);
170 string category = tr.ReadLine ().Substring (10);
171 short categoryNumber = short.Parse(category, CultureInfo.InvariantCulture);
172 string categoryName = "(" + category + ")";
173 DateTime timeGenerated = DateTime.ParseExact (tr.ReadLine ().Substring (15),
174 DateFormat, CultureInfo.InvariantCulture);
175 DateTime timeWritten = File.GetLastWriteTime (file);
176 int stringNums = int.Parse (tr.ReadLine ().Substring (20));
177 ArrayList replacementTemp = new ArrayList ();
178 StringBuilder sb = new StringBuilder ();
179 while (replacementTemp.Count < stringNums) {
180 char c = (char) tr.Read ();
182 replacementTemp.Add (sb.ToString ());
188 string [] replacementStrings = new string [replacementTemp.Count];
189 replacementTemp.CopyTo (replacementStrings, 0);
191 string message = FormatMessage (source, instanceID, replacementStrings);
192 int eventID = EventLog.GetEventID (instanceID);
194 byte [] bin = Convert.FromBase64String (tr.ReadToEnd ());
195 return new EventLogEntry (categoryName, categoryNumber, eventIndex,
196 eventID, source, message, null, Environment.MachineName,
197 type, timeGenerated, timeWritten, bin, replacementStrings,
203 protected override string GetLogDisplayName ()
205 return CoreEventLog.Log;
208 protected override string [] GetLogNames (string machineName)
210 if (!Directory.Exists (EventLogStore))
211 return new string [0];
213 string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
214 string [] logNames = new string [logDirs.Length];
215 for (int i = 0; i < logDirs.Length; i++)
216 logNames [i] = Path.GetFileName (logDirs [i]);
220 public override string LogNameFromSourceName (string source, string machineName)
222 if (!Directory.Exists (EventLogStore))
225 string sourceDir = FindSourceDirectory (source);
226 if (sourceDir == null)
228 DirectoryInfo info = new DirectoryInfo (sourceDir);
229 return info.Parent.Name;
232 public override bool SourceExists (string source, string machineName)
234 if (!Directory.Exists (EventLogStore))
236 string sourceDir = FindSourceDirectory (source);
237 return (sourceDir != null);
240 public override void WriteEntry (string [] replacementStrings, EventLogEntryType type, uint instanceID, short category, byte [] rawData)
243 string logDir = FindLogStore (CoreEventLog.Log);
245 int index = GetNewIndex ();
246 string logPath = Path.Combine (logDir, index.ToString (CultureInfo.InvariantCulture) + ".log");
248 using (TextWriter w = File.CreateText (logPath)) {
250 w.WriteLine ("InstanceID: {0}", instanceID.ToString (CultureInfo.InvariantCulture));
252 w.WriteLine ("InstanceID: {0}", instanceID.ToString (CultureInfo.InvariantCulture));
254 w.WriteLine ("EntryType: {0}", (int) type);
255 w.WriteLine ("Source: {0}", CoreEventLog.Source);
256 w.WriteLine ("Category: {0}", category.ToString (CultureInfo.InvariantCulture));
257 w.WriteLine ("TimeGenerated: {0}", DateTime.Now.ToString (
258 DateFormat, CultureInfo.InvariantCulture));
259 w.WriteLine ("ReplacementStrings: {0}", replacementStrings.
260 Length.ToString (CultureInfo.InvariantCulture));
261 StringBuilder sb = new StringBuilder ();
262 for (int i = 0; i < replacementStrings.Length; i++) {
263 string replacement = replacementStrings [i];
264 sb.Append (replacement);
267 w.Write (sb.ToString ());
268 w.Write (Convert.ToBase64String (rawData));
270 } catch (IOException) {
271 File.Delete (logPath);
276 private string FindSourceDirectory (string source)
278 string sourceDir = null;
280 string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
281 for (int i = 0; i < logDirs.Length; i++) {
282 string [] sourceDirs = Directory.GetDirectories (logDirs [i], "*");
283 for (int j = 0; j < sourceDirs.Length; j++) {
284 string relativeDir = Path.GetFileName (sourceDirs [j]);
285 // use a case-insensitive comparison
286 if (string.Compare (relativeDir, source, true, CultureInfo.InvariantCulture) == 0) {
287 sourceDir = sourceDirs [j];
295 private bool RunningOnLinux {
297 return ((int) Environment.OSVersion.Platform == 4 ||
299 Environment.OSVersion.Platform == PlatformID.Unix);
301 (int) Environment.OSVersion.Platform == 128);
306 private string FindLogStore (string logName) {
307 // when the event log store does not yet exist, there's no need
308 // to perform a case-insensitive lookup
309 if (!Directory.Exists (EventLogStore))
310 return Path.Combine (EventLogStore, logName);
312 // we'll use a case-insensitive lookup to match the MS behaviour
313 // while still allowing the original casing of the log name to be
315 string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
316 for (int i = 0; i < logDirs.Length; i++) {
317 string relativeDir = Path.GetFileName (logDirs [i]);
318 // use a case-insensitive comparison
319 if (string.Compare (relativeDir, logName, true, CultureInfo.InvariantCulture) == 0) {
324 return Path.Combine (EventLogStore, logName);
327 private string EventLogStore {
329 // for the local file implementation, the MONO_EVENTLOG_TYPE
330 // environment variable can contain the path of the event log
331 // store by using the following syntax: local:<path>
332 string eventLogType = Environment.GetEnvironmentVariable (EventLog.EVENTLOG_TYPE_VAR);
333 if (eventLogType != null && eventLogType.Length > EventLog.LOCAL_FILE_IMPL.Length + 1)
334 return eventLogType.Substring (EventLog.LOCAL_FILE_IMPL.Length + 1);
335 if (RunningOnLinux) {
336 return "/var/lib/mono/eventlog";
338 return Path.Combine (Environment.GetFolderPath (
339 Environment.SpecialFolder.CommonApplicationData),
345 private int GetNewIndex () {
346 // our file names are one-based
348 string[] logFiles = Directory.GetFiles (FindLogStore (CoreEventLog.Log), "*.log");
349 for (int i = 0; i < logFiles.Length; i++) {
351 string file = logFiles[i];
352 int index = int.Parse (Path.GetFileNameWithoutExtension (
353 file), CultureInfo.InvariantCulture);
354 if (index > maxIndex)
362 private static void ModifyAccessPermissions (string path, string permissions)
364 ProcessStartInfo pi = new ProcessStartInfo ();
365 pi.FileName = "chmod";
366 pi.RedirectStandardOutput = true;
367 pi.RedirectStandardError = true;
368 pi.UseShellExecute = false;
369 pi.Arguments = string.Format ("{0} \"{1}\"", permissions, path);
373 p = Process.Start (pi);
374 } catch (Exception ex) {
375 throw new SecurityException ("Access permissions could not be modified.", ex);
379 if (p.ExitCode != 0) {
381 throw new SecurityException ("Access permissions could not be modified.");