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
;
40 using System
.Threading
;
42 namespace System
.Diagnostics
44 internal class LocalFileEventLog
: EventLogImpl
46 const string DateFormat
= "yyyyMMddHHmmssfff";
47 static readonly object lockObject
= new object ();
48 FileSystemWatcher file_watcher
;
49 int last_notification_index
;
52 public LocalFileEventLog (EventLog coreEventLog
) : base (coreEventLog
)
56 public override void BeginInit () {
59 public override void Clear ()
61 string logDir
= FindLogStore (CoreEventLog
.Log
);
62 if (!Directory
.Exists (logDir
))
65 foreach (string file
in Directory
.GetFiles (logDir
, "*.log"))
69 public override void Close ()
71 if (file_watcher
!= null) {
72 file_watcher
.EnableRaisingEvents
= false;
73 file_watcher
= null; // force creation of new FileSystemWatcher
77 public override void CreateEventSource (EventSourceCreationData sourceData
)
79 // construct path for storing log entries
80 string logDir
= FindLogStore (sourceData
.LogName
);
81 // create event log store (if necessary), and modify access
82 // permissions (unix only)
83 if (!Directory
.Exists (logDir
)) {
84 // ensure the log name is valid for customer logs
85 ValidateCustomerLogName (sourceData
.LogName
, sourceData
.MachineName
);
87 Directory
.CreateDirectory (logDir
);
88 // MS does not allow an event source to be named after an already
89 // existing event log. To speed up checking whether a given event
90 // source already exists (either as a event source or event log)
91 // we create an event source directory named after the event log.
92 // This matches what MS does with the registry-based registration.
93 Directory
.CreateDirectory (Path
.Combine (logDir
, sourceData
.LogName
));
95 ModifyAccessPermissions (logDir
, "777");
96 ModifyAccessPermissions (logDir
, "+t");
99 // create directory for event source, so we can check if the event
100 // source already exists
101 string sourceDir
= Path
.Combine (logDir
, sourceData
.Source
);
102 Directory
.CreateDirectory (sourceDir
);
105 public override void Delete (string logName
, string machineName
)
107 string logDir
= FindLogStore (logName
);
108 if (!Directory
.Exists (logDir
))
109 throw new InvalidOperationException (string.Format (
110 CultureInfo
.InvariantCulture
, "Event Log '{0}'"
111 + " does not exist on computer '{1}'.", logName
,
114 Directory
.Delete (logDir
, true);
117 public override void DeleteEventSource (string source
, string machineName
)
119 if (!Directory
.Exists (EventLogStore
))
120 throw new ArgumentException (string.Format (
121 CultureInfo
.InvariantCulture
, "The source '{0}' is not"
122 + " registered on computer '{1}'.", source
, machineName
));
124 string sourceDir
= FindSourceDirectory (source
);
125 if (sourceDir
== null)
126 throw new ArgumentException (string.Format (
127 CultureInfo
.InvariantCulture
, "The source '{0}' is not"
128 + " registered on computer '{1}'.", source
, machineName
));
129 Directory
.Delete (sourceDir
);
132 public override void Dispose (bool disposing
)
137 public override void DisableNotification ()
139 if (file_watcher
== null)
141 file_watcher
.EnableRaisingEvents
= false;
144 public override void EnableNotification ()
146 if (file_watcher
== null) {
147 string logDir
= FindLogStore (CoreEventLog
.Log
);
148 if (!Directory
.Exists (logDir
))
149 Directory
.CreateDirectory (logDir
);
151 file_watcher
= new FileSystemWatcher ();
152 file_watcher
.Path
= logDir
;
153 file_watcher
.Created
+= delegate (object o
, FileSystemEventArgs e
) {
160 // allow for file to be finished writing
163 // Process every new entry in one notification event.
165 while (GetLatestIndex () > last_notification_index
) {
167 CoreEventLog
.OnEntryWritten (GetEntry (last_notification_index
++));
168 } catch (Exception ex
) {
169 // FIXME: find some proper way to output this error
170 Debug
.WriteLine (ex
);
179 last_notification_index
= GetLatestIndex ();
180 file_watcher
.EnableRaisingEvents
= true;
183 public override void EndInit () { }
185 public override bool Exists (string logName
, string machineName
)
187 string logDir
= FindLogStore (logName
);
188 return Directory
.Exists (logDir
);
191 [MonoTODO ("Use MessageTable from PE for lookup")]
192 protected override string FormatMessage (string source
, uint eventID
, string [] replacementStrings
)
194 return string.Join (", ", replacementStrings
);
197 protected override int GetEntryCount ()
199 string logDir
= FindLogStore (CoreEventLog
.Log
);
200 if (!Directory
.Exists (logDir
))
203 string[] logFiles
= Directory
.GetFiles (logDir
, "*.log");
204 return logFiles
.Length
;
207 protected override EventLogEntry
GetEntry (int index
)
209 string logDir
= FindLogStore (CoreEventLog
.Log
);
211 // our file names are one-based
212 string file
= Path
.Combine (logDir
, (index
+ 1).ToString (
213 CultureInfo
.InvariantCulture
) + ".log");
215 using (TextReader tr
= File
.OpenText (file
)) {
216 int eventIndex
= int.Parse (Path
.GetFileNameWithoutExtension (file
),
217 CultureInfo
.InvariantCulture
);
218 uint instanceID
= uint.Parse (tr
.ReadLine ().Substring (12),
219 CultureInfo
.InvariantCulture
);
220 EventLogEntryType type
= (EventLogEntryType
)
221 Enum
.Parse (typeof (EventLogEntryType
), tr
.ReadLine ().Substring (11));
222 string source
= tr
.ReadLine ().Substring (8);
223 string category
= tr
.ReadLine ().Substring (10);
224 short categoryNumber
= short.Parse(category
, CultureInfo
.InvariantCulture
);
225 string categoryName
= "(" + category
+ ")";
226 DateTime timeGenerated
= DateTime
.ParseExact (tr
.ReadLine ().Substring (15),
227 DateFormat
, CultureInfo
.InvariantCulture
);
228 DateTime timeWritten
= File
.GetLastWriteTime (file
);
229 int stringNums
= int.Parse (tr
.ReadLine ().Substring (20));
230 ArrayList replacementTemp
= new ArrayList ();
231 StringBuilder sb
= new StringBuilder ();
232 while (replacementTemp
.Count
< stringNums
) {
233 char c
= (char) tr
.Read ();
235 replacementTemp
.Add (sb
.ToString ());
241 string [] replacementStrings
= new string [replacementTemp
.Count
];
242 replacementTemp
.CopyTo (replacementStrings
, 0);
244 string message
= FormatMessage (source
, instanceID
, replacementStrings
);
245 int eventID
= EventLog
.GetEventID (instanceID
);
247 byte [] bin
= Convert
.FromBase64String (tr
.ReadToEnd ());
248 return new EventLogEntry (categoryName
, categoryNumber
, eventIndex
,
249 eventID
, source
, message
, null, Environment
.MachineName
,
250 type
, timeGenerated
, timeWritten
, bin
, replacementStrings
,
256 protected override string GetLogDisplayName ()
258 return CoreEventLog
.Log
;
261 protected override string [] GetLogNames (string machineName
)
263 if (!Directory
.Exists (EventLogStore
))
264 return new string [0];
266 string [] logDirs
= Directory
.GetDirectories (EventLogStore
, "*");
267 string [] logNames
= new string [logDirs
.Length
];
268 for (int i
= 0; i
< logDirs
.Length
; i
++)
269 logNames
[i
] = Path
.GetFileName (logDirs
[i
]);
273 public override string LogNameFromSourceName (string source
, string machineName
)
275 if (!Directory
.Exists (EventLogStore
))
278 string sourceDir
= FindSourceDirectory (source
);
279 if (sourceDir
== null)
281 DirectoryInfo info
= new DirectoryInfo (sourceDir
);
282 return info
.Parent
.Name
;
285 public override bool SourceExists (string source
, string machineName
)
287 if (!Directory
.Exists (EventLogStore
))
289 string sourceDir
= FindSourceDirectory (source
);
290 return (sourceDir
!= null);
293 public override void WriteEntry (string [] replacementStrings
, EventLogEntryType type
, uint instanceID
, short category
, byte [] rawData
)
296 string logDir
= FindLogStore (CoreEventLog
.Log
);
298 int index
= GetLatestIndex () + 1;
299 string logPath
= Path
.Combine (logDir
, index
.ToString (CultureInfo
.InvariantCulture
) + ".log");
301 using (TextWriter w
= File
.CreateText (logPath
)) {
303 w
.WriteLine ("InstanceID: {0}", instanceID
.ToString (CultureInfo
.InvariantCulture
));
305 w
.WriteLine ("InstanceID: {0}", instanceID
.ToString (CultureInfo
.InvariantCulture
));
307 w
.WriteLine ("EntryType: {0}", (int) type
);
308 w
.WriteLine ("Source: {0}", CoreEventLog
.Source
);
309 w
.WriteLine ("Category: {0}", category
.ToString (CultureInfo
.InvariantCulture
));
310 w
.WriteLine ("TimeGenerated: {0}", DateTime
.Now
.ToString (
311 DateFormat
, CultureInfo
.InvariantCulture
));
312 w
.WriteLine ("ReplacementStrings: {0}", replacementStrings
.
313 Length
.ToString (CultureInfo
.InvariantCulture
));
314 StringBuilder sb
= new StringBuilder ();
315 for (int i
= 0; i
< replacementStrings
.Length
; i
++) {
316 string replacement
= replacementStrings
[i
];
317 sb
.Append (replacement
);
320 w
.Write (sb
.ToString ());
321 w
.Write (Convert
.ToBase64String (rawData
));
323 } catch (IOException
) {
324 File
.Delete (logPath
);
329 private string FindSourceDirectory (string source
)
331 string sourceDir
= null;
333 string [] logDirs
= Directory
.GetDirectories (EventLogStore
, "*");
334 for (int i
= 0; i
< logDirs
.Length
; i
++) {
335 string [] sourceDirs
= Directory
.GetDirectories (logDirs
[i
], "*");
336 for (int j
= 0; j
< sourceDirs
.Length
; j
++) {
337 string relativeDir
= Path
.GetFileName (sourceDirs
[j
]);
338 // use a case-insensitive comparison
339 if (string.Compare (relativeDir
, source
, true, CultureInfo
.InvariantCulture
) == 0) {
340 sourceDir
= sourceDirs
[j
];
348 private bool RunningOnUnix
{
350 int p
= (int) Environment
.OSVersion
.Platform
;
351 return ((p
== 4) || (p
== 128) || (p
== 6));
355 private string FindLogStore (string logName
) {
356 // when the event log store does not yet exist, there's no need
357 // to perform a case-insensitive lookup
358 if (!Directory
.Exists (EventLogStore
))
359 return Path
.Combine (EventLogStore
, logName
);
361 // we'll use a case-insensitive lookup to match the MS behaviour
362 // while still allowing the original casing of the log name to be
364 string [] logDirs
= Directory
.GetDirectories (EventLogStore
, "*");
365 for (int i
= 0; i
< logDirs
.Length
; i
++) {
366 string relativeDir
= Path
.GetFileName (logDirs
[i
]);
367 // use a case-insensitive comparison
368 if (string.Compare (relativeDir
, logName
, true, CultureInfo
.InvariantCulture
) == 0) {
373 return Path
.Combine (EventLogStore
, logName
);
376 private string EventLogStore
{
378 // for the local file implementation, the MONO_EVENTLOG_TYPE
379 // environment variable can contain the path of the event log
380 // store by using the following syntax: local:<path>
381 string eventLogType
= Environment
.GetEnvironmentVariable (EventLog
.EVENTLOG_TYPE_VAR
);
382 if (eventLogType
!= null && eventLogType
.Length
> EventLog
.LOCAL_FILE_IMPL
.Length
+ 1)
383 return eventLogType
.Substring (EventLog
.LOCAL_FILE_IMPL
.Length
+ 1);
385 return "/var/lib/mono/eventlog";
387 return Path
.Combine (Environment
.GetFolderPath (
388 Environment
.SpecialFolder
.CommonApplicationData
),
394 private int GetLatestIndex () {
395 // our file names are one-based
397 string[] logFiles
= Directory
.GetFiles (FindLogStore (CoreEventLog
.Log
), "*.log");
398 for (int i
= 0; i
< logFiles
.Length
; i
++) {
400 string file
= logFiles
[i
];
401 int index
= int.Parse (Path
.GetFileNameWithoutExtension (
402 file
), CultureInfo
.InvariantCulture
);
403 if (index
> maxIndex
)
411 private static void ModifyAccessPermissions (string path
, string permissions
)
413 ProcessStartInfo pi
= new ProcessStartInfo ();
414 pi
.FileName
= "chmod";
415 pi
.RedirectStandardOutput
= true;
416 pi
.RedirectStandardError
= true;
417 pi
.UseShellExecute
= false;
418 pi
.Arguments
= string.Format ("{0} \"{1}\"", permissions
, path
);
422 p
= Process
.Start (pi
);
423 } catch (Exception ex
) {
424 throw new SecurityException ("Access permissions could not be modified.", ex
);
428 if (p
.ExitCode
!= 0) {
430 throw new SecurityException ("Access permissions could not be modified.");
436 public override OverflowAction OverflowAction
{
437 get { return OverflowAction.DoNotOverwrite; }
440 public override int MinimumRetentionDays
{
441 get { return int.MaxValue; }
444 public override long MaximumKilobytes
{
445 get { return long.MaxValue; }
446 set { throw new NotSupportedException ("This EventLog implementation does not support setting max kilobytes policy"); }
449 public override void ModifyOverflowPolicy (OverflowAction action
, int retentionDays
)
451 throw new NotSupportedException ("This EventLog implementation does not support modifying overflow policy");
454 public override void RegisterDisplayName (string resourceFile
, long resourceId
)
456 throw new NotSupportedException ("This EventLog implementation does not support registering display name");