/ CFPlugIn_PlugIn.c
CFPlugIn_PlugIn.c
  1  /*
  2   * Copyright (c) 2015 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  
 24  /*      CFPlugIn_PlugIn.c
 25          Copyright (c) 1999-2014, Apple Inc.  All rights reserved.
 26          Responsibility: Tony Parker
 27  */
 28  
 29  #include "CFBundle_Internal.h"
 30  #include "CFInternal.h"
 31  
 32  
 33  static void _registerFactory(const void *key, const void *val, void *context) {
 34      CFStringRef factoryIDStr = (CFStringRef)key;
 35      CFStringRef factoryFuncStr = (CFStringRef)val;
 36      CFBundleRef bundle = (CFBundleRef)context;
 37      CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL;
 38      if (!factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr);
 39      if (CFGetTypeID(factoryFuncStr) != CFStringGetTypeID() || CFStringGetLength(factoryFuncStr) <= 0) factoryFuncStr = NULL;
 40      CFPlugInRegisterFactoryFunctionByName(factoryID, bundle, factoryFuncStr);
 41      if (factoryID) CFRelease(factoryID);
 42  }
 43  
 44  static void _registerType(const void *key, const void *val, void *context) {
 45      CFStringRef typeIDStr = (CFStringRef)key;
 46      CFArrayRef factoryIDStrArray = (CFArrayRef)val;
 47      CFBundleRef bundle = (CFBundleRef)context;
 48      SInt32 i, c = (CFGetTypeID(factoryIDStrArray) == CFArrayGetTypeID()) ? CFArrayGetCount(factoryIDStrArray) : 0;
 49      CFStringRef curFactoryIDStr;
 50      CFUUIDRef typeID = (CFGetTypeID(typeIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, typeIDStr) : NULL;
 51      CFUUIDRef curFactoryID;
 52      if (!typeID) typeID = (CFUUIDRef)CFRetain(typeIDStr);
 53      if (0 == c && CFGetTypeID(factoryIDStrArray) != CFArrayGetTypeID()) {
 54          curFactoryIDStr = (CFStringRef)val;
 55          curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
 56          if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
 57          CFPlugInRegisterPlugInType(curFactoryID, typeID);
 58          if (curFactoryID) CFRelease(curFactoryID);
 59      } else for (i = 0; i < c; i++) {
 60          curFactoryIDStr = (CFStringRef)CFArrayGetValueAtIndex(factoryIDStrArray, i);
 61          curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL;
 62          if (!curFactoryID) curFactoryID = (CFUUIDRef)CFRetain(curFactoryIDStr);
 63          CFPlugInRegisterPlugInType(curFactoryID, typeID);
 64          if (curFactoryID) CFRelease(curFactoryID);
 65      }
 66      if (typeID) CFRelease(typeID);
 67  }
 68  
 69  CF_PRIVATE Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle) {
 70      Boolean result = false;
 71      CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle), factoryDict;
 72      CFStringRef tempStr;
 73      if (infoDict) {
 74          factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey);
 75          if (factoryDict && CFGetTypeID(factoryDict) == CFDictionaryGetTypeID()) result = true;
 76          tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey);
 77          if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) result = true;
 78      }
 79      return result;
 80  }
 81  
 82  CF_PRIVATE void _CFBundleInitPlugIn(CFBundleRef bundle) {
 83      CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
 84      Boolean doDynamicReg = false;
 85      CFDictionaryRef infoDict;
 86      CFDictionaryRef factoryDict;
 87      CFDictionaryRef typeDict;
 88      CFStringRef tempStr;
 89  
 90      infoDict = CFBundleGetInfoDictionary(bundle);
 91      if (!infoDict) return;
 92      
 93      factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey);
 94      if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL;
 95      tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey);
 96      if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) doDynamicReg = true;
 97      if (!factoryDict && !doDynamicReg) return;  // This is not a plug-in.
 98  
 99      /* loadOnDemand is true by default if the plugIn does not do dynamic registration.  It is false, by default if it does do dynamic registration.  The dynamic register function can set this. */
