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