Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
35 kB · 1322 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323/* * Copyright(c) 2000-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */
/* * Modification History * * February 16, 2004 Allan Nathanson <ajn@apple.com> * - add preference notification APIs * * June 1, 2001 Allan Nathanson <ajn@apple.com> * - public API conversion * * November 9, 2000 Allan Nathanson <ajn@apple.com> * - initial revision */
#include <TargetConditionals.h>#include <fcntl.h>#include <pthread.h>#include <sandbox.h>#include <unistd.h>#include <sys/errno.h>#include <sys/cdefs.h>#include <dispatch/dispatch.h>
#include "SCPreferencesInternal.h"#include "SCD.h"#include "SCHelper_client.h"#include "dy_framework.h"
const AuthorizationRef kSCPreferencesUseEntitlementAuthorization = (AuthorizationRef)CFSTR("UseEntitlement");
__private_extern__ os_log_t__log_SCPreferences(void){ static os_log_t log = NULL;
if (log == NULL) { log = os_log_create("com.apple.SystemConfiguration", "SCPreferences"); }
return log;}
static __inline__ CFTypeRefisA_SCPreferences(CFTypeRef obj){ return (isA_CFType(obj, SCPreferencesGetTypeID()));}
static CFStringRef__SCPreferencesCopyDescription(CFTypeRef cf) { CFAllocatorRef allocator = CFGetAllocator(cf); SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf; CFMutableStringRef result;
result = CFStringCreateMutable(allocator, 0); CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator); CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name); CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID != NULL ? prefsPrivate->prefsID : CFSTR("[default]")); CFStringAppendFormat(result, NULL, CFSTR(", path = %s"), prefsPrivate->newPath != NULL ? prefsPrivate->newPath : prefsPrivate->path); if (prefsPrivate->accessed) { CFStringAppendFormat(result, NULL, CFSTR(", accessed")); } if (prefsPrivate->changed) { CFStringAppendFormat(result, NULL, CFSTR(", changed")); } if (prefsPrivate->locked) { CFStringAppendFormat(result, NULL, CFSTR(", locked")); } if (prefsPrivate->helper_port != MACH_PORT_NULL) { CFStringAppendFormat(result, NULL, CFSTR(", helper port = 0x%x"), prefsPrivate->helper_port); } CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;}
static void__SCPreferencesDeallocate(CFTypeRef cf){ SCPreferencesRef prefs = (SCPreferencesRef)cf; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
SC_log(LOG_DEBUG, "release %@", prefsPrivate);
if (prefsPrivate->locked) { __SCPreferencesUpdateLockedState(prefs, FALSE); }
/* release resources */
pthread_mutex_destroy(&prefsPrivate->lock);
if (prefsPrivate->name) CFRelease(prefsPrivate->name); if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID); if (prefsPrivate->options) CFRelease(prefsPrivate->options); if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path); if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath); if (prefsPrivate->lockFD != -1) { if (prefsPrivate->lockPath != NULL) { unlink(prefsPrivate->lockPath); } close(prefsPrivate->lockFD); } if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath); if (prefsPrivate->signature) CFRelease(prefsPrivate->signature); if (prefsPrivate->sessionNoO_EXLOCK != NULL) { CFRelease(prefsPrivate->sessionNoO_EXLOCK); } if (prefsPrivate->sessionKeyLock) CFRelease(prefsPrivate->sessionKeyLock); if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit); if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply); if (prefsPrivate->rlsContext.release != NULL) { (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info); } if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs); if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData); if (prefsPrivate->helper_port != MACH_PORT_NULL) { (void) _SCHelperExec(prefsPrivate->helper_port, SCHELPER_MSG_PREFS_CLOSE, NULL, NULL, NULL); _SCHelperClose(&prefsPrivate->helper_port); }
return;}
static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCPreferencesClass = { 0, // version "SCPreferences", // className NULL, // init NULL, // copy __SCPreferencesDeallocate, // dealloc NULL, // equal NULL, // hash NULL, // copyFormattingDesc __SCPreferencesCopyDescription // copyDebugDesc};
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static void__SCPreferencesInitialize(void) { /* register with CoreFoundation */ __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass); return;}
static SCPreferencesPrivateRef__SCPreferencesCreatePrivate(CFAllocatorRef allocator){ SCPreferencesPrivateRef prefsPrivate; uint32_t size;
/* initialize runtime */ pthread_once(&initialized, __SCPreferencesInitialize);
/* allocate prefs session */ size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase); prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCPreferencesTypeID, size, NULL); if (prefsPrivate == NULL) { return NULL; }
/* initialize non-zero/NULL members */ pthread_mutex_init(&prefsPrivate->lock, NULL); prefsPrivate->lockFD = -1; prefsPrivate->isRoot = (geteuid() == 0);
return prefsPrivate;}
__private_extern__ Boolean__SCPreferencesCreate_helper(SCPreferencesRef prefs){ CFDataRef data = NULL; CFMutableDictionaryRef info; CFNumberRef num; Boolean ok; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; uint32_t status = kSCStatusOK; CFStringRef str; uint32_t pid = getpid();
// start helper ok = _SCHelperOpen(prefsPrivate->authorizationData, &prefsPrivate->helper_port); if (!ok) { goto fail; }
// create a dictionary of information to pass to the helper info = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// save prefsID if (prefsPrivate->prefsID != NULL) { CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID); }
// save options if (prefsPrivate->options != NULL) { CFDictionarySetValue(info, CFSTR("options"), prefsPrivate->options); }
// save preferences session "name" CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name);
// save PID num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid); CFDictionarySetValue(info, CFSTR("PID"), num); CFRelease(num);
// save process name str = CFStringCreateWithCString(NULL, getprogname(), kCFStringEncodingUTF8); CFDictionarySetValue(info, CFSTR("PROC_NAME"), str); CFRelease(str);
// serialize the info ok = _SCSerialize(info, &data, NULL, NULL); CFRelease(info); if (data == NULL || !ok) { goto fail; }
// have the helper "open" the prefs ok = _SCHelperExec(prefsPrivate->helper_port, SCHELPER_MSG_PREFS_OPEN, data, &status, NULL); if (data != NULL) CFRelease(data); if (!ok) { goto fail; }
if (status != kSCStatusOK) { goto error; }
return TRUE;
fail :
// close helper if (prefsPrivate->helper_port != MACH_PORT_NULL) { _SCHelperClose(&prefsPrivate->helper_port); }
status = kSCStatusAccessError;
error :
// return error _SCErrorSet(status); return FALSE;}
static Boolean__SCPreferencesAccess_helper(SCPreferencesRef prefs){ Boolean ok; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; CFDictionaryRef serverDict = NULL; CFDictionaryRef serverPrefs = NULL; CFDictionaryRef serverSignature = NULL; uint32_t status = kSCStatusOK; CFDataRef reply = NULL;
if (prefsPrivate->helper_port == MACH_PORT_NULL) { ok = __SCPreferencesCreate_helper(prefs); if (!ok) { return FALSE; } }
// have the helper "access" the prefs ok = _SCHelperExec(prefsPrivate->helper_port, SCHELPER_MSG_PREFS_ACCESS, NULL, &status, &reply); if (!ok) { goto fail; }
if (status != kSCStatusOK) { goto error; }
if (reply == NULL) { goto fail; }
ok = _SCUnserialize((CFPropertyListRef *)&serverDict, reply, NULL, 0); CFRelease(reply); if (!ok) { goto fail; }
if (isA_CFDictionary(serverDict)) { serverPrefs = CFDictionaryGetValue(serverDict, CFSTR("preferences")); serverPrefs = isA_CFDictionary(serverPrefs);
serverSignature = CFDictionaryGetValue(serverDict, CFSTR("signature")); serverSignature = isA_CFData(serverSignature); }
if ((serverPrefs == NULL) || (serverSignature == NULL)) { if (serverDict != NULL) CFRelease(serverDict); goto fail; }
prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, serverPrefs); prefsPrivate->signature = CFRetain(serverSignature); prefsPrivate->accessed = TRUE; CFRelease(serverDict);
return TRUE;
fail :
// close helper if (prefsPrivate->helper_port != MACH_PORT_NULL) { _SCHelperClose(&prefsPrivate->helper_port); }
status = kSCStatusAccessError;
error :
// return error _SCErrorSet(status); return FALSE;}
static SCPreferencesPrivateRef__SCPreferencesCreate(CFAllocatorRef allocator, CFStringRef name, CFStringRef prefsID, CFDataRef authorizationData, CFDictionaryRef options){ SCPreferencesPrivateRef prefsPrivate; int sc_status = kSCStatusOK;
/* * allocate and initialize a new prefs session */ prefsPrivate = __SCPreferencesCreatePrivate(allocator); if (prefsPrivate == NULL) { return NULL; }
prefsPrivate->name = CFStringCreateCopy(allocator, name); if (prefsID != NULL) { prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID); } if (authorizationData != NULL) { prefsPrivate->authorizationData = CFRetain(authorizationData); } if (options != NULL) { prefsPrivate->options = CFDictionaryCreateCopy(allocator, options); }
retry :
/* * convert prefsID to path */ prefsPrivate->path = __SCPreferencesPath(allocator, prefsID, (prefsPrivate->newPath == NULL)); if (prefsPrivate->path == NULL) { sc_status = kSCStatusFailed; goto error; }
if (access(prefsPrivate->path, R_OK) == 0) { goto done; }
switch (errno) { case ENOENT : /* no prefs file */ if ((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/"))) { /* if default preference ID or relative path */ if (prefsPrivate->newPath == NULL) { /* * we've looked in the "new" prefs directory * without success. Save the "new" path and * look in the "old" prefs directory. */ prefsPrivate->newPath = prefsPrivate->path; goto retry; } else { /* * we've looked in both the "new" and "old" * prefs directories without success. Use * the "new" path. */ CFAllocatorDeallocate(NULL, prefsPrivate->path); prefsPrivate->path = prefsPrivate->newPath; prefsPrivate->newPath = NULL; } }
/* no preference data, start fresh */ sc_status = kSCStatusNoConfigFile; goto done; case EPERM : case EACCES : if (prefsPrivate->authorizationData != NULL) { /* no problem, we'll be using the helper */ goto done; }
SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno)); sc_status = kSCStatusAccessError; break; default : SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno)); sc_status = kSCStatusFailed; break; }
error:
CFRelease(prefsPrivate); _SCErrorSet(sc_status); return NULL;
done :
/* all OK */ _SCErrorSet(sc_status); return prefsPrivate;}
__private_extern__ void__SCPreferencesAccess(SCPreferencesRef prefs){ CFAllocatorRef allocator = CFGetAllocator(prefs); int fd = -1; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; struct stat statBuf;
if (prefsPrivate->accessed) { // if preference data has already been accessed return; }
if (access(prefsPrivate->path, R_OK) == 0) { fd = open(prefsPrivate->path, O_RDONLY, 0644); } else { fd = -1; } if (fd != -1) { // create signature if (fstat(fd, &statBuf) == -1) { SC_log(LOG_NOTICE, "fstat() failed: %s", strerror(errno)); memset(&statBuf, 0, sizeof(statBuf)); } } else { switch (errno) { case ENOENT : /* no preference data, start fresh */ break; case EPERM : case EACCES : if (prefsPrivate->authorizationData != NULL) { if (__SCPreferencesAccess_helper(prefs)) { goto done; } else { SC_log(LOG_NOTICE, "__SCPreferencesAccess_helper() failed: %s", SCErrorString(SCError())); } break; } // fall through default : SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno)); break; } memset(&statBuf, 0, sizeof(statBuf)); }
if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature); prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
if (statBuf.st_size > 0) { CFDictionaryRef dict; CFErrorRef error = NULL; CFMutableDataRef xmlData;
/* * extract property list */ xmlData = CFDataCreateMutable(allocator, (CFIndex)statBuf.st_size); CFDataSetLength(xmlData, (CFIndex)statBuf.st_size); if (read(fd, (void *)CFDataGetBytePtr(xmlData), (CFIndex)statBuf.st_size) != (CFIndex)statBuf.st_size) { /* corrupt prefs file, start fresh */ SC_log(LOG_INFO, "read(): could not load preference data"); CFRelease(xmlData); xmlData = NULL; goto done; }
/* * load preferences */ dict = CFPropertyListCreateWithData(allocator, xmlData, kCFPropertyListImmutable, NULL, &error); CFRelease(xmlData); if (dict == NULL) { /* corrupt prefs file, start fresh */ if (error != NULL) { SC_log(LOG_NOTICE, "CFPropertyListCreateWithData(): %@", error); CFRelease(error); } goto done; }
/* * make sure that we've got a dictionary */ if (!isA_CFDictionary(dict)) { /* corrupt prefs file, start fresh */ SC_log(LOG_INFO, "CFGetTypeID(): not a dictionary"); CFRelease(dict); goto done; }
prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict); CFRelease(dict); }
done :
if (fd != -1) { (void) close(fd); }
if (prefsPrivate->prefs == NULL) { /* * new file, create empty preferences */// SC_log(LOG_INFO, "creating new preferences file"); prefsPrivate->prefs = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); prefsPrivate->changed = TRUE; }
SC_log(LOG_DEBUG, "SCPreferences() access: %s, size=%lld", prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path, __SCPreferencesPrefsSize(prefs));
prefsPrivate->accessed = TRUE; return;}
SCPreferencesRefSCPreferencesCreate(CFAllocatorRef allocator, CFStringRef name, CFStringRef prefsID){ SCPreferencesPrivateRef prefsPrivate;
prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, NULL, NULL); if (prefsPrivate != NULL) { SC_log(LOG_DEBUG, "create %@", prefsPrivate); }
return (SCPreferencesRef)prefsPrivate;}
SCPreferencesRefSCPreferencesCreateWithAuthorization(CFAllocatorRef allocator, CFStringRef name, CFStringRef prefsID, AuthorizationRef authorization){ SCPreferencesRef prefs;
#if !TARGET_OS_IPHONE if (authorization == NULL) { authorization = kSCPreferencesUseEntitlementAuthorization; }#else // !TARGET_OS_IPHONE authorization = kSCPreferencesUseEntitlementAuthorization;#endif // !TARGET_OS_IPHONE
prefs = SCPreferencesCreateWithOptions(allocator, name, prefsID, authorization, NULL); return prefs;}
SCPreferencesRefSCPreferencesCreateWithOptions(CFAllocatorRef allocator, CFStringRef name, CFStringRef prefsID, AuthorizationRef authorization, CFDictionaryRef options){ CFDataRef authorizationData = NULL; SCPreferencesPrivateRef prefsPrivate;
if (options != NULL) { if (!isA_CFDictionary(options)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } }
if (authorization != NULL) { CFMutableDictionaryRef authorizationDict; CFBundleRef bundle; CFStringRef bundleID = NULL;
authorizationDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);#if !TARGET_OS_IPHONE if (authorization != kSCPreferencesUseEntitlementAuthorization) { CFDataRef data; AuthorizationExternalForm extForm; OSStatus os_status;
os_status = AuthorizationMakeExternalForm(authorization, &extForm); if (os_status != errAuthorizationSuccess) { SC_log(LOG_INFO, "AuthorizationMakeExternalForm() failed"); _SCErrorSet(kSCStatusInvalidArgument); CFRelease(authorizationDict); return NULL; }
data = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes)); CFDictionaryAddValue(authorizationDict, kSCHelperAuthAuthorization, data); CFRelease(data); }#endif // !TARGET_OS_IPHONE
/* get the application/executable/bundle name */ bundle = CFBundleGetMainBundle(); if (bundle != NULL) { bundleID = CFBundleGetIdentifier(bundle); if (bundleID != NULL) { CFRetain(bundleID); } else { CFURLRef url;
url = CFBundleCopyExecutableURL(bundle); if (url != NULL) { bundleID = CFURLCopyPath(url); CFRelease(url); } }
if (bundleID != NULL) { if (CFEqual(bundleID, CFSTR("/"))) { CFRelease(bundleID); bundleID = NULL; } } } if (bundleID == NULL) { bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown(%d)"), getpid()); } CFDictionaryAddValue(authorizationDict, kSCHelperAuthCallerInfo, bundleID); CFRelease(bundleID);
if (authorizationDict != NULL) { (void) _SCSerialize((CFPropertyListRef)authorizationDict, &authorizationData, NULL, NULL); CFRelease(authorizationDict); } }
prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData, options); if (prefsPrivate != NULL) { const char *astr = ""; const char *ostr = "";
if (options != NULL) { ostr = "options"; }
if (authorization != NULL) { if (authorization == kSCPreferencesUseEntitlementAuthorization) { astr = "entitlement"; } else { astr = "authorization"; } }
SC_log(LOG_DEBUG, "create w/%s%s%s %@", ostr, ((ostr != "") && (astr != "")) ? " + " : "", astr, prefsPrivate); }
if (authorizationData != NULL) CFRelease(authorizationData);
return (SCPreferencesRef)prefsPrivate;}
CFTypeIDSCPreferencesGetTypeID(void) { pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */ return __kSCPreferencesTypeID;}
static voidprefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info){#pragma unused(store) void *context_info; void (*context_release)(const void *); CFIndex i; CFIndex n; SCPreferencesNotification notify = 0; SCPreferencesRef prefs = (SCPreferencesRef)info; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; SCPreferencesCallBack rlsFunction;
n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0; for (i = 0; i < n; i++) { CFStringRef key;
key = CFArrayGetValueAtIndex(changedKeys, i);
// check if "commit" if (CFEqual(key, prefsPrivate->sessionKeyCommit)) { // if preferences have been saved notify |= kSCPreferencesNotificationCommit; continue; }
// check if "apply" if (CFEqual(key, prefsPrivate->sessionKeyApply)) { // if stored preferences should be applied to current configuration notify |= kSCPreferencesNotificationApply; continue; } }
if (notify == 0) { // if no changes return; }
pthread_mutex_lock(&prefsPrivate->lock);
/* callout */ rlsFunction = prefsPrivate->rlsFunction; if (prefsPrivate->rlsContext.retain != NULL) { context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info); context_release = prefsPrivate->rlsContext.release; } else { context_info = prefsPrivate->rlsContext.info; context_release = NULL; }
pthread_mutex_unlock(&prefsPrivate->lock);
if (rlsFunction != NULL) { SC_log(LOG_DEBUG, "exec SCPreferences callout: %s%s%s", ((notify & kSCPreferencesNotificationCommit) != 0) ? "commit" : "", (((notify & kSCPreferencesNotificationCommit) != 0) && ((notify & kSCPreferencesNotificationApply ) != 0)) ? ", " : "", ((notify & kSCPreferencesNotificationApply) != 0) ? "apply" : ""); (*rlsFunction)(prefs, notify, context_info); }
if (context_release != NULL) { (*context_release)(context_info); }
return;}
__private_extern__ void__SCPreferencesAddSessionKeys(SCPreferencesRef prefs){ SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
/* create the session "commit" key */ if (prefsPrivate->sessionKeyCommit == NULL) { prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL, prefsPrivate->prefsID, kSCPreferencesKeyCommit); }
/* create the session "apply" key */ if (prefsPrivate->sessionKeyApply == NULL) { prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL, prefsPrivate->prefsID, kSCPreferencesKeyApply); }
return;}
__private_extern__ Boolean__SCPreferencesAddSession(SCPreferencesRef prefs){ CFAllocatorRef allocator = CFGetAllocator(prefs); SCDynamicStoreContext context = { 0 , (void *)prefs , CFRetain , CFRelease , CFCopyDescription }; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefsPrivate->sessionRefcnt == 0) { /* establish a dynamic store session */ prefsPrivate->session = SCDynamicStoreCreate(allocator, prefsPrivate->name, prefsNotify, &context); if (prefsPrivate->session == NULL) { SC_log(LOG_INFO, "SCDynamicStoreCreate() failed"); return FALSE; }
SC_log(LOG_DEBUG, "added SCDynamicStore session (for prefs)"); }
prefsPrivate->sessionRefcnt++; return TRUE;}
__private_extern__ void__SCPreferencesRemoveSession(SCPreferencesRef prefs){ SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefsPrivate->sessionRefcnt > 0) { if (--prefsPrivate->sessionRefcnt == 0) { CFRelease(prefsPrivate->session); prefsPrivate->session = NULL;
SC_log(LOG_DEBUG, "removed SCDynamicStore session (for prefs)"); } }
return;}
static voidappendLockedPreferences(const void *key, const void *value, void *context){#pragma unused(key) CFMutableStringRef str = (CFMutableStringRef)context;
CFStringAppendFormat(str, NULL, CFSTR("%s%@"), (CFStringGetLength(str) > 0) ? "\n" : "", value); return;}
__private_extern__ void__SCPreferencesUpdateLockedState(SCPreferencesRef prefs, Boolean locked){ static dispatch_queue_t lockedQueue; static CFMutableDictionaryRef lockedState; static dispatch_once_t once; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
dispatch_once(&once, ^{ os_state_block_t state_block;
lockedQueue = dispatch_queue_create("SCPreferences locked state queue", NULL);
lockedState = CFDictionaryCreateMutable(NULL, 0, NULL, // NO retain/release &kCFTypeDictionaryValueCallBacks);
state_block = ^os_state_data_t(os_state_hints_t hints) {#pragma unused(hints) CFDataRef data = NULL; Boolean ok; os_state_data_t state_data; size_t state_data_size; CFIndex state_len; CFMutableStringRef str;
if (CFDictionaryGetCount(lockedState) == 0) { // if no locked preferences return NULL; }
str = CFStringCreateMutable(NULL, 0); CFDictionaryApplyFunction(lockedState, appendLockedPreferences, str); ok = _SCSerialize(str, &data, NULL, NULL); CFRelease(str);
state_len = (ok && (data != NULL)) ? CFDataGetLength(data) : 0; state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len); if (state_data_size > MAX_STATEDUMP_SIZE) { SC_log(LOG_ERR, "locked SCPreferences : state data too large (%zd > %zd)", state_data_size, (size_t)MAX_STATEDUMP_SIZE); if (data != NULL) CFRelease(data); return NULL; }
state_data = calloc(1, state_data_size); if (state_data == NULL) { SC_log(LOG_ERR, "locked SCPreferences: could not allocate state data"); if (data != NULL) CFRelease(data); return NULL; }
state_data->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT; state_data->osd_data_size = (uint32_t)state_len; strlcpy(state_data->osd_title, "open/locked SCPreferences", sizeof(state_data->osd_title)); if (state_len > 0) { memcpy(state_data->osd_data, CFDataGetBytePtr(data), state_len); } if (data != NULL) CFRelease(data);
return state_data; };
(void) os_state_add_handler(lockedQueue, state_block); });
// update the locked state prefsPrivate->locked = locked;
// add (or update) the locked preferences dispatch_sync(lockedQueue, ^{ if (locked) { CFStringRef str;
str = CFCopyDescription(prefs); CFDictionarySetValue(lockedState, prefs, str); CFRelease(str); } else { CFDictionaryRemoveValue(lockedState, prefs); } });
return;}
BooleanSCPreferencesSetCallback(SCPreferencesRef prefs, SCPreferencesCallBack callout, SCPreferencesContext *context){ SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (!isA_SCPreferences(prefs)) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoPrefsSession); return FALSE; }
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->rlsContext.release != NULL) { /* let go of the current context */ (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info); }
prefsPrivate->rlsFunction = callout; prefsPrivate->rlsContext.info = NULL; prefsPrivate->rlsContext.retain = NULL; prefsPrivate->rlsContext.release = NULL; prefsPrivate->rlsContext.copyDescription = NULL; if (context != NULL) { memcpy(&prefsPrivate->rlsContext, context, sizeof(SCPreferencesContext)); if (context->retain != NULL) { prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info); } }
pthread_mutex_unlock(&prefsPrivate->lock);
return TRUE;}
static Boolean__SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs, CFRunLoopRef runLoop, CFStringRef runLoopMode, dispatch_queue_t queue){ Boolean ok = FALSE; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
pthread_mutex_lock(&prefsPrivate->lock);
if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (!prefsPrivate->scheduled) { CFMutableArrayRef keys;
// add SCDynamicStore session (for notifications) ... and hold a 'prefs' reference if (prefsPrivate->session == NULL) { ok = __SCPreferencesAddSession(prefs); if (!ok) { goto done; } assert(prefsPrivate->session != NULL); }
// add SCDynamicStore "keys" __SCPreferencesAddSessionKeys(prefs);
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit); CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply); (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL); CFRelease(keys);
if (runLoop != NULL) { prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0); prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); }
SC_log(LOG_DEBUG, "scheduled");
prefsPrivate->scheduled = TRUE; }
if (queue != NULL) { ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue); if (!ok) { prefsPrivate->scheduled = FALSE; (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL); __SCPreferencesRemoveSession(prefs); goto done; }
prefsPrivate->dispatchQueue = queue; dispatch_retain(prefsPrivate->dispatchQueue); } else { if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) { /* * if we do not already have notifications scheduled with * this runLoop / runLoopMode */ CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode); }
_SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList); }
ok = TRUE;
done :
pthread_mutex_unlock(&prefsPrivate->lock); return ok;}
static Boolean__SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs, CFRunLoopRef runLoop, CFStringRef runLoopMode){ SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; CFIndex n = 0; Boolean ok = FALSE;
pthread_mutex_lock(&prefsPrivate->lock);
if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not) _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not) ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue) _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (runLoop == NULL) { SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL); dispatch_release(prefsPrivate->dispatchQueue); prefsPrivate->dispatchQueue = NULL; } else { if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) { // if not currently scheduled on this runLoop / runLoopMode _SCErrorSet(kSCStatusInvalidArgument); goto done; }
n = CFArrayGetCount(prefsPrivate->rlList); if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) { /* * if we are no longer scheduled to receive notifications for * this runLoop / runLoopMode */ CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
if (n == 0) { // if *all* notifications have been unscheduled CFRelease(prefsPrivate->rlList); prefsPrivate->rlList = NULL; CFRunLoopSourceInvalidate(prefsPrivate->rls); CFRelease(prefsPrivate->rls); prefsPrivate->rls = NULL; } } }
if (n == 0) { CFArrayRef changedKeys;
SC_log(LOG_DEBUG, "unscheduled");
// if *all* notifications have been unscheduled prefsPrivate->scheduled = FALSE;
// no need to track changes (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
// clear out any pending notifications changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session); if (changedKeys != NULL) { CFRelease(changedKeys); }
// remove SCDynamicStore session, release 'prefs' reference __SCPreferencesRemoveSession(prefs); }
ok = TRUE;
done :
pthread_mutex_unlock(&prefsPrivate->lock); return ok;}
BooleanSCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs, CFRunLoopRef runLoop, CFStringRef runLoopMode){ if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL);}
BooleanSCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs, CFRunLoopRef runLoop, CFStringRef runLoopMode){ if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode);}
BooleanSCPreferencesSetDispatchQueue(SCPreferencesRef prefs, dispatch_queue_t queue){ Boolean ok = FALSE;
if (!isA_SCPreferences(prefs)) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoPrefsSession); return FALSE; }
if (queue != NULL) { ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue); } else { ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL); }
return ok;}
static void__SCPreferencesSynchronize_helper(SCPreferencesRef prefs){ Boolean ok; SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; uint32_t status = kSCStatusOK;
if (prefsPrivate->helper_port == MACH_PORT_NULL) { // if no helper return; }
// have the helper "synchronize" the prefs ok = _SCHelperExec(prefsPrivate->helper_port, SCHELPER_MSG_PREFS_SYNCHRONIZE, NULL, &status, NULL); if (!ok) { // close helper if (prefsPrivate->helper_port != MACH_PORT_NULL) { _SCHelperClose(&prefsPrivate->helper_port); } }
return;}
voidSCPreferencesSynchronize(SCPreferencesRef prefs){ SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (!isA_SCPreferences(prefs)) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoPrefsSession); return; }
SC_log(LOG_DEBUG, "SCPreferences() synchronize: %s", prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
if (prefsPrivate->authorizationData != NULL) { __SCPreferencesSynchronize_helper(prefs); } if (prefsPrivate->prefs != NULL) { CFRelease(prefsPrivate->prefs); prefsPrivate->prefs = NULL; } if (prefsPrivate->signature != NULL) { CFRelease(prefsPrivate->signature); prefsPrivate->signature = NULL; } prefsPrivate->accessed = FALSE; prefsPrivate->changed = FALSE;
return;}