this repo has no description
at fixPythonPipStalling 543 lines 13 kB view raw
1/* 2 * Copyright (c) 2000-2010, 2013, 2015-2018 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * June 1, 2001 Allan Nathanson <ajn@apple.com> 28 * - public API conversion 29 * 30 * November 9, 2000 Allan Nathanson <ajn@apple.com> 31 * - initial revision 32 */ 33 34#include "SCPreferencesInternal.h" 35#include "SCHelper_client.h" 36 37#include <grp.h> 38#include <fcntl.h> 39#include <stdio.h> 40#include <unistd.h> 41#include <pthread.h> 42#include <sys/attr.h> 43#include <sys/errno.h> 44#include <sys/mount.h> 45#include <sys/param.h> 46 47 48static Boolean 49__SCPreferencesLock_helper(SCPreferencesRef prefs, Boolean wait) 50{ 51 Boolean ok; 52 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 53 uint32_t status = kSCStatusOK; 54 CFDataRef reply = NULL; 55 56 if (prefsPrivate->helper_port == MACH_PORT_NULL) { 57 ok = __SCPreferencesCreate_helper(prefs); 58 if (!ok) { 59 return FALSE; 60 } 61 } 62 63 // have the helper "lock" the prefs 64 status = kSCStatusOK; 65 reply = NULL; 66 ok = _SCHelperExec(prefsPrivate->helper_port, 67 wait ? SCHELPER_MSG_PREFS_LOCKWAIT : SCHELPER_MSG_PREFS_LOCK, 68 prefsPrivate->signature, 69 &status, 70 NULL); 71 if (!ok) { 72 goto fail; 73 } 74 75 if (status != kSCStatusOK) { 76 goto error; 77 } 78 79 __SCPreferencesUpdateLockedState(prefs, TRUE); 80 return TRUE; 81 82 fail : 83 84 // close helper 85 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 86 _SCHelperClose(&prefsPrivate->helper_port); 87 } 88 89 status = kSCStatusAccessError; 90 91 error : 92 93 // return error 94 _SCErrorSet(status); 95 return FALSE; 96} 97 98 99static int 100createParentDirectory(const char *path) 101{ 102 char dir[PATH_MAX]; 103 int ret; 104 char *scan; 105 char *slash; 106 107 // get parent directory path 108 if (strlcpy(dir, path, sizeof(dir)) >= sizeof(dir)) { 109 errno = ENOENT; 110 return -1; 111 } 112 113 slash = strrchr(dir, '/'); 114 if ((slash == NULL) || (slash == dir)) { 115 errno = ENOENT; 116 return -1; 117 } 118 *slash = '\0'; 119 120 // create parent directories 121 for (scan = dir; TRUE; scan = slash) { 122 mode_t mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; // 755 123 char sep = '\0'; 124 125 if (slash != NULL) { 126 sep = *slash; 127 *slash = '\0'; 128 } 129 130 ret = mkdir(dir, mode); 131 if (ret == 0) { 132 static gid_t group = -1; 133 134 // set group 135 if (group == (gid_t)-1) { 136 char buf[256]; 137 struct group grp; 138 struct group *grpP = NULL; 139 140 if ((getgrnam_r("wheel", &grp, buf, sizeof(buf), &grpP) == 0) && 141 (grpP != NULL)) { 142 group = grpP->gr_gid; 143 } else { 144 SC_log(LOG_NOTICE, "getgrnam_r() failed: %s", strerror(errno)); 145 group = 0; // wheel 146 } 147 } 148 149 if (chown(dir, -1, group) == -1) { 150 SC_log(LOG_NOTICE, "chown() failed: %s", strerror(errno)); 151 } 152 153 // set [force] mode 154 if (chmod(dir, mode) == -1) { 155 SC_log(LOG_NOTICE, "chmod() failed: %s", strerror(errno)); 156 } 157 158 if ((slash == NULL) || (scan == dir)) { 159 return 0; 160 } 161 } else if ((errno == ENOENT) && (scan == dir)) { 162 // the initial mkdir (of the full dir path) can fail 163 ; 164 } else if (errno == EROFS) { 165 return -1; 166 } else if (errno != EEXIST) { 167 break; 168 } 169 170 if (slash != NULL) { 171 *slash = sep; 172 } else { 173 break; 174 } 175 slash = strchr(scan + 1, '/'); 176 } 177 178 SC_log(LOG_NOTICE, "mkdir() failed: %s", strerror(errno)); 179 return -1; 180} 181 182 183static void 184reportDelay(SCPreferencesRef prefs, struct timeval *delay, Boolean isStale) 185{ 186 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 187 188 SC_log(LOG_ERR, 189 "SCPreferences(%@:%@) lock delayed for %d.%3.3d seconds%s", 190 prefsPrivate->name, 191 prefsPrivate->prefsID, 192 (int)delay->tv_sec, 193 delay->tv_usec / 1000, 194 isStale ? " (stale)" : ""); 195 return; 196} 197 198 199static Boolean 200has_O_EXLOCK(SCPreferencesPrivateRef prefsPrivate) 201{ 202#pragma pack(push, 4) 203 struct { 204 u_int32_t size; 205 vol_capabilities_attr_t capabilities; 206 } attrbuf; 207#pragma pack(pop) 208 struct attrlist attrs; 209 int fd; 210 int ret; 211 struct statfs statbuf; 212 213 fd = open(prefsPrivate->lockPath, O_WRONLY|O_CREAT, 0644); 214 if (fd == -1) { 215 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno)); 216 return FALSE; 217 } 218 219 ret = fstatfs(fd, &statbuf); 220 unlink(prefsPrivate->lockPath); 221 close(fd); 222 if (ret == -1) { 223 SC_log(LOG_NOTICE, "fstatfs() failed: %s", strerror(errno)); 224 return FALSE; 225 } 226 227 memset(&attrs, 0, sizeof(attrs)); 228 attrs.bitmapcount = ATTR_BIT_MAP_COUNT; 229 attrs.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES; 230 memset(&attrbuf, 0, sizeof(attrbuf)); 231 ret = getattrlist(statbuf.f_mntonname, // path (of mount point) 232 &attrs, // attribute list 233 &attrbuf, // attribute buffer 234 sizeof(attrbuf), 235 0); // options 236 if (ret == -1) { 237 SC_log(LOG_NOTICE, "getattrlist() failed: %s", strerror(errno)); 238 return FALSE; 239 } 240 241 if ((attrbuf.capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_FLOCK) && 242 (attrbuf.capabilities.valid [VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_FLOCK)) { 243 return TRUE; 244 } 245 246 return FALSE; 247} 248 249 250static Boolean 251lockWithSCDynamicStore(SCPreferencesRef prefs, Boolean wait) 252{ 253 CFArrayRef changes; 254 Boolean locked = FALSE; 255 Boolean ok; 256 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 257 int sc_status = kSCStatusOK; 258 259 // add SCDynamicStore session (for lock monitoring) 260 ok = __SCPreferencesAddSession(prefs); 261 if (!ok) { 262 return FALSE; 263 } 264 265 // add [lock] notification 266 ok = SCDynamicStoreAddWatchedKey(prefsPrivate->session, 267 prefsPrivate->sessionKeyLock, 268 FALSE); 269 if (!ok) { 270 sc_status = SCError(); 271 SC_log(LOG_INFO, "SCDynamicStoreAddWatchedKey() failed"); 272 } 273 274 // add SCDynamicStore session (for the actual lock) 275 if (ok) { 276 prefsPrivate->sessionNoO_EXLOCK = SCDynamicStoreCreate(NULL, prefsPrivate->name, NULL, NULL); 277 if (prefsPrivate->sessionNoO_EXLOCK == NULL) { 278 sc_status = SCError(); 279 SC_log(LOG_INFO, "SCDynamicStoreCreate() failed"); 280 ok = FALSE; 281 } 282 } 283 284 while (ok) { 285 CFDateRef value; 286 287 // Attempt to acquire the lock 288 value = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 289 ok = SCDynamicStoreAddTemporaryValue(prefsPrivate->sessionNoO_EXLOCK, 290 prefsPrivate->sessionKeyLock, 291 value); 292 CFRelease(value); 293 if (ok) { 294 locked = TRUE; 295 break; 296 } 297 298 if (!wait) { 299 sc_status = kSCStatusPrefsBusy; 300 break; 301 } 302 303 // wait for the lock to be released 304 ok = SCDynamicStoreNotifyWait(prefsPrivate->session); 305 if (!ok) { 306 sc_status = SCError(); 307 SC_log(LOG_INFO, "SCDynamicStoreNotifyWait() failed"); 308 break; 309 } 310 311 // clear out any notifications 312 changes = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session); 313 if (changes != NULL) { 314 CFRelease(changes); 315 } else { 316 SC_log(LOG_INFO, "SCDynamicStoreCopyNotifiedKeys() failed"); 317 break; 318 } 319 } 320 321 // remove [lock] notification 322 (void) SCDynamicStoreRemoveWatchedKey(prefsPrivate->session, 323 prefsPrivate->sessionKeyLock, 324 0); 325 326 // clear out any notifications 327 changes = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session); 328 if (changes != NULL) { 329 CFRelease(changes); 330 } 331 332 __SCPreferencesRemoveSession(prefs); 333 334 if (!locked && (prefsPrivate->sessionNoO_EXLOCK != NULL)) { 335 CFRelease(prefsPrivate->sessionNoO_EXLOCK); 336 prefsPrivate->sessionNoO_EXLOCK = NULL; 337 } 338 339 if (sc_status != kSCStatusOK) { 340 _SCErrorSet(sc_status); 341 } 342 343 return locked; 344} 345 346 347Boolean 348SCPreferencesLock(SCPreferencesRef prefs, Boolean wait) 349{ 350 char buf[32]; 351 struct timeval lockStart; 352 struct timeval lockElapsed; 353 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 354 int sc_status = kSCStatusFailed; 355 struct stat statBuf; 356 struct stat statBuf2; 357 358 if (prefs == NULL) { 359 /* sorry, you must provide a session */ 360 _SCErrorSet(kSCStatusNoPrefsSession); 361 return FALSE; 362 } 363 364 if (prefsPrivate->locked) { 365 /* sorry, you already have the lock */ 366 _SCErrorSet(kSCStatusLocked); 367 return FALSE; 368 } 369 370 if (prefsPrivate->authorizationData != NULL) { 371 return __SCPreferencesLock_helper(prefs, wait); 372 } 373 374 if (!prefsPrivate->isRoot) { 375 _SCErrorSet(kSCStatusAccessError); 376 return FALSE; 377 } 378 379 pthread_mutex_lock(&prefsPrivate->lock); 380 381 __SCPreferencesAddSessionKeys(prefs); 382 383 if (prefsPrivate->lockPath == NULL) { 384 char *path; 385 CFIndex pathLen; 386 387 path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path; 388 pathLen = strlen(path) + sizeof("-lock"); 389 prefsPrivate->lockPath = CFAllocatorAllocate(NULL, pathLen, 0); 390 snprintf(prefsPrivate->lockPath, pathLen, "%s-lock", path); 391 } 392 393 (void)gettimeofday(&lockStart, NULL); 394 395 retry : 396 397 if (prefsPrivate->sessionKeyLock != NULL) { 398 if (lockWithSCDynamicStore(prefs, wait)) { 399 goto locked; 400 } 401 402 goto error; 403 } 404 405 prefsPrivate->lockFD = open(prefsPrivate->lockPath, 406 wait ? O_WRONLY|O_CREAT|O_EXLOCK 407 : O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 408 0644); 409 if (prefsPrivate->lockFD == -1) { 410 switch (errno) { 411 case ENOENT : 412 if ((prefsPrivate->prefsID == NULL) || 413 !CFStringHasPrefix(prefsPrivate->prefsID, CFSTR("/"))) { 414 int ret; 415 416 // create parent (/Library/Preferences/SystemConfiguration) 417 ret = createParentDirectory(prefsPrivate->lockPath); 418 if (ret == 0) { 419 SC_log(LOG_INFO, "created directory for \"%s\"", 420 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path); 421 goto retry; 422 } else if (errno == EROFS) { 423 goto locked; 424 } 425 } 426 break; 427 case EROFS : 428 // if read-only filesystem 429 goto locked; 430 case EWOULDBLOCK : 431 // if already locked (and we are not blocking) 432 sc_status = kSCStatusPrefsBusy; 433 goto error; 434 case ENOTSUP : 435 if (!has_O_EXLOCK(prefsPrivate)) { 436 // O_EXLOCK *not* available, use SCDynamicStore 437 prefsPrivate->sessionKeyLock = _SCPNotificationKey(NULL, 438 prefsPrivate->prefsID, 439 kSCPreferencesKeyLock); 440 goto retry; 441 } 442 errno = ENOTSUP; 443 break; 444 default : 445 break; 446 } 447 448 sc_status = errno; 449 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno)); 450 goto error; 451 } 452 453 if ((stat(prefsPrivate->lockPath, &statBuf) == -1) || 454 (fstat(prefsPrivate->lockFD, &statBuf2) == -1) || 455 (statBuf.st_dev != statBuf2.st_dev) || 456 (statBuf.st_ino != statBuf2.st_ino)) { 457 // if the lock file was unlinked or re-created 458 close(prefsPrivate->lockFD); 459 prefsPrivate->lockFD = -1; 460 goto retry; 461 } 462 463 // we have the lock 464 465 snprintf(buf, sizeof(buf), "%d\n", getpid()); 466 (void) write(prefsPrivate->lockFD, buf, strlen(buf)); 467 468 locked : 469 470 (void)gettimeofday(&prefsPrivate->lockTime, NULL); 471 timersub(&prefsPrivate->lockTime, &lockStart, &lockElapsed); 472 473 if (prefsPrivate->accessed) { 474 CFDataRef currentSignature; 475 Boolean match; 476 477 /* 478 * the preferences have been accessed since the 479 * session was created so we need to compare 480 * the signature of the stored preferences. 481 */ 482 if (stat(prefsPrivate->path, &statBuf) == -1) { 483 if (errno == ENOENT) { 484 memset(&statBuf, 0, sizeof(statBuf)); 485 } else { 486 SC_log(LOG_INFO, "stat() failed: %s", 487 strerror(errno)); 488 goto stale; 489 } 490 } 491 492 currentSignature = __SCPSignatureFromStatbuf(&statBuf); 493 match = CFEqual(prefsPrivate->signature, currentSignature); 494 CFRelease(currentSignature); 495 if (!match) { 496 /* 497 * the preferences have been updated since the 498 * session was accessed so we've got no choice 499 * but to deny the lock request. 500 */ 501 goto stale; 502 } 503// } else { 504// /* 505// * the file contents have changed but since we 506// * haven't accessed any of the preference data we 507// * don't need to return an error. Simply proceed. 508// */ 509 } 510 511 if (lockElapsed.tv_sec > 0) { 512 // if we waited more than 1 second to acquire the lock 513 reportDelay(prefs, &lockElapsed, FALSE); 514 } 515 516 SC_log(LOG_DEBUG, "SCPreferences() lock: %s", 517 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path); 518 519 __SCPreferencesUpdateLockedState(prefs, TRUE); 520 pthread_mutex_unlock(&prefsPrivate->lock); 521 return TRUE; 522 523 stale : 524 525 sc_status = kSCStatusStale; 526 unlink(prefsPrivate->lockPath); 527 528 if (lockElapsed.tv_sec > 0) { 529 // if we waited more than 1 second to acquire the lock 530 reportDelay(prefs, &lockElapsed, TRUE); 531 } 532 533 error : 534 535 if (prefsPrivate->lockFD != -1) { 536 close(prefsPrivate->lockFD); 537 prefsPrivate->lockFD = -1; 538 } 539 540 pthread_mutex_unlock(&prefsPrivate->lock); 541 _SCErrorSet(sc_status); 542 return FALSE; 543}