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