Skip to content

Commit a39b1ce

Browse files
committed
Updated dbtool to support MTA files
1 parent 1b44640 commit a39b1ce

File tree

11 files changed

+592
-244
lines changed

11 files changed

+592
-244
lines changed
Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// // Copyright (c) Microsoft Corporation.
1+
// // Copyright (c) Microsoft Corporation.
22
// // Licensed under the MIT License.
33

44
using EventLogExpert.Eventing.EventProviderDatabase;
@@ -20,6 +20,14 @@ public static Command GetCommand()
2020
Description = "File to create. Must have a .db extension."
2121
};
2222

23+
Argument<string?> sourceArgument = new("source")
24+
{
25+
Description = "Optional provider source: a .db file, an exported .evtx file, or a folder containing " +
26+
".db and/or .evtx files (top-level only). When omitted, local providers on this machine are used. " +
27+
"When supplied, ONLY the source is used (no fallback to local providers).",
28+
Arity = ArgumentArity.ZeroOrOne
29+
};
30+
2331
Option<string> filterOption = new("--filter")
2432
{
2533
Description = "Only providers matching specified regex string will be added to the database."
@@ -28,7 +36,8 @@ public static Command GetCommand()
2836
Option<string> skipProvidersInFileOption = new("--skip-providers-in-file")
2937
{
3038
Description =
31-
"Any providers found in the specified database file will not be included in the new database. " +
39+
"Any providers found in the specified source (a .db file, an exported .evtx file, or a folder " +
40+
"containing them, top-level only) will not be included in the new database. " +
3241
"For example, when creating a database of event providers for Exchange Server, it may be useful " +
3342
"to provide a database of all providers from a fresh OS install with no other products. That way, all the " +
3443
"OS providers are skipped, and only providers added by Exchange or other installed products " +
@@ -41,6 +50,7 @@ public static Command GetCommand()
4150
};
4251

4352
createDatabaseCommand.Arguments.Add(fileArgument);
53+
createDatabaseCommand.Arguments.Add(sourceArgument);
4454
createDatabaseCommand.Options.Add(filterOption);
4555
createDatabaseCommand.Options.Add(skipProvidersInFileOption);
4656
createDatabaseCommand.Options.Add(verboseOption);
@@ -51,76 +61,63 @@ public static Command GetCommand()
5161
new CreateDatabaseCommand(sp.GetRequiredService<ITraceLogger>())
5262
.CreateDatabase(
5363
result.GetRequiredValue(fileArgument),
64+
result.GetValue(sourceArgument),
5465
result.GetValue(filterOption),
5566
result.GetValue(skipProvidersInFileOption));
5667
});
5768

5869
return createDatabaseCommand;
5970
}
6071

61-
private void CreateDatabase(string path, string? filter, string? skipProvidersInFile)
72+
private void CreateDatabase(string path, string? source, string? filter, string? skipProvidersInFile)
6273
{
6374
if (File.Exists(path))
6475
{
6576
Logger.Error($"Cannot create database because file already exists: {path}");
6677
return;
6778
}
6879

69-
if (Path.GetExtension(path) != ".db")
80+
if (!string.Equals(Path.GetExtension(path), ".db", StringComparison.OrdinalIgnoreCase))
7081
{
7182
Logger.Error($"File extension must be .db.");
7283
return;
7384
}
7485

75-
HashSet<string> skipProviderNames = [];
86+
if (source is not null && !ProviderSource.TryValidate(source, Logger)) { return; }
87+
88+
HashSet<string> skipProviderNames = new(StringComparer.OrdinalIgnoreCase);
7689

7790
if (!string.IsNullOrWhiteSpace(skipProvidersInFile))
7891
{
79-
if (!File.Exists(skipProvidersInFile))
80-
{
81-
Logger.Error($"File not found: {skipProvidersInFile}");
82-
return;
83-
}
84-
85-
using var skipDbContext = new EventProviderDbContext(skipProvidersInFile, true, Logger);
92+
if (!ProviderSource.TryValidate(skipProvidersInFile, Logger)) { return; }
8693

87-
foreach (var provider in skipDbContext.ProviderDetails)
94+
foreach (var name in ProviderSource.LoadProviderNames(skipProvidersInFile, Logger))
8895
{
89-
skipProviderNames.Add(provider.ProviderName);
96+
skipProviderNames.Add(name);
9097
}
9198

92-
Logger.Info($"Found {skipProviderNames.Count} providers in file {skipProvidersInFile}. These will not be included in the new database.");
99+
Logger.Info($"Found {skipProviderNames.Count} providers in {skipProvidersInFile}. These will not be included in the new database.");
93100
}
94101

95-
var providerNames = GetLocalProviderNames(filter);
102+
IEnumerable<ProviderDetails> providersToAdd = source is null
103+
? LoadLocalProviders(filter, skipProviderNames)
104+
: ProviderSource.LoadProviders(source, Logger, filter, skipProviderNames);
96105

97-
if (!providerNames.Any())
98-
{
99-
Logger.Warn($"No providers found matching filter {filter}.");
100-
return;
101-
}
102-
103-
var providerNamesNotSkipped = providerNames.Where(name => !skipProviderNames.Contains(name)).ToList();
106+
var providersNotSkipped = providersToAdd.ToList();
104107

105-
var numberSkipped = providerNames.Count - providerNamesNotSkipped.Count;
106-
107-
if (numberSkipped > 0)
108+
if (providersNotSkipped.Count == 0)
108109
{
109-
Logger.Info($"{numberSkipped} providers were skipped due to being present in the specified database.");
110+
Logger.Warn($"No providers to add to the new database.");
111+
return;
110112
}
111113

112114
using var dbContext = new EventProviderDbContext(path, false, Logger);
113115

114-
LogProviderDetailHeader(providerNamesNotSkipped);
116+
LogProviderDetailHeader(providersNotSkipped.Select(p => p.ProviderName));
115117

116-
foreach (var providerName in providerNamesNotSkipped)
118+
foreach (var details in providersNotSkipped)
117119
{
118-
var provider = new EventMessageProvider(providerName, Logger);
119-
120-
var details = provider.LoadProviderDetails();
121-
122120
dbContext.ProviderDetails.Add(details);
123-
124121
LogProviderDetails(details);
125122
}
126123

@@ -131,4 +128,5 @@ private void CreateDatabase(string path, string? filter, string? skipProvidersIn
131128

132129
Logger.Info($"Done!");
133130
}
131+
134132
}

