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