]> git.saurik.com Git - apple/configd.git/blob - Plugins/PreferencesMonitor/prefsmon.c
configd-963.200.27.tar.gz
[apple/configd.git] / Plugins / PreferencesMonitor / prefsmon.c
1 /*
2 * Copyright (c) 2000-2008, 2010, 2012-2018 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 <net/if.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44
45
46 #define SC_LOG_HANDLE __log_PreferencesMonitor()
47 #define SC_LOG_HANDLE_TYPE static
48 #include <SystemConfiguration/SystemConfiguration.h>
49 #include <SystemConfiguration/SCPrivate.h>
50 #include <SystemConfiguration/SCValidation.h>
51 #include "plugin_shared.h"
52
53
54 #include <CommonCrypto/CommonDigest.h>
55
56
57 /* globals */
58 static SCPreferencesRef prefs = NULL;
59 static SCDynamicStoreRef store = NULL;
60
61 /* InterfaceNamer[.plugin] monitoring globals */
62 static CFMutableArrayRef excluded_interfaces = NULL; // of SCNetworkInterfaceRef
63 static CFMutableArrayRef excluded_names = NULL; // of CFStringRef (BSD name)
64 Boolean haveConfiguration = FALSE;
65 static CFStringRef namerKey = NULL;
66 static CFMutableArrayRef preconfigured_interfaces= NULL; // of SCNetworkInterfaceRef
67 static CFMutableArrayRef preconfigured_names = NULL; // of CFStringRef (BSD name)
68
69 /* KernelEventMonitor[.plugin] monitoring globals */
70 static CFStringRef interfacesKey = NULL;
71
72 /* SCDynamicStore (Setup:) */
73 static CFMutableDictionaryRef currentPrefs; /* current prefs */
74 static CFMutableDictionaryRef newPrefs; /* new prefs */
75 static CFMutableArrayRef unchangedPrefsKeys; /* new prefs keys which match current */
76 static CFMutableArrayRef removedPrefsKeys; /* old prefs keys to be removed */
77
78 static Boolean rofs = FALSE;
79 static Boolean restorePrefs = FALSE;
80
81 #define MY_PLUGIN_NAME "PreferencesMonitor"
82 #define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
83
84
85 static void
86 updateConfiguration(SCPreferencesRef prefs,
87 SCPreferencesNotification notificationType,
88 void *info);
89
90
91 static os_log_t
92 __log_PreferencesMonitor(void)
93 {
94 static os_log_t log = NULL;
95
96 if (log == NULL) {
97 log = os_log_create("com.apple.SystemConfiguration", "PreferencesMonitor");
98 }
99
100 return log;
101 }
102
103
104 static Boolean
105 restorePreferences()
106 {
107 Boolean ok = FALSE;
108 CFStringRef currentModel = NULL;
109 CFMutableStringRef modelPrefixStr = NULL;
110 CFArrayRef keyList = NULL;
111 CFIndex keyListCount;
112 CFIndex idx;
113 Boolean modified = FALSE;
114 int sc_status = kSCStatusFailed;
115
116 while (TRUE) {
117 ok = SCPreferencesLock(prefs, TRUE);
118 if (ok) {
119 break;
120 }
121
122 sc_status = SCError();
123 if (sc_status == kSCStatusStale) {
124 SCPreferencesSynchronize(prefs);
125 } else {
126 SC_log(LOG_NOTICE, "Could not acquire network configuration lock: %s",
127 SCErrorString(sc_status));
128 return FALSE;
129 }
130 }
131
132 keyList = SCPreferencesCopyKeyList(prefs);
133 if (keyList == NULL) {
134 goto error;
135 }
136
137 currentModel = _SC_hw_model(FALSE);
138 if (currentModel == NULL) {
139 goto error;
140 }
141
142 /* Create "model:" string for prefix-check */
143 modelPrefixStr = CFStringCreateMutableCopy(NULL, 0, currentModel);
144 CFStringAppend(modelPrefixStr, CFSTR(":"));
145
146 keyListCount = CFArrayGetCount(keyList);
147 for (idx = 0; idx < keyListCount; idx++) {
148 CFStringRef existingKey = CFArrayGetValueAtIndex(keyList, idx);
149 CFStringRef key;
150 CFArrayRef splitKey = NULL;
151 CFPropertyListRef value;
152
153 if (isA_CFString(existingKey) == NULL) {
154 continue;
155 }
156
157 if (!CFStringHasPrefix(existingKey, modelPrefixStr)) {
158 continue;
159 }
160
161 splitKey = CFStringCreateArrayBySeparatingStrings(NULL, existingKey, CFSTR(":"));
162 key = CFArrayGetValueAtIndex(splitKey, 1);
163 value = SCPreferencesGetValue(prefs, existingKey);
164 SCPreferencesSetValue(prefs, key, value);
165 SCPreferencesRemoveValue(prefs, existingKey);
166 modified = TRUE;
167 CFRelease(splitKey);
168 }
169
170 if (modified) {
171 SCPreferencesRef ni_prefs = NULL;
172 ni_prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, CFSTR("NetworkInterfaces.plist"));
173 if (ni_prefs == NULL) {
174 goto error;
175 }
176
177 ok = _SCNetworkConfigurationCheckValidityWithPreferences(prefs, ni_prefs, NULL);
178 CFRelease(ni_prefs);
179
180 //Commit the changes only if prefs files valid
181 if (ok) {
182 if (!SCPreferencesCommitChanges(prefs)) {
183 if (SCError() != EROFS) {
184 SC_log(LOG_NOTICE, "SCPreferencesCommitChanges() failed: %s",
185 SCErrorString(SCError()));
186 }
187 goto error;
188
189 }
190
191 (void) SCPreferencesApplyChanges(prefs);
192 }
193 }
194
195 error:
196 (void) SCPreferencesUnlock(prefs);
197
198 if (keyList != NULL) {
199 CFRelease(keyList);
200 }
201 if (modelPrefixStr != NULL) {
202 CFRelease(modelPrefixStr);
203 }
204
205 return modified;
206 }
207
208 static Boolean
209 establishNewPreferences()
210 {
211 SCNetworkSetRef current = NULL;
212 CFStringRef new_model;
213 Boolean ok = FALSE;
214 int sc_status = kSCStatusFailed;
215 SCNetworkSetRef set = NULL;
216 Boolean updated = FALSE;
217
218 while (TRUE) {
219 ok = SCPreferencesLock(prefs, TRUE);
220 if (ok) {
221 break;
222 }
223
224 sc_status = SCError();
225 if (sc_status == kSCStatusStale) {
226 SCPreferencesSynchronize(prefs);
227 } else {
228 SC_log(LOG_NOTICE, "Could not acquire network configuration lock: %s",
229 SCErrorString(sc_status));
230 return FALSE;
231 }
232 }
233
234 /* Ensure that the preferences has the new model */
235 new_model = _SC_hw_model(FALSE);
236
237 /* Need to regenerate the new configuration for new model */
238 if (new_model != NULL) {
239 CFStringRef old_model;
240
241 old_model = SCPreferencesGetValue(prefs, MODEL);
242 if ((old_model != NULL) && !_SC_CFEqual(old_model, new_model)) {
243 CFIndex count;
244 CFIndex index;
245 CFArrayRef keys;
246
247 keys = SCPreferencesCopyKeyList(prefs);
248 count = (keys != NULL) ? CFArrayGetCount(keys) : 0;
249 // if new hardware
250 for (index = 0; index < count; index++) {
251 CFStringRef existing_key;
252
253 existing_key = CFArrayGetValueAtIndex(keys, index);
254 if (isA_CFString(existing_key) != NULL) {
255 CFStringRef new_key;
256 CFPropertyListRef value;
257
258 /* If it already contains a Model
259 or if it already contains a MODEL:KEY key skip it*/
260 if (CFEqual(existing_key, MODEL)
261 || CFStringFind(existing_key, CFSTR(":"), 0).location
262 != kCFNotFound) {
263 continue;
264 }
265
266 value = SCPreferencesGetValue(prefs, existing_key);
267
268 /* Create a new key as OLD_MODEL:OLD_KEY */
269 new_key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:%@"),
270 old_model, existing_key);
271 SCPreferencesSetValue(prefs, new_key, value);
272 if (!CFEqual(existing_key, kSCPrefSystem)) {
273 /* preserve existing host names */
274 SCPreferencesRemoveValue(prefs, existing_key);
275 }
276 CFRelease(new_key);
277 }
278 }
279
280 if (keys != NULL) {
281 CFRelease(keys);
282 }
283 }
284 /* Set the new model */
285 SCPreferencesSetValue(prefs, MODEL, new_model);
286 }
287
288 current = SCNetworkSetCopyCurrent(prefs);
289 if (current != NULL) {
290 set = current;
291 }
292
293 if (set == NULL) {
294 set = _SCNetworkSetCreateDefault(prefs);
295 if (set == NULL) {
296 ok = FALSE;
297 sc_status = SCError();
298 goto done;
299 }
300 }
301
302 ok = SCNetworkSetEstablishDefaultConfiguration(set);
303 if (!ok) {
304 sc_status = SCError();
305 goto done;
306 }
307
308 done :
309
310 if (ok) {
311 ok = SCPreferencesCommitChanges(prefs);
312 if (ok) {
313 SC_log(LOG_NOTICE, "New network configuration saved");
314 updated = TRUE;
315 } else {
316 sc_status = SCError();
317 if (sc_status == EROFS) {
318 /* a read-only fileysstem is OK */
319 ok = TRUE;
320
321 /* ... but we don't want to synchronize */
322 rofs = TRUE;
323 }
324 }
325
326 /* apply (committed or temporary/read-only) changes */
327 (void) SCPreferencesApplyChanges(prefs);
328 } else if ((current == NULL) && (set != NULL)) {
329 (void) SCNetworkSetRemove(set);
330 }
331
332 if (!ok) {
333 if (sc_status == kSCStatusOK) {
334 SC_log(LOG_NOTICE, "Network configuration not updated");
335 } else {
336 SC_log(LOG_NOTICE, "Could not establish network configuration: %s",
337 SCErrorString(sc_status));
338 }
339 }
340
341 (void)SCPreferencesUnlock(prefs);
342 if (set != NULL) CFRelease(set);
343 return updated;
344 }
345
346
347 static void
348 watchSCDynamicStore()
349 {
350 CFMutableArrayRef keys;
351 Boolean ok;
352 CFRunLoopSourceRef rls;
353
354 /*
355 * watch for KernelEventMonitor[.bundle] changes (the list of
356 * active network interfaces)
357 */
358 interfacesKey = SCDynamicStoreKeyCreateNetworkInterface(NULL,
359 kSCDynamicStoreDomainState);
360
361 /*
362 * watch for InterfaceNamer[.bundle] changes (quiet, timeout,
363 * and the list of pre-configured interfaces)
364 */
365 namerKey = SCDynamicStoreKeyCreate(NULL,
366 CFSTR("%@" "InterfaceNamer"),
367 kSCDynamicStoreDomainPlugin);
368
369 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
370 if (rls == NULL) {
371 SC_log(LOG_NOTICE, "SCDynamicStoreCreateRunLoopSource() failed: %s", SCErrorString(SCError()));
372 haveConfiguration = TRUE;
373 return;
374 }
375 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
376 CFRelease(rls);
377
378 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
379 CFArrayAppendValue(keys, interfacesKey);
380 CFArrayAppendValue(keys, namerKey);
381 ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
382 CFRelease(keys);
383 if (!ok) {
384 SC_log(LOG_NOTICE, "SCDynamicStoreSetNotificationKeys() failed: %s", SCErrorString(SCError()));
385 haveConfiguration = TRUE;
386 }
387
388 return;
389 }
390
391
392 static Boolean
393 previousConfigurationAvailable()
394 {
395 CFStringRef backupKey = NULL;
396 CFStringRef currentModel = NULL;
397 CFPropertyListRef properties = NULL;
398
399 currentModel = _SC_hw_model(FALSE);
400 if (currentModel == NULL) {
401 goto done;
402 }
403
404 /* Currently relying only if a backup of "Sets" is present */
405 backupKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:Sets"), currentModel);
406 properties = SCPreferencesGetValue(prefs, backupKey);
407 CFRelease(backupKey);
408 done:
409 return (properties != NULL);
410 }
411
412
413 static Boolean
414 findInterfaces(CFArrayRef interfaces, CFMutableArrayRef *matched_interfaces, CFMutableArrayRef *matched_names)
415 {
416 CFIndex n;
417 CFIndex nx = 0;
418 Boolean updated = FALSE;
419
420 // start clean
421 if (*matched_interfaces != NULL) {
422 CFRelease(*matched_interfaces);
423 *matched_interfaces = NULL;
424 }
425 if (*matched_names != NULL) {
426 nx = CFArrayGetCount(*matched_names);
427 CFRelease(*matched_names);
428 *matched_names = NULL;
429 }
430
431 n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
432 for (CFIndex i = 0; i < n; i++) {
433 CFStringRef bsdName = CFArrayGetValueAtIndex(interfaces, i);
434 SCNetworkInterfaceRef interface;
435
436 for (int retry = 0; retry < 10; retry++) {
437 if (retry != 0) {
438 // add short delay (before retry)
439 usleep(20 * 1000); // 20ms
440 }
441
442 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
443 if (interface == NULL) {
444 SC_log(LOG_ERR, "could not create network interface for %@", bsdName);
445 } else if (_SCNetworkInterfaceGetIOPath(interface) == NULL) {
446 SC_log(LOG_ERR, "could not get IOPath for %@", bsdName);
447 CFRelease(interface);
448 interface = NULL;
449 }
450
451 if (interface == NULL) {
452 // if SCNetworkInterface not [currently] available
453 continue;
454 }
455
456 // keep track of the interface name (quicker than having to iterate the list
457 // of SCNetworkInterfaces, extract the name, and compare).
458 if (*matched_names == NULL) {
459 *matched_names = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
460 }
461 CFArrayAppendValue(*matched_names, bsdName);
462
463 if (*matched_interfaces == NULL) {
464 *matched_interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
465 }
466 CFArrayAppendValue(*matched_interfaces, interface);
467 CFRelease(interface);
468
469 updated = TRUE;
470 }
471 }
472
473 // check if all interfaces were detached
474 n = (*matched_names != NULL) ? CFArrayGetCount(*matched_names) : 0;
475 if ((nx > 0) && (n == 0)) {
476 updated = TRUE;
477 }
478
479 return updated;
480 }
481
482
483 static void
484 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
485 {
486 #pragma unused(info)
487 CFDictionaryRef dict;
488 Boolean quiet = FALSE;
489 Boolean timeout = FALSE;
490 Boolean updated = FALSE;
491
492 /*
493 * Capture/process InterfaceNamer[.bundle] info
494 * 1. check if IORegistry "quiet", "timeout"
495 * 2. update list of excluded interfaces (e.g. those requiring that
496 * the attached host be trusted)
497 * 3. update list of named pre-configured interfaces
498 */
499 dict = SCDynamicStoreCopyValue(store, namerKey);
500 if (dict != NULL) {
501 if (isA_CFDictionary(dict)) {
502 CFArrayRef excluded;
503 CFArrayRef preconfigured;
504
505 if (CFDictionaryContainsKey(dict, kInterfaceNamerKey_Quiet)) {
506 quiet = TRUE;
507 }
508 if (CFDictionaryContainsKey(dict, kInterfaceNamerKey_Timeout)) {
509 timeout = TRUE;
510 }
511
512 excluded = CFDictionaryGetValue(dict, kInterfaceNamerKey_ExcludedInterfaces);
513 excluded = isA_CFArray(excluded);
514 if (!_SC_CFEqual(excluded, excluded_names)) {
515 Boolean excluded_updated;
516
517 excluded_updated = findInterfaces(excluded, &excluded_interfaces, &excluded_names);
518 if (excluded_updated) {
519 CFStringRef interfaces = CFSTR("<empty>");
520
521 // report [updated] pre-configured interfaces
522 if (excluded_names != NULL) {
523 interfaces = CFStringCreateByCombiningStrings(NULL, excluded_names, CFSTR(","));
524 } else {
525 CFRetain(interfaces);
526 }
527 SC_log(LOG_INFO, "excluded interface list changed: %@", interfaces);
528 CFRelease(interfaces);
529
530 updated = TRUE;
531 }
532 }
533
534 preconfigured = CFDictionaryGetValue(dict, kInterfaceNamerKey_PreConfiguredInterfaces);
535 preconfigured = isA_CFArray(preconfigured);
536 if (!_SC_CFEqual(preconfigured, preconfigured_names)) {
537 Boolean preconfigured_updated;
538
539 preconfigured_updated = findInterfaces(preconfigured, &preconfigured_interfaces, &preconfigured_names);
540 if (preconfigured_updated) {
541 CFStringRef interfaces = CFSTR("<empty>");
542
543 // report [updated] pre-configured interfaces
544 if (preconfigured_names != NULL) {
545 interfaces = CFStringCreateByCombiningStrings(NULL, preconfigured_names, CFSTR(","));
546 } else {
547 CFRetain(interfaces);
548 }
549 SC_log(LOG_INFO, "pre-configured interface list changed: %@", interfaces);
550 CFRelease(interfaces);
551
552 updated = TRUE;
553 }
554 }
555 }
556
557 CFRelease(dict);
558 }
559
560 if (!haveConfiguration && (quiet || timeout)) {
561 static int logged = 0;
562
563 if (quiet
564 #if !TARGET_OS_IPHONE
565 || timeout
566 #endif /* !TARGET_OS_IPHONE */
567 ) {
568 haveConfiguration = TRUE;
569 }
570
571 (void) establishNewPreferences();
572
573 if (restorePrefs) {
574 (void) restorePreferences();
575 restorePrefs = FALSE;
576 }
577
578 if (timeout && (logged++ == 0)) {
579 SC_log(LOG_ERR, "Network configuration creation timed out waiting for IORegistry");
580 }
581 }
582
583 if (updated && (changedKeys != NULL)) {
584 // if pre-configured interface list changed
585 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
586 }
587
588 return;
589 }
590
591
592 static void
593 updateCache(const void *key, const void *value, void *context)
594 {
595 #pragma unused(context)
596 CFStringRef configKey = (CFStringRef)key;
597 CFPropertyListRef configData = (CFPropertyListRef)value;
598 CFPropertyListRef cacheData;
599 CFIndex i;
600
601 cacheData = CFDictionaryGetValue(currentPrefs, configKey);
602 if (cacheData) {
603 /* key exists */
604 if (CFEqual(cacheData, configData)) {
605 /*
606 * if the old & new property list values have
607 * not changed then we don't need to update
608 * the preference.
609 */
610 CFArrayAppendValue(unchangedPrefsKeys, configKey);
611 }
612 }
613
614 /* in any case, this key should not be removed */
615 i = CFArrayGetFirstIndexOfValue(removedPrefsKeys,
616 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys)),
617 configKey);
618 if (i != kCFNotFound) {
619 CFArrayRemoveValueAtIndex(removedPrefsKeys, i);
620 }
621
622 return;
623 }
624
625
626 static void
627 flatten(SCPreferencesRef prefs,
628 CFStringRef key,
629 CFDictionaryRef base)
630 {
631 CFDictionaryRef subset;
632 CFStringRef link;
633 CFMutableDictionaryRef myDict;
634 CFStringRef myKey;
635 CFIndex i;
636 CFIndex nKeys;
637 const void **keys;
638 const void **vals;
639
640 if (!CFDictionaryGetValueIfPresent(base, kSCResvLink, (const void **)&link)) {
641 /* if this dictionary is not linked */
642 subset = base;
643 } else {
644 /* if __LINK__ key is present */
645 subset = SCPreferencesPathGetValue(prefs, link);
646 if (!subset) {
647 /* if error with link */
648 SC_log(LOG_NOTICE, "SCPreferencesPathGetValue(,%@,) failed: %s",
649 link,
650 SCErrorString(SCError()));
651 return;
652 }
653 }
654
655 if (CFDictionaryContainsKey(subset, kSCResvInactive)) {
656 /* if __INACTIVE__ key is present */
657 return;
658 }
659
660 myKey = CFStringCreateWithFormat(NULL,
661 NULL,
662 CFSTR("%@%@"),
663 kSCDynamicStoreDomainSetup,
664 key);
665
666 myDict = (CFMutableDictionaryRef)CFDictionaryGetValue(newPrefs, myKey);
667 if (myDict) {
668 myDict = CFDictionaryCreateMutableCopy(NULL,
669 0,
670 (CFDictionaryRef)myDict);
671 } else {
672 myDict = CFDictionaryCreateMutable(NULL,
673 0,
674 &kCFTypeDictionaryKeyCallBacks,
675 &kCFTypeDictionaryValueCallBacks);
676 }
677
678 nKeys = CFDictionaryGetCount(subset);
679 if (nKeys > 0) {
680 keys = CFAllocatorAllocate(NULL, nKeys * sizeof(CFStringRef) , 0);
681 vals = CFAllocatorAllocate(NULL, nKeys * sizeof(CFPropertyListRef), 0);
682 CFDictionaryGetKeysAndValues(subset, keys, vals);
683 for (i = 0; i < nKeys; i++) {
684 if (CFGetTypeID((CFTypeRef)vals[i]) != CFDictionaryGetTypeID()) {
685 /* add this key/value to the current dictionary */
686 CFDictionarySetValue(myDict, keys[i], vals[i]);
687 } else {
688 CFStringRef subKey;
689
690 /* flatten [sub]dictionaries */
691 subKey = CFStringCreateWithFormat(NULL,
692 NULL,
693 CFSTR("%@%s%@"),
694 key,
695 CFEqual(key, CFSTR("/")) ? "" : "/",
696 keys[i]);
697 flatten(prefs, subKey, vals[i]);
698 CFRelease(subKey);
699 }
700 }
701 CFAllocatorDeallocate(NULL, keys);
702 CFAllocatorDeallocate(NULL, vals);
703 }
704
705 if (CFDictionaryGetCount(myDict) > 0) {
706 /* add this dictionary to the new preferences */
707 CFDictionarySetValue(newPrefs, myKey, myDict);
708 }
709
710 CFRelease(myDict);
711 CFRelease(myKey);
712
713 return;
714 }
715
716
717 static CF_RETURNS_RETAINED CFStringRef
718 copyInterfaceUUID(CFStringRef bsdName)
719 {
720 union {
721 unsigned char sha1_bytes[CC_SHA1_DIGEST_LENGTH];
722 CFUUIDBytes uuid_bytes;
723 } bytes;
724 CC_SHA1_CTX ctx;
725 char if_name[IF_NAMESIZE];
726 CFUUIDRef uuid;
727 CFStringRef uuid_str;
728
729 // start with interface name
730 bzero(&if_name, sizeof(if_name));
731 (void) _SC_cfstring_to_cstring(bsdName,
732 if_name,
733 sizeof(if_name),
734 kCFStringEncodingASCII);
735
736 // create SHA1 hash
737 bzero(&bytes, sizeof(bytes));
738 CC_SHA1_Init(&ctx);
739 CC_SHA1_Update(&ctx,
740 if_name,
741 sizeof(if_name));
742 CC_SHA1_Final(bytes.sha1_bytes, &ctx);
743
744 // create UUID string
745 uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes.uuid_bytes);
746 uuid_str = CFUUIDCreateString(NULL, uuid);
747 CFRelease(uuid);
748
749 return uuid_str;
750 }
751
752
753 static void
754 excludeConfigurations(SCPreferencesRef prefs)
755 {
756 Boolean ok;
757 CFRange range;
758 CFArrayRef services;
759 SCNetworkSetRef set;
760
761 range = CFRangeMake(0,
762 (excluded_names != NULL) ? CFArrayGetCount(excluded_names) : 0);
763 if (range.length == 0) {
764 // if no [excluded] interfaces
765 return;
766 }
767
768 set = SCNetworkSetCopyCurrent(prefs);
769 if (set == NULL) {
770 // if no current set
771 return;
772 }
773
774 /*
775 * Check for (and remove) any network services associated with
776 * an excluded interface from the prefs.
777 */
778 services = SCNetworkSetCopyServices(set);
779 if (services != NULL) {
780 CFIndex n;
781
782 n = CFArrayGetCount(services);
783 for (CFIndex i = 0; i < n; i++) {
784 CFStringRef bsdName;
785 SCNetworkInterfaceRef interface;
786 SCNetworkServiceRef service;
787
788 service = CFArrayGetValueAtIndex(services, i);
789
790 interface = SCNetworkServiceGetInterface(service);
791 if (interface == NULL) {
792 // if no interface
793 continue;
794 }
795
796 bsdName = SCNetworkInterfaceGetBSDName(interface);
797 if (bsdName == NULL) {
798 // if no interface name
799 continue;
800 }
801
802 if (!CFArrayContainsValue(excluded_names, range, bsdName)) {
803 // if not excluded
804 continue;
805 }
806
807 // remove [excluded] network service from the prefs
808 SC_log(LOG_NOTICE, "removing network service for %@", bsdName);
809 ok = SCNetworkSetRemoveService(set, service);
810 if (!ok) {
811 SC_log(LOG_ERR, "SCNetworkSetRemoveService() failed: %s",
812 SCErrorString(SCError()));
813 }
814 }
815
816 CFRelease(services);
817 }
818
819 CFRelease(set);
820 return;
821 }
822
823
824 static void
825 updatePreConfiguredConfiguration(SCPreferencesRef prefs)
826 {
827 Boolean ok;
828 CFRange range;
829 CFArrayRef services;
830 SCNetworkSetRef set;
831 Boolean updated = FALSE;
832
833 range = CFRangeMake(0,
834 (preconfigured_names != NULL) ? CFArrayGetCount(preconfigured_names) : 0);
835 if (range.length == 0) {
836 // if no [pre-configured] interfaces
837 return;
838 }
839
840 set = SCNetworkSetCopyCurrent(prefs);
841 if (set == NULL) {
842 // if no current set
843 return;
844 }
845
846 /*
847 * Check for (and remove) any network services associated with
848 * a pre-configured interface from the prefs.
849 */
850 services = SCNetworkServiceCopyAll(prefs);
851 if (services != NULL) {
852 CFIndex n;
853
854 n = CFArrayGetCount(services);
855 for (CFIndex i = 0; i < n; i++) {
856 CFStringRef bsdName;
857 SCNetworkInterfaceRef interface;
858 SCNetworkServiceRef service;
859
860 service = CFArrayGetValueAtIndex(services, i);
861
862 interface = SCNetworkServiceGetInterface(service);
863 if (interface == NULL) {
864 // if no interface
865 continue;
866 }
867
868 bsdName = SCNetworkInterfaceGetBSDName(interface);
869 if (bsdName == NULL) {
870 // if no interface name
871 continue;
872 }
873
874 if (!CFArrayContainsValue(preconfigured_names, range, bsdName)) {
875 // if not preconfigured
876 continue;
877 }
878
879 // remove [preconfigured] network service from the prefs
880 SC_log(LOG_NOTICE, "removing network service for %@", bsdName);
881 ok = SCNetworkServiceRemove(service);
882 if (!ok) {
883 SC_log(LOG_ERR, "SCNetworkServiceRemove() failed: %s",
884 SCErrorString(SCError()));
885 }
886 updated = TRUE;
887 }
888
889 CFRelease(services);
890 }
891
892 if (updated) {
893 // commit the updated prefs ... but don't apply
894 ok = SCPreferencesCommitChanges(prefs);
895 if (!ok) {
896 if (SCError() != EROFS) {
897 SC_log(LOG_ERR, "SCPreferencesCommitChanges() failed: %s",
898 SCErrorString(SCError()));
899 }
900 }
901 }
902
903 /*
904 * Now, add a new network service for each pre-configured interface
905 */
906 for (CFIndex i = 0; i < range.length; i++) {
907 CFStringRef bsdName;
908 SCNetworkInterfaceRef interface = CFArrayGetValueAtIndex(preconfigured_interfaces, i);
909 SCNetworkServiceRef service;
910 CFStringRef serviceID;
911
912 bsdName = SCNetworkInterfaceGetBSDName(interface);
913
914 // create network service
915 service = SCNetworkServiceCreate(prefs, interface);
916 if (service == NULL) {
917 SC_log(LOG_ERR, "could not create network service for \"%@\": %s",
918 bsdName,
919 SCErrorString(SCError()));
920 continue;
921 }
922
923 // update network service to use a consistent serviceID
924 serviceID = copyInterfaceUUID(bsdName);
925 if (serviceID != NULL) {
926 ok = _SCNetworkServiceSetServiceID(service, serviceID);
927 CFRelease(serviceID);
928 if (!ok) {
929 SC_log(LOG_ERR, "_SCNetworkServiceSetServiceID() failed: %s",
930 SCErrorString(SCError()));
931 // ... and keep whatever random UUID was created for the service
932 }
933 } else {
934 SC_log(LOG_ERR, "could not create serviceID for \"%@\"", bsdName);
935 // ... and we'll use whatever random UUID was created for the service
936 }
937
938 // establish [template] configuration
939 ok = SCNetworkServiceEstablishDefaultConfiguration(service);
940 if (!ok) {
941 SC_log(LOG_ERR, "could not establish network service for \"%@\": %s",
942 bsdName,
943 SCErrorString(SCError()));
944 SCNetworkServiceRemove(service);
945 CFRelease(service);
946 continue;
947 }
948
949 // add network service to the current set
950 ok = SCNetworkSetAddService(set, service);
951 if (!ok) {
952 SC_log(LOG_ERR, "could not add service for \"%@\": %s",
953 bsdName,
954 SCErrorString(SCError()));
955 SCNetworkServiceRemove(service);
956 CFRelease(service);
957 continue;
958 }
959
960 SC_log(LOG_INFO, "network service %@ added for \"%@\"",
961 SCNetworkServiceGetServiceID(service),
962 bsdName);
963
964 CFRelease(service);
965 }
966
967 CFRelease(set);
968 return;
969 }
970
971
972 static void
973 updateSCDynamicStore(SCPreferencesRef prefs)
974 {
975 CFStringRef current = NULL;
976 CFDateRef date = NULL;
977 CFMutableDictionaryRef dict = NULL;
978 CFDictionaryRef global = NULL;
979 CFIndex i;
980 CFArrayRef keys;
981 CFIndex n;
982 CFStringRef pattern;
983 CFMutableArrayRef patterns;
984 CFDictionaryRef set = NULL;
985
986 /*
987 * initialize old preferences, new preferences, an array
988 * of keys which have not changed, and an array of keys
989 * to be removed (cleaned up).
990 */
991
992 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
993 pattern = CFStringCreateWithFormat(NULL,
994 NULL,
995 CFSTR("^%@.*"),
996 kSCDynamicStoreDomainSetup);
997 CFArrayAppendValue(patterns, pattern);
998 dict = (CFMutableDictionaryRef)SCDynamicStoreCopyMultiple(store, NULL, patterns);
999 CFRelease(patterns);
1000 CFRelease(pattern);
1001 if (dict) {
1002 currentPrefs = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1003 CFRelease(dict);
1004 } else {
1005 currentPrefs = CFDictionaryCreateMutable(NULL,
1006 0,
1007 &kCFTypeDictionaryKeyCallBacks,
1008 &kCFTypeDictionaryValueCallBacks);
1009 }
1010
1011 unchangedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1012
1013 i = CFDictionaryGetCount(currentPrefs);
1014 if (i > 0) {
1015 const void **currentKeys;
1016 CFArrayRef array;
1017
1018 currentKeys = CFAllocatorAllocate(NULL, i * sizeof(CFStringRef), 0);
1019 CFDictionaryGetKeysAndValues(currentPrefs, currentKeys, NULL);
1020 array = CFArrayCreate(NULL, currentKeys, i, &kCFTypeArrayCallBacks);
1021 removedPrefsKeys = CFArrayCreateMutableCopy(NULL, 0, array);
1022 CFRelease(array);
1023 CFAllocatorDeallocate(NULL, currentKeys);
1024 } else {
1025 removedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1026 }
1027
1028 /*
1029 * The "newPrefs" dictionary will contain the new / updated
1030 * configuration which will be written to the configuration cache.
1031 */
1032 newPrefs = CFDictionaryCreateMutable(NULL,
1033 0,
1034 &kCFTypeDictionaryKeyCallBacks,
1035 &kCFTypeDictionaryValueCallBacks);
1036
1037 /*
1038 * create status dictionary associated with current configuration
1039 * information including:
1040 * - current set "name" to cache
1041 * - time stamp indicating when the cache preferences were
1042 * last updated.
1043 */
1044 dict = CFDictionaryCreateMutable(NULL,
1045 0,
1046 &kCFTypeDictionaryKeyCallBacks,
1047 &kCFTypeDictionaryValueCallBacks);
1048 date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
1049
1050 /*
1051 * load preferences
1052 */
1053 keys = SCPreferencesCopyKeyList(prefs);
1054 if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
1055 SC_log(LOG_NOTICE, "updateConfiguration(): no preferences");
1056 goto done;
1057 }
1058
1059 /*
1060 * get "global" system preferences
1061 */
1062 global = SCPreferencesGetValue(prefs, kSCPrefSystem);
1063 if (!global) {
1064 /* if no global preferences are defined */
1065 goto getSet;
1066 }
1067
1068 if (!isA_CFDictionary(global)) {
1069 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
1070 kSCPrefSystem);
1071 goto done;
1072 }
1073
1074 /* flatten property list */
1075 flatten(prefs, CFSTR("/"), global);
1076
1077 getSet :
1078
1079 /*
1080 * get current set name
1081 */
1082 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
1083 if (!current) {
1084 /* if current set not defined */
1085 goto done;
1086 }
1087
1088 if (!isA_CFString(current)) {
1089 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a string",
1090 kSCPrefCurrentSet);
1091 goto done;
1092 }
1093
1094 /*
1095 * get current set
1096 */
1097 set = SCPreferencesPathGetValue(prefs, current);
1098 if (!set) {
1099 /* if error with path */
1100 SC_log(LOG_NOTICE, "%@ value (%@) not valid",
1101 kSCPrefCurrentSet,
1102 current);
1103 goto done;
1104 }
1105
1106 if (!isA_CFDictionary(set)) {
1107 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
1108 current);
1109 goto done;
1110 }
1111
1112 /* flatten property list */
1113 flatten(prefs, CFSTR("/"), set);
1114
1115 CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current);
1116
1117 done :
1118
1119 /* add last updated time stamp */
1120 CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date);
1121
1122 /* add Setup: key */
1123 CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict);
1124
1125 /* compare current and new preferences */
1126 CFDictionaryApplyFunction(newPrefs, updateCache, NULL);
1127
1128 /* remove those keys which have not changed from the update */
1129 n = CFArrayGetCount(unchangedPrefsKeys);
1130 for (i = 0; i < n; i++) {
1131 CFStringRef key;
1132
1133 key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i);
1134 CFDictionaryRemoveValue(newPrefs, key);
1135 }
1136
1137 /* Update the dynamic store */
1138 #ifndef MAIN
1139 if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
1140 SC_log(LOG_NOTICE, "SCDynamicStoreSetMultiple() failed: %s", SCErrorString(SCError()));
1141 }
1142 #else // !MAIN
1143 SC_log(LOG_DEBUG, "SCDynamicStore\nset: %@\nremove: %@",
1144 newPrefs,
1145 removedPrefsKeys);
1146 #endif // !MAIN
1147
1148 CFRelease(currentPrefs);
1149 CFRelease(newPrefs);
1150 CFRelease(unchangedPrefsKeys);
1151 CFRelease(removedPrefsKeys);
1152 if (dict) CFRelease(dict);
1153 if (date) CFRelease(date);
1154 if (keys) CFRelease(keys);
1155 return;
1156 }
1157
1158
1159 static void
1160 updateConfiguration(SCPreferencesRef prefs,
1161 SCPreferencesNotification notificationType,
1162 void *info)
1163 {
1164 #pragma unused(info)
1165 os_activity_t activity;
1166
1167 activity = os_activity_create("processing [SC] preferences.plist changes",
1168 OS_ACTIVITY_CURRENT,
1169 OS_ACTIVITY_FLAG_DEFAULT);
1170 os_activity_scope(activity);
1171
1172 #if !TARGET_OS_IPHONE
1173 if ((notificationType & kSCPreferencesNotificationCommit) == kSCPreferencesNotificationCommit) {
1174 SCNetworkSetRef current;
1175
1176 current = SCNetworkSetCopyCurrent(prefs);
1177 if (current != NULL) {
1178 /* network configuration available, disable template creation */
1179 haveConfiguration = TRUE;
1180 CFRelease(current);
1181 }
1182 }
1183 #endif /* !TARGET_OS_IPHONE */
1184
1185 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
1186 goto done;
1187 }
1188
1189 SC_log(LOG_INFO, "updating configuration");
1190
1191 /* add any [Apple] pre-configured network services */
1192 updatePreConfiguredConfiguration(prefs);
1193
1194 /* remove any excluded network services */
1195 excludeConfigurations(prefs);
1196
1197 /* update SCDynamicStore (Setup:) */
1198 updateSCDynamicStore(prefs);
1199
1200 /* finished with current prefs, wait for changes */
1201 if (!rofs) {
1202 SCPreferencesSynchronize(prefs);
1203 }
1204
1205 done :
1206
1207 os_release(activity);
1208
1209 return;
1210 }
1211
1212
1213 __private_extern__
1214 void
1215 prime_PreferencesMonitor()
1216 {
1217 SC_log(LOG_DEBUG, "prime() called");
1218
1219 /* load the initial configuration from the database */
1220 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
1221
1222 return;
1223 }
1224
1225
1226 __private_extern__
1227 void
1228 load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
1229 {
1230 #pragma unused(bundle)
1231 #pragma unused(bundleVerbose)
1232 SC_log(LOG_DEBUG, "load() called");
1233 SC_log(LOG_DEBUG, " bundle ID = %@", CFBundleGetIdentifier(bundle));
1234
1235 /* open a SCDynamicStore session to allow cache updates */
1236 store = SCDynamicStoreCreate(NULL,
1237 CFSTR("PreferencesMonitor.bundle"),
1238 storeCallback,
1239 NULL);
1240 if (store == NULL) {
1241 SC_log(LOG_NOTICE, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError()));
1242 goto error;
1243 }
1244
1245 /* open a SCPreferences session */
1246 #ifndef MAIN
1247 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
1248 #else // !MAIN
1249 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), CFSTR("/tmp/preferences.plist"));
1250 #endif // !MAIN
1251 if (prefs != NULL) {
1252 Boolean need_update = FALSE;
1253 CFStringRef new_model;
1254
1255 new_model = _SC_hw_model(FALSE);
1256
1257 /* Need to regenerate the new configuration for new model */
1258 if (new_model != NULL) {
1259 CFStringRef old_model;
1260
1261 old_model = SCPreferencesGetValue(prefs, MODEL);
1262 if (old_model != NULL && !_SC_CFEqual(old_model, new_model)) {
1263 // if new hardware
1264 need_update = TRUE;
1265 restorePrefs = previousConfigurationAvailable();
1266 }
1267 }
1268
1269 if (!need_update) {
1270 SCNetworkSetRef current;
1271
1272 current = SCNetworkSetCopyCurrent(prefs);
1273 if (current != NULL) {
1274 /* network configuration available, disable template creation */
1275 haveConfiguration = TRUE;
1276 CFRelease(current);
1277 }
1278 }
1279 } else {
1280 SC_log(LOG_NOTICE, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
1281 goto error;
1282 }
1283
1284 /*
1285 * register for change notifications.
1286 */
1287 if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
1288 SC_log(LOG_NOTICE, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
1289 goto error;
1290 }
1291
1292 if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
1293 SC_log(LOG_NOTICE, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
1294 goto error;
1295 }
1296
1297 /*
1298 * watch InterfaceNamer and KernelEventMonitor changes to know when
1299 * the IORegistry has quiesced (to create the initial configuration
1300 * template), to track any pre-configured interfaces, and to ensure
1301 * that we create a network service for any active interfaces.
1302 */
1303 watchSCDynamicStore();
1304 storeCallback(store, NULL, NULL);
1305
1306 return;
1307
1308 error :
1309
1310 if (store != NULL) CFRelease(store);
1311 if (prefs != NULL) CFRelease(prefs);
1312 haveConfiguration = TRUE;
1313
1314 return;
1315 }
1316
1317
1318 #ifdef MAIN
1319 int
1320 main(int argc, char **argv)
1321 {
1322 _sc_log = FALSE;
1323 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1324
1325 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
1326 prime_PreferencesMonitor();
1327 CFRunLoopRun();
1328 /* not reached */
1329 exit(0);
1330 return 0;
1331 }
1332 #endif