#include <SystemConfiguration/SCP.h>
#include "SCPPrivate.h"
#include <SystemConfiguration/SCD.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/errno.h>
SCPStatus
SCPLock(SCPSessionRef session, boolean_t wait)
{
SCPStatus scp_status;
SCDStatus scd_status;
SCPSessionPrivateRef sessionPrivate;
SCDHandleRef handle = NULL;
CFDateRef value;
CFArrayRef changes;
struct stat statBuf;
CFDataRef currentSignature;
if (session == NULL) {
return SCP_FAILED;
}
sessionPrivate = (SCPSessionPrivateRef)session;
if (!sessionPrivate->isRoot) {
goto notRoot;
}
if (sessionPrivate->session == NULL) {
scd_status = SCDOpen(&sessionPrivate->session, sessionPrivate->name);
if (scd_status != SCD_OK) {
SCDLog(LOG_INFO, CFSTR("SCDOpen() failed: %s"), SCDError(scd_status));
return SCP_FAILED;
}
}
if (sessionPrivate->sessionKeyLock == NULL) {
sessionPrivate->sessionKeyLock = _SCPNotificationKey(sessionPrivate->prefsID,
sessionPrivate->perUser,
sessionPrivate->user,
kSCPKeyLock);
}
scd_status = SCDNotifierAdd(sessionPrivate->session,
sessionPrivate->sessionKeyLock,
0);
if (scd_status != SCD_OK) {
SCDLog(LOG_INFO, CFSTR("SCDNotifierAdd() failed: %s"), SCDError(scd_status));
scp_status = SCP_FAILED;
goto error;
}
handle = SCDHandleInit();
value = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
SCDHandleSetData(handle, value);
CFRelease(value);
while (TRUE) {
scd_status = SCDAddSession(sessionPrivate->session,
sessionPrivate->sessionKeyLock,
handle);
switch (scd_status) {
case SCD_OK :
scp_status = SCP_OK;
goto done;
case SCD_EXISTS :
if (!wait) {
scp_status = SCP_BUSY;
goto error;
}
break;
default :
SCDLog(LOG_INFO, CFSTR("SCDAddSession() failed: %s"), SCDError(scd_status));
scp_status = SCP_FAILED;
goto error;
}
scd_status = SCDNotifierWait(sessionPrivate->session);
if (scd_status != SCD_OK) {
SCDLog(LOG_INFO, CFSTR("SCDAddSession() failed: %s"), SCDError(scd_status));
scp_status = SCP_FAILED;
goto error;
}
}
done :
SCDHandleRelease(handle);
handle = NULL;
scd_status = SCDNotifierRemove(sessionPrivate->session,
sessionPrivate->sessionKeyLock,
0);
if (scd_status != SCD_OK) {
SCDLog(LOG_INFO, CFSTR("SCDNotifierRemove() failed: %s"), SCDError(scd_status));
scp_status = SCP_FAILED;
goto error;
}
scd_status = SCDNotifierGetChanges(sessionPrivate->session, &changes);
if (scd_status != SCD_OK) {
SCDLog(LOG_INFO, CFSTR("SCDNotifierGetChanges() failed: %s"), SCDError(scd_status));
scp_status = SCP_FAILED;
goto error;
}
CFRelease(changes);
notRoot:
if (stat(sessionPrivate->path, &statBuf) == -1) {
if (errno == ENOENT) {
bzero(&statBuf, sizeof(statBuf));
} else {
SCDLog(LOG_DEBUG, CFSTR("stat() failed: %s"), strerror(errno));
scp_status = SCP_STALE;
goto error;
}
}
currentSignature = _SCPSignatureFromStatbuf(&statBuf);
if (!CFEqual(sessionPrivate->signature, currentSignature)) {
CFRelease(currentSignature);
scp_status = SCP_STALE;
goto error;
}
CFRelease(currentSignature);
sessionPrivate->locked = TRUE;
return SCD_OK;
error :
if (handle) SCDHandleRelease(handle);
return scp_status;
}