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);