]> git.saurik.com Git - apple/configd.git/blob - Plugins/PreferencesMonitor/prefsmon.c
configd-137.3.tar.gz
[apple/configd.git] / Plugins / PreferencesMonitor / prefsmon.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, 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 static SCPreferencesRef prefs = NULL;
52 static SCDynamicStoreRef store = NULL;
53
54 static CFMutableDictionaryRef currentPrefs; /* current prefs */
55 static CFMutableDictionaryRef newPrefs; /* new prefs */
56 static CFMutableArrayRef unchangedPrefsKeys; /* new prefs keys which match current */
57 static CFMutableArrayRef removedPrefsKeys; /* old prefs keys to be removed */
58
59 static Boolean _verbose = FALSE;
60
61
62 static void
63 updateCache(const void *key, const void *value, void *context)
64 {
65 CFStringRef configKey = (CFStringRef)key;
66 CFPropertyListRef configData = (CFPropertyListRef)value;
67 CFPropertyListRef cacheData;
68 CFIndex i;
69
70 cacheData = CFDictionaryGetValue(currentPrefs, configKey);
71 if (cacheData) {
72 /* key exists */
73 if (CFEqual(cacheData, configData)) {
74 /*
75 * if the old & new property list values have
76 * not changed then we don't need to update
77 * the preference.
78 */
79 CFArrayAppendValue(unchangedPrefsKeys, configKey);
80 }
81 }
82
83 /* in any case, this key should not be removed */
84 i = CFArrayGetFirstIndexOfValue(removedPrefsKeys,
85 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys)),
86 configKey);
87 if (i != kCFNotFound) {
88 CFArrayRemoveValueAtIndex(removedPrefsKeys, i);
89 }
90
91 return;
92 }
93
94
95 static void
96 flatten(SCPreferencesRef prefs,
97 CFStringRef key,
98 CFDictionaryRef base)
99 {
100 CFDictionaryRef subset;
101 CFStringRef link;
102 CFMutableDictionaryRef myDict;
103 CFStringRef myKey;
104 CFIndex i;
105 CFIndex nKeys;
106 const void **keys;
107 const void **vals;
108
109 if (!CFDictionaryGetValueIfPresent(base, kSCResvLink, (const void **)&link)) {
110 /* if this dictionary is not linked */
111 subset = base;
112 } else {
113 /* if __LINK__ key is present */
114 subset = SCPreferencesPathGetValue(prefs, link);
115 if (!subset) {
116 /* if error with link */
117 SCLog(TRUE, LOG_ERR,
118 CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
119 link,
120 SCErrorString(SCError()));
121 return;
122 }
123 }
124
125 if (CFDictionaryContainsKey(subset, kSCResvInactive)) {
126 /* if __INACTIVE__ key is present */
127 return;
128 }
129
130 myKey = CFStringCreateWithFormat(NULL,
131 NULL,
132 CFSTR("%@%@"),
133 kSCDynamicStoreDomainSetup,
134 key);
135
136 myDict = (CFMutableDictionaryRef)CFDictionaryGetValue(newPrefs, myKey);
137 if (myDict) {
138 myDict = CFDictionaryCreateMutableCopy(NULL,
139 0,
140 (CFDictionaryRef)myDict);
141 } else {
142 myDict = CFDictionaryCreateMutable(NULL,
143 0,
144 &kCFTypeDictionaryKeyCallBacks,
145 &kCFTypeDictionaryValueCallBacks);
146 }
147
148 nKeys = CFDictionaryGetCount(subset);
149 if (nKeys > 0) {
150 keys = CFAllocatorAllocate(NULL, nKeys * sizeof(CFStringRef) , 0);
151 vals = CFAllocatorAllocate(NULL, nKeys * sizeof(CFPropertyListRef), 0);
152 CFDictionaryGetKeysAndValues(subset, keys, vals);
153 for (i = 0; i < nKeys; i++) {
154 if (CFGetTypeID((CFTypeRef)vals[i]) != CFDictionaryGetTypeID()) {
155 /* add this key/value to the current dictionary */
156 CFDictionarySetValue(myDict, keys[i], vals[i]);
157 } else {
158 CFStringRef subKey;
159
160 /* flatten [sub]dictionaries */
161 subKey = CFStringCreateWithFormat(NULL,
162 NULL,
163 CFSTR("%@%s%@"),
164 key,
165 CFEqual(key, CFSTR("/")) ? "" : "/",
166 keys[i]);
167 flatten(prefs, subKey, vals[i]);
168 CFRelease(subKey);
169 }
170 }
171 CFAllocatorDeallocate(NULL, keys);
172 CFAllocatorDeallocate(NULL, vals);
173 }
174
175 if (CFDictionaryGetCount(myDict) > 0) {
176 /* add this dictionary to the new preferences */
177 CFDictionarySetValue(newPrefs, myKey, myDict);
178 }
179
180 CFRelease(myDict);
181 CFRelease(myKey);
182
183 return;
184 }
185
186
187 static void
188 updateConfiguration(SCPreferencesRef prefs,
189 SCPreferencesNotification notificationType,
190 void *info)
191 {
192 CFStringRef current = NULL;
193 CFDateRef date = NULL;
194 CFMutableDictionaryRef dict = NULL;
195 CFDictionaryRef global = NULL;
196 CFIndex i;
197 CFArrayRef keys;
198 CFIndex n;
199 CFStringRef pattern;
200 CFMutableArrayRef patterns;
201 CFDictionaryRef set = NULL;
202
203
204 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
205 return;
206 }
207
208 SCLog(_verbose, LOG_DEBUG, CFSTR("updating configuration"));
209
210 /*
211 * initialize old preferences, new preferences, an array
212 * of keys which have not changed, and an array of keys
213 * to be removed (cleaned up).
214 */
215
216 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
217 pattern = CFStringCreateWithFormat(NULL,
218 NULL,
219 CFSTR("^%@.*"),
220 kSCDynamicStoreDomainSetup);
221 CFArrayAppendValue(patterns, pattern);
222 dict = (CFMutableDictionaryRef)SCDynamicStoreCopyMultiple(store, NULL, patterns);
223 CFRelease(patterns);
224 CFRelease(pattern);
225 if (dict) {
226 currentPrefs = CFDictionaryCreateMutableCopy(NULL, 0, dict);
227 CFRelease(dict);
228 } else {
229 currentPrefs = CFDictionaryCreateMutable(NULL,
230 0,
231 &kCFTypeDictionaryKeyCallBacks,
232 &kCFTypeDictionaryValueCallBacks);
233 }
234
235 unchangedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
236
237 i = CFDictionaryGetCount(currentPrefs);
238 if (i > 0) {
239 const void **currentKeys;
240 CFArrayRef array;
241
242 currentKeys = CFAllocatorAllocate(NULL, i * sizeof(CFStringRef), 0);
243 CFDictionaryGetKeysAndValues(currentPrefs, currentKeys, NULL);
244 array = CFArrayCreate(NULL, currentKeys, i, &kCFTypeArrayCallBacks);
245 removedPrefsKeys = CFArrayCreateMutableCopy(NULL, 0, array);
246 CFRelease(array);
247 CFAllocatorDeallocate(NULL, currentKeys);
248 } else {
249 removedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
250 }
251
252 /*
253 * The "newPrefs" dictionary will contain the new / updated
254 * configuration which will be written to the configuration cache.
255 */
256 newPrefs = CFDictionaryCreateMutable(NULL,
257 0,
258 &kCFTypeDictionaryKeyCallBacks,
259 &kCFTypeDictionaryValueCallBacks);
260
261 /*
262 * create status dictionary associated with current configuration
263 * information including:
264 * - current set "name" to cache
265 * - time stamp indicating when the cache preferences were
266 * last updated.
267 */
268 dict = CFDictionaryCreateMutable(NULL,
269 0,
270 &kCFTypeDictionaryKeyCallBacks,
271 &kCFTypeDictionaryValueCallBacks);
272 date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
273
274 /*
275 * load preferences
276 */
277 keys = SCPreferencesCopyKeyList(prefs);
278 if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
279 SCLog(TRUE, LOG_NOTICE, CFSTR("updateConfiguration(): no preferences."));
280 goto done;
281 }
282
283 /*
284 * get "global" system preferences
285 */
286 global = SCPreferencesGetValue(prefs, kSCPrefSystem);
287 if (!global) {
288 /* if no global preferences are defined */
289 goto getSet;
290 }
291
292 if (!isA_CFDictionary(global)) {
293 SCLog(TRUE, LOG_ERR,
294 CFSTR("updateConfiguration(): %@ is not a dictionary."),
295 kSCPrefSystem);
296 goto done;
297 }
298
299 /* flatten property list */
300 flatten(prefs, CFSTR("/"), global);
301
302 getSet :
303
304 /*
305 * get current set name
306 */
307 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
308 if (!current) {
309 /* if current set not defined */
310 goto done;
311 }
312
313 if (!isA_CFString(current)) {
314 SCLog(TRUE, LOG_ERR,
315 CFSTR("updateConfiguration(): %@ is not a string."),
316 kSCPrefCurrentSet);
317 goto done;
318 }
319
320 /*
321 * get current set
322 */
323 set = SCPreferencesPathGetValue(prefs, current);
324 if (!set) {
325 /* if error with path */
326 SCLog(TRUE, LOG_ERR,
327 CFSTR("%@ value (%@) not valid"),
328 kSCPrefCurrentSet,
329 current);
330 goto done;
331 }
332
333 if (!isA_CFDictionary(set)) {
334 SCLog(TRUE, LOG_ERR,
335 CFSTR("updateConfiguration(): %@ is not a dictionary."),
336 current);
337 goto done;
338 }
339
340 /* flatten property list */
341 flatten(prefs, CFSTR("/"), set);
342
343 CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current);
344
345 done :
346
347 /* add last updated time stamp */
348 CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date);
349
350 /* add Setup: key */
351 CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict);
352
353 /* compare current and new preferences */
354 CFDictionaryApplyFunction(newPrefs, updateCache, NULL);
355
356 /* remove those keys which have not changed from the update */
357 n = CFArrayGetCount(unchangedPrefsKeys);
358 for (i = 0; i < n; i++) {
359 CFStringRef key;
360
361 key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i);
362 CFDictionaryRemoveValue(newPrefs, key);
363 }
364
365 /* Update the dynamic store */
366 if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
367 SCLog(TRUE, LOG_ERR,
368 CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
369 SCErrorString(SCError()));
370 }
371
372 /* finished with current prefs, wait for changes */
373 SCPreferencesSynchronize(prefs);
374
375 CFRelease(currentPrefs);
376 CFRelease(newPrefs);
377 CFRelease(unchangedPrefsKeys);
378 CFRelease(removedPrefsKeys);
379 if (dict) CFRelease(dict);
380 if (date) CFRelease(date);
381 if (keys) CFRelease(keys);
382 return;
383 }
384
385
386 __private_extern__
387 void
388 stop_PreferencesMonitor(CFRunLoopSourceRef stopRls)
389 {
390 // cleanup
391
392 if (prefs != NULL) {
393 if (!SCPreferencesUnscheduleFromRunLoop(prefs,
394 CFRunLoopGetCurrent(),
395 kCFRunLoopDefaultMode)) {
396 SCLog(TRUE, LOG_ERR,
397 CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"),
398 SCErrorString(SCError()));
399 }
400 CFRelease(prefs);
401 prefs = NULL;
402 }
403
404 if (store != NULL) {
405 CFRelease(store);
406 store = NULL;
407 }
408
409 CFRunLoopSourceSignal(stopRls);
410 return;
411 }
412
413
414 __private_extern__
415 void
416 prime_PreferencesMonitor()
417 {
418 SCLog(_verbose, LOG_DEBUG, CFSTR("prime() called"));
419
420 /* load the initial configuration from the database */
421 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
422
423 return;
424 }
425
426
427 __private_extern__
428 void
429 load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
430 {
431 if (bundleVerbose) {
432 _verbose = TRUE;
433 }
434
435 SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
436 SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
437
438 /* open a SCDynamicStore session to allow cache updates */
439 store = SCDynamicStoreCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL, NULL);
440 if (store == NULL) {
441 SCLog(TRUE, LOG_ERR,
442 CFSTR("SCDynamicStoreCreate() failed: %s"),
443 SCErrorString(SCError()));
444 goto error;
445 }
446
447 /* open a SCPreferences session */
448 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
449 if (prefs == NULL) {
450 SCLog(TRUE, LOG_ERR,
451 CFSTR("SCPreferencesCreate() failed: %s"),
452 SCErrorString(SCError()));
453 goto error;
454 }
455
456 if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
457 SCLog(TRUE, LOG_ERR,
458 CFSTR("SCPreferencesSetCallBack() failed: %s"),
459 SCErrorString(SCError()));
460 goto error;
461 }
462
463 /*
464 * register for change notifications.
465 */
466 if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
467 SCLog(TRUE, LOG_ERR,
468 CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
469 SCErrorString(SCError()));
470 goto error;
471 }
472
473 return;
474
475 error :
476
477 if (store) CFRelease(store);
478 if (prefs) CFRelease(prefs);
479
480 return;
481 }
482
483
484 #ifdef MAIN
485 int
486 main(int argc, char **argv)
487 {
488 _sc_log = FALSE;
489 _sc_verbose = (argc > 1) ? TRUE : FALSE;
490
491 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
492 prime_PreferencesMonitor();
493 CFRunLoopRun();
494 /* not reached */
495 exit(0);
496 return 0;
497 }
498 #endif