#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCPreferencesInternal.h"
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/errno.h>
Boolean
SCPreferencesLock(SCPreferencesRef prefs, Boolean wait)
{
CFAllocatorRef allocator = CFGetAllocator(prefs);
CFArrayRef changes;
Boolean haveLock = FALSE;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
CFDateRef value = NULL;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (prefsPrivate->locked) {
_SCErrorSet(kSCStatusLocked);
return FALSE;
}
if (!prefsPrivate->isRoot) {
if (!prefsPrivate->perUser) {
_SCErrorSet(kSCStatusAccessError);
return FALSE;
} else {
goto perUser;
}
}
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->session == NULL) {
__SCPreferencesAddSession(prefs);
}
if (prefsPrivate->sessionKeyLock == NULL) {
prefsPrivate->sessionKeyLock = _SCPNotificationKey(allocator,
prefsPrivate->prefsID,
prefsPrivate->perUser,
prefsPrivate->user,
kSCPreferencesKeyLock);
}
pthread_mutex_unlock(&prefsPrivate->lock);
if (!SCDynamicStoreAddWatchedKey(prefsPrivate->session,
prefsPrivate->sessionKeyLock,
FALSE)) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCPreferencesLock SCDynamicStoreAddWatchedKey() failed"));
goto error;
}
value = CFDateCreate(allocator, CFAbsoluteTimeGetCurrent());
while (TRUE) {
CFArrayRef changedKeys;
if (SCDynamicStoreAddTemporaryValue(prefsPrivate->session,
prefsPrivate->sessionKeyLock,
value)) {
haveLock = TRUE;
goto done;
} else {
if (!wait) {
_SCErrorSet(kSCStatusPrefsBusy);
goto error;
}
}
if (!SCDynamicStoreNotifyWait(prefsPrivate->session)) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCPreferencesLock SCDynamicStoreNotifyWait() failed"));
goto error;
}
changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
if (!changedKeys) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCPreferencesLock SCDynamicStoreCopyNotifiedKeys() failed"));
goto error;
}
CFRelease(changedKeys);
}
done :
CFRelease(value);
value = NULL;
if (!SCDynamicStoreRemoveWatchedKey(prefsPrivate->session,
prefsPrivate->sessionKeyLock,
0)) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCPreferencesLock SCDynamicStoreRemoveWatchedKey() failed"));
goto error;
}
changes = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
if (!changes) {
SCLog(_sc_verbose, LOG_INFO, CFSTR("SCPreferencesLock SCDynamicStoreCopyNotifiedKeys() failed"));
goto error;
}
CFRelease(changes);
perUser:
if (prefsPrivate->accessed) {
CFDataRef currentSignature;
Boolean match;
struct stat statBuf;
if (stat(prefsPrivate->path, &statBuf) == -1) {
if (errno == ENOENT) {
bzero(&statBuf, sizeof(statBuf));
} else {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesLock stat() failed: %s"), strerror(errno));
_SCErrorSet(kSCStatusStale);
goto error;
}
}
currentSignature = __SCPSignatureFromStatbuf(&statBuf);
match = CFEqual(prefsPrivate->signature, currentSignature);
CFRelease(currentSignature);
if (!match) {
_SCErrorSet(kSCStatusStale);
goto error;
}
}
prefsPrivate->locked = TRUE;
return TRUE;
error :
if (haveLock) {
SCDynamicStoreRemoveValue(prefsPrivate->session,
prefsPrivate->sessionKeyLock);
}
if (value) CFRelease(value);
return FALSE;
}