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