]> git.saurik.com Git - apple/configd.git/blobdiff - EventFactory/EventFactory.m
configd-963.200.27.tar.gz
[apple/configd.git] / EventFactory / EventFactory.m
diff --git a/EventFactory/EventFactory.m b/EventFactory/EventFactory.m
new file mode 100644 (file)
index 0000000..1886b5d
--- /dev/null
@@ -0,0 +1,224 @@
+//
+//  EventFactory.m
+//  SystemConfigurationNetworkEventFactory
+//
+//  Created by Allan Nathanson on 11/15/17.
+//
+//
+
+#import "EventFactory.h"
+#import <os/log.h>
+
+#pragma mark -
+#pragma mark Logging
+
+static os_log_t
+__log_Spectacles(void)
+{
+       static os_log_t log     = NULL;
+
+       if (log == NULL) {
+               log = os_log_create("com.apple.spectacles", "SystemConfiguration");
+       }
+
+       return log;
+}
+
+#define specs_log_err(format, ...)     os_log_error(__log_Spectacles(), format, ##__VA_ARGS__)
+#define specs_log_notice(format, ...)  os_log      (__log_Spectacles(), format, ##__VA_ARGS__)
+#define specs_log_info(format, ...)    os_log_info (__log_Spectacles(), format, ##__VA_ARGS__)
+#define specs_log_debug(format, ...)   os_log_debug(__log_Spectacles(), format, ##__VA_ARGS__)
+
+#pragma mark -
+#pragma mark Matching
+
+#define REMatched(re_matches, args)            \
+       ((re_matches != nil) && (re_matches.count == 1) && (re_matches[0].numberOfRanges == (args + 1)))
+
+#define REMatchRange(re_matches, arg)  \
+       [re_matches[0] rangeAtIndex:arg]
+
+#pragma mark -
+#pragma mark SystemConfiguratioin Network Event Factory
+
+@interface EventFactory ()
+
+@property (readonly, nonatomic) NSRegularExpression *kevExpressionInterfaceAttach;
+@property (readonly, nonatomic) NSRegularExpression *kevExpressionLink;
+@property (readonly, nonatomic) NSRegularExpression *kevExpressionLinkQuality;
+
+@end
+
+@implementation EventFactory
+
+- (instancetype)init
+{
+       self = [super init];
+       if (self) {
+               NSError *expressionError;
+
+               expressionError = nil;
+               _kevExpressionInterfaceAttach = [[NSRegularExpression alloc] initWithPattern:@"Process interface (attach|detach): (\\w+)" options:0 error:&expressionError];
+               if (expressionError != nil) {
+                       specs_log_info("Failed to create a regular expression: %@", expressionError);
+               }
+
+               expressionError = nil;
+               _kevExpressionLink = [[NSRegularExpression alloc] initWithPattern:@"Process interface link (down|up): (\\w+)" options:0 error:&expressionError];
+               if (expressionError != nil) {
+                       specs_log_info("Failed to create a regular expression: %@", expressionError);
+               }
+
+               expressionError = nil;
+               _kevExpressionLinkQuality = [[NSRegularExpression alloc] initWithPattern:@"Process interface quality: (\\w+) \\(q=([-\\d]+)\\)" options:0 error:&expressionError];
+               if (expressionError != nil) {
+                       specs_log_info("Failed to create a regular expression: %@", expressionError);
+               }
+       }
+
+       return self;
+}
+
+- (void)startWithLogSourceAttributes:(NSDictionary<NSString *, NSObject *> *)attributes
+{
+       //
+       // Prepare for parsing logs
+       //
+       specs_log_info("Event factory is starting with attributes: %@", attributes);
+}
+
+- (void)handleLogEvent:(EFLogEvent *)logEvent completionHandler:(void (^)(NSArray<EFEvent *> * _Nullable))completionHandler
+{
+       NSString                                        *category;
+       NSString                                        *message;
+       EFNetworkControlPathEvent       *newNetworkEvent        = nil;
+
+       message = logEvent.eventMessage;
+       if (message == nil) {
+               return;
+       }
+
+       //
+       // Parse logEvent and continue constructing SpectaclesNetworkEvent objects
+       //
+       // Note: if one or more NetworkEvent objects are complete, send them to the
+       // app in the completion handler block.
+       //
+
+
+       category = logEvent.category;
+       if ([category isEqualToString:@"InterfaceNamer"]) {
+
+               do {
+               } while (false);
+
+               specs_log_debug("Skipped [%@] message: %@", category, message);
+
+       } else if ([category isEqualToString:@"IPMonitor"]) {
+
+               do {
+               } while (false);
+
+               specs_log_debug("Skipped [%@] message: %@", category, message);
+
+       } else if ([category isEqualToString:@"KernelEventMonitor"]) {
+
+               do {
+                       NSArray<NSTextCheckingResult *> *matches;
+                       NSRange                                                 range   = NSMakeRange(0, message.length);
+
+                       //
+                       // interface attach/detach
+                       //
+                       matches = [_kevExpressionInterfaceAttach matchesInString:message
+                                                                                                                        options:NSMatchingReportProgress
+                                                                                                                          range:range];
+                       if (REMatched(matches, 2)) {
+                               NSString        *event;
+                               NSString        *interface;
+
+                               interface = [message substringWithRange:REMatchRange(matches, 2)];
+                               event     = [message substringWithRange:REMatchRange(matches, 1)];
+                               specs_log_debug("interface attach/detach: %@ --> %@", interface, event);
+
+                               newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
+                               newNetworkEvent.interfaceBSDName = interface;
+                               newNetworkEvent.interfaceStatus = [event isEqualToString:@"attach"] ? @"interface attached" : @"interface detached";
+                               break;
+                       }
+
+                       //
+                       // interface link up/down
+                       //
+                       matches = [_kevExpressionLink matchesInString:message
+                                                                                                 options:NSMatchingReportProgress
+                                                                                                       range:range];
+                       if (REMatched(matches, 2)) {
+                               NSString        *event;
+                               NSString        *interface;
+
+                               interface = [message substringWithRange:REMatchRange(matches, 2)];
+                               event     = [message substringWithRange:REMatchRange(matches, 1)];
+                               specs_log_debug("link change: %@ --> %@", interface, event);
+
+                               newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
+                               newNetworkEvent.interfaceBSDName = interface;
+                               newNetworkEvent.interfaceStatus = [event isEqualToString:@"up"] ? @"link up" : @"link down";
+                               break;
+                       }
+
+                       //
+                       // interface link quality
+                       //
+                       matches = [_kevExpressionLinkQuality matchesInString:message
+                                                                                                                options:NSMatchingReportProgress
+                                                                                                                  range:range];
+                       if (REMatched(matches, 2)) {
+                               NSString        *interface;
+                               NSString        *quality;
+
+                               interface = [message substringWithRange:REMatchRange(matches, 1)];
+                               quality   = [message substringWithRange:REMatchRange(matches, 2)];
+                               specs_log_debug("link quality: %@ --> %@", interface, quality);
+
+                               newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
+                               newNetworkEvent.interfaceBSDName = interface;
+                               newNetworkEvent.interfaceStatus = [NSString stringWithFormat:@"link quality = %@", quality];
+                               break;
+                       }
+
+                       specs_log_debug("Skipped [%@] message: %@", category, message);
+               } while (false);
+
+       } else if ([category isEqualToString:@"PreferencesMonitor"]) {
+
+                       do {
+                       } while (false);
+
+                       specs_log_debug("Skipped [%@] message: %@", category, message);
+
+       } else {
+               // if we have no handler for this category
+               specs_log_debug("Skipped [%@] message: %@", category, message);
+       }
+
+       if (newNetworkEvent != nil) {
+               completionHandler(@[ newNetworkEvent ]);
+       } else {
+               completionHandler(nil);
+       }
+}
+
+- (void)finishWithCompletionHandler:(void (^)(NSArray<EFEvent *> * _Nullable))completionHandler
+{
+       //
+       // Clean up
+       //
+       // Note: if one or more SpectaclesNetworkEvent objects are in the process of
+       // being built, return them in the completion handler block.
+       //
+       specs_log_notice("Event factory is finishing");
+       completionHandler(nil);
+}
+
+@end