1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2010 Apple Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <CoreFoundation/CoreFoundation.h>
19 #include <CoreFoundation/CFXPCBridge.h>
21 #include <UserEventAgentInterface.h>
31 static const char* sPluginIdentifier
= "com.apple.bonjour.events";
34 static const CFStringRef sServiceNameKey
= CFSTR("ServiceName");
35 static const CFStringRef sServiceTypeKey
= CFSTR("ServiceType");
36 static const CFStringRef sServiceDomainKey
= CFSTR("ServiceDomain");
38 static const CFStringRef sOnServiceAddKey
= CFSTR("OnServiceAdd");
39 static const CFStringRef sOnServiceRemoveKey
= CFSTR("OnServiceRemove");
41 static const CFStringRef sLaunchdTokenKey
= CFSTR("LaunchdToken");
42 static const CFStringRef sLaunchdDictKey
= CFSTR("LaunchdDict");
45 /************************************************
46 * Launch Event Dictionary (input from launchd)
47 * Passed To: ManageEventsCallback
48 *-----------------------------------------------
49 * Typing in this dictionary is not enforced
50 * above us. So this may not be true. Type check
51 * all input before using it.
52 *-----------------------------------------------
53 * sServiceNameKey - CFString (Optional)
54 * sServiceTypeKey - CFString
55 * sServiceDomainKey - CFString
57 * One or more of the following.
58 *-----------------------------------
59 * sOnServiceAddKey - CFBoolean
60 * sOnServiceRemoveKey - CFBoolean
61 * sWhileServiceExistsKey - CFBoolean
62 ************************************************/
64 /************************************************
66 *-----------------------------------------------
67 * sServiceDomainKey - CFString
68 * sServiceTypeKey - CFString
69 ************************************************/
71 /************************************************
73 *-----------------------------------------------
74 * sServiceNameKey - CFString (Optional)
75 * sLaunchdTokenKey - CFNumber
76 ************************************************/
79 UserEventAgentInterfaceStruct
* _UserEventAgentInterface
;
85 CFMutableDictionaryRef _tokenToBrowserMap
; // Maps a token to a browser that can be used to scan the remaining dictionaries.
86 CFMutableDictionaryRef _browsers
; // A Dictionary of Browser Dictionaries where the resposible browser is the key.
87 CFMutableDictionaryRef _onAddEvents
; // A Dictionary of Event Dictionaries that describe events to trigger on a service appearing.
88 CFMutableDictionaryRef _onRemoveEvents
; // A Dictionary of Event Dictionaries that describe events to trigger on a service disappearing.
89 } BonjourUserEventsPlugin
;
93 DNSServiceRef browserRef
;
97 #pragma mark Prototypes
100 static HRESULT
QueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
);
101 static ULONG
AddRef(void* instance
);
102 static ULONG
Release(void* instance
);
104 static BonjourUserEventsPlugin
* Alloc(CFUUIDRef factoryID
);
105 static void Dealloc(BonjourUserEventsPlugin
* plugin
);
107 void * UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
);
110 static void Install(void* instance
);
111 static void ManageEventsCallback(
112 UserEventAgentLaunchdAction action
,
114 CFTypeRef eventMatchDict
,
119 void AddEventToPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchdToken
, CFDictionaryRef eventParameters
);
120 void RemoveEventFromPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchToken
);
122 NetBrowserInfo
* CreateBrowser(BonjourUserEventsPlugin
* plugin
, CFStringRef type
, CFStringRef domain
);
123 NetBrowserInfo
* BrowserForSDRef(BonjourUserEventsPlugin
* plugin
, DNSServiceRef sdRef
);
124 void AddEventDictionary(CFDictionaryRef eventDict
, CFMutableDictionaryRef allEventsDictionary
, NetBrowserInfo
* key
);
125 void RemoveEventFromArray(CFMutableArrayRef array
, CFNumberRef launchdToken
);
127 // Net Service Browser Stuff
128 void ServiceBrowserCallback (DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
, const char* serviceName
, const char* regtype
, const char* replyDomain
, void* context
);
129 void HandleTemporaryEventsForService(BonjourUserEventsPlugin
* plugin
, NetBrowserInfo
* browser
, CFStringRef serviceName
, CFMutableDictionaryRef eventsDictionary
);
132 const char* CStringFromCFString(CFStringRef string
);
134 // NetBrowserInfo "Object"
135 NetBrowserInfo
* NetBrowserInfoCreate(CFStringRef serviceType
, CFStringRef domain
, void* context
);
136 const void* NetBrowserInfoRetain(CFAllocatorRef allocator
, const void* info
);
137 void NetBrowserInfoRelease(CFAllocatorRef allocator
, const void* info
);
138 Boolean
NetBrowserInfoEqual(const void *value1
, const void *value2
);
139 CFHashCode
NetBrowserInfoHash(const void *value
);
140 CFStringRef
NetBrowserInfoCopyDescription(const void *value
);
142 static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks
= {
144 NetBrowserInfoRetain
,
145 NetBrowserInfoRelease
,
146 NetBrowserInfoCopyDescription
,
151 static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks
= {
153 NetBrowserInfoRetain
,
154 NetBrowserInfoRelease
,
155 NetBrowserInfoCopyDescription
,
159 // COM type definition goop.
160 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
161 NULL
, // Required padding for COM
162 QueryInterface
, // Query Interface
164 Release
, // Release()
169 #pragma mark COM Management
172 /*****************************************************************************
173 *****************************************************************************/
174 static HRESULT
QueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
176 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
178 // Test the requested ID against the valid interfaces.
179 if(CFEqual(interfaceID
, kUserEventAgentInterfaceID
))
181 ((BonjourUserEventsPlugin
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
183 CFRelease(interfaceID
);
186 else if(CFEqual(interfaceID
, IUnknownUUID
))
188 ((BonjourUserEventsPlugin
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
190 CFRelease(interfaceID
);
193 else // Requested interface unknown, bail with error.
196 CFRelease(interfaceID
);
197 return E_NOINTERFACE
;
201 /*****************************************************************************
202 *****************************************************************************/
203 static ULONG
AddRef(void* instance
)
205 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)instance
;
206 return ++plugin
->_refCount
;
209 /*****************************************************************************
210 *****************************************************************************/
211 static ULONG
Release(void* instance
)
213 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)instance
;
215 if (plugin
->_refCount
!= 0)
218 if (plugin
->_refCount
== 0)
224 return plugin
->_refCount
;
227 /*****************************************************************************
230 * Functionas as both +[alloc] and -[init] for the plugin. Add any
231 * initalization of member variables here.
232 *****************************************************************************/
233 static BonjourUserEventsPlugin
* Alloc(CFUUIDRef factoryID
)
235 BonjourUserEventsPlugin
* plugin
= malloc(sizeof(BonjourUserEventsPlugin
));
237 plugin
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
238 plugin
->_pluginContext
= NULL
;
242 plugin
->_factoryID
= (CFUUIDRef
)CFRetain(factoryID
);
243 CFPlugInAddInstanceForFactory(factoryID
);
246 plugin
->_refCount
= 1;
247 plugin
->_tokenToBrowserMap
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kNetBrowserInfoDictionaryValueCallbacks
);
248 plugin
->_browsers
= CFDictionaryCreateMutable(NULL
, 0, &kNetBrowserInfoDictionaryKeyCallbacks
, &kCFTypeDictionaryValueCallBacks
);
249 plugin
->_onAddEvents
= CFDictionaryCreateMutable(NULL
, 0, &kNetBrowserInfoDictionaryKeyCallbacks
, &kCFTypeDictionaryValueCallBacks
);
250 plugin
->_onRemoveEvents
= CFDictionaryCreateMutable(NULL
, 0, &kNetBrowserInfoDictionaryKeyCallbacks
, &kCFTypeDictionaryValueCallBacks
);
255 /*****************************************************************************
258 * Much like Obj-C dealloc this method is responsible for releasing any object
259 * this plugin is holding. Unlike ObjC, you call directly free() instead of
261 *****************************************************************************/
262 static void Dealloc(BonjourUserEventsPlugin
* plugin
)
264 CFUUIDRef factoryID
= plugin
->_factoryID
;
268 CFPlugInRemoveInstanceForFactory(factoryID
);
269 CFRelease(factoryID
);
272 if (plugin
->_tokenToBrowserMap
)
273 CFRelease(plugin
->_tokenToBrowserMap
);
275 if (plugin
->_browsers
)
276 CFRelease(plugin
->_browsers
);
278 if (plugin
->_onAddEvents
)
279 CFRelease(plugin
->_onAddEvents
);
281 if (plugin
->_onRemoveEvents
)
282 CFRelease(plugin
->_onRemoveEvents
);
287 /*******************************************************************************
288 *******************************************************************************/
289 void * UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
292 BonjourUserEventsPlugin
* result
= NULL
;
294 if (typeID
&& CFEqual(typeID
, kUserEventAgentTypeID
)) {
295 result
= Alloc(kUserEventAgentFactoryID
);
298 return (void *)result
;
302 #pragma mark Plugin Management
304 /*****************************************************************************
307 * This is invoked once when the plugin is loaded to do initial setup and
308 * allow us to register with launchd. If UserEventAgent crashes, the plugin
309 * will need to be reloaded, and hence this will get invoked again.
310 *****************************************************************************/
311 static void Install(void *instance
)
313 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)instance
;
315 plugin
->_pluginContext
= UserEventAgentRegisterForLaunchEvents(sPluginIdentifier
, &ManageEventsCallback
, plugin
);
317 if (!plugin
->_pluginContext
)
319 fprintf(stderr
, "%s:%s failed to register for launch events.\n", sPluginIdentifier
, __FUNCTION__
);
325 /*****************************************************************************
326 * ManageEventsCallback
328 * This is invoked when launchd loads a event dictionary and needs to inform
329 * us what a daemon / agent is looking for.
330 *****************************************************************************/
331 static void ManageEventsCallback(UserEventAgentLaunchdAction action
, CFNumberRef token
, CFTypeRef eventMatchDict
, void* vContext
)
333 if (action
== kUserEventAgentLaunchdAdd
)
337 fprintf(stderr
, "%s:%s empty dictionary\n", sPluginIdentifier
, __FUNCTION__
);
340 if (CFGetTypeID(eventMatchDict
) != CFDictionaryGetTypeID())
342 fprintf(stderr
, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier
, __FUNCTION__
, action
);
345 // Launchd wants us to add a launch event for this token and matching dictionary.
346 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s calling AddEventToPlugin", sPluginIdentifier
, __FUNCTION__
);
347 AddEventToPlugin((BonjourUserEventsPlugin
*)vContext
, token
, (CFDictionaryRef
)eventMatchDict
);
349 else if (action
== kUserEventAgentLaunchdRemove
)
351 // Launchd wants us to remove the event hook we setup for this token / matching dictionary.
352 // Note: eventMatchDict can be NULL for Remove.
353 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier
, __FUNCTION__
);
354 RemoveEventFromPlugin((BonjourUserEventsPlugin
*)vContext
, token
);
358 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s unknown callback event\n", sPluginIdentifier
, __FUNCTION__
);
364 #pragma mark Plugin Guts
367 /*****************************************************************************
370 * This method is invoked when launchd wishes the plugin to setup a launch
371 * event matching the parameters in the dictionary.
372 *****************************************************************************/
373 void AddEventToPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchdToken
, CFDictionaryRef eventParameters
)
375 CFStringRef domain
= CFDictionaryGetValue(eventParameters
, sServiceDomainKey
);
376 CFStringRef type
= CFDictionaryGetValue(eventParameters
, sServiceTypeKey
);
377 CFStringRef name
= CFDictionaryGetValue(eventParameters
, sServiceNameKey
);
378 CFBooleanRef cfOnAdd
= CFDictionaryGetValue(eventParameters
, sOnServiceAddKey
);
379 CFBooleanRef cfOnRemove
= CFDictionaryGetValue(eventParameters
, sOnServiceRemoveKey
);
381 Boolean onAdd
= false;
382 Boolean onRemove
= false;
384 if (cfOnAdd
&& CFGetTypeID(cfOnAdd
) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd
))
387 if (cfOnRemove
&& CFGetTypeID(cfOnRemove
) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove
))
390 // A type is required. If none is specified, BAIL
391 if (!type
|| CFGetTypeID(type
) != CFStringGetTypeID())
393 fprintf(stderr
, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier
, __FUNCTION__
);
397 // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore.
398 if (!onAdd
&& !onRemove
)
400 fprintf(stderr
, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier
, __FUNCTION__
);
404 // If no domain is specified, assume local.
407 domain
= CFSTR("local");
409 else if (CFGetTypeID(domain
) != CFStringGetTypeID() ) // If the domain is not a string, fail
411 fprintf(stderr
, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier
, __FUNCTION__
);
415 // If we have a name filter, but it's not a string. This event is broken, bail.
416 if (name
&& CFGetTypeID(name
) != CFStringGetTypeID())
418 fprintf(stderr
, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier
, __FUNCTION__
);
423 NetBrowserInfo
* browser
= CreateBrowser(plugin
, type
, domain
);
427 fprintf(stderr
, "%s:%s cannot create browser\n", sPluginIdentifier
, __FUNCTION__
);
431 // Create Event Dictionary
432 CFMutableDictionaryRef eventDictionary
= CFDictionaryCreateMutable(NULL
, 4, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
434 // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs
435 // the token and UserEventAgentSetFireEvent needs both the token and the dictionary
436 CFDictionarySetValue(eventDictionary
, sLaunchdTokenKey
, launchdToken
);
437 CFDictionarySetValue(eventDictionary
, sLaunchdDictKey
, eventParameters
);
440 CFDictionarySetValue(eventDictionary
, sServiceNameKey
, name
);
442 // Add to the correct dictionary.
445 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Adding browser to AddEvents", sPluginIdentifier
, __FUNCTION__
);
446 AddEventDictionary(eventDictionary
, plugin
->_onAddEvents
, browser
);
451 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier
, __FUNCTION__
);
452 AddEventDictionary(eventDictionary
, plugin
->_onRemoveEvents
, browser
);
456 CFDictionarySetValue(plugin
->_tokenToBrowserMap
, launchdToken
, browser
);
459 CFRelease(eventDictionary
);
462 /*****************************************************************************
463 * RemoveEventFromPlugin
465 * This method is invoked when launchd wishes the plugin to setup a launch
466 * event matching the parameters in the dictionary.
467 *****************************************************************************/
468 void RemoveEventFromPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchdToken
)
470 NetBrowserInfo
* browser
= (NetBrowserInfo
*)CFDictionaryGetValue(plugin
->_tokenToBrowserMap
, launchdToken
);
471 Boolean othersUsingBrowser
= false;
476 CFNumberGetValue(launchdToken
, kCFNumberLongLongType
, &value
);
477 fprintf(stderr
, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier
, __FUNCTION__
, value
);
481 CFMutableArrayRef onAddEvents
= (CFMutableArrayRef
)CFDictionaryGetValue(plugin
->_onAddEvents
, browser
);
482 CFMutableArrayRef onRemoveEvents
= (CFMutableArrayRef
)CFDictionaryGetValue(plugin
->_onRemoveEvents
, browser
);
486 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier
, __FUNCTION__
);
487 RemoveEventFromArray(onAddEvents
, launchdToken
);
489 // Is the array now empty, clean up
490 if (CFArrayGetCount(onAddEvents
) == 0)
492 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier
, __FUNCTION__
);
493 CFDictionaryRemoveValue(plugin
->_onAddEvents
, browser
);
499 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier
, __FUNCTION__
);
500 RemoveEventFromArray(onRemoveEvents
, launchdToken
);
502 // Is the array now empty, clean up
503 if (CFArrayGetCount(onRemoveEvents
) == 0)
505 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier
, __FUNCTION__
);
506 CFDictionaryRemoveValue(plugin
->_onRemoveEvents
, browser
);
510 // Remove ourselves from the token dictionary.
511 CFDictionaryRemoveValue(plugin
->_tokenToBrowserMap
, launchdToken
);
513 // Check to see if anyone else is using this browser.
515 CFIndex count
= CFDictionaryGetCount(plugin
->_tokenToBrowserMap
);
516 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
518 // Fetch the values of the token dictionary
519 CFDictionaryGetKeysAndValues(plugin
->_tokenToBrowserMap
, NULL
, (const void**)browsers
);
521 for (i
= 0; i
< count
; ++i
)
523 if (NetBrowserInfoEqual(browsers
[i
], browser
))
525 othersUsingBrowser
= true;
530 // If no one else is useing our browser, clean up!
531 if (!othersUsingBrowser
)
533 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier
, __FUNCTION__
, browser
);
534 CFDictionaryRemoveValue(plugin
->_browsers
, browser
); // This triggers release and dealloc of the browser
538 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Decrementing browsers %p count", sPluginIdentifier
, __FUNCTION__
, browser
);
539 // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser)
540 NetBrowserInfoRelease(NULL
, browser
);
547 /*****************************************************************************
550 * This method returns a NetBrowserInfo that is looking for a type of
551 * service in a domain. If no browser exists, it will create one and return it.
552 *****************************************************************************/
553 NetBrowserInfo
* CreateBrowser(BonjourUserEventsPlugin
* plugin
, CFStringRef type
, CFStringRef domain
)
556 CFIndex count
= CFDictionaryGetCount(plugin
->_browsers
);
557 NetBrowserInfo
* browser
= NULL
;
558 CFDictionaryRef
* dicts
= malloc(count
* sizeof(CFDictionaryRef
));
559 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
561 // Fetch the values of the browser dictionary
562 CFDictionaryGetKeysAndValues(plugin
->_browsers
, (const void**)browsers
, (const void**)dicts
);
565 // Loop thru the browsers list and see if we can find a matching one.
566 for (i
= 0; i
< count
; ++i
)
568 CFDictionaryRef browserDict
= dicts
[i
];
570 CFStringRef browserType
= CFDictionaryGetValue(browserDict
, sServiceTypeKey
);
571 CFStringRef browserDomain
= CFDictionaryGetValue(browserDict
, sServiceDomainKey
);
573 // If we have a matching browser, break
574 if ((CFStringCompare(browserType
, type
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) &&
575 (CFStringCompare(browserDomain
, domain
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
))
577 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: found a duplicate browser\n", sPluginIdentifier
, __FUNCTION__
);
578 browser
= browsers
[i
];
579 NetBrowserInfoRetain(NULL
, browser
);
584 // No match found, lets create one!
588 browser
= NetBrowserInfoCreate(type
, domain
, plugin
);
592 fprintf(stderr
, "%s:%s failed to search for %s.%s", sPluginIdentifier
, __FUNCTION__
, CStringFromCFString(type
), CStringFromCFString(domain
));
598 // Service browser created, lets add this to ourselves to the dictionary.
599 CFMutableDictionaryRef browserDict
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
601 CFDictionarySetValue(browserDict
, sServiceTypeKey
, type
);
602 CFDictionarySetValue(browserDict
, sServiceDomainKey
, domain
);
604 // Add the dictionary to the browsers dictionary.
605 CFDictionarySetValue(plugin
->_browsers
, browser
, browserDict
);
607 NetBrowserInfoRelease(NULL
, browser
);
610 CFRelease(browserDict
);
619 /*****************************************************************************
622 * This method returns a NetBrowserInfo that matches the calling SDRef passed
623 * in via the callback.
624 *****************************************************************************/
625 NetBrowserInfo
* BrowserForSDRef(BonjourUserEventsPlugin
* plugin
, DNSServiceRef sdRef
)
628 CFIndex count
= CFDictionaryGetCount(plugin
->_browsers
);
629 NetBrowserInfo
* browser
= NULL
;
630 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
632 // Fetch the values of the browser dictionary
633 CFDictionaryGetKeysAndValues(plugin
->_browsers
, (const void**)browsers
, NULL
);
635 // Loop thru the browsers list and see if we can find a matching one.
636 for (i
= 0; i
< count
; ++i
)
638 NetBrowserInfo
* currentBrowser
= browsers
[i
];
640 if (currentBrowser
->browserRef
== sdRef
)
642 browser
= currentBrowser
;
653 /*****************************************************************************
656 * Adds a event to a browser's event dictionary
657 *****************************************************************************/
659 void AddEventDictionary(CFDictionaryRef eventDict
, CFMutableDictionaryRef allEventsDictionary
, NetBrowserInfo
* key
)
661 CFMutableArrayRef eventsForBrowser
= (CFMutableArrayRef
)CFDictionaryGetValue(allEventsDictionary
, key
);
663 if (!eventsForBrowser
) // We have no events for this browser yet, lets add him.
665 eventsForBrowser
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
666 CFDictionarySetValue(allEventsDictionary
, key
, eventsForBrowser
);
667 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s creating a new array", sPluginIdentifier
, __FUNCTION__
);
671 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s Incrementing refcount", sPluginIdentifier
, __FUNCTION__
);
672 CFRetain(eventsForBrowser
);
675 CFArrayAppendValue(eventsForBrowser
, eventDict
);
676 CFRelease(eventsForBrowser
);
679 /*****************************************************************************
680 * RemoveEventFromArray
682 * Searches a Array of Event Dictionaries to find one with a matching launchd
683 * token and remove it.
684 *****************************************************************************/
686 void RemoveEventFromArray(CFMutableArrayRef array
, CFNumberRef launchdToken
)
689 CFIndex count
= CFArrayGetCount(array
);
691 // Loop thru looking for us.
692 for (i
= 0; i
< count
; )
694 CFDictionaryRef eventDict
= CFArrayGetValueAtIndex(array
, i
);
695 CFNumberRef token
= CFDictionaryGetValue(eventDict
, sLaunchdTokenKey
);
697 if (CFEqual(token
, launchdToken
)) // This is the same event?
699 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s found token", sPluginIdentifier
, __FUNCTION__
);
700 CFArrayRemoveValueAtIndex(array
, i
); // Remove the event,
701 break; // The token should only exist once, so it makes no sense to continue.
705 ++i
; // If it's not us, advance.
708 if (i
== count
) asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s did not find token", sPluginIdentifier
, __FUNCTION__
);
712 #pragma mark Net Service Browser Stuff
715 /*****************************************************************************
716 * ServiceBrowserCallback
718 * This method is the heart of the plugin. It's the runloop callback annoucing
719 * the appearence and disappearance of network services.
720 *****************************************************************************/
722 void ServiceBrowserCallback (DNSServiceRef sdRef
,
723 DNSServiceFlags flags
,
724 uint32_t interfaceIndex
,
725 DNSServiceErrorType errorCode
,
726 const char* serviceName
,
728 const char* replyDomain
,
731 (void)interfaceIndex
;
734 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)context
;
735 NetBrowserInfo
* browser
= BrowserForSDRef(plugin
, sdRef
);
737 if (!browser
) // Missing browser?
739 fprintf(stderr
, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier
, __FUNCTION__
);
743 if (errorCode
!= kDNSServiceErr_NoError
)
745 fprintf(stderr
, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier
, __FUNCTION__
, errorCode
);
749 CFStringRef cfServiceName
= CFStringCreateWithCString(NULL
, serviceName
, kCFStringEncodingUTF8
);
751 if (flags
& kDNSServiceFlagsAdd
)
753 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier
, __FUNCTION__
);
754 HandleTemporaryEventsForService(plugin
, browser
, cfServiceName
, plugin
->_onAddEvents
);
758 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier
, __FUNCTION__
);
759 HandleTemporaryEventsForService(plugin
, browser
, cfServiceName
, plugin
->_onRemoveEvents
);
762 CFRelease(cfServiceName
);
765 /*****************************************************************************
766 * HandleTemporaryEventsForService
768 * This method handles the firing of one shot events. Aka. Events that are
769 * signaled when a service appears / disappears. They have a temporarly
771 *****************************************************************************/
772 void HandleTemporaryEventsForService(BonjourUserEventsPlugin
* plugin
, NetBrowserInfo
* browser
, CFStringRef serviceName
, CFMutableDictionaryRef eventsDictionary
)
774 CFArrayRef events
= (CFArrayRef
)CFDictionaryGetValue(eventsDictionary
, browser
); // Get events for the browser we passed in.
778 if (!events
) // Somehow we have a orphan browser...
781 count
= CFArrayGetCount(events
);
783 // Go thru the events and run filters, notifity if they pass.
784 for (i
= 0; i
< count
; ++i
)
786 CFDictionaryRef eventDict
= (CFDictionaryRef
)CFArrayGetValueAtIndex(events
, i
);
787 CFStringRef eventServiceName
= (CFStringRef
)CFDictionaryGetValue(eventDict
, sServiceNameKey
);
788 CFNumberRef token
= (CFNumberRef
) CFDictionaryGetValue(eventDict
, sLaunchdTokenKey
);
789 CFDictionaryRef dict
= (CFDictionaryRef
) CFDictionaryGetValue(eventDict
, sLaunchdDictKey
);
791 // Currently we only filter on service name, that makes this as simple as...
792 if (!eventServiceName
|| CFEqual(serviceName
, eventServiceName
))
794 uint64_t tokenUint64
;
795 // Signal Event: This is edge trigger. When the action has been taken, it will not
796 // be remembered anymore.
798 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier
, __FUNCTION__
);
799 CFNumberGetValue(token
, kCFNumberLongLongType
, &tokenUint64
);
801 xpc_object_t jobRequest
= _CFXPCCreateXPCObjectFromCFObject(dict
);
803 UserEventAgentFireEvent(plugin
->_pluginContext
, tokenUint64
, jobRequest
);
804 xpc_release(jobRequest
);
810 #pragma mark Convenience
813 /*****************************************************************************
814 * CStringFromCFString
816 * Silly convenence function for dealing with non-critical CFSTR -> cStr
818 *****************************************************************************/
820 const char* CStringFromCFString(CFStringRef string
)
822 const char* defaultString
= "??????";
826 return defaultString
;
828 cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
830 return (cstring
) ? cstring
: defaultString
;
835 #pragma mark NetBrowserInfo "Object"
837 /*****************************************************************************
838 * NetBrowserInfoCreate
840 * The method creates a NetBrowserInfo Object and initalizes it.
841 *****************************************************************************/
842 NetBrowserInfo
* NetBrowserInfoCreate(CFStringRef serviceType
, CFStringRef domain
, void* context
)
844 NetBrowserInfo
* outObj
= NULL
;
845 DNSServiceRef browserRef
= NULL
;
846 char* cServiceType
= NULL
;
847 char* cDomain
= NULL
;
848 Boolean success
= true;
850 CFIndex serviceSize
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType
), kCFStringEncodingUTF8
);
851 cServiceType
= calloc(serviceSize
, 1);
852 success
= CFStringGetCString(serviceType
, cServiceType
, serviceSize
, kCFStringEncodingUTF8
);
857 CFIndex domainSize
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain
), kCFStringEncodingUTF8
);
858 cDomain
= calloc(serviceSize
, 1);
859 success
= success
&& CFStringGetCString(domain
, cDomain
, domainSize
, kCFStringEncodingUTF8
);
864 fprintf(stderr
, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier
, __FUNCTION__
);
873 DNSServiceErrorType err
= DNSServiceBrowse(&browserRef
, 0, 0, cServiceType
, cDomain
, ServiceBrowserCallback
, context
);
875 if (err
!= kDNSServiceErr_NoError
)
877 fprintf(stderr
, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier
, __FUNCTION__
, cServiceType
, cDomain
);
886 DNSServiceSetDispatchQueue(browserRef
, dispatch_get_main_queue());
889 outObj
= malloc(sizeof(NetBrowserInfo
));
891 outObj
->refCount
= 1;
892 outObj
->browserRef
= browserRef
;
894 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: created new object %p", sPluginIdentifier
, __FUNCTION__
, outObj
);
904 /*****************************************************************************
905 * NetBrowserInfoRetain
907 * The method retains a NetBrowserInfo object.
908 *****************************************************************************/
909 const void* NetBrowserInfoRetain(CFAllocatorRef allocator
, const void* info
)
912 NetBrowserInfo
* obj
= (NetBrowserInfo
*)info
;
918 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
, (int)obj
->refCount
);
923 /*****************************************************************************
924 * NetBrowserInfoRelease
926 * The method releases a NetBrowserInfo object.
927 *****************************************************************************/
928 void NetBrowserInfoRelease(CFAllocatorRef allocator
, const void* info
)
931 NetBrowserInfo
* obj
= (NetBrowserInfo
*)info
;
936 if (obj
->refCount
== 1)
938 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
);
939 DNSServiceRefDeallocate(obj
->browserRef
);
945 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
, (int)obj
->refCount
);
950 /*****************************************************************************
951 * NetBrowserInfoEqual
953 * The method is used to compare two NetBrowserInfo objects for equality.
954 *****************************************************************************/
955 Boolean
NetBrowserInfoEqual(const void *value1
, const void *value2
)
957 NetBrowserInfo
* obj1
= (NetBrowserInfo
*)value1
;
958 NetBrowserInfo
* obj2
= (NetBrowserInfo
*)value2
;
960 if (obj1
->browserRef
== obj2
->browserRef
)
966 /*****************************************************************************
969 * The method is used to make a hash for the object. We can cheat and use the
971 *****************************************************************************/
972 CFHashCode
NetBrowserInfoHash(const void *value
)
974 return (CFHashCode
)((NetBrowserInfo
*)value
)->browserRef
;
978 /*****************************************************************************
979 * NetBrowserInfoCopyDescription
982 *****************************************************************************/
983 CFStringRef
NetBrowserInfoCopyDescription(const void *value
)
986 return CFStringCreateWithCString(NULL
, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8
);