]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2007-2018, 2020 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * Modification History | |
26 | * | |
27 | * October 24, 2007 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <unistd.h> | |
34 | #include <CoreFoundation/CoreFoundation.h> | |
35 | ||
36 | #define SC_LOG_HANDLE __log_SCMonitor | |
37 | #define SC_LOG_HANDLE_TYPE static | |
38 | #include <SystemConfiguration/SystemConfiguration.h> | |
39 | #include <SystemConfiguration/SCPrivate.h> | |
40 | ||
41 | #include <IOKit/IOKitLib.h> | |
42 | #include <IOKit/IOKitKeysPrivate.h> | |
43 | #include <IOKit/IOMessage.h> | |
44 | #include <CoreServices/CoreServices.h> | |
45 | #include "UserEventAgentInterface.h" | |
46 | ||
47 | #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor" | |
48 | #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns" | |
49 | ||
50 | #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane" | |
51 | #define NETWORK_PREF_CMD "New Interface" | |
52 | ||
53 | /* | |
54 | * The following keys/values control the actions taken when a new interface | |
55 | * has been detected and whether we interact with the user. | |
56 | * | |
57 | * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist | |
58 | * file) or per-inteface in the IORegistry. | |
59 | * | |
60 | * For the "New Interface Detected Action" key we define the following [CFString] | |
61 | * values : | |
62 | * | |
63 | * "None" No action, ignore this interface. | |
64 | * "Prompt" Post a "new interface detected" notification to the user. | |
65 | * "Configure" Automatically configure this interface without any | |
66 | * intervention. | |
67 | * | |
68 | * Note: automatic configuration may not be possible if the logged in user | |
69 | * is NOT "root" (eUID==0) or if the authorization right that governs | |
70 | * SCHelper write operations (kSCPreferencesAuthorizationRight_write) | |
71 | * is not currently available. | |
72 | * | |
73 | * An [older] "User Intervention" key is also supported. That CFBoolean | |
74 | * key, if present and TRUE, implies "Configure" configuration of the | |
75 | * interface without intervention. | |
76 | */ | |
77 | ||
78 | typedef struct { | |
79 | UserEventAgentInterfaceStruct *_UserEventAgentInterface; | |
80 | CFUUIDRef _factoryID; | |
81 | UInt32 _refCount; | |
82 | ||
83 | Boolean debug; | |
84 | ||
85 | CFStringRef configuration_action; | |
86 | ||
87 | CFRunLoopSourceRef monitorRls; | |
88 | ||
89 | IONotificationPortRef notifyPort; | |
90 | io_iterator_t notifyIterator; | |
91 | CFMutableArrayRef notifyNodes; | |
92 | ||
93 | // interfaces that we already know about | |
94 | CFMutableSetRef interfaces_known; | |
95 | ||
96 | // interfaces that should be auto-configured (no user notification) | |
97 | CFMutableArrayRef interfaces_configure; | |
98 | ||
99 | // interfaces that require user notification | |
100 | CFMutableArrayRef interfaces_prompt; | |
101 | ||
102 | CFUserNotificationRef userNotification; | |
103 | CFRunLoopSourceRef userRls; | |
104 | ||
105 | AuthorizationRef authorization; | |
106 | } MyType; | |
107 | ||
108 | static CFMutableDictionaryRef notify_to_instance = NULL; | |
109 | ||
110 | ||
111 | #pragma mark - | |
112 | #pragma mark Logging | |
113 | ||
114 | ||
115 | /* | |
116 | * Logging | |
117 | */ | |
118 | static os_log_t | |
119 | __log_SCMonitor(void) | |
120 | { | |
121 | static os_log_t log = NULL; | |
122 | ||
123 | if (log == NULL) { | |
124 | log = os_log_create("com.apple.SystemConfiguration", "SCMonitor"); | |
125 | } | |
126 | ||
127 | return log; | |
128 | } | |
129 | ||
130 | ||
131 | #pragma mark - | |
132 | #pragma mark Authorization | |
133 | ||
134 | ||
135 | static AuthorizationRef | |
136 | getAuthorization(MyType *myInstance) | |
137 | { | |
138 | if (myInstance->authorization == NULL) { | |
139 | AuthorizationFlags flags = kAuthorizationFlagDefaults; | |
140 | OSStatus status; | |
141 | ||
142 | status = AuthorizationCreate(NULL, | |
143 | kAuthorizationEmptyEnvironment, | |
144 | flags, | |
145 | &myInstance->authorization); | |
146 | if (status != errAuthorizationSuccess) { | |
147 | SC_log(LOG_ERR, "AuthorizationCreate() failed: status = %d", (int)status); | |
148 | } | |
149 | } | |
150 | ||
151 | return myInstance->authorization; | |
152 | } | |
153 | ||
154 | ||
155 | static Boolean | |
156 | hasAuthorization(MyType *myInstance) | |
157 | { | |
158 | AuthorizationRef authorization; | |
159 | Boolean isAdmin = FALSE; | |
160 | ||
161 | authorization = getAuthorization(myInstance); | |
162 | if (authorization != NULL) { | |
163 | AuthorizationFlags flags = kAuthorizationFlagDefaults; | |
164 | AuthorizationItem items[1]; | |
165 | AuthorizationRights rights; | |
166 | OSStatus status; | |
167 | ||
168 | items[0].name = kSCPreferencesAuthorizationRight_write; | |
169 | items[0].value = NULL; | |
170 | items[0].valueLength = 0; | |
171 | items[0].flags = 0; | |
172 | ||
173 | rights.count = sizeof(items) / sizeof(items[0]); | |
174 | rights.items = items; | |
175 | ||
176 | status = AuthorizationCopyRights(authorization, | |
177 | &rights, | |
178 | kAuthorizationEmptyEnvironment, | |
179 | flags, | |
180 | NULL); | |
181 | isAdmin = (status == errAuthorizationSuccess); | |
182 | } | |
183 | ||
184 | return isAdmin; | |
185 | } | |
186 | ||
187 | ||
188 | static void | |
189 | freeAuthorization(MyType *myInstance) | |
190 | { | |
191 | if (myInstance->authorization != NULL) { | |
192 | AuthorizationFree(myInstance->authorization, kAuthorizationFlagDefaults); | |
193 | // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights); | |
194 | myInstance->authorization = NULL; | |
195 | } | |
196 | ||
197 | return; | |
198 | } | |
199 | ||
200 | ||
201 | #pragma mark - | |
202 | #pragma mark New interface notification / configuration | |
203 | ||
204 | ||
205 | static void | |
206 | open_NetworkPrefPane(MyType *myInstance) | |
207 | { | |
208 | #pragma unused(myInstance) | |
209 | AEDesc aeDesc = { typeNull, NULL }; | |
210 | CFArrayRef prefArray; | |
211 | CFURLRef prefURL; | |
212 | LSLaunchURLSpec prefSpec; | |
213 | OSStatus status; | |
214 | ||
215 | prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, | |
216 | CFSTR(NETWORK_PREF_APP), | |
217 | kCFURLPOSIXPathStyle, | |
218 | FALSE); | |
219 | prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks); | |
220 | CFRelease(prefURL); | |
221 | ||
222 | status = AECreateDesc('ptru', | |
223 | (const void *)NETWORK_PREF_CMD, | |
224 | strlen(NETWORK_PREF_CMD), | |
225 | &aeDesc); | |
226 | if (status != noErr) { | |
227 | SC_log(LOG_ERR, "AECreateDesc() failed: %d", (int)status); | |
228 | } | |
229 | ||
230 | prefSpec.appURL = NULL; | |
231 | prefSpec.itemURLs = prefArray; | |
232 | prefSpec.passThruParams = &aeDesc; | |
233 | prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents; | |
234 | prefSpec.asyncRefCon = NULL; | |
235 | ||
236 | status = LSOpenFromURLSpec(&prefSpec, NULL); | |
237 | if (status != noErr) { | |
238 | SC_log(LOG_ERR, "LSOpenFromURLSpec() failed: %d", (int)status); | |
239 | } | |
240 | ||
241 | CFRelease(prefArray); | |
242 | if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc); | |
243 | return; | |
244 | } | |
245 | ||
246 | ||
247 | static void | |
248 | notify_remove(MyType *myInstance, Boolean cancel) | |
249 | { | |
250 | if (myInstance->interfaces_configure != NULL) { | |
251 | CFRelease(myInstance->interfaces_configure); | |
252 | myInstance->interfaces_configure = NULL; | |
253 | } | |
254 | ||
255 | if (myInstance->interfaces_prompt != NULL) { | |
256 | CFRelease(myInstance->interfaces_prompt); | |
257 | myInstance->interfaces_prompt = NULL; | |
258 | } | |
259 | ||
260 | if (myInstance->userRls != NULL) { | |
261 | CFRunLoopSourceInvalidate(myInstance->userRls); | |
262 | CFRelease(myInstance->userRls); | |
263 | myInstance->userRls = NULL; | |
264 | } | |
265 | ||
266 | if (myInstance->userNotification != NULL) { | |
267 | if (cancel) { | |
268 | SInt32 status; | |
269 | ||
270 | status = CFUserNotificationCancel(myInstance->userNotification); | |
271 | if (status != 0) { | |
272 | SC_log(LOG_ERR, | |
273 | "CFUserNotificationCancel() failed, status=%d", | |
274 | (int)status); | |
275 | } | |
276 | } | |
277 | CFRelease(myInstance->userNotification); | |
278 | myInstance->userNotification = NULL; | |
279 | } | |
280 | ||
281 | return; | |
282 | } | |
283 | ||
284 | ||
285 | static void | |
286 | notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags) | |
287 | { | |
288 | MyType *myInstance = NULL; | |
289 | ||
290 | // get instance for notification | |
291 | if (notify_to_instance != NULL) { | |
292 | myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification); | |
293 | if (myInstance != NULL) { | |
294 | CFDictionaryRemoveValue(notify_to_instance, userNotification); | |
295 | if (CFDictionaryGetCount(notify_to_instance) == 0) { | |
296 | CFRelease(notify_to_instance); | |
297 | notify_to_instance = NULL; | |
298 | } | |
299 | } | |
300 | } | |
301 | if (myInstance == NULL) { | |
302 | SC_log(LOG_ERR, "can't find user notification"); | |
303 | return; | |
304 | } | |
305 | ||
306 | // process response | |
307 | switch (response_flags & 0x3) { | |
308 | case kCFUserNotificationDefaultResponse: | |
309 | // user asked to configure interface | |
310 | open_NetworkPrefPane(myInstance); | |
311 | break; | |
312 | default: | |
313 | // user cancelled | |
314 | break; | |
315 | } | |
316 | ||
317 | notify_remove(myInstance, FALSE); | |
318 | return; | |
319 | } | |
320 | ||
321 | ||
322 | static void | |
323 | notify_add(MyType *myInstance) | |
324 | { | |
325 | CFBundleRef bundle; | |
326 | CFMutableDictionaryRef dict = NULL; | |
327 | SInt32 error = 0; | |
328 | CFIndex i; | |
329 | CFIndex n = CFArrayGetCount(myInstance->interfaces_prompt); | |
330 | CFURLRef url = NULL; | |
331 | ||
332 | if (myInstance->userNotification != NULL) { | |
333 | CFMutableArrayRef save = NULL; | |
334 | ||
335 | if (n > 0) { | |
336 | CFRetain(myInstance->interfaces_prompt); | |
337 | save = myInstance->interfaces_prompt; | |
338 | } | |
339 | notify_remove(myInstance, TRUE); | |
340 | myInstance->interfaces_prompt = save; | |
341 | if (n == 0) { | |
342 | return; | |
343 | } | |
344 | } | |
345 | ||
346 | dict = CFDictionaryCreateMutable(NULL, | |
347 | 0, | |
348 | &kCFTypeDictionaryKeyCallBacks, | |
349 | &kCFTypeDictionaryValueCallBacks); | |
350 | ||
351 | // set localization URL | |
352 | bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); | |
353 | if (bundle != NULL) { | |
354 | url = CFBundleCopyBundleURL(bundle); | |
355 | } | |
356 | #ifdef MAIN | |
357 | if (url == NULL) { | |
358 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
359 | (const UInt8 *)"/System/Library/UserEventPlugins/SCMonitor.plugin", | |
360 | strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"), | |
361 | FALSE); | |
362 | if (bundle == NULL) { | |
363 | bundle = CFBundleCreate(NULL, url); | |
364 | } | |
365 | } | |
366 | #endif // MAIN | |
367 | if (url != NULL) { | |
368 | // set URL | |
369 | CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url); | |
370 | CFRelease(url); | |
371 | } else { | |
372 | SC_log(LOG_ERR, "can't find bundle"); | |
373 | goto done; | |
374 | } | |
375 | ||
376 | // set icon URL | |
377 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
378 | (const UInt8 *)MY_ICON_PATH, | |
379 | strlen(MY_ICON_PATH), | |
380 | FALSE); | |
381 | if (url != NULL) { | |
382 | CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url); | |
383 | CFRelease(url); | |
384 | } | |
385 | ||
386 | // header | |
387 | CFDictionarySetValue(dict, | |
388 | kCFUserNotificationAlertHeaderKey, | |
389 | (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N")); | |
390 | ||
391 | // message | |
392 | if (n == 1) { | |
393 | CFStringRef format; | |
394 | SCNetworkInterfaceRef interface; | |
395 | CFStringRef message; | |
396 | CFStringRef name; | |
397 | ||
398 | #define MESSAGE_1 "The “%@” network interface has not been set up. To set up this interface, use Network Preferences." | |
399 | ||
400 | format = CFBundleCopyLocalizedString(bundle, | |
401 | CFSTR("MESSAGE_1"), | |
402 | CFSTR(MESSAGE_1), | |
403 | NULL); | |
404 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, 0); | |
405 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); | |
406 | message = CFStringCreateWithFormat(NULL, NULL, format, name); | |
407 | CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); | |
408 | CFRelease(message); | |
409 | CFRelease(format); | |
410 | } else { | |
411 | CFMutableArrayRef message; | |
412 | ||
413 | message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
414 | CFArrayAppendValue(message, CFSTR("MESSAGE_SN")); | |
415 | for (i = 0; i < n; i++) { | |
416 | SCNetworkInterfaceRef interface; | |
417 | CFStringRef name; | |
418 | CFStringRef str; | |
419 | ||
420 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, i); | |
421 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); | |
422 | str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\n\t%@"), name); | |
423 | CFArrayAppendValue(message, str); | |
424 | CFRelease(str); | |
425 | } | |
426 | CFArrayAppendValue(message, CFSTR("MESSAGE_EN")); | |
427 | CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); | |
428 | CFRelease(message); | |
429 | } | |
430 | ||
431 | // button titles | |
432 | CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP")); | |
433 | CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL")); | |
434 | ||
435 | // create and post notification | |
436 | myInstance->userNotification = CFUserNotificationCreate(NULL, | |
437 | 0, | |
438 | kCFUserNotificationNoteAlertLevel, | |
439 | &error, | |
440 | dict); | |
441 | if (myInstance->userNotification == NULL) { | |
442 | SC_log(LOG_ERR, "CFUserNotificationCreate() failed: %d", (int)error); | |
443 | goto done; | |
444 | } | |
445 | ||
446 | // establish callback | |
447 | myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL, | |
448 | myInstance->userNotification, | |
449 | notify_reply, | |
450 | 0); | |
451 | if (myInstance->userRls == NULL) { | |
452 | SC_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed"); | |
453 | CFRelease(myInstance->userNotification); | |
454 | myInstance->userNotification = NULL; | |
455 | goto done; | |
456 | } | |
457 | CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode); | |
458 | ||
459 | // add instance for notification | |
460 | if (notify_to_instance == NULL) { | |
461 | notify_to_instance = CFDictionaryCreateMutable(NULL, | |
462 | 0, | |
463 | &kCFTypeDictionaryKeyCallBacks, | |
464 | NULL); // no retain/release/... for values | |
465 | } | |
466 | CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance); | |
467 | ||
468 | done : | |
469 | ||
470 | if (dict != NULL) CFRelease(dict); | |
471 | return; | |
472 | } | |
473 | ||
474 | ||
475 | static void | |
476 | notify_configure(MyType *myInstance) | |
477 | { | |
478 | CFIndex i; | |
479 | Boolean locked; | |
480 | CFIndex n = CFArrayGetCount(myInstance->interfaces_configure); | |
481 | Boolean ok; | |
482 | SCPreferencesRef prefs = NULL; | |
483 | SCNetworkSetRef set = NULL; | |
484 | ||
485 | if (geteuid() == 0) { | |
486 | prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); | |
487 | } else { | |
488 | AuthorizationRef authorization; | |
489 | ||
490 | authorization = getAuthorization(myInstance); | |
491 | if (authorization == NULL) { | |
492 | return; | |
493 | } | |
494 | ||
495 | prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization); | |
496 | } | |
497 | ||
498 | locked = SCPreferencesLock(prefs, TRUE); | |
499 | if (!locked) { | |
500 | SC_log(LOG_ERR, | |
501 | "SCPreferencesLock() failed: %s", | |
502 | SCErrorString(SCError())); | |
503 | goto done; | |
504 | } | |
505 | ||
506 | set = SCNetworkSetCopyCurrent(prefs); | |
507 | if (set == NULL) { | |
508 | // if no "current" set, create new/default ("Automatic") set | |
509 | set = _SCNetworkSetCreateDefault(prefs); | |
510 | if (set == NULL) { | |
511 | goto done; | |
512 | } | |
513 | SC_log(LOG_DEBUG, "added new \"default\" set"); | |
514 | } | |
515 | ||
516 | for (i = 0; i < n; i++) { | |
517 | SCNetworkInterfaceRef interface; | |
518 | ||
519 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_configure, i); | |
520 | ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); | |
521 | if (ok) { | |
522 | CFStringRef name; | |
523 | ||
524 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); | |
525 | SC_log(LOG_NOTICE, "add/update service for %@", name); | |
526 | } | |
527 | } | |
528 | ||
529 | ok = SCPreferencesCommitChanges(prefs); | |
530 | if (!ok) { | |
531 | SC_log(LOG_ERR, | |
532 | "SCPreferencesCommitChanges() failed: %s", | |
533 | SCErrorString(SCError())); | |
534 | goto done; | |
535 | } | |
536 | ||
537 | ok = SCPreferencesApplyChanges(prefs); | |
538 | if (!ok) { | |
539 | SC_log(LOG_ERR, | |
540 | "SCPreferencesApplyChanges() failed: %s", | |
541 | SCErrorString(SCError())); | |
542 | goto done; | |
543 | } | |
544 | ||
545 | done : | |
546 | ||
547 | if (set != NULL) { | |
548 | CFRelease(set); | |
549 | set = NULL; | |
550 | } | |
551 | ||
552 | if (locked) { | |
553 | SCPreferencesUnlock(prefs); | |
554 | } | |
555 | ||
556 | CFRelease(prefs); | |
557 | prefs = NULL; | |
558 | ||
559 | CFRelease(myInstance->interfaces_configure); | |
560 | myInstance->interfaces_configure = NULL; | |
561 | ||
562 | return; | |
563 | } | |
564 | ||
565 | ||
566 | #pragma mark - | |
567 | ||
568 | static Boolean | |
569 | onConsole() | |
570 | { | |
571 | CFArrayRef console_sessions; | |
572 | Boolean on = FALSE; | |
573 | io_registry_entry_t root; | |
574 | uid_t uid = geteuid(); | |
575 | ||
576 | root = IORegistryGetRootEntry(kIOMasterPortDefault); | |
577 | console_sessions = IORegistryEntryCreateCFProperty(root, | |
578 | CFSTR(kIOConsoleUsersKey), | |
579 | NULL, | |
580 | 0); | |
581 | if (console_sessions != NULL) { | |
582 | CFIndex n; | |
583 | ||
584 | n = isA_CFArray(console_sessions) ? CFArrayGetCount(console_sessions) : 0; | |
585 | for (CFIndex i = 0; i < n; i++) { | |
586 | CFBooleanRef bVal; | |
587 | CFDictionaryRef session; | |
588 | uint64_t sessionUID; | |
589 | CFNumberRef val; | |
590 | ||
591 | session = CFArrayGetValueAtIndex(console_sessions, i); | |
592 | if (!isA_CFDictionary(session)) { | |
593 | // if not dictionary | |
594 | continue; | |
595 | } | |
596 | ||
597 | if (!CFDictionaryGetValueIfPresent(session, | |
598 | CFSTR(kIOConsoleSessionUIDKey), | |
599 | (const void **)&val) || | |
600 | !isA_CFNumber(val) || | |
601 | !CFNumberGetValue(val, kCFNumberSInt64Type, (void *)&sessionUID) || | |
602 | (uid != sessionUID)) { | |
603 | // if not my session | |
604 | continue; | |
605 | } | |
606 | ||
607 | if (CFDictionaryGetValueIfPresent(session, | |
608 | CFSTR(kIOConsoleSessionOnConsoleKey), | |
609 | (const void **)&bVal) && | |
610 | isA_CFBoolean(bVal) && | |
611 | CFBooleanGetValue(bVal)) { | |
612 | // if "on console" session | |
613 | on = TRUE; | |
614 | } | |
615 | ||
616 | break; | |
617 | } | |
618 | ||
619 | CFRelease(console_sessions); | |
620 | } | |
621 | IOObjectRelease(root); | |
622 | ||
623 | return on; | |
624 | } | |
625 | ||
626 | ||
627 | #pragma mark - | |
628 | ||
629 | ||
630 | // configure ONLY IF authorized | |
631 | #define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized") | |
632 | ||
633 | ||
634 | static void | |
635 | updateInterfaceList(MyType *myInstance) | |
636 | { | |
637 | Boolean changed = FALSE; | |
638 | CFIndex i; | |
639 | CFArrayRef interfaces; | |
640 | CFMutableSetRef interfaces_old = NULL; | |
641 | CFIndex n; | |
642 | SCPreferencesRef prefs; | |
643 | SCNetworkSetRef set = NULL; | |
644 | ||
645 | if (!onConsole()) { | |
646 | return; | |
647 | } | |
648 | ||
649 | prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); | |
650 | if (prefs == NULL) { | |
651 | return; | |
652 | } | |
653 | ||
654 | set = SCNetworkSetCopyCurrent(prefs); | |
655 | if (set == NULL) { | |
656 | // if no "current" set, create new/default ("Automatic") set | |
657 | set = _SCNetworkSetCreateDefault(prefs); | |
658 | if (set == NULL) { | |
659 | goto done; | |
660 | } | |
661 | } | |
662 | ||
663 | interfaces_old = CFSetCreateMutableCopy(NULL, 0, myInstance->interfaces_known); | |
664 | ||
665 | interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs); | |
666 | if (interfaces != NULL) { | |
667 | n = CFArrayGetCount(interfaces); | |
668 | for (i = 0; i < n; i++) { | |
669 | SCNetworkInterfaceRef interface; | |
670 | Boolean ok; | |
671 | ||
672 | interface = CFArrayGetValueAtIndex(interfaces, i); | |
673 | ||
674 | // track new vs. old (removed) interfaces | |
675 | CFSetRemoveValue(interfaces_old, interface); | |
676 | if (CFSetContainsValue(myInstance->interfaces_known, interface)) { | |
677 | // if we already know about this interface | |
678 | continue; | |
679 | } | |
680 | CFSetAddValue(myInstance->interfaces_known, interface); | |
681 | changed = TRUE; | |
682 | ||
683 | ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); | |
684 | if (ok) { | |
685 | CFStringRef action; | |
686 | ||
687 | // this is a *new* interface | |
688 | ||
689 | action = _SCNetworkInterfaceGetConfigurationAction(interface); | |
690 | if (action == NULL) { | |
691 | // if no per-interface action, use [global] default | |
692 | action = myInstance->configuration_action; | |
693 | } | |
694 | if ((action == NULL) || | |
695 | (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone) && | |
696 | !CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure))) { | |
697 | if (_SCNetworkInterfaceIsBuiltin(interface)) { | |
698 | // if built-in interface | |
699 | action = kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized; | |
700 | } else { | |
701 | action = kSCNetworkInterfaceConfigurationActionValuePrompt; | |
702 | } | |
703 | } | |
704 | ||
705 | if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) { | |
706 | continue; | |
707 | } else if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure)) { | |
708 | // configure automatically (without user intervention) | |
709 | if (myInstance->interfaces_configure == NULL) { | |
710 | myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
711 | } | |
712 | CFArrayAppendValue(myInstance->interfaces_configure, interface); | |
713 | } else if (hasAuthorization(myInstance)) { | |
714 | // if we already have the "admin" (kSCPreferencesAuthorizationRight_write) | |
715 | // right, configure automatically (without user intervention) | |
716 | if (myInstance->interfaces_configure == NULL) { | |
717 | myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
718 | } | |
719 | CFArrayAppendValue(myInstance->interfaces_configure, interface); | |
720 | } else if (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized)) { | |
721 | // notify user | |
722 | if (myInstance->interfaces_prompt == NULL) { | |
723 | myInstance->interfaces_prompt = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
724 | } | |
725 | CFArrayAppendValue(myInstance->interfaces_prompt, interface); | |
726 | } | |
727 | } | |
728 | } | |
729 | ||
730 | CFRelease(interfaces); | |
731 | } | |
732 | ||
733 | // remove any posted notifications for network interfaces that have been removed | |
734 | n = CFSetGetCount(interfaces_old); | |
735 | if (n > 0) { | |
736 | const void * paths_q[32]; | |
737 | const void ** paths = paths_q; | |
738 | ||
739 | if (n > (CFIndex)(sizeof(paths_q) / sizeof(CFTypeRef))) | |
740 | paths = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
741 | CFSetGetValues(interfaces_old, paths); | |
742 | for (i = 0; i < n; i++) { | |
743 | if (myInstance->interfaces_prompt != NULL) { | |
744 | CFIndex j; | |
745 | ||
746 | j = CFArrayGetCount(myInstance->interfaces_prompt); | |
747 | while (j > 0) { | |
748 | SCNetworkInterfaceRef interface; | |
749 | ||
750 | j--; | |
751 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, j); | |
752 | if (CFEqual(interface, paths[i])) { | |
753 | // if we have previously posted a notification | |
754 | // for this no-longer-present interface | |
755 | CFArrayRemoveValueAtIndex(myInstance->interfaces_prompt, j); | |
756 | changed = TRUE; | |
757 | } | |
758 | } | |
759 | } | |
760 | ||
761 | CFSetRemoveValue(myInstance->interfaces_known, paths[i]); | |
762 | } | |
763 | if (paths != paths_q) CFAllocatorDeallocate(NULL, paths); | |
764 | } | |
765 | ||
766 | done : | |
767 | ||
768 | if (changed) { | |
769 | if (myInstance->interfaces_configure != NULL) { | |
770 | // if we have network services to configure automatically | |
771 | notify_configure(myInstance); | |
772 | } | |
773 | ||
774 | if (myInstance->interfaces_prompt != NULL) { | |
775 | // if we have network services that require user intervention | |
776 | // post notification for new interfaces | |
777 | notify_add(myInstance); | |
778 | } | |
779 | } | |
780 | ||
781 | if (interfaces_old != NULL) CFRelease(interfaces_old); | |
782 | if (set != NULL) CFRelease(set); | |
783 | CFRelease(prefs); | |
784 | return; | |
785 | } | |
786 | ||
787 | ||
788 | #pragma mark - | |
789 | #pragma mark Watch for new [network] interfaces | |
790 | ||
791 | ||
792 | static void | |
793 | update_lan(SCDynamicStoreRef store, CFArrayRef changes, void * arg) | |
794 | { | |
795 | #pragma unused(store) | |
796 | #pragma unused(changes) | |
797 | MyType *myInstance = (MyType *)arg; | |
798 | ||
799 | updateInterfaceList(myInstance); | |
800 | return; | |
801 | } | |
802 | ||
803 | ||
804 | static void | |
805 | watcher_add_lan(MyType *myInstance) | |
806 | { | |
807 | SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL }; | |
808 | CFStringRef key; | |
809 | CFArrayRef keys; | |
810 | SCDynamicStoreRef store; | |
811 | ||
812 | store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context); | |
813 | if (store == NULL) { | |
814 | SC_log(LOG_ERR, | |
815 | "SCDynamicStoreCreate() failed: %s", | |
816 | SCErrorString(SCError())); | |
817 | return; | |
818 | } | |
819 | ||
820 | key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState); | |
821 | ||
822 | // watch for changes to the list of network interfaces | |
823 | keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); | |
824 | SCDynamicStoreSetNotificationKeys(store, keys, NULL); | |
825 | CFRelease(keys); | |
826 | myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); | |
827 | CFRunLoopAddSource(CFRunLoopGetCurrent(), | |
828 | myInstance->monitorRls, | |
829 | kCFRunLoopDefaultMode); | |
830 | ||
831 | // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write) | |
832 | // right. If so, we can automatically configure (without user intervention) any | |
833 | // "new" network interfaces that are present at login (e.g. a USB ethernet | |
834 | // dongle that was plugged in before login). | |
835 | if (!hasAuthorization(myInstance)) { | |
836 | CFDictionaryRef dict; | |
837 | ||
838 | // ... and if we don't have the right then we populate the list of | |
839 | // known interfaces with those already named so that we avoid any | |
840 | // login prompts (that the user might have no choice but to dismiss) | |
841 | dict = SCDynamicStoreCopyValue(store, key); | |
842 | if (dict != NULL) { | |
843 | if (isA_CFDictionary(dict)) { | |
844 | CFIndex i; | |
845 | CFArrayRef interfaces; | |
846 | CFIndex n; | |
847 | ||
848 | interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces); | |
849 | n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0; | |
850 | for (i = 0; i < n; i++) { | |
851 | CFStringRef bsdName; | |
852 | ||
853 | bsdName = CFArrayGetValueAtIndex(interfaces, i); | |
854 | if (isA_CFString(bsdName)) { | |
855 | SCNetworkInterfaceRef interface; | |
856 | ||
857 | interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces); | |
858 | if (interface != NULL) { | |
859 | CFSetAddValue(myInstance->interfaces_known, interface); | |
860 | CFRelease(interface); | |
861 | } | |
862 | } | |
863 | } | |
864 | } | |
865 | ||
866 | CFRelease(dict); | |
867 | } | |
868 | } | |
869 | ||
870 | CFRelease(key); | |
871 | CFRelease(store); | |
872 | return; | |
873 | } | |
874 | ||
875 | ||
876 | static void | |
877 | watcher_remove_lan(MyType *myInstance) | |
878 | { | |
879 | if (myInstance->monitorRls != NULL) { | |
880 | CFRunLoopSourceInvalidate(myInstance->monitorRls); | |
881 | CFRelease(myInstance->monitorRls); | |
882 | myInstance->monitorRls = NULL; | |
883 | } | |
884 | ||
885 | return; | |
886 | } | |
887 | ||
888 | ||
889 | #pragma mark - | |
890 | ||
891 | ||
892 | typedef struct { | |
893 | io_registry_entry_t interface; | |
894 | io_registry_entry_t interface_node; | |
895 | MyType *myInstance; | |
896 | io_object_t notification; | |
897 | } MyNode; | |
898 | ||
899 | ||
900 | static void | |
901 | add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface); | |
902 | ||
903 | ||
904 | static void | |
905 | update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) | |
906 | { | |
907 | #pragma unused(messageArgument) | |
908 | CFIndex i; | |
909 | CFDataRef myData = (CFDataRef)refCon; | |
910 | MyType *myInstance; | |
911 | MyNode *myNode; | |
912 | ||
913 | /* ALIGN: CF aligns to at least >8 bytes */ | |
914 | myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); | |
915 | myInstance = myNode->myInstance; | |
916 | ||
917 | switch (messageType) { | |
918 | case kIOMessageServicePropertyChange : { | |
919 | Boolean initializing = FALSE; | |
920 | SCNetworkInterfaceRef interface; | |
921 | CFTypeRef val; | |
922 | ||
923 | if (myNode->interface == MACH_PORT_NULL) { | |
924 | // if we are not watching the "Initializing" property | |
925 | return; | |
926 | } | |
927 | ||
928 | val = IORegistryEntryCreateCFProperty(service, kSCNetworkInterfaceInitializingKey, NULL, 0); | |
929 | if (val != NULL) { | |
930 | initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val)); | |
931 | CFRelease(val); | |
932 | if (initializing) { | |
933 | // if initialization not complete, keep watching | |
934 | return; | |
935 | } | |
936 | } | |
937 | ||
938 | // node is ready | |
939 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface); | |
940 | if (interface != NULL) { | |
941 | CFRelease(interface); | |
942 | ||
943 | // watch interface (to see when/if it's removed) | |
944 | add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL); | |
945 | } | |
946 | break; | |
947 | } | |
948 | ||
949 | case kIOMessageServiceIsTerminated : | |
950 | break; | |
951 | ||
952 | default : | |
953 | return; | |
954 | } | |
955 | ||
956 | // remove no-longer-needed notification | |
957 | if (myNode->interface != myNode->interface_node) { | |
958 | IOObjectRelease(myNode->interface_node); | |
959 | } | |
960 | if (myNode->interface != MACH_PORT_NULL) { | |
961 | IOObjectRelease(myNode->interface); | |
962 | } | |
963 | IOObjectRelease(myNode->notification); | |
964 | i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes, | |
965 | CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)), | |
966 | myData); | |
967 | if (i != kCFNotFound) { | |
968 | CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i); | |
969 | if (CFArrayGetCount(myInstance->notifyNodes) == 0) { | |
970 | CFRelease(myInstance->notifyNodes); | |
971 | myInstance->notifyNodes = NULL; | |
972 | } | |
973 | } | |
974 | ||
975 | updateInterfaceList(myInstance); | |
976 | return; | |
977 | } | |
978 | ||
979 | ||
980 | static void | |
981 | add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface) | |
982 | { | |
983 | kern_return_t kr; | |
984 | CFMutableDataRef myData; | |
985 | MyNode *myNode; | |
986 | ||
987 | // wait for initialization to complete | |
988 | myData = CFDataCreateMutable(NULL, sizeof(MyNode)); | |
989 | CFDataSetLength(myData, sizeof(MyNode)); | |
990 | ||
991 | /* ALIGN: CF aligns to at least >8 bytes */ | |
992 | myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); | |
993 | ||
994 | memset(myNode, 0, sizeof(MyNode)); | |
995 | myNode->interface = interface; | |
996 | if (myNode->interface != MACH_PORT_NULL) { | |
997 | IOObjectRetain(myNode->interface); | |
998 | } | |
999 | myNode->interface_node = (interface == MACH_PORT_NULL) ? node : interface; | |
1000 | if (myNode->interface != myNode->interface_node) { | |
1001 | IOObjectRetain(myNode->interface_node); | |
1002 | } | |
1003 | myNode->myInstance = myInstance; | |
1004 | myNode->notification = MACH_PORT_NULL; | |
1005 | ||
1006 | kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef | |
1007 | node, // io_service_t | |
1008 | kIOGeneralInterest, // interestType | |
1009 | update_node, // IOServiceInterestCallback | |
1010 | (void *)myData, // refCon | |
1011 | &myNode->notification); // notification | |
1012 | if (kr == KERN_SUCCESS) { | |
1013 | if (myInstance->notifyNodes == NULL) { | |
1014 | myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1015 | } | |
1016 | CFArrayAppendValue(myInstance->notifyNodes, myData); | |
1017 | } else { | |
1018 | SC_log(LOG_ERR, | |
1019 | "add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x", | |
1020 | kr); | |
1021 | } | |
1022 | CFRelease(myData); | |
1023 | } | |
1024 | ||
1025 | ||
1026 | static void | |
1027 | add_init_watcher(MyType *myInstance, io_registry_entry_t interface) | |
1028 | { | |
1029 | kern_return_t kr; | |
1030 | io_registry_entry_t node = interface; | |
1031 | CFTypeRef val = NULL; | |
1032 | ||
1033 | while (node != MACH_PORT_NULL) { | |
1034 | io_registry_entry_t parent; | |
1035 | ||
1036 | val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceHiddenPortKey, NULL, 0); | |
1037 | if (val != NULL) { | |
1038 | CFRelease(val); | |
1039 | val = NULL; | |
1040 | break; | |
1041 | } | |
1042 | ||
1043 | val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceInitializingKey, NULL, 0); | |
1044 | if (val != NULL) { | |
1045 | break; | |
1046 | } | |
1047 | ||
1048 | parent = MACH_PORT_NULL; | |
1049 | kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent); | |
1050 | switch (kr) { | |
1051 | case kIOReturnSuccess : // if we have a parent node | |
1052 | case kIOReturnNoDevice : // if we have hit the root node | |
1053 | break; | |
1054 | default : | |
1055 | SC_log(LOG_ERR, "add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x", kr); | |
1056 | break; | |
1057 | } | |
1058 | if (node != interface) { | |
1059 | IOObjectRelease(node); | |
1060 | } | |
1061 | node = parent; | |
1062 | } | |
1063 | ||
1064 | if (val != NULL) { | |
1065 | if (isA_CFBoolean(val) && CFBooleanGetValue(val)) { | |
1066 | // watch the "Initializing" node | |
1067 | add_node_watcher(myInstance, node, interface); | |
1068 | } | |
1069 | ||
1070 | CFRelease(val); | |
1071 | } | |
1072 | ||
1073 | if ((node != MACH_PORT_NULL) && (node != interface)) { | |
1074 | IOObjectRelease(node); | |
1075 | } | |
1076 | ||
1077 | return; | |
1078 | } | |
1079 | ||
1080 | ||
1081 | static void | |
1082 | update_serial(void *refcon, io_iterator_t iter) | |
1083 | { | |
1084 | MyType *myInstance = (MyType *)refcon; | |
1085 | io_registry_entry_t obj; | |
1086 | ||
1087 | while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) { | |
1088 | SCNetworkInterfaceRef interface; | |
1089 | ||
1090 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj); | |
1091 | if (interface != NULL) { | |
1092 | CFRelease(interface); | |
1093 | ||
1094 | // watch interface (to see when/if it's removed) | |
1095 | add_node_watcher(myInstance, obj, MACH_PORT_NULL); | |
1096 | } else { | |
1097 | // check interface, watch if initializing | |
1098 | add_init_watcher(myInstance, obj); | |
1099 | } | |
1100 | ||
1101 | IOObjectRelease(obj); | |
1102 | } | |
1103 | ||
1104 | return; | |
1105 | } | |
1106 | ||
1107 | ||
1108 | static void | |
1109 | update_serial_nodes(void *refcon, io_iterator_t iter) | |
1110 | { | |
1111 | MyType *myInstance = (MyType *)refcon; | |
1112 | ||
1113 | update_serial(refcon, iter); | |
1114 | updateInterfaceList(myInstance); | |
1115 | } | |
1116 | ||
1117 | ||
1118 | static void | |
1119 | watcher_add_serial(MyType *myInstance) | |
1120 | { | |
1121 | kern_return_t kr; | |
1122 | ||
1123 | myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault); | |
1124 | if (myInstance->notifyPort == NULL) { | |
1125 | SC_log(LOG_ERR, "IONotificationPortCreate failed"); | |
1126 | return; | |
1127 | } | |
1128 | ||
1129 | // watch for the introduction of new network serial devices | |
1130 | kr = IOServiceAddMatchingNotification(myInstance->notifyPort, | |
1131 | kIOFirstMatchNotification, | |
1132 | IOServiceMatching("IOSerialBSDClient"), | |
1133 | &update_serial_nodes, | |
1134 | (void *)myInstance, // refCon | |
1135 | &myInstance->notifyIterator); // notification | |
1136 | if (kr != KERN_SUCCESS) { | |
1137 | SC_log(LOG_ERR, "SCMonitor : IOServiceAddMatchingNotification returned 0x%x", kr); | |
1138 | return; | |
1139 | } | |
1140 | ||
1141 | myInstance->notifyNodes = NULL; | |
1142 | ||
1143 | // Get the current list of matches and arm the notification for | |
1144 | // future interface arrivals. | |
1145 | update_serial((void *)myInstance, myInstance->notifyIterator); | |
1146 | ||
1147 | if (myInstance->notifyNodes != NULL) { | |
1148 | // if we have any serial nodes, check if we already have the | |
1149 | // "admin" (kSCPreferencesAuthorizationRight_write) right. If | |
1150 | // so, we can automatically configure (without user intervention) | |
1151 | // any "new" network interfaces that are present at login (e.g. a | |
1152 | // USB modem that was plugged in before login). | |
1153 | ||
1154 | if (!hasAuthorization(myInstance)) { | |
1155 | CFIndex i; | |
1156 | CFIndex n = CFArrayGetCount(myInstance->notifyNodes); | |
1157 | ||
1158 | // ... and if we don't have the right then we populate the list of | |
1159 | // known interfaces with those already named so that we avoid any | |
1160 | // login prompts (that the user might have no choice but to dismiss) | |
1161 | ||
1162 | for (i = 0; i < n; i++) { | |
1163 | SCNetworkInterfaceRef interface; | |
1164 | CFDataRef myData; | |
1165 | MyNode *myNode; | |
1166 | ||
1167 | myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i); | |
1168 | ||
1169 | /* ALIGN: CF aligns to at least >8 bytes */ | |
1170 | myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); | |
1171 | ||
1172 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface_node); | |
1173 | if (interface != NULL) { | |
1174 | CFSetAddValue(myInstance->interfaces_known, interface); | |
1175 | CFRelease(interface); | |
1176 | } | |
1177 | } | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | // and keep watching | |
1182 | CFRunLoopAddSource(CFRunLoopGetCurrent(), | |
1183 | IONotificationPortGetRunLoopSource(myInstance->notifyPort), | |
1184 | kCFRunLoopDefaultMode); | |
1185 | return; | |
1186 | } | |
1187 | ||
1188 | ||
1189 | static void | |
1190 | watcher_remove_serial(MyType *myInstance) | |
1191 | { | |
1192 | if (myInstance->notifyNodes != NULL) { | |
1193 | CFIndex i; | |
1194 | CFIndex n = CFArrayGetCount(myInstance->notifyNodes); | |
1195 | ||
1196 | for (i = 0; i < n; i++) { | |
1197 | CFDataRef myData; | |
1198 | MyNode *myNode; | |
1199 | ||
1200 | myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i); | |
1201 | ||
1202 | /* ALIGN: CF aligns to at least >8 bytes */ | |
1203 | myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); | |
1204 | ||
1205 | if (myNode->interface != myNode->interface_node) { | |
1206 | IOObjectRelease(myNode->interface_node); | |
1207 | } | |
1208 | if (myNode->interface != MACH_PORT_NULL) { | |
1209 | IOObjectRelease(myNode->interface); | |
1210 | } | |
1211 | IOObjectRelease(myNode->notification); | |
1212 | } | |
1213 | ||
1214 | CFRelease(myInstance->notifyNodes); | |
1215 | myInstance->notifyNodes = NULL; | |
1216 | } | |
1217 | ||
1218 | if (myInstance->notifyIterator != MACH_PORT_NULL) { | |
1219 | IOObjectRelease(myInstance->notifyIterator); | |
1220 | myInstance->notifyIterator = MACH_PORT_NULL; | |
1221 | } | |
1222 | ||
1223 | if (myInstance->notifyPort != MACH_PORT_NULL) { | |
1224 | IONotificationPortDestroy(myInstance->notifyPort); | |
1225 | myInstance->notifyPort = NULL; | |
1226 | } | |
1227 | ||
1228 | return; | |
1229 | } | |
1230 | ||
1231 | ||
1232 | #pragma mark - | |
1233 | ||
1234 | ||
1235 | static void | |
1236 | watcher_add(MyType *myInstance) | |
1237 | { | |
1238 | CFBundleRef bundle; | |
1239 | ||
1240 | bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); | |
1241 | if (bundle != NULL) { | |
1242 | CFStringRef action; | |
1243 | CFBooleanRef bVal; | |
1244 | CFDictionaryRef info; | |
1245 | ||
1246 | info = CFBundleGetInfoDictionary(bundle); | |
1247 | ||
1248 | bVal = CFDictionaryGetValue(info, CFSTR("Debug")); | |
1249 | bVal = isA_CFBoolean(bVal); | |
1250 | if (bVal != NULL) { | |
1251 | myInstance->debug = CFBooleanGetValue(bVal); | |
1252 | } | |
1253 | ||
1254 | action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey); | |
1255 | action = isA_CFString(action); | |
1256 | if (action != NULL) { | |
1257 | myInstance->configuration_action = action; | |
1258 | } else { | |
1259 | CFBooleanRef user_intervention; | |
1260 | ||
1261 | user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention")); | |
1262 | if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) { | |
1263 | myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure; | |
1264 | } | |
1265 | } | |
1266 | } | |
1267 | ||
1268 | // initialize the list of known interfaces | |
1269 | myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); | |
1270 | ||
1271 | // add LAN interfaces | |
1272 | watcher_add_lan(myInstance); | |
1273 | ||
1274 | // add SERIAL interfaces | |
1275 | watcher_add_serial(myInstance); | |
1276 | ||
1277 | // auto-configure (as needed) | |
1278 | updateInterfaceList(myInstance); | |
1279 | ||
1280 | return; | |
1281 | } | |
1282 | ||
1283 | ||
1284 | static void | |
1285 | watcher_remove(MyType *myInstance) | |
1286 | { | |
1287 | watcher_remove_lan(myInstance); | |
1288 | watcher_remove_serial(myInstance); | |
1289 | ||
1290 | if (myInstance->interfaces_known != NULL) { | |
1291 | CFRelease(myInstance->interfaces_known); | |
1292 | myInstance->interfaces_known = NULL; | |
1293 | } | |
1294 | ||
1295 | return; | |
1296 | } | |
1297 | ||
1298 | ||
1299 | #pragma mark - | |
1300 | #pragma mark UserEventAgent stubs | |
1301 | ||
1302 | ||
1303 | static HRESULT | |
1304 | myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) | |
1305 | { | |
1306 | CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); | |
1307 | ||
1308 | // Test the requested ID against the valid interfaces. | |
1309 | if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) { | |
1310 | ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
1311 | *ppv = myInstance; | |
1312 | CFRelease(interfaceID); | |
1313 | return S_OK; | |
1314 | } | |
1315 | ||
1316 | if (CFEqual(interfaceID, IUnknownUUID)) { | |
1317 | ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
1318 | *ppv = myInstance; | |
1319 | CFRelease(interfaceID); | |
1320 | return S_OK; | |
1321 | } | |
1322 | ||
1323 | // Requested interface unknown, bail with error. | |
1324 | *ppv = NULL; | |
1325 | CFRelease(interfaceID); | |
1326 | return E_NOINTERFACE; | |
1327 | } | |
1328 | ||
1329 | ||
1330 | static ULONG | |
1331 | myAddRef(void *myInstance) | |
1332 | { | |
1333 | ((MyType *) myInstance)->_refCount++; | |
1334 | return ((MyType *) myInstance)->_refCount; | |
1335 | } | |
1336 | ||
1337 | ||
1338 | static ULONG | |
1339 | myRelease(void *myInstance) | |
1340 | { | |
1341 | ((MyType *) myInstance)->_refCount--; | |
1342 | if (((MyType *) myInstance)->_refCount == 0) { | |
1343 | CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID; | |
1344 | ||
1345 | if (factoryID != NULL) { | |
1346 | CFPlugInRemoveInstanceForFactory(factoryID); | |
1347 | CFRelease(factoryID); | |
1348 | ||
1349 | watcher_remove((MyType *)myInstance); | |
1350 | notify_remove((MyType *)myInstance, TRUE); | |
1351 | freeAuthorization((MyType *)myInstance); | |
1352 | } | |
1353 | free(myInstance); | |
1354 | return 0; | |
1355 | } | |
1356 | ||
1357 | return ((MyType *) myInstance)->_refCount; | |
1358 | } | |
1359 | ||
1360 | ||
1361 | static void | |
1362 | myInstall(void *myInstance) | |
1363 | { | |
1364 | watcher_add((MyType *)myInstance); | |
1365 | return; | |
1366 | } | |
1367 | ||
1368 | ||
1369 | static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { | |
1370 | NULL, // Required padding for COM | |
1371 | myQueryInterface, // These three are the required COM functions | |
1372 | myAddRef, | |
1373 | myRelease, | |
1374 | myInstall // Interface implementation | |
1375 | }; | |
1376 | ||
1377 | ||
1378 | void * | |
1379 | UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) | |
1380 | { | |
1381 | #pragma unused(allocator) | |
1382 | MyType *newOne = NULL; | |
1383 | ||
1384 | if (CFEqual(typeID, kUserEventAgentTypeID)) { | |
1385 | newOne = (MyType *)malloc(sizeof(MyType)); | |
1386 | memset(newOne, 0, sizeof(*newOne)); | |
1387 | newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; | |
1388 | newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID); | |
1389 | CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID); | |
1390 | newOne->_refCount = 1; | |
1391 | } | |
1392 | ||
1393 | return newOne; | |
1394 | } | |
1395 | ||
1396 | ||
1397 | #ifdef MAIN | |
1398 | int | |
1399 | main(int argc, char **argv) | |
1400 | { | |
1401 | MyType *newOne = (MyType *)malloc(sizeof(MyType)); | |
1402 | ||
1403 | _sc_log = kSCLogDestinationFile; | |
1404 | _sc_verbose = (argc > 1) ? TRUE : FALSE; | |
1405 | ||
1406 | memset(newOne, 0, sizeof(*newOne)); | |
1407 | myInstall(newOne); | |
1408 | CFRunLoopRun(); | |
1409 | exit(0); | |
1410 | return (0); | |
1411 | } | |
1412 | #endif // MAIN |