]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPLock.c
configd-293.4.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPLock.c
1 /*
2 * Copyright (c) 2000, 2001, 2004-2008 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 <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCPrivate.h>
36 #include "SCPreferencesInternal.h"
37 #include "SCHelper_client.h"
38
39 #include <grp.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <pthread.h>
44 #include <sys/errno.h>
45
46
47
48 #if TARGET_OS_IPHONE
49 __private_extern__ int
50 getgrnam_r(const char *name, __unused struct group *grp, __unused char *buf, __unused size_t bufsize, struct group **grpP)
51 {
52 *grpP = getgrnam(name);
53 return (*grpP == NULL) ? -1 : 0;
54 }
55 #endif /* TARGET_OS_IPHONE */
56
57
58 static Boolean
59 __SCPreferencesLock_helper(SCPreferencesRef prefs, Boolean wait)
60 {
61 Boolean ok;
62 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
63 uint32_t status = kSCStatusOK;
64 CFDataRef reply = NULL;
65
66 if (prefsPrivate->helper == -1) {
67 ok = __SCPreferencesCreate_helper(prefs);
68 if (!ok) {
69 return FALSE;
70 }
71 }
72
73 // have the helper "lock" the prefs
74 status = kSCStatusOK;
75 reply = NULL;
76 ok = _SCHelperExec(prefsPrivate->helper,
77 wait ? SCHELPER_MSG_PREFS_LOCKWAIT : SCHELPER_MSG_PREFS_LOCK,
78 prefsPrivate->signature,
79 &status,
80 NULL);
81 if (!ok) {
82 goto fail;
83 }
84
85 if (status != kSCStatusOK) {
86 goto error;
87 }
88
89 prefsPrivate->locked = TRUE;
90 return TRUE;
91
92 fail :
93
94 // close helper
95 if (prefsPrivate->helper != -1) {
96 _SCHelperClose(prefsPrivate->helper);
97 prefsPrivate->helper = -1;
98 }
99
100 status = kSCStatusAccessError;
101
102 error :
103
104 // return error
105 _SCErrorSet(status);
106 return FALSE;
107 }
108
109
110 static int
111 createParentDirectory(const char *path)
112 {
113 char dir[PATH_MAX];
114 int ret;
115 char *scan;
116 char *slash;
117
118 // get parent directory path
119 if (strlcpy(dir, path, sizeof(dir)) >= sizeof(dir)) {
120 errno = ENOENT;
121 return -1;
122 }
123
124 slash = strrchr(dir, '/');
125 if ((slash == NULL) || (slash == dir)) {
126 errno = ENOENT;
127 return -1;
128 }
129 *slash = '\0';
130
131 // create parent directories
132 for (scan = dir; TRUE; scan = slash) {
133 mode_t mode;
134 char sep = '\0';
135
136 if ((slash == NULL) || (scan == dir)) {
137 mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; // 755
138 } else {
139 mode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; // 775
140 }
141
142 if (slash != NULL) {
143 sep = *slash;
144 *slash = '\0';
145 }
146
147 ret = mkdir(dir, mode);
148 if (ret == 0) {
149 static gid_t admin = -1;
150
151 // set group
152 if (admin == -1) {
153 char buf[256];
154 struct group grp;
155 struct group *grpP = NULL;
156
157 if ((getgrnam_r("admin", &grp, buf, sizeof(buf), &grpP) == 0) &&
158 (grpP != NULL)) {
159 admin = grpP->gr_gid;
160 } else {
161 SCLog(TRUE, LOG_ERR,
162 CFSTR("SCPreferencesLock getgrnam_r() failed: %s"),
163 strerror(errno));
164 admin = 80;
165 }
166 }
167
168 if (chown(dir, -1, admin) == -1) {
169 SCLog(TRUE, LOG_ERR,
170 CFSTR("SCPreferencesLock chown() failed: %s"),
171 strerror(errno));
172 }
173
174 // set [force] mode
175 if (chmod(dir, mode) == -1) {
176 SCLog(TRUE, LOG_ERR,
177 CFSTR("SCPreferencesLock chmod() failed: %s"),
178 strerror(errno));
179 }
180
181 if ((slash == NULL) || (scan == dir)) {
182 return 0;
183 }
184 } else if ((errno == ENOENT) && (scan == dir)) {
185 // the initial mkdir (of the full dir path) can fail
186 ;
187 } else if (errno == EROFS) {
188 return -1;
189 } else if (errno != EEXIST) {
190 break;
191 }
192
193 if (slash != NULL) {
194 *slash = sep;
195 } else {
196 break;
197 }
198 slash = strchr(scan + 1, '/');
199 }
200
201 SCLog(TRUE, LOG_ERR,
202 CFSTR("SCPreferencesLock mkdir() failed: %s"),
203 strerror(errno));
204 return -1;
205 }
206
207
208 static void
209 reportDelay(SCPreferencesRef prefs, struct timeval *delay, Boolean isStale)
210 {
211 aslmsg m;
212 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
213 char str[256];
214
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),
222 "%d.%3.3d",
223 (int)delay->tv_sec,
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"),
228 prefsPrivate->name,
229 prefsPrivate->prefsID,
230 (int)delay->tv_sec,
231 delay->tv_usec / 1000,
232 isStale ? " (stale)" : "");
233 asl_free(m);
234
235 return;
236 }
237
238
239 Boolean
240 SCPreferencesLock(SCPreferencesRef prefs, Boolean wait)
241 {
242 char buf[32];
243 struct timeval lockStart;
244 struct timeval lockElapsed;
245 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
246 int sc_status = kSCStatusFailed;
247 struct stat statBuf;
248 struct stat statBuf2;
249
250 if (prefs == NULL) {
251 /* sorry, you must provide a session */
252 _SCErrorSet(kSCStatusNoPrefsSession);
253 return FALSE;
254 }
255
256 if (prefsPrivate->locked) {
257 /* sorry, you already have the lock */
258 _SCErrorSet(kSCStatusLocked);
259 return FALSE;
260 }
261
262 if (prefsPrivate->authorizationData != NULL) {
263 return __SCPreferencesLock_helper(prefs, wait);
264 }
265
266 if (!prefsPrivate->isRoot) {
267 _SCErrorSet(kSCStatusAccessError);
268 return FALSE;
269 }
270
271
272 pthread_mutex_lock(&prefsPrivate->lock);
273
274 if (prefsPrivate->session == NULL) {
275 __SCPreferencesAddSession(prefs);
276 }
277
278 if (prefsPrivate->lockPath == NULL) {
279 char *path;
280 int pathLen;
281
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);
286 }
287
288 (void)gettimeofday(&lockStart, NULL);
289
290 retry :
291
292 prefsPrivate->lockFD = open(prefsPrivate->lockPath,
293 wait ? O_WRONLY|O_CREAT|O_EXLOCK
294 : O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
295 0644);
296 if (prefsPrivate->lockFD == -1) {
297 switch (errno) {
298 case ENOENT :
299 if ((prefsPrivate->prefsID == NULL) ||
300 !CFStringHasPrefix(prefsPrivate->prefsID, CFSTR("/"))) {
301 int ret;
302
303 // create parent (/Library/Preferences/SystemConfiguration)
304 ret = createParentDirectory(prefsPrivate->lockPath);
305 if (ret == 0) {
306 SCLog(TRUE, LOG_NOTICE,
307 CFSTR("created directory for \"%s\""),
308 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
309 goto retry;
310 } else if (errno == EROFS) {
311 goto locked;
312 }
313 }
314 break;
315 case EROFS :
316 // if read-only filesystem
317 goto locked;
318 case EWOULDBLOCK :
319 // if already locked (and we are not blocking)
320 sc_status = kSCStatusPrefsBusy;
321 goto error;
322 default :
323 break;
324 }
325
326 sc_status = errno;
327 SCLog(TRUE, LOG_ERR,
328 CFSTR("SCPreferencesLock open() failed: %s"),
329 strerror(errno));
330 goto error;
331 }
332
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;
340 goto retry;
341 }
342
343 // we have the lock
344
345 snprintf(buf, sizeof(buf), "%d\n", getpid());
346 write(prefsPrivate->lockFD, buf, strlen(buf));
347
348 locked :
349
350 (void)gettimeofday(&prefsPrivate->lockTime, NULL);
351 timersub(&prefsPrivate->lockTime, &lockStart, &lockElapsed);
352
353 if (prefsPrivate->accessed) {
354 CFDataRef currentSignature;
355 Boolean match;
356
357 /*
358 * the preferences have been accessed since the
359 * session was created so we need to compare
360 * the signature of the stored preferences.
361 */
362 if (stat(prefsPrivate->path, &statBuf) == -1) {
363 if (errno == ENOENT) {
364 bzero(&statBuf, sizeof(statBuf));
365 } else {
366 SCLog(TRUE, LOG_DEBUG,
367 CFSTR("SCPreferencesLock stat() failed: %s"),
368 strerror(errno));
369 goto stale;
370 }
371 }
372
373 currentSignature = __SCPSignatureFromStatbuf(&statBuf);
374 match = CFEqual(prefsPrivate->signature, currentSignature);
375 CFRelease(currentSignature);
376 if (!match) {
377 /*
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.
381 */
382 goto stale;
383 }
384 // } else {
385 // /*
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.
389 // */
390 }
391
392 if (lockElapsed.tv_sec > 0) {
393 // if we waited more than 1 second to acquire the lock
394 reportDelay(prefs, &lockElapsed, FALSE);
395 }
396
397 prefsPrivate->locked = TRUE;
398 pthread_mutex_unlock(&prefsPrivate->lock);
399 return TRUE;
400
401 stale :
402
403 sc_status = kSCStatusStale;
404 unlink(prefsPrivate->lockPath);
405
406 if (lockElapsed.tv_sec > 0) {
407 // if we waited more than 1 second to acquire the lock
408 reportDelay(prefs, &lockElapsed, TRUE);
409 }
410
411 error :
412
413 if (prefsPrivate->lockFD != -1) {
414 close(prefsPrivate->lockFD);
415 prefsPrivate->lockFD = -1;
416 }
417
418 pthread_mutex_unlock(&prefsPrivate->lock);
419 _SCErrorSet(sc_status);
420 return FALSE;
421 }