2 * Copyright (c) 2000-2003 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
, zone
.str
, sizeof(zone
.str
), kCFStringEncodingASCII
)) {
143 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert default zone to C string"));
147 zone
.len
= strlen(zone
.str
);
148 status
= at_setdefaultzone(ifr_name
, &zone
);
150 SCLog(TRUE
, LOG_ERR
, CFSTR("at_setdefaultzone() failed"));
161 addZoneToPorts(const void *key
, const void *val
, void *context
)
163 CFStringRef zone
= (CFStringRef
)key
;
164 CFArrayRef ifArray
= (CFArrayRef
)val
;
165 CFMutableArrayRef zones
= (CFMutableArrayRef
)context
;
167 CFStringRef configInfo
;
169 ifList
= CFStringCreateByCombiningStrings(NULL
, ifArray
, CFSTR(":"));
170 configInfo
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(":%@:%@"), zone
, ifList
);
171 CFArrayAppendValue(zones
, configInfo
);
172 CFRelease(configInfo
);
179 * Function: parse_component
181 * Given a string 'key' and a string prefix 'prefix',
182 * return the next component in the slash '/' separated
186 * 1. key = "a/b/c" prefix = "a/"
188 * 2. key = "a/b/c" prefix = "a/b/"
192 parse_component(CFStringRef key
, CFStringRef prefix
)
194 CFMutableStringRef comp
;
197 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
200 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
201 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
202 range
= CFStringFind(comp
, CFSTR("/"), 0);
203 if (range
.location
== kCFNotFound
) {
206 range
.length
= CFStringGetLength(comp
) - range
.location
;
207 CFStringDelete(comp
, range
);
212 static CFDictionaryRef
213 entity_one(SCDynamicStoreRef store
, CFStringRef key
)
215 CFDictionaryRef ent_dict
= NULL
;
216 CFDictionaryRef if_dict
= NULL
;
217 CFStringRef if_key
= NULL
;
219 CFMutableDictionaryRef new_dict
= NULL
;
220 static CFStringRef pre
= NULL
;
221 CFStringRef serviceID
= NULL
;
222 CFStringRef serviceType
;
225 pre
= SCDynamicStoreKeyCreate(NULL
,
227 kSCDynamicStoreDomainSetup
,
233 * get entity dictionary for service
235 ent_dict
= cache_SCDynamicStoreCopyValue(store
, key
);
236 if (!isA_CFDictionary(ent_dict
)) {
241 * get interface dictionary for service
243 serviceID
= parse_component(key
, pre
);
248 if_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
249 kSCDynamicStoreDomainSetup
,
252 if_dict
= cache_SCDynamicStoreCopyValue(store
, if_key
);
254 if (!isA_CFDictionary(if_dict
)) {
258 /* check the interface type */
259 serviceType
= CFDictionaryGetValue(if_dict
,
260 kSCPropNetInterfaceType
);
261 if (!isA_CFString(serviceType
) ||
262 !CFEqual(serviceType
, kSCValNetInterfaceTypeEthernet
)) {
263 /* sorry, no AT networking on this interface */
268 * get port name (from interface dictionary).
270 if_port
= CFDictionaryGetValue(if_dict
, kSCPropNetInterfaceDeviceName
);
271 if (!isA_CFString(if_port
)) {
276 * add ServiceID and interface port name to entity dictionary.
278 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, ent_dict
);
279 CFDictionarySetValue(new_dict
, CFSTR("ServiceID"), serviceID
);
280 CFDictionarySetValue(new_dict
, kSCPropNetInterfaceDeviceName
, if_port
);
284 if (ent_dict
) CFRelease(ent_dict
);
285 if (if_dict
) CFRelease(if_dict
);
286 if (serviceID
) CFRelease(serviceID
);
287 return (CFDictionaryRef
)new_dict
;
292 entity_all(SCDynamicStoreRef store
, CFStringRef entity
, CFArrayRef order
)
294 CFMutableArrayRef defined
= NULL
;
297 CFMutableArrayRef ordered
= NULL
;
300 ordered
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
302 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
303 kSCDynamicStoreDomainSetup
,
306 defined
= (CFMutableArrayRef
)SCDynamicStoreCopyKeyList(store
, pattern
);
308 if (defined
&& (CFArrayGetCount(defined
) > 0)) {
312 defined
= CFArrayCreateMutableCopy(NULL
, 0, tmp
);
318 n
= order
? CFArrayGetCount(order
) : 0;
319 for (i
= 0; i
< n
; i
++) {
320 CFDictionaryRef dict
;
325 service
= CFArrayGetValueAtIndex(order
, i
);
326 if (!isA_CFString(service
)) {
330 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
331 kSCDynamicStoreDomainSetup
,
334 dict
= entity_one(store
, key
);
336 CFArrayAppendValue(ordered
, dict
);
340 j
= CFArrayGetFirstIndexOfValue(defined
,
341 CFRangeMake(0, CFArrayGetCount(defined
)),
343 if (j
!= kCFNotFound
) {
344 CFArrayRemoveValueAtIndex(defined
, j
);
350 n
= CFArrayGetCount(defined
);
351 for (i
= 0; i
< n
; i
++) {
352 CFDictionaryRef dict
;
355 key
= CFArrayGetValueAtIndex(defined
, i
);
356 dict
= entity_one(store
, key
);
358 CFArrayAppendValue(ordered
, dict
);
365 if (defined
) CFRelease(defined
);
366 if (CFArrayGetCount(ordered
) == 0) {
375 encodeName(CFStringRef name
,
376 CFStringEncoding encoding
,
377 CFMutableDictionaryRef startup
,
378 CFMutableDictionaryRef globals
)
381 CFMutableStringRef encodedName
= NULL
;
384 if (!isA_CFString(name
)) {
388 if (encoding
== kCFStringEncodingASCII
) {
389 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
394 * encode the potentially non-printable string
396 bytes
= CFStringCreateExternalRepresentation(NULL
,
405 * check if the MacRoman string can be represented as ASCII
407 if (encoding
== kCFStringEncodingMacRoman
) {
410 ascii
= CFStringCreateExternalRepresentation(NULL
,
412 kCFStringEncodingASCII
,
417 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
422 encodedName
= CFStringCreateMutable(NULL
, 0);
424 len
= CFDataGetLength(bytes
);
425 byte
= (unsigned char *)CFDataGetBytePtr(bytes
);
426 for (i
= 0; i
< len
; i
++, byte
++) {
427 CFStringAppendFormat(encodedName
,
434 * add "encoded string" markers
436 CFStringInsert(encodedName
, 0, CFSTR("*"));
437 CFStringAppend(encodedName
, CFSTR("*"));
446 /* update "startup" dictionary */
447 CFDictionaryAddValue(startup
, CFSTR("APPLETALK_HOSTNAME"), encodedName
);
453 /* update "global" dictionary */
454 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &encoding
);
455 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerName
, name
);
456 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerNameEncoding
, num
);
460 CFRelease(encodedName
);
468 updateConfiguration(int *newState
)
470 boolean_t changed
= FALSE
;
471 CFStringRef computerName
;
472 CFStringEncoding computerNameEncoding
;
473 CFArrayRef configuredServices
= NULL
;
474 CFDictionaryRef dict
;
477 CFMutableArrayRef info
= NULL
;
478 CFArrayRef interfaces
= NULL
;
482 CFMutableArrayRef newConfigFile
;
483 CFMutableDictionaryRef newDefaults
;
484 CFMutableDictionaryRef newDict
;
485 CFMutableDictionaryRef newGlobals
;
486 CFMutableDictionaryRef newGlobalsX
; /* newGlobals without ServiceID */
487 CFMutableDictionaryRef newStartup
;
488 CFMutableDictionaryRef newZones
;
490 CFMutableDictionaryRef curGlobalsX
; /* curGlobals without ServiceID */
492 boolean_t postGlobals
= FALSE
;
493 CFStringRef primaryPort
= NULL
; /* primary interface */
494 CFStringRef primaryZone
= NULL
;
495 CFArrayRef serviceOrder
= NULL
;
496 CFDictionaryRef setGlobals
= NULL
;
501 * establish the "new" AppleTalk configuration
503 *newState
= curState
;
504 newConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
505 newGlobals
= CFDictionaryCreateMutable(NULL
,
507 &kCFTypeDictionaryKeyCallBacks
,
508 &kCFTypeDictionaryValueCallBacks
);
509 newDefaults
= CFDictionaryCreateMutable(NULL
,
511 &kCFTypeDictionaryKeyCallBacks
,
512 &kCFTypeDictionaryValueCallBacks
);
513 newStartup
= CFDictionaryCreateMutable(NULL
,
515 &kCFTypeDictionaryKeyCallBacks
,
516 &kCFTypeDictionaryValueCallBacks
);
517 newZones
= CFDictionaryCreateMutable(NULL
,
519 &kCFTypeDictionaryKeyCallBacks
,
520 &kCFTypeDictionaryValueCallBacks
);
522 /* initialize overall state */
523 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-NO-"));
526 * get the global settings (ServiceOrder, ComputerName, ...)
528 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
529 kSCDynamicStoreDomainSetup
,
531 setGlobals
= cache_SCDynamicStoreCopyValue(store
, key
);
534 if (isA_CFDictionary(setGlobals
)) {
535 /* get service order */
536 serviceOrder
= CFDictionaryGetValue(setGlobals
,
537 kSCPropNetServiceOrder
);
538 serviceOrder
= isA_CFArray(serviceOrder
);
540 CFRetain(serviceOrder
);
543 CFRelease(setGlobals
);
549 * if we don't have an AppleTalk ServiceOrder, use IPv4's (if defined)
552 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
553 kSCDynamicStoreDomainSetup
,
555 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
558 if (isA_CFDictionary(dict
)) {
559 serviceOrder
= CFDictionaryGetValue(dict
,
560 kSCPropNetServiceOrder
);
561 serviceOrder
= isA_CFArray(serviceOrder
);
563 CFRetain(serviceOrder
);
571 * get the list of ALL configured services
573 configuredServices
= entity_all(store
, kSCEntNetAppleTalk
, serviceOrder
);
574 if (configuredServices
) {
575 ifCount
= CFArrayGetCount(configuredServices
);
578 if (serviceOrder
) CFRelease(serviceOrder
);
581 * get the list of ALL active interfaces
583 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
584 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
587 if (isA_CFDictionary(dict
)) {
588 interfaces
= CFDictionaryGetValue(dict
,
589 kSCDynamicStorePropNetInterfaces
);
590 interfaces
= isA_CFArray(interfaces
);
592 CFRetain(interfaces
);
599 * get the list of previously configured services
601 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
602 kSCDynamicStoreDomainState
,
605 keys
= SCDynamicStoreCopyKeyList(store
, pattern
);
608 info
= CFArrayCreateMutableCopy(NULL
, 0, keys
);
613 * iterate over each configured service to establish the new
616 for (i
= 0; i
< ifCount
; i
++) {
617 CFDictionaryRef service
;
619 CFStringRef configMethod
;
620 CFMutableStringRef portConfig
= NULL
;
621 CFArrayRef networkRange
; /* for seed ports, CFArray[2] of CFNumber (lo, hi) */
624 CFArrayRef zoneList
; /* for seed ports, CFArray[] of CFString (zones names) */
627 CFMutableDictionaryRef ifDefaults
= NULL
;
628 CFNumberRef defaultNetwork
;
629 CFNumberRef defaultNode
;
630 CFStringRef defaultZone
;
632 /* get AppleTalk service dictionary */
633 service
= CFArrayGetValueAtIndex(configuredServices
, i
);
635 /* get interface name */
636 ifName
= CFDictionaryGetValue(service
, kSCPropNetInterfaceDeviceName
);
638 /* check inteface availability */
640 !CFArrayContainsValue(interfaces
, CFRangeMake(0, CFArrayGetCount(interfaces
)), ifName
)) {
641 /* if interface not available */
645 /* check interface link status */
646 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
647 kSCDynamicStoreDomainState
,
650 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
653 Boolean linkStatus
= TRUE
; /* assume the link is "up" */
654 Boolean ifDetaching
= FALSE
; /* assume link is not detaching */
656 /* the link key for this interface is available */
657 if (isA_CFDictionary(dict
)) {
660 bVal
= CFDictionaryGetValue(dict
, kSCPropNetLinkActive
);
661 if (isA_CFBoolean(bVal
)) {
662 linkStatus
= CFBooleanGetValue(bVal
);
665 /* check if interface is detaching - value
666 doesn't really matter, only that it exists */
667 ifDetaching
= CFDictionaryContainsKey(dict
, kSCPropNetLinkDetaching
);
671 if (!linkStatus
|| ifDetaching
) {
672 /* if link status down or the interface is detaching */
678 * Determine configuration method for this service
680 configMethod
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkConfigMethod
);
681 if (!isA_CFString(configMethod
)) {
682 /* if no ConfigMethod */
686 if (!CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodNode
) &&
687 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) &&
688 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
689 /* if not one of the expected values, disable */
690 SCLog(TRUE
, LOG_NOTICE
,
691 CFSTR("Unexpected AppleTalk ConfigMethod: %@"),
697 * the first service to be defined will always be "primary"
699 if (CFArrayGetCount(newConfigFile
) == 0) {
700 CFDictionaryRef active
;
702 CFDictionarySetValue(newGlobals
,
703 kSCDynamicStorePropNetPrimaryService
,
704 CFDictionaryGetValue(service
, CFSTR("ServiceID")));
705 CFDictionarySetValue(newGlobals
,
706 kSCDynamicStorePropNetPrimaryInterface
,
709 /* and check if AT newtorking is active on the primary interface */
710 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
711 kSCDynamicStoreDomainState
,
714 active
= cache_SCDynamicStoreCopyValue(store
, key
);
717 if (isA_CFDictionary(active
)) {
727 portConfig
= CFStringCreateMutable(NULL
, 0);
728 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%@:"), ifName
);
730 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
734 * we have been asked to configure this interface as a
735 * seed port. Ensure that we have been provided at least
736 * one network number, have been provided with at least
740 networkRange
= CFDictionaryGetValue(service
,
741 kSCPropNetAppleTalkSeedNetworkRange
);
742 if (!isA_CFArray(networkRange
) || (CFArrayGetCount(networkRange
) == 0)) {
743 SCLog(TRUE
, LOG_NOTICE
,
744 CFSTR("AppleTalk configuration error (%@)"),
745 kSCPropNetAppleTalkSeedNetworkRange
);
750 * establish the starting and ending network numbers
752 num
= CFArrayGetValueAtIndex(networkRange
, 0);
753 if (!isA_CFNumber(num
)) {
754 SCLog(TRUE
, LOG_NOTICE
,
755 CFSTR("AppleTalk configuration error (%@)"),
756 kSCPropNetAppleTalkSeedNetworkRange
);
759 CFNumberGetValue(num
, kCFNumberIntType
, &sNetwork
);
762 if (CFArrayGetCount(networkRange
) > 1) {
763 num
= CFArrayGetValueAtIndex(networkRange
, 1);
764 if (!isA_CFNumber(num
)) {
765 SCLog(TRUE
, LOG_NOTICE
,
766 CFSTR("AppleTalk configuration error (%@)"),
767 kSCPropNetAppleTalkSeedNetworkRange
);
770 CFNumberGetValue(num
, kCFNumberIntType
, &eNetwork
);
772 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%d:%d:"), sNetwork
, eNetwork
);
775 * establish the zones associated with this port
777 zoneList
= CFDictionaryGetValue(service
,
778 kSCPropNetAppleTalkSeedZones
);
779 if (!isA_CFArray(zoneList
)) {
780 SCLog(TRUE
, LOG_NOTICE
,
781 CFSTR("AppleTalk configuration error (%@)"),
782 kSCPropNetAppleTalkSeedZones
);
786 zCount
= CFArrayGetCount(zoneList
);
787 for (j
= 0; j
< zCount
; j
++) {
790 CFMutableArrayRef newIFList
;
792 zone
= CFArrayGetValueAtIndex(zoneList
, j
);
793 if (!isA_CFString(zone
)) {
797 if (CFDictionaryGetValueIfPresent(newZones
, zone
, (const void **)&ifList
)) {
799 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
802 newIFList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
804 CFArrayAppendValue(newIFList
, ifName
);
805 CFArraySortValues(newIFList
,
806 CFRangeMake(0, CFArrayGetCount(newIFList
)),
807 (CFComparatorFunction
)CFStringCompare
,
809 CFDictionarySetValue(newZones
, zone
, newIFList
);
810 CFRelease(newIFList
);
813 * flag the default zone
816 primaryZone
= CFRetain(zone
);
820 SCLog(TRUE
, LOG_NOTICE
,
821 CFSTR("AppleTalk configuration error (%@)"),
822 kSCPropNetAppleTalkSeedZones
);
827 /* get the (per-interface) "Computer Name" */
828 computerName
= CFDictionaryGetValue(service
,
829 kSCPropNetAppleTalkComputerName
);
830 if (CFDictionaryGetValueIfPresent(service
,
831 kSCPropNetAppleTalkComputerNameEncoding
,
832 (const void **)&num
) &&
834 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
836 computerNameEncoding
= CFStringGetSystemEncoding();
838 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
841 * declare the first configured AppleTalk service / interface
842 * as the "home port".
844 if (CFArrayGetCount(newConfigFile
) == 0) {
845 CFStringAppend(portConfig
, CFSTR("*"));
846 primaryPort
= CFRetain(ifName
);
848 CFArrayAppendValue(newConfigFile
, portConfig
);
851 * get the per-interface defaults
853 ifDefaults
= CFDictionaryCreateMutable(NULL
,
855 &kCFTypeDictionaryKeyCallBacks
,
856 &kCFTypeDictionaryValueCallBacks
);
858 defaultNetwork
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNetworkID
);
859 defaultNode
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNodeID
);
860 if (isA_CFNumber(defaultNetwork
) && isA_CFNumber(defaultNode
)) {
862 * set the default node and network
864 CFDictionarySetValue(ifDefaults
,
865 kSCPropNetAppleTalkNetworkID
,
867 CFDictionarySetValue(ifDefaults
,
868 kSCPropNetAppleTalkNodeID
,
872 if ((CFDictionaryGetValueIfPresent(service
,
873 kSCPropNetAppleTalkDefaultZone
,
874 (const void **)&defaultZone
) == TRUE
)) {
876 * set the default zone for this interface
878 CFDictionarySetValue(ifDefaults
,
879 kSCPropNetAppleTalkDefaultZone
,
883 CFDictionarySetValue(newDefaults
, ifName
, ifDefaults
);
884 CFRelease(ifDefaults
);
886 switch (CFArrayGetCount(newConfigFile
)) {
889 * first AppleTalk interface
891 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), ifName
);
894 /* second AppleTalk interface */
895 if (!CFEqual(CFDictionaryGetValue(newStartup
, CFSTR("APPLETALK")),
896 CFSTR("-ROUTER-"))) {
898 * if not routing (yet), configure as multi-home
900 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-MULTIHOME-"));
905 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) ||
906 CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
907 /* if not a simple node, enable routing */
908 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-ROUTER-"));
912 * establish the State:/Network/Service/nnn/AppleTalk key info
914 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
915 kSCDynamicStoreDomainState
,
916 CFDictionaryGetValue(service
, CFSTR("ServiceID")),
918 newDict
= CFDictionaryCreateMutable(NULL
,
920 &kCFTypeDictionaryKeyCallBacks
,
921 &kCFTypeDictionaryValueCallBacks
);
922 CFDictionaryAddValue(newDict
, kSCPropInterfaceName
, ifName
);
923 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
926 j
= CFArrayGetFirstIndexOfValue(info
,
927 CFRangeMake(0, CFArrayGetCount(info
)),
929 if (j
!= kCFNotFound
) {
930 CFArrayRemoveValueAtIndex(info
, j
);
937 if (portConfig
) CFRelease(portConfig
);
942 CFMutableArrayRef newIFList
;
944 ifList
= CFDictionaryGetValue(newZones
, primaryZone
);
945 if (CFArrayContainsValue(ifList
,
946 CFRangeMake(0, CFArrayGetCount(ifList
)),
948 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
949 CFArrayAppendValue(newIFList
, CFSTR("*"));
950 CFDictionarySetValue(newZones
, primaryZone
, newIFList
);
951 CFRelease(newIFList
);
953 CFRelease(primaryZone
);
956 CFRelease(primaryPort
);
960 i
= CFArrayGetCount(newConfigFile
);
961 CFArraySortValues(newConfigFile
,
963 (CFComparatorFunction
)CFStringCompare
,
966 /* add the zones to the configuration */
967 CFDictionaryApplyFunction(newZones
, addZoneToPorts
, newConfigFile
);
971 CFArraySortValues(newConfigFile
,
972 CFRangeMake(i
, CFArrayGetCount(newConfigFile
)-i
),
973 (CFComparatorFunction
)CFStringCompare
,
976 /* ensure that the last line of the configuration file is terminated */
977 CFArrayAppendValue(newConfigFile
, CFSTR(""));
980 * Check if we have a "ComputerName" and look elsewhere if we don't have
983 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME")) &&
984 (setGlobals
!= NULL
)) {
985 computerName
= CFDictionaryGetValue(setGlobals
,
986 kSCPropNetAppleTalkComputerName
);
987 if (CFDictionaryGetValueIfPresent(setGlobals
,
988 kSCPropNetAppleTalkComputerNameEncoding
,
989 (const void **)&num
) &&
991 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
993 computerNameEncoding
= CFStringGetSystemEncoding();
995 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
997 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
998 computerName
= SCDynamicStoreCopyComputerName(store
, &computerNameEncoding
);
1000 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
1001 CFRelease(computerName
);
1004 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
1005 struct utsname name
;
1007 if (uname(&name
) == 0) {
1008 computerName
= CFStringCreateWithCString(NULL
, name
.nodename
, kCFStringEncodingASCII
);
1010 encodeName(computerName
, kCFStringEncodingASCII
, NULL
, newGlobals
);
1011 CFRelease(computerName
);
1016 /* compare the previous and current configurations */
1018 curGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, curGlobals
);
1019 CFDictionaryRemoveValue(curGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1021 newGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, newGlobals
);
1022 CFDictionaryRemoveValue(newGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1024 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1025 kSCDynamicStoreDomainState
,
1026 kSCEntNetAppleTalk
);
1028 if (CFEqual(curGlobalsX
, newGlobalsX
) &&
1029 CFEqual(curConfigFile
, newConfigFile
) &&
1030 CFEqual(curDefaults
, newDefaults
) &&
1031 CFEqual(curStartup
, newStartup
)
1034 * the configuration has not changed.
1039 * the requested configuration hasn't changed but we
1040 * now need to tell everyone that AppleTalk is active.
1042 if (!SCDynamicStoreSetValue(store
, key
, newGlobals
)) {
1045 CFSTR("SCDynamicStoreSetValue() failed: %s"),
1046 SCErrorString(SCError()));
1050 CFRelease(newGlobals
);
1051 CFRelease(newConfigFile
);
1052 CFRelease(newDefaults
);
1053 CFRelease(newStartup
);
1054 } else if (CFArrayGetCount(newConfigFile
) <= 1) {
1056 * the configuration has changed but there are no
1057 * longer any interfaces configured for AppleTalk
1062 * remove the global (State:/Network/Global/AppleTalk) key.
1064 * Note: it will be restored later after AT networking has
1068 /* remove the (/etc/appletalk.cfg) configuration file */
1069 (void)unlink(AT_CFG_FILE
);
1072 * update the per-service (and global) state
1074 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1075 n
= CFArrayGetCount(info
);
1076 for (i
= 0; i
< n
; i
++) {
1077 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1079 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1083 /* flag this as a new configuration */
1084 *newState
= -(abs(curState
) + 1);
1088 * the configuration has changed.
1091 /* update the (/etc/appletalk.cfg) configuration file */
1092 configWrite(AT_CFG_FILE
, newConfigFile
);
1095 * update the per-service (and global) state
1097 * Note: if present, we remove any existing global state key and allow it
1098 * to be restored after the stack has been re-started.
1100 CFDictionaryApplyFunction(newDefaults
, updateDefaults
, NULL
);
1101 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1102 n
= CFArrayGetCount(info
);
1103 for (i
= 0; i
< n
; i
++) {
1104 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1106 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1110 /* flag this as a new configuration */
1111 *newState
= abs(curState
) + 1;
1115 CFRelease(curGlobalsX
);
1116 CFRelease(newGlobalsX
);
1120 CFRelease(curGlobals
);
1121 curGlobals
= newGlobals
;
1122 CFRelease(curConfigFile
);
1123 curConfigFile
= newConfigFile
;
1124 CFRelease(curDefaults
);
1125 curDefaults
= newDefaults
;
1126 CFRelease(curStartup
);
1127 curStartup
= newStartup
;
1130 if (info
) CFRelease(info
);
1131 if (interfaces
) CFRelease(interfaces
);
1132 if (configuredServices
) CFRelease(configuredServices
);
1133 if (setGlobals
) CFRelease(setGlobals
);
1141 #include <sysexits.h>
1142 #define AT_CMD_SUCCESS EX_OK /* success */
1143 #define AT_CMD_ALREADY_RUNNING EX__MAX + 10
1144 #define AT_CMD_NOT_RUNNING EX__MAX + 11
1154 sock
= socket(AF_APPLETALK
, SOCK_RAW
, 0);
1155 ret
= ioctl(sock
, AIOCGETSTATE
, (caddr_t
)&state
);
1158 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(AIOCGETSTATE) failed: %s"), strerror(errno
));
1162 if (state
.flags
& AT_ST_STARTED
) {
1163 return abs(curState
);
1165 return -(abs(curState
));
1170 static pid_t execCommand
= 0;
1171 static int execRetry
;
1175 stopComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1179 if (WIFEXITED(status
)) {
1180 switch (WEXITSTATUS(status
)) {
1181 case AT_CMD_SUCCESS
:
1182 case AT_CMD_NOT_RUNNING
:
1183 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown complete"));
1185 // the stack is down but we really want it up
1186 startAppleTalk(NULL
, (void *)curState
);
1194 SCLog(TRUE
, LOG_ERR
,
1195 CFSTR("AppleTalk shutdown failed, status = %d%s"),
1196 WEXITSTATUS(status
),
1197 (execRetry
> 1) ? " (retrying)" : "");
1199 // shutdown failed, retry
1200 if (--execRetry
> 0) {
1201 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1202 CFRunLoopTimerRef timer
;
1204 timer
= CFRunLoopTimerCreate(NULL
,
1205 CFAbsoluteTimeGetCurrent() + 1.0,
1211 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1215 // we weren't able to stop
1216 curState
= stackState();
1224 stopAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1226 char *argv
[] = { "appletalk",
1230 if (execCommand
== 0) {
1231 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown"));
1233 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown skipped, transition in progress"));
1237 execCommand
= _SCDPluginExecCommand(stopComplete
, // callback
1241 "/usr/sbin/appletalk", // path
1245 execRetry
= 5; // initialize retry count
1253 startComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1257 if (WIFEXITED(status
)) {
1258 switch (WEXITSTATUS(status
)) {
1259 case AT_CMD_SUCCESS
:
1260 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup complete"));
1261 if ((curState
< 0) || (curState
> (int)context
)) {
1262 // the stack is now up but we really want it down
1263 stopAppleTalk(NULL
, (void *)curState
);
1266 case AT_CMD_ALREADY_RUNNING
:
1267 // the stack is already up but we're not sure
1268 // if the configuration is correct, restart
1269 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk already running, restarting"));
1270 stopAppleTalk(NULL
, (void *)curState
);
1277 SCLog(TRUE
, LOG_ERR
,
1278 CFSTR("AppleTalk startup failed, status = %d%s"),
1279 WEXITSTATUS(status
),
1280 (execRetry
> 1) ? " (retrying)" : "");
1282 // startup failed, retry
1283 if (--execRetry
> 0) {
1284 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1285 CFRunLoopTimerRef timer
;
1287 timer
= CFRunLoopTimerCreate(NULL
,
1288 CFAbsoluteTimeGetCurrent() + 1.0,
1294 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1298 // we weren't able to start
1299 curState
= stackState();
1307 startAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1311 char *computerName
= NULL
;
1312 char *interface
= NULL
;
1313 CFStringRef mode
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK"));
1314 CFStringRef name
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK_HOSTNAME"));
1316 if (execCommand
== 0) {
1317 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup"));
1319 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup skipped, transition in progress"));
1329 argv
[argc
++] = "appletalk";
1333 computerName
= _SC_cfstring_to_cstring(name
, NULL
, 0, kCFStringEncodingASCII
);
1335 argv
[argc
++] = "-C";
1336 argv
[argc
++] = computerName
;
1338 // could not convert name
1344 if (CFEqual(mode
, CFSTR("-ROUTER-"))) {
1345 argv
[argc
++] = "-r";
1346 } else if (CFEqual(mode
, CFSTR("-MULTIHOME-"))) {
1347 argv
[argc
++] = "-x";
1349 interface
= _SC_cfstring_to_cstring(mode
, NULL
, 0, kCFStringEncodingASCII
);
1351 argv
[argc
++] = "-u";
1352 argv
[argc
++] = interface
;
1354 // could not convert interface
1359 // set non-interactive
1360 argv
[argc
++] = "-q";
1362 // close argument list
1363 argv
[argc
++] = NULL
;
1365 execCommand
= _SCDPluginExecCommand(startComplete
, // callback
1369 "/usr/sbin/appletalk", // path
1373 execRetry
= 5; // initialize retry count
1378 if (computerName
) CFAllocatorDeallocate(NULL
, computerName
);
1379 if (interface
) CFAllocatorDeallocate(NULL
, interface
);
1386 atConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
1388 boolean_t configChanged
;
1391 configChanged
= updateConfiguration(&newState
);
1393 if (configChanged
&& (execCommand
== 0)) {
1394 // if the configuration has changed and we're not already transitioning
1397 // already running, restart [with new configuration]
1398 stopAppleTalk(NULL
, (void *)newState
);
1400 startAppleTalk(NULL
, (void *)newState
);
1404 stopAppleTalk(NULL
, (void *)newState
);
1409 curState
= newState
;
1416 stop_ATconfig(CFRunLoopSourceRef stopRls
)
1420 if (storeRls
!= NULL
) {
1421 CFRunLoopSourceInvalidate(storeRls
);
1422 CFRelease(storeRls
);
1426 if (store
!= NULL
) {
1429 CFRelease(curGlobals
);
1430 CFRelease(curConfigFile
);
1431 CFRelease(curDefaults
);
1432 CFRelease(curStartup
);
1435 CFRunLoopSourceSignal(stopRls
);
1441 load_ATconfig(CFBundleRef bundle
, Boolean bundleVerbose
)
1444 CFMutableArrayRef keys
= NULL
;
1445 CFStringRef pattern
;
1446 CFMutableArrayRef patterns
= NULL
;
1448 if (bundleVerbose
) {
1452 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
1453 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
1455 /* initialize a few globals */
1457 curGlobals
= CFDictionaryCreateMutable(NULL
,
1459 &kCFTypeDictionaryKeyCallBacks
,
1460 &kCFTypeDictionaryValueCallBacks
);
1461 curConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1462 curDefaults
= CFDictionaryCreateMutable(NULL
,
1464 &kCFTypeDictionaryKeyCallBacks
,
1465 &kCFTypeDictionaryValueCallBacks
);
1466 curStartup
= CFDictionaryCreateMutable(NULL
,
1468 &kCFTypeDictionaryKeyCallBacks
,
1469 &kCFTypeDictionaryValueCallBacks
);
1471 /* open a "configd" store to allow cache updates */
1472 store
= SCDynamicStoreCreate(NULL
,
1473 CFSTR("AppleTalk Configuraton plug-in"),
1474 atConfigChangedCallback
,
1477 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
1481 /* establish notification keys and patterns */
1483 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1484 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1486 /* ...watch for (global) AppleTalk configuration changes */
1487 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1488 kSCDynamicStoreDomainSetup
,
1489 kSCEntNetAppleTalk
);
1490 CFArrayAppendValue(keys
, key
);
1493 /* ...watch for (per-service) AppleTalk configuration changes */
1494 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1495 kSCDynamicStoreDomainSetup
,
1497 kSCEntNetAppleTalk
);
1498 CFArrayAppendValue(patterns
, pattern
);
1501 /* ...watch for network interface link status changes */
1502 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1503 kSCDynamicStoreDomainState
,
1506 CFArrayAppendValue(patterns
, pattern
);
1509 /* ...watch for (per-interface) AppleTalk configuration changes */
1510 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1511 kSCDynamicStoreDomainState
,
1513 kSCEntNetAppleTalk
);
1514 CFArrayAppendValue(patterns
, pattern
);
1517 /* ...watch for computer name changes */
1518 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
1519 CFArrayAppendValue(keys
, key
);
1522 /* register the keys/patterns */
1523 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
1524 SCLog(TRUE
, LOG_ERR
,
1525 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
1526 SCErrorString(SCError()));
1530 CFRelease(patterns
);
1532 storeRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
1534 SCLog(TRUE
, LOG_ERR
,
1535 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
1536 SCErrorString(SCError()));
1539 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRls
, kCFRunLoopDefaultMode
);
1545 if (curGlobals
) CFRelease(curGlobals
);
1546 if (curConfigFile
) CFRelease(curConfigFile
);
1547 if (curDefaults
) CFRelease(curDefaults
);
1548 if (curStartup
) CFRelease(curStartup
);
1549 if (store
) CFRelease(store
);
1550 if (keys
) CFRelease(keys
);
1551 if (patterns
) CFRelease(patterns
);
1557 #include "cfManager.c"
1559 main(int argc
, char **argv
)
1562 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1564 load_ATconfig(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);