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