1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2010-2015 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
))
296 result
= Alloc(kUserEventAgentFactoryID
);
299 return (void *)result
;
303 #pragma mark Plugin Management
305 /*****************************************************************************
308 * This is invoked once when the plugin is loaded to do initial setup and
309 * allow us to register with launchd. If UserEventAgent crashes, the plugin
310 * will need to be reloaded, and hence this will get invoked again.
311 *****************************************************************************/
312 static void Install(void *instance
)
314 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)instance
;
316 plugin
->_pluginContext
= UserEventAgentRegisterForLaunchEvents(sPluginIdentifier
, &ManageEventsCallback
, plugin
);
318 if (!plugin
->_pluginContext
)
320 fprintf(stderr
, "%s:%s failed to register for launch events.\n", sPluginIdentifier
, __FUNCTION__
);
326 /*****************************************************************************
327 * ManageEventsCallback
329 * This is invoked when launchd loads a event dictionary and needs to inform
330 * us what a daemon / agent is looking for.
331 *****************************************************************************/
332 static void ManageEventsCallback(UserEventAgentLaunchdAction action
, CFNumberRef token
, CFTypeRef eventMatchDict
, void* vContext
)
334 if (action
== kUserEventAgentLaunchdAdd
)
338 fprintf(stderr
, "%s:%s empty dictionary\n", sPluginIdentifier
, __FUNCTION__
);
341 if (CFGetTypeID(eventMatchDict
) != CFDictionaryGetTypeID())
343 fprintf(stderr
, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier
, __FUNCTION__
, action
);
346 // Launchd wants us to add a launch event for this token and matching dictionary.
347 os_log_info(OS_LOG_DEFAULT
, "%s:%s calling AddEventToPlugin", sPluginIdentifier
, __FUNCTION__
);
348 AddEventToPlugin((BonjourUserEventsPlugin
*)vContext
, token
, (CFDictionaryRef
)eventMatchDict
);
350 else if (action
== kUserEventAgentLaunchdRemove
)
352 // Launchd wants us to remove the event hook we setup for this token / matching dictionary.
353 // Note: eventMatchDict can be NULL for Remove.
354 os_log_info(OS_LOG_DEFAULT
, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier
, __FUNCTION__
);
355 RemoveEventFromPlugin((BonjourUserEventsPlugin
*)vContext
, token
);
359 os_log_info(OS_LOG_DEFAULT
, "%s:%s unknown callback event\n", sPluginIdentifier
, __FUNCTION__
);
365 #pragma mark Plugin Guts
368 /*****************************************************************************
371 * This method is invoked when launchd wishes the plugin to setup a launch
372 * event matching the parameters in the dictionary.
373 *****************************************************************************/
374 void AddEventToPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchdToken
, CFDictionaryRef eventParameters
)
376 CFStringRef domain
= CFDictionaryGetValue(eventParameters
, sServiceDomainKey
);
377 CFStringRef type
= CFDictionaryGetValue(eventParameters
, sServiceTypeKey
);
378 CFStringRef name
= CFDictionaryGetValue(eventParameters
, sServiceNameKey
);
379 CFBooleanRef cfOnAdd
= CFDictionaryGetValue(eventParameters
, sOnServiceAddKey
);
380 CFBooleanRef cfOnRemove
= CFDictionaryGetValue(eventParameters
, sOnServiceRemoveKey
);
382 Boolean onAdd
= false;
383 Boolean onRemove
= false;
385 if (cfOnAdd
&& CFGetTypeID(cfOnAdd
) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd
))
388 if (cfOnRemove
&& CFGetTypeID(cfOnRemove
) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove
))
391 // A type is required. If none is specified, BAIL
392 if (!type
|| CFGetTypeID(type
) != CFStringGetTypeID())
394 fprintf(stderr
, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier
, __FUNCTION__
);
398 // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore.
399 if (!onAdd
&& !onRemove
)
401 fprintf(stderr
, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier
, __FUNCTION__
);
405 // If no domain is specified, assume local.
408 domain
= CFSTR("local");
410 else if (CFGetTypeID(domain
) != CFStringGetTypeID() ) // If the domain is not a string, fail
412 fprintf(stderr
, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier
, __FUNCTION__
);
416 // If we have a name filter, but it's not a string. This event is broken, bail.
417 if (name
&& CFGetTypeID(name
) != CFStringGetTypeID())
419 fprintf(stderr
, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier
, __FUNCTION__
);
424 NetBrowserInfo
* browser
= CreateBrowser(plugin
, type
, domain
);
428 fprintf(stderr
, "%s:%s cannot create browser\n", sPluginIdentifier
, __FUNCTION__
);
432 // Create Event Dictionary
433 CFMutableDictionaryRef eventDictionary
= CFDictionaryCreateMutable(NULL
, 4, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
435 // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs
436 // the token and UserEventAgentSetFireEvent needs both the token and the dictionary
437 CFDictionarySetValue(eventDictionary
, sLaunchdTokenKey
, launchdToken
);
438 CFDictionarySetValue(eventDictionary
, sLaunchdDictKey
, eventParameters
);
441 CFDictionarySetValue(eventDictionary
, sServiceNameKey
, name
);
443 // Add to the correct dictionary.
446 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Adding browser to AddEvents", sPluginIdentifier
, __FUNCTION__
);
447 AddEventDictionary(eventDictionary
, plugin
->_onAddEvents
, browser
);
452 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier
, __FUNCTION__
);
453 AddEventDictionary(eventDictionary
, plugin
->_onRemoveEvents
, browser
);
457 CFDictionarySetValue(plugin
->_tokenToBrowserMap
, launchdToken
, browser
);
460 CFRelease(eventDictionary
);
463 /*****************************************************************************
464 * RemoveEventFromPlugin
466 * This method is invoked when launchd wishes the plugin to setup a launch
467 * event matching the parameters in the dictionary.
468 *****************************************************************************/
469 void RemoveEventFromPlugin(BonjourUserEventsPlugin
* plugin
, CFNumberRef launchdToken
)
471 NetBrowserInfo
* browser
= (NetBrowserInfo
*)CFDictionaryGetValue(plugin
->_tokenToBrowserMap
, launchdToken
);
472 Boolean othersUsingBrowser
= false;
477 CFNumberGetValue(launchdToken
, kCFNumberLongLongType
, &value
);
478 fprintf(stderr
, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier
, __FUNCTION__
, value
);
482 CFMutableArrayRef onAddEvents
= (CFMutableArrayRef
)CFDictionaryGetValue(plugin
->_onAddEvents
, browser
);
483 CFMutableArrayRef onRemoveEvents
= (CFMutableArrayRef
)CFDictionaryGetValue(plugin
->_onRemoveEvents
, browser
);
487 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier
, __FUNCTION__
);
488 RemoveEventFromArray(onAddEvents
, launchdToken
);
490 // Is the array now empty, clean up
491 if (CFArrayGetCount(onAddEvents
) == 0)
493 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier
, __FUNCTION__
);
494 CFDictionaryRemoveValue(plugin
->_onAddEvents
, browser
);
500 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier
, __FUNCTION__
);
501 RemoveEventFromArray(onRemoveEvents
, launchdToken
);
503 // Is the array now empty, clean up
504 if (CFArrayGetCount(onRemoveEvents
) == 0)
506 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier
, __FUNCTION__
);
507 CFDictionaryRemoveValue(plugin
->_onRemoveEvents
, browser
);
511 // Remove ourselves from the token dictionary.
512 CFDictionaryRemoveValue(plugin
->_tokenToBrowserMap
, launchdToken
);
514 // Check to see if anyone else is using this browser.
516 CFIndex count
= CFDictionaryGetCount(plugin
->_tokenToBrowserMap
);
517 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
519 // Fetch the values of the token dictionary
520 CFDictionaryGetKeysAndValues(plugin
->_tokenToBrowserMap
, NULL
, (const void**)browsers
);
522 for (i
= 0; i
< count
; ++i
)
524 if (NetBrowserInfoEqual(browsers
[i
], browser
))
526 othersUsingBrowser
= true;
531 // If no one else is useing our browser, clean up!
532 if (!othersUsingBrowser
)
534 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier
, __FUNCTION__
, browser
);
535 CFDictionaryRemoveValue(plugin
->_browsers
, browser
); // This triggers release and dealloc of the browser
539 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Decrementing browsers %p count", sPluginIdentifier
, __FUNCTION__
, browser
);
540 // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser)
541 NetBrowserInfoRelease(NULL
, browser
);
548 /*****************************************************************************
551 * This method returns a NetBrowserInfo that is looking for a type of
552 * service in a domain. If no browser exists, it will create one and return it.
553 *****************************************************************************/
554 NetBrowserInfo
* CreateBrowser(BonjourUserEventsPlugin
* plugin
, CFStringRef type
, CFStringRef domain
)
557 CFIndex count
= CFDictionaryGetCount(plugin
->_browsers
);
558 NetBrowserInfo
* browser
= NULL
;
559 CFDictionaryRef
* dicts
= malloc(count
* sizeof(CFDictionaryRef
));
560 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
562 // Fetch the values of the browser dictionary
563 CFDictionaryGetKeysAndValues(plugin
->_browsers
, (const void**)browsers
, (const void**)dicts
);
566 // Loop thru the browsers list and see if we can find a matching one.
567 for (i
= 0; i
< count
; ++i
)
569 CFDictionaryRef browserDict
= dicts
[i
];
571 CFStringRef browserType
= CFDictionaryGetValue(browserDict
, sServiceTypeKey
);
572 CFStringRef browserDomain
= CFDictionaryGetValue(browserDict
, sServiceDomainKey
);
574 // If we have a matching browser, break
575 if ((CFStringCompare(browserType
, type
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) &&
576 (CFStringCompare(browserDomain
, domain
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
))
578 os_log_info(OS_LOG_DEFAULT
, "%s:%s: found a duplicate browser\n", sPluginIdentifier
, __FUNCTION__
);
579 browser
= browsers
[i
];
580 NetBrowserInfoRetain(NULL
, browser
);
585 // No match found, lets create one!
589 browser
= NetBrowserInfoCreate(type
, domain
, plugin
);
593 fprintf(stderr
, "%s:%s failed to search for %s.%s", sPluginIdentifier
, __FUNCTION__
, CStringFromCFString(type
), CStringFromCFString(domain
));
599 // Service browser created, lets add this to ourselves to the dictionary.
600 CFMutableDictionaryRef browserDict
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
602 CFDictionarySetValue(browserDict
, sServiceTypeKey
, type
);
603 CFDictionarySetValue(browserDict
, sServiceDomainKey
, domain
);
605 // Add the dictionary to the browsers dictionary.
606 CFDictionarySetValue(plugin
->_browsers
, browser
, browserDict
);
609 CFRelease(browserDict
);
618 /*****************************************************************************
621 * This method returns a NetBrowserInfo that matches the calling SDRef passed
622 * in via the callback.
623 *****************************************************************************/
624 NetBrowserInfo
* BrowserForSDRef(BonjourUserEventsPlugin
* plugin
, DNSServiceRef sdRef
)
627 CFIndex count
= CFDictionaryGetCount(plugin
->_browsers
);
628 NetBrowserInfo
* browser
= NULL
;
629 NetBrowserInfo
** browsers
= malloc(count
* sizeof(NetBrowserInfo
*));
631 // Fetch the values of the browser dictionary
632 CFDictionaryGetKeysAndValues(plugin
->_browsers
, (const void**)browsers
, NULL
);
634 // Loop thru the browsers list and see if we can find a matching one.
635 for (i
= 0; i
< count
; ++i
)
637 NetBrowserInfo
* currentBrowser
= browsers
[i
];
639 if (currentBrowser
->browserRef
== sdRef
)
641 browser
= currentBrowser
;
652 /*****************************************************************************
655 * Adds a event to a browser's event dictionary
656 *****************************************************************************/
658 void AddEventDictionary(CFDictionaryRef eventDict
, CFMutableDictionaryRef allEventsDictionary
, NetBrowserInfo
* key
)
660 CFMutableArrayRef eventsForBrowser
= (CFMutableArrayRef
)CFDictionaryGetValue(allEventsDictionary
, key
);
662 if (!eventsForBrowser
) // We have no events for this browser yet, lets add him.
664 eventsForBrowser
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
665 CFDictionarySetValue(allEventsDictionary
, key
, eventsForBrowser
);
666 os_log_info(OS_LOG_DEFAULT
, "%s:%s creating a new array", sPluginIdentifier
, __FUNCTION__
);
670 os_log_info(OS_LOG_DEFAULT
, "%s:%s Incrementing refcount", sPluginIdentifier
, __FUNCTION__
);
671 CFRetain(eventsForBrowser
);
674 CFArrayAppendValue(eventsForBrowser
, eventDict
);
675 CFRelease(eventsForBrowser
);
678 /*****************************************************************************
679 * RemoveEventFromArray
681 * Searches a Array of Event Dictionaries to find one with a matching launchd
682 * token and remove it.
683 *****************************************************************************/
685 void RemoveEventFromArray(CFMutableArrayRef array
, CFNumberRef launchdToken
)
688 CFIndex count
= CFArrayGetCount(array
);
690 // Loop thru looking for us.
691 for (i
= 0; i
< count
; )
693 CFDictionaryRef eventDict
= CFArrayGetValueAtIndex(array
, i
);
694 CFNumberRef token
= CFDictionaryGetValue(eventDict
, sLaunchdTokenKey
);
696 if (CFEqual(token
, launchdToken
)) // This is the same event?
698 os_log_info(OS_LOG_DEFAULT
, "%s:%s found token", sPluginIdentifier
, __FUNCTION__
);
699 CFArrayRemoveValueAtIndex(array
, i
); // Remove the event,
700 break; // The token should only exist once, so it makes no sense to continue.
704 ++i
; // If it's not us, advance.
707 if (i
== count
) os_log_info(OS_LOG_DEFAULT
, "%s:%s did not find token", sPluginIdentifier
, __FUNCTION__
);
711 #pragma mark Net Service Browser Stuff
714 /*****************************************************************************
715 * ServiceBrowserCallback
717 * This method is the heart of the plugin. It's the runloop callback annoucing
718 * the appearence and disappearance of network services.
719 *****************************************************************************/
721 void ServiceBrowserCallback (DNSServiceRef sdRef
,
722 DNSServiceFlags flags
,
723 uint32_t interfaceIndex
,
724 DNSServiceErrorType errorCode
,
725 const char* serviceName
,
727 const char* replyDomain
,
730 (void)interfaceIndex
;
733 BonjourUserEventsPlugin
* plugin
= (BonjourUserEventsPlugin
*)context
;
734 NetBrowserInfo
* browser
= BrowserForSDRef(plugin
, sdRef
);
736 if (!browser
) // Missing browser?
738 fprintf(stderr
, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier
, __FUNCTION__
);
742 if (errorCode
!= kDNSServiceErr_NoError
)
744 fprintf(stderr
, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier
, __FUNCTION__
, errorCode
);
748 CFStringRef cfServiceName
= CFStringCreateWithCString(NULL
, serviceName
, kCFStringEncodingUTF8
);
749 if (cfServiceName
== NULL
)
751 static int msgCount
= 0;
754 os_log_info(OS_LOG_DEFAULT
, "%s:%s Can not create CFString for serviceName %s", sPluginIdentifier
, __FUNCTION__
, serviceName
);
760 if (flags
& kDNSServiceFlagsAdd
)
762 os_log_info(OS_LOG_DEFAULT
, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier
, __FUNCTION__
);
763 HandleTemporaryEventsForService(plugin
, browser
, cfServiceName
, plugin
->_onAddEvents
);
767 os_log_info(OS_LOG_DEFAULT
, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier
, __FUNCTION__
);
768 HandleTemporaryEventsForService(plugin
, browser
, cfServiceName
, plugin
->_onRemoveEvents
);
771 CFRelease(cfServiceName
);
774 /*****************************************************************************
775 * HandleTemporaryEventsForService
777 * This method handles the firing of one shot events. Aka. Events that are
778 * signaled when a service appears / disappears. They have a temporarly
780 *****************************************************************************/
781 void HandleTemporaryEventsForService(BonjourUserEventsPlugin
* plugin
, NetBrowserInfo
* browser
, CFStringRef serviceName
, CFMutableDictionaryRef eventsDictionary
)
783 CFArrayRef events
= (CFArrayRef
)CFDictionaryGetValue(eventsDictionary
, browser
); // Get events for the browser we passed in.
787 if (!events
) // Somehow we have a orphan browser...
790 count
= CFArrayGetCount(events
);
792 // Go thru the events and run filters, notifity if they pass.
793 for (i
= 0; i
< count
; ++i
)
795 CFDictionaryRef eventDict
= (CFDictionaryRef
)CFArrayGetValueAtIndex(events
, i
);
796 CFStringRef eventServiceName
= (CFStringRef
)CFDictionaryGetValue(eventDict
, sServiceNameKey
);
797 CFNumberRef token
= (CFNumberRef
) CFDictionaryGetValue(eventDict
, sLaunchdTokenKey
);
798 CFDictionaryRef dict
= (CFDictionaryRef
) CFDictionaryGetValue(eventDict
, sLaunchdDictKey
);
800 // Currently we only filter on service name, that makes this as simple as...
801 if (!eventServiceName
|| CFEqual(serviceName
, eventServiceName
))
803 uint64_t tokenUint64
;
804 // Signal Event: This is edge trigger. When the action has been taken, it will not
805 // be remembered anymore.
807 os_log_info(OS_LOG_DEFAULT
, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier
, __FUNCTION__
);
808 CFNumberGetValue(token
, kCFNumberLongLongType
, &tokenUint64
);
810 xpc_object_t jobRequest
= _CFXPCCreateXPCObjectFromCFObject(dict
);
812 UserEventAgentFireEvent(plugin
->_pluginContext
, tokenUint64
, jobRequest
);
813 xpc_release(jobRequest
);
819 #pragma mark Convenience
822 /*****************************************************************************
823 * CStringFromCFString
825 * Silly convenence function for dealing with non-critical CFSTR -> cStr
827 *****************************************************************************/
829 const char* CStringFromCFString(CFStringRef string
)
831 const char* defaultString
= "??????";
835 return defaultString
;
837 cstring
= CFStringGetCStringPtr(string
, kCFStringEncodingUTF8
);
839 return (cstring
) ? cstring
: defaultString
;
844 #pragma mark NetBrowserInfo "Object"
846 /*****************************************************************************
847 * NetBrowserInfoCreate
849 * The method creates a NetBrowserInfo Object and initalizes it.
850 *****************************************************************************/
851 NetBrowserInfo
* NetBrowserInfoCreate(CFStringRef serviceType
, CFStringRef domain
, void* context
)
853 NetBrowserInfo
* outObj
= NULL
;
854 DNSServiceRef browserRef
= NULL
;
855 char* cServiceType
= NULL
;
856 char* cDomain
= NULL
;
857 Boolean success
= true;
859 CFIndex serviceSize
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType
), kCFStringEncodingUTF8
);
860 cServiceType
= calloc(serviceSize
, 1);
861 success
= CFStringGetCString(serviceType
, cServiceType
, serviceSize
, kCFStringEncodingUTF8
);
866 CFIndex domainSize
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain
), kCFStringEncodingUTF8
);
869 cDomain
= calloc(domainSize
, 1);
870 success
= success
&& CFStringGetCString(domain
, cDomain
, domainSize
, kCFStringEncodingUTF8
);
876 fprintf(stderr
, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier
, __FUNCTION__
);
885 DNSServiceErrorType err
= DNSServiceBrowse(&browserRef
, 0, 0, cServiceType
, cDomain
, ServiceBrowserCallback
, context
);
887 if (err
!= kDNSServiceErr_NoError
)
889 fprintf(stderr
, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier
, __FUNCTION__
, cServiceType
, cDomain
);
898 DNSServiceSetDispatchQueue(browserRef
, dispatch_get_main_queue());
901 outObj
= malloc(sizeof(NetBrowserInfo
));
903 outObj
->refCount
= 1;
904 outObj
->browserRef
= browserRef
;
906 os_log_info(OS_LOG_DEFAULT
, "%s:%s: created new object %p", sPluginIdentifier
, __FUNCTION__
, outObj
);
916 /*****************************************************************************
917 * NetBrowserInfoRetain
919 * The method retains a NetBrowserInfo object.
920 *****************************************************************************/
921 const void* NetBrowserInfoRetain(CFAllocatorRef allocator
, const void* info
)
924 NetBrowserInfo
* obj
= (NetBrowserInfo
*)info
;
930 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
, (int)obj
->refCount
);
935 /*****************************************************************************
936 * NetBrowserInfoRelease
938 * The method releases a NetBrowserInfo object.
939 *****************************************************************************/
940 void NetBrowserInfoRelease(CFAllocatorRef allocator
, const void* info
)
943 NetBrowserInfo
* obj
= (NetBrowserInfo
*)info
;
948 if (obj
->refCount
== 1)
950 os_log_info(OS_LOG_DEFAULT
, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
);
951 DNSServiceRefDeallocate(obj
->browserRef
);
957 os_log_info(OS_LOG_DEFAULT
, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier
, __FUNCTION__
, obj
->browserRef
, (int)obj
->refCount
);
962 /*****************************************************************************
963 * NetBrowserInfoEqual
965 * The method is used to compare two NetBrowserInfo objects for equality.
966 *****************************************************************************/
967 Boolean
NetBrowserInfoEqual(const void *value1
, const void *value2
)
969 NetBrowserInfo
* obj1
= (NetBrowserInfo
*)value1
;
970 NetBrowserInfo
* obj2
= (NetBrowserInfo
*)value2
;
972 if (obj1
->browserRef
== obj2
->browserRef
)
978 /*****************************************************************************
981 * The method is used to make a hash for the object. We can cheat and use the
983 *****************************************************************************/
984 CFHashCode
NetBrowserInfoHash(const void *value
)
986 return (CFHashCode
)((NetBrowserInfo
*)value
)->browserRef
;
990 /*****************************************************************************
991 * NetBrowserInfoCopyDescription
994 *****************************************************************************/
995 CFStringRef
NetBrowserInfoCopyDescription(const void *value
)
998 return CFStringCreateWithCString(NULL
, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8
);