]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPOpen.c
7558a9ed5f36dbbdbe0d6917767aab0d7b2f121c
[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 <pthread.h>
51 #include <unistd.h>
52 #include <sys/errno.h>
53
54
55 const AuthorizationRef kSCPreferencesUseEntitlementAuthorization = (AuthorizationRef)CFSTR("UseEntitlement");
56
57
58 static __inline__ CFTypeRef
59 isA_SCPreferences(CFTypeRef obj)
60 {
61 return (isA_CFType(obj, SCPreferencesGetTypeID()));
62 }
63
64
65 static CFStringRef
66 __SCPreferencesCopyDescription(CFTypeRef cf) {
67 CFAllocatorRef allocator = CFGetAllocator(cf);
68 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
69 CFMutableStringRef result;
70
71 result = CFStringCreateMutable(allocator, 0);
72 CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator);
73 CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name);
74 CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID);
75 CFStringAppendFormat(result, NULL, CFSTR(", path = %s"),
76 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
77 if (prefsPrivate->accessed) {
78 CFStringAppendFormat(result, NULL, CFSTR(", accessed"));
79 }
80 if (prefsPrivate->changed) {
81 CFStringAppendFormat(result, NULL, CFSTR(", changed"));
82 }
83 if (prefsPrivate->locked) {
84 CFStringAppendFormat(result, NULL, CFSTR(", locked"));
85 }
86 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
87 CFStringAppendFormat(result, NULL, CFSTR(", helper port = %p"), prefsPrivate->helper_port);
88 }
89 CFStringAppendFormat(result, NULL, CFSTR("}"));
90
91 return result;
92 }
93
94
95 static void
96 __SCPreferencesDeallocate(CFTypeRef cf)
97 {
98 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf;
99
100 /* release resources */
101
102 pthread_mutex_destroy(&prefsPrivate->lock);
103
104 if (prefsPrivate->name) CFRelease(prefsPrivate->name);
105 if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID);
106 if (prefsPrivate->options) CFRelease(prefsPrivate->options);
107 if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path);
108 if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath);
109 if (prefsPrivate->lockFD != -1) {
110 if (prefsPrivate->lockPath != NULL) {
111 unlink(prefsPrivate->lockPath);
112 }
113 close(prefsPrivate->lockFD);
114 }
115 if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath);
116 if (prefsPrivate->signature) CFRelease(prefsPrivate->signature);
117 if (prefsPrivate->session) CFRelease(prefsPrivate->session);
118 if (prefsPrivate->sessionKeyLock) CFRelease(prefsPrivate->sessionKeyLock);
119 if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit);
120 if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply);
121 if (prefsPrivate->rlsContext.release != NULL) {
122 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
123 }
124 if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs);
125 if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData);
126 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
127 (void) _SCHelperExec(prefsPrivate->helper_port,
128 SCHELPER_MSG_PREFS_CLOSE,
129 NULL,
130 NULL,
131 NULL);
132 _SCHelperClose(&prefsPrivate->helper_port);
133 }
134
135 return;
136 }
137
138
139 static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID;
140
141
142 static const CFRuntimeClass __SCPreferencesClass = {
143 0, // version
144 "SCPreferences", // className
145 NULL, // init
146 NULL, // copy
147 __SCPreferencesDeallocate, // dealloc
148 NULL, // equal
149 NULL, // hash
150 NULL, // copyFormattingDesc
151 __SCPreferencesCopyDescription // copyDebugDesc
152 };
153
154
155 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
156
157 static void
158 __SCPreferencesInitialize(void) {
159 __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass);
160 return;
161 }
162
163
164 static SCPreferencesPrivateRef
165 __SCPreferencesCreatePrivate(CFAllocatorRef allocator)
166 {
167 SCPreferencesPrivateRef prefsPrivate;
168 uint32_t size;
169
170 /* initialize runtime */
171 pthread_once(&initialized, __SCPreferencesInitialize);
172
173 /* allocate prefs session */
174 size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase);
175 prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator,
176 __kSCPreferencesTypeID,
177 size,
178 NULL);
179 if (prefsPrivate == NULL) {
180 return NULL;
181 }
182
183 pthread_mutex_init(&prefsPrivate->lock, NULL);
184
185 prefsPrivate->name = NULL;
186 prefsPrivate->prefsID = NULL;
187 prefsPrivate->options = NULL;
188 prefsPrivate->path = NULL;
189 prefsPrivate->newPath = NULL; // new prefs path
190 prefsPrivate->locked = FALSE;
191 prefsPrivate->lockFD = -1;
192 prefsPrivate->lockPath = NULL;
193 prefsPrivate->signature = NULL;
194 prefsPrivate->session = NULL;
195 prefsPrivate->sessionKeyLock = NULL;
196 prefsPrivate->sessionKeyCommit = NULL;
197 prefsPrivate->sessionKeyApply = NULL;
198 prefsPrivate->scheduled = FALSE;
199 prefsPrivate->rls = NULL;
200 prefsPrivate->rlsFunction = NULL;
201 prefsPrivate->rlsContext.info = NULL;
202 prefsPrivate->rlsContext.retain = NULL;
203 prefsPrivate->rlsContext.release = NULL;
204 prefsPrivate->rlsContext.copyDescription = NULL;
205 prefsPrivate->rlList = NULL;
206 prefsPrivate->dispatchQueue = NULL;
207 prefsPrivate->prefs = NULL;
208 prefsPrivate->accessed = FALSE;
209 prefsPrivate->changed = FALSE;
210 prefsPrivate->isRoot = (geteuid() == 0);
211 prefsPrivate->authorizationData = NULL;
212 prefsPrivate->helper_port = MACH_PORT_NULL;
213
214 return prefsPrivate;
215 }
216
217
218 __private_extern__ Boolean
219 __SCPreferencesCreate_helper(SCPreferencesRef prefs)
220 {
221 CFDataRef data = NULL;
222 CFMutableDictionaryRef info;
223 CFNumberRef num;
224 Boolean ok;
225 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
226 uint32_t status = kSCStatusOK;
227 CFStringRef str;
228 uint32_t pid = getpid();
229
230 // start helper
231 ok = _SCHelperOpen(prefsPrivate->authorizationData,
232 &prefsPrivate->helper_port);
233 if (!ok) {
234 goto fail;
235 }
236
237 // create a dictionary of information to pass to the helper
238 info = CFDictionaryCreateMutable(NULL,
239 0,
240 &kCFTypeDictionaryKeyCallBacks,
241 &kCFTypeDictionaryValueCallBacks);
242
243 // save prefsID
244 if (prefsPrivate->prefsID != NULL) {
245 CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID);
246 }
247
248 // save options
249 if (prefsPrivate->options != NULL) {
250 CFDictionarySetValue(info, CFSTR("options"), prefsPrivate->options);
251 }
252
253 // save preferences session "name"
254 CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name);
255
256 // save PID
257 num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid);
258 CFDictionarySetValue(info, CFSTR("PID"), num);
259 CFRelease(num);
260
261 // save process name
262 str = CFStringCreateWithCString(NULL, getprogname(), 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 = NULL;
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 if (authorization == NULL) {
636 authorization = kSCPreferencesUseEntitlementAuthorization;
637 }
638 #else // !TARGET_OS_IPHONE
639 authorization = kSCPreferencesUseEntitlementAuthorization;
640 #endif // !TARGET_OS_IPHONE
641
642 prefs = SCPreferencesCreateWithOptions(allocator, name, prefsID, authorization, NULL);
643 return prefs;
644 }
645
646
647 SCPreferencesRef
648 SCPreferencesCreateWithOptions(CFAllocatorRef allocator,
649 CFStringRef name,
650 CFStringRef prefsID,
651 AuthorizationRef authorization,
652 CFDictionaryRef options)
653 {
654 CFDataRef authorizationData = NULL;
655 SCPreferencesPrivateRef prefsPrivate;
656
657 if (options != NULL) {
658 if (!isA_CFDictionary(options)) {
659 _SCErrorSet(kSCStatusInvalidArgument);
660 return NULL;
661 }
662 }
663
664 if (authorization != NULL) {
665 CFMutableDictionaryRef authorizationDict;
666 CFBundleRef bundle;
667 CFStringRef bundleID = NULL;
668
669 authorizationDict = CFDictionaryCreateMutable(NULL,
670 0,
671 &kCFTypeDictionaryKeyCallBacks,
672 &kCFTypeDictionaryValueCallBacks);
673 #if !TARGET_OS_IPHONE
674 if (authorization != kSCPreferencesUseEntitlementAuthorization) {
675 CFDataRef authorizationRefData;
676 AuthorizationExternalForm extForm;
677 OSStatus os_status;
678
679 os_status = AuthorizationMakeExternalForm(authorization, &extForm);
680 if (os_status != errAuthorizationSuccess) {
681 SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen AuthorizationMakeExternalForm() failed"));
682 _SCErrorSet(kSCStatusInvalidArgument);
683 CFRelease(authorizationDict);
684 return NULL;
685 }
686
687 authorizationRefData = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes));
688 CFDictionaryAddValue(authorizationDict,
689 kSCHelperAuthAuthorization,
690 authorizationRefData);
691 CFRelease(authorizationRefData);
692 }
693 #endif
694
695 /* get the application/executable/bundle name */
696 bundle = CFBundleGetMainBundle();
697 if (bundle != NULL) {
698 bundleID = CFBundleGetIdentifier(bundle);
699 if (bundleID != NULL) {
700 CFRetain(bundleID);
701 } else {
702 CFURLRef url;
703
704 url = CFBundleCopyExecutableURL(bundle);
705 if (url != NULL) {
706 bundleID = CFURLCopyPath(url);
707 CFRelease(url);
708 }
709 }
710
711 if (bundleID != NULL) {
712 if (CFEqual(bundleID, CFSTR("/"))) {
713 CFRelease(bundleID);
714 bundleID = NULL;
715 }
716 }
717 }
718 if (bundleID == NULL) {
719 bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown(%d)"), getpid());
720 }
721 CFDictionaryAddValue(authorizationDict,
722 kSCHelperAuthCallerInfo,
723 bundleID);
724 CFRelease(bundleID);
725
726 if (authorizationDict != NULL) {
727 _SCSerialize((CFPropertyListRef)authorizationDict,
728 &authorizationData,
729 NULL,
730 NULL);
731 CFRelease(authorizationDict);
732 }
733 }
734
735 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData, options);
736 if (authorizationData != NULL) CFRelease(authorizationData);
737
738 return (SCPreferencesRef)prefsPrivate;
739 }
740
741
742 CFTypeID
743 SCPreferencesGetTypeID(void) {
744 pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */
745 return __kSCPreferencesTypeID;
746 }
747
748
749 static void
750 prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
751 {
752 void *context_info;
753 void (*context_release)(const void *);
754 CFIndex i;
755 CFIndex n;
756 SCPreferencesNotification notify = 0;
757 SCPreferencesRef prefs = (SCPreferencesRef)info;
758 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
759 SCPreferencesCallBack rlsFunction;
760
761 n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0;
762 for (i = 0; i < n; i++) {
763 CFStringRef key;
764
765 key = CFArrayGetValueAtIndex(changedKeys, i);
766 if (CFEqual(key, prefsPrivate->sessionKeyCommit)) {
767 // if preferences have been saved
768 notify |= kSCPreferencesNotificationCommit;
769 }
770 if (CFEqual(key, prefsPrivate->sessionKeyApply)) {
771 // if stored preferences should be applied to current configuration
772 notify |= kSCPreferencesNotificationApply;
773 }
774 }
775
776 if (notify == 0) {
777 // if no changes
778 return;
779 }
780
781 pthread_mutex_lock(&prefsPrivate->lock);
782
783 /* callout */
784 rlsFunction = prefsPrivate->rlsFunction;
785 if (prefsPrivate->rlsContext.retain != NULL) {
786 context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info);
787 context_release = prefsPrivate->rlsContext.release;
788 } else {
789 context_info = prefsPrivate->rlsContext.info;
790 context_release = NULL;
791 }
792
793 pthread_mutex_unlock(&prefsPrivate->lock);
794
795 if (rlsFunction != NULL) {
796 (*rlsFunction)(prefs, notify, context_info);
797 }
798
799 if (context_release != NULL) {
800 (*context_release)(context_info);
801 }
802
803 return;
804 }
805
806
807 __private_extern__ Boolean
808 __SCPreferencesAddSession(SCPreferencesRef prefs)
809 {
810 CFAllocatorRef allocator = CFGetAllocator(prefs);
811 SCDynamicStoreContext context = { 0
812 , (void *)prefs
813 , NULL
814 , NULL
815 , NULL
816 };
817 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
818
819 /* establish a dynamic store session */
820 prefsPrivate->session = SCDynamicStoreCreate(allocator,
821 prefsPrivate->name,
822 prefsNotify,
823 &context);
824 if (prefsPrivate->session == NULL) {
825 SCLog(_sc_verbose, LOG_INFO, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed"));
826 return FALSE;
827 }
828
829 /* create the session "commit" key */
830 prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL,
831 prefsPrivate->prefsID,
832 kSCPreferencesKeyCommit);
833
834 /* create the session "apply" key */
835 prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL,
836 prefsPrivate->prefsID,
837 kSCPreferencesKeyApply);
838
839 return TRUE;
840 }
841
842
843 Boolean
844 SCPreferencesSetCallback(SCPreferencesRef prefs,
845 SCPreferencesCallBack callout,
846 SCPreferencesContext *context)
847 {
848 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
849
850 if (!isA_SCPreferences(prefs)) {
851 /* sorry, you must provide a session */
852 _SCErrorSet(kSCStatusNoPrefsSession);
853 return FALSE;
854 }
855
856 pthread_mutex_lock(&prefsPrivate->lock);
857
858 if (prefsPrivate->rlsContext.release != NULL) {
859 /* let go of the current context */
860 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info);
861 }
862
863 prefsPrivate->rlsFunction = callout;
864 prefsPrivate->rlsContext.info = NULL;
865 prefsPrivate->rlsContext.retain = NULL;
866 prefsPrivate->rlsContext.release = NULL;
867 prefsPrivate->rlsContext.copyDescription = NULL;
868 if (context != NULL) {
869 bcopy(context, &prefsPrivate->rlsContext, sizeof(SCPreferencesContext));
870 if (context->retain != NULL) {
871 prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
872 }
873 }
874
875 pthread_mutex_unlock(&prefsPrivate->lock);
876
877 return TRUE;
878 }
879
880
881 static Boolean
882 __SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
883 CFRunLoopRef runLoop,
884 CFStringRef runLoopMode,
885 dispatch_queue_t queue)
886 {
887 Boolean ok = FALSE;
888 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
889
890 pthread_mutex_lock(&prefsPrivate->lock);
891
892 if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
893 ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
894 _SCErrorSet(kSCStatusInvalidArgument);
895 goto done;
896 }
897
898 if (!prefsPrivate->scheduled) {
899 CFMutableArrayRef keys;
900
901 if (prefsPrivate->session == NULL) {
902 ok = __SCPreferencesAddSession(prefs);
903 if (!ok) {
904 goto done;
905 }
906 }
907
908 CFRetain(prefs); // hold a reference to the prefs
909
910 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
911 CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit);
912 CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply);
913 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL);
914 CFRelease(keys);
915
916 if (runLoop != NULL) {
917 prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0);
918 prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
919 }
920
921 prefsPrivate->scheduled = TRUE;
922 }
923
924 if (queue != NULL) {
925 ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue);
926 if (!ok) {
927 prefsPrivate->scheduled = FALSE;
928 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
929 CFRelease(prefs);
930 goto done;
931 }
932
933 prefsPrivate->dispatchQueue = queue;
934 dispatch_retain(prefsPrivate->dispatchQueue);
935 } else {
936 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
937 /*
938 * if we do not already have notifications scheduled with
939 * this runLoop / runLoopMode
940 */
941 CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode);
942 }
943
944 _SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList);
945 }
946
947 ok = TRUE;
948
949 done :
950
951 pthread_mutex_unlock(&prefsPrivate->lock);
952 return ok;
953 }
954
955
956 static Boolean
957 __SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
958 CFRunLoopRef runLoop,
959 CFStringRef runLoopMode)
960 {
961 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
962 CFIndex n = 0;
963 Boolean ok = FALSE;
964
965 pthread_mutex_lock(&prefsPrivate->lock);
966
967 if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not)
968 _SCErrorSet(kSCStatusInvalidArgument);
969 goto done;
970 }
971
972 if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
973 ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
974 _SCErrorSet(kSCStatusInvalidArgument);
975 goto done;
976 }
977
978 if (runLoop == NULL) {
979 SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL);
980 dispatch_release(prefsPrivate->dispatchQueue);
981 prefsPrivate->dispatchQueue = NULL;
982 } else {
983 if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) {
984 // if not currently scheduled on this runLoop / runLoopMode
985 _SCErrorSet(kSCStatusInvalidArgument);
986 goto done;
987 }
988
989 n = CFArrayGetCount(prefsPrivate->rlList);
990 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) {
991 /*
992 * if we are no longer scheduled to receive notifications for
993 * this runLoop / runLoopMode
994 */
995 CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode);
996
997 if (n == 0) {
998 // if *all* notifications have been unscheduled
999 CFRelease(prefsPrivate->rlList);
1000 prefsPrivate->rlList = NULL;
1001 CFRunLoopSourceInvalidate(prefsPrivate->rls);
1002 CFRelease(prefsPrivate->rls);
1003 prefsPrivate->rls = NULL;
1004 }
1005 }
1006 }
1007
1008 if (n == 0) {
1009 CFArrayRef changedKeys;
1010
1011 // if *all* notifications have been unscheduled
1012 prefsPrivate->scheduled = FALSE;
1013
1014 // no need to track changes
1015 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL);
1016
1017 // clear out any pending notifications
1018 changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session);
1019 if (changedKeys != NULL) {
1020 CFRelease(changedKeys);
1021 }
1022
1023 // release our reference to the prefs
1024 CFRelease(prefs);
1025 }
1026
1027 ok = TRUE;
1028
1029 done :
1030
1031 pthread_mutex_unlock(&prefsPrivate->lock);
1032 return ok;
1033 }
1034
1035
1036 Boolean
1037 SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs,
1038 CFRunLoopRef runLoop,
1039 CFStringRef runLoopMode)
1040 {
1041 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1042 _SCErrorSet(kSCStatusInvalidArgument);
1043 return FALSE;
1044 }
1045
1046 return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL);
1047 }
1048
1049
1050 Boolean
1051 SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs,
1052 CFRunLoopRef runLoop,
1053 CFStringRef runLoopMode)
1054 {
1055 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) {
1056 _SCErrorSet(kSCStatusInvalidArgument);
1057 return FALSE;
1058 }
1059
1060 return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode);
1061 }
1062
1063
1064 Boolean
1065 SCPreferencesSetDispatchQueue(SCPreferencesRef prefs,
1066 dispatch_queue_t queue)
1067 {
1068 Boolean ok = FALSE;
1069
1070 if (!isA_SCPreferences(prefs)) {
1071 /* sorry, you must provide a session */
1072 _SCErrorSet(kSCStatusNoPrefsSession);
1073 return FALSE;
1074 }
1075
1076 if (queue != NULL) {
1077 ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue);
1078 } else {
1079 ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL);
1080 }
1081
1082 return ok;
1083 }
1084
1085
1086 static void
1087 __SCPreferencesSynchronize_helper(SCPreferencesRef prefs)
1088 {
1089 Boolean ok;
1090 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1091 uint32_t status = kSCStatusOK;
1092
1093 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
1094 // if no helper
1095 return;
1096 }
1097
1098 // have the helper "synchronize" the prefs
1099 ok = _SCHelperExec(prefsPrivate->helper_port,
1100 SCHELPER_MSG_PREFS_SYNCHRONIZE,
1101 NULL,
1102 &status,
1103 NULL);
1104 if (!ok) {
1105 // close helper
1106 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
1107 _SCHelperClose(&prefsPrivate->helper_port);
1108 }
1109 }
1110
1111 return;
1112 }
1113
1114
1115 void
1116 SCPreferencesSynchronize(SCPreferencesRef prefs)
1117 {
1118 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1119
1120 if (!isA_SCPreferences(prefs)) {
1121 /* sorry, you must provide a session */
1122 _SCErrorSet(kSCStatusNoPrefsSession);
1123 return;
1124 }
1125
1126 if (prefsPrivate->authorizationData != NULL) {
1127 __SCPreferencesSynchronize_helper(prefs);
1128 }
1129 if (prefsPrivate->prefs != NULL) {
1130 CFRelease(prefsPrivate->prefs);
1131 prefsPrivate->prefs = NULL;
1132 }
1133 if (prefsPrivate->signature != NULL) {
1134 CFRelease(prefsPrivate->signature);
1135 prefsPrivate->signature = NULL;
1136 }
1137 prefsPrivate->accessed = FALSE;
1138 prefsPrivate->changed = FALSE;
1139
1140 return;
1141 }