]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPLock.c
configd-210.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPLock.c
1 /*
2 * Copyright (c) 2000, 2001, 2004-2007 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 <unistd.h>
42 #include <pthread.h>
43 #include <sys/errno.h>
44
45
46
47 static Boolean
48 __SCPreferencesLock_helper(SCPreferencesRef prefs, Boolean wait)
49 {
50 Boolean ok;
51 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
52 uint32_t status = kSCStatusOK;
53 CFDataRef reply = NULL;
54
55 if (prefsPrivate->helper == -1) {
56 ok = __SCPreferencesCreate_helper(prefs);
57 if (!ok) {
58 return FALSE;
59 }
60 }
61
62 // have the helper "lock" the prefs
63 status = kSCStatusOK;
64 reply = NULL;
65 ok = _SCHelperExec(prefsPrivate->helper,
66 wait ? SCHELPER_MSG_PREFS_LOCKWAIT : SCHELPER_MSG_PREFS_LOCK,
67 prefsPrivate->signature,
68 &status,
69 NULL);
70 if (!ok) {
71 goto fail;
72 }
73
74 if (status != kSCStatusOK) {
75 goto error;
76 }
77
78 prefsPrivate->locked = TRUE;
79 return TRUE;
80
81 fail :
82
83 // close helper
84 if (prefsPrivate->helper != -1) {
85 _SCHelperClose(prefsPrivate->helper);
86 prefsPrivate->helper = -1;
87 }
88
89 status = kSCStatusAccessError;
90
91 error :
92
93 // return error
94 _SCErrorSet(status);
95 return FALSE;
96 }
97
98
99 static int
100 createParentDirectory(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;
123 char sep = '\0';
124
125 if ((slash == NULL) || (scan == dir)) {
126 mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; // 755
127 } else {
128 mode = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH; // 775
129 }
130
131 if (slash != NULL) {
132 sep = *slash;
133 *slash = '\0';
134 }
135
136 ret = mkdir(dir, mode);
137 if (ret == 0) {
138 static gid_t admin = -1;
139
140 // set group
141 if (admin == -1) {
142 char buf[256];
143 struct group grp;
144 struct group *grpP = NULL;
145
146 if ((getgrnam_r("admin", &grp, buf, sizeof(buf), &grpP) == 0) &&
147 (grpP != NULL)) {
148 admin = grpP->gr_gid;
149 } else {
150 SCLog(TRUE, LOG_ERR,
151 CFSTR("SCPreferencesLock getgrnam_r() failed: %s"),
152 strerror(errno));
153 admin = 80;
154 }
155 }
156
157 if (chown(dir, -1, admin) == -1) {
158 SCLog(TRUE, LOG_ERR,
159 CFSTR("SCPreferencesLock chown() failed: %s"),
160 strerror(errno));
161 }
162
163 // set [force] mode
164 if (chmod(dir, mode) == -1) {
165 SCLog(TRUE, LOG_ERR,
166 CFSTR("SCPreferencesLock chmod() failed: %s"),
167 strerror(errno));
168 }
169
170 if ((slash == NULL) || (scan == dir)) {
171 return 0;
172 }
173 } else if ((errno == ENOENT) && (scan == dir)) {
174 // the initial mkdir (of the full dir path) can fail
175 ;
176 } else if (errno == EROFS) {
177 return -1;
178 } else if (errno != EEXIST) {
179 break;
180 }
181
182 if (slash != NULL) {
183 *slash = sep;
184 } else {
185 break;
186 }
187 slash = strchr(scan + 1, '/');
188 }
189
190 SCLog(TRUE, LOG_ERR,
191 CFSTR("SCPreferencesLock mkdir() failed: %s"),
192 strerror(errno));
193 return -1;
194 }
195
196
197 Boolean
198 SCPreferencesLock(SCPreferencesRef prefs, Boolean wait)
199 {
200 char buf[32];
201 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
202 int sc_status = kSCStatusFailed;
203 struct stat statBuf;
204 struct stat statBuf2;
205
206 if (prefs == NULL) {
207 /* sorry, you must provide a session */
208 _SCErrorSet(kSCStatusNoPrefsSession);
209 return FALSE;
210 }
211
212 if (prefsPrivate->locked) {
213 /* sorry, you already have the lock */
214 _SCErrorSet(kSCStatusLocked);
215 return FALSE;
216 }
217
218 if (prefsPrivate->authorizationData != NULL) {
219 return __SCPreferencesLock_helper(prefs, wait);
220 }
221
222 if (!prefsPrivate->isRoot) {
223 _SCErrorSet(kSCStatusAccessError);
224 return FALSE;
225 }
226
227
228 pthread_mutex_lock(&prefsPrivate->lock);
229
230 if (prefsPrivate->session == NULL) {
231 __SCPreferencesAddSession(prefs);
232 }
233
234 if (prefsPrivate->lockPath == NULL) {
235 char *path;
236 int pathLen;
237
238 path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path;
239 pathLen = strlen(path) + sizeof("-lock");
240 prefsPrivate->lockPath = CFAllocatorAllocate(NULL, pathLen, 0);
241 snprintf(prefsPrivate->lockPath, pathLen, "%s-lock", path);
242 }
243
244 retry :
245
246 prefsPrivate->lockFD = open(prefsPrivate->lockPath,
247 wait ? O_WRONLY|O_CREAT|O_EXLOCK
248 : O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
249 0644);
250 if (prefsPrivate->lockFD == -1) {
251 switch (errno) {
252 case ENOENT :
253 if ((prefsPrivate->prefsID == NULL) ||
254 !CFStringHasPrefix(prefsPrivate->prefsID, CFSTR("/"))) {
255 int ret;
256
257 // create parent (/Library/Preferences/SystemConfiguration)
258 ret = createParentDirectory(prefsPrivate->lockPath);
259 if (ret == 0) {
260 SCLog(TRUE, LOG_NOTICE,
261 CFSTR("created directory for \"%s\""),
262 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
263 goto retry;
264 } else if (errno == EROFS) {
265 goto locked;
266 }
267 }
268 break;
269 case EROFS :
270 // if read-only filesystem
271 goto locked;
272 case EWOULDBLOCK :
273 // if already locked (and we are not blocking)
274 sc_status = kSCStatusPrefsBusy;
275 goto error;
276 default :
277 break;
278 }
279
280 sc_status = errno;
281 SCLog(TRUE, LOG_ERR,
282 CFSTR("SCPreferencesLock open() failed: %s"),
283 strerror(errno));
284 goto error;
285 }
286
287 if ((stat(prefsPrivate->lockPath, &statBuf) == -1) ||
288 (fstat(prefsPrivate->lockFD, &statBuf2) == -1) ||
289 (statBuf.st_dev != statBuf2.st_dev) ||
290 (statBuf.st_ino != statBuf2.st_ino)) {
291 // if the lock file was unlinked or re-created
292 close(prefsPrivate->lockFD);
293 prefsPrivate->lockFD = -1;
294 goto retry;
295 }
296
297 // we have the lock
298
299 snprintf(buf, sizeof(buf), "%d\n", getpid());
300 write(prefsPrivate->lockFD, buf, strlen(buf));
301
302 locked :
303
304 if (prefsPrivate->accessed) {
305 CFDataRef currentSignature;
306 Boolean match;
307
308 /*
309 * the preferences have been accessed since the
310 * session was created so we need to compare
311 * the signature of the stored preferences.
312 */
313 if (stat(prefsPrivate->path, &statBuf) == -1) {
314 if (errno == ENOENT) {
315 bzero(&statBuf, sizeof(statBuf));
316 } else {
317 SCLog(TRUE, LOG_DEBUG,
318 CFSTR("SCPreferencesLock stat() failed: %s"),
319 strerror(errno));
320 sc_status = kSCStatusStale;
321 unlink(prefsPrivate->lockPath);
322 goto error;
323 }
324 }
325
326 currentSignature = __SCPSignatureFromStatbuf(&statBuf);
327 match = CFEqual(prefsPrivate->signature, currentSignature);
328 CFRelease(currentSignature);
329 if (!match) {
330 /*
331 * the preferences have been updated since the
332 * session was accessed so we've got no choice
333 * but to deny the lock request.
334 */
335 sc_status = kSCStatusStale;
336 unlink(prefsPrivate->lockPath);
337 goto error;
338 }
339 // } else {
340 // /*
341 // * the file contents have changed but since we
342 // * haven't accessed any of the preference data we
343 // * don't need to return an error. Simply proceed.
344 // */
345 }
346
347 prefsPrivate->locked = TRUE;
348 pthread_mutex_unlock(&prefsPrivate->lock);
349 return TRUE;
350
351 error :
352
353 if (prefsPrivate->lockFD != -1) {
354 close(prefsPrivate->lockFD);
355 prefsPrivate->lockFD = -1;
356 }
357
358 pthread_mutex_unlock(&prefsPrivate->lock);
359 _SCErrorSet(sc_status);
360 return FALSE;
361 }