]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPLock.c
493c1ece7091e4700b0993d3b9281b05f5512504
[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
205 if (prefs == NULL) {
206 /* sorry, you must provide a session */
207 _SCErrorSet(kSCStatusNoPrefsSession);
208 return FALSE;
209 }
210
211 if (prefsPrivate->locked) {
212 /* sorry, you already have the lock */
213 _SCErrorSet(kSCStatusLocked);
214 return FALSE;
215 }
216
217 if (prefsPrivate->authorizationData != NULL) {
218 return __SCPreferencesLock_helper(prefs, wait);
219 }
220
221 if (!prefsPrivate->isRoot) {
222 _SCErrorSet(kSCStatusAccessError);
223 return FALSE;
224 }
225
226
227 pthread_mutex_lock(&prefsPrivate->lock);
228
229 if (prefsPrivate->session == NULL) {
230 __SCPreferencesAddSession(prefs);
231 }
232
233 if (prefsPrivate->lockPath == NULL) {
234 char *path;
235 int pathLen;
236
237 path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path;
238 pathLen = strlen(path) + sizeof("-lock");
239 prefsPrivate->lockPath = CFAllocatorAllocate(NULL, pathLen, 0);
240 snprintf(prefsPrivate->lockPath, pathLen, "%s-lock", path);
241 }
242
243 retry :
244
245 prefsPrivate->lockFD = open(prefsPrivate->lockPath, O_WRONLY|O_CREAT, 0644);
246 if (prefsPrivate->lockFD == -1) {
247 if (errno == EROFS) {
248 goto locked;
249 }
250
251 if ((errno == ENOENT) &&
252 ((prefsPrivate->prefsID == NULL) || !CFStringHasPrefix(prefsPrivate->prefsID, CFSTR("/")))) {
253 int ret;
254
255 // create parent (/Library/Preferences/SystemConfiguration)
256 ret = createParentDirectory(prefsPrivate->lockPath);
257 if (ret == 0) {
258 SCLog(TRUE, LOG_NOTICE,
259 CFSTR("created directory for \"%s\""),
260 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
261 goto retry;
262 } else if (errno == EROFS) {
263 goto locked;
264 }
265 }
266
267 sc_status = errno;
268 SCLog(TRUE, LOG_ERR,
269 CFSTR("SCPreferencesLock open() failed: %s"),
270 strerror(errno));
271 goto error;
272 }
273
274 if (flock(prefsPrivate->lockFD, wait ? LOCK_EX : LOCK_EX|LOCK_NB) == -1) {
275 switch (errno) {
276 case EWOULDBLOCK :
277 // if already locked (and we are not blocking)
278 sc_status = kSCStatusPrefsBusy;
279 break;
280 default :
281 sc_status = errno;
282 SCLog(TRUE, LOG_ERR,
283 CFSTR("SCPreferencesLock flock() failed: %s"),
284 strerror(errno));
285 break;
286 }
287
288 goto error;
289 }
290
291 if (stat(prefsPrivate->lockPath, &statbuf) == -1) {
292 // if the lock file was unlinked
293 close(prefsPrivate->lockFD);
294 prefsPrivate->lockFD = -1;
295 goto retry;
296 }
297
298 // we have the lock
299
300 snprintf(buf, sizeof(buf), "%d\n", getpid());
301 write(prefsPrivate->lockFD, buf, strlen(buf));
302
303 locked :
304
305 if (prefsPrivate->accessed) {
306 CFDataRef currentSignature;
307 Boolean match;
308 struct stat statBuf;
309
310 /*
311 * the preferences have been accessed since the
312 * session was created so we need to compare
313 * the signature of the stored preferences.
314 */
315 if (stat(prefsPrivate->path, &statBuf) == -1) {
316 if (errno == ENOENT) {
317 bzero(&statBuf, sizeof(statBuf));
318 } else {
319 SCLog(TRUE, LOG_DEBUG,
320 CFSTR("SCPreferencesLock stat() failed: %s"),
321 strerror(errno));
322 sc_status = kSCStatusStale;
323 unlink(prefsPrivate->lockPath);
324 goto error;
325 }
326 }
327
328 currentSignature = __SCPSignatureFromStatbuf(&statBuf);
329 match = CFEqual(prefsPrivate->signature, currentSignature);
330 CFRelease(currentSignature);
331 if (!match) {
332 /*
333 * the preferences have been updated since the
334 * session was accessed so we've got no choice
335 * but to deny the lock request.
336 */
337 sc_status = kSCStatusStale;
338 unlink(prefsPrivate->lockPath);
339 goto error;
340 }
341 // } else {
342 // /*
343 // * the file contents have changed but since we
344 // * haven't accessed any of the preference data we
345 // * don't need to return an error. Simply proceed.
346 // */
347 }
348
349 prefsPrivate->locked = TRUE;
350 pthread_mutex_unlock(&prefsPrivate->lock);
351 return TRUE;
352
353 error :
354
355 if (prefsPrivate->lockFD != -1) {
356 close(prefsPrivate->lockFD);
357 prefsPrivate->lockFD = -1;
358 }
359
360 pthread_mutex_unlock(&prefsPrivate->lock);
361 _SCErrorSet(sc_status);
362 return FALSE;
363 }