]>
Commit | Line | Data |
---|---|---|
6d034b4e | 1 | /* |
a40a14f8 | 2 | * Copyright (c) 2007-2009 Apple Inc. All rights reserved. |
6d034b4e A |
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> | |
a40a14f8 | 34 | #include <asl.h> |
6d034b4e A |
35 | #include <CoreFoundation/CoreFoundation.h> |
36 | #include <SystemConfiguration/SystemConfiguration.h> | |
37 | #include <SystemConfiguration/SCPrivate.h> | |
a40a14f8 A |
38 | #include <IOKit/IOKitLib.h> |
39 | #include <IOKit/IOMessage.h> | |
6d034b4e A |
40 | #include <ApplicationServices/ApplicationServices.h> |
41 | #include "UserEventAgentInterface.h" | |
42 | ||
a40a14f8 | 43 | #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor" |
6d034b4e A |
44 | #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns" |
45 | ||
46 | #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane" | |
47 | #define NETWORK_PREF_CMD "New Interface" | |
48 | ||
a40a14f8 A |
49 | /* |
50 | * The following keys/values control the actions taken when a new interface | |
51 | * has been detected and whether we interact with the user. | |
52 | * | |
53 | * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist | |
54 | * file) or per-inteface in the IORegistry. | |
55 | * | |
56 | * For the "New Interface Detected Action" key we define the following [CFString] | |
57 | * values : | |
58 | * | |
59 | * "None" No action, ignore this interface. | |
60 | * "Prompt" Post a "new interface detected" notification to the user. | |
61 | * "Configure" Automatically configure this interface without any | |
62 | * intervention. | |
63 | * | |
64 | * Note: automatic configuration may require authorization if the logged | |
65 | * in user is NOT "root" (eUID==0) or if the "system.preferences" | |
66 | * administrator right is not currently available. | |
67 | * | |
68 | * An [older] "User Intervention" key is also supported. That CFBoolean | |
69 | * key, if present and TRUE, implies "Configure" configuration of the | |
70 | * interface without intervention. | |
71 | */ | |
72 | ||
6d034b4e A |
73 | typedef struct { |
74 | UserEventAgentInterfaceStruct *_UserEventAgentInterface; | |
75 | CFUUIDRef _factoryID; | |
76 | UInt32 _refCount; | |
77 | ||
a40a14f8 A |
78 | aslmsg log_msg; |
79 | ||
80 | CFStringRef configuration_action; | |
be997540 | 81 | |
6d034b4e A |
82 | CFRunLoopSourceRef monitorRls; |
83 | ||
a40a14f8 A |
84 | IONotificationPortRef notifyPort; |
85 | io_iterator_t notifyIterator; | |
86 | CFMutableArrayRef notifyNodes; | |
87 | ||
88 | // interfaces that we already know about | |
89 | CFMutableSetRef interfaces_known; | |
90 | ||
91 | // interfaces that should be auto-configured (no user notification) | |
92 | CFMutableArrayRef interfaces_configure; | |
93 | ||
94 | // interfaces that require user notification | |
95 | CFMutableArrayRef interfaces_prompt; | |
6d034b4e | 96 | |
6d034b4e A |
97 | CFUserNotificationRef userNotification; |
98 | CFRunLoopSourceRef userRls; | |
99 | } MyType; | |
100 | ||
101 | static CFMutableDictionaryRef notify_to_instance = NULL; | |
102 | ||
103 | ||
104 | #pragma mark - | |
a40a14f8 | 105 | #pragma mark New interface notification / configuration |
6d034b4e A |
106 | |
107 | ||
108 | static void | |
a40a14f8 | 109 | open_NetworkPrefPane(MyType *myInstance) |
6d034b4e A |
110 | { |
111 | AEDesc aeDesc = { typeNull, NULL }; | |
112 | CFArrayRef prefArray; | |
113 | CFURLRef prefURL; | |
114 | LSLaunchURLSpec prefSpec; | |
115 | OSStatus status; | |
116 | ||
117 | prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, | |
118 | CFSTR(NETWORK_PREF_APP), | |
119 | kCFURLPOSIXPathStyle, | |
120 | FALSE); | |
121 | prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks); | |
122 | CFRelease(prefURL); | |
123 | ||
124 | status = AECreateDesc('ptru', | |
125 | (const void *)NETWORK_PREF_CMD, | |
126 | strlen(NETWORK_PREF_CMD), | |
127 | &aeDesc); | |
128 | if (status != noErr) { | |
a40a14f8 | 129 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status); |
6d034b4e A |
130 | } |
131 | ||
132 | prefSpec.appURL = NULL; | |
133 | prefSpec.itemURLs = prefArray; | |
134 | prefSpec.passThruParams = &aeDesc; | |
135 | prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents; | |
136 | prefSpec.asyncRefCon = NULL; | |
137 | ||
138 | status = LSOpenFromURLSpec(&prefSpec, NULL); | |
139 | if (status != noErr) { | |
a40a14f8 | 140 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status); |
6d034b4e A |
141 | } |
142 | ||
143 | CFRelease(prefArray); | |
144 | if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc); | |
145 | return; | |
146 | } | |
147 | ||
148 | ||
149 | static void | |
150 | notify_remove(MyType *myInstance, Boolean cancel) | |
151 | { | |
a40a14f8 A |
152 | if (myInstance->interfaces_configure != NULL) { |
153 | CFRelease(myInstance->interfaces_configure); | |
154 | myInstance->interfaces_configure = NULL; | |
155 | } | |
156 | ||
157 | if (myInstance->interfaces_prompt != NULL) { | |
158 | CFRelease(myInstance->interfaces_prompt); | |
159 | myInstance->interfaces_prompt = NULL; | |
6d034b4e A |
160 | } |
161 | ||
162 | if (myInstance->userRls != NULL) { | |
163 | CFRunLoopSourceInvalidate(myInstance->userRls); | |
164 | CFRelease(myInstance->userRls); | |
165 | myInstance->userRls = NULL; | |
166 | } | |
167 | ||
168 | if (myInstance->userNotification != NULL) { | |
169 | if (cancel) { | |
170 | SInt32 status; | |
171 | ||
172 | status = CFUserNotificationCancel(myInstance->userNotification); | |
173 | if (status != 0) { | |
a40a14f8 | 174 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, |
6d034b4e A |
175 | CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"), |
176 | status); | |
177 | } | |
178 | } | |
179 | CFRelease(myInstance->userNotification); | |
180 | myInstance->userNotification = NULL; | |
181 | } | |
182 | ||
183 | return; | |
184 | } | |
185 | ||
186 | ||
187 | static void | |
188 | notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags) | |
189 | { | |
190 | MyType *myInstance = NULL; | |
191 | ||
192 | // get instance for notification | |
193 | if (notify_to_instance != NULL) { | |
194 | myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification); | |
195 | if (myInstance != NULL) { | |
196 | CFDictionaryRemoveValue(notify_to_instance, userNotification); | |
197 | if (CFDictionaryGetCount(notify_to_instance) == 0) { | |
198 | CFRelease(notify_to_instance); | |
199 | notify_to_instance = NULL; | |
200 | } | |
201 | } | |
202 | } | |
203 | if (myInstance == NULL) { | |
a40a14f8 | 204 | SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find user notification")); |
6d034b4e A |
205 | return; |
206 | } | |
207 | ||
208 | // process response | |
209 | switch (response_flags & 0x3) { | |
210 | case kCFUserNotificationDefaultResponse: | |
211 | // user asked to configure interface | |
a40a14f8 | 212 | open_NetworkPrefPane(myInstance); |
6d034b4e A |
213 | break; |
214 | default: | |
215 | // user cancelled | |
216 | break; | |
217 | } | |
218 | ||
219 | notify_remove(myInstance, FALSE); | |
220 | return; | |
221 | } | |
222 | ||
be997540 | 223 | |
6d034b4e A |
224 | static void |
225 | notify_add(MyType *myInstance) | |
226 | { | |
227 | CFBundleRef bundle; | |
228 | CFMutableDictionaryRef dict = NULL; | |
229 | SInt32 error = 0; | |
230 | CFIndex i; | |
a40a14f8 | 231 | CFIndex n = CFArrayGetCount(myInstance->interfaces_prompt); |
6d034b4e A |
232 | CFURLRef url = NULL; |
233 | ||
234 | if (myInstance->userNotification != NULL) { | |
235 | CFMutableArrayRef save = NULL; | |
236 | ||
237 | if (n > 0) { | |
a40a14f8 A |
238 | CFRetain(myInstance->interfaces_prompt); |
239 | save = myInstance->interfaces_prompt; | |
6d034b4e A |
240 | } |
241 | notify_remove(myInstance, TRUE); | |
a40a14f8 | 242 | myInstance->interfaces_prompt = save; |
6d034b4e A |
243 | if (n == 0) { |
244 | return; | |
245 | } | |
246 | } | |
247 | ||
248 | dict = CFDictionaryCreateMutable(NULL, | |
249 | 0, | |
250 | &kCFTypeDictionaryKeyCallBacks, | |
251 | &kCFTypeDictionaryValueCallBacks); | |
252 | ||
253 | // set localization URL | |
a40a14f8 | 254 | bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); |
6d034b4e A |
255 | if (bundle != NULL) { |
256 | url = CFBundleCopyBundleURL(bundle); | |
257 | } | |
a40a14f8 A |
258 | #ifdef MAIN |
259 | if (url == NULL) { | |
260 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
261 | (const UInt8 *)"/System/Library/UserEventPlugins/SCMonitor.plugin", | |
262 | strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"), | |
263 | FALSE); | |
264 | if (bundle == NULL) { | |
265 | bundle = CFBundleCreate(NULL, url); | |
266 | } | |
267 | } | |
268 | #endif // MAIN | |
6d034b4e A |
269 | if (url != NULL) { |
270 | // set URL | |
271 | CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url); | |
272 | CFRelease(url); | |
273 | } else { | |
a40a14f8 | 274 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find bundle")); |
6d034b4e A |
275 | goto done; |
276 | } | |
277 | ||
278 | // set icon URL | |
279 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
280 | (const UInt8 *)MY_ICON_PATH, | |
281 | strlen(MY_ICON_PATH), | |
282 | FALSE); | |
283 | if (url != NULL) { | |
284 | CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url); | |
285 | CFRelease(url); | |
286 | } | |
287 | ||
288 | // header | |
289 | CFDictionarySetValue(dict, | |
290 | kCFUserNotificationAlertHeaderKey, | |
291 | (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N")); | |
292 | ||
293 | // message | |
a40a14f8 A |
294 | if (n == 1) { |
295 | CFStringRef format; | |
6d034b4e | 296 | SCNetworkInterfaceRef interface; |
a40a14f8 | 297 | CFStringRef message; |
6d034b4e A |
298 | CFStringRef name; |
299 | ||
a40a14f8 A |
300 | #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences." |
301 | ||
302 | format = CFBundleCopyLocalizedString(bundle, | |
303 | CFSTR("MESSAGE_1"), | |
304 | CFSTR(MESSAGE_1), | |
305 | NULL); | |
306 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, 0); | |
6d034b4e | 307 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); |
a40a14f8 A |
308 | message = CFStringCreateWithFormat(NULL, NULL, format, name); |
309 | CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); | |
310 | CFRelease(message); | |
311 | CFRelease(format); | |
312 | } else { | |
313 | CFMutableArrayRef message; | |
314 | ||
315 | message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
316 | CFArrayAppendValue(message, CFSTR("MESSAGE_SN")); | |
317 | for (i = 0; i < n; i++) { | |
318 | SCNetworkInterfaceRef interface; | |
319 | CFStringRef name; | |
320 | CFStringRef str; | |
6d034b4e | 321 | |
a40a14f8 A |
322 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, i); |
323 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); | |
6d034b4e A |
324 | str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name); |
325 | CFArrayAppendValue(message, str); | |
326 | CFRelease(str); | |
327 | } | |
a40a14f8 A |
328 | CFArrayAppendValue(message, CFSTR("MESSAGE_EN")); |
329 | CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); | |
330 | CFRelease(message); | |
6d034b4e | 331 | } |
6d034b4e A |
332 | |
333 | // button titles | |
334 | CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP")); | |
335 | CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL")); | |
336 | ||
337 | // create and post notification | |
338 | myInstance->userNotification = CFUserNotificationCreate(NULL, | |
339 | 0, | |
340 | kCFUserNotificationNoteAlertLevel, | |
341 | &error, | |
342 | dict); | |
343 | if (myInstance->userNotification == NULL) { | |
a40a14f8 | 344 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error); |
6d034b4e A |
345 | goto done; |
346 | } | |
347 | ||
348 | // establish callback | |
349 | myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL, | |
350 | myInstance->userNotification, | |
351 | notify_reply, | |
352 | 0); | |
353 | if (myInstance->userRls == NULL) { | |
a40a14f8 | 354 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed")); |
6d034b4e A |
355 | CFRelease(myInstance->userNotification); |
356 | myInstance->userNotification = NULL; | |
357 | goto done; | |
358 | } | |
359 | CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode); | |
360 | ||
361 | // add instance for notification | |
362 | if (notify_to_instance == NULL) { | |
363 | notify_to_instance = CFDictionaryCreateMutable(NULL, | |
364 | 0, | |
365 | &kCFTypeDictionaryKeyCallBacks, | |
366 | NULL); // no retain/release/... for values | |
367 | } | |
368 | CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance); | |
369 | ||
370 | done : | |
371 | ||
372 | if (dict != NULL) CFRelease(dict); | |
373 | return; | |
374 | } | |
375 | ||
376 | ||
be997540 A |
377 | static void |
378 | notify_configure(MyType *myInstance) | |
379 | { | |
380 | AuthorizationRef authorization = NULL; | |
381 | CFIndex i; | |
a40a14f8 | 382 | CFIndex n = CFArrayGetCount(myInstance->interfaces_configure); |
be997540 A |
383 | Boolean ok; |
384 | SCPreferencesRef prefs = NULL; | |
385 | SCNetworkSetRef set = NULL; | |
386 | ||
387 | if (geteuid() == 0) { | |
388 | prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); | |
389 | } else { | |
390 | AuthorizationFlags flags = kAuthorizationFlagDefaults; | |
391 | OSStatus status; | |
392 | ||
393 | status = AuthorizationCreate(NULL, | |
394 | kAuthorizationEmptyEnvironment, | |
395 | flags, | |
396 | &authorization); | |
397 | if (status != errAuthorizationSuccess) { | |
a40a14f8 A |
398 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, |
399 | CFSTR("AuthorizationCreate() failed: status = %d"), | |
be997540 A |
400 | status); |
401 | return; | |
402 | } | |
403 | ||
404 | prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization); | |
405 | } | |
406 | ||
407 | set = SCNetworkSetCopyCurrent(prefs); | |
408 | if (set == NULL) { | |
409 | set = SCNetworkSetCreate(prefs); | |
410 | if (set == NULL) { | |
411 | goto done; | |
412 | } | |
413 | } | |
414 | ||
be997540 A |
415 | for (i = 0; i < n; i++) { |
416 | SCNetworkInterfaceRef interface; | |
a40a14f8 A |
417 | |
418 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_configure, i); | |
be997540 A |
419 | ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); |
420 | if (ok) { | |
421 | CFStringRef name; | |
422 | ||
423 | name = SCNetworkInterfaceGetLocalizedDisplayName(interface); | |
a40a14f8 | 424 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_NOTICE, CFSTR("add service for %@"), name); |
be997540 A |
425 | } |
426 | } | |
a40a14f8 | 427 | |
be997540 A |
428 | ok = SCPreferencesCommitChanges(prefs); |
429 | if (!ok) { | |
a40a14f8 A |
430 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, |
431 | CFSTR("SCPreferencesCommitChanges() failed: %s"), | |
be997540 | 432 | SCErrorString(SCError())); |
a40a14f8 A |
433 | goto done; |
434 | } | |
435 | ||
436 | ok = SCPreferencesApplyChanges(prefs); | |
be997540 | 437 | if (!ok) { |
a40a14f8 A |
438 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, |
439 | CFSTR("SCPreferencesApplyChanges() failed: %s"), | |
be997540 | 440 | SCErrorString(SCError())); |
a40a14f8 A |
441 | goto done; |
442 | } | |
be997540 A |
443 | |
444 | done : | |
a40a14f8 | 445 | |
be997540 A |
446 | if (set != NULL) { |
447 | CFRelease(set); | |
448 | set = NULL; | |
449 | } | |
450 | ||
451 | if (prefs != NULL) { | |
452 | CFRelease(prefs); | |
453 | prefs = NULL; | |
454 | } | |
a40a14f8 A |
455 | |
456 | if (authorization != NULL) { | |
457 | AuthorizationFree(authorization, kAuthorizationFlagDefaults); | |
458 | // AuthorizationFree(authorization, kAuthorizationFlagDestroyRights); | |
459 | authorization = NULL; | |
460 | } | |
461 | ||
462 | CFRelease(myInstance->interfaces_configure); | |
463 | myInstance->interfaces_configure = NULL; | |
464 | ||
be997540 A |
465 | return; |
466 | } | |
467 | ||
468 | ||
a40a14f8 A |
469 | #pragma mark - |
470 | ||
471 | ||
6d034b4e | 472 | static void |
a40a14f8 | 473 | updateInterfaceList(MyType *myInstance) |
6d034b4e | 474 | { |
a40a14f8 | 475 | Boolean changed = FALSE; |
6d034b4e A |
476 | CFIndex i; |
477 | CFArrayRef interfaces; | |
a40a14f8 | 478 | CFMutableSetRef interfaces_old = NULL; |
6d034b4e A |
479 | CFIndex n; |
480 | SCPreferencesRef prefs; | |
6d034b4e A |
481 | SCNetworkSetRef set = NULL; |
482 | ||
483 | prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); | |
484 | if (prefs == NULL) { | |
485 | return; | |
486 | } | |
487 | ||
488 | set = SCNetworkSetCopyCurrent(prefs); | |
489 | if (set == NULL) { | |
490 | set = SCNetworkSetCreate(prefs); | |
491 | if (set == NULL) { | |
492 | goto done; | |
493 | } | |
494 | } | |
495 | ||
a40a14f8 | 496 | interfaces_old = CFSetCreateMutableCopy(NULL, 0, myInstance->interfaces_known); |
6d034b4e A |
497 | |
498 | interfaces = SCNetworkInterfaceCopyAll(); | |
499 | if (interfaces != NULL) { | |
6d034b4e A |
500 | n = CFArrayGetCount(interfaces); |
501 | for (i = 0; i < n; i++) { | |
6d034b4e A |
502 | SCNetworkInterfaceRef interface; |
503 | Boolean ok; | |
504 | ||
505 | interface = CFArrayGetValueAtIndex(interfaces, i); | |
a40a14f8 A |
506 | |
507 | if (_SCNetworkInterfaceIsBuiltin(interface)) { | |
508 | // skip built-in interfaces | |
6d034b4e A |
509 | continue; |
510 | } | |
511 | ||
a40a14f8 A |
512 | // track new vs. old (removed) interfaces |
513 | CFSetRemoveValue(interfaces_old, interface); | |
514 | if (CFSetContainsValue(myInstance->interfaces_known, interface)) { | |
515 | // if we already know about this interface | |
6d034b4e A |
516 | continue; |
517 | } | |
a40a14f8 A |
518 | CFSetAddValue(myInstance->interfaces_known, interface); |
519 | changed = TRUE; | |
6d034b4e A |
520 | |
521 | ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); | |
522 | if (ok) { | |
a40a14f8 A |
523 | CFStringRef action; |
524 | ||
6d034b4e | 525 | // this is a *new* interface |
a40a14f8 A |
526 | |
527 | action = _SCNetworkInterfaceGetConfigurationAction(interface); | |
528 | if (action == NULL) { | |
529 | // if no per-interface action, use [global] default | |
530 | action = myInstance->configuration_action; | |
531 | } | |
532 | if ((action == NULL) || | |
533 | (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone) && | |
534 | !CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure))) { | |
535 | action = kSCNetworkInterfaceConfigurationActionValuePrompt; | |
536 | } | |
537 | ||
538 | if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) { | |
539 | continue; | |
540 | } else if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure)) { | |
541 | // configure automatically (without user intervention) | |
542 | if (myInstance->interfaces_configure == NULL) { | |
543 | myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
544 | } | |
545 | CFArrayAppendValue(myInstance->interfaces_configure, interface); | |
546 | } else { | |
547 | // notify user | |
548 | if (myInstance->interfaces_prompt == NULL) { | |
549 | myInstance->interfaces_prompt = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
550 | } | |
551 | CFArrayAppendValue(myInstance->interfaces_prompt, interface); | |
6d034b4e | 552 | } |
6d034b4e A |
553 | } |
554 | } | |
555 | ||
556 | CFRelease(interfaces); | |
557 | } | |
558 | ||
a40a14f8 A |
559 | // remove any posted notifications for network interfaces that have been removed |
560 | n = CFSetGetCount(interfaces_old); | |
6d034b4e | 561 | if (n > 0) { |
a40a14f8 A |
562 | const void * paths_q[32]; |
563 | const void ** paths = paths_q; | |
6d034b4e | 564 | |
a40a14f8 A |
565 | if (n > (CFIndex)(sizeof(paths_q) / sizeof(CFTypeRef))) |
566 | paths = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
567 | CFSetGetValues(interfaces_old, paths); | |
6d034b4e | 568 | for (i = 0; i < n; i++) { |
a40a14f8 | 569 | if (myInstance->interfaces_prompt != NULL) { |
6d034b4e A |
570 | CFIndex j; |
571 | ||
a40a14f8 A |
572 | j = CFArrayGetCount(myInstance->interfaces_prompt); |
573 | while (j > 0) { | |
6d034b4e A |
574 | SCNetworkInterfaceRef interface; |
575 | ||
a40a14f8 A |
576 | j--; |
577 | interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, j); | |
578 | if (CFEqual(interface, paths[i])) { | |
6d034b4e A |
579 | // if we have previously posted a notification |
580 | // for this no-longer-present interface | |
a40a14f8 A |
581 | CFArrayRemoveValueAtIndex(myInstance->interfaces_prompt, j); |
582 | changed = TRUE; | |
6d034b4e A |
583 | } |
584 | } | |
585 | } | |
586 | ||
a40a14f8 | 587 | CFSetRemoveValue(myInstance->interfaces_known, paths[i]); |
6d034b4e | 588 | } |
a40a14f8 | 589 | if (paths != paths_q) CFAllocatorDeallocate(NULL, paths); |
6d034b4e A |
590 | } |
591 | ||
592 | done : | |
593 | ||
a40a14f8 A |
594 | if (changed) { |
595 | if (myInstance->interfaces_configure != NULL) { | |
596 | // if we have network services to configure automatically | |
be997540 | 597 | notify_configure(myInstance); |
a40a14f8 A |
598 | } |
599 | ||
600 | if (myInstance->interfaces_prompt != NULL) { | |
601 | // if we have network services that require user intervention | |
602 | // post notification for new interfaces | |
be997540 A |
603 | notify_add(myInstance); |
604 | } | |
6d034b4e A |
605 | } |
606 | ||
a40a14f8 | 607 | if (interfaces_old != NULL) CFRelease(interfaces_old); |
6d034b4e A |
608 | if (set != NULL) CFRelease(set); |
609 | CFRelease(prefs); | |
610 | return; | |
611 | } | |
612 | ||
613 | ||
a40a14f8 A |
614 | #pragma mark - |
615 | #pragma mark Watch for new [network] interfaces | |
616 | ||
617 | ||
6d034b4e | 618 | static void |
a40a14f8 | 619 | update_lan(SCDynamicStoreRef store, CFArrayRef changes, void * arg) |
6d034b4e | 620 | { |
a40a14f8 | 621 | MyType *myInstance = (MyType *)arg; |
6d034b4e | 622 | |
a40a14f8 | 623 | updateInterfaceList(myInstance); |
6d034b4e A |
624 | return; |
625 | } | |
626 | ||
627 | ||
628 | static void | |
a40a14f8 | 629 | watcher_add_lan(MyType *myInstance) |
6d034b4e A |
630 | { |
631 | SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL }; | |
632 | CFDictionaryRef dict; | |
633 | CFStringRef key; | |
634 | CFArrayRef keys; | |
635 | SCDynamicStoreRef store; | |
636 | ||
a40a14f8 | 637 | store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context); |
6d034b4e | 638 | if (store == NULL) { |
a40a14f8 | 639 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, |
6d034b4e A |
640 | CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"), |
641 | SCErrorString(SCError())); | |
642 | return; | |
643 | } | |
644 | ||
645 | key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState); | |
646 | ||
647 | // watch for changes to the list of network interfaces | |
648 | keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); | |
649 | SCDynamicStoreSetNotificationKeys(store, NULL, keys); | |
650 | CFRelease(keys); | |
651 | myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); | |
652 | CFRunLoopAddSource(CFRunLoopGetCurrent(), | |
653 | myInstance->monitorRls, | |
654 | kCFRunLoopDefaultMode); | |
655 | ||
656 | // initialize the list of known interfaces | |
a40a14f8 | 657 | myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); |
6d034b4e A |
658 | dict = SCDynamicStoreCopyValue(store, key); |
659 | if (dict != NULL) { | |
660 | if (isA_CFDictionary(dict)) { | |
661 | CFIndex i; | |
662 | CFArrayRef interfaces; | |
663 | CFIndex n; | |
664 | ||
665 | interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces); | |
666 | n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0; | |
667 | for (i = 0; i < n; i++) { | |
668 | CFStringRef bsdName; | |
669 | ||
670 | bsdName = CFArrayGetValueAtIndex(interfaces, i); | |
671 | if (isA_CFString(bsdName)) { | |
a40a14f8 A |
672 | SCNetworkInterfaceRef interface; |
673 | ||
674 | interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces); | |
675 | if (interface != NULL) { | |
676 | CFSetAddValue(myInstance->interfaces_known, interface); | |
677 | CFRelease(interface); | |
678 | } | |
6d034b4e A |
679 | } |
680 | } | |
681 | } | |
682 | ||
683 | CFRelease(dict); | |
684 | } | |
685 | ||
686 | CFRelease(key); | |
687 | CFRelease(store); | |
688 | return; | |
689 | } | |
690 | ||
691 | ||
a40a14f8 A |
692 | static void |
693 | watcher_remove_lan(MyType *myInstance) | |
694 | { | |
695 | if (myInstance->monitorRls != NULL) { | |
696 | CFRunLoopSourceInvalidate(myInstance->monitorRls); | |
697 | CFRelease(myInstance->monitorRls); | |
698 | myInstance->monitorRls = NULL; | |
699 | } | |
700 | ||
701 | if (myInstance->interfaces_known != NULL) { | |
702 | CFRelease(myInstance->interfaces_known); | |
703 | myInstance->interfaces_known = NULL; | |
704 | } | |
705 | ||
706 | return; | |
707 | } | |
708 | ||
709 | ||
710 | #pragma mark - | |
711 | ||
712 | ||
713 | typedef struct { | |
714 | io_registry_entry_t interface; | |
715 | MyType *myInstance; | |
716 | io_object_t notification; | |
717 | } MyNode; | |
718 | ||
719 | ||
720 | static void | |
721 | add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface); | |
722 | ||
723 | ||
724 | static void | |
725 | update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) | |
726 | { | |
727 | CFIndex i; | |
728 | CFDataRef myData = (CFDataRef)refCon; | |
729 | MyType *myInstance; | |
730 | MyNode *myNode; | |
731 | ||
732 | myNode = (MyNode *)CFDataGetBytePtr(myData); | |
733 | myInstance = myNode->myInstance; | |
734 | ||
735 | switch (messageType) { | |
736 | case kIOMessageServicePropertyChange : { | |
737 | Boolean initializing = FALSE; | |
738 | SCNetworkInterfaceRef interface; | |
739 | CFTypeRef val; | |
740 | ||
741 | if (myNode->interface == MACH_PORT_NULL) { | |
742 | // if we are not watching the "Initializing" property | |
743 | return; | |
744 | } | |
745 | ||
746 | val = IORegistryEntryCreateCFProperty(service, CFSTR("Initializing"), NULL, 0); | |
747 | if (val != NULL) { | |
748 | initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val)); | |
749 | CFRelease(val); | |
750 | if (initializing) { | |
751 | // if initialization not complete, keep watching | |
752 | return; | |
753 | } | |
754 | } | |
755 | ||
756 | // node is ready | |
757 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface); | |
758 | if (interface != NULL) { | |
759 | CFRelease(interface); | |
760 | ||
761 | // watch interface (to see when/if it's removed) | |
762 | add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL); | |
763 | } | |
764 | break; | |
765 | } | |
766 | ||
767 | case kIOMessageServiceIsTerminated : | |
768 | break; | |
769 | ||
770 | default : | |
771 | return; | |
772 | } | |
773 | ||
774 | // remove no-longer-needed notification | |
775 | if (myNode->interface != MACH_PORT_NULL) { | |
776 | IOObjectRelease(myNode->interface); | |
777 | myNode->interface = MACH_PORT_NULL; | |
778 | } | |
779 | IOObjectRelease(myNode->notification); | |
780 | i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes, | |
781 | CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)), | |
782 | myData); | |
783 | if (i != kCFNotFound) { | |
784 | CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i); | |
785 | if (CFArrayGetCount(myInstance->notifyNodes) == 0) { | |
786 | CFRelease(myInstance->notifyNodes); | |
787 | myInstance->notifyNodes = NULL; | |
788 | } | |
789 | } | |
790 | ||
791 | updateInterfaceList(myInstance); | |
792 | return; | |
793 | } | |
794 | ||
795 | ||
796 | static void | |
797 | add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface) | |
798 | { | |
799 | kern_return_t kr; | |
800 | CFMutableDataRef myData; | |
801 | MyNode *myNode; | |
802 | ||
803 | // wait for initialization to complete | |
804 | myData = CFDataCreateMutable(NULL, sizeof(MyNode)); | |
805 | CFDataSetLength(myData, sizeof(MyNode)); | |
806 | myNode = (MyNode *)CFDataGetBytePtr(myData); | |
807 | bzero(myNode, sizeof(MyNode)); | |
808 | if (interface != MACH_PORT_NULL) { | |
809 | IOObjectRetain(interface); | |
810 | } | |
811 | myNode->interface = interface; | |
812 | myNode->myInstance = myInstance; | |
813 | myNode->notification = MACH_PORT_NULL; | |
814 | ||
815 | kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef | |
816 | node, // io_service_t | |
817 | kIOGeneralInterest, // interestType | |
818 | update_node, // IOServiceInterestCallback | |
819 | (void *)myData, // refCon | |
820 | &myNode->notification); // notification | |
821 | if (kr == KERN_SUCCESS) { | |
822 | if (myInstance->notifyNodes == NULL) { | |
823 | myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
824 | } | |
825 | CFArrayAppendValue(myInstance->notifyNodes, myData); | |
826 | } else { | |
827 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, | |
828 | CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr); | |
829 | } | |
830 | CFRelease(myData); | |
831 | } | |
832 | ||
833 | ||
834 | static void | |
835 | add_init_watcher(MyType *myInstance, io_registry_entry_t interface) | |
836 | { | |
837 | kern_return_t kr; | |
838 | io_registry_entry_t node = interface; | |
839 | CFTypeRef val = NULL; | |
840 | ||
841 | while (node != MACH_PORT_NULL) { | |
842 | io_registry_entry_t parent; | |
843 | ||
844 | val = IORegistryEntryCreateCFProperty(node, CFSTR("HiddenPort"), NULL, 0); | |
845 | if (val != NULL) { | |
846 | CFRelease(val); | |
847 | val = NULL; | |
848 | break; | |
849 | } | |
850 | ||
851 | val = IORegistryEntryCreateCFProperty(node, CFSTR("Initializing"), NULL, 0); | |
852 | if (val != NULL) { | |
853 | break; | |
854 | } | |
855 | ||
856 | parent = MACH_PORT_NULL; | |
857 | kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent); | |
858 | switch (kr) { | |
859 | case kIOReturnSuccess : // if we have a parent node | |
860 | case kIOReturnNoDevice : // if we have hit the root node | |
861 | break; | |
862 | default : | |
863 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr); | |
864 | break; | |
865 | } | |
866 | if (node != interface) { | |
867 | IOObjectRelease(node); | |
868 | } | |
869 | node = parent; | |
870 | } | |
871 | ||
872 | if (val != NULL) { | |
873 | if (isA_CFBoolean(val) && CFBooleanGetValue(val)) { | |
874 | // watch the "Initializing" node | |
875 | add_node_watcher(myInstance, node, interface); | |
876 | } | |
877 | ||
878 | CFRelease(val); | |
879 | } | |
880 | ||
881 | if ((node != MACH_PORT_NULL) && (node != interface)) { | |
882 | IOObjectRelease(node); | |
883 | } | |
884 | ||
885 | return; | |
886 | } | |
887 | ||
888 | ||
889 | static void | |
890 | update_serial(void *refcon, io_iterator_t iter) | |
891 | { | |
892 | MyType *myInstance = (MyType *)refcon; | |
893 | io_registry_entry_t obj; | |
894 | ||
895 | while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) { | |
896 | SCNetworkInterfaceRef interface; | |
897 | ||
898 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj); | |
899 | if (interface != NULL) { | |
900 | CFRelease(interface); | |
901 | ||
902 | // watch interface (to see when/if it's removed) | |
903 | add_node_watcher(myInstance, obj, MACH_PORT_NULL); | |
904 | } else { | |
905 | // check interface, watch if initializing | |
906 | add_init_watcher(myInstance, obj); | |
907 | } | |
908 | ||
909 | IOObjectRelease(obj); | |
910 | } | |
911 | ||
912 | updateInterfaceList(myInstance); | |
913 | return; | |
914 | } | |
915 | ||
916 | ||
917 | static void | |
918 | watcher_add_serial(MyType *myInstance) | |
919 | { | |
920 | kern_return_t kr; | |
921 | ||
922 | myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault); | |
923 | if (myInstance->notifyPort == NULL) { | |
924 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, | |
925 | CFSTR("SCMonitor: IONotificationPortCreate failed")); | |
926 | return; | |
927 | } | |
928 | ||
929 | // watch for the introduction of new network serial devices | |
930 | kr = IOServiceAddMatchingNotification(myInstance->notifyPort, | |
931 | kIOFirstMatchNotification, | |
932 | IOServiceMatching("IOSerialBSDClient"), | |
933 | &update_serial, | |
934 | (void *)myInstance, // refCon | |
935 | &myInstance->notifyIterator); // notification | |
936 | if (kr != KERN_SUCCESS) { | |
937 | SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, | |
938 | CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"), | |
939 | kr); | |
940 | return; | |
941 | } | |
942 | ||
943 | myInstance->notifyNodes = NULL; | |
944 | ||
945 | // Get the current list of matches and arm the notification for | |
946 | // future interface arrivals. | |
947 | update_serial((void *)myInstance, myInstance->notifyIterator); | |
948 | ||
949 | // and keep watching | |
950 | CFRunLoopAddSource(CFRunLoopGetCurrent(), | |
951 | IONotificationPortGetRunLoopSource(myInstance->notifyPort), | |
952 | kCFRunLoopDefaultMode); | |
953 | return; | |
954 | } | |
955 | ||
956 | ||
957 | static void | |
958 | watcher_remove_serial(MyType *myInstance) | |
959 | { | |
960 | if (myInstance->notifyNodes != NULL) { | |
961 | CFIndex i; | |
962 | CFIndex n = CFArrayGetCount(myInstance->notifyNodes); | |
963 | ||
964 | for (i = 0; i < n; i++) { | |
965 | CFDataRef myData; | |
966 | MyNode *myNode; | |
967 | ||
968 | myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i); | |
969 | myNode = (MyNode *)CFDataGetBytePtr(myData); | |
970 | if (myNode->interface != MACH_PORT_NULL) { | |
971 | IOObjectRelease(myNode->interface); | |
972 | } | |
973 | IOObjectRelease(myNode->notification); | |
974 | } | |
975 | ||
976 | CFRelease(myInstance->notifyNodes); | |
977 | myInstance->notifyNodes = NULL; | |
978 | } | |
979 | ||
980 | if (myInstance->notifyIterator != MACH_PORT_NULL) { | |
981 | IOObjectRelease(myInstance->notifyIterator); | |
982 | myInstance->notifyIterator = MACH_PORT_NULL; | |
983 | } | |
984 | ||
985 | if (myInstance->notifyPort != MACH_PORT_NULL) { | |
986 | IONotificationPortDestroy(myInstance->notifyPort); | |
987 | myInstance->notifyPort = NULL; | |
988 | } | |
989 | ||
990 | return; | |
991 | } | |
992 | ||
993 | ||
994 | #pragma mark - | |
995 | ||
996 | ||
997 | static void | |
998 | watcher_add(MyType *myInstance) | |
999 | { | |
1000 | CFBundleRef bundle; | |
1001 | ||
1002 | if (myInstance->log_msg == NULL) { | |
1003 | myInstance->log_msg = asl_new(ASL_TYPE_MSG); | |
1004 | asl_set(myInstance->log_msg, ASL_KEY_FACILITY, MY_BUNDLE_ID); | |
1005 | } | |
1006 | ||
1007 | bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); | |
1008 | if (bundle != NULL) { | |
1009 | CFStringRef action; | |
1010 | CFDictionaryRef info; | |
1011 | ||
1012 | info = CFBundleGetInfoDictionary(bundle); | |
1013 | action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey); | |
1014 | action = isA_CFString(action); | |
1015 | ||
1016 | if (action != NULL) { | |
1017 | myInstance->configuration_action = action; | |
1018 | } else { | |
1019 | CFBooleanRef user_intervention; | |
1020 | ||
1021 | user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention")); | |
1022 | if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) { | |
1023 | myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure; | |
1024 | } | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | watcher_add_lan(myInstance); | |
1029 | watcher_add_serial(myInstance); | |
1030 | return; | |
1031 | } | |
1032 | ||
1033 | ||
1034 | static void | |
1035 | watcher_remove(MyType *myInstance) | |
1036 | { | |
1037 | watcher_remove_lan(myInstance); | |
1038 | watcher_remove_serial(myInstance); | |
1039 | ||
1040 | asl_free(myInstance->log_msg); | |
1041 | myInstance->log_msg = NULL; | |
1042 | return; | |
1043 | } | |
1044 | ||
1045 | ||
6d034b4e A |
1046 | #pragma mark - |
1047 | #pragma mark UserEventAgent stubs | |
1048 | ||
1049 | ||
1050 | static HRESULT | |
1051 | myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) | |
1052 | { | |
1053 | CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); | |
1054 | ||
1055 | // Test the requested ID against the valid interfaces. | |
1056 | if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) { | |
1057 | ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
1058 | *ppv = myInstance; | |
1059 | CFRelease(interfaceID); | |
1060 | return S_OK; | |
1061 | } | |
1062 | ||
1063 | if (CFEqual(interfaceID, IUnknownUUID)) { | |
1064 | ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
1065 | *ppv = myInstance; | |
1066 | CFRelease(interfaceID); | |
1067 | return S_OK; | |
1068 | } | |
1069 | ||
1070 | // Requested interface unknown, bail with error. | |
1071 | *ppv = NULL; | |
1072 | CFRelease(interfaceID); | |
1073 | return E_NOINTERFACE; | |
1074 | } | |
1075 | ||
1076 | ||
1077 | static ULONG | |
1078 | myAddRef(void *myInstance) | |
1079 | { | |
1080 | ((MyType *) myInstance)->_refCount++; | |
1081 | return ((MyType *) myInstance)->_refCount; | |
1082 | } | |
1083 | ||
1084 | ||
1085 | static ULONG | |
1086 | myRelease(void *myInstance) | |
1087 | { | |
1088 | ((MyType *) myInstance)->_refCount--; | |
1089 | if (((MyType *) myInstance)->_refCount == 0) { | |
1090 | CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID; | |
1091 | ||
1092 | if (factoryID != NULL) { | |
1093 | CFPlugInRemoveInstanceForFactory(factoryID); | |
1094 | CFRelease(factoryID); | |
1095 | ||
1096 | watcher_remove((MyType *)myInstance); | |
1097 | notify_remove((MyType *)myInstance, TRUE); | |
1098 | } | |
1099 | free(myInstance); | |
1100 | return 0; | |
1101 | } | |
1102 | ||
1103 | return ((MyType *) myInstance)->_refCount; | |
1104 | } | |
1105 | ||
1106 | ||
1107 | static void | |
1108 | myInstall(void *myInstance) | |
1109 | { | |
1110 | watcher_add((MyType *)myInstance); | |
1111 | return; | |
1112 | } | |
1113 | ||
1114 | ||
1115 | static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { | |
1116 | NULL, // Required padding for COM | |
1117 | myQueryInterface, // These three are the required COM functions | |
1118 | myAddRef, | |
1119 | myRelease, | |
1120 | myInstall // Interface implementation | |
1121 | }; | |
1122 | ||
1123 | ||
1124 | void * | |
1125 | UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) | |
1126 | { | |
1127 | MyType *newOne = NULL; | |
1128 | ||
1129 | if (CFEqual(typeID, kUserEventAgentTypeID)) { | |
1130 | newOne = (MyType *)malloc(sizeof(MyType)); | |
1131 | bzero(newOne, sizeof(*newOne)); | |
1132 | newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; | |
1133 | newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID); | |
1134 | CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID); | |
1135 | newOne->_refCount = 1; | |
1136 | } | |
1137 | ||
1138 | return newOne; | |
1139 | } | |
1140 | ||
1141 | ||
1142 | #ifdef MAIN | |
1143 | int | |
1144 | main(int argc, char **argv) | |
1145 | { | |
1146 | MyType *newOne = (MyType *)malloc(sizeof(MyType)); | |
1147 | ||
1148 | _sc_log = FALSE; | |
1149 | _sc_verbose = (argc > 1) ? TRUE : FALSE; | |
1150 | ||
1151 | bzero(newOne, sizeof(*newOne)); | |
1152 | myInstall(newOne); | |
1153 | CFRunLoopRun(); | |
1154 | exit(0); | |
1155 | return (0); | |
1156 | } | |
1157 | #endif // MAIN |