2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 15, 2003 Allan Nathanson <ajn@apple.com>
28 * - startup/shutdown AT networking without Kicker's help and
29 * publish the state information after the configuration is
32 * April 29, 2002 Allan Nathanson <ajn@apple.com>
33 * - add global state information (primary service, interface)
35 * June 24, 2001 Allan Nathanson <ajn@apple.com>
36 * - update to public SystemConfiguration.framework APIs
38 * July 7, 2000 Allan Nathanson <ajn@apple.com>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <sys/utsname.h>
51 #include <netat/appletalk.h>
52 #include <netat/at_var.h>
53 #include <AppleTalk/at_paths.h>
54 #include <AppleTalk/at_proto.h>
56 #include <CoreFoundation/CoreFoundation.h>
57 #include <SystemConfiguration/SystemConfiguration.h>
58 #include <SystemConfiguration/SCPrivate.h>
59 #include <SystemConfiguration/SCDPlugin.h>
60 #include <SystemConfiguration/SCValidation.h>
63 #include "cfManager.h"
65 #define HOSTCONFIG "/etc/hostconfig"
67 static SCDynamicStoreRef store
= NULL
;
68 static CFRunLoopSourceRef storeRls
= NULL
;
70 static int curState
= 0; // abs(state) == sequence #, < 0 == stop, > 0 == start
71 static CFMutableDictionaryRef curGlobals
= NULL
;
72 static CFMutableArrayRef curConfigFile
= NULL
;
73 static CFMutableDictionaryRef curDefaults
= NULL
;
74 static CFMutableDictionaryRef curStartup
= NULL
;
76 static Boolean _verbose
= FALSE
;
79 static void stopAppleTalk (CFRunLoopTimerRef timer
, void *info
);
80 static void startAppleTalk(CFRunLoopTimerRef timer
, void *info
);
84 updateDefaults(const void *key
, const void *val
, void *context
)
86 CFStringRef ifName
= (CFStringRef
)key
;
87 CFDictionaryRef oldDict
;
88 CFDictionaryRef newDict
= (CFDictionaryRef
)val
;
89 CFNumberRef defaultNode
;
90 CFNumberRef defaultNetwork
;
91 CFStringRef defaultZone
;
93 if (!CFDictionaryGetValueIfPresent(curDefaults
, ifName
, (const void **)&oldDict
) ||
94 !CFEqual(oldDict
, newDict
)) {
95 char ifr_name
[IFNAMSIZ
+1];
97 bzero(&ifr_name
, sizeof(ifr_name
));
98 if (!_SC_cfstring_to_cstring(ifName
, ifr_name
, sizeof(ifr_name
), kCFStringEncodingASCII
)) {
99 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert interface name to C string"));
104 * Set preferred Network and Node ID
106 if (CFDictionaryGetValueIfPresent(newDict
,
107 kSCPropNetAppleTalkNetworkID
,
108 (const void **)&defaultNetwork
) &&
109 CFDictionaryGetValueIfPresent(newDict
,
110 kSCPropNetAppleTalkNodeID
,
111 (const void **)&defaultNode
)
113 struct at_addr init_address
;
117 * set the default node and network
119 CFNumberGetValue(defaultNetwork
, kCFNumberShortType
, &init_address
.s_net
);
120 CFNumberGetValue(defaultNode
, kCFNumberCharType
, &init_address
.s_node
);
121 status
= at_setdefaultaddr(ifr_name
, &init_address
);
123 SCLog(TRUE
, LOG_ERR
, CFSTR("at_setdefaultaddr() failed"));
131 if (CFDictionaryGetValueIfPresent(newDict
,
132 kSCPropNetAppleTalkDefaultZone
,
133 (const void **)&defaultZone
)
139 * set the "default zone" for this interface
141 bzero(&zone
, sizeof(zone
));
142 if (!_SC_cfstring_to_cstring(defaultZone
,
145 kCFStringEncodingASCII
)) {
146 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert default zone to C string"));
150 zone
.len
= strlen((const char *)zone
.str
);
151 status
= at_setdefaultzone(ifr_name
, &zone
);
153 SCLog(TRUE
, LOG_ERR
, CFSTR("at_setdefaultzone() failed"));
164 addZoneToPorts(const void *key
, const void *val
, void *context
)
166 CFStringRef zone
= (CFStringRef
)key
;
167 CFArrayRef ifArray
= (CFArrayRef
)val
;
168 CFMutableArrayRef zones
= (CFMutableArrayRef
)context
;
170 CFStringRef configInfo
;
172 ifList
= CFStringCreateByCombiningStrings(NULL
, ifArray
, CFSTR(":"));
173 configInfo
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(":%@:%@"), zone
, ifList
);
174 CFArrayAppendValue(zones
, configInfo
);
175 CFRelease(configInfo
);
182 * Function: parse_component
184 * Given a string 'key' and a string prefix 'prefix',
185 * return the next component in the slash '/' separated
189 * 1. key = "a/b/c" prefix = "a/"
191 * 2. key = "a/b/c" prefix = "a/b/"
195 parse_component(CFStringRef key
, CFStringRef prefix
)
197 CFMutableStringRef comp
;
200 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
203 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
204 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
205 range
= CFStringFind(comp
, CFSTR("/"), 0);
206 if (range
.location
== kCFNotFound
) {
209 range
.length
= CFStringGetLength(comp
) - range
.location
;
210 CFStringDelete(comp
, range
);
215 static CFDictionaryRef
216 entity_one(SCDynamicStoreRef store
, CFStringRef key
)
218 CFDictionaryRef ent_dict
= NULL
;
219 CFDictionaryRef if_dict
= NULL
;
220 CFStringRef if_key
= NULL
;
222 CFMutableDictionaryRef new_dict
= NULL
;
223 static CFStringRef pre
= NULL
;
224 CFStringRef serviceID
= NULL
;
225 CFStringRef serviceType
;
228 pre
= SCDynamicStoreKeyCreate(NULL
,
230 kSCDynamicStoreDomainSetup
,
236 * get entity dictionary for service
238 ent_dict
= cache_SCDynamicStoreCopyValue(store
, key
);
239 if (!isA_CFDictionary(ent_dict
)) {
244 * get interface dictionary for service
246 serviceID
= parse_component(key
, pre
);
251 if_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
252 kSCDynamicStoreDomainSetup
,
255 if_dict
= cache_SCDynamicStoreCopyValue(store
, if_key
);
257 if (!isA_CFDictionary(if_dict
)) {
261 /* check the interface type */
262 serviceType
= CFDictionaryGetValue(if_dict
,
263 kSCPropNetInterfaceType
);
264 if (!isA_CFString(serviceType
) ||
265 !CFEqual(serviceType
, kSCValNetInterfaceTypeEthernet
)) {
266 /* sorry, no AT networking on this interface */
271 * get port name (from interface dictionary).
273 if_port
= CFDictionaryGetValue(if_dict
, kSCPropNetInterfaceDeviceName
);
274 if (!isA_CFString(if_port
)) {
279 * add ServiceID and interface port name to entity dictionary.
281 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, ent_dict
);
282 CFDictionarySetValue(new_dict
, CFSTR("ServiceID"), serviceID
);
283 CFDictionarySetValue(new_dict
, kSCPropNetInterfaceDeviceName
, if_port
);
287 if (ent_dict
) CFRelease(ent_dict
);
288 if (if_dict
) CFRelease(if_dict
);
289 if (serviceID
) CFRelease(serviceID
);
290 return (CFDictionaryRef
)new_dict
;
295 entity_all(SCDynamicStoreRef store
, CFStringRef entity
, CFArrayRef order
)
297 CFMutableArrayRef defined
= NULL
;
300 CFMutableArrayRef ordered
= NULL
;
303 ordered
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
305 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
306 kSCDynamicStoreDomainSetup
,
309 defined
= (CFMutableArrayRef
)SCDynamicStoreCopyKeyList(store
, pattern
);
311 if (defined
&& (CFArrayGetCount(defined
) > 0)) {
315 defined
= CFArrayCreateMutableCopy(NULL
, 0, tmp
);
321 n
= order
? CFArrayGetCount(order
) : 0;
322 for (i
= 0; i
< n
; i
++) {
323 CFDictionaryRef dict
;
328 service
= CFArrayGetValueAtIndex(order
, i
);
329 if (!isA_CFString(service
)) {
333 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
334 kSCDynamicStoreDomainSetup
,
337 dict
= entity_one(store
, key
);
339 CFArrayAppendValue(ordered
, dict
);
343 j
= CFArrayGetFirstIndexOfValue(defined
,
344 CFRangeMake(0, CFArrayGetCount(defined
)),
346 if (j
!= kCFNotFound
) {
347 CFArrayRemoveValueAtIndex(defined
, j
);
353 n
= CFArrayGetCount(defined
);
354 for (i
= 0; i
< n
; i
++) {
355 CFDictionaryRef dict
;
358 key
= CFArrayGetValueAtIndex(defined
, i
);
359 dict
= entity_one(store
, key
);
361 CFArrayAppendValue(ordered
, dict
);
368 if (defined
) CFRelease(defined
);
369 if (CFArrayGetCount(ordered
) == 0) {
378 encodeName(CFStringRef name
,
379 CFStringEncoding encoding
,
380 CFMutableDictionaryRef startup
,
381 CFMutableDictionaryRef globals
)
384 CFMutableStringRef encodedName
= NULL
;
387 if (!isA_CFString(name
)) {
391 if (encoding
== kCFStringEncodingASCII
) {
392 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
397 * encode the potentially non-printable string
399 bytes
= CFStringCreateExternalRepresentation(NULL
,
408 * check if the MacRoman string can be represented as ASCII
410 if (encoding
== kCFStringEncodingMacRoman
) {
413 ascii
= CFStringCreateExternalRepresentation(NULL
,
415 kCFStringEncodingASCII
,
420 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
425 encodedName
= CFStringCreateMutable(NULL
, 0);
427 len
= CFDataGetLength(bytes
);
428 byte
= (unsigned char *)CFDataGetBytePtr(bytes
);
429 for (i
= 0; i
< len
; i
++, byte
++) {
430 CFStringAppendFormat(encodedName
,
437 * add "encoded string" markers
439 CFStringInsert(encodedName
, 0, CFSTR("*"));
440 CFStringAppend(encodedName
, CFSTR("*"));
449 /* update "startup" dictionary */
450 CFDictionaryAddValue(startup
, CFSTR("APPLETALK_HOSTNAME"), encodedName
);
456 /* update "global" dictionary */
457 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &encoding
);
458 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerName
, name
);
459 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerNameEncoding
, num
);
463 CFRelease(encodedName
);
471 updateConfiguration(int *newState
)
473 boolean_t changed
= FALSE
;
474 CFStringRef computerName
;
475 CFStringEncoding computerNameEncoding
;
476 CFArrayRef configuredServices
= NULL
;
477 CFDictionaryRef dict
;
480 CFMutableArrayRef info
= NULL
;
481 CFArrayRef interfaces
= NULL
;
485 CFMutableArrayRef newConfigFile
;
486 CFMutableDictionaryRef newDefaults
;
487 CFMutableDictionaryRef newDict
;
488 CFMutableDictionaryRef newGlobals
;
489 CFMutableDictionaryRef newGlobalsX
; /* newGlobals without ServiceID */
490 CFMutableDictionaryRef newStartup
;
491 CFMutableDictionaryRef newZones
;
493 CFMutableDictionaryRef curGlobalsX
; /* curGlobals without ServiceID */
495 boolean_t postGlobals
= FALSE
;
496 CFStringRef primaryPort
= NULL
; /* primary interface */
497 CFStringRef primaryZone
= NULL
;
498 CFArrayRef serviceOrder
= NULL
;
499 CFDictionaryRef setGlobals
= NULL
;
504 * establish the "new" AppleTalk configuration
506 *newState
= curState
;
507 newConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
508 newGlobals
= CFDictionaryCreateMutable(NULL
,
510 &kCFTypeDictionaryKeyCallBacks
,
511 &kCFTypeDictionaryValueCallBacks
);
512 newDefaults
= CFDictionaryCreateMutable(NULL
,
514 &kCFTypeDictionaryKeyCallBacks
,
515 &kCFTypeDictionaryValueCallBacks
);
516 newStartup
= CFDictionaryCreateMutable(NULL
,
518 &kCFTypeDictionaryKeyCallBacks
,
519 &kCFTypeDictionaryValueCallBacks
);
520 newZones
= CFDictionaryCreateMutable(NULL
,
522 &kCFTypeDictionaryKeyCallBacks
,
523 &kCFTypeDictionaryValueCallBacks
);
525 /* initialize overall state */
526 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-NO-"));
529 * get the global settings (ServiceOrder, ComputerName, ...)
531 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
532 kSCDynamicStoreDomainSetup
,
534 setGlobals
= cache_SCDynamicStoreCopyValue(store
, key
);
537 if (isA_CFDictionary(setGlobals
)) {
538 /* get service order */
539 serviceOrder
= CFDictionaryGetValue(setGlobals
,
540 kSCPropNetServiceOrder
);
541 serviceOrder
= isA_CFArray(serviceOrder
);
543 CFRetain(serviceOrder
);
546 CFRelease(setGlobals
);
552 * if we don't have an AppleTalk ServiceOrder, use IPv4's (if defined)
555 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
556 kSCDynamicStoreDomainSetup
,
558 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
561 if (isA_CFDictionary(dict
)) {
562 serviceOrder
= CFDictionaryGetValue(dict
,
563 kSCPropNetServiceOrder
);
564 serviceOrder
= isA_CFArray(serviceOrder
);
566 CFRetain(serviceOrder
);
574 * get the list of ALL configured services
576 configuredServices
= entity_all(store
, kSCEntNetAppleTalk
, serviceOrder
);
577 if (configuredServices
) {
578 ifCount
= CFArrayGetCount(configuredServices
);
581 if (serviceOrder
) CFRelease(serviceOrder
);
584 * get the list of ALL active interfaces
586 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
587 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
590 if (isA_CFDictionary(dict
)) {
591 interfaces
= CFDictionaryGetValue(dict
,
592 kSCDynamicStorePropNetInterfaces
);
593 interfaces
= isA_CFArray(interfaces
);
595 CFRetain(interfaces
);
602 * get the list of previously configured services
604 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
605 kSCDynamicStoreDomainState
,
608 keys
= SCDynamicStoreCopyKeyList(store
, pattern
);
611 info
= CFArrayCreateMutableCopy(NULL
, 0, keys
);
616 * iterate over each configured service to establish the new
619 for (i
= 0; i
< ifCount
; i
++) {
620 CFDictionaryRef service
;
622 CFStringRef configMethod
;
623 CFMutableStringRef portConfig
= NULL
;
624 CFArrayRef networkRange
; /* for seed ports, CFArray[2] of CFNumber (lo, hi) */
627 CFArrayRef zoneList
; /* for seed ports, CFArray[] of CFString (zones names) */
630 CFMutableDictionaryRef ifDefaults
= NULL
;
631 CFNumberRef defaultNetwork
;
632 CFNumberRef defaultNode
;
633 CFStringRef defaultZone
;
635 /* get AppleTalk service dictionary */
636 service
= CFArrayGetValueAtIndex(configuredServices
, i
);
638 /* get interface name */
639 ifName
= CFDictionaryGetValue(service
, kSCPropNetInterfaceDeviceName
);
641 /* check inteface availability */
643 !CFArrayContainsValue(interfaces
, CFRangeMake(0, CFArrayGetCount(interfaces
)), ifName
)) {
644 /* if interface not available */
648 /* check interface link status */
649 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
650 kSCDynamicStoreDomainState
,
653 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
656 Boolean linkStatus
= TRUE
; /* assume the link is "up" */
657 Boolean ifDetaching
= FALSE
; /* assume link is not detaching */
659 /* the link key for this interface is available */
660 if (isA_CFDictionary(dict
)) {
663 bVal
= CFDictionaryGetValue(dict
, kSCPropNetLinkActive
);
664 if (isA_CFBoolean(bVal
)) {
665 linkStatus
= CFBooleanGetValue(bVal
);
668 /* check if interface is detaching - value
669 doesn't really matter, only that it exists */
670 ifDetaching
= CFDictionaryContainsKey(dict
, kSCPropNetLinkDetaching
);
674 if (!linkStatus
|| ifDetaching
) {
675 /* if link status down or the interface is detaching */
681 * Determine configuration method for this service
683 configMethod
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkConfigMethod
);
684 if (!isA_CFString(configMethod
)) {
685 /* if no ConfigMethod */
689 if (!CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodNode
) &&
690 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) &&
691 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
692 /* if not one of the expected values, disable */
693 SCLog(TRUE
, LOG_NOTICE
,
694 CFSTR("Unexpected AppleTalk ConfigMethod: %@"),
700 * the first service to be defined will always be "primary"
702 if (CFArrayGetCount(newConfigFile
) == 0) {
703 CFDictionaryRef active
;
705 CFDictionarySetValue(newGlobals
,
706 kSCDynamicStorePropNetPrimaryService
,
707 CFDictionaryGetValue(service
, CFSTR("ServiceID")));
708 CFDictionarySetValue(newGlobals
,
709 kSCDynamicStorePropNetPrimaryInterface
,
712 /* and check if AT newtorking is active on the primary interface */
713 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
714 kSCDynamicStoreDomainState
,
717 active
= cache_SCDynamicStoreCopyValue(store
, key
);
720 if (isA_CFDictionary(active
)) {
730 portConfig
= CFStringCreateMutable(NULL
, 0);
731 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%@:"), ifName
);
733 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
737 * we have been asked to configure this interface as a
738 * seed port. Ensure that we have been provided at least
739 * one network number, have been provided with at least
743 networkRange
= CFDictionaryGetValue(service
,
744 kSCPropNetAppleTalkSeedNetworkRange
);
745 if (!isA_CFArray(networkRange
) || (CFArrayGetCount(networkRange
) == 0)) {
746 SCLog(TRUE
, LOG_NOTICE
,
747 CFSTR("AppleTalk configuration error (%@)"),
748 kSCPropNetAppleTalkSeedNetworkRange
);
753 * establish the starting and ending network numbers
755 num
= CFArrayGetValueAtIndex(networkRange
, 0);
756 if (!isA_CFNumber(num
)) {
757 SCLog(TRUE
, LOG_NOTICE
,
758 CFSTR("AppleTalk configuration error (%@)"),
759 kSCPropNetAppleTalkSeedNetworkRange
);
762 CFNumberGetValue(num
, kCFNumberIntType
, &sNetwork
);
765 if (CFArrayGetCount(networkRange
) > 1) {
766 num
= CFArrayGetValueAtIndex(networkRange
, 1);
767 if (!isA_CFNumber(num
)) {
768 SCLog(TRUE
, LOG_NOTICE
,
769 CFSTR("AppleTalk configuration error (%@)"),
770 kSCPropNetAppleTalkSeedNetworkRange
);
773 CFNumberGetValue(num
, kCFNumberIntType
, &eNetwork
);
775 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%d:%d:"), sNetwork
, eNetwork
);
778 * establish the zones associated with this port
780 zoneList
= CFDictionaryGetValue(service
,
781 kSCPropNetAppleTalkSeedZones
);
782 if (!isA_CFArray(zoneList
)) {
783 SCLog(TRUE
, LOG_NOTICE
,
784 CFSTR("AppleTalk configuration error (%@)"),
785 kSCPropNetAppleTalkSeedZones
);
789 zCount
= CFArrayGetCount(zoneList
);
790 for (j
= 0; j
< zCount
; j
++) {
793 CFMutableArrayRef newIFList
;
795 zone
= CFArrayGetValueAtIndex(zoneList
, j
);
796 if (!isA_CFString(zone
)) {
800 if (CFDictionaryGetValueIfPresent(newZones
, zone
, (const void **)&ifList
)) {
802 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
805 newIFList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
807 CFArrayAppendValue(newIFList
, ifName
);
808 CFArraySortValues(newIFList
,
809 CFRangeMake(0, CFArrayGetCount(newIFList
)),
810 (CFComparatorFunction
)CFStringCompare
,
812 CFDictionarySetValue(newZones
, zone
, newIFList
);
813 CFRelease(newIFList
);
816 * flag the default zone
819 primaryZone
= CFRetain(zone
);
823 SCLog(TRUE
, LOG_NOTICE
,
824 CFSTR("AppleTalk configuration error (%@)"),
825 kSCPropNetAppleTalkSeedZones
);
830 /* get the (per-interface) "Computer Name" */
831 computerName
= CFDictionaryGetValue(service
,
832 kSCPropNetAppleTalkComputerName
);
833 if (CFDictionaryGetValueIfPresent(service
,
834 kSCPropNetAppleTalkComputerNameEncoding
,
835 (const void **)&num
) &&
837 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
839 computerNameEncoding
= CFStringGetSystemEncoding();
841 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
844 * declare the first configured AppleTalk service / interface
845 * as the "home port".
847 if (CFArrayGetCount(newConfigFile
) == 0) {
848 CFStringAppend(portConfig
, CFSTR("*"));
849 primaryPort
= CFRetain(ifName
);
851 CFArrayAppendValue(newConfigFile
, portConfig
);
854 * get the per-interface defaults
856 ifDefaults
= CFDictionaryCreateMutable(NULL
,
858 &kCFTypeDictionaryKeyCallBacks
,
859 &kCFTypeDictionaryValueCallBacks
);
861 defaultNetwork
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNetworkID
);
862 defaultNode
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNodeID
);
863 if (isA_CFNumber(defaultNetwork
) && isA_CFNumber(defaultNode
)) {
865 * set the default node and network
867 CFDictionarySetValue(ifDefaults
,
868 kSCPropNetAppleTalkNetworkID
,
870 CFDictionarySetValue(ifDefaults
,
871 kSCPropNetAppleTalkNodeID
,
875 if ((CFDictionaryGetValueIfPresent(service
,
876 kSCPropNetAppleTalkDefaultZone
,
877 (const void **)&defaultZone
) == TRUE
)) {
879 * set the default zone for this interface
881 CFDictionarySetValue(ifDefaults
,
882 kSCPropNetAppleTalkDefaultZone
,
886 CFDictionarySetValue(newDefaults
, ifName
, ifDefaults
);
887 CFRelease(ifDefaults
);
889 switch (CFArrayGetCount(newConfigFile
)) {
892 * first AppleTalk interface
894 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), ifName
);
897 /* second AppleTalk interface */
898 if (!CFEqual(CFDictionaryGetValue(newStartup
, CFSTR("APPLETALK")),
899 CFSTR("-ROUTER-"))) {
901 * if not routing (yet), configure as multi-home
903 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-MULTIHOME-"));
908 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) ||
909 CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
910 /* if not a simple node, enable routing */
911 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-ROUTER-"));
915 * establish the State:/Network/Service/nnn/AppleTalk key info
917 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
918 kSCDynamicStoreDomainState
,
919 CFDictionaryGetValue(service
, CFSTR("ServiceID")),
921 newDict
= CFDictionaryCreateMutable(NULL
,
923 &kCFTypeDictionaryKeyCallBacks
,
924 &kCFTypeDictionaryValueCallBacks
);
925 CFDictionaryAddValue(newDict
, kSCPropInterfaceName
, ifName
);
926 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
929 j
= CFArrayGetFirstIndexOfValue(info
,
930 CFRangeMake(0, CFArrayGetCount(info
)),
932 if (j
!= kCFNotFound
) {
933 CFArrayRemoveValueAtIndex(info
, j
);
940 if (portConfig
) CFRelease(portConfig
);
945 CFMutableArrayRef newIFList
;
947 ifList
= CFDictionaryGetValue(newZones
, primaryZone
);
948 if (CFArrayContainsValue(ifList
,
949 CFRangeMake(0, CFArrayGetCount(ifList
)),
951 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
952 CFArrayAppendValue(newIFList
, CFSTR("*"));
953 CFDictionarySetValue(newZones
, primaryZone
, newIFList
);
954 CFRelease(newIFList
);
956 CFRelease(primaryZone
);
959 CFRelease(primaryPort
);
963 i
= CFArrayGetCount(newConfigFile
);
964 CFArraySortValues(newConfigFile
,
966 (CFComparatorFunction
)CFStringCompare
,
969 /* add the zones to the configuration */
970 CFDictionaryApplyFunction(newZones
, addZoneToPorts
, newConfigFile
);
974 CFArraySortValues(newConfigFile
,
975 CFRangeMake(i
, CFArrayGetCount(newConfigFile
)-i
),
976 (CFComparatorFunction
)CFStringCompare
,
979 /* ensure that the last line of the configuration file is terminated */
980 CFArrayAppendValue(newConfigFile
, CFSTR(""));
983 * Check if we have a "ComputerName" and look elsewhere if we don't have
986 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME")) &&
987 (setGlobals
!= NULL
)) {
988 computerName
= CFDictionaryGetValue(setGlobals
,
989 kSCPropNetAppleTalkComputerName
);
990 if (CFDictionaryGetValueIfPresent(setGlobals
,
991 kSCPropNetAppleTalkComputerNameEncoding
,
992 (const void **)&num
) &&
994 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
996 computerNameEncoding
= CFStringGetSystemEncoding();
998 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
1000 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
1001 computerName
= SCDynamicStoreCopyComputerName(store
, &computerNameEncoding
);
1003 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
1004 CFRelease(computerName
);
1007 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
1008 struct utsname name
;
1010 if (uname(&name
) == 0) {
1011 computerName
= CFStringCreateWithCString(NULL
, name
.nodename
, kCFStringEncodingASCII
);
1013 encodeName(computerName
, kCFStringEncodingASCII
, NULL
, newGlobals
);
1014 CFRelease(computerName
);
1019 /* compare the previous and current configurations */
1021 curGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, curGlobals
);
1022 CFDictionaryRemoveValue(curGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1024 newGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, newGlobals
);
1025 CFDictionaryRemoveValue(newGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1027 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1028 kSCDynamicStoreDomainState
,
1029 kSCEntNetAppleTalk
);
1031 if (CFEqual(curGlobalsX
, newGlobalsX
) &&
1032 CFEqual(curConfigFile
, newConfigFile
) &&
1033 CFEqual(curDefaults
, newDefaults
) &&
1034 CFEqual(curStartup
, newStartup
)
1037 * the configuration has not changed.
1042 * the requested configuration hasn't changed but we
1043 * now need to tell everyone that AppleTalk is active.
1045 if (!SCDynamicStoreSetValue(store
, key
, newGlobals
)) {
1048 CFSTR("SCDynamicStoreSetValue() failed: %s"),
1049 SCErrorString(SCError()));
1053 CFRelease(newGlobals
);
1054 CFRelease(newConfigFile
);
1055 CFRelease(newDefaults
);
1056 CFRelease(newStartup
);
1057 } else if (CFArrayGetCount(newConfigFile
) <= 1) {
1059 * the configuration has changed but there are no
1060 * longer any interfaces configured for AppleTalk
1065 * remove the global (State:/Network/Global/AppleTalk) key.
1067 * Note: it will be restored later after AT networking has
1071 /* remove the (/etc/appletalk.cfg) configuration file */
1072 (void)unlink(AT_CFG_FILE
);
1075 * update the per-service (and global) state
1077 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1078 n
= CFArrayGetCount(info
);
1079 for (i
= 0; i
< n
; i
++) {
1080 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1082 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1086 /* flag this as a new configuration */
1087 *newState
= -(abs(curState
) + 1);
1091 * the configuration has changed.
1094 /* update the (/etc/appletalk.cfg) configuration file */
1095 configWrite(AT_CFG_FILE
, newConfigFile
);
1098 * update the per-service (and global) state
1100 * Note: if present, we remove any existing global state key and allow it
1101 * to be restored after the stack has been re-started.
1103 CFDictionaryApplyFunction(newDefaults
, updateDefaults
, NULL
);
1104 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1105 n
= CFArrayGetCount(info
);
1106 for (i
= 0; i
< n
; i
++) {
1107 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1109 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1113 /* flag this as a new configuration */
1114 *newState
= abs(curState
) + 1;
1118 CFRelease(curGlobalsX
);
1119 CFRelease(newGlobalsX
);
1123 CFRelease(curGlobals
);
1124 curGlobals
= newGlobals
;
1125 CFRelease(curConfigFile
);
1126 curConfigFile
= newConfigFile
;
1127 CFRelease(curDefaults
);
1128 curDefaults
= newDefaults
;
1129 CFRelease(curStartup
);
1130 curStartup
= newStartup
;
1133 if (info
) CFRelease(info
);
1134 if (interfaces
) CFRelease(interfaces
);
1135 if (configuredServices
) CFRelease(configuredServices
);
1136 if (setGlobals
) CFRelease(setGlobals
);
1144 #include <sysexits.h>
1145 #define AT_CMD_SUCCESS EX_OK /* success */
1146 #define AT_CMD_ALREADY_RUNNING EX__MAX + 10
1147 #define AT_CMD_NOT_RUNNING EX__MAX + 11
1157 sock
= socket(AF_APPLETALK
, SOCK_RAW
, 0);
1158 ret
= ioctl(sock
, AIOCGETSTATE
, (caddr_t
)&state
);
1161 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(AIOCGETSTATE) failed: %s"), strerror(errno
));
1165 if (state
.flags
& AT_ST_STARTED
) {
1166 return abs(curState
);
1168 return -(abs(curState
));
1173 static pid_t execCommand
= 0;
1174 static int execRetry
;
1178 stopComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1182 if (WIFEXITED(status
)) {
1183 switch (WEXITSTATUS(status
)) {
1184 case AT_CMD_SUCCESS
:
1185 case AT_CMD_NOT_RUNNING
:
1186 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown complete"));
1188 // the stack is down but we really want it up
1189 startAppleTalk(NULL
, (void *)curState
);
1197 SCLog(TRUE
, LOG_ERR
,
1198 CFSTR("AppleTalk shutdown failed, status = %d%s"),
1199 WEXITSTATUS(status
),
1200 (execRetry
> 1) ? " (retrying)" : "");
1202 // shutdown failed, retry
1203 if (--execRetry
> 0) {
1204 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1205 CFRunLoopTimerRef timer
;
1207 timer
= CFRunLoopTimerCreate(NULL
,
1208 CFAbsoluteTimeGetCurrent() + 1.0,
1214 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1218 // we weren't able to stop
1219 curState
= stackState();
1227 stopAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1229 char *argv
[] = { "appletalk",
1233 if (execCommand
== 0) {
1234 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown"));
1236 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown skipped, transition in progress"));
1240 execCommand
= _SCDPluginExecCommand(stopComplete
, // callback
1244 "/usr/sbin/appletalk", // path
1248 execRetry
= 5; // initialize retry count
1256 startComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1260 if (WIFEXITED(status
)) {
1261 switch (WEXITSTATUS(status
)) {
1262 case AT_CMD_SUCCESS
:
1263 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup complete"));
1264 if ((curState
< 0) || (curState
> (int)context
)) {
1265 // the stack is now up but we really want it down
1266 stopAppleTalk(NULL
, (void *)curState
);
1269 case AT_CMD_ALREADY_RUNNING
:
1270 // the stack is already up but we're not sure
1271 // if the configuration is correct, restart
1272 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk already running, restarting"));
1273 stopAppleTalk(NULL
, (void *)curState
);
1280 SCLog(TRUE
, LOG_ERR
,
1281 CFSTR("AppleTalk startup failed, status = %d%s"),
1282 WEXITSTATUS(status
),
1283 (execRetry
> 1) ? " (retrying)" : "");
1285 // startup failed, retry
1286 if (--execRetry
> 0) {
1287 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1288 CFRunLoopTimerRef timer
;
1290 timer
= CFRunLoopTimerCreate(NULL
,
1291 CFAbsoluteTimeGetCurrent() + 1.0,
1297 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1301 // we weren't able to start
1302 curState
= stackState();
1310 startAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1314 char *computerName
= NULL
;
1315 char *interface
= NULL
;
1316 CFStringRef mode
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK"));
1317 CFStringRef name
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK_HOSTNAME"));
1319 if (execCommand
== 0) {
1320 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup"));
1322 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup skipped, transition in progress"));
1332 argv
[argc
++] = "appletalk";
1336 computerName
= _SC_cfstring_to_cstring(name
, NULL
, 0, kCFStringEncodingASCII
);
1338 argv
[argc
++] = "-C";
1339 argv
[argc
++] = computerName
;
1341 // could not convert name
1347 if (CFEqual(mode
, CFSTR("-ROUTER-"))) {
1348 argv
[argc
++] = "-r";
1349 } else if (CFEqual(mode
, CFSTR("-MULTIHOME-"))) {
1350 argv
[argc
++] = "-x";
1352 interface
= _SC_cfstring_to_cstring(mode
, NULL
, 0, kCFStringEncodingASCII
);
1354 argv
[argc
++] = "-u";
1355 argv
[argc
++] = interface
;
1357 // could not convert interface
1362 // set non-interactive
1363 argv
[argc
++] = "-q";
1365 // close argument list
1366 argv
[argc
++] = NULL
;
1368 execCommand
= _SCDPluginExecCommand(startComplete
, // callback
1372 "/usr/sbin/appletalk", // path
1376 execRetry
= 5; // initialize retry count
1381 if (computerName
) CFAllocatorDeallocate(NULL
, computerName
);
1382 if (interface
) CFAllocatorDeallocate(NULL
, interface
);
1389 atConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
1391 boolean_t configChanged
;
1394 configChanged
= updateConfiguration(&newState
);
1396 if (configChanged
&& (execCommand
== 0)) {
1397 // if the configuration has changed and we're not already transitioning
1400 // already running, restart [with new configuration]
1401 stopAppleTalk(NULL
, (void *)newState
);
1403 startAppleTalk(NULL
, (void *)newState
);
1407 stopAppleTalk(NULL
, (void *)newState
);
1412 curState
= newState
;
1419 stop_ATconfig(CFRunLoopSourceRef stopRls
)
1423 if (storeRls
!= NULL
) {
1424 CFRunLoopSourceInvalidate(storeRls
);
1425 CFRelease(storeRls
);
1429 if (store
!= NULL
) {
1432 CFRelease(curGlobals
);
1433 CFRelease(curConfigFile
);
1434 CFRelease(curDefaults
);
1435 CFRelease(curStartup
);
1438 CFRunLoopSourceSignal(stopRls
);
1444 load_ATconfig(CFBundleRef bundle
, Boolean bundleVerbose
)
1447 CFMutableArrayRef keys
= NULL
;
1448 CFStringRef pattern
;
1449 CFMutableArrayRef patterns
= NULL
;
1451 if (bundleVerbose
) {
1455 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
1456 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
1458 /* initialize a few globals */
1460 curGlobals
= CFDictionaryCreateMutable(NULL
,
1462 &kCFTypeDictionaryKeyCallBacks
,
1463 &kCFTypeDictionaryValueCallBacks
);
1464 curConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1465 curDefaults
= CFDictionaryCreateMutable(NULL
,
1467 &kCFTypeDictionaryKeyCallBacks
,
1468 &kCFTypeDictionaryValueCallBacks
);
1469 curStartup
= CFDictionaryCreateMutable(NULL
,
1471 &kCFTypeDictionaryKeyCallBacks
,
1472 &kCFTypeDictionaryValueCallBacks
);
1474 /* open a "configd" store to allow cache updates */
1475 store
= SCDynamicStoreCreate(NULL
,
1476 CFSTR("AppleTalk Configuraton plug-in"),
1477 atConfigChangedCallback
,
1480 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
1484 /* establish notification keys and patterns */
1486 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1487 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1489 /* ...watch for (global) AppleTalk configuration changes */
1490 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1491 kSCDynamicStoreDomainSetup
,
1492 kSCEntNetAppleTalk
);
1493 CFArrayAppendValue(keys
, key
);
1496 /* ...watch for (per-service) AppleTalk configuration changes */
1497 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1498 kSCDynamicStoreDomainSetup
,
1500 kSCEntNetAppleTalk
);
1501 CFArrayAppendValue(patterns
, pattern
);
1504 /* ...watch for network interface link status changes */
1505 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1506 kSCDynamicStoreDomainState
,
1509 CFArrayAppendValue(patterns
, pattern
);
1512 /* ...watch for (per-interface) AppleTalk configuration changes */
1513 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1514 kSCDynamicStoreDomainState
,
1516 kSCEntNetAppleTalk
);
1517 CFArrayAppendValue(patterns
, pattern
);
1520 /* ...watch for computer name changes */
1521 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
1522 CFArrayAppendValue(keys
, key
);
1525 /* register the keys/patterns */
1526 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
1527 SCLog(TRUE
, LOG_ERR
,
1528 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
1529 SCErrorString(SCError()));
1533 CFRelease(patterns
);
1535 storeRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
1537 SCLog(TRUE
, LOG_ERR
,
1538 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
1539 SCErrorString(SCError()));
1542 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRls
, kCFRunLoopDefaultMode
);
1548 if (curGlobals
) CFRelease(curGlobals
);
1549 if (curConfigFile
) CFRelease(curConfigFile
);
1550 if (curDefaults
) CFRelease(curDefaults
);
1551 if (curStartup
) CFRelease(curStartup
);
1552 if (store
) CFRelease(store
);
1553 if (keys
) CFRelease(keys
);
1554 if (patterns
) CFRelease(patterns
);
1560 #include "cfManager.c"
1562 main(int argc
, char **argv
)
1565 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1567 load_ATconfig(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);