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