]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPOpen.c
9dfde1d1c16bcf8f1c7a40a67497c383f6729f8b
[apple/configd.git] / SystemConfiguration.fproj / SCPOpen.c
1 /*
2 * Copyright(c) 2000-2009 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 * February 16, 2004 Allan Nathanson <ajn@apple.com>
28 * - add preference notification APIs
29 *
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
32 *
33 * November 9, 2000 Allan Nathanson <ajn@apple.com>
34 * - initial revision
35 */
36
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #if !TARGET_OS_IPHONE
41 #include <dispatch/dispatch.h>
42 #endif // !TARGET_OS_IPHONE
43 #include <SystemConfiguration/SystemConfiguration.h>
44 #include <SystemConfiguration/SCValidation.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include "SCPreferencesInternal.h"
47 #include "SCHelper_client.h"
48
49 #include "dy_framework.h"
50
51 #include <fcntl.h>
52 #include <pthread.h>
53 #include <unistd.h>
54 #include <sys/errno.h>
55
56
57 static __inline__ CFTypeRef
58 isA_SCPreferences(CFTypeRef obj)
59 {
60 return (isA_CFType(obj, SCPreferencesGetTypeID()));
61 }
62
63
64 static CFStringRef
65 __SCPreferencesCopyDescription(CFTypeRef cf) {
66 CFAllocatorRef allocator = CFGetAllocator(cf);
67 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
68 CFMutableStringRef result;
69
70 result = CFStringCreateMutable(allocator, 0);
71 CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator);
72 CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name);
73 CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID);
74 CFStringAppendFormat(result, NULL, CFSTR(", path = %s"),
75 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
76 if (prefsPrivate->accessed) {
77 CFStringAppendFormat(result, NULL, CFSTR(", accessed"));
78 }
79 if (prefsPrivate->changed) {
80 CFStringAppendFormat(result, NULL, CFSTR(", changed"));
81 }
82 if (prefsPrivate->locked) {
83 CFStringAppendFormat(result, NULL, CFSTR(", locked"));
84 }
85 if (prefsPrivate->helper != -1) {
86 CFStringAppendFormat(result, NULL, CFSTR(", helper fd=%d"), prefsPrivate->helper);
87 }
88 CFStringAppendFormat(result, NULL, CFSTR("}"));
89
90 return result;
91 }
92
93
94 static void
95 __SCPreferencesDeallocate(CFTypeRef cf)
96 {
97 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
98
99 /* release resources */
100
101 pthread_mutex_destroy(&prefsPrivate->lock);
102
103 if (prefsPrivate->name) CFRelease(prefsPrivate->name);
104 if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID);
105 if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path);
106 if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath);
107 if (prefsPrivate->lockFD != -1) {
108 if (prefsPrivate->lockPath != NULL) {
109 unlink(prefsPrivate->lockPath);
110 }
111 close(prefsPrivate->lockFD);
112 }
113 if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath);
114 if (prefsPrivate->signature) CFRelease(prefsPrivate->signature);
115 if (prefsPrivate->session) CFRelease(prefsPrivate->session);
116 if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit);
117 if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply);
118 if (prefsPrivate->rlsContext.release != NULL) {
119 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
120 }
121 if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs);
122 if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData);
123 if (prefsPrivate->helper != -1) {
124 (void) _SCHelperExec(prefsPrivate->helper, SCHELPER_MSG_PREFS_CLOSE, NULL, NULL, NULL);
125 _SCHelperClose(prefsPrivate->helper);
126 }
127
128 return;
129 }
130
131
132 static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
133
134
135 static const CFRuntimeClass __SCPreferencesClass = {
136 0, // version
137 "SCPreferences", // className
138 NULL, // init
139 NULL, // copy
140 __SCPreferencesDeallocate, // dealloc
141 NULL, // equal
142 NULL, // hash
143 NULL, // copyFormattingDesc
144 __SCPreferencesCopyDescription // copyDebugDesc
145 };
146
147
148 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
149
150 static void
151 __SCPreferencesInitialize(void) {
152 __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass);
153 return;
154 }
155
156
157 static SCPreferencesPrivateRef
158 __SCPreferencesCreatePrivate(CFAllocatorRef allocator)
159 {
160 SCPreferencesPrivateRef prefsPrivate;
161 uint32_t size;
162
163 /* initialize runtime */
164 pthread_once(&initialized, __SCPreferencesInitialize);
165
166 /* allocate prefs session */
167 size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase);
168 prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
169 __kSCPreferencesTypeID,
170 size,
171 NULL);
172 if (prefsPrivate == NULL) {
173 return NULL;
174 }
175
176 pthread_mutex_init(&prefsPrivate->lock, NULL);
177
178 prefsPrivate->name = NULL;
179 prefsPrivate->prefsID = NULL;
180 prefsPrivate->path = NULL;
181 prefsPrivate->newPath = NULL; // new prefs path
182 prefsPrivate->locked = FALSE;
183 prefsPrivate->lockFD = -1;
184 prefsPrivate->lockPath = NULL;
185 prefsPrivate->signature = NULL;
186 prefsPrivate->session = NULL;
187 prefsPrivate->sessionKeyCommit = NULL;
188 prefsPrivate->sessionKeyApply = NULL;
189 prefsPrivate->scheduled = FALSE;
190 prefsPrivate->rls = NULL;
191 prefsPrivate->rlsFunction = NULL;
192 prefsPrivate->rlsContext.info = NULL;
193 prefsPrivate->rlsContext.retain = NULL;
194 prefsPrivate->rlsContext.release = NULL;
195 prefsPrivate->rlsContext.copyDescription = NULL;
196 prefsPrivate->rlList = NULL;
197 #if !TARGET_OS_IPHONE
198 prefsPrivate->dispatchQueue = NULL;
199 #endif // !TARGET_OS_IPHONE
200 prefsPrivate->prefs = NULL;
201 prefsPrivate->accessed = FALSE;
202 prefsPrivate->changed = FALSE;
203 prefsPrivate->isRoot = (geteuid() == 0);
204 prefsPrivate->authorizationData = NULL;
205 prefsPrivate->helper = -1;
206
207 return prefsPrivate;
208 }
209
210
211 __private_extern__ Boolean
212 __SCPreferencesCreate_helper(SCPreferencesRef prefs)
213 {
214 CFDataRef data = NULL;
215 CFMutableDictionaryRef info;
216 CFNumberRef num;
217 Boolean ok;
218 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
219 uint32_t status = kSCStatusOK;
220 uint32_t pid = getpid();
221
222 // start helper
223 prefsPrivate->helper = _SCHelperOpen(prefsPrivate->authorizationData);
224 if (prefsPrivate->helper == -1) {
225 goto fail;
226 }
227
228 // create a dictionary of information to pass to the helper
229 info = CFDictionaryCreateMutable(NULL,
230 0,
231 &kCFTypeDictionaryKeyCallBacks,
232 &kCFTypeDictionaryValueCallBacks);
233 if (prefsPrivate->prefsID != NULL) {
234 CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID);
235 }
236 CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name);
237 num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid);
238 CFDictionarySetValue(info, CFSTR("PID"), num);
239 CFRelease(num);
240
241 // serialize the info
242 ok = _SCSerialize(info, &data, NULL, NULL);
243 CFRelease(info);
244 if (data == NULL || !ok) {
245 goto fail;
246 }
247
248 // have the helper "open" the prefs
249 ok = _SCHelperExec(prefsPrivate->helper,
250 SCHELPER_MSG_PREFS_OPEN,
251 data,
252 &status,
253 NULL);
254 if (data != NULL) CFRelease(data);
255 if (!ok) {
256 goto fail;
257 }
258
259 if (status != kSCStatusOK) {
260 goto error;
261 }
262
263 return TRUE;
264
265 fail :
266
267 // close helper
268 if (prefsPrivate->helper != -1) {
269 _SCHelperClose(prefsPrivate->helper);
270 prefsPrivate->helper = -1;
271 }
272
273 status = kSCStatusAccessError;
274
275 error :
276
277 // return error
278 _SCErrorSet(status);
279 return FALSE;
280 }
281
282
283 static Boolean
284 __SCPreferencesAccess_helper(SCPreferencesRef prefs)
285 {
286 Boolean ok;
287 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
288 CFDictionaryRef serverDict = NULL;
289 CFDictionaryRef serverPrefs = NULL;
290 CFDictionaryRef serverSignature = NULL;
291 uint32_t status = kSCStatusOK;
292 CFDataRef reply = NULL;
293
294 if (prefsPrivate->helper == -1) {
295 ok = __SCPreferencesCreate_helper(prefs);
296 if (!ok) {
297 return FALSE;
298 }
299 }
300
301 // have the helper "access" the prefs
302 ok = _SCHelperExec(prefsPrivate->helper,
303 SCHELPER_MSG_PREFS_ACCESS,
304 NULL,
305 &status,
306 &reply);
307 if (!ok) {
308 goto fail;
309 }
310
311 if (status != kSCStatusOK) {
312 goto error;
313 }
314
315 if (reply == NULL) {
316 goto fail;
317 }
318
319 ok = _SCUnserialize((CFPropertyListRef *)&serverDict, reply, NULL, 0);
320 CFRelease(reply);
321 if (!ok) {
322 goto fail;
323 }
324
325 if (isA_CFDictionary(serverDict)) {
326 serverPrefs = CFDictionaryGetValue(serverDict, CFSTR("preferences"));
327 serverSignature = CFDictionaryGetValue(serverDict, CFSTR("signature"));
328 }
329
330 if (!isA_CFDictionary(serverPrefs) || !isA_CFData(serverSignature)) {
331 CFRelease(serverDict);
332 goto fail;
333 }
334
335 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, serverPrefs);
336 prefsPrivate->signature = CFRetain(serverSignature);
337 prefsPrivate->accessed = TRUE;
338 CFRelease(serverDict);
339
340 return TRUE;
341
342 fail :
343
344 // close helper
345 if (prefsPrivate->helper != -1) {
346 _SCHelperClose(prefsPrivate->helper);
347 prefsPrivate->helper = -1;
348 }
349
350 status = kSCStatusAccessError;
351
352 error :
353
354 // return error
355 _SCErrorSet(status);
356 return FALSE;
357 }
358
359
360 static SCPreferencesPrivateRef
361 __SCPreferencesCreate(CFAllocatorRef allocator,
362 CFStringRef name,
363 CFStringRef prefsID,
364 CFDataRef authorizationData)
365 {
366 int fd = -1;
367 SCPreferencesPrivateRef prefsPrivate;
368 int sc_status = kSCStatusOK;
369
370 /*
371 * allocate and initialize a new prefs session
372 */
373 prefsPrivate = __SCPreferencesCreatePrivate(allocator);
374 if (prefsPrivate == NULL) {
375 return NULL;
376 }
377
378 prefsPrivate->name = CFStringCreateCopy(allocator, name);
379 if (prefsID != NULL) {
380 prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID);
381 }
382 if (authorizationData != NULL) {
383 prefsPrivate->authorizationData = CFRetain(authorizationData);
384 }
385
386 retry :
387
388 /*
389 * convert prefsID to path
390 */
391 prefsPrivate->path = __SCPreferencesPath(allocator,
392 prefsID,
393 (prefsPrivate->newPath == NULL));
394 if (prefsPrivate->path == NULL) {
395 sc_status = kSCStatusFailed;
396 goto error;
397 }
398
399 /*
400 * open file
401 */
402 fd = open(prefsPrivate->path, O_RDONLY, 0644);
403 if (fd != -1) {
404 (void) close(fd);
405 } else {
406 switch (errno) {
407 case ENOENT :
408 /* no prefs file */
409 if ((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/"))) {
410 /* if default preference ID or relative path */
411 if (prefsPrivate->newPath == NULL) {
412 /*
413 * we've looked in the "new" prefs directory
414 * without success. Save the "new" path and
415 * look in the "old" prefs directory.
416 */
417 prefsPrivate->newPath = prefsPrivate->path;
418 goto retry;
419 } else {
420 /*
421 * we've looked in both the "new" and "old"
422 * prefs directories without success. Use
423 * the "new" path.
424 */
425 CFAllocatorDeallocate(NULL, prefsPrivate->path);
426 prefsPrivate->path = prefsPrivate->newPath;
427 prefsPrivate->newPath = NULL;
428 }
429 }
430
431 /* no preference data, start fresh */
432 sc_status = kSCStatusNoConfigFile;
433 goto done;
434 case EACCES :
435 if (prefsPrivate->authorizationData != NULL) {
436 /* no problem, we'll be using the helper */
437 goto done;
438 }
439
440 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno));
441 sc_status = kSCStatusAccessError;
442 break;
443 default :
444 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno));
445 sc_status = kSCStatusFailed;
446 break;
447 }
448 goto error;
449 }
450
451 done :
452
453 /* all OK */
454 _SCErrorSet(sc_status);
455 return prefsPrivate;
456
457 error :
458
459 if (fd != -1) (void) close(fd);
460 CFRelease(prefsPrivate);
461 _SCErrorSet(sc_status);
462 return NULL;
463 }
464
465
466 __private_extern__ void
467 __SCPreferencesAccess(SCPreferencesRef prefs)
468 {
469 CFAllocatorRef allocator = CFGetAllocator(prefs);
470 int fd = -1;
471 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
472 struct stat statBuf;
473
474 if (prefsPrivate->accessed) {
475 // if preference data has already been accessed
476 return;
477 }
478
479 fd = open(prefsPrivate->path, O_RDONLY, 0644);
480 if (fd != -1) {
481 // create signature
482 if (fstat(fd, &statBuf) == -1) {
483 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesAccess fstat() failed: %s"), strerror(errno));
484 bzero(&statBuf, sizeof(statBuf));
485 }
486 } else {
487 switch (errno) {
488 case ENOENT :
489 /* no preference data, start fresh */
490 break;
491 case EACCES :
492 if (prefsPrivate->authorizationData != NULL) {
493 if (__SCPreferencesAccess_helper(prefs)) {
494 goto done;
495 } else {
496 SCLog(TRUE, LOG_ERR,
497 CFSTR("__SCPreferencesAccess_helper() failed: %s"),
498 SCErrorString(SCError()));
499 }
500 break;
501 }
502 // fall through
503 default :
504 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesAccess open() failed: %s"), strerror(errno));
505 break;
506 }
507 bzero(&statBuf, sizeof(statBuf));
508 }
509
510 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
511 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
512
513 if (statBuf.st_size > 0) {
514 CFDictionaryRef dict;
515 CFMutableDataRef xmlData;
516 CFStringRef xmlError;
517
518 /*
519 * extract property list
520 */
521 xmlData = CFDataCreateMutable(allocator, statBuf.st_size);
522 CFDataSetLength(xmlData, statBuf.st_size);
523 if (read(fd, (void *)CFDataGetBytePtr(xmlData), statBuf.st_size) != statBuf.st_size) {
524 /* corrupt prefs file, start fresh */
525 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess read(): could not load preference data."));
526 CFRelease(xmlData);
527 xmlData = NULL;
528 goto done;
529 }
530
531 /*
532 * load preferences
533 */
534 dict = CFPropertyListCreateFromXMLData(allocator,
535 xmlData,
536 kCFPropertyListImmutable,
537 &xmlError);
538 CFRelease(xmlData);
539 if (dict == NULL) {
540 /* corrupt prefs file, start fresh */
541 if (xmlError != NULL) {
542 SCLog(TRUE, LOG_ERR,
543 CFSTR("__SCPreferencesAccess CFPropertyListCreateFromXMLData(): %@"),
544 xmlError);
545 CFRelease(xmlError);
546 }
547 goto done;
548 }
549
550 /*
551 * make sure that we've got a dictionary
552 */
553 if (!isA_CFDictionary(dict)) {
554 /* corrupt prefs file, start fresh */
555 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess CFGetTypeID(): not a dictionary."));
556 CFRelease(dict);
557 goto done;
558 }
559
560 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict);
561 CFRelease(dict);
562 }
563
564 done :
565
566 if (fd != -1) {
567 (void) close(fd);
568 }
569
570 if (prefsPrivate->prefs == NULL) {
571 /*
572 * new file, create empty preferences
573 */
574 // SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess(): creating new preferences file."));
575 prefsPrivate->prefs = CFDictionaryCreateMutable(allocator,
576 0,
577 &kCFTypeDictionaryKeyCallBacks,
578 &kCFTypeDictionaryValueCallBacks);
579 prefsPrivate->changed = TRUE;
580 }
581
582 prefsPrivate->accessed = TRUE;
583 return;
584 }
585
586
587 SCPreferencesRef
588 SCPreferencesCreate(CFAllocatorRef allocator,
589 CFStringRef name,
590 CFStringRef prefsID)
591 {
592 SCPreferencesPrivateRef prefsPrivate;
593
594 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, NULL);
595 return (SCPreferencesRef)prefsPrivate;
596 }
597
598
599 SCPreferencesRef
600 SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator,
601 CFStringRef name,
602 CFStringRef prefsID,
603 AuthorizationRef authorization)
604 {
605 CFDataRef authorizationData = NULL;
606 SCPreferencesPrivateRef prefsPrivate;
607
608 #if !TARGET_OS_IPHONE
609 AuthorizationExternalForm extForm;
610 OSStatus os_status;
611
612 os_status = AuthorizationMakeExternalForm(authorization, &extForm);
613 if (os_status != errAuthorizationSuccess) {
614 SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen AuthorizationMakeExternalForm() failed"));
615 _SCErrorSet(kSCStatusInvalidArgument);
616 return NULL;
617 }
618
619 authorizationData = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes));
620 #else // !TARGET_OS_IPHONE
621 CFBundleRef bundle;
622 CFStringRef bundleID = NULL;
623
624 /* get the application/executable/bundle name */
625 bundle = CFBundleGetMainBundle();
626 if (bundle != NULL) {
627 bundleID = CFBundleGetIdentifier(bundle);
628 if (bundleID != NULL) {
629 CFRetain(bundleID);
630 } else {
631 CFURLRef url;
632
633 url = CFBundleCopyExecutableURL(bundle);
634 if (url != NULL) {
635 bundleID = CFURLCopyPath(url);
636 CFRelease(url);
637 }
638 }
639
640 if (bundleID != NULL) {
641 if (CFEqual(bundleID, CFSTR("/"))) {
642 CFRelease(bundleID);
643 bundleID = NULL;
644 }
645 }
646 }
647 if (bundleID == NULL) {
648 bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown(%d)"), getpid());
649 }
650
651 _SCSerializeString(bundleID, &authorizationData, NULL, NULL);
652 CFRelease(bundleID);
653 #endif // !TARGET_OS_IPHONE
654
655 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData);
656 CFRelease(authorizationData);
657
658 return (SCPreferencesRef)prefsPrivate;
659 }
660
661
662 CFTypeID
663 SCPreferencesGetTypeID(void) {
664 pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */
665 return __kSCPreferencesTypeID;
666 }
667
668
669 static void
670 prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
671 {
672 void *context_info;
673 void (*context_release)(const void *);
674 CFIndex i;
675 CFIndex n;
676 SCPreferencesNotification notify = 0;
677 SCPreferencesRef prefs = (SCPreferencesRef)info;
678 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
679 SCPreferencesCallBack rlsFunction;
680
681 n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0;
682 for (i = 0; i < n; i++) {
683 CFStringRef key;
684
685 key = CFArrayGetValueAtIndex(changedKeys, i);
686 if (CFEqual(key, prefsPrivate->sessionKeyCommit)) {
687 // if preferences have been saved
688 notify |= kSCPreferencesNotificationCommit;
689 }
690 if (CFEqual(key, prefsPrivate->sessionKeyApply)) {
691 // if stored preferences should be applied to current configuration
692 notify |= kSCPreferencesNotificationApply;
693 }
694 }
695
696 if (notify == 0) {
697 // if no changes
698 return;
699 }
700
701 pthread_mutex_lock(&prefsPrivate->lock);
702
703 /* callout */
704 rlsFunction = prefsPrivate->rlsFunction;
705 if (prefsPrivate->rlsContext.retain != NULL) {
706 context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info);
707 context_release = prefsPrivate->rlsContext.release;
708 } else {
709 context_info = prefsPrivate->rlsContext.info;
710 context_release = NULL;
711 }
712
713 pthread_mutex_unlock(&prefsPrivate->lock);
714
715 if (rlsFunction != NULL) {
716 (*rlsFunction)(prefs, notify, context_info);
717 }
718
719 if (context_release != NULL) {
720 (*context_release)(context_info);
721 }
722
723 return;
724 }
725
726
727 __private_extern__ Boolean
728 __SCPreferencesAddSession(SCPreferencesRef prefs)
729 {
730 CFAllocatorRef allocator = CFGetAllocator(prefs);
731 SCDynamicStoreContext context = { 0
732 , (void *)prefs
733 , NULL
734 , NULL
735 , NULL
736 };
737 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
738
739 /* establish a dynamic store session */
740 prefsPrivate->session = SCDynamicStoreCreate(allocator,
741 prefsPrivate->name,
742 prefsNotify,
743 &context);
744 if (prefsPrivate->session == NULL) {
745 SCLog(_sc_verbose, LOG_INFO, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed"));
746 return FALSE;
747 }
748
749 /* create the session "commit" key */
750 prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL,
751 prefsPrivate->prefsID,
752 kSCPreferencesKeyCommit);
753
754 /* create the session "apply" key */
755 prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL,
756 prefsPrivate->prefsID,
757 kSCPreferencesKeyApply);
758
759 return TRUE;
760 }
761
762
763 Boolean
764 SCPreferencesSetCallback(SCPreferencesRef prefs,
765 SCPreferencesCallBack callout,
766 SCPreferencesContext *context)
767 {
768 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
769
770 if (!isA_SCPreferences(prefs)) {
771 /* sorry, you must provide a session */
772 _SCErrorSet(kSCStatusNoPrefsSession);
773 return FALSE;
774 }
775
776 pthread_mutex_lock(&prefsPrivate->lock);
777
778 if (prefsPrivate->rlsContext.release != NULL) {
779 /* let go of the current context */
780 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
781 }
782
783 prefsPrivate->rlsFunction = callout;
784 prefsPrivate->rlsContext.info = NULL;
785 prefsPrivate->rlsContext.retain = NULL;
786 prefsPrivate->rlsContext.release = NULL;
787 prefsPrivate->rlsContext.copyDescription = NULL;
788 if (context != NULL) {
789 bcopy(context, &prefsPrivate->rlsContext, sizeof(SCPreferencesContext));
790 if (context->retain != NULL) {
791 prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
792 }
793 }
794
795 pthread_mutex_unlock(&prefsPrivate->lock);
796
797 return TRUE;
798 }
799
800
801 static Boolean
802 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
803 CFRunLoopRef runLoop,
804 CFStringRef runLoopMode,
805 #if !TARGET_OS_IPHONE
806 dispatch_queue_t queue
807 #else // !TARGET_OS_IPHONE
808 void *queue
809 #endif // !TARGET_OS_IPHONE
810 )
811 {
812 Boolean ok = FALSE;
813 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
814
815 pthread_mutex_lock(&prefsPrivate->lock);
816
817 #if !TARGET_OS_IPHONE
818 if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
819 ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
820 _SCErrorSet(kSCStatusInvalidArgument);
821 goto done;
822 }
823 #endif // !TARGET_OS_IPHONE
824
825 if (!prefsPrivate->scheduled) {
826 CFMutableArrayRef keys;
827
828 if (prefsPrivate->session == NULL) {
829 ok = __SCPreferencesAddSession(prefs);
830 if (!ok) {
831 goto done;
832 }
833 }
834
835 CFRetain(prefs); // hold a reference to the prefs
836
837 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
838 CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit);
839 CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply);
840 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL);
841 CFRelease(keys);
842
843 if (runLoop != NULL) {
844 prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0);
845 prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
846 }
847
848 prefsPrivate->scheduled = TRUE;
849 }
850
851 #if !TARGET_OS_IPHONE
852 if (queue != NULL) {
853 ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue);
854 if (!ok) {
855 prefsPrivate->scheduled = FALSE;
856 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
857 CFRelease(prefs);
858 goto done;
859 }
860
861 prefsPrivate->dispatchQueue = queue;
862 dispatch_retain(prefsPrivate->dispatchQueue);
863 } else
864 #endif // !TARGET_OS_IPHONE
865 {
866 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
867 /*
868 * if we do not already have notifications scheduled with
869 * this runLoop / runLoopMode
870 */
871 CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode);
872 }
873
874 _SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList);
875 }
876
877 ok = TRUE;
878
879 done :
880
881 pthread_mutex_unlock(&prefsPrivate->lock);
882 return ok;
883 }
884
885
886 static Boolean
887 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
888 CFRunLoopRef runLoop,
889 CFStringRef runLoopMode)
890 {
891 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
892 CFIndex n = 0;
893 Boolean ok = FALSE;
894
895 pthread_mutex_lock(&prefsPrivate->lock);
896
897 if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not)
898 _SCErrorSet(kSCStatusInvalidArgument);
899 goto done;
900 }
901
902 #if !TARGET_OS_IPHONE
903 if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
904 ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
905 _SCErrorSet(kSCStatusInvalidArgument);
906 goto done;
907 }
908 #endif // !TARGET_OS_IPHONE
909
910 #if !TARGET_OS_IPHONE
911 if (runLoop == NULL) {
912 SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL);
913 dispatch_release(prefsPrivate->dispatchQueue);
914 prefsPrivate->dispatchQueue = NULL;
915 } else
916 #endif // !TARGET_OS_IPHONE
917 {
918 if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) {
919 // if not currently scheduled on this runLoop / runLoopMode
920 _SCErrorSet(kSCStatusInvalidArgument);
921 goto done;
922 }
923
924 n = CFArrayGetCount(prefsPrivate->rlList);
925 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
926 /*
927 * if we are no longer scheduled to receive notifications for
928 * this runLoop / runLoopMode
929 */
930 CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
931
932 if (n == 0) {
933 // if *all* notifications have been unscheduled
934 CFRelease(prefsPrivate->rlList);
935 prefsPrivate->rlList = NULL;
936 CFRunLoopSourceInvalidate(prefsPrivate->rls);
937 CFRelease(prefsPrivate->rls);
938 prefsPrivate->rls = NULL;
939 }
940 }
941 }
942
943 if (n == 0) {
944 CFArrayRef changedKeys;
945
946 // if *all* notifications have been unscheduled
947 prefsPrivate->scheduled = FALSE;
948
949 // no need to track changes
950 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
951
952 // clear out any pending notifications
953 changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
954 if (changedKeys != NULL) {
955 CFRelease(changedKeys);
956 }
957
958 // release our reference to the prefs
959 CFRelease(prefs);
960 }
961
962 ok = TRUE;
963
964 done :
965
966 pthread_mutex_unlock(&prefsPrivate->lock);
967 return ok;
968 }
969
970 Boolean
971 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
972 CFRunLoopRef runLoop,
973 CFStringRef runLoopMode)
974 {
975 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
976 _SCErrorSet(kSCStatusInvalidArgument);
977 return FALSE;
978 }
979
980 return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL);
981 }
982
983 Boolean
984 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
985 CFRunLoopRef runLoop,
986 CFStringRef runLoopMode)
987 {
988 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
989 _SCErrorSet(kSCStatusInvalidArgument);
990 return FALSE;
991 }
992
993 return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode);
994 }
995
996 #if !TARGET_OS_IPHONE
997 Boolean
998 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs,
999 dispatch_queue_t queue)
1000 {
1001 Boolean ok = FALSE;
1002
1003 if (!isA_SCPreferences(prefs)) {
1004 /* sorry, you must provide a session */
1005 _SCErrorSet(kSCStatusNoPrefsSession);
1006 return FALSE;
1007 }
1008
1009 if (queue != NULL) {
1010 ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue);
1011 } else {
1012 ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL);
1013 }
1014
1015 return ok;
1016 }
1017 #endif // !TARGET_OS_IPHONE
1018
1019
1020 static void
1021 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs)
1022 {
1023 Boolean ok;
1024 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1025 uint32_t status = kSCStatusOK;
1026
1027 if (prefsPrivate->helper == -1) {
1028 // if no helper
1029 return;
1030 }
1031
1032 // have the helper "synchronize" the prefs
1033 ok = _SCHelperExec(prefsPrivate->helper,
1034 SCHELPER_MSG_PREFS_SYNCHRONIZE,
1035 NULL,
1036 &status,
1037 NULL);
1038 if (!ok) {
1039 // close helper
1040 if (prefsPrivate->helper != -1) {
1041 _SCHelperClose(prefsPrivate->helper);
1042 prefsPrivate->helper = -1;
1043 }
1044 }
1045
1046 return;
1047 }
1048
1049
1050 void
1051 SCPreferencesSynchronize(SCPreferencesRef prefs)
1052 {
1053 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1054
1055 if (!isA_SCPreferences(prefs)) {
1056 /* sorry, you must provide a session */
1057 _SCErrorSet(kSCStatusNoPrefsSession);
1058 return;
1059 }
1060
1061 if (prefsPrivate->authorizationData != NULL) {
1062 __SCPreferencesSynchronize_helper(prefs);
1063 }
1064 if (prefsPrivate->prefs != NULL) {
1065 CFRelease(prefsPrivate->prefs);
1066 prefsPrivate->prefs = NULL;
1067 }
1068 if (prefsPrivate->signature != NULL) {
1069 CFRelease(prefsPrivate->signature);
1070 prefsPrivate->signature = NULL;
1071 }
1072 prefsPrivate->accessed = FALSE;
1073 prefsPrivate->changed = FALSE;
1074
1075 return;
1076 }