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