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