1/* 2 * Copyright (c) 2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#ifdef HAVE_CFPLUGIN 24 25#include <IOKit/IOCFPlugIn.h> 26 27#if 0 // for local logging 28#include <asl.h> 29void __iocfpluginlog(const char *format, ...); 30#endif 31 32// Inited by class IOCFPlugInIniter 33CFUUIDRef gIOCFPlugInInterfaceID = NULL; 34 35typedef struct LookUUIDContextStruct { 36 const void * result; 37 CFUUIDRef key; 38} LookUUIDContext; 39 40static void 41_IOGetWithUUIDKey(const void *key, const void * value, void *ctx) 42{ 43 LookUUIDContext * context = (LookUUIDContext *) ctx; 44 CFUUIDRef uuid; 45 46 uuid = CFUUIDCreateFromString(NULL, (CFStringRef)key); 47 if( uuid) { 48 if( CFEqual( uuid, context->key)) 49 context->result = value; 50 CFRelease(uuid); 51 } 52} 53 54static CFURLRef 55_CreateIfReachable( CFStringRef thePath ) 56{ 57 CFURLRef pathURL = NULL; // caller will release 58 59 pathURL = CFURLCreateWithFileSystemPath(NULL, thePath, 60 kCFURLPOSIXPathStyle, 61 TRUE); 62 if (pathURL) { 63 if (CFURLResourceIsReachable(pathURL, NULL) == false) { 64 CFRelease( pathURL ); 65 pathURL = NULL; 66 } 67 } 68 return(pathURL); 69} 70 71/* Starting in 10.9 Plugins will be looked up in the following order: 72 * 1) if IOCFPlugInTypes in the registry entry has a plugin name starting with 73 * a '/' we assume it is a full path and look for the plugin there 74 * 2) if #1 fails we will append "/System/Library/Extensions/" to the plugin 75 * name we get from the registry (this was the pre 10.9 behavior) 76 * 3) if #2 fails we will append "/Library/Extensions/" to the plugin name we 77 * get from the registry (10.9 is where we started loading kexts from /L/E/ 78 * and /S/L/E/ ) 79 */ 80 81static kern_return_t 82IOFindPlugIns( io_service_t service, 83 CFUUIDRef pluginType, 84 CFArrayRef * factories, CFArrayRef * plists ) 85{ 86 CFURLRef pluginURL = NULL; // must release 87 CFPlugInRef onePlugin = NULL; 88 CFBundleRef bundle; 89 CFDictionaryRef plist; 90 CFDictionaryRef matching; 91 CFDictionaryRef pluginTypes = NULL; // must release 92 CFMutableStringRef pluginPath = NULL; // must release 93 LookUUIDContext context; 94 CFStringRef pluginName = NULL; // do not release 95 boolean_t matches; 96 kern_return_t kr = kIOReturnSuccess; 97 98 *factories = 0; 99 *plists = 0; 100 101 do { 102 pluginPath = CFStringCreateMutable( kCFAllocatorDefault, 0 ); 103 if ( pluginPath == NULL ) { 104 continue; 105 } 106 107 pluginTypes = IORegistryEntryCreateCFProperty( service, CFSTR(kIOCFPlugInTypesKey), 108 kCFAllocatorDefault, kNilOptions ); 109 if ( pluginTypes == NULL ) { 110 continue; 111 } 112 113 context.key = pluginType; 114 context.result = 0; 115 CFDictionaryApplyFunction( pluginTypes, &_IOGetWithUUIDKey, &context); 116 pluginName = (CFStringRef) context.result; 117 if ( pluginName == NULL ) { 118 continue; 119 } 120 121#if 0 122 const char * myPtr; 123 myPtr = CFStringGetCStringPtr(pluginName, kCFStringEncodingMacRoman); 124 __iocfpluginlog("%s pluginName \"%s\" \n", __func__, 125 myPtr ? myPtr : "no name"); 126#endif 127 128 // see if the plugin name is possibly a full path 129 if ( CFStringGetCharacterAtIndex(pluginName, 0) == '/' ) { 130 CFStringAppend(pluginPath, pluginName); 131 pluginURL = _CreateIfReachable(pluginPath); 132 } 133 134 if ( pluginURL == NULL ) { 135 // no full path to plugin, so let's try /S/L/E/ 136 CFStringReplaceAll(pluginPath, CFSTR("")); 137 CFStringAppendCString(pluginPath, 138 "/System/Library/Extensions/", 139 kCFStringEncodingMacRoman); 140 CFStringAppend(pluginPath, pluginName); 141 pluginURL = _CreateIfReachable(pluginPath); 142 } 143 144 if ( pluginURL == NULL ) { 145 // no full path to plugin, so let's try /L/E/ 146 CFStringReplaceAll(pluginPath, CFSTR("")); 147 CFStringAppendCString(pluginPath, 148 "/Library/Extensions/", 149 kCFStringEncodingMacRoman); 150 CFStringAppend(pluginPath, pluginName); 151 pluginURL = _CreateIfReachable(pluginPath); 152 } 153 } while ( FALSE ); 154 155 if ( pluginURL ) { 156 onePlugin = CFPlugInCreate(NULL, pluginURL); 157 } 158 159 if ( onePlugin 160 && (bundle = CFPlugInGetBundle(onePlugin)) 161 && (plist = CFBundleGetInfoDictionary(bundle)) 162 && (matching = (CFDictionaryRef) 163 CFDictionaryGetValue(plist, CFSTR("Personality")))) { 164 kr = IOServiceMatchPropertyTable( service, matching, &matches ); 165 if ( kr != kIOReturnSuccess ) 166 matches = FALSE; 167 } else { 168 matches = TRUE; 169 } 170 171 if ( matches ) { 172 if ( onePlugin ) { 173 *factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(pluginType, onePlugin); 174 } 175 } 176 177 if ( pluginURL ) 178 CFRelease( pluginURL ); 179 if ( pluginPath ) 180 CFRelease( pluginPath ); 181 if ( pluginTypes ) 182 CFRelease( pluginTypes ); 183 184#if 0 185 __iocfpluginlog("%s kr %d \n", __func__, kr); 186#endif 187 188 return( kr ); 189} 190 191kern_return_t 192IOCreatePlugInInterfaceForService(io_service_t service, 193 CFUUIDRef pluginType, CFUUIDRef interfaceType, 194 IOCFPlugInInterface *** theInterface, SInt32 * theScore) 195{ 196 CFDictionaryRef plist = 0; 197 CFArrayRef plists; 198 CFArrayRef factories; 199 CFMutableArrayRef candidates; 200 CFMutableArrayRef scores; 201 CFIndex index; 202 CFIndex insert; 203 CFUUIDRef factoryID; 204 kern_return_t kr; 205 SInt32 score; 206 IOCFPlugInInterface ** interface; 207 Boolean haveOne; 208 209 kr = IOFindPlugIns( service, pluginType, 210 &factories, &plists ); 211 if( KERN_SUCCESS != kr) { 212 if (factories) CFRelease(factories); 213 if (plists) CFRelease(plists); 214 return( kr ); 215 } 216 if ((KERN_SUCCESS != kr) 217 || (factories == NULL) 218 || (0 == CFArrayGetCount(factories))) { 219// printf("No factories for type\n"); 220 if (factories) CFRelease(factories); 221 if (plists) CFRelease(plists); 222 return( kIOReturnUnsupported ); 223 } 224 candidates = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 225 scores = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 226 227 // allocate and Probe all 228 if (candidates && scores) { 229 CFIndex numfactories = CFArrayGetCount(factories); 230 for ( index = 0; index < numfactories; index++ ) { 231 IUnknownVTbl ** iunknown; 232 233 factoryID = (CFUUIDRef) CFArrayGetValueAtIndex(factories, index); 234 iunknown = (IUnknownVTbl **) 235 CFPlugInInstanceCreate(NULL, factoryID, pluginType); 236 if (!iunknown) { 237 // printf("Failed to create instance (link error?)\n"); 238 continue; 239 } 240 (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(interfaceType), 241 (LPVOID *)&interface); 242 243 // Now we are done with IUnknown interface 244 (*iunknown)->Release(iunknown); 245 246 if (!interface) { 247 // printf("Failed to get interface.\n"); 248 continue; 249 } 250 if (plists) 251 plist = (CFDictionaryRef) CFArrayGetValueAtIndex( plists, index ); 252 score = 0; // from property table 253 kr = (*interface)->Probe(interface, plist, service, &score); 254 255 if (kIOReturnSuccess == kr) { 256 CFIndex numscores = CFArrayGetCount(scores); 257 for (insert = 0; insert < numscores; insert++) { 258 if (score > (SInt32) ((intptr_t) CFArrayGetValueAtIndex(scores, insert))) 259 break; 260 } 261 CFArrayInsertValueAtIndex(candidates, insert, (void *) interface); 262 CFArrayInsertValueAtIndex(scores, insert, (void *) (intptr_t) score); 263 } else 264 (*interface)->Release(interface); 265 } 266 } 267 268 269 // Start in score order 270 CFIndex candidatecount = CFArrayGetCount(candidates); 271 for (haveOne = false, index = 0; 272 index < candidatecount; 273 index++) { 274 275 Boolean freeIt; 276 277 if (plists) 278 plist = (CFDictionaryRef) CFArrayGetValueAtIndex(plists, index ); 279 interface = (IOCFPlugInInterface **) 280 CFArrayGetValueAtIndex(candidates, index ); 281 if (!haveOne) { 282 haveOne = (kIOReturnSuccess == (*interface)->Start(interface, plist, service)); 283 freeIt = !haveOne; 284 if (haveOne) { 285 *theInterface = interface; 286 *theScore = (SInt32) (intptr_t) 287 CFArrayGetValueAtIndex(scores, index ); 288 } 289 } else 290 freeIt = true; 291 if (freeIt) 292 (*interface)->Release(interface); 293 } 294 295 if (factories) 296 CFRelease(factories); 297 if (plists) 298 CFRelease(plists); 299 if (candidates) 300 CFRelease(candidates); 301 if (scores) 302 CFRelease(scores); 303 // CFRelease(plugin); 304 305 return (haveOne ? kIOReturnSuccess : kIOReturnNoResources); 306} 307 308kern_return_t 309IODestroyPlugInInterface(IOCFPlugInInterface ** interface) 310{ 311 kern_return_t err; 312 313 err = (*interface)->Stop(interface); 314 (*interface)->Release(interface); 315 316 return( err ); 317} 318 319kern_return_t 320IOCreatePlugInInterfaces(CFUUIDRef pluginType, CFUUIDRef interfaceType); 321 322#if 0 // local logging 323void __iocfpluginlog(const char *format, ...) 324{ 325 aslmsg msg = NULL; 326 327 msg = asl_new(ASL_TYPE_MSG); 328 asl_set(msg, ASL_KEY_FACILITY, "com.apple.iokit.IOCFPlugin"); 329 if (msg) { 330 va_list ap; 331 va_start(ap, format); 332 asl_vlog(NULL, msg, ASL_LEVEL_NOTICE, format, ap); 333 va_end(ap); 334 asl_free(msg); 335 } 336} 337#endif 338 339 340#endif /* !HAVE_CFPLUGIN */ 341