#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCPreferencesInternal.h"
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/errno.h>
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);
if (prefsPrivate->perUser) {
CFStringAppendFormat(result, NULL, CFSTR(" (for user %@)"), prefsPrivate->user);
}
CFStringAppendFormat(result, NULL, CFSTR(", path = %s"),
prefsPrivate->newPath ? 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"));
}
CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;
}
static void
__SCPreferencesDeallocate(CFTypeRef cf)
{
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
pthread_mutex_destroy(&prefsPrivate->lock);
if (prefsPrivate->name) CFRelease(prefsPrivate->name);
if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID);
if (prefsPrivate->user) CFRelease(prefsPrivate->user);
if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path);
if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath);
if (prefsPrivate->signature) CFRelease(prefsPrivate->signature);
if (prefsPrivate->session) CFRelease(prefsPrivate->session);
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);
return;
}
static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCPreferencesClass = {
0, "SCPreferences", NULL, NULL, __SCPreferencesDeallocate, NULL, NULL, NULL, __SCPreferencesCopyDescription };
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static void
__SCPreferencesInitialize(void) {
__kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass);
return;
}
static SCPreferencesPrivateRef
__SCPreferencesCreatePrivate(CFAllocatorRef allocator)
{
SCPreferencesPrivateRef prefsPrivate;
uint32_t size;
pthread_once(&initialized, __SCPreferencesInitialize);
size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase);
prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
__kSCPreferencesTypeID,
size,
NULL);
if (prefsPrivate == NULL) {
return NULL;
}
pthread_mutex_init(&prefsPrivate->lock, NULL);
prefsPrivate->name = NULL;
prefsPrivate->prefsID = NULL;
prefsPrivate->perUser = FALSE;
prefsPrivate->user = NULL;
prefsPrivate->path = NULL;
prefsPrivate->newPath = NULL; prefsPrivate->signature = NULL;
prefsPrivate->session = NULL;
prefsPrivate->sessionKeyLock = NULL;
prefsPrivate->sessionKeyCommit = NULL;
prefsPrivate->sessionKeyApply = NULL;
prefsPrivate->rls = NULL;
prefsPrivate->rlsFunction = NULL;
prefsPrivate->rlsContext.info = NULL;
prefsPrivate->rlsContext.retain = NULL;
prefsPrivate->rlsContext.release = NULL;
prefsPrivate->rlsContext.copyDescription = NULL;
prefsPrivate->rlList = NULL;
prefsPrivate->prefs = NULL;
prefsPrivate->accessed = FALSE;
prefsPrivate->changed = FALSE;
prefsPrivate->locked = FALSE;
prefsPrivate->isRoot = (geteuid() == 0);
return prefsPrivate;
}
__private_extern__ SCPreferencesRef
__SCPreferencesCreate(CFAllocatorRef allocator,
CFStringRef name,
CFStringRef prefsID,
Boolean perUser,
CFStringRef user)
{
int fd = -1;
SCPreferencesPrivateRef prefsPrivate;
int sc_status = kSCStatusOK;
prefsPrivate = __SCPreferencesCreatePrivate(allocator);
if (prefsPrivate == NULL) {
return NULL;
}
retry :
prefsPrivate->path = __SCPreferencesPath(allocator,
prefsID,
perUser,
user,
(prefsPrivate->newPath == NULL));
if (prefsPrivate->path == NULL) {
sc_status = kSCStatusFailed;
goto error;
}
fd = open(prefsPrivate->path, O_RDONLY, 0644);
if (fd != -1) {
(void) close(fd);
} else {
switch (errno) {
case ENOENT :
if (!perUser &&
((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/")))) {
if (prefsPrivate->newPath == NULL) {
prefsPrivate->newPath = prefsPrivate->path;
goto retry;
} else {
CFAllocatorDeallocate(NULL, prefsPrivate->path);
prefsPrivate->path = prefsPrivate->newPath;
prefsPrivate->newPath = NULL;
}
}
goto done;
case EACCES :
sc_status = kSCStatusAccessError;
break;
default :
sc_status = kSCStatusFailed;
break;
}
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno));
goto error;
}
done :
prefsPrivate->name = CFStringCreateCopy(allocator, name);
if (prefsID != NULL) prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID);
prefsPrivate->perUser = perUser;
if (user != NULL) prefsPrivate->user = CFStringCreateCopy(allocator, user);
return (SCPreferencesRef)prefsPrivate;
error :
if (fd != -1) (void) close(fd);
CFRelease(prefsPrivate);
_SCErrorSet(sc_status);
return NULL;
}
__private_extern__ Boolean
__SCPreferencesAccess(SCPreferencesRef prefs)
{
CFAllocatorRef allocator = CFGetAllocator(prefs);
int fd = -1;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
int sc_status = kSCStatusOK;
struct stat statBuf;
if (prefsPrivate->accessed) {
return TRUE;
}
fd = open(prefsPrivate->path, O_RDONLY, 0644);
if (fd != -1) {
if (fstat(fd, &statBuf) == -1) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess fstat() failed: %s"), strerror(errno));
sc_status = kSCStatusFailed;
goto error;
}
} else {
switch (errno) {
case ENOENT :
bzero(&statBuf, sizeof(statBuf));
goto create_1;
case EACCES :
sc_status = kSCStatusAccessError;
break;
default :
sc_status = kSCStatusFailed;
break;
}
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess open() failed: %s"), strerror(errno));
goto error;
}
create_1 :
if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
if (statBuf.st_size > 0) {
CFDictionaryRef dict;
CFMutableDataRef xmlData;
CFStringRef xmlError;
xmlData = CFDataCreateMutable(allocator, statBuf.st_size);
CFDataSetLength(xmlData, statBuf.st_size);
if (read(fd, (void *)CFDataGetBytePtr(xmlData), statBuf.st_size) != statBuf.st_size) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess read(): could not load preference data."));
CFRelease(xmlData);
xmlData = NULL;
goto create_2;
}
dict = CFPropertyListCreateFromXMLData(allocator,
xmlData,
kCFPropertyListImmutable,
&xmlError);
CFRelease(xmlData);
if (dict == NULL) {
if (xmlError != NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR("__SCPreferencesAccess CFPropertyListCreateFromXMLData(): %@"),
xmlError);
CFRelease(xmlError);
}
goto create_2;
}
if (!isA_CFDictionary(dict)) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess CFGetTypeID(): not a dictionary."));
CFRelease(dict);
goto create_2;
}
prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict);
CFRelease(dict);
}
create_2 :
if (fd != -1) {
(void) close(fd);
fd = -1;
}
if (prefsPrivate->prefs == NULL) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess(): creating new dictionary."));
prefsPrivate->prefs = CFDictionaryCreateMutable(allocator,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
prefsPrivate->changed = TRUE;
}
prefsPrivate->accessed = TRUE;
return TRUE;
error :
if (fd != -1) (void) close(fd);
_SCErrorSet(sc_status);
return FALSE;
}
SCPreferencesRef
SCPreferencesCreate(CFAllocatorRef allocator,
CFStringRef name,
CFStringRef prefsID)
{
return __SCPreferencesCreate(allocator, name, prefsID, FALSE, NULL);
}
SCPreferencesRef
SCUserPreferencesCreate(CFAllocatorRef allocator,
CFStringRef name,
CFStringRef prefsID,
CFStringRef user)
{
return __SCPreferencesCreate(allocator, name, prefsID, TRUE, user);
}
CFTypeID
SCPreferencesGetTypeID(void) {
pthread_once(&initialized, __SCPreferencesInitialize);
return __kSCPreferencesTypeID;
}
static void
prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
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);
if (CFEqual(key, prefsPrivate->sessionKeyCommit)) {
notify |= kSCPreferencesNotificationCommit;
}
if (CFEqual(key, prefsPrivate->sessionKeyApply)) {
notify |= kSCPreferencesNotificationApply;
}
}
if (notify == 0) {
return;
}
pthread_mutex_lock(&prefsPrivate->lock);
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) {
(*rlsFunction)(prefs, notify, context_info);
}
if (context_release != NULL) {
(*context_release)(context_info);
}
return;
}
__private_extern__ Boolean
__SCPreferencesAddSession(SCPreferencesRef prefs)
{
CFAllocatorRef allocator = CFGetAllocator(prefs);
SCDynamicStoreContext context = { 0
, (void *)prefs
, NULL
, NULL
, NULL
};
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
prefsPrivate->session = SCDynamicStoreCreate(allocator,
CFSTR("SCPreferences"),
prefsNotify,
&context);
if (prefsPrivate->session == NULL) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed"));
return FALSE;
}
prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL,
prefsPrivate->prefsID,
prefsPrivate->perUser,
prefsPrivate->user,
kSCPreferencesKeyCommit);
prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL,
prefsPrivate->prefsID,
prefsPrivate->perUser,
prefsPrivate->user,
kSCPreferencesKeyApply);
return TRUE;
}
Boolean
SCPreferencesSetCallback(SCPreferencesRef prefs,
SCPreferencesCallBack callout,
SCPreferencesContext *context)
{
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->rlsContext.release != NULL) {
(*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) {
bcopy(context, &prefsPrivate->rlsContext, sizeof(SCPreferencesContext));
if (context->retain != NULL) {
prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
}
}
pthread_mutex_unlock(&prefsPrivate->lock);
return TRUE;
}
Boolean
SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->rls == NULL) {
CFMutableArrayRef keys;
if (prefsPrivate->session == NULL) {
__SCPreferencesAddSession(prefs);
}
CFRetain(prefs);
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit);
CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply);
(void)SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL);
CFRelease(keys);
prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0);
prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode);
}
_SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList);
pthread_mutex_unlock(&prefsPrivate->lock);
return TRUE;
}
Boolean
SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
CFIndex n;
Boolean ok = FALSE;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->rls == NULL) {
goto done;
}
if (!_SC_unschedule(NULL, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) {
goto done;
}
n = CFArrayGetCount(prefsPrivate->rlList);
if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
if (n == 0) {
CFArrayRef changedKeys;
CFRunLoopSourceInvalidate(prefsPrivate->rls);
CFRelease(prefsPrivate->rls);
prefsPrivate->rls = NULL;
CFRelease(prefsPrivate->rlList);
prefsPrivate->rlList = NULL;
CFRelease(prefs);
(void)SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
if (changedKeys != NULL) {
CFRelease(changedKeys);
}
}
}
ok = TRUE;
done :
pthread_mutex_unlock(&prefsPrivate->lock);
return ok;
}
void
SCPreferencesSynchronize(SCPreferencesRef prefs)
{
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return;
}
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;
}