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 44static 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 117static ssize_t 118writen(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 138Boolean 139SCPreferencesCommitChanges(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} 341