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