]> git.saurik.com Git - apple/configd.git/blob - Plugins/PreferencesMonitor/prefsmon.c
configd-395.6.tar.gz
[apple/configd.git] / Plugins / PreferencesMonitor / prefsmon.c
1 /*
2 * Copyright (c) 2000-2008, 2010 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 * April 2, 2004 Allan Nathanson <ajn@apple.com>
28 * - use SCPreference notification APIs
29 *
30 * June 24, 2001 Allan Nathanson <ajn@apple.com>
31 * - update to public SystemConfiguration.framework APIs
32 *
33 * November 10, 2000 Allan Nathanson <ajn@apple.com>
34 * - initial revision
35 */
36
37
38 #include <TargetConditionals.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43
44
45 #include <SystemConfiguration/SystemConfiguration.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/SCValidation.h>
48
49
50
51
52 /* globals */
53 static SCPreferencesRef prefs = NULL;
54 static SCDynamicStoreRef store = NULL;
55
56 /* preferences "initialization" globals */
57 static CFStringRef initKey = NULL;
58 static CFRunLoopSourceRef initRls = NULL;
59
60 /* SCDynamicStore (Setup:) */
61 static CFMutableDictionaryRef currentPrefs; /* current prefs */
62 static CFMutableDictionaryRef newPrefs; /* new prefs */
63 static CFMutableArrayRef unchangedPrefsKeys; /* new prefs keys which match current */
64 static CFMutableArrayRef removedPrefsKeys; /* old prefs keys to be removed */
65
66 static Boolean rofs = FALSE;
67 static Boolean _verbose = FALSE;
68
69
70 static Boolean
71 establishNewPreferences()
72 {
73 CFBundleRef bundle;
74 SCNetworkSetRef current = NULL;
75 Boolean ok = FALSE;
76 int sc_status = kSCStatusFailed;
77 SCNetworkSetRef set = NULL;
78 CFStringRef setName = NULL;
79 Boolean updated = FALSE;
80
81 while (TRUE) {
82 ok = SCPreferencesLock(prefs, TRUE);
83 if (ok) {
84 break;
85 }
86
87 sc_status = SCError();
88 if (sc_status == kSCStatusStale) {
89 SCPreferencesSynchronize(prefs);
90 } else {
91 SCLog(TRUE, LOG_ERR,
92 CFSTR("Could not acquire network configuration lock: %s"),
93 SCErrorString(sc_status));
94 return FALSE;
95 }
96 }
97
98 current = SCNetworkSetCopyCurrent(prefs);
99 if (current != NULL) {
100 set = current;
101 }
102
103 if (set == NULL) {
104 set = SCNetworkSetCreate(prefs);
105 if (set == NULL) {
106 ok = FALSE;
107 sc_status = SCError();
108 goto done;
109 }
110
111 bundle = _SC_CFBundleGet();
112 if (bundle != NULL) {
113 setName = CFBundleCopyLocalizedString(bundle,
114 CFSTR("DEFAULT_SET_NAME"),
115 CFSTR("Automatic"),
116 NULL);
117 }
118
119 ok = SCNetworkSetSetName(set, (setName != NULL) ? setName : CFSTR("Automatic"));
120 if (!ok) {
121 sc_status = SCError();
122 goto done;
123 }
124
125 ok = SCNetworkSetSetCurrent(set);
126 if (!ok) {
127 sc_status = SCError();
128 goto done;
129 }
130 }
131
132 ok = SCNetworkSetEstablishDefaultConfiguration(set);
133 if (!ok) {
134 sc_status = SCError();
135 goto done;
136 }
137
138 done :
139
140 if (ok) {
141 ok = SCPreferencesCommitChanges(prefs);
142 if (ok) {
143 SCLog(TRUE, LOG_NOTICE, CFSTR("New network configuration saved"));
144 updated = TRUE;
145 } else {
146 sc_status = SCError();
147 if (sc_status == EROFS) {
148 /* a read-only fileysstem is OK */
149 ok = TRUE;
150
151 /* ... but we don't want to synchronize */
152 rofs = TRUE;
153 }
154 }
155
156 /* apply (committed or temporary/read-only) changes */
157 (void) SCPreferencesApplyChanges(prefs);
158 } else if ((current == NULL) && (set != NULL)) {
159 (void) SCNetworkSetRemove(set);
160 }
161
162 if (!ok) {
163 SCLog(TRUE, LOG_ERR,
164 CFSTR("Could not establish network configuration: %s"),
165 SCErrorString(sc_status));
166 }
167
168 (void)SCPreferencesUnlock(prefs);
169 if (setName != NULL) CFRelease(setName);
170 if (set != NULL) CFRelease(set);
171 return updated;
172 }
173
174
175 static Boolean
176 quiet(Boolean *timeout)
177 {
178 CFDictionaryRef dict;
179 Boolean _quiet = FALSE;
180 Boolean _timeout = FALSE;
181
182 // check if quiet
183 dict = SCDynamicStoreCopyValue(store, initKey);
184 if (dict != NULL) {
185 if (isA_CFDictionary(dict)) {
186 if (CFDictionaryContainsKey(dict, CFSTR("*QUIET*"))) {
187 _quiet = TRUE;
188 }
189 if (CFDictionaryContainsKey(dict, CFSTR("*TIMEOUT*"))) {
190 _timeout = TRUE;
191 }
192 }
193 CFRelease(dict);
194 }
195
196 if (timeout != NULL) {
197 *timeout = _timeout;
198 }
199 return _quiet;
200 }
201
202
203 static void
204 watchQuietDisable()
205 {
206 if ((initKey == NULL) || (initRls == NULL)) {
207 return;
208 }
209
210 (void) SCDynamicStoreSetNotificationKeys(store, NULL, NULL);
211
212 CFRunLoopSourceInvalidate(initRls);
213 CFRelease(initRls);
214 initRls = NULL;
215
216 CFRelease(initKey);
217 initKey = NULL;
218
219 return;
220 }
221
222
223 static void
224 watchQuietEnable()
225 {
226 CFArrayRef keys;
227 Boolean ok;
228
229 initKey = SCDynamicStoreKeyCreate(NULL,
230 CFSTR("%@" "InterfaceNamer"),
231 kSCDynamicStoreDomainPlugin);
232
233 initRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
234 CFRunLoopAddSource(CFRunLoopGetCurrent(), initRls, kCFRunLoopDefaultMode);
235
236 keys = CFArrayCreate(NULL, (const void **)&initKey, 1, &kCFTypeArrayCallBacks);
237 ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
238 CFRelease(keys);
239 if (!ok) {
240 SCLog(TRUE, LOG_ERR,
241 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
242 watchQuietDisable();
243 }
244
245 return;
246 }
247
248 static void
249 watchQuietCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
250 {
251 Boolean _quiet;
252 Boolean _timeout = FALSE;
253
254 _quiet = quiet(&_timeout);
255 if (_quiet
256 #if !TARGET_OS_IPHONE
257 || _timeout
258 #endif /* !TARGET_OS_IPHONE */
259 ) {
260 watchQuietDisable();
261 }
262
263 if (_quiet || _timeout) {
264 static int logged = 0;
265
266 (void) establishNewPreferences();
267 if (_timeout && (logged++ == 0)) {
268 SCLog(TRUE, LOG_NOTICE,
269 CFSTR("Network configuration creation timed out waiting for IORegistry"));
270 }
271 }
272
273 return;
274 }
275
276
277 static void
278 updateCache(const void *key, const void *value, void *context)
279 {
280 CFStringRef configKey = (CFStringRef)key;
281 CFPropertyListRef configData = (CFPropertyListRef)value;
282 CFPropertyListRef cacheData;
283 CFIndex i;
284
285 cacheData = CFDictionaryGetValue(currentPrefs, configKey);
286 if (cacheData) {
287 /* key exists */
288 if (CFEqual(cacheData, configData)) {
289 /*
290 * if the old & new property list values have
291 * not changed then we don't need to update
292 * the preference.
293 */
294 CFArrayAppendValue(unchangedPrefsKeys, configKey);
295 }
296 }
297
298 /* in any case, this key should not be removed */
299 i = CFArrayGetFirstIndexOfValue(removedPrefsKeys,
300 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys)),
301 configKey);
302 if (i != kCFNotFound) {
303 CFArrayRemoveValueAtIndex(removedPrefsKeys, i);
304 }
305
306 return;
307 }
308
309
310 static void
311 flatten(SCPreferencesRef prefs,
312 CFStringRef key,
313 CFDictionaryRef base)
314 {
315 CFDictionaryRef subset;
316 CFStringRef link;
317 CFMutableDictionaryRef myDict;
318 CFStringRef myKey;
319 CFIndex i;
320 CFIndex nKeys;
321 const void **keys;
322 const void **vals;
323
324 if (!CFDictionaryGetValueIfPresent(base, kSCResvLink, (const void **)&link)) {
325 /* if this dictionary is not linked */
326 subset = base;
327 } else {
328 /* if __LINK__ key is present */
329 subset = SCPreferencesPathGetValue(prefs, link);
330 if (!subset) {
331 /* if error with link */
332 SCLog(TRUE, LOG_ERR,
333 CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
334 link,
335 SCErrorString(SCError()));
336 return;
337 }
338 }
339
340 if (CFDictionaryContainsKey(subset, kSCResvInactive)) {
341 /* if __INACTIVE__ key is present */
342 return;
343 }
344
345 myKey = CFStringCreateWithFormat(NULL,
346 NULL,
347 CFSTR("%@%@"),
348 kSCDynamicStoreDomainSetup,
349 key);
350
351 myDict = (CFMutableDictionaryRef)CFDictionaryGetValue(newPrefs, myKey);
352 if (myDict) {
353 myDict = CFDictionaryCreateMutableCopy(NULL,
354 0,
355 (CFDictionaryRef)myDict);
356 } else {
357 myDict = CFDictionaryCreateMutable(NULL,
358 0,
359 &kCFTypeDictionaryKeyCallBacks,
360 &kCFTypeDictionaryValueCallBacks);
361 }
362
363 nKeys = CFDictionaryGetCount(subset);
364 if (nKeys > 0) {
365 keys = CFAllocatorAllocate(NULL, nKeys * sizeof(CFStringRef) , 0);
366 vals = CFAllocatorAllocate(NULL, nKeys * sizeof(CFPropertyListRef), 0);
367 CFDictionaryGetKeysAndValues(subset, keys, vals);
368 for (i = 0; i < nKeys; i++) {
369 if (CFGetTypeID((CFTypeRef)vals[i]) != CFDictionaryGetTypeID()) {
370 /* add this key/value to the current dictionary */
371 CFDictionarySetValue(myDict, keys[i], vals[i]);
372 } else {
373 CFStringRef subKey;
374
375 /* flatten [sub]dictionaries */
376 subKey = CFStringCreateWithFormat(NULL,
377 NULL,
378 CFSTR("%@%s%@"),
379 key,
380 CFEqual(key, CFSTR("/")) ? "" : "/",
381 keys[i]);
382 flatten(prefs, subKey, vals[i]);
383 CFRelease(subKey);
384 }
385 }
386 CFAllocatorDeallocate(NULL, keys);
387 CFAllocatorDeallocate(NULL, vals);
388 }
389
390 if (CFDictionaryGetCount(myDict) > 0) {
391 /* add this dictionary to the new preferences */
392 CFDictionarySetValue(newPrefs, myKey, myDict);
393 }
394
395 CFRelease(myDict);
396 CFRelease(myKey);
397
398 return;
399 }
400
401
402 static void
403 updateSCDynamicStore(SCPreferencesRef prefs)
404 {
405 CFStringRef current = NULL;
406 CFDateRef date = NULL;
407 CFMutableDictionaryRef dict = NULL;
408 CFDictionaryRef global = NULL;
409 CFIndex i;
410 CFArrayRef keys;
411 CFIndex n;
412 CFStringRef pattern;
413 CFMutableArrayRef patterns;
414 CFDictionaryRef set = NULL;
415
416 /*
417 * initialize old preferences, new preferences, an array
418 * of keys which have not changed, and an array of keys
419 * to be removed (cleaned up).
420 */
421
422 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
423 pattern = CFStringCreateWithFormat(NULL,
424 NULL,
425 CFSTR("^%@.*"),
426 kSCDynamicStoreDomainSetup);
427 CFArrayAppendValue(patterns, pattern);
428 dict = (CFMutableDictionaryRef)SCDynamicStoreCopyMultiple(store, NULL, patterns);
429 CFRelease(patterns);
430 CFRelease(pattern);
431 if (dict) {
432 currentPrefs = CFDictionaryCreateMutableCopy(NULL, 0, dict);
433 CFRelease(dict);
434 } else {
435 currentPrefs = CFDictionaryCreateMutable(NULL,
436 0,
437 &kCFTypeDictionaryKeyCallBacks,
438 &kCFTypeDictionaryValueCallBacks);
439 }
440
441 unchangedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
442
443 i = CFDictionaryGetCount(currentPrefs);
444 if (i > 0) {
445 const void **currentKeys;
446 CFArrayRef array;
447
448 currentKeys = CFAllocatorAllocate(NULL, i * sizeof(CFStringRef), 0);
449 CFDictionaryGetKeysAndValues(currentPrefs, currentKeys, NULL);
450 array = CFArrayCreate(NULL, currentKeys, i, &kCFTypeArrayCallBacks);
451 removedPrefsKeys = CFArrayCreateMutableCopy(NULL, 0, array);
452 CFRelease(array);
453 CFAllocatorDeallocate(NULL, currentKeys);
454 } else {
455 removedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
456 }
457
458 /*
459 * The "newPrefs" dictionary will contain the new / updated
460 * configuration which will be written to the configuration cache.
461 */
462 newPrefs = CFDictionaryCreateMutable(NULL,
463 0,
464 &kCFTypeDictionaryKeyCallBacks,
465 &kCFTypeDictionaryValueCallBacks);
466
467 /*
468 * create status dictionary associated with current configuration
469 * information including:
470 * - current set "name" to cache
471 * - time stamp indicating when the cache preferences were
472 * last updated.
473 */
474 dict = CFDictionaryCreateMutable(NULL,
475 0,
476 &kCFTypeDictionaryKeyCallBacks,
477 &kCFTypeDictionaryValueCallBacks);
478 date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
479
480 /*
481 * load preferences
482 */
483 keys = SCPreferencesCopyKeyList(prefs);
484 if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
485 SCLog(TRUE, LOG_NOTICE, CFSTR("updateConfiguration(): no preferences."));
486 goto done;
487 }
488
489 /*
490 * get "global" system preferences
491 */
492 global = SCPreferencesGetValue(prefs, kSCPrefSystem);
493 if (!global) {
494 /* if no global preferences are defined */
495 goto getSet;
496 }
497
498 if (!isA_CFDictionary(global)) {
499 SCLog(TRUE, LOG_ERR,
500 CFSTR("updateConfiguration(): %@ is not a dictionary."),
501 kSCPrefSystem);
502 goto done;
503 }
504
505 /* flatten property list */
506 flatten(prefs, CFSTR("/"), global);
507
508 getSet :
509
510 /*
511 * get current set name
512 */
513 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
514 if (!current) {
515 /* if current set not defined */
516 goto done;
517 }
518
519 if (!isA_CFString(current)) {
520 SCLog(TRUE, LOG_ERR,
521 CFSTR("updateConfiguration(): %@ is not a string."),
522 kSCPrefCurrentSet);
523 goto done;
524 }
525
526 /*
527 * get current set
528 */
529 set = SCPreferencesPathGetValue(prefs, current);
530 if (!set) {
531 /* if error with path */
532 SCLog(TRUE, LOG_ERR,
533 CFSTR("%@ value (%@) not valid"),
534 kSCPrefCurrentSet,
535 current);
536 goto done;
537 }
538
539 if (!isA_CFDictionary(set)) {
540 SCLog(TRUE, LOG_ERR,
541 CFSTR("updateConfiguration(): %@ is not a dictionary."),
542 current);
543 goto done;
544 }
545
546 /* flatten property list */
547 flatten(prefs, CFSTR("/"), set);
548
549 CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current);
550
551 done :
552
553 /* add last updated time stamp */
554 CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date);
555
556 /* add Setup: key */
557 CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict);
558
559 /* compare current and new preferences */
560 CFDictionaryApplyFunction(newPrefs, updateCache, NULL);
561
562 /* remove those keys which have not changed from the update */
563 n = CFArrayGetCount(unchangedPrefsKeys);
564 for (i = 0; i < n; i++) {
565 CFStringRef key;
566
567 key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i);
568 CFDictionaryRemoveValue(newPrefs, key);
569 }
570
571 /* Update the dynamic store */
572 if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
573 SCLog(TRUE, LOG_ERR,
574 CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
575 SCErrorString(SCError()));
576 }
577
578 CFRelease(currentPrefs);
579 CFRelease(newPrefs);
580 CFRelease(unchangedPrefsKeys);
581 CFRelease(removedPrefsKeys);
582 if (dict) CFRelease(dict);
583 if (date) CFRelease(date);
584 if (keys) CFRelease(keys);
585 return;
586 }
587
588
589 static void
590 updateConfiguration(SCPreferencesRef prefs,
591 SCPreferencesNotification notificationType,
592 void *info)
593 {
594
595
596 #if !TARGET_OS_IPHONE
597 if ((notificationType & kSCPreferencesNotificationCommit) == kSCPreferencesNotificationCommit) {
598 SCNetworkSetRef current;
599
600 current = SCNetworkSetCopyCurrent(prefs);
601 if (current != NULL) {
602 /* network configuration available, disable template creation */
603 watchQuietDisable();
604 CFRelease(current);
605 }
606 }
607 #endif /* !TARGET_OS_IPHONE */
608
609 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
610 return;
611 }
612
613 SCLog(_verbose, LOG_DEBUG, CFSTR("updating configuration"));
614
615 /* update SCDynamicStore (Setup:) */
616 updateSCDynamicStore(prefs);
617
618 /* finished with current prefs, wait for changes */
619 if (!rofs) {
620 SCPreferencesSynchronize(prefs);
621 }
622
623 return;
624 }
625
626
627 __private_extern__
628 void
629 prime_PreferencesMonitor()
630 {
631 SCLog(_verbose, LOG_DEBUG, CFSTR("prime() called"));
632
633 /* load the initial configuration from the database */
634 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
635
636 return;
637 }
638
639
640 __private_extern__
641 void
642 load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
643 {
644 Boolean initPrefs = TRUE;
645
646 if (bundleVerbose) {
647 _verbose = TRUE;
648 }
649
650 SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
651 SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
652
653 /* open a SCDynamicStore session to allow cache updates */
654 store = SCDynamicStoreCreate(NULL,
655 CFSTR("PreferencesMonitor.bundle"),
656 watchQuietCallback,
657 NULL);
658 if (store == NULL) {
659 SCLog(TRUE, LOG_ERR,
660 CFSTR("SCDynamicStoreCreate() failed: %s"),
661 SCErrorString(SCError()));
662 goto error;
663 }
664
665 /* open a SCPreferences session */
666 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
667 if (prefs != NULL) {
668 SCNetworkSetRef current;
669
670 current = SCNetworkSetCopyCurrent(prefs);
671 if (current != NULL) {
672 /* network configuration available, disable template creation */
673 initPrefs = FALSE;
674 CFRelease(current);
675 }
676 } else {
677 SCLog(TRUE, LOG_ERR,
678 CFSTR("SCPreferencesCreate() failed: %s"),
679 SCErrorString(SCError()));
680 goto error;
681 }
682
683 /*
684 * register for change notifications.
685 */
686 if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
687 SCLog(TRUE, LOG_ERR,
688 CFSTR("SCPreferencesSetCallBack() failed: %s"),
689 SCErrorString(SCError()));
690 goto error;
691 }
692
693 if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
694 SCLog(TRUE, LOG_ERR,
695 CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
696 SCErrorString(SCError()));
697 goto error;
698 }
699
700 /*
701 * if no preferences, initialize with a template (now or
702 * when IOKit has quiesced).
703 */
704 if (initPrefs) {
705 watchQuietEnable();
706 watchQuietCallback(store, NULL, NULL);
707 }
708
709 return;
710
711 error :
712
713 watchQuietDisable();
714 if (store != NULL) CFRelease(store);
715 if (prefs != NULL) CFRelease(prefs);
716
717 return;
718 }
719
720
721 #ifdef MAIN
722 int
723 main(int argc, char **argv)
724 {
725 _sc_log = FALSE;
726 _sc_verbose = (argc > 1) ? TRUE : FALSE;
727
728 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
729 prime_PreferencesMonitor();
730 CFRunLoopRun();
731 /* not reached */
732 exit(0);
733 return 0;
734 }
735 #endif