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