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