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 if ( pluginURL ) { 133 onePlugin = CFPlugInCreate(NULL, pluginURL); 134 CFRelease( pluginURL ); 135 pluginURL = NULL; 136 if ( onePlugin ) { 137 continue; 138 } 139 } 140 } 141 142 // no full path to plugin, so let's try /S/L/E/ 143 CFStringReplaceAll(pluginPath, CFSTR("")); 144 CFStringAppendCString(pluginPath, 145 "/System/Library/Extensions/", 146 kCFStringEncodingMacRoman); 147 CFStringAppend(pluginPath, pluginName); 148 pluginURL = CFURLCreateWithFileSystemPath(NULL, 149 pluginPath, 150 kCFURLPOSIXPathStyle, 151 TRUE); 152 153 // NOTE - on embedded we have cases where the plugin bundle is cached 154 // so do NOT use _CreateIfReachable. In the cached case 155 // CFPlugInCreate will actually create the plugin for us. 156 if ( pluginURL ) { 157 onePlugin = CFPlugInCreate(NULL, pluginURL); 158 CFRelease( pluginURL ); 159 pluginURL = NULL; 160 if ( onePlugin ) { 161 continue; 162 } 163 } 164 165 // no full path to plugin or /S/L/E/, so let's try /L/E/ 166 CFStringReplaceAll(pluginPath, CFSTR("")); 167 CFStringAppendCString(pluginPath, 168 "/Library/Extensions/", 169 kCFStringEncodingMacRoman); 170 CFStringAppend(pluginPath, pluginName); 171 pluginURL = CFURLCreateWithFileSystemPath(NULL, 172 pluginPath, 173 kCFURLPOSIXPathStyle, 174 TRUE); 175 if ( pluginURL ) { 176 onePlugin = CFPlugInCreate(NULL, pluginURL); 177 CFRelease( pluginURL ); 178 pluginURL = NULL; 179 } 180 } while ( FALSE ); 181#if 0 182 const char * myPtr; 183 myPtr = CFStringGetCStringPtr(pluginPath, kCFStringEncodingMacRoman); 184 __iocfpluginlog("%s pluginPath \"%s\" \n", __func__, 185 myPtr ? myPtr : "no name"); 186#endif 187 188 if ( onePlugin 189 && (bundle = CFPlugInGetBundle(onePlugin)) 190 && (plist = CFBundleGetInfoDictionary(bundle)) 191 && (matching = (CFDictionaryRef) 192 CFDictionaryGetValue(plist, CFSTR("Personality")))) { 193 kr = IOServiceMatchPropertyTable( service, matching, &matches ); 194 if ( kr != kIOReturnSuccess ) 195 matches = FALSE; 196 } else { 197 matches = TRUE; 198 } 199 200 if ( matches ) { 201 if ( onePlugin ) { 202 *factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(pluginType, onePlugin); 203 } 204 } 205 206 if ( pluginPath ) 207 CFRelease( pluginPath ); 208 if ( pluginTypes ) 209 CFRelease( pluginTypes ); 210 211#if 0 212 __iocfpluginlog("%s kr %d \n", __func__, kr); 213#endif 214 215 return( kr ); 216} 217 218kern_return_t 219IOCreatePlugInInterfaceForService(io_service_t service, 220 CFUUIDRef pluginType, CFUUIDRef interfaceType, 221 IOCFPlugInInterface *** theInterface, SInt32 * theScore) 222{ 223 CFDictionaryRef plist = 0; 224 CFArrayRef plists; 225 CFArrayRef factories; 226 CFMutableArrayRef candidates; 227 CFMutableArrayRef scores; 228 CFIndex index; 229 CFIndex insert; 230 CFUUIDRef factoryID; 231 kern_return_t kr; 232 SInt32 score; 233 IOCFPlugInInterface ** interface; 234 Boolean haveOne; 235 236 kr = IOFindPlugIns( service, pluginType, 237 &factories, &plists ); 238 if( KERN_SUCCESS != kr) { 239 if (factories) CFRelease(factories); 240 if (plists) CFRelease(plists); 241 return( kr ); 242 } 243 if ((KERN_SUCCESS != kr) 244 || (factories == NULL) 245 || (0 == CFArrayGetCount(factories))) { 246// printf("No factories for type\n"); 247 if (factories) CFRelease(factories); 248 if (plists) CFRelease(plists); 249 return( kIOReturnUnsupported ); 250 } 251 candidates = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 252 scores = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 253 254 // allocate and Probe all 255 if (candidates && scores) { 256 CFIndex numfactories = CFArrayGetCount(factories); 257 for ( index = 0; index < numfactories; index++ ) { 258 IUnknownVTbl ** iunknown; 259 260 factoryID = (CFUUIDRef) CFArrayGetValueAtIndex(factories, index); 261 iunknown = (IUnknownVTbl **) 262 CFPlugInInstanceCreate(NULL, factoryID, pluginType); 263 if (!iunknown) { 264 // printf("Failed to create instance (link error?)\n"); 265 continue; 266 } 267 (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(interfaceType), 268 (LPVOID *)&interface); 269 270 // Now we are done with IUnknown interface 271 (*iunknown)->Release(iunknown); 272 273 if (!interface) { 274 // printf("Failed to get interface.\n"); 275 continue; 276 } 277 if (plists) 278 plist = (CFDictionaryRef) CFArrayGetValueAtIndex( plists, index ); 279 score = 0; // from property table 280 kr = (*interface)->Probe(interface, plist, service, &score); 281 282 if (kIOReturnSuccess == kr) { 283 CFIndex numscores = CFArrayGetCount(scores); 284 for (insert = 0; insert < numscores; insert++) { 285 if (score > (SInt32) ((intptr_t) CFArrayGetValueAtIndex(scores, insert))) 286 break; 287 } 288 CFArrayInsertValueAtIndex(candidates, insert, (void *) interface); 289 CFArrayInsertValueAtIndex(scores, insert, (void *) (intptr_t) score); 290 } else 291 (*interface)->Release(interface); 292 } 293 } 294 295 296 // Start in score order 297 CFIndex candidatecount = CFArrayGetCount(candidates); 298 for (haveOne = false, index = 0; 299 index < candidatecount; 300 index++) { 301 302 Boolean freeIt; 303 304 if (plists) 305 plist = (CFDictionaryRef) CFArrayGetValueAtIndex(plists, index ); 306 interface = (IOCFPlugInInterface **) 307 CFArrayGetValueAtIndex(candidates, index ); 308 if (!haveOne) { 309 haveOne = (kIOReturnSuccess == (*interface)->Start(interface, plist, service)); 310 freeIt = !haveOne; 311 if (haveOne) { 312 *theInterface = interface; 313 *theScore = (SInt32) (intptr_t) 314 CFArrayGetValueAtIndex(scores, index ); 315 } 316 } else 317 freeIt = true; 318 if (freeIt) 319 (*interface)->Release(interface); 320 } 321 322 if (factories) 323 CFRelease(factories); 324 if (plists) 325 CFRelease(plists); 326 if (candidates) 327 CFRelease(candidates); 328 if (scores) 329 CFRelease(scores); 330 // CFRelease(plugin); 331 332 return (haveOne ? kIOReturnSuccess : kIOReturnNoResources); 333} 334 335kern_return_t 336IODestroyPlugInInterface(IOCFPlugInInterface ** interface) 337{ 338 kern_return_t err; 339 340 err = (*interface)->Stop(interface); 341 (*interface)->Release(interface); 342 343 return( err ); 344} 345 346kern_return_t 347IOCreatePlugInInterfaces(CFUUIDRef pluginType, CFUUIDRef interfaceType); 348 349#if 0 // local logging 350void __iocfpluginlog(const char *format, ...) 351{ 352 va_list args; 353 354 va_start(args, format); 355 asl_vlog(NULL, NULL, ASL_LEVEL_CRIT, format, args); 356 va_end(args); 357} 358#endif 359 360#endif /* !HAVE_CFPLUGIN */ 361