100      __CFBundleGetPlugInData(bundle)->_isPlugIn = true;
101      __CFBundleGetPlugInData(bundle)->_loadOnDemand = true;
102      __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false;
103      __CFBundleGetPlugInData(bundle)->_instanceCount = 0;
104  
105      __CFBundleGetPlugInData(bundle)->_factories = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &_pluginFactoryArrayCallbacks);
106  
107      /* Now do the registration */
108  
109      /* First do static registrations, if any. */
110      if (factoryDict) CFDictionaryApplyFunction(factoryDict, _registerFactory, bundle);
111      typeDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInTypesKey);
112      if (typeDict && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL;
113      if (typeDict) CFDictionaryApplyFunction(typeDict, _registerType, bundle);
114  
115      /* Now set key for dynamic registration if necessary */
116      if (doDynamicReg) {
117          CFDictionarySetValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"), CFSTR("YES"));
118          if (CFBundleIsExecutableLoaded(bundle)) _CFBundlePlugInLoaded(bundle);
119      }
120  }
121  
122  CF_PRIVATE void _CFBundlePlugInLoaded(CFBundleRef bundle) {
123      CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
124      CFStringRef tempStr;
125      CFPlugInDynamicRegisterFunction func = NULL;
126  
127      if (!__CFBundleGetPlugInData(bundle)->_isPlugIn || __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration || !infoDict || !CFBundleIsExecutableLoaded(bundle)) return;
128  
129      tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
130      if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
131          CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
132          tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey);
133          if (!tempStr || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) tempStr = CFSTR("CFPlugInDynamicRegister");
134          __CFBundleGetPlugInData(bundle)->_loadOnDemand = false;
135          __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = true;
136  
137          /* Find the symbol and call it. */
138          func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr);
139          if (func) {
140              func(bundle);
141              // MF:!!! Unload function is never called.  Need to deal with this!
142          }
143  
144          __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false;
145          if (__CFBundleGetPlugInData(bundle)->_loadOnDemand && __CFBundleGetPlugInData(bundle)->_instanceCount == 0) CFBundleUnloadExecutable(bundle);   // Unload now if we can/should.
146      } else {
147          CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, CFSTR("CFPlugInNeedsDynamicRegistration"));
148      }
149  }
150  
151  CF_PRIVATE void _CFBundleDeallocatePlugIn(CFBundleRef bundle) {
152      if (__CFBundleGetPlugInData(bundle)->_isPlugIn) {
153          SInt32 c;
154  
155          /* Go through factories disabling them.  Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances.  So go backwards. */
156          c = CFArrayGetCount(__CFBundleGetPlugInData(bundle)->_factories);
157          while (c-- > 0) _CFPFactoryDisable((_CFPFactoryRef)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(bundle)->_factories, c));
158          CFRelease(__CFBundleGetPlugInData(bundle)->_factories);
159  
160          __CFBundleGetPlugInData(bundle)->_isPlugIn = false;
161      }
162  }
163  
164  CFTypeID CFPlugInGetTypeID(void) {
165      return CFBundleGetTypeID();
166  }
167  
168  CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL) {
169      CFBundleRef bundle = CFBundleCreate(allocator, plugInURL);
170      return (CFPlugInRef)bundle;
171  }
172  
173  CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn) {
174      return (CFBundleRef)plugIn;
175  }
176  
177  void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag) {
178      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
179          __CFBundleGetPlugInData(plugIn)->_loadOnDemand = flag;
180          if (__CFBundleGetPlugInData(plugIn)->_loadOnDemand && !__CFBundleGetPlugInData(plugIn)->_isDoingDynamicRegistration && __CFBundleGetPlugInData(plugIn)->_instanceCount == 0) {
181              /* Unload now if we can/should. */
182              /* If we are doing dynamic registration currently, do not unload.  The unloading will happen when dynamic registration is done, if necessary. */
183              CFBundleUnloadExecutable(plugIn);
184          } else if (!__CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
185              /* Make sure we're loaded now. */
186              CFBundleLoadExecutable(plugIn);
187          }
188      }
189  }
190  
191  Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn) {
192      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
193          return __CFBundleGetPlugInData(plugIn)->_loadOnDemand;
194      } else {
195          return false;
196      }
197  }
198  
199  CF_PRIVATE void _CFPlugInWillUnload(CFPlugInRef plugIn) {
200      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
201          SInt32 c = CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories);
202          /* First, flush all the function pointers that may be cached by our factories. */
203          while (c-- > 0) _CFPFactoryFlushFunctionCache((_CFPFactoryRef)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, c));
204      }
205  }
206  
207  CF_PRIVATE void _CFPlugInAddPlugInInstance(CFPlugInRef plugIn) {
208      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
209          if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) _CFBundleUnscheduleForUnloading(CFPlugInGetBundle(plugIn));     // Make sure we are not scheduled for unloading
210          __CFBundleGetPlugInData(plugIn)->_instanceCount++;
211          /* Instances also retain the CFBundle */
212          CFRetain(plugIn);
213      }
214  }
215  
216  CF_PRIVATE void _CFPlugInRemovePlugInInstance(CFPlugInRef plugIn) {
217      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
218          /* MF:!!! Assert that instanceCount > 0. */
219          __CFBundleGetPlugInData(plugIn)->_instanceCount--;
220          if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) {
221              // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself.  If we unload now, we will hose things.
222              //CFBundleUnloadExecutable(plugIn);
223              _CFBundleScheduleForUnloading(CFPlugInGetBundle(plugIn));
224          }
225          /* Instances also retain the CFPlugIn */
226          /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */
227          CFRelease(plugIn);
228      }
229  }
230  
231  CF_PRIVATE void _CFPlugInAddFactory(CFPlugInRef plugIn, _CFPFactoryRef factory) {
232      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) CFArrayAppendValue(__CFBundleGetPlugInData(plugIn)->_factories, factory);
233  }
234  
235  CF_PRIVATE void _CFPlugInRemoveFactory(CFPlugInRef plugIn, _CFPFactoryRef factory) {
236      if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) {
237          SInt32 idx = CFArrayGetFirstIndexOfValue(__CFBundleGetPlugInData(plugIn)->_factories, CFRangeMake(0, CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories)), factory);
238          if (idx >= 0) CFArrayRemoveValueAtIndex(__CFBundleGetPlugInData(plugIn)->_factories, idx);
239      }
240  }