2  * Copyright (c) 2000, 2001, 2004-2010 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 <TargetConditionals.h> 
  35 #include <SystemConfiguration/SystemConfiguration.h> 
  36 #include <SystemConfiguration/SCPrivate.h> 
  37 #include "SCPreferencesInternal.h" 
  38 #include "SCHelper_client.h" 
  46 #include <sys/errno.h> 
  47 #include <sys/mount.h> 
  48 #include <sys/param.h> 
  53 __SCPreferencesLock_helper(SCPreferencesRef prefs
, Boolean wait
) 
  56         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
  57         uint32_t                status          
= kSCStatusOK
; 
  58         CFDataRef               reply           
= NULL
; 
  60         if (prefsPrivate
->helper_port 
== MACH_PORT_NULL
) { 
  61                 ok 
= __SCPreferencesCreate_helper(prefs
); 
  67         // have the helper "lock" the prefs 
  70         ok 
= _SCHelperExec(prefsPrivate
->helper_port
, 
  71                            wait 
? SCHELPER_MSG_PREFS_LOCKWAIT 
: SCHELPER_MSG_PREFS_LOCK
, 
  72                            prefsPrivate
->signature
, 
  79         if (status 
!= kSCStatusOK
) { 
  83         prefsPrivate
->locked 
= TRUE
; 
  89         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
  90                 _SCHelperClose(&prefsPrivate
->helper_port
); 
  93         status 
= kSCStatusAccessError
; 
 104 createParentDirectory(const char *path
) 
 111         // get parent directory path 
 112         if (strlcpy(dir
, path
, sizeof(dir
)) >= sizeof(dir
)) { 
 117         slash 
= strrchr(dir
, '/'); 
 118         if ((slash 
== NULL
) || (slash 
== dir
)) { 
 124         // create parent directories 
 125         for (scan 
= dir
; TRUE
; scan 
= slash
) { 
 126                 mode_t  mode    
= S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
;      // 755 
 134                 ret 
= mkdir(dir
, mode
); 
 136                         static  gid_t   group   
= -1; 
 142                                 struct group    
*grpP   
= NULL
; 
 144                                 if ((getgrnam_r("wheel", &grp
, buf
, sizeof(buf
), &grpP
) == 0) && 
 146                                         group 
= grpP
->gr_gid
; 
 149                                               CFSTR("SCPreferencesLock getgrnam_r() failed: %s"), 
 155                         if (chown(dir
, -1, group
) == -1) { 
 157                                       CFSTR("SCPreferencesLock chown() failed: %s"), 
 162                         if (chmod(dir
, mode
) == -1) { 
 164                                       CFSTR("SCPreferencesLock chmod() failed: %s"), 
 168                         if ((slash 
== NULL
) || (scan 
== dir
)) { 
 171                 } else if ((errno 
== ENOENT
) && (scan 
== dir
)) { 
 172                         // the initial mkdir (of the full dir path) can fail 
 174                 } else if (errno 
== EROFS
) { 
 176                 } else if (errno 
!= EEXIST
) { 
 185                 slash 
= strchr(scan 
+ 1, '/'); 
 189               CFSTR("SCPreferencesLock mkdir() failed: %s"), 
 196 reportDelay(SCPreferencesRef prefs
, struct timeval 
*delay
, Boolean isStale
) 
 199         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 202         m 
= asl_new(ASL_TYPE_MSG
); 
 203         asl_set(m
, "com.apple.message.domain", "com.apple.SystemConfiguration.SCPreferencesLock"); 
 204         (void) _SC_cfstring_to_cstring(prefsPrivate
->name
, str
, sizeof(str
), kCFStringEncodingUTF8
); 
 205         asl_set(m
, "com.apple.message.signature", str
); 
 206         (void) _SC_cfstring_to_cstring(prefsPrivate
->prefsID
, str
, sizeof(str
), kCFStringEncodingUTF8
); 
 207         asl_set(m
, "com.apple.message.signature2", str
); 
 208         (void) snprintf(str
, sizeof(str
), 
 211                         delay
->tv_usec 
/ 1000); 
 212         asl_set(m
, "com.apple.message.value", str
); 
 213         SCLOG(NULL
, m
, ASL_LEVEL_DEBUG
, 
 214               CFSTR("SCPreferences(%@:%@) lock delayed for %d.%3.3d seconds%s"), 
 216               prefsPrivate
->prefsID
, 
 218               delay
->tv_usec 
/ 1000, 
 219               isStale 
? " (stale)" : ""); 
 227 has_O_EXLOCK(SCPreferencesPrivateRef prefsPrivate
) 
 229 #pragma pack(push, 4) 
 232                 vol_capabilities_attr_t capabilities
; 
 235         struct attrlist                 attrs
; 
 238         struct statfs                   statbuf
; 
 240         fd 
= open(prefsPrivate
->lockPath
, O_WRONLY
|O_CREAT
, 0644); 
 243                       CFSTR("SCPreferencesLock open() failed: %s"), 
 248         ret 
= fstatfs(fd
, &statbuf
); 
 249         unlink(prefsPrivate
->lockPath
); 
 253                       CFSTR("SCPreferencesLock fstatfs() failed: %s"), 
 258         bzero(&attrs
, sizeof(attrs
)); 
 259         attrs
.bitmapcount 
= ATTR_BIT_MAP_COUNT
; 
 260         attrs
.volattr     
= ATTR_VOL_INFO 
| ATTR_VOL_CAPABILITIES
; 
 261         bzero(&attrbuf
, sizeof(attrbuf
)); 
 262         ret 
= getattrlist(statbuf
.f_mntonname
,  // path (of mount point) 
 263                           &attrs
,               // attribute list 
 264                           &attrbuf
,             // attribute buffer 
 269                       CFSTR("SCPreferencesLock getattrlist() failed: %s"), 
 274         if ((attrbuf
.capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_FLOCK
) && 
 275             (attrbuf
.capabilities
.valid       
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_FLOCK
)) { 
 284 lockWithSCDynamicStore(SCPreferencesPrivateRef  prefsPrivate
, Boolean wait
) 
 287         Boolean         locked          
= FALSE
; 
 289         int             sc_status       
= kSCStatusOK
; 
 291         // add [lock] notification 
 292         ok 
= SCDynamicStoreAddWatchedKey(prefsPrivate
->session
, 
 293                                          prefsPrivate
->sessionKeyLock
, 
 296                 sc_status 
= SCError(); 
 297                 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesLock SCDynamicStoreAddWatchedKey() failed")); 
 303                 // Attempt to acquire the lock 
 304                 value 
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent()); 
 305                 ok 
= SCDynamicStoreAddTemporaryValue(prefsPrivate
->session
, 
 306                                                      prefsPrivate
->sessionKeyLock
, 
 315                         sc_status 
= kSCStatusPrefsBusy
; 
 319                 // wait for the lock to be released 
 320                 ok 
= SCDynamicStoreNotifyWait(prefsPrivate
->session
); 
 322                         sc_status 
= SCError(); 
 323                         SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesLock SCDynamicStoreNotifyWait() failed")); 
 327                 // clear out any notifications 
 328                 changes 
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
); 
 329                 if (changes 
!= NULL
) { 
 332                         SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesLock SCDynamicStoreCopyNotifiedKeys() failed")); 
 337         // remove [lock] notification 
 338         (void) SCDynamicStoreRemoveWatchedKey(prefsPrivate
->session
, 
 339                                               prefsPrivate
->sessionKeyLock
, 
 342         // clear out any notifications 
 343         changes 
= SCDynamicStoreCopyNotifiedKeys(prefsPrivate
->session
); 
 344         if (changes 
!= NULL
) { 
 348         if (sc_status 
!= kSCStatusOK
) { 
 349                 _SCErrorSet(sc_status
); 
 356 SCPreferencesLock(SCPreferencesRef prefs
, Boolean wait
) 
 359         struct timeval          lockStart
; 
 360         struct timeval          lockElapsed
; 
 361         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 362         int                     sc_status       
= kSCStatusFailed
; 
 364         struct stat             statBuf2
; 
 367                 /* sorry, you must provide a session */ 
 368                 _SCErrorSet(kSCStatusNoPrefsSession
); 
 372         if (prefsPrivate
->locked
) { 
 373                 /* sorry, you already have the lock */ 
 374                 _SCErrorSet(kSCStatusLocked
); 
 378         if (prefsPrivate
->authorizationData 
!= NULL
) { 
 379                 return __SCPreferencesLock_helper(prefs
, wait
); 
 382         if (!prefsPrivate
->isRoot
) { 
 383                 _SCErrorSet(kSCStatusAccessError
); 
 388         pthread_mutex_lock(&prefsPrivate
->lock
); 
 390         if (prefsPrivate
->session 
== NULL
) { 
 391                 __SCPreferencesAddSession(prefs
); 
 394         if (prefsPrivate
->lockPath 
== NULL
) { 
 398                 path 
= prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
; 
 399                 pathLen 
= strlen(path
) + sizeof("-lock"); 
 400                 prefsPrivate
->lockPath 
= CFAllocatorAllocate(NULL
, pathLen
, 0); 
 401                 snprintf(prefsPrivate
->lockPath
, pathLen
, "%s-lock", path
); 
 404         (void)gettimeofday(&lockStart
, NULL
); 
 408         if (prefsPrivate
->sessionKeyLock 
!= NULL
) { 
 409                 if (lockWithSCDynamicStore(prefsPrivate
, wait
)) { 
 416         prefsPrivate
->lockFD 
= open(prefsPrivate
->lockPath
, 
 417                                     wait 
? O_WRONLY
|O_CREAT
|O_EXLOCK
 
 418                                          : O_WRONLY
|O_CREAT
|O_EXLOCK
|O_NONBLOCK
, 
 420         if (prefsPrivate
->lockFD 
== -1) { 
 423                                 if ((prefsPrivate
->prefsID 
== NULL
) || 
 424                                     !CFStringHasPrefix(prefsPrivate
->prefsID
, CFSTR("/"))) { 
 427                                         // create parent (/Library/Preferences/SystemConfiguration) 
 428                                         ret 
= createParentDirectory(prefsPrivate
->lockPath
); 
 430                                                 SCLog(TRUE
, LOG_NOTICE
, 
 431                                                       CFSTR("created directory for \"%s\""), 
 432                                                       prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
); 
 434                                         } else if (errno 
== EROFS
) { 
 440                                 // if read-only filesystem 
 443                                 // if already locked (and we are not blocking) 
 444                                 sc_status 
= kSCStatusPrefsBusy
; 
 447                                 if (!has_O_EXLOCK(prefsPrivate
)) { 
 448                                         // O_EXLOCK *not* available, use SCDynamicStore 
 449                                         prefsPrivate
->sessionKeyLock 
= _SCPNotificationKey(NULL
, 
 450                                                                                            prefsPrivate
->prefsID
, 
 451                                                                                            kSCPreferencesKeyLock
); 
 462                       CFSTR("SCPreferencesLock open() failed: %s"), 
 467         if ((stat(prefsPrivate
->lockPath
, &statBuf
) == -1) || 
 468             (fstat(prefsPrivate
->lockFD
, &statBuf2
) == -1) || 
 469             (statBuf
.st_dev 
!= statBuf2
.st_dev
) || 
 470             (statBuf
.st_ino 
!= statBuf2
.st_ino
)) { 
 471                 // if the lock file was unlinked or re-created 
 472                 close(prefsPrivate
->lockFD
); 
 473                 prefsPrivate
->lockFD 
= -1; 
 479         snprintf(buf
, sizeof(buf
), "%d\n", getpid()); 
 480         write(prefsPrivate
->lockFD
, buf
, strlen(buf
)); 
 484         (void)gettimeofday(&prefsPrivate
->lockTime
, NULL
); 
 485         timersub(&prefsPrivate
->lockTime
, &lockStart
, &lockElapsed
); 
 487         if (prefsPrivate
->accessed
) { 
 488                 CFDataRef       currentSignature
; 
 492                  * the preferences have been accessed since the 
 493                  * session was created so we need to compare 
 494                  * the signature of the stored preferences. 
 496                 if (stat(prefsPrivate
->path
, &statBuf
) == -1) { 
 497                         if (errno 
== ENOENT
) { 
 498                                 bzero(&statBuf
, sizeof(statBuf
)); 
 500                                 SCLog(TRUE
, LOG_DEBUG
, 
 501                                       CFSTR("SCPreferencesLock stat() failed: %s"), 
 507                 currentSignature 
= __SCPSignatureFromStatbuf(&statBuf
); 
 508                 match 
= CFEqual(prefsPrivate
->signature
, currentSignature
); 
 509                 CFRelease(currentSignature
); 
 512                          * the preferences have been updated since the 
 513                          * session was accessed so we've got no choice 
 514                          * but to deny the lock request. 
 520 //               * the file contents have changed but since we 
 521 //               * haven't accessed any of the preference data we 
 522 //               * don't need to return an error.  Simply proceed. 
 526         if (lockElapsed
.tv_sec 
> 0) { 
 527                 // if we waited more than 1 second to acquire the lock 
 528                 reportDelay(prefs
, &lockElapsed
, FALSE
); 
 531         prefsPrivate
->locked 
= TRUE
; 
 532         pthread_mutex_unlock(&prefsPrivate
->lock
); 
 537         sc_status 
= kSCStatusStale
; 
 538         unlink(prefsPrivate
->lockPath
); 
 540         if (lockElapsed
.tv_sec 
> 0) { 
 541                 // if we waited more than 1 second to acquire the lock 
 542                 reportDelay(prefs
, &lockElapsed
, TRUE
); 
 547         if (prefsPrivate
->lockFD 
!= -1) { 
 548                 close(prefsPrivate
->lockFD
); 
 549                 prefsPrivate
->lockFD 
= -1; 
 552         pthread_mutex_unlock(&prefsPrivate
->lock
); 
 553         _SCErrorSet(sc_status
);