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