#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCPreferencesInternal.h"
#include "SCHelper_client.h"
#include <grp.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/errno.h>
static Boolean
__SCPreferencesLock_helper(SCPreferencesRef prefs, Boolean wait)
{
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
uint32_t status = kSCStatusOK;
CFDataRef reply = NULL;
if (prefsPrivate->helper == -1) {
ok = __SCPreferencesCreate_helper(prefs);
if (!ok) {
return FALSE;
}
}
status = kSCStatusOK;
reply = NULL;
ok = _SCHelperExec(prefsPrivate->helper,
wait ? SCHELPER_MSG_PREFS_LOCKWAIT : SCHELPER_MSG_PREFS_LOCK,
prefsPrivate->signature,
&status,
NULL);
if (!ok) {
goto fail;
}
if (status != kSCStatusOK) {
goto error;
}
prefsPrivate->locked = TRUE;
return TRUE;
fail :
if (prefsPrivate->helper != -1) {
_SCHelperClose(prefsPrivate->helper);
prefsPrivate->helper = -1;
}
status = kSCStatusAccessError;
error :
_SCErrorSet(status);
return FALSE;
}
static int
createParentDirectory(const char *path)
{
char dir[PATH_MAX];
int ret;
char *scan;
char *slash;
if (strlcpy(dir, path, sizeof(dir)) >= sizeof(dir)) {
errno = ENOENT;
return -1;
}
slash = strrchr(dir, '/');
if ((slash == NULL) || (slash == dir)) {
errno = ENOENT;
return -1;
}
*slash = '\0';
for (scan = dir; TRUE; scan = slash) {
mode_t mode;
char sep = '\0';
if ((slash == NULL) || (scan == dir)) {
mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; } else {
mode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; }
if (slash != NULL) {
sep = *slash;
*slash = '\0';
}
ret = mkdir(dir, mode);
if (ret == 0) {
static gid_t admin = -1;
if (admin == -1) {
char buf[256];
struct group grp;
struct group *grpP = NULL;
if ((getgrnam_r("admin", &grp, buf, sizeof(buf), &grpP) == 0) &&
(grpP != NULL)) {
admin = grpP->gr_gid;
} else {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesLock getgrnam_r() failed: %s"),
strerror(errno));
admin = 80;
}
}
if (chown(dir, -1, admin) == -1) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesLock chown() failed: %s"),
strerror(errno));
}
if (chmod(dir, mode) == -1) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesLock chmod() failed: %s"),
strerror(errno));
}
if ((slash == NULL) || (scan == dir)) {
return 0;
}
} else if ((errno == ENOENT) && (scan == dir)) {
;
} else if (errno == EROFS) {
return -1;
} else if (errno != EEXIST) {
break;
}
if (slash != NULL) {
*slash = sep;
} else {
break;
}
slash = strchr(scan + 1, '/');
}
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesLock mkdir() failed: %s"),
strerror(errno));
return -1;
}
Boolean
SCPreferencesLock(SCPreferencesRef prefs, Boolean wait)
{
char buf[32];
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
int sc_status = kSCStatusFailed;
struct stat statBuf;
struct stat statBuf2;
if (prefs == NULL) {
_SCErrorSet(kSCStatusNoPrefsSession);
return FALSE;
}
if (prefsPrivate->locked) {
_SCErrorSet(kSCStatusLocked);
return FALSE;
}
if (prefsPrivate->authorizationData != NULL) {
return __SCPreferencesLock_helper(prefs, wait);
}
if (!prefsPrivate->isRoot) {
_SCErrorSet(kSCStatusAccessError);
return FALSE;
}
pthread_mutex_lock(&prefsPrivate->lock);
if (prefsPrivate->session == NULL) {
__SCPreferencesAddSession(prefs);
}
if (prefsPrivate->lockPath == NULL) {
char *path;
int pathLen;
path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path;
pathLen = strlen(path) + sizeof("-lock");
prefsPrivate->lockPath = CFAllocatorAllocate(NULL, pathLen, 0);
snprintf(prefsPrivate->lockPath, pathLen, "%s-lock", path);
}
retry :
prefsPrivate->lockFD = open(prefsPrivate->lockPath,
wait ? O_WRONLY|O_CREAT|O_EXLOCK
: O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
0644);
if (prefsPrivate->lockFD == -1) {
switch (errno) {
case ENOENT :
if ((prefsPrivate->prefsID == NULL) ||
!CFStringHasPrefix(prefsPrivate->prefsID, CFSTR("/"))) {
int ret;
ret = createParentDirectory(prefsPrivate->lockPath);
if (ret == 0) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("created directory for \"%s\""),
prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
goto retry;
} else if (errno == EROFS) {
goto locked;
}
}
break;
case EROFS :
goto locked;
case EWOULDBLOCK :
sc_status = kSCStatusPrefsBusy;
goto error;
default :
break;
}
sc_status = errno;
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesLock open() failed: %s"),
strerror(errno));
goto error;
}
if ((stat(prefsPrivate->lockPath, &statBuf) == -1) ||
(fstat(prefsPrivate->lockFD, &statBuf2) == -1) ||
(statBuf.st_dev != statBuf2.st_dev) ||
(statBuf.st_ino != statBuf2.st_ino)) {
close(prefsPrivate->lockFD);
prefsPrivate->lockFD = -1;
goto retry;
}
snprintf(buf, sizeof(buf), "%d\n", getpid());
write(prefsPrivate->lockFD, buf, strlen(buf));
locked :
if (prefsPrivate->accessed) {
CFDataRef currentSignature;
Boolean match;
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));
sc_status = kSCStatusStale;
unlink(prefsPrivate->lockPath);
goto error;
}
}
currentSignature = __SCPSignatureFromStatbuf(&statBuf);
match = CFEqual(prefsPrivate->signature, currentSignature);
CFRelease(currentSignature);
if (!match) {
sc_status = kSCStatusStale;
unlink(prefsPrivate->lockPath);
goto error;
}
}
prefsPrivate->locked = TRUE;
pthread_mutex_unlock(&prefsPrivate->lock);
return TRUE;
error :
if (prefsPrivate->lockFD != -1) {
close(prefsPrivate->lockFD);
prefsPrivate->lockFD = -1;
}
pthread_mutex_unlock(&prefsPrivate->lock);
_SCErrorSet(sc_status);
return FALSE;
}