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
;
206 /* sorry, you must provide a session */
207 _SCErrorSet(kSCStatusNoPrefsSession
);
211 if (prefsPrivate
->locked
) {
212 /* sorry, you already have the lock */
213 _SCErrorSet(kSCStatusLocked
);
217 if (prefsPrivate
->authorizationData
!= NULL
) {
218 return __SCPreferencesLock_helper(prefs
, wait
);
221 if (!prefsPrivate
->isRoot
) {
222 _SCErrorSet(kSCStatusAccessError
);
227 pthread_mutex_lock(&prefsPrivate
->lock
);
229 if (prefsPrivate
->session
== NULL
) {
230 __SCPreferencesAddSession(prefs
);
233 if (prefsPrivate
->lockPath
== NULL
) {
237 path
= prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
;
238 pathLen
= strlen(path
) + sizeof("-lock");
239 prefsPrivate
->lockPath
= CFAllocatorAllocate(NULL
, pathLen
, 0);
240 snprintf(prefsPrivate
->lockPath
, pathLen
, "%s-lock", path
);
245 prefsPrivate
->lockFD
= open(prefsPrivate
->lockPath
, O_WRONLY
|O_CREAT
, 0644);
246 if (prefsPrivate
->lockFD
== -1) {
247 if (errno
== EROFS
) {
251 if ((errno
== ENOENT
) &&
252 ((prefsPrivate
->prefsID
== NULL
) || !CFStringHasPrefix(prefsPrivate
->prefsID
, CFSTR("/")))) {
255 // create parent (/Library/Preferences/SystemConfiguration)
256 ret
= createParentDirectory(prefsPrivate
->lockPath
);
258 SCLog(TRUE
, LOG_NOTICE
,
259 CFSTR("created directory for \"%s\""),
260 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
262 } else if (errno
== EROFS
) {
269 CFSTR("SCPreferencesLock open() failed: %s"),
274 if (flock(prefsPrivate
->lockFD
, wait
? LOCK_EX
: LOCK_EX
|LOCK_NB
) == -1) {
277 // if already locked (and we are not blocking)
278 sc_status
= kSCStatusPrefsBusy
;
283 CFSTR("SCPreferencesLock flock() failed: %s"),
291 if (stat(prefsPrivate
->lockPath
, &statbuf
) == -1) {
292 // if the lock file was unlinked
293 close(prefsPrivate
->lockFD
);
294 prefsPrivate
->lockFD
= -1;
300 snprintf(buf
, sizeof(buf
), "%d\n", getpid());
301 write(prefsPrivate
->lockFD
, buf
, strlen(buf
));
305 if (prefsPrivate
->accessed
) {
306 CFDataRef currentSignature
;
311 * the preferences have been accessed since the
312 * session was created so we need to compare
313 * the signature of the stored preferences.
315 if (stat(prefsPrivate
->path
, &statBuf
) == -1) {
316 if (errno
== ENOENT
) {
317 bzero(&statBuf
, sizeof(statBuf
));
319 SCLog(TRUE
, LOG_DEBUG
,
320 CFSTR("SCPreferencesLock stat() failed: %s"),
322 sc_status
= kSCStatusStale
;
323 unlink(prefsPrivate
->lockPath
);
328 currentSignature
= __SCPSignatureFromStatbuf(&statBuf
);
329 match
= CFEqual(prefsPrivate
->signature
, currentSignature
);
330 CFRelease(currentSignature
);
333 * the preferences have been updated since the
334 * session was accessed so we've got no choice
335 * but to deny the lock request.
337 sc_status
= kSCStatusStale
;
338 unlink(prefsPrivate
->lockPath
);
343 // * the file contents have changed but since we
344 // * haven't accessed any of the preference data we
345 // * don't need to return an error. Simply proceed.
349 prefsPrivate
->locked
= TRUE
;
350 pthread_mutex_unlock(&prefsPrivate
->lock
);
355 if (prefsPrivate
->lockFD
!= -1) {
356 close(prefsPrivate
->lockFD
);
357 prefsPrivate
->lockFD
= -1;
360 pthread_mutex_unlock(&prefsPrivate
->lock
);
361 _SCErrorSet(sc_status
);