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