diff --git a/BUILD b/BUILD index da1683f35..95d353df9 100644 --- a/BUILD +++ b/BUILD @@ -235,8 +235,7 @@ test_suite( "//Source/common:SNTMetricSetTest", "//Source/common:SNTPrefixTreeTest", "//Source/common:SantaCacheTest", - "//Source/santactl:SNTCommandFileInfoTest", - "//Source/santactl:SNTCommandSyncTest", + "//Source/santactl:unit_tests", "//Source/santad:SNTApplicationCoreMetricsTest", "//Source/santad:SNTApplicationTest", "//Source/santad:SNTEndpointSecurityManagerTest", diff --git a/Source/common/BUILD b/Source/common/BUILD index d22aae3dc..b08dc44ef 100644 --- a/Source/common/BUILD +++ b/Source/common/BUILD @@ -213,6 +213,7 @@ objc_library( name = "SNTMetricSet", srcs = ["SNTMetricSet.m"], hdrs = ["SNTMetricSet.h"], + deps = [":SNTCommonEnums"], ) objc_library( diff --git a/Source/common/SNTMetricSet.h b/Source/common/SNTMetricSet.h index c62f90dd4..d6c6a98d6 100644 --- a/Source/common/SNTMetricSet.h +++ b/Source/common/SNTMetricSet.h @@ -13,6 +13,7 @@ /// limitations under the License. #import +#import "SNTCommonEnums.h" /** * Provides an abstraction for various metric systems that will be exported to @@ -48,6 +49,8 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { SNTMetricTypeCounter = 9, }; +NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType); + @interface SNTMetric : NSObject - (NSDictionary *)export; @end @@ -183,4 +186,12 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { - (NSDictionary *)export; @end +// Returns a human readble string from an SNTMetricFormat type +NSString *SNTMetricStringFromMetricFormatType(SNTMetricFormatType format); + +/** Normalizes dates in an exported dictionary to be ISO8601 timestamp strings in + * UTC time. + */ +NSDictionary *SNTMetricConvertDatesToISO8601Strings(NSDictionary *metrics); + NS_ASSUME_NONNULL_END diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index c1c7101df..0d7a3571d 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -13,6 +13,24 @@ /// limitations under the License. #import "SNTMetricSet.h" +#import "SNTCommonEnums.h" + +NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) { + NSString *typeStr; + switch (metricType) { + case SNTMetricTypeConstantBool: typeStr = @"SNTMetricTypeConstantBool"; break; + case SNTMetricTypeConstantString: typeStr = @"SNTMetricTypeConstantString"; break; + case SNTMetricTypeConstantInt64: typeStr = @"SNTMetricTypeConstantInt64"; break; + case SNTMetricTypeConstantDouble: typeStr = @"SNTMetricTypeConstantDouble"; break; + case SNTMetricTypeGaugeBool: typeStr = @"SNTMetricTypeGaugeBool"; break; + case SNTMetricTypeGaugeString: typeStr = @"SNTMetricTypeGaugeString"; break; + case SNTMetricTypeGaugeInt64: typeStr = @"SNTMetricTypeGaugeInt64"; break; + case SNTMetricTypeGaugeDouble: typeStr = @"SNTMetricTypeGaugeDouble"; break; + case SNTMetricTypeCounter: typeStr = @"SNTMetricTypeCounter"; break; + default: typeStr = @"SNTMetricTypeUnknown"; break; + } + return [NSString stringWithFormat:@"%@ %ld", typeStr, metricType]; +} /** * SNTMetricValue encapsulates the value of a metric along with the creation @@ -612,4 +630,48 @@ - (NSDictionary *)export { } return exported; } + +// Returns a human readble string from an SNTMetricFormat type +NSString *SNTMetricStringFromMetricFormatType(SNTMetricFormatType format) { + switch (format) { + case SNTMetricFormatTypeRawJSON: return @"rawjson"; + case SNTMetricFormatTypeMonarchJSON: return @"monarchjson"; + default: return @"Unknown Metric Format"; + } +} + +NSDictionary *SNTMetricConvertDatesToISO8601Strings(NSDictionary *metrics) { + NSMutableDictionary *mutableMetrics = [metrics mutableCopy]; + + id formatter; + + if (@available(macOS 10.13, *)) { + NSISO8601DateFormatter *isoFormatter = [[NSISO8601DateFormatter alloc] init]; + + isoFormatter.formatOptions = + NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithFractionalSeconds; + formatter = isoFormatter; + } else { + NSDateFormatter *localFormatter = [[NSDateFormatter alloc] init]; + [localFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"]; + [localFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; + formatter = localFormatter; + } + + for (NSString *metricName in mutableMetrics[@"metrics"]) { + NSMutableDictionary *metric = mutableMetrics[@"metrics"][metricName]; + + for (NSString *field in metric[@"fields"]) { + NSMutableArray *values = metric[@"fields"][field]; + + [values enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) { + values[index][@"created"] = [formatter stringFromDate:values[index][@"created"]]; + values[index][@"last_updated"] = [formatter stringFromDate:values[index][@"last_updated"]]; + }]; + } + } + + return mutableMetrics; +} + @end diff --git a/Source/common/SNTMetricSetTest.m b/Source/common/SNTMetricSetTest.m index 9202c44fb..72dee34b4 100644 --- a/Source/common/SNTMetricSetTest.m +++ b/Source/common/SNTMetricSetTest.m @@ -20,6 +20,9 @@ @interface SNTMetricStringGaugeTest : XCTestCase @interface SNTMetricSetTest : XCTestCase @end +@interface SNTMetricSetHelperFunctionsTest : XCTestCase +@end + // Stub out NSDate's date method @implementation NSDate (custom) @@ -549,3 +552,56 @@ - (void)testExportNSDictionary { } @end + +@implementation SNTMetricSetHelperFunctionsTest +- (void)testMakeMetricString { + NSArray *tests = @[ + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeUnknown], + @"expected" : @"SNTMetricTypeUnknown 0" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeConstantBool], + @"expected" : @"SNTMetricTypeConstantBool 1" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeConstantString], + @"expected" : @"SNTMetricTypeConstantString 2" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeConstantInt64], + @"expected" : @"SNTMetricTypeConstantInt64 3" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeConstantDouble], + @"expected" : @"SNTMetricTypeConstantDouble 4" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeBool], + @"expected" : @"SNTMetricTypeGaugeBool 5" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeString], + @"expected" : @"SNTMetricTypeGaugeString 6" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeInt64], + @"expected" : @"SNTMetricTypeGaugeInt64 7" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeGaugeDouble], + @"expected" : @"SNTMetricTypeGaugeDouble 8" + }, + @{ + @"input" : [NSNumber numberWithInt:SNTMetricTypeCounter], + @"expected" : @"SNTMetricTypeCounter 9" + } + ]; + + for (NSDictionary *test in tests) { + NSString *output = SNTMetricMakeStringFromMetricType([test[@"input"] integerValue]); + XCTAssertEqualObjects(test[@"expected"], output, @"expected %@ got %@", test[@"expected"], + output); + } +} +@end diff --git a/Source/common/SNTXPCUnprivilegedControlInterface.h b/Source/common/SNTXPCUnprivilegedControlInterface.h index 75c2fbc33..74a3b85af 100644 --- a/Source/common/SNTXPCUnprivilegedControlInterface.h +++ b/Source/common/SNTXPCUnprivilegedControlInterface.h @@ -72,6 +72,11 @@ - (void)enableBundles:(void (^)(BOOL))reply; - (void)enableTransitiveRules:(void (^)(BOOL))reply; +/// +/// Metrics ops +/// +- (void)metrics:(void (^)(NSDictionary *))reply; + /// /// GUI Ops /// diff --git a/Source/santactl/BUILD b/Source/santactl/BUILD index 3e4acd076..749f7ada2 100644 --- a/Source/santactl/BUILD +++ b/Source/santactl/BUILD @@ -3,6 +3,8 @@ load("//:helper.bzl", "santa_unit_test") licenses(["notice"]) +package(default_visibility = ["//:santa_package_group"]) + objc_library( name = "santactl_lib", srcs = [ @@ -15,6 +17,8 @@ objc_library( "Commands/SNTCommandRule.m", "Commands/SNTCommandStatus.m", "Commands/SNTCommandVersion.m", + "Commands/SNTCommandMetrics.h", + "Commands/SNTCommandMetrics.m", "Commands/sync/NSData+Zlib.h", "Commands/sync/NSData+Zlib.m", "Commands/sync/SNTCommandSync.m", @@ -54,6 +58,7 @@ objc_library( "//Source/common:SNTFileInfo", "//Source/common:SNTKernelCommon", "//Source/common:SNTLogging", + "//Source/common:SNTMetricSet", "//Source/common:SNTRule", "//Source/common:SNTStoredEvent", "//Source/common:SNTStrengthify", @@ -94,7 +99,6 @@ macos_command_line_application( "//conditions:default": "Santa_Dev.provisionprofile", }), version = "//:version", - visibility = ["//:santa_package_group"], deps = [":santactl_lib"], ) @@ -169,3 +173,25 @@ santa_unit_test( "@OCMock", ], ) + +santa_unit_test( + name = "SNTCommandMetricsTest", + srcs = ["Commands/SNTCommandMetricsTest.m"], + structured_resources = glob(["Commands/testdata/*"]), + visibility = ["//:santa_package_group"], + deps = [ + ":santactl_lib", + "//Source/santametricservice/Formats:SNTMetricFormatTestHelper", + "@OCMock", + ], +) + +test_suite( + name = "unit_tests", + tests = [ + ":SNTCommandFileInfoTest", + ":SNTCommandMetricsTest", + ":SNTCommandSyncTest", + ], + visibility = ["//:santa_package_group"], +) diff --git a/Source/santactl/Commands/SNTCommandMetrics.h b/Source/santactl/Commands/SNTCommandMetrics.h new file mode 100644 index 000000000..2c606ec44 --- /dev/null +++ b/Source/santactl/Commands/SNTCommandMetrics.h @@ -0,0 +1,22 @@ +/// Copyright 2021 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import + +#import "Source/santactl/SNTCommand.h" +#import "Source/santactl/SNTCommandController.h" + +@interface SNTCommandMetrics : SNTCommand +- (void)prettyPrintMetrics:(NSDictionary *)metircs asJSON:(BOOL)exportJSON; +@end diff --git a/Source/santactl/Commands/SNTCommandMetrics.m b/Source/santactl/Commands/SNTCommandMetrics.m new file mode 100644 index 000000000..9aafb3f88 --- /dev/null +++ b/Source/santactl/Commands/SNTCommandMetrics.m @@ -0,0 +1,145 @@ +/// Copyright 2021 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import +#import + +#import "Source/common/SNTCommonEnums.h" +#import "Source/common/SNTConfigurator.h" +#import "Source/common/SNTMetricSet.h" +#import "Source/common/SNTXPCControlInterface.h" +#import "Source/santactl/Commands/SNTCommandMetrics.h" +#import "Source/santactl/SNTCommand.h" +#import "Source/santactl/SNTCommandController.h" + +@implementation SNTCommandMetrics + +REGISTER_COMMAND_NAME(@"metrics") + ++ (BOOL)requiresRoot { + return NO; +} + ++ (BOOL)requiresDaemonConn { + return YES; +} + ++ (NSString *)shortHelpText { + return @"Show Santa metric information."; +} + ++ (NSString *)longHelpText { + return (@"Provides metrics about Santa's operation while it's running.\n" + @" Use --json to output in JSON format"); +} + +- (void)prettyPrintRootLabels:(NSDictionary *)rootLabels { + for (NSString *label in rootLabels) { + const char *labelStr = [label cStringUsingEncoding:NSUTF8StringEncoding]; + const char *valueStr = [rootLabels[label] cStringUsingEncoding:NSUTF8StringEncoding]; + + printf(" %-25s | %s\n", labelStr, valueStr); + } +} + +- (void)prettyPrintMetricValues:(NSDictionary *)metrics { + for (NSString *metricName in metrics) { + NSDictionary *metric = metrics[metricName]; + const char *metricNameStr = [metricName UTF8String]; + const char *description = [metric[@"description"] UTF8String]; + NSString *metricType = SNTMetricMakeStringFromMetricType([metric[@"type"] integerValue]); + const char *metricTypeStr = [metricType UTF8String]; + + printf(" %-25s | %s\n", "Metric Name", metricNameStr); + printf(" %-25s | %s\n", "Description", description); + printf(" %-25s | %s\n", "Type", metricTypeStr); + + for (NSString *fieldName in metric[@"fields"]) { + for (NSDictionary *field in metric[@"fields"][fieldName]) { + const char *fieldNameStr = [fieldName cStringUsingEncoding:NSUTF8StringEncoding]; + const char *fieldValueStr = [field[@"value"] cStringUsingEncoding:NSUTF8StringEncoding]; + const char *createdStr = [field[@"created"] UTF8String]; + const char *lastUpdatedStr = [field[@"last_updated"] UTF8String]; + const char *data = [[NSString stringWithFormat:@"%@", field[@"data"]] UTF8String]; + + if (strlen(fieldNameStr) > 0) { + printf(" %-25s | %s=%s\n", "Field", fieldNameStr, fieldValueStr); + } + + printf(" %-25s | %s\n", "Created", createdStr); + printf(" %-25s | %s\n", "Last Updated", lastUpdatedStr); + printf(" %-25s | %s\n", "Data", data); + } + } + printf("\n"); + } +} + +- (void)prettyPrintMetrics:(NSDictionary *)metrics asJSON:(BOOL)exportJSON { + BOOL exportMetrics = [[SNTConfigurator configurator] exportMetrics]; + NSURL *metricsURLStr = [[SNTConfigurator configurator] metricURL]; + SNTMetricFormatType metricFormat = [[SNTConfigurator configurator] metricFormat]; + NSUInteger metricExportInterval = [[SNTConfigurator configurator] metricExportInterval]; + NSDictionary *normalizedMetrics = SNTMetricConvertDatesToISO8601Strings(metrics); + + if (exportJSON) { + // Format + NSData *metricData = [NSJSONSerialization dataWithJSONObject:normalizedMetrics + options:NSJSONWritingPrettyPrinted + error:nil]; + NSString *metricStr = [[NSString alloc] initWithData:metricData encoding:NSUTF8StringEncoding]; + printf("%s\n", [metricStr UTF8String]); + return; + } + + if (!exportMetrics) { + printf("Metrics not configured\n"); + return; + } + + printf(">>> Metrics Info\n"); + printf(" %-25s | %s\n", "Metrics Server", [metricsURLStr.absoluteString UTF8String]); + printf(" %-25s | %s\n", "Metrics Format", + [SNTMetricStringFromMetricFormatType(metricFormat) UTF8String]); + printf(" %-25s | %lu\n", "Export Interval (seconds)", metricExportInterval); + printf("\n"); + + printf(">>> Root Labels\n"); + [self prettyPrintRootLabels:normalizedMetrics[@"root_labels"]]; + printf("\n"); + printf(">>> Metrics \n"); + [self prettyPrintMetricValues:normalizedMetrics[@"metrics"]]; +} + +- (void)runWithArguments:(NSArray *)arguments { + __block NSDictionary *metrics; + + dispatch_group_t group = dispatch_group_create(); + dispatch_group_enter(group); + + [[self.daemonConn remoteObjectProxy] metrics:^(NSDictionary *exportedMetrics) { + metrics = exportedMetrics; + dispatch_group_leave(group); + }]; + + // Wait a maximum of 5s for metrics collected from daemon to arrive. + if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) { + fprintf(stderr, "Failed to retrieve metrics from daemon\n\n"); + } + + [self prettyPrintMetrics:metrics asJSON:[arguments containsObject:@"--json"]]; + exit(0); +} + +@end diff --git a/Source/santactl/Commands/SNTCommandMetricsTest.m b/Source/santactl/Commands/SNTCommandMetricsTest.m new file mode 100644 index 000000000..c0a4ebc3d --- /dev/null +++ b/Source/santactl/Commands/SNTCommandMetricsTest.m @@ -0,0 +1,134 @@ +/// Copyright 2021 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import +#import + +#import "Source/common/SNTConfigurator.h" +#import "Source/santactl/Commands/SNTCommandMetrics.h" +#import "Source/santametricservice/Formats/SNTMetricFormatTestHelper.h" + +@interface SNTCommandMetricsTest : XCTestCase +@property NSString *tempDir; +@property id mockConfigurator; +@end + +@implementation SNTCommandMetricsTest + +- (void)setUp { + // create a temp dir + char template[] = "/tmp/sntcommandmetrictest.XXXXXXX"; + char *tempPath = mkdtemp(template); + + if (tempPath == NULL) { + NSLog(@"Unable to make temp directory"); + exit(1); + } + + self.tempDir = + [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempPath + length:strlen(tempPath)]; + // mock the SNTConfigurator + self.mockConfigurator = OCMClassMock([SNTConfigurator class]); + OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator); + OCMStub([self.mockConfigurator exportMetrics]).andReturn(YES); + OCMStub([self.mockConfigurator metricFormat]).andReturn(SNTMetricFormatTypeMonarchJSON); + OCMStub([self.mockConfigurator metricURL]) + .andReturn([NSURL URLWithString:@"http://localhost:2444/submit"]); + OCMStub([self.mockConfigurator metricExportInterval]).andReturn((NSUInteger)30); +} + +- (void)tearDown { + // delete the temp dir + NSError *err; + [[NSFileManager defaultManager] removeItemAtPath:self.tempDir error:&err]; + + if (err != nil) { + NSLog(@"unable to remove %@, error: %@", self.tempDir, err); + } + + dup2(1, STDOUT_FILENO); +} + +- (void)testPrettyPrintingJSON { + NSError *err; + NSString *path = [[NSBundle bundleForClass:[self class]] resourcePath]; + path = [path stringByAppendingPathComponent:@"Commands/testdata/metrics-prettyprint.json"]; + + NSString *goldenFileContents = [[NSString alloc] + initWithData:[NSData dataWithContentsOfFile:path options:NSDataReadingUncached error:&err] + encoding:NSUTF8StringEncoding]; + + XCTAssertNil(err, @"failed to read golden file %@ for testPrettyPrintingJSON", path); + + SNTCommandMetrics *metricsCmd = [[SNTCommandMetrics alloc] init]; + + NSString *outputPath = [NSString pathWithComponents:@[ self.tempDir, @"test.data" ]]; + + // redirect stdout + int fd = open([outputPath UTF8String], O_TRUNC | O_WRONLY | O_CREAT, 0600); + int saved_stdout = dup(fileno(stdout)); + dup2(fd, fileno(stdout)); + + [metricsCmd prettyPrintMetrics:[SNTMetricFormatTestHelper createValidMetricsDictionary] + asJSON:YES]; + + // restore stdout + fflush(stdout); + dup2(saved_stdout, fileno(stdout)); + + // open test file assert equal with golden file + NSString *commandOutput = + [[NSString alloc] initWithData:[NSData dataWithContentsOfFile:outputPath] + encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(goldenFileContents, commandOutput, + @"Metrics command command did not produce expected output"); +} + +- (void)testPrettyPrinting { + NSError *err; + NSString *path = [[NSBundle bundleForClass:[self class]] resourcePath]; + path = [path stringByAppendingPathComponent:@"Commands/testdata/metrics-prettyprint.txt"]; + + NSString *goldenFileContents = [[NSString alloc] + initWithData:[NSData dataWithContentsOfFile:path options:NSDataReadingUncached error:&err] + encoding:NSUTF8StringEncoding]; + + XCTAssertNil(err, @"failed to read golden file %@ for testPrettyPrinting", path); + + SNTCommandMetrics *metricsCmd = [[SNTCommandMetrics alloc] init]; + + NSString *outputPath = [NSString pathWithComponents:@[ self.tempDir, @"test.data" ]]; + + // redirect stdout + int fd = open([outputPath UTF8String], O_TRUNC | O_WRONLY | O_CREAT, 0600); + int saved_stdout = dup(fileno(stdout)); + dup2(fd, fileno(stdout)); + + [metricsCmd prettyPrintMetrics:[SNTMetricFormatTestHelper createValidMetricsDictionary] + asJSON:NO]; + + // restore stdout + fflush(stdout); + dup2(saved_stdout, fileno(stdout)); + + // open test file assert equal with golden file + NSString *commandOutput = + [[NSString alloc] initWithData:[NSData dataWithContentsOfFile:outputPath] + encoding:NSUTF8StringEncoding]; + XCTAssertEqualObjects(goldenFileContents, commandOutput, + @"Metrics command command did not produce expected output"); +} + +@end diff --git a/Source/santactl/Commands/testdata/metrics-prettyprint.json b/Source/santactl/Commands/testdata/metrics-prettyprint.json new file mode 100644 index 000000000..c8241ea5d --- /dev/null +++ b/Source/santactl/Commands/testdata/metrics-prettyprint.json @@ -0,0 +1,118 @@ +{ + "metrics" : { + "\/santa\/rules" : { + "type" : 7, + "description" : "Number of rules", + "fields" : { + "rule_type" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "binary", + "data" : 1 + }, + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "certificate", + "data" : 3 + } + ] + } + }, + "\/proc\/memory\/resident_size" : { + "type" : 7, + "description" : "The resident set size of this process", + "fields" : { + "" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "", + "data" : 123456789 + } + ] + } + }, + "\/santa\/events" : { + "type" : 9, + "description" : "Count of process exec events on the host", + "fields" : { + "rule_type" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "binary", + "data" : 1 + }, + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "certificate", + "data" : 2 + } + ] + } + }, + "\/santa\/using_endpoint_security_framework" : { + "type" : 1, + "description" : "Is santad using the endpoint security framework", + "fields" : { + "" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "", + "data" : true + } + ] + } + }, + "\/proc\/birth_timestamp" : { + "type" : 3, + "description" : "Start time of this santad instance, in microseconds since epoch", + "fields" : { + "" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "", + "data" : 1250999830800 + } + ] + } + }, + "\/proc\/memory\/virtual_size" : { + "type" : 7, + "description" : "The virtual memory size of this process", + "fields" : { + "" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "", + "data" : 987654321 + } + ] + } + }, + "\/build\/label" : { + "type" : 2, + "description" : "Software version running", + "fields" : { + "" : [ + { + "created" : "2021-09-16T21:07:34.826Z", + "last_updated" : "2021-09-16T21:07:34.826Z", + "value" : "", + "data" : "20210809.0.1" + } + ] + } + } + }, + "root_labels" : { + "hostname" : "testHost", + "username" : "testUser" + } +} diff --git a/Source/santactl/Commands/testdata/metrics-prettyprint.txt b/Source/santactl/Commands/testdata/metrics-prettyprint.txt new file mode 100644 index 000000000..0e7c017b6 --- /dev/null +++ b/Source/santactl/Commands/testdata/metrics-prettyprint.txt @@ -0,0 +1,69 @@ +>>> Metrics Info + Metrics Server | http://localhost:2444/submit + Metrics Format | monarchjson + Export Interval (seconds) | 30 + +>>> Root Labels + hostname | testHost + username | testUser + +>>> Metrics + Metric Name | /santa/rules + Description | Number of rules + Type | SNTMetricTypeGaugeInt64 7 + Field | rule_type=binary + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 1 + Field | rule_type=certificate + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 3 + + Metric Name | /proc/memory/resident_size + Description | The resident set size of this process + Type | SNTMetricTypeGaugeInt64 7 + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 123456789 + + Metric Name | /santa/events + Description | Count of process exec events on the host + Type | SNTMetricTypeCounter 9 + Field | rule_type=binary + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 1 + Field | rule_type=certificate + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 2 + + Metric Name | /santa/using_endpoint_security_framework + Description | Is santad using the endpoint security framework + Type | SNTMetricTypeConstantBool 1 + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 1 + + Metric Name | /proc/birth_timestamp + Description | Start time of this santad instance, in microseconds since epoch + Type | SNTMetricTypeConstantInt64 3 + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 1250999830800 + + Metric Name | /proc/memory/virtual_size + Description | The virtual memory size of this process + Type | SNTMetricTypeGaugeInt64 7 + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 987654321 + + Metric Name | /build/label + Description | Software version running + Type | SNTMetricTypeConstantString 2 + Created | 2021-09-16T21:07:34.826Z + Last Updated | 2021-09-16T21:07:34.826Z + Data | 20210809.0.1 + diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index e064374f1..93b8fbf19 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -20,6 +20,7 @@ #import "Source/common/SNTCommonEnums.h" #import "Source/common/SNTConfigurator.h" #import "Source/common/SNTLogging.h" +#import "Source/common/SNTMetricSet.h" #import "Source/common/SNTRule.h" #import "Source/common/SNTStoredEvent.h" #import "Source/common/SNTStrengthify.h" @@ -242,6 +243,18 @@ - (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply { reply(); } +#pragma mark Metrics Ops + +- (void)metrics:(void (^)(NSDictionary *))reply { + // If metrics are not enabled send nil back + if (![[SNTConfigurator configurator] exportMetrics]) { + reply(nil); + } + + SNTMetricSet *metricSet = [SNTMetricSet sharedInstance]; + reply([metricSet export]); +} + #pragma mark GUI Ops - (void)setNotificationListener:(NSXPCListenerEndpoint *)listener {