#include <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <net/if.h>
#include <netat/appletalk.h>
#include <netat/at_var.h>
#include <AppleTalk/at_paths.h>
#include <AppleTalk/at_proto.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <SystemConfiguration/SCValidation.h>
#include "cache.h"
#include "cfManager.h"
#define HOSTCONFIG "/etc/hostconfig"
static SCDynamicStoreRef store = NULL;
static CFRunLoopSourceRef storeRls = NULL;
static int curState = 0; static CFMutableDictionaryRef curGlobals = NULL;
static CFMutableArrayRef curConfigFile = NULL;
static CFMutableDictionaryRef curDefaults = NULL;
static CFMutableDictionaryRef curStartup = NULL;
static Boolean _verbose = FALSE;
static void stopAppleTalk (CFRunLoopTimerRef timer, void *info);
static void startAppleTalk(CFRunLoopTimerRef timer, void *info);
static void
updateDefaults(const void *key, const void *val, void *context)
{
CFStringRef ifName = (CFStringRef)key;
CFDictionaryRef oldDict;
CFDictionaryRef newDict = (CFDictionaryRef)val;
CFNumberRef defaultNode;
CFNumberRef defaultNetwork;
CFStringRef defaultZone;
if (!CFDictionaryGetValueIfPresent(curDefaults, ifName, (const void **)&oldDict) ||
!CFEqual(oldDict, newDict)) {
char ifr_name[IFNAMSIZ+1];
bzero(&ifr_name, sizeof(ifr_name));
if (!_SC_cfstring_to_cstring(ifName, ifr_name, sizeof(ifr_name), kCFStringEncodingASCII)) {
SCLog(TRUE, LOG_ERR, CFSTR("could not convert interface name to C string"));
return;
}
if (CFDictionaryGetValueIfPresent(newDict,
kSCPropNetAppleTalkNetworkID,
(const void **)&defaultNetwork) &&
CFDictionaryGetValueIfPresent(newDict,
kSCPropNetAppleTalkNodeID,
(const void **)&defaultNode)
) {
struct at_addr init_address;
int status;
CFNumberGetValue(defaultNetwork, kCFNumberShortType, &init_address.s_net);
CFNumberGetValue(defaultNode, kCFNumberCharType, &init_address.s_node);
status = at_setdefaultaddr(ifr_name, &init_address);
if (status == -1) {
SCLog(TRUE, LOG_ERR, CFSTR("at_setdefaultaddr() failed"));
return;
}
}
if (CFDictionaryGetValueIfPresent(newDict,
kSCPropNetAppleTalkDefaultZone,
(const void **)&defaultZone)
) {
int status;
at_nvestr_t zone;
bzero(&zone, sizeof(zone));
if (!_SC_cfstring_to_cstring(defaultZone,
(char *)zone.str,
sizeof(zone.str),
kCFStringEncodingASCII)) {
SCLog(TRUE, LOG_ERR, CFSTR("could not convert default zone to C string"));
return;
}
zone.len = strlen((const char *)zone.str);
status = at_setdefaultzone(ifr_name, &zone);
if (status == -1) {
SCLog(TRUE, LOG_ERR, CFSTR("at_setdefaultzone() failed"));
return;
}
}
}
return;
}
static void
addZoneToPorts(const void *key, const void *val, void *context)
{
CFStringRef zone = (CFStringRef)key;
CFArrayRef ifArray = (CFArrayRef)val;
CFMutableArrayRef zones = (CFMutableArrayRef)context;
CFStringRef ifList;
CFStringRef configInfo;
ifList = CFStringCreateByCombiningStrings(NULL, ifArray, CFSTR(":"));
configInfo = CFStringCreateWithFormat(NULL, NULL, CFSTR(":%@:%@"), zone, ifList);
CFArrayAppendValue(zones, configInfo);
CFRelease(configInfo);
CFRelease(ifList);
return;
}
static CFStringRef
parse_component(CFStringRef key, CFStringRef prefix)
{
CFMutableStringRef comp;
CFRange range;
if (CFStringHasPrefix(key, prefix) == FALSE) {
return NULL;
}
comp = CFStringCreateMutableCopy(NULL, 0, key);
CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
range = CFStringFind(comp, CFSTR("/"), 0);
if (range.location == kCFNotFound) {
return comp;
}
range.length = CFStringGetLength(comp) - range.location;
CFStringDelete(comp, range);
return comp;
}
static CFDictionaryRef
entity_one(SCDynamicStoreRef store, CFStringRef key)
{
CFDictionaryRef ent_dict = NULL;
CFDictionaryRef if_dict = NULL;
CFStringRef if_key = NULL;
CFStringRef if_port;
CFMutableDictionaryRef new_dict = NULL;
static CFStringRef pre = NULL;
CFStringRef serviceID = NULL;
CFStringRef serviceType;
if (!pre) {
pre = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainSetup,
kSCCompNetwork,
kSCCompService);
}
ent_dict = cache_SCDynamicStoreCopyValue(store, key);
if (!isA_CFDictionary(ent_dict)) {
goto done;
}
serviceID = parse_component(key, pre);
if (!serviceID) {
goto done;
}
if_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
serviceID,
kSCEntNetInterface);
if_dict = cache_SCDynamicStoreCopyValue(store, if_key);
CFRelease(if_key);
if (!isA_CFDictionary(if_dict)) {
goto done;
}
serviceType = CFDictionaryGetValue(if_dict,
kSCPropNetInterfaceType);
if (!isA_CFString(serviceType) ||
!CFEqual(serviceType, kSCValNetInterfaceTypeEthernet)) {
goto done;
}
if_port = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceDeviceName);
if (!isA_CFString(if_port)) {
goto done;
}
new_dict = CFDictionaryCreateMutableCopy(NULL, 0, ent_dict);
CFDictionarySetValue(new_dict, CFSTR("ServiceID"), serviceID);
CFDictionarySetValue(new_dict, kSCPropNetInterfaceDeviceName, if_port);
done:
if (ent_dict) CFRelease(ent_dict);
if (if_dict) CFRelease(if_dict);
if (serviceID) CFRelease(serviceID);
return (CFDictionaryRef)new_dict;
}
static CFArrayRef
entity_all(SCDynamicStoreRef store, CFStringRef entity, CFArrayRef order)
{
CFMutableArrayRef defined = NULL;
CFIndex i;
CFIndex n;
CFMutableArrayRef ordered = NULL;
CFStringRef pattern;
ordered = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
entity);
defined = (CFMutableArrayRef)SCDynamicStoreCopyKeyList(store, pattern);
CFRelease(pattern);
if (defined && (CFArrayGetCount(defined) > 0)) {
CFArrayRef tmp;
tmp = defined;
defined = CFArrayCreateMutableCopy(NULL, 0, tmp);
CFRelease(tmp);
} else {
goto done;
}
n = order ? CFArrayGetCount(order) : 0;
for (i = 0; i < n; i++) {
CFDictionaryRef dict;
CFStringRef key;
CFIndex j;
CFStringRef service;
service = CFArrayGetValueAtIndex(order, i);
if (!isA_CFString(service)) {
continue;
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
service,
entity);
dict = entity_one(store, key);
if (dict) {
CFArrayAppendValue(ordered, dict);
CFRelease(dict);
}
j = CFArrayGetFirstIndexOfValue(defined,
CFRangeMake(0, CFArrayGetCount(defined)),
key);
if (j != kCFNotFound) {
CFArrayRemoveValueAtIndex(defined, j);
}
CFRelease(key);
}
n = CFArrayGetCount(defined);
for (i = 0; i < n; i++) {
CFDictionaryRef dict;
CFStringRef key;
key = CFArrayGetValueAtIndex(defined, i);
dict = entity_one(store, key);
if (dict) {
CFArrayAppendValue(ordered, dict);
CFRelease(dict);
}
}
done:
if (defined) CFRelease(defined);
if (CFArrayGetCount(ordered) == 0) {
CFRelease(ordered);
ordered = NULL;
}
return ordered;
}
static void
encodeName(CFStringRef name,
CFStringEncoding encoding,
CFMutableDictionaryRef startup,
CFMutableDictionaryRef globals)
{
CFDataRef bytes;
CFMutableStringRef encodedName = NULL;
CFIndex len;
if (!isA_CFString(name)) {
return;
}
if (encoding == kCFStringEncodingASCII) {
encodedName = (CFMutableStringRef)CFStringCreateCopy(NULL, name);
goto done;
}
bytes = CFStringCreateExternalRepresentation(NULL,
name,
encoding,
0);
if (bytes) {
unsigned char *byte;
CFIndex i;
if (encoding == kCFStringEncodingMacRoman) {
CFDataRef ascii;
ascii = CFStringCreateExternalRepresentation(NULL,
name,
kCFStringEncodingASCII,
0);
if (ascii) {
CFRelease(ascii);
CFRelease(bytes);
encodedName = (CFMutableStringRef)CFStringCreateCopy(NULL, name);
goto done;
}
}
encodedName = CFStringCreateMutable(NULL, 0);
len = CFDataGetLength(bytes);
byte = (unsigned char *)CFDataGetBytePtr(bytes);
for (i = 0; i < len; i++, byte++) {
CFStringAppendFormat(encodedName,
NULL,
CFSTR("%02x"),
*byte);
}
CFStringInsert(encodedName, 0, CFSTR("*"));
CFStringAppend(encodedName, CFSTR("*"));
CFRelease(bytes);
}
done :
if (encodedName) {
if (startup) {
CFDictionaryAddValue(startup, CFSTR("APPLETALK_HOSTNAME"), encodedName);
}
if (globals) {
CFNumberRef num;
num = CFNumberCreate(NULL, kCFNumberIntType, &encoding);
CFDictionaryAddValue(globals, kSCPropNetAppleTalkComputerName, name);
CFDictionaryAddValue(globals, kSCPropNetAppleTalkComputerNameEncoding, num);
CFRelease(num);
}
CFRelease(encodedName);
}
return;
}
static boolean_t
updateConfiguration(int *newState)
{
boolean_t changed = FALSE;
CFStringRef computerName;
CFStringEncoding computerNameEncoding;
CFArrayRef configuredServices = NULL;
CFDictionaryRef dict;
CFIndex i;
CFIndex ifCount = 0;
CFMutableArrayRef info = NULL;
CFArrayRef interfaces = NULL;
CFStringRef key;
CFArrayRef keys;
CFIndex n;
CFMutableArrayRef newConfigFile;
CFMutableDictionaryRef newDefaults;
CFMutableDictionaryRef newDict;
CFMutableDictionaryRef newGlobals;
CFMutableDictionaryRef newGlobalsX;
CFMutableDictionaryRef newStartup;
CFMutableDictionaryRef newZones;
CFNumberRef num;
CFMutableDictionaryRef curGlobalsX;
CFStringRef pattern;
boolean_t postGlobals = FALSE;
CFStringRef primaryPort = NULL;
CFStringRef primaryZone = NULL;
CFArrayRef serviceOrder = NULL;
CFDictionaryRef setGlobals = NULL;
cache_open();
*newState = curState;
newConfigFile = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
newGlobals = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newDefaults = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newStartup = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newZones = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-NO-"));
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetAppleTalk);
setGlobals = cache_SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (setGlobals) {
if (isA_CFDictionary(setGlobals)) {
serviceOrder = CFDictionaryGetValue(setGlobals,
kSCPropNetServiceOrder);
serviceOrder = isA_CFArray(serviceOrder);
if (serviceOrder) {
CFRetain(serviceOrder);
}
} else {
CFRelease(setGlobals);
setGlobals = NULL;
}
}
if (!serviceOrder) {
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
dict = cache_SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict) {
if (isA_CFDictionary(dict)) {
serviceOrder = CFDictionaryGetValue(dict,
kSCPropNetServiceOrder);
serviceOrder = isA_CFArray(serviceOrder);
if (serviceOrder) {
CFRetain(serviceOrder);
}
}
CFRelease(dict);
}
}
configuredServices = entity_all(store, kSCEntNetAppleTalk, serviceOrder);
if (configuredServices) {
ifCount = CFArrayGetCount(configuredServices);
}
if (serviceOrder) CFRelease(serviceOrder);
key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
dict = cache_SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict) {
if (isA_CFDictionary(dict)) {
interfaces = CFDictionaryGetValue(dict,
kSCDynamicStorePropNetInterfaces);
interfaces = isA_CFArray(interfaces);
if (interfaces) {
CFRetain(interfaces);
}
}
CFRelease(dict);
}
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetAppleTalk);
keys = SCDynamicStoreCopyKeyList(store, pattern);
CFRelease(pattern);
if (keys) {
info = CFArrayCreateMutableCopy(NULL, 0, keys);
CFRelease(keys);
}
for (i = 0; i < ifCount; i++) {
CFDictionaryRef service;
CFStringRef ifName;
CFStringRef configMethod;
CFMutableStringRef portConfig = NULL;
CFArrayRef networkRange;
int sNetwork;
int eNetwork;
CFArrayRef zoneList;
CFIndex zCount;
CFIndex j;
CFMutableDictionaryRef ifDefaults = NULL;
CFNumberRef defaultNetwork;
CFNumberRef defaultNode;
CFStringRef defaultZone;
service = CFArrayGetValueAtIndex(configuredServices, i);
ifName = CFDictionaryGetValue(service, kSCPropNetInterfaceDeviceName);
if (!interfaces ||
!CFArrayContainsValue(interfaces, CFRangeMake(0, CFArrayGetCount(interfaces)), ifName)) {
goto nextIF;
}
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
ifName,
kSCEntNetLink);
dict = cache_SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict) {
Boolean linkStatus = TRUE;
Boolean ifDetaching = FALSE;
if (isA_CFDictionary(dict)) {
CFBooleanRef bVal;
bVal = CFDictionaryGetValue(dict, kSCPropNetLinkActive);
if (isA_CFBoolean(bVal)) {
linkStatus = CFBooleanGetValue(bVal);
}
ifDetaching = CFDictionaryContainsKey(dict, kSCPropNetLinkDetaching);
}
CFRelease(dict);
if (!linkStatus || ifDetaching) {
goto nextIF;
}
}
configMethod = CFDictionaryGetValue(service, kSCPropNetAppleTalkConfigMethod);
if (!isA_CFString(configMethod)) {
goto nextIF;
}
if (!CFEqual(configMethod, kSCValNetAppleTalkConfigMethodNode ) &&
!CFEqual(configMethod, kSCValNetAppleTalkConfigMethodRouter ) &&
!CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("Unexpected AppleTalk ConfigMethod: %@"),
configMethod);
goto nextIF;
}
if (CFArrayGetCount(newConfigFile) == 0) {
CFDictionaryRef active;
CFDictionarySetValue(newGlobals,
kSCDynamicStorePropNetPrimaryService,
CFDictionaryGetValue(service, CFSTR("ServiceID")));
CFDictionarySetValue(newGlobals,
kSCDynamicStorePropNetPrimaryInterface,
ifName);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
ifName,
kSCEntNetAppleTalk);
active = cache_SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (active) {
if (isA_CFDictionary(active)) {
postGlobals = TRUE;
}
CFRelease(active);
}
}
portConfig = CFStringCreateMutable(NULL, 0);
CFStringAppendFormat(portConfig, NULL, CFSTR("%@:"), ifName);
if (CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
CFNumberRef num;
networkRange = CFDictionaryGetValue(service,
kSCPropNetAppleTalkSeedNetworkRange);
if (!isA_CFArray(networkRange) || (CFArrayGetCount(networkRange) == 0)) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("AppleTalk configuration error (%@)"),
kSCPropNetAppleTalkSeedNetworkRange);
goto nextIF;
}
num = CFArrayGetValueAtIndex(networkRange, 0);
if (!isA_CFNumber(num)) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("AppleTalk configuration error (%@)"),
kSCPropNetAppleTalkSeedNetworkRange);
goto nextIF;
}
CFNumberGetValue(num, kCFNumberIntType, &sNetwork);
eNetwork = sNetwork;
if (CFArrayGetCount(networkRange) > 1) {
num = CFArrayGetValueAtIndex(networkRange, 1);
if (!isA_CFNumber(num)) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("AppleTalk configuration error (%@)"),
kSCPropNetAppleTalkSeedNetworkRange);
goto nextIF;
}
CFNumberGetValue(num, kCFNumberIntType, &eNetwork);
}
CFStringAppendFormat(portConfig, NULL, CFSTR("%d:%d:"), sNetwork, eNetwork);
zoneList = CFDictionaryGetValue(service,
kSCPropNetAppleTalkSeedZones);
if (!isA_CFArray(zoneList)) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("AppleTalk configuration error (%@)"),
kSCPropNetAppleTalkSeedZones);
goto nextIF;
}
zCount = CFArrayGetCount(zoneList);
for (j = 0; j < zCount; j++) {
CFStringRef zone;
CFArrayRef ifList;
CFMutableArrayRef newIFList;
zone = CFArrayGetValueAtIndex(zoneList, j);
if (!isA_CFString(zone)) {
continue;
}
if (CFDictionaryGetValueIfPresent(newZones, zone, (const void **)&ifList)) {
newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList);
} else {
newIFList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(newIFList, ifName);
CFArraySortValues(newIFList,
CFRangeMake(0, CFArrayGetCount(newIFList)),
(CFComparatorFunction)CFStringCompare,
NULL);
CFDictionarySetValue(newZones, zone, newIFList);
CFRelease(newIFList);
if (!primaryZone) {
primaryZone = CFRetain(zone);
}
}
if (!primaryZone) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("AppleTalk configuration error (%@)"),
kSCPropNetAppleTalkSeedZones);
goto nextIF;
}
}
computerName = CFDictionaryGetValue(service,
kSCPropNetAppleTalkComputerName);
if (CFDictionaryGetValueIfPresent(service,
kSCPropNetAppleTalkComputerNameEncoding,
(const void **)&num) &&
isA_CFNumber(num)) {
CFNumberGetValue(num, kCFNumberIntType, &computerNameEncoding);
} else {
computerNameEncoding = CFStringGetSystemEncoding();
}
encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
if (CFArrayGetCount(newConfigFile) == 0) {
CFStringAppend(portConfig, CFSTR("*"));
primaryPort = CFRetain(ifName);
}
CFArrayAppendValue(newConfigFile, portConfig);
ifDefaults = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
defaultNetwork = CFDictionaryGetValue(service, kSCPropNetAppleTalkNetworkID);
defaultNode = CFDictionaryGetValue(service, kSCPropNetAppleTalkNodeID);
if (isA_CFNumber(defaultNetwork) && isA_CFNumber(defaultNode)) {
CFDictionarySetValue(ifDefaults,
kSCPropNetAppleTalkNetworkID,
defaultNetwork);
CFDictionarySetValue(ifDefaults,
kSCPropNetAppleTalkNodeID,
defaultNode);
}
if ((CFDictionaryGetValueIfPresent(service,
kSCPropNetAppleTalkDefaultZone,
(const void **)&defaultZone) == TRUE)) {
CFDictionarySetValue(ifDefaults,
kSCPropNetAppleTalkDefaultZone,
defaultZone);
}
CFDictionarySetValue(newDefaults, ifName, ifDefaults);
CFRelease(ifDefaults);
switch (CFArrayGetCount(newConfigFile)) {
case 1:
CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), ifName);
break;
case 2:
if (!CFEqual(CFDictionaryGetValue(newStartup, CFSTR("APPLETALK")),
CFSTR("-ROUTER-"))) {
CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-MULTIHOME-"));
}
break;
}
if (CFEqual(configMethod, kSCValNetAppleTalkConfigMethodRouter) ||
CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-ROUTER-"));
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
CFDictionaryGetValue(service, CFSTR("ServiceID")),
kSCEntNetAppleTalk);
newDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(newDict, kSCPropInterfaceName, ifName);
cache_SCDynamicStoreSetValue(store, key, newDict);
CFRelease(newDict);
if (info) {
j = CFArrayGetFirstIndexOfValue(info,
CFRangeMake(0, CFArrayGetCount(info)),
key);
if (j != kCFNotFound) {
CFArrayRemoveValueAtIndex(info, j);
}
}
CFRelease(key);
nextIF :
if (portConfig) CFRelease(portConfig);
}
if (primaryZone) {
CFArrayRef ifList;
CFMutableArrayRef newIFList;
ifList = CFDictionaryGetValue(newZones, primaryZone);
if (CFArrayContainsValue(ifList,
CFRangeMake(0, CFArrayGetCount(ifList)),
primaryPort)) {
newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList);
CFArrayAppendValue(newIFList, CFSTR("*"));
CFDictionarySetValue(newZones, primaryZone, newIFList);
CFRelease(newIFList);
}
CFRelease(primaryZone);
}
if (primaryPort) {
CFRelease(primaryPort);
}
i = CFArrayGetCount(newConfigFile);
CFArraySortValues(newConfigFile,
CFRangeMake(0, i),
(CFComparatorFunction)CFStringCompare,
NULL);
CFDictionaryApplyFunction(newZones, addZoneToPorts, newConfigFile);
CFRelease(newZones);
CFArraySortValues(newConfigFile,
CFRangeMake(i, CFArrayGetCount(newConfigFile)-i),
(CFComparatorFunction)CFStringCompare,
NULL);
CFArrayAppendValue(newConfigFile, CFSTR(""));
if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME")) &&
(setGlobals != NULL)) {
computerName = CFDictionaryGetValue(setGlobals,
kSCPropNetAppleTalkComputerName);
if (CFDictionaryGetValueIfPresent(setGlobals,
kSCPropNetAppleTalkComputerNameEncoding,
(const void **)&num) &&
isA_CFNumber(num)) {
CFNumberGetValue(num, kCFNumberIntType, &computerNameEncoding);
} else {
computerNameEncoding = CFStringGetSystemEncoding();
}
encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
}
if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME"))) {
computerName = SCDynamicStoreCopyComputerName(store, &computerNameEncoding);
if (computerName) {
encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
CFRelease(computerName);
}
}
if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME"))) {
struct utsname name;
if (uname(&name) == 0) {
computerName = CFStringCreateWithCString(NULL, name.nodename, kCFStringEncodingASCII);
if (computerName) {
encodeName(computerName, kCFStringEncodingASCII, NULL, newGlobals);
CFRelease(computerName);
}
}
}
curGlobalsX = CFDictionaryCreateMutableCopy(NULL, 0, curGlobals);
CFDictionaryRemoveValue(curGlobalsX, kSCDynamicStorePropNetPrimaryService);
newGlobalsX = CFDictionaryCreateMutableCopy(NULL, 0, newGlobals);
CFDictionaryRemoveValue(newGlobalsX, kSCDynamicStorePropNetPrimaryService);
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetAppleTalk);
if (CFEqual(curGlobalsX , newGlobalsX ) &&
CFEqual(curConfigFile , newConfigFile) &&
CFEqual(curDefaults , newDefaults ) &&
CFEqual(curStartup , newStartup )
) {
if (postGlobals) {
if (!SCDynamicStoreSetValue(store, key, newGlobals)) {
SCLog(TRUE,
LOG_ERR,
CFSTR("SCDynamicStoreSetValue() failed: %s"),
SCErrorString(SCError()));
}
}
CFRelease(newGlobals);
CFRelease(newConfigFile);
CFRelease(newDefaults);
CFRelease(newStartup);
} else if (CFArrayGetCount(newConfigFile) <= 1) {
(void)unlink(AT_CFG_FILE);
cache_SCDynamicStoreRemoveValue(store, key); n = CFArrayGetCount(info);
for (i = 0; i < n; i++) {
CFStringRef xKey = CFArrayGetValueAtIndex(info, i);
cache_SCDynamicStoreRemoveValue(store, xKey);
}
cache_write(store);
*newState = -(abs(curState) + 1);
changed = TRUE;
} else {
configWrite(AT_CFG_FILE, newConfigFile);
CFDictionaryApplyFunction(newDefaults, updateDefaults, NULL);
cache_SCDynamicStoreRemoveValue(store, key); n = CFArrayGetCount(info);
for (i = 0; i < n; i++) {
CFStringRef xKey = CFArrayGetValueAtIndex(info, i);
cache_SCDynamicStoreRemoveValue(store, xKey);
}
cache_write(store);
*newState = abs(curState) + 1;
changed = TRUE;
}
CFRelease(curGlobalsX);
CFRelease(newGlobalsX);
CFRelease(key);
if (changed) {
CFRelease(curGlobals);
curGlobals = newGlobals;
CFRelease(curConfigFile);
curConfigFile = newConfigFile;
CFRelease(curDefaults);
curDefaults = newDefaults;
CFRelease(curStartup);
curStartup = newStartup;
}
if (info) CFRelease(info);
if (interfaces) CFRelease(interfaces);
if (configuredServices) CFRelease(configuredServices);
if (setGlobals) CFRelease(setGlobals);
cache_close();
return changed;
}
#include <sysexits.h>
#define AT_CMD_SUCCESS EX_OK
#define AT_CMD_ALREADY_RUNNING EX__MAX + 10
#define AT_CMD_NOT_RUNNING EX__MAX + 11
static int
stackState()
{
int ret;
int sock;
at_state_t state;
sock = socket(AF_APPLETALK, SOCK_RAW, 0);
ret = ioctl(sock, AIOCGETSTATE, (caddr_t)&state);
(void)close(sock);
if (ret == -1) {
SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(AIOCGETSTATE) failed: %s"), strerror(errno));
return FALSE;
}
if (state.flags & AT_ST_STARTED) {
return abs(curState);
} else {
return -(abs(curState));
}
}
static pid_t execCommand = 0;
static int execRetry;
static void
stopComplete(pid_t pid, int status, struct rusage *rusage, void *context)
{
execCommand = 0;
if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case AT_CMD_SUCCESS :
case AT_CMD_NOT_RUNNING :
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown complete"));
if (curState > 0) {
startAppleTalk(NULL, (void *)curState);
}
return;
default :
break;
}
}
SCLog(TRUE, LOG_ERR,
CFSTR("AppleTalk shutdown failed, status = %d%s"),
WEXITSTATUS(status),
(execRetry > 1) ? " (retrying)" : "");
if (--execRetry > 0) {
CFRunLoopTimerContext timerContext = { 0, (void *)curState, NULL, NULL, NULL };
CFRunLoopTimerRef timer;
timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + 1.0,
0.0,
0,
0,
stopAppleTalk,
&timerContext);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
return;
} else {
curState = stackState();
}
return;
}
static void
stopAppleTalk(CFRunLoopTimerRef timer, void *info)
{
char *argv[] = { "appletalk",
"-d",
NULL };
if (execCommand == 0) {
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown"));
} else {
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown skipped, transition in progress"));
return;
}
execCommand = _SCDPluginExecCommand(stopComplete, info, 0, 0, "/usr/sbin/appletalk", argv);
if (!timer) {
execRetry = 5; }
return;
}
static void
startComplete(pid_t pid, int status, struct rusage *rusage, void *context)
{
execCommand = 0;
if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case AT_CMD_SUCCESS :
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup complete"));
if ((curState < 0) || (curState > (int)context)) {
stopAppleTalk(NULL, (void *)curState);
}
return;
case AT_CMD_ALREADY_RUNNING :
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk already running, restarting"));
stopAppleTalk(NULL, (void *)curState);
return;
default :
break;
}
}
SCLog(TRUE, LOG_ERR,
CFSTR("AppleTalk startup failed, status = %d%s"),
WEXITSTATUS(status),
(execRetry > 1) ? " (retrying)" : "");
if (--execRetry > 0) {
CFRunLoopTimerContext timerContext = { 0, (void *)curState, NULL, NULL, NULL };
CFRunLoopTimerRef timer;
timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + 1.0,
0.0,
0,
0,
startAppleTalk,
&timerContext);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
return;
} else {
curState = stackState();
}
return;
}
static void
startAppleTalk(CFRunLoopTimerRef timer, void *info)
{
int argc = 0;
char *argv[8];
char *computerName = NULL;
char *interface = NULL;
CFStringRef mode = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK"));
CFStringRef name = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK_HOSTNAME"));
if (execCommand == 0) {
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup"));
} else {
SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup skipped, transition in progress"));
return;
}
if (!mode) {
return;
}
argv[argc++] = "appletalk";
if (name) {
computerName = _SC_cfstring_to_cstring(name, NULL, 0, kCFStringEncodingASCII);
if (computerName) {
argv[argc++] = "-C";
argv[argc++] = computerName;
} else {
goto done;
}
}
if (CFEqual(mode, CFSTR("-ROUTER-"))) {
argv[argc++] = "-r";
} else if (CFEqual(mode, CFSTR("-MULTIHOME-"))) {
argv[argc++] = "-x";
} else {
interface = _SC_cfstring_to_cstring(mode, NULL, 0, kCFStringEncodingASCII);
if (interface) {
argv[argc++] = "-u";
argv[argc++] = interface;
} else {
goto done;
}
}
argv[argc++] = "-q";
argv[argc++] = NULL;
execCommand = _SCDPluginExecCommand(startComplete, info, 0, 0, "/usr/sbin/appletalk", argv);
if (!timer) {
execRetry = 5; }
done :
if (computerName) CFAllocatorDeallocate(NULL, computerName);
if (interface) CFAllocatorDeallocate(NULL, interface);
return;
}
static void
atConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
{
boolean_t configChanged;
int newState;
configChanged = updateConfiguration(&newState);
if (configChanged && (execCommand == 0)) {
if (newState > 0) {
if (curState > 0) {
stopAppleTalk(NULL, (void *)newState);
} else {
startAppleTalk(NULL, (void *)newState);
}
} else {
if (curState > 0) {
stopAppleTalk(NULL, (void *)newState);
}
}
}
curState = newState;
return;
}
void
stop_ATconfig(CFRunLoopSourceRef stopRls)
{
if (storeRls != NULL) {
CFRunLoopSourceInvalidate(storeRls);
CFRelease(storeRls);
storeRls = NULL;
}
if (store != NULL) {
CFRelease(store);
store = NULL;
CFRelease(curGlobals);
CFRelease(curConfigFile);
CFRelease(curDefaults);
CFRelease(curStartup);
}
CFRunLoopSourceSignal(stopRls);
return;
}
void
load_ATconfig(CFBundleRef bundle, Boolean bundleVerbose)
{
CFStringRef key;
CFMutableArrayRef keys = NULL;
CFStringRef pattern;
CFMutableArrayRef patterns = NULL;
if (bundleVerbose) {
_verbose = TRUE;
}
SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
curGlobals = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
curConfigFile = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
curDefaults = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
curStartup = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
store = SCDynamicStoreCreate(NULL,
CFSTR("AppleTalk Configuraton plug-in"),
atConfigChangedCallback,
NULL);
if (!store) {
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
goto error;
}
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetAppleTalk);
CFArrayAppendValue(keys, key);
CFRelease(key);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetAppleTalk);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetLink);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetAppleTalk);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
key = SCDynamicStoreKeyCreateComputerName(NULL);
CFArrayAppendValue(keys, key);
CFRelease(key);
if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
SCErrorString(SCError()));
goto error;
}
CFRelease(keys);
CFRelease(patterns);
storeRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (!storeRls) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
SCErrorString(SCError()));
goto error;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRls, kCFRunLoopDefaultMode);
return;
error :
if (curGlobals) CFRelease(curGlobals);
if (curConfigFile) CFRelease(curConfigFile);
if (curDefaults) CFRelease(curDefaults);
if (curStartup) CFRelease(curStartup);
if (store) CFRelease(store);
if (keys) CFRelease(keys);
if (patterns) CFRelease(patterns);
return;
}
#ifdef MAIN
#include "cfManager.c"
int
main(int argc, char **argv)
{
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
load_ATconfig(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
CFRunLoopRun();
exit(0);
return 0;
}
#endif