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