2 * Copyright (c) 2000, 2001, 2004-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCPrivate.h>
36 #include "SCPreferencesInternal.h"
37 #include "SCHelper_client.h"
43 #include <sys/errno.h>
48 __SCPreferencesLock_helper(SCPreferencesRef prefs
, Boolean wait
)
51 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
52 uint32_t status
= kSCStatusOK
;
53 CFDataRef reply
= NULL
;
55 if (prefsPrivate
->helper
== -1) {
56 ok
= __SCPreferencesCreate_helper(prefs
);
62 // have the helper "lock" the prefs
65 ok
= _SCHelperExec(prefsPrivate
->helper
,
66 wait
? SCHELPER_MSG_PREFS_LOCKWAIT
: SCHELPER_MSG_PREFS_LOCK
,
67 prefsPrivate
->signature
,
74 if (status
!= kSCStatusOK
) {
78 prefsPrivate
->locked
= TRUE
;
84 if (prefsPrivate
->helper
!= -1) {
85 _SCHelperClose(prefsPrivate
->helper
);
86 prefsPrivate
->helper
= -1;
89 status
= kSCStatusAccessError
;
100 createParentDirectory(const char *path
)
107 // get parent directory path
108 if (strlcpy(dir
, path
, sizeof(dir
)) >= sizeof(dir
)) {
113 slash
= strrchr(dir
, '/');
114 if ((slash
== NULL
) || (slash
== dir
)) {
120 // create parent directories
121 for (scan
= dir
; TRUE
; scan
= slash
) {
125 if ((slash
== NULL
) || (scan
== dir
)) {
126 mode
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
; // 755
128 mode
= S_IRWXU
|S_IRWXG
|S_IROTH
|S_IXOTH
; // 775
136 ret
= mkdir(dir
, mode
);
138 static gid_t admin
= -1;
144 struct group
*grpP
= NULL
;
146 if ((getgrnam_r("admin", &grp
, buf
, sizeof(buf
), &grpP
) == 0) &&
148 admin
= grpP
->gr_gid
;
151 CFSTR("SCPreferencesLock getgrnam_r() failed: %s"),
157 if (chown(dir
, -1, admin
) == -1) {
159 CFSTR("SCPreferencesLock chown() failed: %s"),
164 if (chmod(dir
, mode
) == -1) {
166 CFSTR("SCPreferencesLock chmod() failed: %s"),
170 if ((slash
== NULL
) || (scan
== dir
)) {
173 } else if ((errno
== ENOENT
) && (scan
== dir
)) {
174 // the initial mkdir (of the full dir path) can fail
176 } else if (errno
== EROFS
) {
178 } else if (errno
!= EEXIST
) {
187 slash
= strchr(scan
+ 1, '/');
191 CFSTR("SCPreferencesLock mkdir() failed: %s"),
198 SCPreferencesLock(SCPreferencesRef prefs
, Boolean wait
)
201 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
202 int sc_status
= kSCStatusFailed
;
204 struct stat statBuf2
;
207 /* sorry, you must provide a session */
208 _SCErrorSet(kSCStatusNoPrefsSession
);
212 if (prefsPrivate
->locked
) {
213 /* sorry, you already have the lock */
214 _SCErrorSet(kSCStatusLocked
);
218 if (prefsPrivate
->authorizationData
!= NULL
) {
219 return __SCPreferencesLock_helper(prefs
, wait
);
222 if (!prefsPrivate
->isRoot
) {
223 _SCErrorSet(kSCStatusAccessError
);
228 pthread_mutex_lock(&prefsPrivate
->lock
);
230 if (prefsPrivate
->session
== NULL
) {
231 __SCPreferencesAddSession(prefs
);
234 if (prefsPrivate
->lockPath
== NULL
) {
238 path
= prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
;
239 pathLen
= strlen(path
) + sizeof("-lock");
240 prefsPrivate
->lockPath
= CFAllocatorAllocate(NULL
, pathLen
, 0);
241 snprintf(prefsPrivate
->lockPath
, pathLen
, "%s-lock", path
);
246 prefsPrivate
->lockFD
= open(prefsPrivate
->lockPath
,
247 wait
? O_WRONLY
|O_CREAT
|O_EXLOCK
248 : O_WRONLY
|O_CREAT
|O_EXLOCK
|O_NONBLOCK
,
250 if (prefsPrivate
->lockFD
== -1) {
253 if ((prefsPrivate
->prefsID
== NULL
) ||
254 !CFStringHasPrefix(prefsPrivate
->prefsID
, CFSTR("/"))) {
257 // create parent (/Library/Preferences/SystemConfiguration)
258 ret
= createParentDirectory(prefsPrivate
->lockPath
);
260 SCLog(TRUE
, LOG_NOTICE
,
261 CFSTR("created directory for \"%s\""),
262 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
264 } else if (errno
== EROFS
) {
270 // if read-only filesystem
273 // if already locked (and we are not blocking)
274 sc_status
= kSCStatusPrefsBusy
;
282 CFSTR("SCPreferencesLock open() failed: %s"),
287 if ((stat(prefsPrivate
->lockPath
, &statBuf
) == -1) ||
288 (fstat(prefsPrivate
->lockFD
, &statBuf2
) == -1) ||
289 (statBuf
.st_dev
!= statBuf2
.st_dev
) ||
290 (statBuf
.st_ino
!= statBuf2
.st_ino
)) {
291 // if the lock file was unlinked or re-created
292 close(prefsPrivate
->lockFD
);
293 prefsPrivate
->lockFD
= -1;
299 snprintf(buf
, sizeof(buf
), "%d\n", getpid());
300 write(prefsPrivate
->lockFD
, buf
, strlen(buf
));
304 if (prefsPrivate
->accessed
) {
305 CFDataRef currentSignature
;
309 * the preferences have been accessed since the
310 * session was created so we need to compare
311 * the signature of the stored preferences.
313 if (stat(prefsPrivate
->path
, &statBuf
) == -1) {
314 if (errno
== ENOENT
) {
315 bzero(&statBuf
, sizeof(statBuf
));
317 SCLog(TRUE
, LOG_DEBUG
,
318 CFSTR("SCPreferencesLock stat() failed: %s"),
320 sc_status
= kSCStatusStale
;
321 unlink(prefsPrivate
->lockPath
);
326 currentSignature
= __SCPSignatureFromStatbuf(&statBuf
);
327 match
= CFEqual(prefsPrivate
->signature
, currentSignature
);
328 CFRelease(currentSignature
);
331 * the preferences have been updated since the
332 * session was accessed so we've got no choice
333 * but to deny the lock request.
335 sc_status
= kSCStatusStale
;
336 unlink(prefsPrivate
->lockPath
);
341 // * the file contents have changed but since we
342 // * haven't accessed any of the preference data we
343 // * don't need to return an error. Simply proceed.
347 prefsPrivate
->locked
= TRUE
;
348 pthread_mutex_unlock(&prefsPrivate
->lock
);
353 if (prefsPrivate
->lockFD
!= -1) {
354 close(prefsPrivate
->lockFD
);
355 prefsPrivate
->lockFD
= -1;
358 pthread_mutex_unlock(&prefsPrivate
->lock
);
359 _SCErrorSet(sc_status
);