2 * Copyright (c) 2000-2008, 2010-2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
34 #include <TargetConditionals.h>
35 #include <SystemConfiguration/SystemConfiguration.h>
36 #include <SystemConfiguration/SCPrivate.h>
37 #include "SCPreferencesInternal.h"
38 #include "SCHelper_client.h"
42 #include <sys/errno.h>
45 __SCPreferencesCommitChanges_helper(SCPreferencesRef prefs
)
47 CFDataRef data
= NULL
;
49 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
50 uint32_t status
= kSCStatusOK
;
51 CFDataRef reply
= NULL
;
53 if (prefsPrivate
->helper_port
== MACH_PORT_NULL
) {
55 status
= kSCStatusAccessError
;
59 if (prefsPrivate
->changed
) {
60 ok
= _SCSerialize(prefsPrivate
->prefs
, &data
, NULL
, NULL
);
62 status
= kSCStatusFailed
;
65 CFSTR("SCPreferencesCommitChanges(-->helper) CFPropertyListCreateData() failed"));
68 prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
);
74 // have the helper "commit" the prefs
75 // status = kSCStatusOK;
77 ok
= _SCHelperExec(prefsPrivate
->helper_port
,
78 SCHELPER_MSG_PREFS_COMMIT
,
82 if (data
!= NULL
) CFRelease(data
);
87 if (status
!= kSCStatusOK
) {
91 if (prefsPrivate
->changed
) {
92 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
93 prefsPrivate
->signature
= reply
;
95 if (reply
!= NULL
) CFRelease(reply
);
98 prefsPrivate
->changed
= FALSE
;
104 if (prefsPrivate
->helper_port
!= MACH_PORT_NULL
) {
105 _SCHelperClose(&prefsPrivate
->helper_port
);
111 if (reply
!= NULL
) CFRelease(reply
);
118 writen(int ref
, const void *data
, size_t len
)
122 const void *p
= data
;
125 if ((n
= write(ref
, p
, left
)) == -1) {
126 if (errno
!= EINTR
) {
139 SCPreferencesCommitChanges(SCPreferencesRef prefs
)
143 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
149 /* sorry, you must provide a session */
150 _SCErrorSet(kSCStatusNoPrefsSession
);
155 * Determine if the we have exclusive access to the preferences
156 * and acquire the lock if necessary.
158 wasLocked
= prefsPrivate
->locked
;
160 if (!SCPreferencesLock(prefs
, TRUE
)) {
161 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges SCPreferencesLock() failed"));
166 if (prefsPrivate
->authorizationData
!= NULL
) {
167 ok
= __SCPreferencesCommitChanges_helper(prefs
);
169 prefsPrivate
->changed
= FALSE
;
175 * if necessary, apply changes
177 if (!prefsPrivate
->changed
) {
182 * check if the preferences should be removed
184 if (CFDictionaryGetCount(prefsPrivate
->prefs
) == 0) {
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 */
199 path
= prefsPrivate
->newPath
? prefsPrivate
->newPath
: prefsPrivate
->path
;
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();
213 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges stat() failed: %s"), strerror(errno
));
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
);
223 fd
= open(thePath
, O_WRONLY
|O_CREAT
, statBuf
.st_mode
);
226 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges open() failed: %s"), strerror(errno
));
227 CFAllocatorDeallocate(NULL
, thePath
);
231 /* preserve permissions */
232 (void) fchown(fd
, statBuf
.st_uid
, statBuf
.st_gid
);
233 (void) fchmod(fd
, statBuf
.st_mode
);
235 /* write the new preferences */
236 newPrefs
= CFPropertyListCreateData(NULL
,
239 kCFPropertyListBinaryFormat_v1_0
,
240 #else // TARGET_OS_IPHONE
241 kCFPropertyListXMLFormat_v1_0
,
242 #endif // TARGET_OS_IPHONE
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
);
253 if (writen(fd
, (const void *)CFDataGetBytePtr(newPrefs
), CFDataGetLength(newPrefs
)) == -1) {
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
);
264 #if !TARGET_OS_IPHONE
265 /* synchronize the file's in-core state with that on disk */
266 if (fsync(fd
) == -1) {
268 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges fsync() failed: %s"), strerror(errno
));
269 SCLog(_sc_verbose
, LOG_ERR
, CFSTR(" path = %s"), thePath
);
270 (void) unlink(thePath
);
271 CFAllocatorDeallocate(NULL
, thePath
);
278 * ... and ask the drive to flush to the media
280 * Note: at present, this only works on HFS filesystems
282 (void) fcntl(fd
, F_FULLFSYNC
, 0);
283 #endif // !TARGET_OS_IPHONE
285 /* new preferences have been written */
286 if (close(fd
) == -1) {
288 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges close() failed: %s"), strerror(errno
));
289 SCLog(_sc_verbose
, LOG_ERR
, CFSTR(" path = %s"), thePath
);
290 (void) unlink(thePath
);
291 CFAllocatorDeallocate(NULL
, thePath
);
297 /* rename new->old */
298 if (rename(thePath
, path
) == -1) {
300 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges rename() failed: %s"), strerror(errno
));
301 SCLog(_sc_verbose
, LOG_ERR
, CFSTR(" path = %s --> %s"), thePath
, path
);
302 CFAllocatorDeallocate(NULL
, thePath
);
305 CFAllocatorDeallocate(NULL
, thePath
);
307 if (prefsPrivate
->newPath
) {
308 /* prefs file saved in "new" directory */
309 (void) unlink(prefsPrivate
->path
);
310 (void) symlink(prefsPrivate
->newPath
, prefsPrivate
->path
);
311 CFAllocatorDeallocate(NULL
, prefsPrivate
->path
);
312 prefsPrivate
->path
= path
;
313 prefsPrivate
->newPath
= NULL
;
316 /* grab the new signature */
317 if (stat(path
, &statBuf
) == -1) {
319 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges stat() failed: %s"), strerror(errno
));
320 SCLog(_sc_verbose
, LOG_ERR
, CFSTR(" path = %s"), thePath
);
324 /* remove the empty .plist */
327 /* init the new signature */
328 bzero(&statBuf
, sizeof(statBuf
));
331 /* update signature */
332 if (prefsPrivate
->signature
!= NULL
) CFRelease(prefsPrivate
->signature
);
333 prefsPrivate
->signature
= __SCPSignatureFromStatbuf(&statBuf
);
337 /* post notification */
338 if (prefsPrivate
->session
== NULL
) {
341 ok
= SCDynamicStoreNotifyValue(prefsPrivate
->session
, prefsPrivate
->sessionKeyCommit
);
343 SCLog(_sc_verbose
, LOG_ERR
, CFSTR("SCPreferencesCommitChanges SCDynamicStoreNotifyValue() failed"));
344 _SCErrorSet(kSCStatusFailed
);
349 prefsPrivate
->changed
= FALSE
;
356 status
= SCError(); // preserve status across unlock
357 (void) SCPreferencesUnlock(prefs
);