3 // SystemConfigurationNetworkEventFactory
5 // Created by Allan Nathanson on 11/15/17.
9 #import "EventFactory.h"
16 __log_Spectacles(void)
18 static os_log_t log = NULL;
21 log = os_log_create("com.apple.spectacles", "SystemConfiguration");
27 #define specs_log_err(format, ...) os_log_error(__log_Spectacles(), format, ##__VA_ARGS__)
28 #define specs_log_notice(format, ...) os_log (__log_Spectacles(), format, ##__VA_ARGS__)
29 #define specs_log_info(format, ...) os_log_info (__log_Spectacles(), format, ##__VA_ARGS__)
30 #define specs_log_debug(format, ...) os_log_debug(__log_Spectacles(), format, ##__VA_ARGS__)
35 #define REMatched(re_matches, args) \
36 ((re_matches != nil) && (re_matches.count == 1) && (re_matches[0].numberOfRanges == (args + 1)))
38 #define REMatchRange(re_matches, arg) \
39 [re_matches[0] rangeAtIndex:arg]
42 #pragma mark SystemConfiguratioin Network Event Factory
44 @interface EventFactory ()
46 @property (readonly, nonatomic) NSRegularExpression *kevExpressionInterfaceAttach;
47 @property (readonly, nonatomic) NSRegularExpression *kevExpressionLink;
48 @property (readonly, nonatomic) NSRegularExpression *kevExpressionLinkQuality;
52 @implementation EventFactory
58 NSError *expressionError;
60 expressionError = nil;
61 _kevExpressionInterfaceAttach = [[NSRegularExpression alloc] initWithPattern:@"Process interface (attach|detach): (\\w+)" options:0 error:&expressionError];
62 if (expressionError != nil) {
63 specs_log_info("Failed to create a regular expression: %@", expressionError);
66 expressionError = nil;
67 _kevExpressionLink = [[NSRegularExpression alloc] initWithPattern:@"Process interface link (down|up): (\\w+)" options:0 error:&expressionError];
68 if (expressionError != nil) {
69 specs_log_info("Failed to create a regular expression: %@", expressionError);
72 expressionError = nil;
73 _kevExpressionLinkQuality = [[NSRegularExpression alloc] initWithPattern:@"Process interface quality: (\\w+) \\(q=([-\\d]+)\\)" options:0 error:&expressionError];
74 if (expressionError != nil) {
75 specs_log_info("Failed to create a regular expression: %@", expressionError);
82 - (void)startWithLogSourceAttributes:(NSDictionary<NSString *, NSObject *> *)attributes
85 // Prepare for parsing logs
87 specs_log_info("Event factory is starting with attributes: %@", attributes);
90 - (void)handleLogEvent:(EFLogEvent *)logEvent completionHandler:(void (^)(NSArray<EFEvent *> * _Nullable))completionHandler
94 EFNetworkControlPathEvent *newNetworkEvent = nil;
96 message = logEvent.eventMessage;
102 // Parse logEvent and continue constructing SpectaclesNetworkEvent objects
104 // Note: if one or more NetworkEvent objects are complete, send them to the
105 // app in the completion handler block.
109 category = logEvent.category;
110 if ([category isEqualToString:@"InterfaceNamer"]) {
115 specs_log_debug("Skipped [%@] message: %@", category, message);
117 } else if ([category isEqualToString:@"IPMonitor"]) {
122 specs_log_debug("Skipped [%@] message: %@", category, message);
124 } else if ([category isEqualToString:@"KernelEventMonitor"]) {
127 NSArray<NSTextCheckingResult *> *matches;
128 NSRange range = NSMakeRange(0, message.length);
131 // interface attach/detach
133 matches = [_kevExpressionInterfaceAttach matchesInString:message
134 options:NSMatchingReportProgress
136 if (REMatched(matches, 2)) {
140 interface = [message substringWithRange:REMatchRange(matches, 2)];
141 event = [message substringWithRange:REMatchRange(matches, 1)];
142 specs_log_debug("interface attach/detach: %@ --> %@", interface, event);
144 newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
145 newNetworkEvent.interfaceBSDName = interface;
146 newNetworkEvent.interfaceStatus = [event isEqualToString:@"attach"] ? @"interface attached" : @"interface detached";
151 // interface link up/down
153 matches = [_kevExpressionLink matchesInString:message
154 options:NSMatchingReportProgress
156 if (REMatched(matches, 2)) {
160 interface = [message substringWithRange:REMatchRange(matches, 2)];
161 event = [message substringWithRange:REMatchRange(matches, 1)];
162 specs_log_debug("link change: %@ --> %@", interface, event);
164 newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
165 newNetworkEvent.interfaceBSDName = interface;
166 newNetworkEvent.interfaceStatus = [event isEqualToString:@"up"] ? @"link up" : @"link down";
171 // interface link quality
173 matches = [_kevExpressionLinkQuality matchesInString:message
174 options:NSMatchingReportProgress
176 if (REMatched(matches, 2)) {
180 interface = [message substringWithRange:REMatchRange(matches, 1)];
181 quality = [message substringWithRange:REMatchRange(matches, 2)];
182 specs_log_debug("link quality: %@ --> %@", interface, quality);
184 newNetworkEvent = [[EFNetworkControlPathEvent alloc] initWithLogEvent:logEvent subsystemIdentifier:[[NSData alloc] init]];
185 newNetworkEvent.interfaceBSDName = interface;
186 newNetworkEvent.interfaceStatus = [NSString stringWithFormat:@"link quality = %@", quality];
190 specs_log_debug("Skipped [%@] message: %@", category, message);
193 } else if ([category isEqualToString:@"PreferencesMonitor"]) {
198 specs_log_debug("Skipped [%@] message: %@", category, message);
201 // if we have no handler for this category
202 specs_log_debug("Skipped [%@] message: %@", category, message);
205 if (newNetworkEvent != nil) {
206 completionHandler(@[ newNetworkEvent ]);
208 completionHandler(nil);
212 - (void)finishWithCompletionHandler:(void (^)(NSArray<EFEvent *> * _Nullable))completionHandler
217 // Note: if one or more SpectaclesNetworkEvent objects are in the process of
218 // being built, return them in the completion handler block.
220 specs_log_notice("Event factory is finishing");
221 completionHandler(nil);