2 * Copyright (c) 2000-2006 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/param.h>
48 #include <sys/socket.h>
50 #include <sys/utsname.h>
54 #include <netat/appletalk.h>
55 #include <netat/at_var.h>
56 #include <AppleTalk/at_paths.h>
57 #include <AppleTalk/at_proto.h>
59 #include <CoreFoundation/CoreFoundation.h>
60 #include <SystemConfiguration/SystemConfiguration.h>
61 #include <SystemConfiguration/SCPrivate.h>
62 #include <SystemConfiguration/SCDPlugin.h>
63 #include <SystemConfiguration/SCValidation.h>
66 #include "cfManager.h"
68 #define HOSTCONFIG "/etc/hostconfig"
70 static SCDynamicStoreRef store
= NULL
;
71 static CFRunLoopSourceRef storeRls
= NULL
;
73 static int curState
= 0; // abs(state) == sequence #, < 0 == stop, > 0 == start
74 static CFMutableDictionaryRef curGlobals
= NULL
;
75 static CFMutableArrayRef curConfigFile
= NULL
;
76 static CFMutableDictionaryRef curDefaults
= NULL
;
77 static CFMutableDictionaryRef curStartup
= NULL
;
79 static Boolean _verbose
= FALSE
;
82 #define RETRY_DELAY 2.0 // seconds
83 #define RETRY_LIMIT 5 // # of startup/shutdown attempts
85 static void startAppleTalk(CFRunLoopTimerRef timer
, void *info
);
86 static void stopAppleTalk (CFRunLoopTimerRef timer
, void *info
);
90 __loadAppleTalk(void) {
91 static void *image
= NULL
;
93 const char *framework
= "/System/Library/Frameworks/AppleTalk.framework/Versions/A/AppleTalk";
95 const char *suffix
= getenv("DYLD_IMAGE_SUFFIX");
96 char path
[MAXPATHLEN
];
98 strlcpy(path
, framework
, sizeof(path
));
99 if (suffix
) strlcat(path
, suffix
, sizeof(path
));
100 if (0 <= stat(path
, &statbuf
)) {
101 image
= dlopen(path
, RTLD_LAZY
| RTLD_LOCAL
);
103 image
= dlopen(framework
, RTLD_LAZY
| RTLD_LOCAL
);
106 return (void *)image
;
111 _at_setdefaultaddr(char *ifName
, struct at_addr
*init_address
)
113 #undef at_setdefaultaddr
114 static typeof (at_setdefaultaddr
) *dyfunc
= NULL
;
116 void *image
= __loadAppleTalk();
117 if (image
) dyfunc
= dlsym(image
, "at_setdefaultaddr");
119 return dyfunc
? dyfunc(ifName
, init_address
) : -1;
121 #define at_setdefaultaddr _at_setdefaultaddr
125 _at_setdefaultzone(char *ifName
, at_nvestr_t
*zone
)
127 #undef at_setdefaultzone
128 static typeof (at_setdefaultzone
) *dyfunc
= NULL
;
130 void *image
= __loadAppleTalk();
131 if (image
) dyfunc
= dlsym(image
, "at_setdefaultzone");
133 return dyfunc
? dyfunc(ifName
, zone
) : -1;
135 #define at_setdefaultzone _at_setdefaultzone
139 updateDefaults(const void *key
, const void *val
, void *context
)
141 CFStringRef ifName
= (CFStringRef
)key
;
142 CFDictionaryRef oldDict
;
143 CFDictionaryRef newDict
= (CFDictionaryRef
)val
;
144 CFNumberRef defaultNode
;
145 CFNumberRef defaultNetwork
;
146 CFStringRef defaultZone
;
148 if (!CFDictionaryGetValueIfPresent(curDefaults
, ifName
, (const void **)&oldDict
) ||
149 !CFEqual(oldDict
, newDict
)) {
150 char ifr_name
[IFNAMSIZ
+ 1];
152 bzero(&ifr_name
, sizeof(ifr_name
));
153 if (!_SC_cfstring_to_cstring(ifName
, ifr_name
, sizeof(ifr_name
), kCFStringEncodingASCII
)) {
154 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert interface name to C string"));
159 * Set preferred Network and Node ID
161 if (CFDictionaryGetValueIfPresent(newDict
,
162 kSCPropNetAppleTalkNetworkID
,
163 (const void **)&defaultNetwork
) &&
164 CFDictionaryGetValueIfPresent(newDict
,
165 kSCPropNetAppleTalkNodeID
,
166 (const void **)&defaultNode
)
168 struct at_addr init_address
;
172 * set the default node and network
174 CFNumberGetValue(defaultNetwork
, kCFNumberShortType
, &init_address
.s_net
);
175 CFNumberGetValue(defaultNode
, kCFNumberCharType
, &init_address
.s_node
);
176 status
= at_setdefaultaddr(ifr_name
, &init_address
);
178 SCLog(TRUE
, LOG_ERR
, CFSTR("at_setdefaultaddr() failed"));
186 if (CFDictionaryGetValueIfPresent(newDict
,
187 kSCPropNetAppleTalkDefaultZone
,
188 (const void **)&defaultZone
)
194 * set the "default zone" for this interface
196 bzero(&zone
, sizeof(zone
));
197 if (!_SC_cfstring_to_cstring(defaultZone
,
200 kCFStringEncodingASCII
)) {
201 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert default zone to C string"));
205 zone
.len
= strlen((const char *)zone
.str
);
206 status
= at_setdefaultzone(ifr_name
, &zone
);
208 SCLog(TRUE
, LOG_ERR
, CFSTR("at_setdefaultzone() failed"));
219 addZoneToPorts(const void *key
, const void *val
, void *context
)
221 CFStringRef zone
= (CFStringRef
)key
;
222 CFArrayRef ifArray
= (CFArrayRef
)val
;
223 CFMutableArrayRef zones
= (CFMutableArrayRef
)context
;
225 CFStringRef configInfo
;
227 ifList
= CFStringCreateByCombiningStrings(NULL
, ifArray
, CFSTR(":"));
228 configInfo
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(":%@:%@"), zone
, ifList
);
229 CFArrayAppendValue(zones
, configInfo
);
230 CFRelease(configInfo
);
237 * Function: parse_component
239 * Given a string 'key' and a string prefix 'prefix',
240 * return the next component in the slash '/' separated
244 * 1. key = "a/b/c" prefix = "a/"
246 * 2. key = "a/b/c" prefix = "a/b/"
250 parse_component(CFStringRef key
, CFStringRef prefix
)
252 CFMutableStringRef comp
;
255 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
258 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
259 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
260 range
= CFStringFind(comp
, CFSTR("/"), 0);
261 if (range
.location
== kCFNotFound
) {
264 range
.length
= CFStringGetLength(comp
) - range
.location
;
265 CFStringDelete(comp
, range
);
270 static CFDictionaryRef
271 entity_one(SCDynamicStoreRef store
, CFStringRef key
)
273 CFDictionaryRef ent_dict
= NULL
;
274 CFDictionaryRef if_dict
= NULL
;
275 CFStringRef if_key
= NULL
;
277 CFMutableDictionaryRef new_dict
= NULL
;
278 static CFStringRef pre
= NULL
;
279 CFStringRef serviceID
= NULL
;
280 CFStringRef serviceType
;
283 pre
= SCDynamicStoreKeyCreate(NULL
,
285 kSCDynamicStoreDomainSetup
,
291 * get entity dictionary for service
293 ent_dict
= cache_SCDynamicStoreCopyValue(store
, key
);
294 if (!isA_CFDictionary(ent_dict
)) {
299 * get interface dictionary for service
301 serviceID
= parse_component(key
, pre
);
306 if_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
307 kSCDynamicStoreDomainSetup
,
310 if_dict
= cache_SCDynamicStoreCopyValue(store
, if_key
);
312 if (!isA_CFDictionary(if_dict
)) {
316 /* check the interface type */
317 serviceType
= CFDictionaryGetValue(if_dict
,
318 kSCPropNetInterfaceType
);
319 if (!isA_CFString(serviceType
) ||
320 !CFEqual(serviceType
, kSCValNetInterfaceTypeEthernet
)) {
321 /* sorry, no AT networking on this interface */
326 * get port name (from interface dictionary).
328 if_port
= CFDictionaryGetValue(if_dict
, kSCPropNetInterfaceDeviceName
);
329 if (!isA_CFString(if_port
)) {
334 * add ServiceID and interface port name to entity dictionary.
336 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, ent_dict
);
337 CFDictionarySetValue(new_dict
, CFSTR("ServiceID"), serviceID
);
338 CFDictionarySetValue(new_dict
, kSCPropNetInterfaceDeviceName
, if_port
);
342 if (ent_dict
) CFRelease(ent_dict
);
343 if (if_dict
) CFRelease(if_dict
);
344 if (serviceID
) CFRelease(serviceID
);
345 return (CFDictionaryRef
)new_dict
;
350 entity_all(SCDynamicStoreRef store
, CFStringRef entity
, CFArrayRef order
)
352 CFMutableArrayRef defined
= NULL
;
355 CFMutableArrayRef ordered
= NULL
;
358 ordered
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
360 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
361 kSCDynamicStoreDomainSetup
,
364 defined
= (CFMutableArrayRef
)SCDynamicStoreCopyKeyList(store
, pattern
);
366 if (defined
&& (CFArrayGetCount(defined
) > 0)) {
370 defined
= CFArrayCreateMutableCopy(NULL
, 0, tmp
);
376 n
= order
? CFArrayGetCount(order
) : 0;
377 for (i
= 0; i
< n
; i
++) {
378 CFDictionaryRef dict
;
383 service
= CFArrayGetValueAtIndex(order
, i
);
384 if (!isA_CFString(service
)) {
388 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
389 kSCDynamicStoreDomainSetup
,
392 dict
= entity_one(store
, key
);
394 CFArrayAppendValue(ordered
, dict
);
398 j
= CFArrayGetFirstIndexOfValue(defined
,
399 CFRangeMake(0, CFArrayGetCount(defined
)),
401 if (j
!= kCFNotFound
) {
402 CFArrayRemoveValueAtIndex(defined
, j
);
408 n
= CFArrayGetCount(defined
);
409 for (i
= 0; i
< n
; i
++) {
410 CFDictionaryRef dict
;
413 key
= CFArrayGetValueAtIndex(defined
, i
);
414 dict
= entity_one(store
, key
);
416 CFArrayAppendValue(ordered
, dict
);
423 if (defined
) CFRelease(defined
);
424 if (CFArrayGetCount(ordered
) == 0) {
433 encodeName(CFStringRef name
,
434 CFStringEncoding encoding
,
435 CFMutableDictionaryRef startup
,
436 CFMutableDictionaryRef globals
)
439 CFMutableStringRef encodedName
= NULL
;
442 if (!isA_CFString(name
)) {
446 if (encoding
== kCFStringEncodingASCII
) {
447 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
452 * encode the potentially non-printable string
454 bytes
= CFStringCreateExternalRepresentation(NULL
,
463 * check if the MacRoman string can be represented as ASCII
465 if (encoding
== kCFStringEncodingMacRoman
) {
468 ascii
= CFStringCreateExternalRepresentation(NULL
,
470 kCFStringEncodingASCII
,
475 encodedName
= (CFMutableStringRef
)CFStringCreateCopy(NULL
, name
);
480 encodedName
= CFStringCreateMutable(NULL
, 0);
482 len
= CFDataGetLength(bytes
);
483 byte
= (unsigned char *)CFDataGetBytePtr(bytes
);
484 for (i
= 0; i
< len
; i
++, byte
++) {
485 CFStringAppendFormat(encodedName
,
492 * add "encoded string" markers
494 CFStringInsert(encodedName
, 0, CFSTR("*"));
495 CFStringAppend(encodedName
, CFSTR("*"));
504 /* update "startup" dictionary */
505 CFDictionaryAddValue(startup
, CFSTR("APPLETALK_HOSTNAME"), encodedName
);
511 /* update "global" dictionary */
512 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &encoding
);
513 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerName
, name
);
514 CFDictionaryAddValue(globals
, kSCPropNetAppleTalkComputerNameEncoding
, num
);
518 CFRelease(encodedName
);
526 updateConfiguration(int *newState
)
528 boolean_t changed
= FALSE
;
529 CFStringRef computerName
;
530 CFStringEncoding computerNameEncoding
;
531 CFArrayRef configuredServices
= NULL
;
532 CFDictionaryRef dict
;
535 CFMutableArrayRef info
= NULL
;
536 CFArrayRef interfaces
= NULL
;
540 CFMutableArrayRef newConfigFile
;
541 CFMutableDictionaryRef newDefaults
;
542 CFMutableDictionaryRef newDict
;
543 CFMutableDictionaryRef newGlobals
;
544 CFMutableDictionaryRef newGlobalsX
; /* newGlobals without ServiceID */
545 CFMutableDictionaryRef newStartup
;
546 CFMutableDictionaryRef newZones
;
548 CFMutableDictionaryRef curGlobalsX
; /* curGlobals without ServiceID */
550 boolean_t postGlobals
= FALSE
;
551 CFStringRef primaryPort
= NULL
; /* primary interface */
552 CFStringRef primaryZone
= NULL
;
553 CFArrayRef serviceOrder
= NULL
;
554 CFDictionaryRef setGlobals
= NULL
;
559 * establish the "new" AppleTalk configuration
561 *newState
= curState
;
562 newConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
563 newGlobals
= CFDictionaryCreateMutable(NULL
,
565 &kCFTypeDictionaryKeyCallBacks
,
566 &kCFTypeDictionaryValueCallBacks
);
567 newDefaults
= CFDictionaryCreateMutable(NULL
,
569 &kCFTypeDictionaryKeyCallBacks
,
570 &kCFTypeDictionaryValueCallBacks
);
571 newStartup
= CFDictionaryCreateMutable(NULL
,
573 &kCFTypeDictionaryKeyCallBacks
,
574 &kCFTypeDictionaryValueCallBacks
);
575 newZones
= CFDictionaryCreateMutable(NULL
,
577 &kCFTypeDictionaryKeyCallBacks
,
578 &kCFTypeDictionaryValueCallBacks
);
580 /* initialize overall state */
581 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-NO-"));
584 * get the global settings (ServiceOrder, ComputerName, ...)
586 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
587 kSCDynamicStoreDomainSetup
,
589 setGlobals
= cache_SCDynamicStoreCopyValue(store
, key
);
592 if (isA_CFDictionary(setGlobals
)) {
593 /* get service order */
594 serviceOrder
= CFDictionaryGetValue(setGlobals
,
595 kSCPropNetServiceOrder
);
596 serviceOrder
= isA_CFArray(serviceOrder
);
598 CFRetain(serviceOrder
);
601 CFRelease(setGlobals
);
607 * if we don't have an AppleTalk ServiceOrder, use IPv4's (if defined)
610 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
611 kSCDynamicStoreDomainSetup
,
613 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
616 if (isA_CFDictionary(dict
)) {
617 serviceOrder
= CFDictionaryGetValue(dict
,
618 kSCPropNetServiceOrder
);
619 serviceOrder
= isA_CFArray(serviceOrder
);
621 CFRetain(serviceOrder
);
629 * get the list of ALL configured services
631 configuredServices
= entity_all(store
, kSCEntNetAppleTalk
, serviceOrder
);
632 if (configuredServices
) {
633 ifCount
= CFArrayGetCount(configuredServices
);
636 if (serviceOrder
) CFRelease(serviceOrder
);
639 * get the list of ALL active interfaces
641 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
642 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
645 if (isA_CFDictionary(dict
)) {
646 interfaces
= CFDictionaryGetValue(dict
,
647 kSCDynamicStorePropNetInterfaces
);
648 interfaces
= isA_CFArray(interfaces
);
650 CFRetain(interfaces
);
657 * get the list of previously configured services
659 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
660 kSCDynamicStoreDomainState
,
663 keys
= SCDynamicStoreCopyKeyList(store
, pattern
);
666 info
= CFArrayCreateMutableCopy(NULL
, 0, keys
);
671 * iterate over each configured service to establish the new
674 for (i
= 0; i
< ifCount
; i
++) {
675 CFDictionaryRef service
;
677 CFStringRef configMethod
;
678 CFMutableStringRef portConfig
= NULL
;
679 CFArrayRef networkRange
; /* for seed ports, CFArray[2] of CFNumber (lo, hi) */
682 CFArrayRef zoneList
; /* for seed ports, CFArray[] of CFString (zones names) */
685 CFMutableDictionaryRef ifDefaults
= NULL
;
686 CFNumberRef defaultNetwork
;
687 CFNumberRef defaultNode
;
688 CFStringRef defaultZone
;
690 /* get AppleTalk service dictionary */
691 service
= CFArrayGetValueAtIndex(configuredServices
, i
);
693 /* get interface name */
694 ifName
= CFDictionaryGetValue(service
, kSCPropNetInterfaceDeviceName
);
696 /* check inteface availability */
698 !CFArrayContainsValue(interfaces
, CFRangeMake(0, CFArrayGetCount(interfaces
)), ifName
)) {
699 /* if interface not available */
703 /* check interface link status */
704 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
705 kSCDynamicStoreDomainState
,
708 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
711 Boolean linkStatus
= TRUE
; /* assume the link is "up" */
712 Boolean ifDetaching
= FALSE
; /* assume link is not detaching */
714 /* the link key for this interface is available */
715 if (isA_CFDictionary(dict
)) {
718 bVal
= CFDictionaryGetValue(dict
, kSCPropNetLinkActive
);
719 if (isA_CFBoolean(bVal
)) {
720 linkStatus
= CFBooleanGetValue(bVal
);
723 /* check if interface is detaching - value
724 doesn't really matter, only that it exists */
725 ifDetaching
= CFDictionaryContainsKey(dict
, kSCPropNetLinkDetaching
);
729 if (!linkStatus
|| ifDetaching
) {
730 /* if link status down or the interface is detaching */
736 * Determine configuration method for this service
738 configMethod
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkConfigMethod
);
739 if (!isA_CFString(configMethod
)) {
740 /* if no ConfigMethod */
744 if (!CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodNode
) &&
745 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) &&
746 !CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
747 /* if not one of the expected values, disable */
748 SCLog(TRUE
, LOG_NOTICE
,
749 CFSTR("Unexpected AppleTalk ConfigMethod: %@"),
755 * the first service to be defined will always be "primary"
757 if (CFArrayGetCount(newConfigFile
) == 0) {
758 CFDictionaryRef active
;
760 CFDictionarySetValue(newGlobals
,
761 kSCDynamicStorePropNetPrimaryService
,
762 CFDictionaryGetValue(service
, CFSTR("ServiceID")));
763 CFDictionarySetValue(newGlobals
,
764 kSCDynamicStorePropNetPrimaryInterface
,
767 /* and check if AT newtorking is active on the primary interface */
768 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
769 kSCDynamicStoreDomainState
,
772 active
= cache_SCDynamicStoreCopyValue(store
, key
);
775 if (isA_CFDictionary(active
)) {
785 portConfig
= CFStringCreateMutable(NULL
, 0);
786 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%@:"), ifName
);
788 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
792 * we have been asked to configure this interface as a
793 * seed port. Ensure that we have been provided at least
794 * one network number, have been provided with at least
798 networkRange
= CFDictionaryGetValue(service
,
799 kSCPropNetAppleTalkSeedNetworkRange
);
800 if (!isA_CFArray(networkRange
) || (CFArrayGetCount(networkRange
) == 0)) {
801 SCLog(TRUE
, LOG_NOTICE
,
802 CFSTR("AppleTalk configuration error (%@)"),
803 kSCPropNetAppleTalkSeedNetworkRange
);
808 * establish the starting and ending network numbers
810 num
= CFArrayGetValueAtIndex(networkRange
, 0);
811 if (!isA_CFNumber(num
)) {
812 SCLog(TRUE
, LOG_NOTICE
,
813 CFSTR("AppleTalk configuration error (%@)"),
814 kSCPropNetAppleTalkSeedNetworkRange
);
817 CFNumberGetValue(num
, kCFNumberIntType
, &sNetwork
);
820 if (CFArrayGetCount(networkRange
) > 1) {
821 num
= CFArrayGetValueAtIndex(networkRange
, 1);
822 if (!isA_CFNumber(num
)) {
823 SCLog(TRUE
, LOG_NOTICE
,
824 CFSTR("AppleTalk configuration error (%@)"),
825 kSCPropNetAppleTalkSeedNetworkRange
);
828 CFNumberGetValue(num
, kCFNumberIntType
, &eNetwork
);
830 CFStringAppendFormat(portConfig
, NULL
, CFSTR("%d:%d:"), sNetwork
, eNetwork
);
833 * establish the zones associated with this port
835 zoneList
= CFDictionaryGetValue(service
,
836 kSCPropNetAppleTalkSeedZones
);
837 if (!isA_CFArray(zoneList
)) {
838 SCLog(TRUE
, LOG_NOTICE
,
839 CFSTR("AppleTalk configuration error (%@)"),
840 kSCPropNetAppleTalkSeedZones
);
844 zCount
= CFArrayGetCount(zoneList
);
845 for (j
= 0; j
< zCount
; j
++) {
848 CFMutableArrayRef newIFList
;
850 zone
= CFArrayGetValueAtIndex(zoneList
, j
);
851 if (!isA_CFString(zone
)) {
855 if (CFDictionaryGetValueIfPresent(newZones
, zone
, (const void **)&ifList
)) {
857 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
860 newIFList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
862 CFArrayAppendValue(newIFList
, ifName
);
863 CFArraySortValues(newIFList
,
864 CFRangeMake(0, CFArrayGetCount(newIFList
)),
865 (CFComparatorFunction
)CFStringCompare
,
867 CFDictionarySetValue(newZones
, zone
, newIFList
);
868 CFRelease(newIFList
);
871 * flag the default zone
874 primaryZone
= CFRetain(zone
);
878 SCLog(TRUE
, LOG_NOTICE
,
879 CFSTR("AppleTalk configuration error (%@)"),
880 kSCPropNetAppleTalkSeedZones
);
885 /* get the (per-interface) "Computer Name" */
886 computerName
= CFDictionaryGetValue(service
,
887 kSCPropNetAppleTalkComputerName
);
888 if (CFDictionaryGetValueIfPresent(service
,
889 kSCPropNetAppleTalkComputerNameEncoding
,
890 (const void **)&num
) &&
892 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
894 computerNameEncoding
= CFStringGetSystemEncoding();
896 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
899 * declare the first configured AppleTalk service / interface
900 * as the "home port".
902 if (CFArrayGetCount(newConfigFile
) == 0) {
903 CFStringAppend(portConfig
, CFSTR("*"));
904 primaryPort
= CFRetain(ifName
);
906 CFArrayAppendValue(newConfigFile
, portConfig
);
909 * get the per-interface defaults
911 ifDefaults
= CFDictionaryCreateMutable(NULL
,
913 &kCFTypeDictionaryKeyCallBacks
,
914 &kCFTypeDictionaryValueCallBacks
);
916 defaultNetwork
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNetworkID
);
917 defaultNode
= CFDictionaryGetValue(service
, kSCPropNetAppleTalkNodeID
);
918 if (isA_CFNumber(defaultNetwork
) && isA_CFNumber(defaultNode
)) {
920 * set the default node and network
922 CFDictionarySetValue(ifDefaults
,
923 kSCPropNetAppleTalkNetworkID
,
925 CFDictionarySetValue(ifDefaults
,
926 kSCPropNetAppleTalkNodeID
,
930 if ((CFDictionaryGetValueIfPresent(service
,
931 kSCPropNetAppleTalkDefaultZone
,
932 (const void **)&defaultZone
) == TRUE
)) {
934 * set the default zone for this interface
936 CFDictionarySetValue(ifDefaults
,
937 kSCPropNetAppleTalkDefaultZone
,
941 CFDictionarySetValue(newDefaults
, ifName
, ifDefaults
);
942 CFRelease(ifDefaults
);
944 switch (CFArrayGetCount(newConfigFile
)) {
947 * first AppleTalk interface
949 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), ifName
);
952 /* second AppleTalk interface */
953 if (!CFEqual(CFDictionaryGetValue(newStartup
, CFSTR("APPLETALK")),
954 CFSTR("-ROUTER-"))) {
956 * if not routing (yet), configure as multi-home
958 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-MULTIHOME-"));
963 if (CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodRouter
) ||
964 CFEqual(configMethod
, kSCValNetAppleTalkConfigMethodSeedRouter
)) {
965 /* if not a simple node, enable routing */
966 CFDictionarySetValue(newStartup
, CFSTR("APPLETALK"), CFSTR("-ROUTER-"));
970 * establish the State:/Network/Service/nnn/AppleTalk key info
972 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
973 kSCDynamicStoreDomainState
,
974 CFDictionaryGetValue(service
, CFSTR("ServiceID")),
976 newDict
= CFDictionaryCreateMutable(NULL
,
978 &kCFTypeDictionaryKeyCallBacks
,
979 &kCFTypeDictionaryValueCallBacks
);
980 CFDictionaryAddValue(newDict
, kSCPropInterfaceName
, ifName
);
981 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
984 j
= CFArrayGetFirstIndexOfValue(info
,
985 CFRangeMake(0, CFArrayGetCount(info
)),
987 if (j
!= kCFNotFound
) {
988 CFArrayRemoveValueAtIndex(info
, j
);
995 if (portConfig
) CFRelease(portConfig
);
1000 CFMutableArrayRef newIFList
;
1002 ifList
= CFDictionaryGetValue(newZones
, primaryZone
);
1003 if (CFArrayContainsValue(ifList
,
1004 CFRangeMake(0, CFArrayGetCount(ifList
)),
1006 newIFList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
1007 CFArrayAppendValue(newIFList
, CFSTR("*"));
1008 CFDictionarySetValue(newZones
, primaryZone
, newIFList
);
1009 CFRelease(newIFList
);
1011 CFRelease(primaryZone
);
1014 CFRelease(primaryPort
);
1017 /* sort the ports */
1018 i
= CFArrayGetCount(newConfigFile
);
1019 CFArraySortValues(newConfigFile
,
1021 (CFComparatorFunction
)CFStringCompare
,
1024 /* add the zones to the configuration */
1025 CFDictionaryApplyFunction(newZones
, addZoneToPorts
, newConfigFile
);
1026 CFRelease(newZones
);
1028 /* sort the zones */
1029 CFArraySortValues(newConfigFile
,
1030 CFRangeMake(i
, CFArrayGetCount(newConfigFile
)-i
),
1031 (CFComparatorFunction
)CFStringCompare
,
1034 /* ensure that the last line of the configuration file is terminated */
1035 CFArrayAppendValue(newConfigFile
, CFSTR(""));
1038 * Check if we have a "ComputerName" and look elsewhere if we don't have
1041 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME")) &&
1042 (setGlobals
!= NULL
)) {
1043 computerName
= CFDictionaryGetValue(setGlobals
,
1044 kSCPropNetAppleTalkComputerName
);
1045 if (CFDictionaryGetValueIfPresent(setGlobals
,
1046 kSCPropNetAppleTalkComputerNameEncoding
,
1047 (const void **)&num
) &&
1048 isA_CFNumber(num
)) {
1049 CFNumberGetValue(num
, kCFNumberIntType
, &computerNameEncoding
);
1051 computerNameEncoding
= CFStringGetSystemEncoding();
1053 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
1055 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
1056 computerName
= SCDynamicStoreCopyComputerName(store
, &computerNameEncoding
);
1058 encodeName(computerName
, computerNameEncoding
, newStartup
, newGlobals
);
1059 CFRelease(computerName
);
1062 if (!CFDictionaryContainsKey(newStartup
, CFSTR("APPLETALK_HOSTNAME"))) {
1063 struct utsname name
;
1065 if (uname(&name
) == 0) {
1066 computerName
= CFStringCreateWithCString(NULL
, name
.nodename
, kCFStringEncodingASCII
);
1068 encodeName(computerName
, kCFStringEncodingASCII
, NULL
, newGlobals
);
1069 CFRelease(computerName
);
1074 /* compare the previous and current configurations */
1076 curGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, curGlobals
);
1077 CFDictionaryRemoveValue(curGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1079 newGlobalsX
= CFDictionaryCreateMutableCopy(NULL
, 0, newGlobals
);
1080 CFDictionaryRemoveValue(newGlobalsX
, kSCDynamicStorePropNetPrimaryService
);
1082 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1083 kSCDynamicStoreDomainState
,
1084 kSCEntNetAppleTalk
);
1086 if (CFEqual(curGlobalsX
, newGlobalsX
) &&
1087 CFEqual(curConfigFile
, newConfigFile
) &&
1088 CFEqual(curDefaults
, newDefaults
) &&
1089 CFEqual(curStartup
, newStartup
)
1092 * the configuration has not changed.
1097 * the requested configuration hasn't changed but we
1098 * now need to tell everyone that AppleTalk is active.
1100 if (!SCDynamicStoreSetValue(store
, key
, newGlobals
)) {
1103 CFSTR("SCDynamicStoreSetValue() failed: %s"),
1104 SCErrorString(SCError()));
1108 CFRelease(newGlobals
);
1109 CFRelease(newConfigFile
);
1110 CFRelease(newDefaults
);
1111 CFRelease(newStartup
);
1112 } else if (CFArrayGetCount(newConfigFile
) <= 1) {
1114 * the configuration has changed but there are no
1115 * longer any interfaces configured for AppleTalk
1120 * remove the global (State:/Network/Global/AppleTalk) key.
1122 * Note: it will be restored later after AT networking has
1126 /* remove the (/etc/appletalk.cfg) configuration file */
1127 (void)unlink(AT_CFG_FILE
);
1130 * update the per-service (and global) state
1132 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1133 n
= CFArrayGetCount(info
);
1134 for (i
= 0; i
< n
; i
++) {
1135 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1137 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1141 /* flag this as a new configuration */
1142 *newState
= -(abs(curState
) + 1);
1146 * the configuration has changed.
1149 /* update the (/etc/appletalk.cfg) configuration file */
1150 configWrite(AT_CFG_FILE
, newConfigFile
);
1153 * update the per-service (and global) state
1155 * Note: if present, we remove any existing global state key and allow it
1156 * to be restored after the stack has been re-started.
1158 CFDictionaryApplyFunction(newDefaults
, updateDefaults
, NULL
);
1159 cache_SCDynamicStoreRemoveValue(store
, key
); // remove State:/Network/Global/AppleTalk
1160 n
= CFArrayGetCount(info
);
1161 for (i
= 0; i
< n
; i
++) {
1162 CFStringRef xKey
= CFArrayGetValueAtIndex(info
, i
);
1164 cache_SCDynamicStoreRemoveValue(store
, xKey
);
1168 /* flag this as a new configuration */
1169 *newState
= abs(curState
) + 1;
1173 CFRelease(curGlobalsX
);
1174 CFRelease(newGlobalsX
);
1178 CFRelease(curGlobals
);
1179 curGlobals
= newGlobals
;
1180 CFRelease(curConfigFile
);
1181 curConfigFile
= newConfigFile
;
1182 CFRelease(curDefaults
);
1183 curDefaults
= newDefaults
;
1184 CFRelease(curStartup
);
1185 curStartup
= newStartup
;
1188 if (info
) CFRelease(info
);
1189 if (interfaces
) CFRelease(interfaces
);
1190 if (configuredServices
) CFRelease(configuredServices
);
1191 if (setGlobals
) CFRelease(setGlobals
);
1199 #include <sysexits.h>
1200 #define AT_CMD_SUCCESS EX_OK /* success */
1201 #define AT_CMD_ALREADY_RUNNING EX__MAX + 10
1202 #define AT_CMD_NOT_RUNNING EX__MAX + 11
1212 sock
= socket(AF_APPLETALK
, SOCK_RAW
, 0);
1213 ret
= ioctl(sock
, AIOCGETSTATE
, (caddr_t
)&state
);
1216 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(AIOCGETSTATE) failed: %s"), strerror(errno
));
1220 if (state
.flags
& AT_ST_STARTED
) {
1221 return abs(curState
);
1223 return -(abs(curState
));
1228 static pid_t execCommand
= 0;
1229 static int execRetry
;
1233 stopComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1237 if (WIFEXITED(status
)) {
1238 switch (WEXITSTATUS(status
)) {
1239 case AT_CMD_SUCCESS
:
1240 case AT_CMD_NOT_RUNNING
:
1241 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown complete"));
1243 // the stack is down but we really want it up
1244 startAppleTalk(NULL
, (void *)curState
);
1253 (execRetry
> 1) ? LOG_NOTICE
: LOG_ERR
,
1254 CFSTR("AppleTalk shutdown failed, status = %d%s"),
1255 WEXITSTATUS(status
),
1256 (execRetry
> 1) ? " (retrying)" : "");
1258 // shutdown failed, retry
1259 if (--execRetry
> 0) {
1260 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1261 CFRunLoopTimerRef timer
;
1263 timer
= CFRunLoopTimerCreate(NULL
,
1264 CFAbsoluteTimeGetCurrent() + RETRY_DELAY
,
1270 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1274 // we weren't able to stop
1275 curState
= stackState();
1283 stopAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1285 char *argv
[] = { "appletalk",
1289 if (execCommand
== 0) {
1290 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown"));
1292 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk shutdown skipped, transition in progress"));
1296 execCommand
= _SCDPluginExecCommand(stopComplete
, // callback
1300 "/usr/sbin/appletalk", // path
1304 execRetry
= RETRY_LIMIT
; // initialize retry count
1312 startComplete(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
1316 if (WIFEXITED(status
)) {
1317 switch (WEXITSTATUS(status
)) {
1318 case AT_CMD_SUCCESS
:
1319 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup complete"));
1320 if ((curState
< 0) || (curState
> (int)context
)) {
1321 // the stack is now up but we really want it down
1322 stopAppleTalk(NULL
, (void *)curState
);
1325 case AT_CMD_ALREADY_RUNNING
:
1326 // the stack is already up but we're not sure
1327 // if the configuration is correct, restart
1328 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk already running, restarting"));
1329 stopAppleTalk(NULL
, (void *)curState
);
1337 (execRetry
> 1) ? LOG_NOTICE
: LOG_ERR
,
1338 CFSTR("AppleTalk startup failed, status = %d%s"),
1339 WEXITSTATUS(status
),
1340 (execRetry
> 1) ? " (retrying)" : "");
1342 // startup failed, retry
1343 if (--execRetry
> 0) {
1344 CFRunLoopTimerContext timerContext
= { 0, (void *)curState
, NULL
, NULL
, NULL
};
1345 CFRunLoopTimerRef timer
;
1347 timer
= CFRunLoopTimerCreate(NULL
,
1348 CFAbsoluteTimeGetCurrent() + RETRY_DELAY
,
1354 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1358 // we weren't able to start
1359 curState
= stackState();
1367 startAppleTalk(CFRunLoopTimerRef timer
, void *info
)
1371 char *computerName
= NULL
;
1372 char *interface
= NULL
;
1373 CFStringRef mode
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK"));
1374 CFStringRef name
= CFDictionaryGetValue(curStartup
, CFSTR("APPLETALK_HOSTNAME"));
1376 if (execCommand
== 0) {
1377 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup"));
1379 SCLog(TRUE
, LOG_NOTICE
, CFSTR("AppleTalk startup skipped, transition in progress"));
1389 argv
[argc
++] = "appletalk";
1393 computerName
= _SC_cfstring_to_cstring(name
, NULL
, 0, kCFStringEncodingASCII
);
1395 argv
[argc
++] = "-C";
1396 argv
[argc
++] = computerName
;
1398 // could not convert name
1404 if (CFEqual(mode
, CFSTR("-ROUTER-"))) {
1405 argv
[argc
++] = "-r";
1406 } else if (CFEqual(mode
, CFSTR("-MULTIHOME-"))) {
1407 argv
[argc
++] = "-x";
1409 interface
= _SC_cfstring_to_cstring(mode
, NULL
, 0, kCFStringEncodingASCII
);
1411 argv
[argc
++] = "-u";
1412 argv
[argc
++] = interface
;
1414 // could not convert interface
1419 // set non-interactive
1420 argv
[argc
++] = "-q";
1422 // close argument list
1423 argv
[argc
++] = NULL
;
1425 execCommand
= _SCDPluginExecCommand(startComplete
, // callback
1429 "/usr/sbin/appletalk", // path
1433 execRetry
= RETRY_LIMIT
; // initialize retry count
1438 if (computerName
) CFAllocatorDeallocate(NULL
, computerName
);
1439 if (interface
) CFAllocatorDeallocate(NULL
, interface
);
1446 atConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
1448 boolean_t configChanged
;
1451 configChanged
= updateConfiguration(&newState
);
1453 if (configChanged
&& (execCommand
== 0)) {
1454 // if the configuration has changed and we're not already transitioning
1457 // already running, restart [with new configuration]
1458 stopAppleTalk(NULL
, (void *)newState
);
1460 startAppleTalk(NULL
, (void *)newState
);
1464 stopAppleTalk(NULL
, (void *)newState
);
1469 curState
= newState
;
1476 stop_ATconfig(CFRunLoopSourceRef stopRls
)
1480 if (storeRls
!= NULL
) {
1481 CFRunLoopSourceInvalidate(storeRls
);
1482 CFRelease(storeRls
);
1486 if (store
!= NULL
) {
1489 CFRelease(curGlobals
);
1490 CFRelease(curConfigFile
);
1491 CFRelease(curDefaults
);
1492 CFRelease(curStartup
);
1495 CFRunLoopSourceSignal(stopRls
);
1501 load_ATconfig(CFBundleRef bundle
, Boolean bundleVerbose
)
1504 CFMutableArrayRef keys
= NULL
;
1505 CFStringRef pattern
;
1506 CFMutableArrayRef patterns
= NULL
;
1508 if (bundleVerbose
) {
1512 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
1513 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
1515 /* initialize a few globals */
1517 curGlobals
= CFDictionaryCreateMutable(NULL
,
1519 &kCFTypeDictionaryKeyCallBacks
,
1520 &kCFTypeDictionaryValueCallBacks
);
1521 curConfigFile
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1522 curDefaults
= CFDictionaryCreateMutable(NULL
,
1524 &kCFTypeDictionaryKeyCallBacks
,
1525 &kCFTypeDictionaryValueCallBacks
);
1526 curStartup
= CFDictionaryCreateMutable(NULL
,
1528 &kCFTypeDictionaryKeyCallBacks
,
1529 &kCFTypeDictionaryValueCallBacks
);
1531 /* open a "configd" store to allow cache updates */
1532 store
= SCDynamicStoreCreate(NULL
,
1533 CFSTR("AppleTalk Configuraton plug-in"),
1534 atConfigChangedCallback
,
1536 if (store
== NULL
) {
1537 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
1541 /* establish notification keys and patterns */
1543 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1544 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1546 /* ...watch for (global) AppleTalk configuration changes */
1547 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1548 kSCDynamicStoreDomainSetup
,
1549 kSCEntNetAppleTalk
);
1550 CFArrayAppendValue(keys
, key
);
1553 /* ...watch for (per-service) AppleTalk configuration changes */
1554 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1555 kSCDynamicStoreDomainSetup
,
1557 kSCEntNetAppleTalk
);
1558 CFArrayAppendValue(patterns
, pattern
);
1561 /* ...watch for network interface link status changes */
1562 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1563 kSCDynamicStoreDomainState
,
1566 CFArrayAppendValue(patterns
, pattern
);
1569 /* ...watch for (per-interface) AppleTalk configuration changes */
1570 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1571 kSCDynamicStoreDomainState
,
1573 kSCEntNetAppleTalk
);
1574 CFArrayAppendValue(patterns
, pattern
);
1577 /* ...watch for computer name changes */
1578 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
1579 CFArrayAppendValue(keys
, key
);
1582 /* register the keys/patterns */
1583 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
1584 SCLog(TRUE
, LOG_ERR
,
1585 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
1586 SCErrorString(SCError()));
1590 CFRelease(patterns
);
1592 storeRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
1594 SCLog(TRUE
, LOG_ERR
,
1595 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
1596 SCErrorString(SCError()));
1599 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRls
, kCFRunLoopDefaultMode
);
1605 if (curGlobals
) CFRelease(curGlobals
);
1606 if (curConfigFile
) CFRelease(curConfigFile
);
1607 if (curDefaults
) CFRelease(curDefaults
);
1608 if (curStartup
) CFRelease(curStartup
);
1609 if (store
) CFRelease(store
);
1610 if (keys
) CFRelease(keys
);
1611 if (patterns
) CFRelease(patterns
);
1617 #include "cfManager.c"
1619 main(int argc
, char **argv
)
1622 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1624 load_ATconfig(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);