Remove excessive shortcut key matching in ToolStrip
[mono.git] / mcs / class / System / System.Diagnostics / LocalFileEventLog.cs
1 //
2 // System.Diagnostics.LocalFileEventLog.cs
3 //
4 // Author:
5 //   Atsushi Enomoto  <atsushi@ximian.com>
6 //   Gert Driesen  <drieseng@users.sourceforge.net>
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
31 using System;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Diagnostics;
35 using System.Globalization;
36 using System.IO;
37 using System.Runtime.InteropServices;
38 using System.Security;
39 using System.Text;
40 using System.Threading;
41
42 namespace System.Diagnostics
43 {
44         internal class LocalFileEventLog : EventLogImpl
45         {
46                 const string DateFormat = "yyyyMMddHHmmssfff";
47                 static readonly object lockObject = new object ();
48                 FileSystemWatcher file_watcher;
49                 int last_notification_index;
50                 bool _notifying;
51
52                 public LocalFileEventLog (EventLog coreEventLog) : base (coreEventLog)
53                 {
54                 }
55
56                 public override void BeginInit () {
57                 }
58
59                 public override void Clear ()
60                 {
61                         string logDir = FindLogStore (CoreEventLog.Log);
62                         if (!Directory.Exists (logDir))
63                                 return;
64
65                         foreach (string file in Directory.GetFiles (logDir, "*.log"))
66                                 File.Delete (file);
67                 }
68
69                 public override void Close ()
70                 {
71                         if (file_watcher != null) {
72                                 file_watcher.EnableRaisingEvents = false;
73                                 file_watcher = null; // force creation of new FileSystemWatcher
74                         }
75                 }
76
77                 public override void CreateEventSource (EventSourceCreationData sourceData)
78                 {
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);
86
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));
94                                 if (RunningOnUnix) {
95                                         ModifyAccessPermissions (logDir, "777");
96                                         ModifyAccessPermissions (logDir, "+t");
97                                 }
98                         }
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);
103                 }
104
105                 public override void Delete (string logName, string machineName)
106                 {
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,
112                                         machineName));
113
114                         Directory.Delete (logDir, true);
115                 }
116
117                 public override void DeleteEventSource (string source, string machineName)
118                 {
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));
123
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);
130                 }
131
132                 public override void Dispose (bool disposing)
133                 {
134                         Close ();
135                 }
136
137                 public override void DisableNotification ()
138                 {
139                         if (file_watcher == null)
140                                 return;
141                         file_watcher.EnableRaisingEvents = false;
142                 }
143
144                 public override void EnableNotification ()
145                 {
146                         if (file_watcher == null) {
147                                 string logDir = FindLogStore (CoreEventLog.Log);
148                                 if (!Directory.Exists (logDir))
149                                         Directory.CreateDirectory (logDir);
150
151                                 file_watcher = new FileSystemWatcher ();
152                                 file_watcher.Path = logDir;
153                                 file_watcher.Created += delegate (object o, FileSystemEventArgs e) {
154                                         lock (this) {
155                                                 if (_notifying)
156                                                         return;
157                                                 _notifying = true;
158                                         }
159
160                                         // allow for file to be finished writing
161                                         Thread.Sleep (100);
162
163                                         // Process every new entry in one notification event.
164                                         try {
165                                                 while (GetLatestIndex () > last_notification_index) {
166                                                         try {
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);
171                                                         }
172                                                 }
173                                         } finally {
174                                                 lock (this)
175                                                         _notifying = false;
176                                         }
177                                 };
178                         }
179                         last_notification_index = GetLatestIndex ();
180                         file_watcher.EnableRaisingEvents = true;
181                 }
182
183                 public override void EndInit () { }
184
185                 public override bool Exists (string logName, string machineName)
186                 {
187                         string logDir = FindLogStore (logName);
188                         return Directory.Exists (logDir);
189                 }
190
191                 [MonoTODO ("Use MessageTable from PE for lookup")]
192                 protected override string FormatMessage (string source, uint eventID, string [] replacementStrings)
193                 {
194                         return string.Join (", ", replacementStrings);
195                 }
196
197                 protected override int GetEntryCount ()
198                 {
199                         string logDir = FindLogStore (CoreEventLog.Log);
200                         if (!Directory.Exists (logDir))
201                                 return 0;
202
203                         string[] logFiles = Directory.GetFiles (logDir, "*.log");
204                         return logFiles.Length;
205                 }
206
207                 protected override EventLogEntry GetEntry (int index)
208                 {
209                         string logDir = FindLogStore (CoreEventLog.Log);
210
211                         // our file names are one-based
212                         string file = Path.Combine (logDir, (index + 1).ToString (
213                                 CultureInfo.InvariantCulture) + ".log");
214
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 ();
234                                         if (c == '\0') {
235                                                 replacementTemp.Add (sb.ToString ());
236                                                 sb.Length = 0;
237                                         } else {
238                                                 sb.Append (c);
239                                         }
240                                 }
241                                 string [] replacementStrings = new string [replacementTemp.Count];
242                                 replacementTemp.CopyTo (replacementStrings, 0);
243
244                                 string message = FormatMessage (source, instanceID, replacementStrings);
245                                 int eventID = EventLog.GetEventID (instanceID);
246
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,
251                                         instanceID);
252                         }
253                 }
254
255                 [MonoTODO]
256                 protected override string GetLogDisplayName ()
257                 {
258                         return CoreEventLog.Log;
259                 }
260
261                 protected override string [] GetLogNames (string machineName)
262                 {
263                         if (!Directory.Exists (EventLogStore))
264                                 return new string [0];
265
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]);
270                         return logNames;
271                 }
272
273                 public override string LogNameFromSourceName (string source, string machineName)
274                 {
275                         if (!Directory.Exists (EventLogStore))
276                                 return string.Empty;
277
278                         string sourceDir = FindSourceDirectory (source);
279                         if (sourceDir == null)
280                                 return string.Empty;
281                         DirectoryInfo info = new DirectoryInfo (sourceDir);
282                         return info.Parent.Name;
283                 }
284
285                 public override bool SourceExists (string source, string machineName)
286                 {
287                         if (!Directory.Exists (EventLogStore))
288                                 return false;
289                         string sourceDir = FindSourceDirectory (source);
290                         return (sourceDir != null);
291                 }
292
293                 public override void WriteEntry (string [] replacementStrings, EventLogEntryType type, uint instanceID, short category, byte [] rawData)
294                 {
295                         lock (lockObject) {
296                                 string logDir = FindLogStore (CoreEventLog.Log);
297
298                                 int index = GetLatestIndex () + 1;
299                                 string logPath = Path.Combine (logDir, index.ToString (CultureInfo.InvariantCulture) + ".log");
300                                 try {
301                                         using (TextWriter w = File.CreateText (logPath)) {
302                                                 w.WriteLine ("InstanceID: {0}", instanceID.ToString (CultureInfo.InvariantCulture));
303                                                 w.WriteLine ("EntryType: {0}", (int) type);
304                                                 w.WriteLine ("Source: {0}", CoreEventLog.Source);
305                                                 w.WriteLine ("Category: {0}", category.ToString (CultureInfo.InvariantCulture));
306                                                 w.WriteLine ("TimeGenerated: {0}", DateTime.Now.ToString (
307                                                         DateFormat, CultureInfo.InvariantCulture));
308                                                 w.WriteLine ("ReplacementStrings: {0}", replacementStrings.
309                                                         Length.ToString (CultureInfo.InvariantCulture));
310                                                 StringBuilder sb = new StringBuilder ();
311                                                 for (int i = 0; i < replacementStrings.Length; i++) {
312                                                         string replacement = replacementStrings [i];
313                                                         sb.Append (replacement);
314                                                         sb.Append ('\0');
315                                                 }
316                                                 w.Write (sb.ToString ());
317                                                 w.Write (Convert.ToBase64String (rawData));
318                                         }
319                                 } catch (IOException) {
320                                         File.Delete (logPath);
321                                 }
322                         }
323                 }
324
325                 private string FindSourceDirectory (string source)
326                 {
327                         string sourceDir = null;
328
329                         string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
330                         for (int i = 0; i < logDirs.Length; i++) {
331                                 string [] sourceDirs = Directory.GetDirectories (logDirs [i], "*");
332                                 for (int j = 0; j < sourceDirs.Length; j++) {
333                                         string relativeDir = Path.GetFileName (sourceDirs [j]);
334                                         // use a case-insensitive comparison
335                                         if (string.Compare (relativeDir, source, true, CultureInfo.InvariantCulture) == 0) {
336                                                 sourceDir = sourceDirs [j];
337                                                 break;
338                                         }
339                                 }
340                         }
341                         return sourceDir;
342                 }
343
344                 private bool RunningOnUnix {
345                         get {
346                                 int p = (int) Environment.OSVersion.Platform;
347                                 return ((p == 4) || (p == 128) || (p == 6));
348                         }
349                 }
350
351                 private string FindLogStore (string logName) {
352                         // when the event log store does not yet exist, there's no need
353                         // to perform a case-insensitive lookup
354                         if (!Directory.Exists (EventLogStore))
355                                 return Path.Combine (EventLogStore, logName);
356
357                         // we'll use a case-insensitive lookup to match the MS behaviour
358                         // while still allowing the original casing of the log name to be
359                         // retained
360                         string [] logDirs = Directory.GetDirectories (EventLogStore, "*");
361                         for (int i = 0; i < logDirs.Length; i++) {
362                                 string relativeDir = Path.GetFileName (logDirs [i]);
363                                 // use a case-insensitive comparison
364                                 if (string.Compare (relativeDir, logName, true, CultureInfo.InvariantCulture) == 0) {
365                                         return logDirs [i];
366                                 }
367                         }
368
369                         return Path.Combine (EventLogStore, logName);
370                 }
371
372                 private string EventLogStore {
373                         get {
374                                 // for the local file implementation, the MONO_EVENTLOG_TYPE
375                                 // environment variable can contain the path of the event log
376                                 // store by using the following syntax: local:<path>
377                                 string eventLogType = Environment.GetEnvironmentVariable (EventLog.EVENTLOG_TYPE_VAR);
378                                 if (eventLogType != null && eventLogType.Length > EventLog.LOCAL_FILE_IMPL.Length + 1)
379                                         return eventLogType.Substring (EventLog.LOCAL_FILE_IMPL.Length + 1);
380                                 if (RunningOnUnix) {
381                                         return "/var/lib/mono/eventlog";
382                                 } else {
383                                         return Path.Combine (Environment.GetFolderPath (
384                                                 Environment.SpecialFolder.CommonApplicationData),
385                                                 "mono\\eventlog");
386                                 }
387                         }
388                 }
389
390                 private int GetLatestIndex () {
391                         // our file names are one-based
392                         int maxIndex = 0;
393                         string[] logFiles = Directory.GetFiles (FindLogStore (CoreEventLog.Log), "*.log");
394                         for (int i = 0; i < logFiles.Length; i++) {
395                                 try {
396                                         string file = logFiles[i];
397                                         int index = int.Parse (Path.GetFileNameWithoutExtension (
398                                                 file), CultureInfo.InvariantCulture);
399                                         if (index > maxIndex)
400                                                 maxIndex = index;
401                                 } catch {
402                                 }
403                         }
404                         return maxIndex;
405                 }
406
407                 private static void ModifyAccessPermissions (string path, string permissions)
408                 {
409                         ProcessStartInfo pi = new ProcessStartInfo ();
410                         pi.FileName = "chmod";
411                         pi.RedirectStandardOutput = true;
412                         pi.RedirectStandardError = true;
413                         pi.UseShellExecute = false;
414                         pi.Arguments = string.Format ("{0} \"{1}\"", permissions, path);
415
416                         Process p = null;
417                         try {
418                                 p = Process.Start (pi);
419                         } catch (Exception ex) {
420                                 throw new SecurityException ("Access permissions could not be modified.", ex);
421                         }
422
423                         p.WaitForExit ();
424                         if (p.ExitCode != 0) {
425                                 p.Close ();
426                                 throw new SecurityException ("Access permissions could not be modified.");
427                         }
428                         p.Close ();
429                 }
430
431                 public override OverflowAction OverflowAction {
432                         get { return OverflowAction.DoNotOverwrite; }
433                 }
434
435                 public override int MinimumRetentionDays {
436                         get { return int.MaxValue; }
437                 }
438
439                 public override long MaximumKilobytes {
440                         get { return long.MaxValue; }
441                         set { throw new NotSupportedException ("This EventLog implementation does not support setting max kilobytes policy"); }
442                 }
443
444                 public override void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
445                 {
446                         throw new NotSupportedException ("This EventLog implementation does not support modifying overflow policy");
447                 }
448
449                 public override void RegisterDisplayName (string resourceFile, long resourceId)
450                 {
451                         throw new NotSupportedException ("This EventLog implementation does not support registering display name");
452                 }
453         }
454 }