]> git.saurik.com Git - apple/configd.git/blob - Plugins/ATconfig/atconfig.c
configd-136.1.tar.gz
[apple/configd.git] / Plugins / ATconfig / atconfig.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * 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
30 * active.
31 *
32 * April 29, 2002 Allan Nathanson <ajn@apple.com>
33 * - add global state information (primary service, interface)
34 *
35 * June 24, 2001 Allan Nathanson <ajn@apple.com>
36 * - update to public SystemConfiguration.framework APIs
37 *
38 * July 7, 2000 Allan Nathanson <ajn@apple.com>
39 * - initial revision
40 */
41
42
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <sys/utsname.h>
49 #include <sys/wait.h>
50 #include <net/if.h>
51 #include <netat/appletalk.h>
52 #include <netat/at_var.h>
53 #include <AppleTalk/at_paths.h>
54 #include <AppleTalk/at_proto.h>
55
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>
61
62 #include "cache.h"
63 #include "cfManager.h"
64
65 #define HOSTCONFIG "/etc/hostconfig"
66
67 static SCDynamicStoreRef store = NULL;
68 static CFRunLoopSourceRef storeRls = NULL;
69
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;
75
76 static Boolean _verbose = FALSE;
77
78
79 static void stopAppleTalk (CFRunLoopTimerRef timer, void *info);
80 static void startAppleTalk(CFRunLoopTimerRef timer, void *info);
81
82
83 static void
84 updateDefaults(const void *key, const void *val, void *context)
85 {
86 CFStringRef ifName = (CFStringRef)key;
87 CFDictionaryRef oldDict;
88 CFDictionaryRef newDict = (CFDictionaryRef)val;
89 CFNumberRef defaultNode;
90 CFNumberRef defaultNetwork;
91 CFStringRef defaultZone;
92
93 if (!CFDictionaryGetValueIfPresent(curDefaults, ifName, (const void **)&oldDict) ||
94 !CFEqual(oldDict, newDict)) {
95 char ifr_name[IFNAMSIZ+1];
96
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"));
100 return;
101 }
102
103 /*
104 * Set preferred Network and Node ID
105 */
106 if (CFDictionaryGetValueIfPresent(newDict,
107 kSCPropNetAppleTalkNetworkID,
108 (const void **)&defaultNetwork) &&
109 CFDictionaryGetValueIfPresent(newDict,
110 kSCPropNetAppleTalkNodeID,
111 (const void **)&defaultNode)
112 ) {
113 struct at_addr init_address;
114 int status;
115
116 /*
117 * set the default node and network
118 */
119 CFNumberGetValue(defaultNetwork, kCFNumberShortType, &init_address.s_net);
120 CFNumberGetValue(defaultNode, kCFNumberCharType, &init_address.s_node);
121 status = at_setdefaultaddr(ifr_name, &init_address);
122 if (status == -1) {
123 SCLog(TRUE, LOG_ERR, CFSTR("at_setdefaultaddr() failed"));
124 return;
125 }
126 }
127
128 /*
129 * Set default zone
130 */
131 if (CFDictionaryGetValueIfPresent(newDict,
132 kSCPropNetAppleTalkDefaultZone,
133 (const void **)&defaultZone)
134 ) {
135 int status;
136 at_nvestr_t zone;
137
138 /*
139 * set the "default zone" for this interface
140 */
141 bzero(&zone, sizeof(zone));
142 if (!_SC_cfstring_to_cstring(defaultZone,
143 (char *)zone.str,
144 sizeof(zone.str),
145 kCFStringEncodingASCII)) {
146 SCLog(TRUE, LOG_ERR, CFSTR("could not convert default zone to C string"));
147 return;
148 }
149
150 zone.len = strlen((const char *)zone.str);
151 status = at_setdefaultzone(ifr_name, &zone);
152 if (status == -1) {
153 SCLog(TRUE, LOG_ERR, CFSTR("at_setdefaultzone() failed"));
154 return;
155 }
156 }
157 }
158
159 return;
160 }
161
162
163 static void
164 addZoneToPorts(const void *key, const void *val, void *context)
165 {
166 CFStringRef zone = (CFStringRef)key;
167 CFArrayRef ifArray = (CFArrayRef)val;
168 CFMutableArrayRef zones = (CFMutableArrayRef)context;
169 CFStringRef ifList;
170 CFStringRef configInfo;
171
172 ifList = CFStringCreateByCombiningStrings(NULL, ifArray, CFSTR(":"));
173 configInfo = CFStringCreateWithFormat(NULL, NULL, CFSTR(":%@:%@"), zone, ifList);
174 CFArrayAppendValue(zones, configInfo);
175 CFRelease(configInfo);
176 CFRelease(ifList);
177 return;
178 }
179
180
181 /*
182 * Function: parse_component
183 * Purpose:
184 * Given a string 'key' and a string prefix 'prefix',
185 * return the next component in the slash '/' separated
186 * key.
187 *
188 * Examples:
189 * 1. key = "a/b/c" prefix = "a/"
190 * returns "b"
191 * 2. key = "a/b/c" prefix = "a/b/"
192 * returns "c"
193 */
194 static CFStringRef
195 parse_component(CFStringRef key, CFStringRef prefix)
196 {
197 CFMutableStringRef comp;
198 CFRange range;
199
200 if (CFStringHasPrefix(key, prefix) == FALSE) {
201 return NULL;
202 }
203 comp = CFStringCreateMutableCopy(NULL, 0, key);
204 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
205 range = CFStringFind(comp, CFSTR("/"), 0);
206 if (range.location == kCFNotFound) {
207 return comp;
208 }
209 range.length = CFStringGetLength(comp) - range.location;
210 CFStringDelete(comp, range);
211 return comp;
212 }
213
214
215 static CFDictionaryRef
216 entity_one(SCDynamicStoreRef store, CFStringRef key)
217 {
218 CFDictionaryRef ent_dict = NULL;
219 CFDictionaryRef if_dict = NULL;
220 CFStringRef if_key = NULL;
221 CFStringRef if_port;
222 CFMutableDictionaryRef new_dict = NULL;
223 static CFStringRef pre = NULL;
224 CFStringRef serviceID = NULL;
225 CFStringRef serviceType;
226
227 if (!pre) {
228 pre = SCDynamicStoreKeyCreate(NULL,
229 CFSTR("%@/%@/%@/"),
230 kSCDynamicStoreDomainSetup,
231 kSCCompNetwork,
232 kSCCompService);
233 }
234
235 /*
236 * get entity dictionary for service
237 */
238 ent_dict = cache_SCDynamicStoreCopyValue(store, key);
239 if (!isA_CFDictionary(ent_dict)) {
240 goto done;
241 }
242
243 /*
244 * get interface dictionary for service
245 */
246 serviceID = parse_component(key, pre);
247 if (!serviceID) {
248 goto done;
249 }
250
251 if_key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
252 kSCDynamicStoreDomainSetup,
253 serviceID,
254 kSCEntNetInterface);
255 if_dict = cache_SCDynamicStoreCopyValue(store, if_key);
256 CFRelease(if_key);
257 if (!isA_CFDictionary(if_dict)) {
258 goto done;
259 }
260
261 /* check the interface type */
262 serviceType = CFDictionaryGetValue(if_dict,
263 kSCPropNetInterfaceType);
264 if (!isA_CFString(serviceType) ||
265 !CFEqual(serviceType, kSCValNetInterfaceTypeEthernet)) {
266 /* sorry, no AT networking on this interface */
267 goto done;
268 }
269
270 /*
271 * get port name (from interface dictionary).
272 */
273 if_port = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceDeviceName);
274 if (!isA_CFString(if_port)) {
275 goto done;
276 }
277
278 /*
279 * add ServiceID and interface port name to entity dictionary.
280 */
281 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, ent_dict);
282 CFDictionarySetValue(new_dict, CFSTR("ServiceID"), serviceID);
283 CFDictionarySetValue(new_dict, kSCPropNetInterfaceDeviceName, if_port);
284
285 done:
286
287 if (ent_dict) CFRelease(ent_dict);
288 if (if_dict) CFRelease(if_dict);
289 if (serviceID) CFRelease(serviceID);
290 return (CFDictionaryRef)new_dict;
291 }
292
293
294 static CFArrayRef
295 entity_all(SCDynamicStoreRef store, CFStringRef entity, CFArrayRef order)
296 {
297 CFMutableArrayRef defined = NULL;
298 CFIndex i;
299 CFIndex n;
300 CFMutableArrayRef ordered = NULL;
301 CFStringRef pattern;
302
303 ordered = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
304
305 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
306 kSCDynamicStoreDomainSetup,
307 kSCCompAnyRegex,
308 entity);
309 defined = (CFMutableArrayRef)SCDynamicStoreCopyKeyList(store, pattern);
310 CFRelease(pattern);
311 if (defined && (CFArrayGetCount(defined) > 0)) {
312 CFArrayRef tmp;
313
314 tmp = defined;
315 defined = CFArrayCreateMutableCopy(NULL, 0, tmp);
316 CFRelease(tmp);
317 } else {
318 goto done;
319 }
320
321 n = order ? CFArrayGetCount(order) : 0;
322 for (i = 0; i < n; i++) {
323 CFDictionaryRef dict;
324 CFStringRef key;
325 CFIndex j;
326 CFStringRef service;
327
328 service = CFArrayGetValueAtIndex(order, i);
329 if (!isA_CFString(service)) {
330 continue;
331 }
332
333 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
334 kSCDynamicStoreDomainSetup,
335 service,
336 entity);
337 dict = entity_one(store, key);
338 if (dict) {
339 CFArrayAppendValue(ordered, dict);
340 CFRelease(dict);
341 }
342
343 j = CFArrayGetFirstIndexOfValue(defined,
344 CFRangeMake(0, CFArrayGetCount(defined)),
345 key);
346 if (j != kCFNotFound) {
347 CFArrayRemoveValueAtIndex(defined, j);
348 }
349
350 CFRelease(key);
351 }
352
353 n = CFArrayGetCount(defined);
354 for (i = 0; i < n; i++) {
355 CFDictionaryRef dict;
356 CFStringRef key;
357
358 key = CFArrayGetValueAtIndex(defined, i);
359 dict = entity_one(store, key);
360 if (dict) {
361 CFArrayAppendValue(ordered, dict);
362 CFRelease(dict);
363 }
364 }
365
366 done:
367
368 if (defined) CFRelease(defined);
369 if (CFArrayGetCount(ordered) == 0) {
370 CFRelease(ordered);
371 ordered = NULL;
372 }
373 return ordered;
374 }
375
376
377 static void
378 encodeName(CFStringRef name,
379 CFStringEncoding encoding,
380 CFMutableDictionaryRef startup,
381 CFMutableDictionaryRef globals)
382 {
383 CFDataRef bytes;
384 CFMutableStringRef encodedName = NULL;
385 CFIndex len;
386
387 if (!isA_CFString(name)) {
388 return;
389 }
390
391 if (encoding == kCFStringEncodingASCII) {
392 encodedName = (CFMutableStringRef)CFStringCreateCopy(NULL, name);
393 goto done;
394 }
395
396 /*
397 * encode the potentially non-printable string
398 */
399 bytes = CFStringCreateExternalRepresentation(NULL,
400 name,
401 encoding,
402 0);
403 if (bytes) {
404 unsigned char *byte;
405 CFIndex i;
406
407 /*
408 * check if the MacRoman string can be represented as ASCII
409 */
410 if (encoding == kCFStringEncodingMacRoman) {
411 CFDataRef ascii;
412
413 ascii = CFStringCreateExternalRepresentation(NULL,
414 name,
415 kCFStringEncodingASCII,
416 0);
417 if (ascii) {
418 CFRelease(ascii);
419 CFRelease(bytes);
420 encodedName = (CFMutableStringRef)CFStringCreateCopy(NULL, name);
421 goto done;
422 }
423 }
424
425 encodedName = CFStringCreateMutable(NULL, 0);
426
427 len = CFDataGetLength(bytes);
428 byte = (unsigned char *)CFDataGetBytePtr(bytes);
429 for (i = 0; i < len; i++, byte++) {
430 CFStringAppendFormat(encodedName,
431 NULL,
432 CFSTR("%02x"),
433 *byte);
434 }
435
436 /*
437 * add "encoded string" markers
438 */
439 CFStringInsert(encodedName, 0, CFSTR("*"));
440 CFStringAppend(encodedName, CFSTR("*"));
441
442 CFRelease(bytes);
443 }
444
445 done :
446
447 if (encodedName) {
448 if (startup) {
449 /* update "startup" dictionary */
450 CFDictionaryAddValue(startup, CFSTR("APPLETALK_HOSTNAME"), encodedName);
451 }
452
453 if (globals) {
454 CFNumberRef num;
455
456 /* update "global" dictionary */
457 num = CFNumberCreate(NULL, kCFNumberIntType, &encoding);
458 CFDictionaryAddValue(globals, kSCPropNetAppleTalkComputerName, name);
459 CFDictionaryAddValue(globals, kSCPropNetAppleTalkComputerNameEncoding, num);
460 CFRelease(num);
461 }
462
463 CFRelease(encodedName);
464 }
465
466 return;
467 }
468
469
470 static boolean_t
471 updateConfiguration(int *newState)
472 {
473 boolean_t changed = FALSE;
474 CFStringRef computerName;
475 CFStringEncoding computerNameEncoding;
476 CFArrayRef configuredServices = NULL;
477 CFDictionaryRef dict;
478 CFIndex i;
479 CFIndex ifCount = 0;
480 CFMutableArrayRef info = NULL;
481 CFArrayRef interfaces = NULL;
482 CFStringRef key;
483 CFArrayRef keys;
484 CFIndex n;
485 CFMutableArrayRef newConfigFile;
486 CFMutableDictionaryRef newDefaults;
487 CFMutableDictionaryRef newDict;
488 CFMutableDictionaryRef newGlobals;
489 CFMutableDictionaryRef newGlobalsX; /* newGlobals without ServiceID */
490 CFMutableDictionaryRef newStartup;
491 CFMutableDictionaryRef newZones;
492 CFNumberRef num;
493 CFMutableDictionaryRef curGlobalsX; /* curGlobals without ServiceID */
494 CFStringRef pattern;
495 boolean_t postGlobals = FALSE;
496 CFStringRef primaryPort = NULL; /* primary interface */
497 CFStringRef primaryZone = NULL;
498 CFArrayRef serviceOrder = NULL;
499 CFDictionaryRef setGlobals = NULL;
500
501 cache_open();
502
503 /*
504 * establish the "new" AppleTalk configuration
505 */
506 *newState = curState;
507 newConfigFile = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
508 newGlobals = CFDictionaryCreateMutable(NULL,
509 0,
510 &kCFTypeDictionaryKeyCallBacks,
511 &kCFTypeDictionaryValueCallBacks);
512 newDefaults = CFDictionaryCreateMutable(NULL,
513 0,
514 &kCFTypeDictionaryKeyCallBacks,
515 &kCFTypeDictionaryValueCallBacks);
516 newStartup = CFDictionaryCreateMutable(NULL,
517 0,
518 &kCFTypeDictionaryKeyCallBacks,
519 &kCFTypeDictionaryValueCallBacks);
520 newZones = CFDictionaryCreateMutable(NULL,
521 0,
522 &kCFTypeDictionaryKeyCallBacks,
523 &kCFTypeDictionaryValueCallBacks);
524
525 /* initialize overall state */
526 CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-NO-"));
527
528 /*
529 * get the global settings (ServiceOrder, ComputerName, ...)
530 */
531 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
532 kSCDynamicStoreDomainSetup,
533 kSCEntNetAppleTalk);
534 setGlobals = cache_SCDynamicStoreCopyValue(store, key);
535 CFRelease(key);
536 if (setGlobals) {
537 if (isA_CFDictionary(setGlobals)) {
538 /* get service order */
539 serviceOrder = CFDictionaryGetValue(setGlobals,
540 kSCPropNetServiceOrder);
541 serviceOrder = isA_CFArray(serviceOrder);
542 if (serviceOrder) {
543 CFRetain(serviceOrder);
544 }
545 } else {
546 CFRelease(setGlobals);
547 setGlobals = NULL;
548 }
549 }
550
551 /*
552 * if we don't have an AppleTalk ServiceOrder, use IPv4's (if defined)
553 */
554 if (!serviceOrder) {
555 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
556 kSCDynamicStoreDomainSetup,
557 kSCEntNetIPv4);
558 dict = cache_SCDynamicStoreCopyValue(store, key);
559 CFRelease(key);
560 if (dict) {
561 if (isA_CFDictionary(dict)) {
562 serviceOrder = CFDictionaryGetValue(dict,
563 kSCPropNetServiceOrder);
564 serviceOrder = isA_CFArray(serviceOrder);
565 if (serviceOrder) {
566 CFRetain(serviceOrder);
567 }
568 }
569 CFRelease(dict);
570 }
571 }
572
573 /*
574 * get the list of ALL configured services
575 */
576 configuredServices = entity_all(store, kSCEntNetAppleTalk, serviceOrder);
577 if (configuredServices) {
578 ifCount = CFArrayGetCount(configuredServices);
579 }
580
581 if (serviceOrder) CFRelease(serviceOrder);
582
583 /*
584 * get the list of ALL active interfaces
585 */
586 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
587 dict = cache_SCDynamicStoreCopyValue(store, key);
588 CFRelease(key);
589 if (dict) {
590 if (isA_CFDictionary(dict)) {
591 interfaces = CFDictionaryGetValue(dict,
592 kSCDynamicStorePropNetInterfaces);
593 interfaces = isA_CFArray(interfaces);
594 if (interfaces) {
595 CFRetain(interfaces);
596 }
597 }
598 CFRelease(dict);
599 }
600
601 /*
602 * get the list of previously configured services
603 */
604 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
605 kSCDynamicStoreDomainState,
606 kSCCompAnyRegex,
607 kSCEntNetAppleTalk);
608 keys = SCDynamicStoreCopyKeyList(store, pattern);
609 CFRelease(pattern);
610 if (keys) {
611 info = CFArrayCreateMutableCopy(NULL, 0, keys);
612 CFRelease(keys);
613 }
614
615 /*
616 * iterate over each configured service to establish the new
617 * configuration.
618 */
619 for (i = 0; i < ifCount; i++) {
620 CFDictionaryRef service;
621 CFStringRef ifName;
622 CFStringRef configMethod;
623 CFMutableStringRef portConfig = NULL;
624 CFArrayRef networkRange; /* for seed ports, CFArray[2] of CFNumber (lo, hi) */
625 int sNetwork;
626 int eNetwork;
627 CFArrayRef zoneList; /* for seed ports, CFArray[] of CFString (zones names) */
628 CFIndex zCount;
629 CFIndex j;
630 CFMutableDictionaryRef ifDefaults = NULL;
631 CFNumberRef defaultNetwork;
632 CFNumberRef defaultNode;
633 CFStringRef defaultZone;
634
635 /* get AppleTalk service dictionary */
636 service = CFArrayGetValueAtIndex(configuredServices, i);
637
638 /* get interface name */
639 ifName = CFDictionaryGetValue(service, kSCPropNetInterfaceDeviceName);
640
641 /* check inteface availability */
642 if (!interfaces ||
643 !CFArrayContainsValue(interfaces, CFRangeMake(0, CFArrayGetCount(interfaces)), ifName)) {
644 /* if interface not available */
645 goto nextIF;
646 }
647
648 /* check interface link status */
649 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
650 kSCDynamicStoreDomainState,
651 ifName,
652 kSCEntNetLink);
653 dict = cache_SCDynamicStoreCopyValue(store, key);
654 CFRelease(key);
655 if (dict) {
656 Boolean linkStatus = TRUE; /* assume the link is "up" */
657 Boolean ifDetaching = FALSE; /* assume link is not detaching */
658
659 /* the link key for this interface is available */
660 if (isA_CFDictionary(dict)) {
661 CFBooleanRef bVal;
662
663 bVal = CFDictionaryGetValue(dict, kSCPropNetLinkActive);
664 if (isA_CFBoolean(bVal)) {
665 linkStatus = CFBooleanGetValue(bVal);
666 }
667
668 /* check if interface is detaching - value
669 doesn't really matter, only that it exists */
670 ifDetaching = CFDictionaryContainsKey(dict, kSCPropNetLinkDetaching);
671 }
672 CFRelease(dict);
673
674 if (!linkStatus || ifDetaching) {
675 /* if link status down or the interface is detaching */
676 goto nextIF;
677 }
678 }
679
680 /*
681 * Determine configuration method for this service
682 */
683 configMethod = CFDictionaryGetValue(service, kSCPropNetAppleTalkConfigMethod);
684 if (!isA_CFString(configMethod)) {
685 /* if no ConfigMethod */
686 goto nextIF;
687 }
688
689 if (!CFEqual(configMethod, kSCValNetAppleTalkConfigMethodNode ) &&
690 !CFEqual(configMethod, kSCValNetAppleTalkConfigMethodRouter ) &&
691 !CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
692 /* if not one of the expected values, disable */
693 SCLog(TRUE, LOG_NOTICE,
694 CFSTR("Unexpected AppleTalk ConfigMethod: %@"),
695 configMethod);
696 goto nextIF;
697 }
698
699 /*
700 * the first service to be defined will always be "primary"
701 */
702 if (CFArrayGetCount(newConfigFile) == 0) {
703 CFDictionaryRef active;
704
705 CFDictionarySetValue(newGlobals,
706 kSCDynamicStorePropNetPrimaryService,
707 CFDictionaryGetValue(service, CFSTR("ServiceID")));
708 CFDictionarySetValue(newGlobals,
709 kSCDynamicStorePropNetPrimaryInterface,
710 ifName);
711
712 /* and check if AT newtorking is active on the primary interface */
713 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
714 kSCDynamicStoreDomainState,
715 ifName,
716 kSCEntNetAppleTalk);
717 active = cache_SCDynamicStoreCopyValue(store, key);
718 CFRelease(key);
719 if (active) {
720 if (isA_CFDictionary(active)) {
721 postGlobals = TRUE;
722 }
723 CFRelease(active);
724 }
725 }
726
727 /*
728 * define the port
729 */
730 portConfig = CFStringCreateMutable(NULL, 0);
731 CFStringAppendFormat(portConfig, NULL, CFSTR("%@:"), ifName);
732
733 if (CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
734 CFNumberRef num;
735
736 /*
737 * we have been asked to configure this interface as a
738 * seed port. Ensure that we have been provided at least
739 * one network number, have been provided with at least
740 * one zonename, ...
741 */
742
743 networkRange = CFDictionaryGetValue(service,
744 kSCPropNetAppleTalkSeedNetworkRange);
745 if (!isA_CFArray(networkRange) || (CFArrayGetCount(networkRange) == 0)) {
746 SCLog(TRUE, LOG_NOTICE,
747 CFSTR("AppleTalk configuration error (%@)"),
748 kSCPropNetAppleTalkSeedNetworkRange);
749 goto nextIF;
750 }
751
752 /*
753 * establish the starting and ending network numbers
754 */
755 num = CFArrayGetValueAtIndex(networkRange, 0);
756 if (!isA_CFNumber(num)) {
757 SCLog(TRUE, LOG_NOTICE,
758 CFSTR("AppleTalk configuration error (%@)"),
759 kSCPropNetAppleTalkSeedNetworkRange);
760 goto nextIF;
761 }
762 CFNumberGetValue(num, kCFNumberIntType, &sNetwork);
763 eNetwork = sNetwork;
764
765 if (CFArrayGetCount(networkRange) > 1) {
766 num = CFArrayGetValueAtIndex(networkRange, 1);
767 if (!isA_CFNumber(num)) {
768 SCLog(TRUE, LOG_NOTICE,
769 CFSTR("AppleTalk configuration error (%@)"),
770 kSCPropNetAppleTalkSeedNetworkRange);
771 goto nextIF;
772 }
773 CFNumberGetValue(num, kCFNumberIntType, &eNetwork);
774 }
775 CFStringAppendFormat(portConfig, NULL, CFSTR("%d:%d:"), sNetwork, eNetwork);
776
777 /*
778 * establish the zones associated with this port
779 */
780 zoneList = CFDictionaryGetValue(service,
781 kSCPropNetAppleTalkSeedZones);
782 if (!isA_CFArray(zoneList)) {
783 SCLog(TRUE, LOG_NOTICE,
784 CFSTR("AppleTalk configuration error (%@)"),
785 kSCPropNetAppleTalkSeedZones);
786 goto nextIF;
787 }
788
789 zCount = CFArrayGetCount(zoneList);
790 for (j = 0; j < zCount; j++) {
791 CFStringRef zone;
792 CFArrayRef ifList;
793 CFMutableArrayRef newIFList;
794
795 zone = CFArrayGetValueAtIndex(zoneList, j);
796 if (!isA_CFString(zone)) {
797 continue;
798 }
799
800 if (CFDictionaryGetValueIfPresent(newZones, zone, (const void **)&ifList)) {
801 /* known zone */
802 newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList);
803 } else {
804 /* new zone */
805 newIFList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
806 }
807 CFArrayAppendValue(newIFList, ifName);
808 CFArraySortValues(newIFList,
809 CFRangeMake(0, CFArrayGetCount(newIFList)),
810 (CFComparatorFunction)CFStringCompare,
811 NULL);
812 CFDictionarySetValue(newZones, zone, newIFList);
813 CFRelease(newIFList);
814
815 /*
816 * flag the default zone
817 */
818 if (!primaryZone) {
819 primaryZone = CFRetain(zone);
820 }
821 }
822 if (!primaryZone) {
823 SCLog(TRUE, LOG_NOTICE,
824 CFSTR("AppleTalk configuration error (%@)"),
825 kSCPropNetAppleTalkSeedZones);
826 goto nextIF;
827 }
828 }
829
830 /* get the (per-interface) "Computer Name" */
831 computerName = CFDictionaryGetValue(service,
832 kSCPropNetAppleTalkComputerName);
833 if (CFDictionaryGetValueIfPresent(service,
834 kSCPropNetAppleTalkComputerNameEncoding,
835 (const void **)&num) &&
836 isA_CFNumber(num)) {
837 CFNumberGetValue(num, kCFNumberIntType, &computerNameEncoding);
838 } else {
839 computerNameEncoding = CFStringGetSystemEncoding();
840 }
841 encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
842
843 /*
844 * declare the first configured AppleTalk service / interface
845 * as the "home port".
846 */
847 if (CFArrayGetCount(newConfigFile) == 0) {
848 CFStringAppend(portConfig, CFSTR("*"));
849 primaryPort = CFRetain(ifName);
850 }
851 CFArrayAppendValue(newConfigFile, portConfig);
852
853 /*
854 * get the per-interface defaults
855 */
856 ifDefaults = CFDictionaryCreateMutable(NULL,
857 0,
858 &kCFTypeDictionaryKeyCallBacks,
859 &kCFTypeDictionaryValueCallBacks);
860
861 defaultNetwork = CFDictionaryGetValue(service, kSCPropNetAppleTalkNetworkID);
862 defaultNode = CFDictionaryGetValue(service, kSCPropNetAppleTalkNodeID);
863 if (isA_CFNumber(defaultNetwork) && isA_CFNumber(defaultNode)) {
864 /*
865 * set the default node and network
866 */
867 CFDictionarySetValue(ifDefaults,
868 kSCPropNetAppleTalkNetworkID,
869 defaultNetwork);
870 CFDictionarySetValue(ifDefaults,
871 kSCPropNetAppleTalkNodeID,
872 defaultNode);
873 }
874
875 if ((CFDictionaryGetValueIfPresent(service,
876 kSCPropNetAppleTalkDefaultZone,
877 (const void **)&defaultZone) == TRUE)) {
878 /*
879 * set the default zone for this interface
880 */
881 CFDictionarySetValue(ifDefaults,
882 kSCPropNetAppleTalkDefaultZone,
883 defaultZone);
884 }
885
886 CFDictionarySetValue(newDefaults, ifName, ifDefaults);
887 CFRelease(ifDefaults);
888
889 switch (CFArrayGetCount(newConfigFile)) {
890 case 1:
891 /*
892 * first AppleTalk interface
893 */
894 CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), ifName);
895 break;
896 case 2:
897 /* second AppleTalk interface */
898 if (!CFEqual(CFDictionaryGetValue(newStartup, CFSTR("APPLETALK")),
899 CFSTR("-ROUTER-"))) {
900 /*
901 * if not routing (yet), configure as multi-home
902 */
903 CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-MULTIHOME-"));
904 }
905 break;
906 }
907
908 if (CFEqual(configMethod, kSCValNetAppleTalkConfigMethodRouter) ||
909 CFEqual(configMethod, kSCValNetAppleTalkConfigMethodSeedRouter)) {
910 /* if not a simple node, enable routing */
911 CFDictionarySetValue(newStartup, CFSTR("APPLETALK"), CFSTR("-ROUTER-"));
912 }
913
914 /*
915 * establish the State:/Network/Service/nnn/AppleTalk key info
916 */
917 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
918 kSCDynamicStoreDomainState,
919 CFDictionaryGetValue(service, CFSTR("ServiceID")),
920 kSCEntNetAppleTalk);
921 newDict = CFDictionaryCreateMutable(NULL,
922 0,
923 &kCFTypeDictionaryKeyCallBacks,
924 &kCFTypeDictionaryValueCallBacks);
925 CFDictionaryAddValue(newDict, kSCPropInterfaceName, ifName);
926 cache_SCDynamicStoreSetValue(store, key, newDict);
927 CFRelease(newDict);
928 if (info) {
929 j = CFArrayGetFirstIndexOfValue(info,
930 CFRangeMake(0, CFArrayGetCount(info)),
931 key);
932 if (j != kCFNotFound) {
933 CFArrayRemoveValueAtIndex(info, j);
934 }
935 }
936 CFRelease(key);
937
938 nextIF :
939
940 if (portConfig) CFRelease(portConfig);
941 }
942
943 if (primaryZone) {
944 CFArrayRef ifList;
945 CFMutableArrayRef newIFList;
946
947 ifList = CFDictionaryGetValue(newZones, primaryZone);
948 if (CFArrayContainsValue(ifList,
949 CFRangeMake(0, CFArrayGetCount(ifList)),
950 primaryPort)) {
951 newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList);
952 CFArrayAppendValue(newIFList, CFSTR("*"));
953 CFDictionarySetValue(newZones, primaryZone, newIFList);
954 CFRelease(newIFList);
955 }
956 CFRelease(primaryZone);
957 }
958 if (primaryPort) {
959 CFRelease(primaryPort);
960 }
961
962 /* sort the ports */
963 i = CFArrayGetCount(newConfigFile);
964 CFArraySortValues(newConfigFile,
965 CFRangeMake(0, i),
966 (CFComparatorFunction)CFStringCompare,
967 NULL);
968
969 /* add the zones to the configuration */
970 CFDictionaryApplyFunction(newZones, addZoneToPorts, newConfigFile);
971 CFRelease(newZones);
972
973 /* sort the zones */
974 CFArraySortValues(newConfigFile,
975 CFRangeMake(i, CFArrayGetCount(newConfigFile)-i),
976 (CFComparatorFunction)CFStringCompare,
977 NULL);
978
979 /* ensure that the last line of the configuration file is terminated */
980 CFArrayAppendValue(newConfigFile, CFSTR(""));
981
982 /*
983 * Check if we have a "ComputerName" and look elsewhere if we don't have
984 * one yet.
985 */
986 if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME")) &&
987 (setGlobals != NULL)) {
988 computerName = CFDictionaryGetValue(setGlobals,
989 kSCPropNetAppleTalkComputerName);
990 if (CFDictionaryGetValueIfPresent(setGlobals,
991 kSCPropNetAppleTalkComputerNameEncoding,
992 (const void **)&num) &&
993 isA_CFNumber(num)) {
994 CFNumberGetValue(num, kCFNumberIntType, &computerNameEncoding);
995 } else {
996 computerNameEncoding = CFStringGetSystemEncoding();
997 }
998 encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
999 }
1000 if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME"))) {
1001 computerName = SCDynamicStoreCopyComputerName(store, &computerNameEncoding);
1002 if (computerName) {
1003 encodeName(computerName, computerNameEncoding, newStartup, newGlobals);
1004 CFRelease(computerName);
1005 }
1006 }
1007 if (!CFDictionaryContainsKey(newStartup, CFSTR("APPLETALK_HOSTNAME"))) {
1008 struct utsname name;
1009
1010 if (uname(&name) == 0) {
1011 computerName = CFStringCreateWithCString(NULL, name.nodename, kCFStringEncodingASCII);
1012 if (computerName) {
1013 encodeName(computerName, kCFStringEncodingASCII, NULL, newGlobals);
1014 CFRelease(computerName);
1015 }
1016 }
1017 }
1018
1019 /* compare the previous and current configurations */
1020
1021 curGlobalsX = CFDictionaryCreateMutableCopy(NULL, 0, curGlobals);
1022 CFDictionaryRemoveValue(curGlobalsX, kSCDynamicStorePropNetPrimaryService);
1023
1024 newGlobalsX = CFDictionaryCreateMutableCopy(NULL, 0, newGlobals);
1025 CFDictionaryRemoveValue(newGlobalsX, kSCDynamicStorePropNetPrimaryService);
1026
1027 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1028 kSCDynamicStoreDomainState,
1029 kSCEntNetAppleTalk);
1030
1031 if (CFEqual(curGlobalsX , newGlobalsX ) &&
1032 CFEqual(curConfigFile , newConfigFile) &&
1033 CFEqual(curDefaults , newDefaults ) &&
1034 CFEqual(curStartup , newStartup )
1035 ) {
1036 /*
1037 * the configuration has not changed.
1038 */
1039
1040 if (postGlobals) {
1041 /*
1042 * the requested configuration hasn't changed but we
1043 * now need to tell everyone that AppleTalk is active.
1044 */
1045 if (!SCDynamicStoreSetValue(store, key, newGlobals)) {
1046 SCLog(TRUE,
1047 LOG_ERR,
1048 CFSTR("SCDynamicStoreSetValue() failed: %s"),
1049 SCErrorString(SCError()));
1050 }
1051 }
1052
1053 CFRelease(newGlobals);
1054 CFRelease(newConfigFile);
1055 CFRelease(newDefaults);
1056 CFRelease(newStartup);
1057 } else if (CFArrayGetCount(newConfigFile) <= 1) {
1058 /*
1059 * the configuration has changed but there are no
1060 * longer any interfaces configured for AppleTalk
1061 * networking.
1062 */
1063
1064 /*
1065 * remove the global (State:/Network/Global/AppleTalk) key.
1066 *
1067 * Note: it will be restored later after AT networking has
1068 * been activated.
1069 */
1070
1071 /* remove the (/etc/appletalk.cfg) configuration file */
1072 (void)unlink(AT_CFG_FILE);
1073
1074 /*
1075 * update the per-service (and global) state
1076 */
1077 cache_SCDynamicStoreRemoveValue(store, key); // remove State:/Network/Global/AppleTalk
1078 n = CFArrayGetCount(info);
1079 for (i = 0; i < n; i++) {
1080 CFStringRef xKey = CFArrayGetValueAtIndex(info, i);
1081
1082 cache_SCDynamicStoreRemoveValue(store, xKey);
1083 }
1084 cache_write(store);
1085
1086 /* flag this as a new configuration */
1087 *newState = -(abs(curState) + 1);
1088 changed = TRUE;
1089 } else {
1090 /*
1091 * the configuration has changed.
1092 */
1093
1094 /* update the (/etc/appletalk.cfg) configuration file */
1095 configWrite(AT_CFG_FILE, newConfigFile);
1096
1097 /*
1098 * update the per-service (and global) state
1099 *
1100 * Note: if present, we remove any existing global state key and allow it
1101 * to be restored after the stack has been re-started.
1102 */
1103 CFDictionaryApplyFunction(newDefaults, updateDefaults, NULL);
1104 cache_SCDynamicStoreRemoveValue(store, key); // remove State:/Network/Global/AppleTalk
1105 n = CFArrayGetCount(info);
1106 for (i = 0; i < n; i++) {
1107 CFStringRef xKey = CFArrayGetValueAtIndex(info, i);
1108
1109 cache_SCDynamicStoreRemoveValue(store, xKey);
1110 }
1111 cache_write(store);
1112
1113 /* flag this as a new configuration */
1114 *newState = abs(curState) + 1;
1115 changed = TRUE;
1116 }
1117
1118 CFRelease(curGlobalsX);
1119 CFRelease(newGlobalsX);
1120 CFRelease(key);
1121
1122 if (changed) {
1123 CFRelease(curGlobals);
1124 curGlobals = newGlobals;
1125 CFRelease(curConfigFile);
1126 curConfigFile = newConfigFile;
1127 CFRelease(curDefaults);
1128 curDefaults = newDefaults;
1129 CFRelease(curStartup);
1130 curStartup = newStartup;
1131 }
1132
1133 if (info) CFRelease(info);
1134 if (interfaces) CFRelease(interfaces);
1135 if (configuredServices) CFRelease(configuredServices);
1136 if (setGlobals) CFRelease(setGlobals);
1137
1138 cache_close();
1139
1140 return changed;
1141 }
1142
1143
1144 #include <sysexits.h>
1145 #define AT_CMD_SUCCESS EX_OK /* success */
1146 #define AT_CMD_ALREADY_RUNNING EX__MAX + 10
1147 #define AT_CMD_NOT_RUNNING EX__MAX + 11
1148
1149
1150 static int
1151 stackState()
1152 {
1153 int ret;
1154 int sock;
1155 at_state_t state;
1156
1157 sock = socket(AF_APPLETALK, SOCK_RAW, 0);
1158 ret = ioctl(sock, AIOCGETSTATE, (caddr_t)&state);
1159 (void)close(sock);
1160 if (ret == -1) {
1161 SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(AIOCGETSTATE) failed: %s"), strerror(errno));
1162 return FALSE;
1163 }
1164
1165 if (state.flags & AT_ST_STARTED) {
1166 return abs(curState);
1167 } else {
1168 return -(abs(curState));
1169 }
1170 }
1171
1172
1173 static pid_t execCommand = 0;
1174 static int execRetry;
1175
1176
1177 static void
1178 stopComplete(pid_t pid, int status, struct rusage *rusage, void *context)
1179 {
1180 execCommand = 0;
1181
1182 if (WIFEXITED(status)) {
1183 switch (WEXITSTATUS(status)) {
1184 case AT_CMD_SUCCESS :
1185 case AT_CMD_NOT_RUNNING :
1186 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown complete"));
1187 if (curState > 0) {
1188 // the stack is down but we really want it up
1189 startAppleTalk(NULL, (void *)curState);
1190 }
1191 return;
1192 default :
1193 break;
1194 }
1195 }
1196
1197 SCLog(TRUE, LOG_ERR,
1198 CFSTR("AppleTalk shutdown failed, status = %d%s"),
1199 WEXITSTATUS(status),
1200 (execRetry > 1) ? " (retrying)" : "");
1201
1202 // shutdown failed, retry
1203 if (--execRetry > 0) {
1204 CFRunLoopTimerContext timerContext = { 0, (void *)curState, NULL, NULL, NULL };
1205 CFRunLoopTimerRef timer;
1206
1207 timer = CFRunLoopTimerCreate(NULL,
1208 CFAbsoluteTimeGetCurrent() + 1.0,
1209 0.0,
1210 0,
1211 0,
1212 stopAppleTalk,
1213 &timerContext);
1214 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
1215 CFRelease(timer);
1216 return;
1217 } else {
1218 // we weren't able to stop
1219 curState = stackState();
1220 }
1221
1222 return;
1223 }
1224
1225
1226 static void
1227 stopAppleTalk(CFRunLoopTimerRef timer, void *info)
1228 {
1229 char *argv[] = { "appletalk",
1230 "-d",
1231 NULL };
1232
1233 if (execCommand == 0) {
1234 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown"));
1235 } else {
1236 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk shutdown skipped, transition in progress"));
1237 return;
1238 }
1239
1240 execCommand = _SCDPluginExecCommand(stopComplete, // callback
1241 info, // context
1242 0, // uid
1243 0, // gid
1244 "/usr/sbin/appletalk", // path
1245 argv); // argv
1246
1247 if (!timer) {
1248 execRetry = 5; // initialize retry count
1249 }
1250
1251 return;
1252 }
1253
1254
1255 static void
1256 startComplete(pid_t pid, int status, struct rusage *rusage, void *context)
1257 {
1258 execCommand = 0;
1259
1260 if (WIFEXITED(status)) {
1261 switch (WEXITSTATUS(status)) {
1262 case AT_CMD_SUCCESS :
1263 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup complete"));
1264 if ((curState < 0) || (curState > (int)context)) {
1265 // the stack is now up but we really want it down
1266 stopAppleTalk(NULL, (void *)curState);
1267 }
1268 return;
1269 case AT_CMD_ALREADY_RUNNING :
1270 // the stack is already up but we're not sure
1271 // if the configuration is correct, restart
1272 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk already running, restarting"));
1273 stopAppleTalk(NULL, (void *)curState);
1274 return;
1275 default :
1276 break;
1277 }
1278 }
1279
1280 SCLog(TRUE, LOG_ERR,
1281 CFSTR("AppleTalk startup failed, status = %d%s"),
1282 WEXITSTATUS(status),
1283 (execRetry > 1) ? " (retrying)" : "");
1284
1285 // startup failed, retry
1286 if (--execRetry > 0) {
1287 CFRunLoopTimerContext timerContext = { 0, (void *)curState, NULL, NULL, NULL };
1288 CFRunLoopTimerRef timer;
1289
1290 timer = CFRunLoopTimerCreate(NULL,
1291 CFAbsoluteTimeGetCurrent() + 1.0,
1292 0.0,
1293 0,
1294 0,
1295 startAppleTalk,
1296 &timerContext);
1297 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
1298 CFRelease(timer);
1299 return;
1300 } else {
1301 // we weren't able to start
1302 curState = stackState();
1303 }
1304
1305 return;
1306 }
1307
1308
1309 static void
1310 startAppleTalk(CFRunLoopTimerRef timer, void *info)
1311 {
1312 int argc = 0;
1313 char *argv[8];
1314 char *computerName = NULL;
1315 char *interface = NULL;
1316 CFStringRef mode = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK"));
1317 CFStringRef name = CFDictionaryGetValue(curStartup, CFSTR("APPLETALK_HOSTNAME"));
1318
1319 if (execCommand == 0) {
1320 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup"));
1321 } else {
1322 SCLog(TRUE, LOG_NOTICE, CFSTR("AppleTalk startup skipped, transition in progress"));
1323 return;
1324 }
1325
1326 if (!mode) {
1327 // Huh?
1328 return;
1329 }
1330
1331 // set command name
1332 argv[argc++] = "appletalk";
1333
1334 // set hostname
1335 if (name) {
1336 computerName = _SC_cfstring_to_cstring(name, NULL, 0, kCFStringEncodingASCII);
1337 if (computerName) {
1338 argv[argc++] = "-C";
1339 argv[argc++] = computerName;
1340 } else {
1341 // could not convert name
1342 goto done;
1343 }
1344 }
1345
1346 // set mode
1347 if (CFEqual(mode, CFSTR("-ROUTER-"))) {
1348 argv[argc++] = "-r";
1349 } else if (CFEqual(mode, CFSTR("-MULTIHOME-"))) {
1350 argv[argc++] = "-x";
1351 } else {
1352 interface = _SC_cfstring_to_cstring(mode, NULL, 0, kCFStringEncodingASCII);
1353 if (interface) {
1354 argv[argc++] = "-u";
1355 argv[argc++] = interface;
1356 } else {
1357 // could not convert interface
1358 goto done;
1359 }
1360 }
1361
1362 // set non-interactive
1363 argv[argc++] = "-q";
1364
1365 // close argument list
1366 argv[argc++] = NULL;
1367
1368 execCommand = _SCDPluginExecCommand(startComplete, // callback
1369 info, // context
1370 0, // uid
1371 0, // gid
1372 "/usr/sbin/appletalk", // path
1373 argv); // argv
1374
1375 if (!timer) {
1376 execRetry = 5; // initialize retry count
1377 }
1378
1379 done :
1380
1381 if (computerName) CFAllocatorDeallocate(NULL, computerName);
1382 if (interface) CFAllocatorDeallocate(NULL, interface);
1383
1384 return;
1385 }
1386
1387
1388 static void
1389 atConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
1390 {
1391 boolean_t configChanged;
1392 int newState;
1393
1394 configChanged = updateConfiguration(&newState);
1395
1396 if (configChanged && (execCommand == 0)) {
1397 // if the configuration has changed and we're not already transitioning
1398 if (newState > 0) {
1399 if (curState > 0) {
1400 // already running, restart [with new configuration]
1401 stopAppleTalk(NULL, (void *)newState);
1402 } else {
1403 startAppleTalk(NULL, (void *)newState);
1404 }
1405 } else {
1406 if (curState > 0) {
1407 stopAppleTalk(NULL, (void *)newState);
1408 }
1409 }
1410 }
1411
1412 curState = newState;
1413
1414 return;
1415 }
1416
1417
1418 void
1419 stop_ATconfig(CFRunLoopSourceRef stopRls)
1420 {
1421 // cleanup
1422
1423 if (storeRls != NULL) {
1424 CFRunLoopSourceInvalidate(storeRls);
1425 CFRelease(storeRls);
1426 storeRls = NULL;
1427 }
1428
1429 if (store != NULL) {
1430 CFRelease(store);
1431 store = NULL;
1432 CFRelease(curGlobals);
1433 CFRelease(curConfigFile);
1434 CFRelease(curDefaults);
1435 CFRelease(curStartup);
1436 }
1437
1438 CFRunLoopSourceSignal(stopRls);
1439 return;
1440 }
1441
1442
1443 void
1444 load_ATconfig(CFBundleRef bundle, Boolean bundleVerbose)
1445 {
1446 CFStringRef key;
1447 CFMutableArrayRef keys = NULL;
1448 CFStringRef pattern;
1449 CFMutableArrayRef patterns = NULL;
1450
1451 if (bundleVerbose) {
1452 _verbose = TRUE;
1453 }
1454
1455 SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
1456 SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
1457
1458 /* initialize a few globals */
1459
1460 curGlobals = CFDictionaryCreateMutable(NULL,
1461 0,
1462 &kCFTypeDictionaryKeyCallBacks,
1463 &kCFTypeDictionaryValueCallBacks);
1464 curConfigFile = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1465 curDefaults = CFDictionaryCreateMutable(NULL,
1466 0,
1467 &kCFTypeDictionaryKeyCallBacks,
1468 &kCFTypeDictionaryValueCallBacks);
1469 curStartup = CFDictionaryCreateMutable(NULL,
1470 0,
1471 &kCFTypeDictionaryKeyCallBacks,
1472 &kCFTypeDictionaryValueCallBacks);
1473
1474 /* open a "configd" store to allow cache updates */
1475 store = SCDynamicStoreCreate(NULL,
1476 CFSTR("AppleTalk Configuraton plug-in"),
1477 atConfigChangedCallback,
1478 NULL);
1479 if (!store) {
1480 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
1481 goto error;
1482 }
1483
1484 /* establish notification keys and patterns */
1485
1486 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1487 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1488
1489 /* ...watch for (global) AppleTalk configuration changes */
1490 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1491 kSCDynamicStoreDomainSetup,
1492 kSCEntNetAppleTalk);
1493 CFArrayAppendValue(keys, key);
1494 CFRelease(key);
1495
1496 /* ...watch for (per-service) AppleTalk configuration changes */
1497 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1498 kSCDynamicStoreDomainSetup,
1499 kSCCompAnyRegex,
1500 kSCEntNetAppleTalk);
1501 CFArrayAppendValue(patterns, pattern);
1502 CFRelease(pattern);
1503
1504 /* ...watch for network interface link status changes */
1505 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1506 kSCDynamicStoreDomainState,
1507 kSCCompAnyRegex,
1508 kSCEntNetLink);
1509 CFArrayAppendValue(patterns, pattern);
1510 CFRelease(pattern);
1511
1512 /* ...watch for (per-interface) AppleTalk configuration changes */
1513 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1514 kSCDynamicStoreDomainState,
1515 kSCCompAnyRegex,
1516 kSCEntNetAppleTalk);
1517 CFArrayAppendValue(patterns, pattern);
1518 CFRelease(pattern);
1519
1520 /* ...watch for computer name changes */
1521 key = SCDynamicStoreKeyCreateComputerName(NULL);
1522 CFArrayAppendValue(keys, key);
1523 CFRelease(key);
1524
1525 /* register the keys/patterns */
1526 if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
1527 SCLog(TRUE, LOG_ERR,
1528 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
1529 SCErrorString(SCError()));
1530 goto error;
1531 }
1532 CFRelease(keys);
1533 CFRelease(patterns);
1534
1535 storeRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
1536 if (!storeRls) {
1537 SCLog(TRUE, LOG_ERR,
1538 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
1539 SCErrorString(SCError()));
1540 goto error;
1541 }
1542 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRls, kCFRunLoopDefaultMode);
1543
1544 return;
1545
1546 error :
1547
1548 if (curGlobals) CFRelease(curGlobals);
1549 if (curConfigFile) CFRelease(curConfigFile);
1550 if (curDefaults) CFRelease(curDefaults);
1551 if (curStartup) CFRelease(curStartup);
1552 if (store) CFRelease(store);
1553 if (keys) CFRelease(keys);
1554 if (patterns) CFRelease(patterns);
1555 return;
1556 }
1557
1558
1559 #ifdef MAIN
1560 #include "cfManager.c"
1561 int
1562 main(int argc, char **argv)
1563 {
1564 _sc_log = FALSE;
1565 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1566
1567 load_ATconfig(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
1568 CFRunLoopRun();
1569 /* not reached */
1570 exit(0);
1571 return 0;
1572 }
1573 #endif