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
);