src/EventLogExpert.EventDbTool/DbToolCommand.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ protected static List<string> GetLocalProviderNames(string? filter)
2626
return providers;
2727
}
2828

29+
protected IEnumerable<ProviderDetails> LoadLocalProviders(string? filter, IReadOnlySet<string>? skipProviderNames = null)
30+
{
31+
foreach (var providerName in GetLocalProviderNames(filter))
32+
{
33+
// Skip BEFORE resolving so we don't pay the cost of loading metadata for providers we
34+
// are about to discard (e.g. when --skip-providers-in-file lists most local providers).
35+
if (skipProviderNames is not null && skipProviderNames.Contains(providerName)) { continue; }
36+
37+
yield return new EventMessageProvider(providerName, Logger).LoadProviderDetails();
38+
}
39+
}
40+
2941
protected void LogProviderDetailHeader(IEnumerable<string> providerNames)
3042
{
3143
var maxNameLength = providerNames.Any() ? providerNames.Max(p => p.Length) : 14;

src/EventLogExpert.EventDbTool/DiffDatabaseCommand.cs

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ public static Command GetCommand()
1515
{
1616
Command diffDatabaseCommand = new(
1717
"diff",
18-
"Given two databases, produces a third database containing all providers " +
19-
"from the second database which are not in the first database.");
18+
"Given two provider sources (each may be a .db, an exported .evtx, or a folder containing them), " +
19+
"produces a database containing all providers from the second source which are not in the first source.");
2020

21-
Argument<string> dbOneArgument = new("first db")
21+
Argument<string> firstArgument = new("first source")
2222
{
23-
Description = "The first database to compare."
23+
Description = "The first source to compare: a .db, an exported .evtx, or a folder containing .db and/or .evtx files (top-level only)."
2424
};
2525

26-
Argument<string> dbTwoArgument = new("second db")
26+
Argument<string> secondArgument = new("second source")
2727
{
28-
Description = "The second database to compare."
28+
Description = "The second source to compare: a .db, an exported .evtx, or a folder containing .db and/or .evtx files (top-level only)."
2929
};
3030

3131
Argument<string> newDbArgument = new("new db")
3232
{
33-
Description = "The new database containing only the providers in the second db which are not in the first db. Must have a .db extension."
33+
Description = "The new database containing only the providers in the second source which are not in the first source. Must have a .db extension."
3434
};
3535

3636
Option<bool> verboseOption = new("--verbose")
3737
{
3838
Description = "Verbose logging. May be useful for troubleshooting."
3939
};
4040

41-
diffDatabaseCommand.Arguments.Add(dbOneArgument);
42-
diffDatabaseCommand.Arguments.Add(dbTwoArgument);
41+
diffDatabaseCommand.Arguments.Add(firstArgument);
42+
diffDatabaseCommand.Arguments.Add(secondArgument);
4343
diffDatabaseCommand.Arguments.Add(newDbArgument);
4444
diffDatabaseCommand.Options.Add(verboseOption);
4545

@@ -48,76 +48,67 @@ public static Command GetCommand()
4848
using var sp = Program.BuildServiceProvider(action.GetValue(verboseOption));
4949
new DiffDatabaseCommand(sp.GetRequiredService<ITraceLogger>())
5050
.DiffDatabase(
51-
action.GetRequiredValue(dbOneArgument),
52-
action.GetRequiredValue(dbTwoArgument),
51+
action.GetRequiredValue(firstArgument),
52+
action.GetRequiredValue(secondArgument),
5353
action.GetRequiredValue(newDbArgument));
5454
});
5555

