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