]> git.saurik.com Git - apple/configd.git/blob - Plugins/PreferencesMonitor/prefsmon.c
configd-1061.0.2.tar.gz
[apple/configd.git] / Plugins / PreferencesMonitor / prefsmon.c
1 /*
2 * Copyright (c) 2000-2008, 2010, 2012-2019 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 break;
471 }
472 }
473
474 // check if all interfaces were detached
475 n = (*matched_names != NULL) ? CFArrayGetCount(*matched_names) : 0;
476 if ((nx > 0) && (n == 0)) {
477 updated = TRUE;
478 }
479
480 return updated;
481 }
482
483
484 static void
485 storeCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
486 {
487 #pragma unused(info)
488 CFDictionaryRef dict;
489 Boolean quiet = FALSE;
490 Boolean timeout = FALSE;
491 Boolean updated = FALSE;
492
493 /*
494 * Capture/process InterfaceNamer[.bundle] info
495 * 1. check if IORegistry "quiet", "timeout"
496 * 2. update list of excluded interfaces (e.g. those requiring that
497 * the attached host be trusted)
498 * 3. update list of named pre-configured interfaces
499 */
500 dict = SCDynamicStoreCopyValue(store, namerKey);
501 if (dict != NULL) {
502 if (isA_CFDictionary(dict)) {
503 CFArrayRef excluded;
504 CFArrayRef preconfigured;
505
506 if (CFDictionaryContainsKey(dict, kInterfaceNamerKey_Quiet)) {
507 quiet = TRUE;
508 }
509 if (CFDictionaryContainsKey(dict, kInterfaceNamerKey_Timeout)) {
510 timeout = TRUE;
511 }
512
513 excluded = CFDictionaryGetValue(dict, kInterfaceNamerKey_ExcludedInterfaces);
514 excluded = isA_CFArray(excluded);
515 if (!_SC_CFEqual(excluded, excluded_names)) {
516 Boolean excluded_updated;
517
518 excluded_updated = findInterfaces(excluded, &excluded_interfaces, &excluded_names);
519 if (excluded_updated) {
520 CFStringRef interfaces = CFSTR("<empty>");
521
522 // report [updated] pre-configured interfaces
523 if (excluded_names != NULL) {
524 interfaces = CFStringCreateByCombiningStrings(NULL, excluded_names, CFSTR(","));
525 } else {
526 CFRetain(interfaces);
527 }
528 SC_log(LOG_INFO, "excluded interface list changed: %@", interfaces);
529 CFRelease(interfaces);
530
531 updated = TRUE;
532 }
533 }
534
535 preconfigured = CFDictionaryGetValue(dict, kInterfaceNamerKey_PreConfiguredInterfaces);
536 preconfigured = isA_CFArray(preconfigured);
537 if (!_SC_CFEqual(preconfigured, preconfigured_names)) {
538 Boolean preconfigured_updated;
539
540 preconfigured_updated = findInterfaces(preconfigured, &preconfigured_interfaces, &preconfigured_names);
541 if (preconfigured_updated) {
542 CFStringRef interfaces = CFSTR("<empty>");
543
544 // report [updated] pre-configured interfaces
545 if (preconfigured_names != NULL) {
546 interfaces = CFStringCreateByCombiningStrings(NULL, preconfigured_names, CFSTR(","));
547 } else {
548 CFRetain(interfaces);
549 }
550 SC_log(LOG_INFO, "pre-configured interface list changed: %@", interfaces);
551 CFRelease(interfaces);
552
553 updated = TRUE;
554 }
555 }
556 }
557
558 CFRelease(dict);
559 }
560
561 if (!haveConfiguration && (quiet || timeout)) {
562 static int logged = 0;
563
564 if (quiet
565 #if !TARGET_OS_IPHONE
566 || timeout
567 #endif /* !TARGET_OS_IPHONE */
568 ) {
569 haveConfiguration = TRUE;
570 }
571
572 (void) establishNewPreferences();
573
574 if (restorePrefs) {
575 (void) restorePreferences();
576 restorePrefs = FALSE;
577 }
578
579 if (timeout && (logged++ == 0)) {
580 SC_log(LOG_ERR, "Network configuration creation timed out waiting for IORegistry");
581 }
582 }
583
584 if (updated && (changedKeys != NULL)) {
585 // if pre-configured interface list changed
586 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
587 }
588
589 return;
590 }
591
592
593 static void
594 updateCache(const void *key, const void *value, void *context)
595 {
596 #pragma unused(context)
597 CFStringRef configKey = (CFStringRef)key;
598 CFPropertyListRef configData = (CFPropertyListRef)value;
599 CFPropertyListRef cacheData;
600 CFIndex i;
601
602 cacheData = CFDictionaryGetValue(currentPrefs, configKey);
603 if (cacheData) {
604 /* key exists */
605 if (CFEqual(cacheData, configData)) {
606 /*
607 * if the old & new property list values have
608 * not changed then we don't need to update
609 * the preference.
610 */
611 CFArrayAppendValue(unchangedPrefsKeys, configKey);
612 }
613 }
614
615 /* in any case, this key should not be removed */
616 i = CFArrayGetFirstIndexOfValue(removedPrefsKeys,
617 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys)),
618 configKey);
619 if (i != kCFNotFound) {
620 CFArrayRemoveValueAtIndex(removedPrefsKeys, i);
621 }
622
623 return;
624 }
625
626
627 static void
628 flatten(SCPreferencesRef prefs,
629 CFStringRef key,
630 CFDictionaryRef base)
631 {
632 CFDictionaryRef subset;
633 CFStringRef link;
634 CFMutableDictionaryRef myDict;
635 CFStringRef myKey;
636 CFIndex i;
637 CFIndex nKeys;
638 const void **keys;
639 const void **vals;
640
641 if (!CFDictionaryGetValueIfPresent(base, kSCResvLink, (const void **)&link)) {
642 /* if this dictionary is not linked */
643 subset = base;
644 } else {
645 /* if __LINK__ key is present */
646 subset = SCPreferencesPathGetValue(prefs, link);
647 if (!subset) {
648 /* if error with link */
649 SC_log(LOG_NOTICE, "SCPreferencesPathGetValue(,%@,) failed: %s",
650 link,
651 SCErrorString(SCError()));
652 return;
653 }
654 }
655
656 if (CFDictionaryContainsKey(subset, kSCResvInactive)) {
657 /* if __INACTIVE__ key is present */
658 return;
659 }
660
661 myKey = CFStringCreateWithFormat(NULL,
662 NULL,
663 CFSTR("%@%@"),
664 kSCDynamicStoreDomainSetup,
665 key);
666
667 myDict = (CFMutableDictionaryRef)CFDictionaryGetValue(newPrefs, myKey);
668 if (myDict) {
669 myDict = CFDictionaryCreateMutableCopy(NULL,
670 0,
671 (CFDictionaryRef)myDict);
672 } else {
673 myDict = CFDictionaryCreateMutable(NULL,
674 0,
675 &kCFTypeDictionaryKeyCallBacks,
676 &kCFTypeDictionaryValueCallBacks);
677 }
678
679 nKeys = CFDictionaryGetCount(subset);
680 if (nKeys > 0) {
681 keys = CFAllocatorAllocate(NULL, nKeys * sizeof(CFStringRef) , 0);
682 vals = CFAllocatorAllocate(NULL, nKeys * sizeof(CFPropertyListRef), 0);
683 CFDictionaryGetKeysAndValues(subset, keys, vals);
684 for (i = 0; i < nKeys; i++) {
685 if (CFGetTypeID((CFTypeRef)vals[i]) != CFDictionaryGetTypeID()) {
686 /* add this key/value to the current dictionary */
687 CFDictionarySetValue(myDict, keys[i], vals[i]);
688 } else {
689 CFStringRef subKey;
690
691 /* flatten [sub]dictionaries */
692 subKey = CFStringCreateWithFormat(NULL,
693 NULL,
694 CFSTR("%@%s%@"),
695 key,
696 CFEqual(key, CFSTR("/")) ? "" : "/",
697 keys[i]);
698 flatten(prefs, subKey, vals[i]);
699 CFRelease(subKey);
700 }
701 }
702 CFAllocatorDeallocate(NULL, keys);
703 CFAllocatorDeallocate(NULL, vals);
704 }
705
706 if (CFDictionaryGetCount(myDict) > 0) {
707 /* add this dictionary to the new preferences */
708 CFDictionarySetValue(newPrefs, myKey, myDict);
709 }
710
711 CFRelease(myDict);
712 CFRelease(myKey);
713
714 return;
715 }
716
717
718 static CF_RETURNS_RETAINED CFStringRef
719 copyInterfaceUUID(CFStringRef bsdName)
720 {
721 union {
722 unsigned char sha256_bytes[CC_SHA256_DIGEST_LENGTH];
723 CFUUIDBytes uuid_bytes;
724 } bytes;
725 CC_SHA256_CTX ctx;
726 char if_name[IF_NAMESIZE];
727 CFUUIDRef uuid;
728 CFStringRef uuid_str;
729
730 // start with interface name
731 memset(&if_name, 0, sizeof(if_name));
732 (void) _SC_cfstring_to_cstring(bsdName,
733 if_name,
734 sizeof(if_name),
735 kCFStringEncodingASCII);
736
737 // create SHA256 hash
738 memset(&bytes, 0, sizeof(bytes));
739 CC_SHA256_Init(&ctx);
740 CC_SHA256_Update(&ctx,
741 if_name,
742 sizeof(if_name));
743 CC_SHA256_Final(bytes.sha256_bytes, &ctx);
744
745 // create UUID string
746 uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes.uuid_bytes);
747 uuid_str = CFUUIDCreateString(NULL, uuid);
748 CFRelease(uuid);
749
750 return uuid_str;
751 }
752
753
754 static void
755 excludeConfigurations(SCPreferencesRef prefs)
756 {
757 Boolean ok;
758 CFRange range;
759 CFArrayRef services;
760 SCNetworkSetRef set;
761
762 range = CFRangeMake(0,
763 (excluded_names != NULL) ? CFArrayGetCount(excluded_names) : 0);
764 if (range.length == 0) {
765 // if no [excluded] interfaces
766 return;
767 }
768
769 set = SCNetworkSetCopyCurrent(prefs);
770 if (set == NULL) {
771 // if no current set
772 return;
773 }
774
775 /*
776 * Check for (and remove) any network services associated with
777 * an excluded interface from the prefs.
778 */
779 services = SCNetworkSetCopyServices(set);
780 if (services != NULL) {
781 CFIndex n;
782
783 n = CFArrayGetCount(services);
784 for (CFIndex i = 0; i < n; i++) {
785 CFStringRef bsdName;
786 SCNetworkInterfaceRef interface;
787 SCNetworkServiceRef service;
788
789 service = CFArrayGetValueAtIndex(services, i);
790
791 interface = SCNetworkServiceGetInterface(service);
792 if (interface == NULL) {
793 // if no interface
794 continue;
795 }
796
797 bsdName = SCNetworkInterfaceGetBSDName(interface);
798 if (bsdName == NULL) {
799 // if no interface name
800 continue;
801 }
802
803 if (!CFArrayContainsValue(excluded_names, range, bsdName)) {
804 // if not excluded
805 continue;
806 }
807
808 // remove [excluded] network service from the prefs
809 SC_log(LOG_NOTICE, "excluding network service for %@", bsdName);
810 ok = SCNetworkSetRemoveService(set, service);
811 if (!ok) {
812 SC_log(LOG_ERR, "SCNetworkSetRemoveService() failed: %s",
813 SCErrorString(SCError()));
814 }
815 }
816
817 CFRelease(services);
818 }
819
820 CFRelease(set);
821 return;
822 }
823
824
825 static void
826 updatePreConfiguredConfiguration(SCPreferencesRef prefs)
827 {
828 Boolean ok;
829 CFRange range;
830 CFArrayRef services;
831 SCNetworkSetRef set;
832 Boolean updated = FALSE;
833
834 range = CFRangeMake(0,
835 (preconfigured_names != NULL) ? CFArrayGetCount(preconfigured_names) : 0);
836 if (range.length == 0) {
837 // if no [pre-configured] interfaces
838 return;
839 }
840
841 set = SCNetworkSetCopyCurrent(prefs);
842 if (set == NULL) {
843 // if no current set
844 return;
845 }
846
847 /*
848 * Check for (and remove) any network services associated with
849 * a pre-configured interface from the prefs.
850 */
851 services = SCNetworkServiceCopyAll(prefs);
852 if (services != NULL) {
853 CFIndex n;
854
855 n = CFArrayGetCount(services);
856 for (CFIndex i = 0; i < n; i++) {
857 CFStringRef bsdName;
858 SCNetworkInterfaceRef interface;
859 SCNetworkServiceRef service;
860
861 service = CFArrayGetValueAtIndex(services, i);
862
863 interface = SCNetworkServiceGetInterface(service);
864 if (interface == NULL) {
865 // if no interface
866 continue;
867 }
868
869 bsdName = SCNetworkInterfaceGetBSDName(interface);
870 if (bsdName == NULL) {
871 // if no interface name
872 continue;
873 }
874
875 if (!CFArrayContainsValue(preconfigured_names, range, bsdName)) {
876 // if not preconfigured
877 continue;
878 }
879
880 // remove [preconfigured] network service from the prefs
881 SC_log(LOG_NOTICE, "removing network service for %@", bsdName);
882 ok = SCNetworkServiceRemove(service);
883 if (!ok) {
884 SC_log(LOG_ERR, "SCNetworkServiceRemove() failed: %s",
885 SCErrorString(SCError()));
886 }
887 updated = TRUE;
888 }
889
890 CFRelease(services);
891 }
892
893 if (updated) {
894 // commit the updated prefs ... but don't apply
895 ok = SCPreferencesCommitChanges(prefs);
896 if (!ok) {
897 if (SCError() != EROFS) {
898 SC_log(LOG_ERR, "SCPreferencesCommitChanges() failed: %s",
899 SCErrorString(SCError()));
900 }
901 }
902 }
903
904 /*
905 * Now, add a new network service for each pre-configured interface
906 */
907 for (CFIndex i = 0; i < range.length; i++) {
908 CFStringRef bsdName;
909 SCNetworkInterfaceRef interface = CFArrayGetValueAtIndex(preconfigured_interfaces, i);
910 SCNetworkServiceRef service;
911 CFStringRef serviceID;
912
913 bsdName = SCNetworkInterfaceGetBSDName(interface);
914
915 // create network service
916 service = SCNetworkServiceCreate(prefs, interface);
917 if (service == NULL) {
918 SC_log(LOG_ERR, "could not create network service for \"%@\": %s",
919 bsdName,
920 SCErrorString(SCError()));
921 continue;
922 }
923
924 // update network service to use a consistent serviceID
925 serviceID = copyInterfaceUUID(bsdName);
926 if (serviceID != NULL) {
927 ok = _SCNetworkServiceSetServiceID(service, serviceID);
928 CFRelease(serviceID);
929 if (!ok) {
930 SC_log(LOG_ERR, "_SCNetworkServiceSetServiceID() failed: %s",
931 SCErrorString(SCError()));
932 // ... and keep whatever random UUID was created for the service
933 }
934 } else {
935 SC_log(LOG_ERR, "could not create serviceID for \"%@\"", bsdName);
936 // ... and we'll use whatever random UUID was created for the service
937 }
938
939 // establish [template] configuration
940 ok = SCNetworkServiceEstablishDefaultConfiguration(service);
941 if (!ok) {
942 SC_log(LOG_ERR, "could not establish network service for \"%@\": %s",
943 bsdName,
944 SCErrorString(SCError()));
945 SCNetworkServiceRemove(service);
946 CFRelease(service);
947 continue;
948 }
949
950 // add network service to the current set
951 ok = SCNetworkSetAddService(set, service);
952 if (!ok) {
953 SC_log(LOG_ERR, "could not add service for \"%@\": %s",
954 bsdName,
955 SCErrorString(SCError()));
956 SCNetworkServiceRemove(service);
957 CFRelease(service);
958 continue;
959 }
960
961 SC_log(LOG_INFO, "network service %@ added for \"%@\"",
962 SCNetworkServiceGetServiceID(service),
963 bsdName);
964
965 CFRelease(service);
966 }
967
968 CFRelease(set);
969 return;
970 }
971
972
973 static void
974 updateSCDynamicStore(SCPreferencesRef prefs)
975 {
976 CFStringRef current = NULL;
977 CFDateRef date = NULL;
978 CFMutableDictionaryRef dict = NULL;
979 CFDictionaryRef global = NULL;
980 CFIndex i;
981 CFArrayRef keys;
982 CFIndex n;
983 CFStringRef pattern;
984 CFMutableArrayRef patterns;
985 CFDictionaryRef set = NULL;
986
987 /*
988 * initialize old preferences, new preferences, an array
989 * of keys which have not changed, and an array of keys
990 * to be removed (cleaned up).
991 */
992
993 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
994 pattern = CFStringCreateWithFormat(NULL,
995 NULL,
996 CFSTR("^%@.*"),
997 kSCDynamicStoreDomainSetup);
998 CFArrayAppendValue(patterns, pattern);
999 dict = (CFMutableDictionaryRef)SCDynamicStoreCopyMultiple(store, NULL, patterns);
1000 CFRelease(patterns);
1001 CFRelease(pattern);
1002 if (dict) {
1003 currentPrefs = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1004 CFRelease(dict);
1005 } else {
1006 currentPrefs = CFDictionaryCreateMutable(NULL,
1007 0,
1008 &kCFTypeDictionaryKeyCallBacks,
1009 &kCFTypeDictionaryValueCallBacks);
1010 }
1011
1012 unchangedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1013
1014 i = CFDictionaryGetCount(currentPrefs);
1015 if (i > 0) {
1016 const void **currentKeys;
1017 CFArrayRef array;
1018
1019 currentKeys = CFAllocatorAllocate(NULL, i * sizeof(CFStringRef), 0);
1020 CFDictionaryGetKeysAndValues(currentPrefs, currentKeys, NULL);
1021 array = CFArrayCreate(NULL, currentKeys, i, &kCFTypeArrayCallBacks);
1022 removedPrefsKeys = CFArrayCreateMutableCopy(NULL, 0, array);
1023 CFRelease(array);
1024 CFAllocatorDeallocate(NULL, currentKeys);
1025 } else {
1026 removedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1027 }
1028
1029 /*
1030 * The "newPrefs" dictionary will contain the new / updated
1031 * configuration which will be written to the configuration cache.
1032 */
1033 newPrefs = CFDictionaryCreateMutable(NULL,
1034 0,
1035 &kCFTypeDictionaryKeyCallBacks,
1036 &kCFTypeDictionaryValueCallBacks);
1037
1038 /*
1039 * create status dictionary associated with current configuration
1040 * information including:
1041 * - current set "name" to cache
1042 * - time stamp indicating when the cache preferences were
1043 * last updated.
1044 */
1045 dict = CFDictionaryCreateMutable(NULL,
1046 0,
1047 &kCFTypeDictionaryKeyCallBacks,
1048 &kCFTypeDictionaryValueCallBacks);
1049 date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
1050
1051 /*
1052 * load preferences
1053 */
1054 keys = SCPreferencesCopyKeyList(prefs);
1055 if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
1056 SC_log(LOG_NOTICE, "updateConfiguration(): no preferences");
1057 goto done;
1058 }
1059
1060 /*
1061 * get "global" system preferences
1062 */
1063 global = SCPreferencesGetValue(prefs, kSCPrefSystem);
1064 if (!global) {
1065 /* if no global preferences are defined */
1066 goto getSet;
1067 }
1068
1069 if (!isA_CFDictionary(global)) {
1070 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
1071 kSCPrefSystem);
1072 goto done;
1073 }
1074
1075 /* flatten property list */
1076 flatten(prefs, CFSTR("/"), global);
1077
1078 getSet :
1079
1080 /*
1081 * get current set name
1082 */
1083 current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
1084 if (!current) {
1085 /* if current set not defined */
1086 goto done;
1087 }
1088
1089 if (!isA_CFString(current)) {
1090 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a string",
1091 kSCPrefCurrentSet);
1092 goto done;
1093 }
1094
1095 /*
1096 * get current set
1097 */
1098 set = SCPreferencesPathGetValue(prefs, current);
1099 if (!set) {
1100 /* if error with path */
1101 SC_log(LOG_NOTICE, "%@ value (%@) not valid",
1102 kSCPrefCurrentSet,
1103 current);
1104 goto done;
1105 }
1106
1107 if (!isA_CFDictionary(set)) {
1108 SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
1109 current);
1110 goto done;
1111 }
1112
1113 /* flatten property list */
1114 flatten(prefs, CFSTR("/"), set);
1115
1116 CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current);
1117
1118 done :
1119
1120 /* add last updated time stamp */
1121 CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date);
1122
1123 /* add Setup: key */
1124 CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict);
1125
1126 /* compare current and new preferences */
1127 CFDictionaryApplyFunction(newPrefs, updateCache, NULL);
1128
1129 /* remove those keys which have not changed from the update */
1130 n = CFArrayGetCount(unchangedPrefsKeys);
1131 for (i = 0; i < n; i++) {
1132 CFStringRef key;
1133
1134 key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i);
1135 CFDictionaryRemoveValue(newPrefs, key);
1136 }
1137
1138 /* Update the dynamic store */
1139 #ifndef MAIN
1140 if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
1141 SC_log(LOG_NOTICE, "SCDynamicStoreSetMultiple() failed: %s", SCErrorString(SCError()));
1142 }
1143 #else // !MAIN
1144 SC_log(LOG_DEBUG, "SCDynamicStore\nset: %@\nremove: %@",
1145 newPrefs,
1146 removedPrefsKeys);
1147 #endif // !MAIN
1148
1149 CFRelease(currentPrefs);
1150 CFRelease(newPrefs);
1151 CFRelease(unchangedPrefsKeys);
1152 CFRelease(removedPrefsKeys);
1153 if (dict) CFRelease(dict);
1154 if (date) CFRelease(date);
1155 if (keys) CFRelease(keys);
1156 return;
1157 }
1158
1159
1160 static void
1161 updateConfiguration(SCPreferencesRef prefs,
1162 SCPreferencesNotification notificationType,
1163 void *info)
1164 {
1165 #pragma unused(info)
1166 #if !TARGET_OS_IPHONE
1167 if ((notificationType & kSCPreferencesNotificationCommit) == kSCPreferencesNotificationCommit) {
1168 SCNetworkSetRef current;
1169
1170 current = SCNetworkSetCopyCurrent(prefs);
1171 if (current != NULL) {
1172 /* network configuration available, disable template creation */
1173 haveConfiguration = TRUE;
1174 CFRelease(current);
1175 }
1176 }
1177 #endif /* !TARGET_OS_IPHONE */
1178
1179 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
1180 goto done;
1181 }
1182
1183 SC_log(LOG_INFO, "updating configuration");
1184
1185 /* add any [Apple] pre-configured network services */
1186 updatePreConfiguredConfiguration(prefs);
1187
1188 /* remove any excluded network services */
1189 excludeConfigurations(prefs);
1190
1191 /* update SCDynamicStore (Setup:) */
1192 updateSCDynamicStore(prefs);
1193
1194 /* finished with current prefs, wait for changes */
1195 if (!rofs) {
1196 SCPreferencesSynchronize(prefs);
1197 }
1198
1199 done :
1200
1201 return;
1202 }
1203
1204
1205 __private_extern__
1206 void
1207 prime_PreferencesMonitor()
1208 {
1209 SC_log(LOG_DEBUG, "prime() called");
1210
1211 /* load the initial configuration from the database */
1212 updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
1213
1214 return;
1215 }
1216
1217
1218 __private_extern__
1219 void
1220 load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
1221 {
1222 #pragma unused(bundle)
1223 #pragma unused(bundleVerbose)
1224 SC_log(LOG_DEBUG, "load() called");
1225 SC_log(LOG_DEBUG, " bundle ID = %@", CFBundleGetIdentifier(bundle));
1226
1227 /* open a SCDynamicStore session to allow cache updates */
1228 store = SCDynamicStoreCreate(NULL,
1229 CFSTR("PreferencesMonitor.bundle"),
1230 storeCallback,
1231 NULL);
1232 if (store == NULL) {
1233 SC_log(LOG_NOTICE, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError()));
1234 goto error;
1235 }
1236
1237 /* open a SCPreferences session */
1238 #ifndef MAIN
1239 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
1240 #else // !MAIN
1241 prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), CFSTR("/tmp/preferences.plist"));
1242 #endif // !MAIN
1243 if (prefs != NULL) {
1244 Boolean need_update = FALSE;
1245 CFStringRef new_model;
1246
1247 new_model = _SC_hw_model(FALSE);
1248
1249 /* Need to regenerate the new configuration for new model */
1250 if (new_model != NULL) {
1251 CFStringRef old_model;
1252
1253 old_model = SCPreferencesGetValue(prefs, MODEL);
1254 if (old_model != NULL && !_SC_CFEqual(old_model, new_model)) {
1255 // if new hardware
1256 need_update = TRUE;
1257 restorePrefs = previousConfigurationAvailable();
1258 }
1259 }
1260
1261 if (!need_update) {
1262 SCNetworkSetRef current;
1263
1264 current = SCNetworkSetCopyCurrent(prefs);
1265 if (current != NULL) {
1266 /* network configuration available, disable template creation */
1267 haveConfiguration = TRUE;
1268 CFRelease(current);
1269 }
1270 }
1271 } else {
1272 SC_log(LOG_NOTICE, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
1273 goto error;
1274 }
1275
1276 /*
1277 * register for change notifications.
1278 */
1279 if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
1280 SC_log(LOG_NOTICE, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
1281 goto error;
1282 }
1283
1284 if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
1285 SC_log(LOG_NOTICE, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
1286 goto error;
1287 }
1288
1289 /*
1290 * watch InterfaceNamer and KernelEventMonitor changes to know when
1291 * the IORegistry has quiesced (to create the initial configuration
1292 * template), to track any pre-configured interfaces, and to ensure
1293 * that we create a network service for any active interfaces.
1294 */
1295 watchSCDynamicStore();
1296 storeCallback(store, NULL, NULL);
1297
1298 return;
1299
1300 error :
1301
1302 if (store != NULL) CFRelease(store);
1303 if (prefs != NULL) CFRelease(prefs);
1304 haveConfiguration = TRUE;
1305
1306 return;
1307 }
1308
1309
1310 #ifdef MAIN
1311 int
1312 main(int argc, char **argv)
1313 {
1314 _sc_log = FALSE;
1315 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1316
1317 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
1318 prime_PreferencesMonitor();
1319 CFRunLoopRun();
1320 /* not reached */
1321 exit(0);
1322 return 0;
1323 }
1324 #endif