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