#include "configd.h"
#include "session.h"
#include "pattern.h"
__private_extern__
int
__SCDynamicStoreSetValue(SCDynamicStoreRef store, CFStringRef key, CFDataRef value, Boolean internal)
{
CFDictionaryRef dict;
serverSessionRef mySession;
CFMutableDictionaryRef newDict;
Boolean newEntry = FALSE;
int sc_status = kSCStatusOK;
CFStringRef sessionKey;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
CFStringRef storeSessionKey;
SC_trace("%s%s : %5d : %@",
internal ? "*set " : "set ",
storePrivate->useSessionKeys ? "t " : " ",
storePrivate->server,
key);
dict = CFDictionaryGetValue(storeData, key);
if (dict) {
newDict = CFDictionaryCreateMutableCopy(NULL,
0,
dict);
} else {
newDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
newEntry = !CFDictionaryContainsKey(newDict, kSCDData);
CFDictionarySetValue(newDict, kSCDData, value);
sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), storePrivate->server);
if (storePrivate->useSessionKeys) {
if (newEntry) {
mySession = getSession(storePrivate->server);
if ((mySession->sessionKeys == NULL) ||
!CFArrayContainsValue(mySession->sessionKeys,
CFRangeMake(0, CFArrayGetCount(mySession->sessionKeys)),
key)) {
if (mySession->sessionKeys == NULL) {
mySession->sessionKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(mySession->sessionKeys, key);
}
CFDictionarySetValue(newDict, kSCDSession, sessionKey);
} else {
dict = CFDictionaryGetValue(storeData, key);
if (!CFDictionaryGetValueIfPresent(dict, kSCDSession, (void *)&storeSessionKey) ||
!CFEqual(sessionKey, storeSessionKey)) {
sc_status = kSCStatusKeyExists;
CFRelease(sessionKey);
CFRelease(newDict);
goto done;
}
}
} else {
if (!newEntry &&
CFDictionaryGetValueIfPresent(newDict, kSCDSession, (void *)&storeSessionKey) &&
!CFEqual(sessionKey, storeSessionKey)) {
CFStringRef removedKey;
CFDictionaryRemoveValue(newDict, kSCDSession);
removedKey = CFStringCreateWithFormat(NULL, 0, CFSTR("%@:%@"), storeSessionKey, key);
CFSetAddValue(removedSessionKeys, removedKey);
CFRelease(removedKey);
}
}
CFRelease(sessionKey);
CFDictionarySetValue(storeData, key, newDict);
CFRelease(newDict);
if (newEntry) {
if (CFSetContainsValue(deferredRemovals, key)) {
CFSetRemoveValue(deferredRemovals, key);
} else {
patternAddKey(key);
}
}
CFSetAddValue(changedKeys, key);
done :
if (!internal) {
__SCDynamicStorePush();
}
return sc_status;
}
__private_extern__
kern_return_t
_configset(mach_port_t server,
xmlData_t keyRef,
mach_msg_type_number_t keyLen,
xmlData_t dataRef,
mach_msg_type_number_t dataLen,
int oldInstance,
int *newInstance,
int *sc_status,
audit_token_t audit_token)
{
#pragma unused(oldInstance)
CFDataRef data = NULL;
CFStringRef key = NULL;
serverSessionRef mySession;
*newInstance = 0;
*sc_status = kSCStatusOK;
if (!_SCUnserializeString(&key, NULL, (void *)keyRef, keyLen)) {
*sc_status = kSCStatusFailed;
}
if (!_SCUnserializeData(&data, (void *)dataRef, dataLen)) {
*sc_status = kSCStatusFailed;
}
if (*sc_status != kSCStatusOK) {
goto done;
}
if (!isA_CFString(key)) {
*sc_status = kSCStatusInvalidArgument;
goto done;
}
mySession = getSession(server);
if (mySession == NULL) {
mySession = tempSession(server, CFSTR("SCDynamicStoreSetValue"), audit_token);
if (mySession == NULL) {
*sc_status = kSCStatusNoStoreSession;
goto done;
}
}
if (!hasWriteAccess(mySession, "set", key)) {
*sc_status = kSCStatusAccessError;
goto done;
}
*sc_status = __SCDynamicStoreSetValue(mySession->store, key, data, FALSE);
done :
if (key) CFRelease(key);
if (data) CFRelease(data);
return KERN_SUCCESS;
}
static void
setSpecificKey(const void *key, const void *value, void *context)
{
CFStringRef k = (CFStringRef)key;
CFDataRef v = (CFDataRef)value;
SCDynamicStoreRef store = (SCDynamicStoreRef)context;
if (!isA_CFString(k)) {
return;
}
if (!isA_CFData(v)) {
return;
}
(void) __SCDynamicStoreSetValue(store, k, v, TRUE);
return;
}
static void
removeSpecificKey(const void *value, void *context)
{
CFStringRef k = (CFStringRef)value;
SCDynamicStoreRef store = (SCDynamicStoreRef)context;
if (!isA_CFString(k)) {
return;
}
(void) __SCDynamicStoreRemoveValue(store, k, TRUE);
return;
}
static void
notifySpecificKey(const void *value, void *context)
{
CFStringRef k = (CFStringRef)value;
SCDynamicStoreRef store = (SCDynamicStoreRef)context;
if (!isA_CFString(k)) {
return;
}
(void) __SCDynamicStoreNotifyValue(store, k, TRUE);
return;
}
__private_extern__
int
__SCDynamicStoreSetMultiple(SCDynamicStoreRef store, CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFArrayRef keysToNotify)
{
int sc_status = kSCStatusOK;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
SC_trace("set m : %5d : %ld set, %ld remove, %ld notify",
storePrivate->server,
keysToSet ? CFDictionaryGetCount(keysToSet) : 0,
keysToRemove ? CFArrayGetCount (keysToRemove) : 0,
keysToNotify ? CFArrayGetCount (keysToNotify) : 0);
if (keysToSet) {
CFDictionaryApplyFunction(keysToSet,
setSpecificKey,
(void *)store);
}
if (keysToRemove) {
CFArrayApplyFunction(keysToRemove,
CFRangeMake(0, CFArrayGetCount(keysToRemove)),
removeSpecificKey,
(void *)store);
}
if (keysToNotify) {
CFArrayApplyFunction(keysToNotify,
CFRangeMake(0, CFArrayGetCount(keysToNotify)),
notifySpecificKey,
(void *)store);
}
__SCDynamicStorePush();
return sc_status;
}
#define N_QUICK 32
__private_extern__
kern_return_t
_configset_m(mach_port_t server,
xmlData_t dictRef,
mach_msg_type_number_t dictLen,
xmlData_t removeRef,
mach_msg_type_number_t removeLen,
xmlData_t notifyRef,
mach_msg_type_number_t notifyLen,
int *sc_status,
audit_token_t audit_token)
{
CFDictionaryRef dict = NULL;
serverSessionRef mySession;
CFArrayRef notify = NULL;
CFArrayRef remove = NULL;
*sc_status = kSCStatusOK;
if ((dictRef != NULL) && (dictLen > 0)) {
if (!_SCUnserialize((CFPropertyListRef *)&dict, NULL, (void *)dictRef, dictLen)) {
*sc_status = kSCStatusFailed;
}
}
if ((removeRef != NULL) && (removeLen > 0)) {
if (!_SCUnserialize((CFPropertyListRef *)&remove, NULL, (void *)removeRef, removeLen)) {
*sc_status = kSCStatusFailed;
}
}
if ((notifyRef != NULL) && (notifyLen > 0)) {
if (!_SCUnserialize((CFPropertyListRef *)¬ify, NULL, (void *)notifyRef, notifyLen)) {
*sc_status = kSCStatusFailed;
}
}
if (*sc_status != kSCStatusOK) {
goto done;
}
if ((dict != NULL) && !isA_CFDictionary(dict)) {
*sc_status = kSCStatusInvalidArgument;
goto done;
}
if ((remove != NULL) && !isA_CFArray(remove)) {
*sc_status = kSCStatusInvalidArgument;
goto done;
}
if ((notify != NULL) && !isA_CFArray(notify)) {
*sc_status = kSCStatusInvalidArgument;
goto done;
}
mySession = getSession(server);
if (mySession == NULL) {
mySession = tempSession(server, CFSTR("SCDynamicStoreSetMultiple"), audit_token);
if (mySession == NULL) {
*sc_status = kSCStatusNoStoreSession;
goto done;
}
}
if (dict != NULL) {
const void * keys_q[N_QUICK];
const void ** keys = keys_q;
CFIndex i;
CFIndex n;
Boolean writeOK = TRUE;
n = CFDictionaryGetCount(dict);
if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) {
keys = CFAllocatorAllocate(NULL, n * sizeof(CFStringRef), 0);
}
CFDictionaryGetKeysAndValues(dict, keys, NULL);
for (i = 0; i < n; i++) {
CFStringRef key;
key = (CFStringRef)keys[i];
if (!hasWriteAccess(mySession, "set (multiple)", key)) {
writeOK = FALSE;
break;
}
}
if (keys != keys_q) {
CFAllocatorDeallocate(NULL, keys);
}
if (!writeOK) {
*sc_status = kSCStatusAccessError;
goto done;
}
}
if (remove != NULL) {
CFIndex i;
CFIndex n = CFArrayGetCount(remove);
for (i = 0; i < n; i++) {
CFStringRef key;
key = CFArrayGetValueAtIndex(remove, i);
if (!hasWriteAccess(mySession, "set/remove (multiple)", key)) {
*sc_status = kSCStatusAccessError;
goto done;
}
}
}
if (notify != NULL) {
CFIndex i;
CFIndex n = CFArrayGetCount(notify);
for (i = 0; i < n; i++) {
CFStringRef key;
key = CFArrayGetValueAtIndex(notify, i);
if (!hasWriteAccess(mySession, "set/notify (multiple)", key)) {
*sc_status = kSCStatusAccessError;
goto done;
}
}
}
*sc_status = __SCDynamicStoreSetMultiple(mySession->store, dict, remove, notify);
done :
if (dict != NULL) CFRelease(dict);
if (remove != NULL) CFRelease(remove);
if (notify != NULL) CFRelease(notify);
return KERN_SUCCESS;
}