]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPCommit.c
configd-395.6.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPCommit.c
1 /*
2 * Copyright (c) 2000-2008, 2010, 2011 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 <fcntl.h>
41 #include <unistd.h>
42 #include <sys/errno.h>
43
44 static Boolean
45 __SCPreferencesCommitChanges_helper(SCPreferencesRef prefs)
46 {
47 CFDataRef data = NULL;
48 Boolean ok;
49 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
50 uint32_t status = kSCStatusOK;
51 CFDataRef reply = NULL;
52
53 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
54 // if no helper
55 status = kSCStatusAccessError;
56 goto fail;
57 }
58
59 if (prefsPrivate->changed) {
60 ok = _SCSerialize(prefsPrivate->prefs, &data, NULL, NULL);
61 if (!ok) {
62 status = kSCStatusFailed;
63 goto error;
64 }
65 }
66
67 // have the helper "commit" the prefs
68 // status = kSCStatusOK;
69 // reply = NULL;
70 ok = _SCHelperExec(prefsPrivate->helper_port,
71 SCHELPER_MSG_PREFS_COMMIT,
72 data,
73 &status,
74 &reply);
75 if (data != NULL) CFRelease(data);
76 if (!ok) {
77 goto fail;
78 }
79
80 if (status != kSCStatusOK) {
81 goto error;
82 }
83
84 if (prefsPrivate->changed) {
85 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
86 prefsPrivate->signature = reply;
87 } else {
88 if (reply != NULL) CFRelease(reply);
89 }
90
91 prefsPrivate->changed = FALSE;
92 return TRUE;
93
94 fail :
95
96 // close helper
97 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
98 _SCHelperClose(&prefsPrivate->helper_port);
99 }
100
101 error :
102
103 // return error
104 if (reply != NULL) CFRelease(reply);
105 _SCErrorSet(status);
106 return FALSE;
107 }
108
109
110 static ssize_t
111 writen(int ref, const void *data, size_t len)
112 {
113 size_t left = len;
114 ssize_t n;
115 const void *p = data;
116
117 while (left > 0) {
118 if ((n = write(ref, p, left)) == -1) {
119 if (errno != EINTR) {
120 return -1;
121 }
122 n = 0;
123 }
124 left -= n;
125 p += n;
126 }
127 return len;
128 }
129
130
131 Boolean
132 SCPreferencesCommitChanges(SCPreferencesRef prefs)
133 {
134 Boolean ok = FALSE;
135 char * path;
136 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
137 Boolean save = TRUE;
138 struct stat statBuf;
139 Boolean wasLocked;
140
141 if (prefs == NULL) {
142 /* sorry, you must provide a session */
143 _SCErrorSet(kSCStatusNoPrefsSession);
144 return FALSE;
145 }
146
147 /*
148 * Determine if the we have exclusive access to the preferences
149 * and acquire the lock if necessary.
150 */
151 wasLocked = prefsPrivate->locked;
152 if (!wasLocked) {
153 if (!SCPreferencesLock(prefs, TRUE)) {
154 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges SCPreferencesLock() failed"));
155 return FALSE;
156 }
157 }
158
159 if (prefsPrivate->authorizationData != NULL) {
160 ok = __SCPreferencesCommitChanges_helper(prefs);
161 if (ok) {
162 prefsPrivate->changed = FALSE;
163 }
164 goto done;
165 }
166
167 /*
168 * if necessary, apply changes
169 */
170 if (!prefsPrivate->changed) {
171 goto committed;
172 }
173
174 /*
175 * check if the preferences should be removed
176 */
177 if (CFDictionaryGetCount(prefsPrivate->prefs) == 0) {
178 CFBooleanRef val;
179
180 /* if empty */
181 if ((prefsPrivate->options != NULL) &&
182 CFDictionaryGetValueIfPresent(prefsPrivate->options,
183 kSCPreferencesOptionRemoveWhenEmpty,
184 (const void **)&val) &&
185 isA_CFBoolean(val) &&
186 CFBooleanGetValue(val)) {
187 /* if we've been asked to remove empty .plists */
188 save = FALSE;
189 }
190 }
191
192 path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path;
193 if (save) {
194 int fd;
195 CFDataRef newPrefs;
196 int pathLen;
197 char * thePath;
198
199 if (stat(prefsPrivate->path, &statBuf) == -1) {
200 if (errno == ENOENT) {
201 bzero(&statBuf, sizeof(statBuf));
202 statBuf.st_mode = 0644;
203 statBuf.st_uid = geteuid();
204 statBuf.st_gid = getegid();
205 } else {
206 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges stat() failed: %s"), strerror(errno));
207 goto done;
208 }
209 }
210
211 /* create the (new) preferences file */
212 pathLen = strlen(path) + sizeof("-new");
213 thePath = CFAllocatorAllocate(NULL, pathLen, 0);
214 snprintf(thePath, pathLen, "%s-new", path);
215
216 fd = open(thePath, O_WRONLY|O_CREAT, statBuf.st_mode);
217 if (fd == -1) {
218 _SCErrorSet(errno);
219 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges open() failed: %s"), strerror(errno));
220 CFAllocatorDeallocate(NULL, thePath);
221 goto done;
222 }
223
224 /* preserve permissions */
225 (void) fchown(fd, statBuf.st_uid, statBuf.st_gid);
226 (void) fchmod(fd, statBuf.st_mode);
227
228 /* write the new preferences */
229 newPrefs = CFPropertyListCreateData(NULL,
230 prefsPrivate->prefs,
231 #if TARGET_OS_IPHONE
232 kCFPropertyListBinaryFormat_v1_0,
233 #else // TARGET_OS_IPHONE
234 kCFPropertyListXMLFormat_v1_0,
235 #endif // TARGET_OS_IPHONE
236 0,
237 NULL);
238 if (!newPrefs) {
239 _SCErrorSet(kSCStatusFailed);
240 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges CFPropertyListCreateData() failed"));
241 SCLog(_sc_verbose, LOG_ERR, CFSTR(" prefs = %s"), path);
242 CFAllocatorDeallocate(NULL, thePath);
243 (void) close(fd);
244 goto done;
245 }
246 if (writen(fd, (const void *)CFDataGetBytePtr(newPrefs), CFDataGetLength(newPrefs)) == -1) {
247 _SCErrorSet(errno);
248 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges write() failed: %s"), strerror(errno));
249 SCLog(_sc_verbose, LOG_ERR, CFSTR(" path = %s"), thePath);
250 (void) unlink(thePath);
251 CFAllocatorDeallocate(NULL, thePath);
252 (void) close(fd);
253 CFRelease(newPrefs);
254 goto done;
255 }
256
257 #if !TARGET_OS_IPHONE
258 /* synchronize the file's in-core state with that on disk */
259 if (fsync(fd) == -1) {
260 _SCErrorSet(errno);
261 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges fsync() failed: %s"), strerror(errno));
262 SCLog(_sc_verbose, LOG_ERR, CFSTR(" path = %s"), thePath);
263 (void) unlink(thePath);
264 CFAllocatorDeallocate(NULL, thePath);
265 (void) close(fd);
266 CFRelease(newPrefs);
267 goto done;
268 }
269
270 /*
271 * ... and ask the drive to flush to the media
272 *
273 * Note: at present, this only works on HFS filesystems
274 */
275 (void) fcntl(fd, F_FULLFSYNC, 0);
276 #endif // !TARGET_OS_IPHONE
277
278 /* new preferences have been written */
279 if (close(fd) == -1) {
280 _SCErrorSet(errno);
281 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges close() failed: %s"), strerror(errno));
282 SCLog(_sc_verbose, LOG_ERR, CFSTR(" path = %s"), thePath);
283 (void) unlink(thePath);
284 CFAllocatorDeallocate(NULL, thePath);
285 CFRelease(newPrefs);
286 goto done;
287 }
288 CFRelease(newPrefs);
289
290 /* rename new->old */
291 if (rename(thePath, path) == -1) {
292 _SCErrorSet(errno);
293 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges rename() failed: %s"), strerror(errno));
294 SCLog(_sc_verbose, LOG_ERR, CFSTR(" path = %s --> %s"), thePath, path);
295 CFAllocatorDeallocate(NULL, thePath);
296 goto done;
297 }
298 CFAllocatorDeallocate(NULL, thePath);
299
300 if (prefsPrivate->newPath) {
301 /* prefs file saved in "new" directory */
302 (void) unlink(prefsPrivate->path);
303 (void) symlink(prefsPrivate->newPath, prefsPrivate->path);
304 CFAllocatorDeallocate(NULL, prefsPrivate->path);
305 prefsPrivate->path = path;
306 prefsPrivate->newPath = NULL;
307 }
308
309 /* grab the new signature */
310 if (stat(path, &statBuf) == -1) {
311 _SCErrorSet(errno);
312 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges stat() failed: %s"), strerror(errno));
313 SCLog(_sc_verbose, LOG_ERR, CFSTR(" path = %s"), thePath);
314 goto done;
315 }
316 } else {
317 /* remove the empty .plist */
318 unlink(path);
319
320 /* init the new signature */
321 bzero(&statBuf, sizeof(statBuf));
322 }
323
324 /* update signature */
325 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
326 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
327
328 committed :
329
330 /* post notification */
331 if (prefsPrivate->session == NULL) {
332 ok = TRUE;
333 } else {
334 ok = SCDynamicStoreNotifyValue(prefsPrivate->session, prefsPrivate->sessionKeyCommit);
335 if (!ok) {
336 SCLog(_sc_verbose, LOG_ERR, CFSTR("SCPreferencesCommitChanges SCDynamicStoreNotifyValue() failed"));
337 _SCErrorSet(kSCStatusFailed);
338 goto done;
339 }
340 }
341
342 prefsPrivate->changed = FALSE;
343
344 done :
345
346 if (!wasLocked) {
347 uint32_t status;
348
349 status = SCError(); // preserve status across unlock
350 (void) SCPreferencesUnlock(prefs);
351 _SCErrorSet(status);
352 }
353 return ok;
354 }