2 * Copyright (c) 2000, 2001, 2004-2008 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"
44 #include <sys/errno.h>
49 __private_extern__
int
50 getgrnam_r(const char *name
, __unused
struct group
*grp
, __unused
char *buf
, __unused
size_t bufsize
, struct group
**grpP
)
52 *grpP
= getgrnam(name
);
53 return (*grpP
== NULL
) ? -1 : 0;
55 #endif /* TARGET_OS_IPHONE */
59 __SCPreferencesLock_helper(SCPreferencesRef prefs
, Boolean wait
)
62 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
63 uint32_t status
= kSCStatusOK
;
64 CFDataRef reply
= NULL
;
66 if (prefsPrivate
->helper
== -1) {
67 ok
= __SCPreferencesCreate_helper(prefs
);
73 // have the helper "lock" the prefs
76 ok
= _SCHelperExec(prefsPrivate
->helper
,
77 wait
? SCHELPER_MSG_PREFS_LOCKWAIT
: SCHELPER_MSG_PREFS_LOCK
,
78 prefsPrivate
->signature
,
85 if (status
!= kSCStatusOK
) {
89 prefsPrivate
->locked
= TRUE
;
95 if (prefsPrivate
->helper
!= -1) {
96 _SCHelperClose(prefsPrivate
->helper
);
97 prefsPrivate
->helper
= -1;
100 status
= kSCStatusAccessError
;
111 createParentDirectory(const char *path
)
118 // get parent directory path
119 if (strlcpy(dir
, path
, sizeof(dir
)) >= sizeof(dir
)) {
124 slash
= strrchr(dir
, '/');
125 if ((slash
== NULL
) || (slash
== dir
)) {
131 // create parent directories
132 for (scan
= dir
; TRUE
; scan
= slash
) {
136 if ((slash
== NULL
) || (scan
== dir
)) {
137 mode
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
; // 755
139 mode
= S_IRWXU
|S_IRWXG
|S_IROTH
|S_IXOTH
; // 775
147 ret
= mkdir(dir
, mode
);
149 static gid_t admin
= -1;
155 struct group
*grpP
= NULL
;
157 if ((getgrnam_r("admin", &grp
, buf
, sizeof(buf
), &grpP
) == 0) &&
159 admin
= grpP
->gr_gid
;
162 CFSTR("SCPreferencesLock getgrnam_r() failed: %s"),
168 if (chown(dir
, -1, admin
) == -1) {
170 CFSTR("SCPreferencesLock chown() failed: %s"),
175 if (chmod(dir
, mode
) == -1) {
177 CFSTR("SCPreferencesLock chmod() failed: %s"),
181 if ((slash
== NULL
) || (scan
== dir
)) {
184 } else if ((errno
== ENOENT
) && (scan
== dir
)) {
185 // the initial mkdir (of the full dir path) can fail
187 } else if (errno
== EROFS
) {
189 } else if (errno
!= EEXIST
) {
198 slash
= strchr(scan
+ 1, '/');
202 CFSTR("SCPreferencesLock mkdir() failed: %s"),
209 reportDelay(SCPreferencesRef prefs
, struct timeval
*delay
, Boolean isStale
)
212 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
215 m
= asl_new(ASL_TYPE_MSG
);
216 asl_set(m
, "com.apple.message.domain", "com.apple.SystemConfiguration.SCPreferencesLock");
217 (void) _SC_cfstring_to_cstring(prefsPrivate
->name
, str
, sizeof(str
), kCFStringEncodingUTF8
);
218 asl_set(m
, "com.apple.message.signature", str
);
219 (void) _SC_cfstring_to_cstring(prefsPrivate
->prefsID
, str
, sizeof(str
), kCFStringEncodingUTF8
);
220 asl_set(m
, "com.apple.message.signature2", str
);
221 (void) snprintf(str
, sizeof(str
),
224 delay
->tv_usec
/ 1000);
225 asl_set(m
, "com.apple.message.value", str
);
226 SCLOG(NULL
, m
, ASL_LEVEL_DEBUG
,
227 CFSTR("SCPreferences(%@:%@) lock delayed for %d.%3.3d seconds%s"),
229 prefsPrivate
->prefsID
,
231 delay
->tv_usec
/ 1000,
232 isStale
? " (stale)" : "");
240 SCPreferencesLock(SCPreferencesRef prefs
, Boolean wait
)
243 struct timeval lockStart
;
244 struct timeval lockElapsed
;
245 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
246 int sc_status
= kSCStatusFailed
;
248 struct stat statBuf2
;
251 /* sorry, you must provide a session */
252 _SCErrorSet(kSCStatusNoPrefsSession
);
256 if (prefsPrivate
->locked
) {
257 /* sorry, you already have the lock */
258 _SCErrorSet(kSCStatusLocked
);
262 if (prefsPrivate
->authorizationData
!= NULL
) {
263 return __SCPreferencesLock_helper(prefs
, wait
);
266 if (!prefsPrivate
->isRoot
) {
267 _SCErrorSet(kSCStatusAccessError
);
272 pthread_mutex_lock(&prefsPrivate
->lock
);
274 if (prefsPrivate
->session
== NULL
) {
275 __SCPreferencesAddSession(prefs
);
278 if (prefsPrivate
->lockPath
== NULL
) {
282 path
= prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
;
283 pathLen
= strlen(path
) + sizeof("-lock");
284 prefsPrivate
->lockPath
= CFAllocatorAllocate(NULL
, pathLen
, 0);
285 snprintf(prefsPrivate
->lockPath
, pathLen
, "%s-lock", path
);
288 (void)gettimeofday(&lockStart
, NULL
);
292 prefsPrivate
->lockFD
= open(prefsPrivate
->lockPath
,
293 wait
? O_WRONLY
|O_CREAT
|O_EXLOCK
294 : O_WRONLY
|O_CREAT
|O_EXLOCK
|O_NONBLOCK
,
296 if (prefsPrivate
->lockFD
== -1) {
299 if ((prefsPrivate
->prefsID
== NULL
) ||
300 !CFStringHasPrefix(prefsPrivate
->prefsID
, CFSTR("/"))) {
303 // create parent (/Library/Preferences/SystemConfiguration)
304 ret
= createParentDirectory(prefsPrivate
->lockPath
);
306 SCLog(TRUE
, LOG_NOTICE
,
307 CFSTR("created directory for \"%s\""),
308 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
310 } else if (errno
== EROFS
) {
316 // if read-only filesystem
319 // if already locked (and we are not blocking)
320 sc_status
= kSCStatusPrefsBusy
;
328 CFSTR("SCPreferencesLock open() failed: %s"),
333 if ((stat(prefsPrivate
->lockPath
, &statBuf
) == -1) ||
334 (fstat(prefsPrivate
->lockFD
, &statBuf2
) == -1) ||
335 (statBuf
.st_dev
!= statBuf2
.st_dev
) ||
336 (statBuf
.st_ino
!= statBuf2
.st_ino
)) {
337 // if the lock file was unlinked or re-created
338 close(prefsPrivate
->lockFD
);
339 prefsPrivate
->lockFD
= -1;
345 snprintf(buf
, sizeof(buf
), "%d\n", getpid());
346 write(prefsPrivate
->lockFD
, buf
, strlen(buf
));
350 (void)gettimeofday(&prefsPrivate
->lockTime
, NULL
);
351 timersub(&prefsPrivate
->lockTime
, &lockStart
, &lockElapsed
);
353 if (prefsPrivate
->accessed
) {
354 CFDataRef currentSignature
;
358 * the preferences have been accessed since the
359 * session was created so we need to compare
360 * the signature of the stored preferences.
362 if (stat(prefsPrivate
->path
, &statBuf
) == -1) {
363 if (errno
== ENOENT
) {
364 bzero(&statBuf
, sizeof(statBuf
));
366 SCLog(TRUE
, LOG_DEBUG
,
367 CFSTR("SCPreferencesLock stat() failed: %s"),
373 currentSignature
= __SCPSignatureFromStatbuf(&statBuf
);
374 match
= CFEqual(prefsPrivate
->signature
, currentSignature
);
375 CFRelease(currentSignature
);
378 * the preferences have been updated since the
379 * session was accessed so we've got no choice
380 * but to deny the lock request.
386 // * the file contents have changed but since we
387 // * haven't accessed any of the preference data we
388 // * don't need to return an error. Simply proceed.
392 if (lockElapsed
.tv_sec
> 0) {
393 // if we waited more than 1 second to acquire the lock
394 reportDelay(prefs
, &lockElapsed
, FALSE
);
397 prefsPrivate
->locked
= TRUE
;
398 pthread_mutex_unlock(&prefsPrivate
->lock
);
403 sc_status
= kSCStatusStale
;
404 unlink(prefsPrivate
->lockPath
);
406 if (lockElapsed
.tv_sec
> 0) {
407 // if we waited more than 1 second to acquire the lock
408 reportDelay(prefs
, &lockElapsed
, TRUE
);
413 if (prefsPrivate
->lockFD
!= -1) {
414 close(prefsPrivate
->lockFD
);
415 prefsPrivate
->lockFD
= -1;
418 pthread_mutex_unlock(&prefsPrivate
->lock
);
419 _SCErrorSet(sc_status
);