5656
return diffDatabaseCommand;
5757
}
5858

59-
private void DiffDatabase(string dbOne, string dbTwo, string newDb)
59+
private void DiffDatabase(string firstSource, string secondSource, string newDb)
6060
{
61-
foreach (var path in new[] { dbOne, dbTwo })
62-
{
63-
if (File.Exists(path)) { continue; }
64-
65-
Logger.Error($"File not found: {path}");
66-
return;
67-
}
61+
if (!ProviderSource.TryValidate(firstSource, Logger)) { return; }
62+
if (!ProviderSource.TryValidate(secondSource, Logger)) { return; }
6863

6964
if (File.Exists(newDb))
7065
{
7166
Logger.Error($"File already exists: {newDb}");
7267
return;
7368
}
7469

75-
if (Path.GetExtension(newDb) != ".db")
70+
if (!string.Equals(Path.GetExtension(newDb), ".db", StringComparison.OrdinalIgnoreCase))
7671
{
7772
Logger.Error($"New db path must have a .db extension.");
7873
return;
7974
}
8075

81-
var dbOneProviderNames = new HashSet<string>();
82-
83-
using (var dbOneContext = new EventProviderDbContext(dbOne, true, Logger))
84-
{
85-
dbOneContext.ProviderDetails.Select(p => p.ProviderName).ToList()
86-
.ForEach(name => dbOneProviderNames.Add(name));
87-
}
76+
var firstProviderNames = new HashSet<string>(
77+
ProviderSource.LoadProviderNames(firstSource, Logger),
78+
StringComparer.OrdinalIgnoreCase);
8879

8980
var providersCopied = new List<ProviderDetails>();
9081

91-
using var dbTwoContext = new EventProviderDbContext(dbTwo, true, Logger);
9282
using var newDbContext = new EventProviderDbContext(newDb, false, Logger);
9383

94-
foreach (var details in dbTwoContext.ProviderDetails)
84+
foreach (var details in ProviderSource.LoadProviders(secondSource, Logger))
9585
{
96-
if (dbOneProviderNames.Contains(details.ProviderName))
86+
if (firstProviderNames.Contains(details.ProviderName))
9787
{
98-
Logger.Info($"Skipping {details.ProviderName} because it is present in both databases.");
88+
Logger.Info($"Skipping {details.ProviderName} because it is present in both sources.");
89+
continue;
9990
}
100-
else
91+
92+
Logger.Info($"Copying {details.ProviderName} because it is present in second source but not first.");
93+
94+
newDbContext.ProviderDetails.Add(new ProviderDetails
10195
{
102-
Logger.Info($"Copying {details.ProviderName} because it is present in second db but not first db.");
103-
newDbContext.ProviderDetails.Add(new ProviderDetails
104-
{
105-
ProviderName = details.ProviderName,
106-
Events = details.Events,
107-
Keywords = details.Keywords,
108-
Messages = details.Messages,
109-
Opcodes = details.Opcodes,
110-
Tasks = details.Tasks
111-
});
112-
113-
providersCopied.Add(details);
114-
}
96+
ProviderName = details.ProviderName,
97+
Events = details.Events,
98+
Parameters = details.Parameters,
99+
Keywords = details.Keywords,
100+
Messages = details.Messages,
101+
Opcodes = details.Opcodes,
102+
Tasks = details.Tasks
103+
});
104+
105+
providersCopied.Add(details);
115106
}
116107

117108
newDbContext.SaveChanges();
118109

119110
if (providersCopied.Count <= 0) { return; }
120-
111+
121112
Logger.Info($"Providers copied to new database:");
122113
Logger.Info($"");
123114
LogProviderDetailHeader(providersCopied.Select(p => p.ProviderName));

0 commit comments

Comments
 (0)