]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/set-hostname.c
configd-136.2.tar.gz
[apple/configd.git] / Plugins / IPMonitor / set-hostname.c
1 /*
2 * Copyright (c) 2004, 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 #include <ctype.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <net/if.h>
31 #include <net/if_dl.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <netdb_async.h>
35
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <SystemConfiguration/SystemConfiguration.h>
38 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
39 #include <SystemConfiguration/SCValidation.h>
40 #include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
41
42 #include <notify.h>
43
44
45 static SCDynamicStoreRef store = NULL;
46 static CFRunLoopSourceRef rls = NULL;
47
48 static Boolean dnsActive = FALSE;
49 static CFMachPortRef dnsPort = NULL;
50 static CFRunLoopSourceRef dnsRLS = NULL;
51 static struct timeval dnsQueryStart;
52
53 static Boolean _verbose = FALSE;
54
55
56 /* SPI (from SCNetworkReachability.c) */
57 Boolean
58 _SC_checkResolverReachability(SCDynamicStoreRef *storeP,
59 SCNetworkConnectionFlags *flags,
60 Boolean *haveDNS,
61 const char * nodename);
62
63
64 /*
65 * checkResolverReachabilityByAddress()
66 *
67 * Given an IP address, determine whether a reverse DNS query can be issued
68 * using the current network configuration.
69 */
70 static Boolean
71 checkResolverReachabilityByAddress(SCDynamicStoreRef store, struct sockaddr *sa)
72 {
73 SCNetworkConnectionFlags flags;
74 Boolean haveDNS;
75 int i;
76 Boolean ok = FALSE;
77 char ptr_name[128];
78
79 /*
80 * Ideally, we would have an API that given a local IP
81 * address would return the DNS server(s) that would field
82 * a given PTR query. Fortunately, we do have an SPI which
83 * which will provide this information given a "name" so we
84 * take the address, convert it into the inverse query name,
85 * and find out which servers should be consulted.
86 */
87
88 switch (sa->sa_family) {
89 case AF_INET : {
90 union {
91 in_addr_t s_addr;
92 unsigned char b[4];
93 } rev;
94 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
95
96 /*
97 * build "PTR" query name
98 * NNN.NNN.NNN.NNN.in-addr.arpa.
99 */
100 rev.s_addr = sin->sin_addr.s_addr;
101 (void) snprintf(ptr_name, sizeof(ptr_name), "%u.%u.%u.%u.in-addr.arpa.",
102 rev.b[3],
103 rev.b[2],
104 rev.b[1],
105 rev.b[0]);
106
107 break;
108 }
109
110 case AF_INET6 : {
111 int s = 0;
112 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
113 int x = sizeof(ptr_name);
114 int n;
115
116 #define USE_NIBBLE_QUERY
117 #ifdef USE_NIBBLE_QUERY
118 /*
119 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
120 * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
121 */
122 for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) {
123 n = snprintf(&ptr_name[s], x, "%x.%x.",
124 ( sin6->sin6_addr.s6_addr[i] & 0xf),
125 ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf));
126 if ((n == -1) || (n >= x)) {
127 goto done;
128 }
129
130 s += n;
131 x -= n;
132 }
133
134 n = snprintf(&ptr_name[s], x, "ip6.arpa.");
135 if ((n == -1) || (n >= x)) {
136 goto done;
137 }
138 #else /* USE_NIBBLE_QUERY */
139 /*
140 * build IPv6 "bit-string" PTR query name (RFC 2673)
141 * \[xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN].ip6.arpa.
142 */
143 n = snprintf(&ptr_name[0], x, "\\[x");
144 if ((n == -1) || (n >= x)) {
145 goto done;
146 }
147
148 s += n;
149 x -= n;
150 for (i = 0; i < 16; i++) {
151 n = snprintf(&ptr_name[s], x, "%2.2x", sin6->sin6_addr.s6_addr[i]);
152 if ((n == -1) || (n >= x)) {
153 goto done;
154 }
155
156 s += n;
157 x -= n;
158 }
159
160 n = snprintf(&ptr_name[s], x, "].ip6.arpa.");
161 if ((n == -1) || (n >= x)) {
162 goto done;
163 }
164 #endif /* USE_NIBBLE_QUERY */
165
166 break;
167 }
168
169 default :
170 goto done;
171 }
172
173 ok = _SC_checkResolverReachability(&store, &flags, &haveDNS, ptr_name);
174 if (ok) {
175 if (!(flags & kSCNetworkFlagsReachable) ||
176 (flags & kSCNetworkFlagsConnectionRequired)) {
177 // if not reachable *OR* connection required
178 ok = FALSE;
179 }
180 }
181
182 done :
183
184 return ok;
185 }
186
187
188 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
189
190
191 static void
192 set_hostname(CFStringRef hostname)
193 {
194 if (hostname != NULL) {
195 char old_name[MAXHOSTNAMELEN];
196 char new_name[MAXHOSTNAMELEN];
197
198 if (gethostname(old_name, sizeof(old_name)) == -1) {
199 SCLog(TRUE, LOG_ERR, CFSTR("gethostname() failed: %s"), strerror(errno));
200 old_name[0] = '\0';
201 }
202
203 if (_SC_cfstring_to_cstring(hostname,
204 new_name,
205 sizeof(new_name),
206 kCFStringEncodingUTF8) == NULL) {
207 SCLog(TRUE, LOG_ERR, CFSTR("could not convert [new] hostname"));
208 new_name[0] = '\0';
209 }
210
211 old_name[sizeof(old_name)-1] = '\0';
212 new_name[sizeof(new_name)-1] = '\0';
213 if (strcmp(old_name, new_name) != 0) {
214 if (sethostname(new_name, strlen(new_name)) == 0) {
215 uint32_t status;
216
217 SCLog(TRUE, LOG_NOTICE,
218 CFSTR("setting hostname to \"%s\""),
219 new_name);
220
221 status = notify_post(HOSTNAME_NOTIFY_KEY);
222 if (status != NOTIFY_STATUS_OK) {
223 SCLog(TRUE, LOG_ERR,
224 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY ") failed: error=%lu"),
225 status);
226 }
227 } else {
228 SCLog(TRUE, LOG_ERR,
229 CFSTR("sethostname(%s, %d) failed: %s"),
230 new_name,
231 strlen(new_name),
232 strerror(errno));
233 }
234 }
235 }
236
237 return;
238 }
239
240
241 #define HOSTCONFIG "/etc/hostconfig"
242 #define HOSTNAME_KEY "HOSTNAME="
243 #define AUTOMATIC "-AUTOMATIC-"
244
245 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
246
247 static CFStringRef
248 copy_static_name()
249 {
250 FILE * f;
251 char buf[256];
252 CFStringRef name = NULL;
253
254 f = fopen(HOSTCONFIG, "r");
255 if (f == NULL) {
256 return NULL;
257 }
258
259 while (fgets(buf, sizeof(buf), f) != NULL) {
260 char * bp;
261 int n;
262 char * np;
263 Boolean str_escape;
264 Boolean str_quote;
265
266 n = strlen(buf);
267 if (buf[n-1] == '\n') {
268 /* the entire line fit in the buffer, remove the newline */
269 buf[n-1] = '\0';
270 } else {
271 /* eat the remainder of the line */
272 do {
273 n = fgetc(f);
274 } while ((n != '\n') && (n != EOF));
275 }
276
277 // skip leading white space
278 bp = &buf[0];
279 while (isspace(*bp)) {
280 bp++;
281 }
282
283 // find "HOSTNAME=" key
284 if (strncmp(bp, HOSTNAME_KEY, HOSTNAME_KEY_LEN) != 0) {
285 continue; // if not
286 }
287
288 // get the hostname string
289 bp += HOSTNAME_KEY_LEN;
290 str_escape = FALSE;
291 str_quote = FALSE;
292
293 np = &buf[0];
294 while (*bp != '\0') {
295 char ch = *bp;
296
297 switch (ch) {
298 case '\\' :
299 if (!str_escape) {
300 str_escape = TRUE;
301 bp++;
302 continue;
303 }
304 break;
305 case '"' :
306 if (!str_escape) {
307 str_quote = !str_quote;
308 bp++;
309 continue;
310 }
311 break;
312 default :
313 break;
314 }
315
316 if (str_escape) {
317 str_escape = FALSE;
318 } else if (!str_quote && (isspace(ch) || (ch == '#'))) {
319 break;
320 }
321
322 *np++ = ch;
323 bp++;
324 }
325
326 *np = '\0';
327
328 if (name != NULL) {
329 CFRelease(name);
330 name = NULL;
331 }
332
333 if (str_quote) {
334 // the shell won't parse this file so neither will we
335 break;
336 }
337
338 if (strcmp(buf, AUTOMATIC) == 0) {
339 // skip "-AUTOMATIC-"
340 continue;
341 }
342
343 name = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
344 }
345
346 (void) fclose(f);
347 return name;
348 }
349
350
351 #ifndef kSCPropNetHostName
352 #define kSCPropNetHostName CFSTR("HostName")
353 #endif
354
355
356 static CFStringRef
357 copy_prefs_hostname(SCDynamicStoreRef store)
358 {
359 CFDictionaryRef dict;
360 CFStringRef key;
361 CFStringRef name = NULL;
362
363 key = SCDynamicStoreKeyCreateComputerName(NULL);
364 dict = SCDynamicStoreCopyValue(store, key);
365 CFRelease(key);
366 if (dict == NULL) {
367 goto done;
368 }
369 if (!isA_CFDictionary(dict)) {
370 goto done;
371 }
372
373 name = isA_CFString(CFDictionaryGetValue(dict, kSCPropNetHostName));
374 if (name == NULL) {
375 goto done;
376 }
377 CFRetain(name);
378
379 done :
380
381 if (dict != NULL) CFRelease(dict);
382
383 return name;
384 }
385
386
387 static CFStringRef
388 copy_primary_service(SCDynamicStoreRef store)
389 {
390 CFDictionaryRef dict;
391 CFStringRef key;
392 CFStringRef serviceID = NULL;
393
394 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
395 kSCDynamicStoreDomainState,
396 kSCEntNetIPv4);
397 dict = SCDynamicStoreCopyValue(store, key);
398 CFRelease(key);
399
400 if (dict != NULL) {
401 if (isA_CFDictionary(dict)) {
402 serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService);
403 if (isA_CFString(serviceID)) {
404 CFRetain(serviceID);
405 } else {
406 serviceID = NULL;
407 }
408 }
409 CFRelease(dict);
410 }
411
412 return serviceID;
413 }
414
415
416 static CFStringRef
417 copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID)
418 {
419 CFDictionaryRef dict;
420 CFStringRef key;
421 CFStringRef address = NULL;
422
423 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
424 kSCDynamicStoreDomainState,
425 serviceID,
426 kSCEntNetIPv4);
427 dict = SCDynamicStoreCopyValue(store, key);
428 CFRelease(key);
429
430 if (dict != NULL) {
431 if (isA_CFDictionary(dict)) {
432 CFArrayRef addresses;
433
434 addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses);
435 if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) {
436 address = CFArrayGetValueAtIndex(addresses, 0);
437 if (isA_CFString(address)) {
438 CFRetain(address);
439 } else {
440 address = NULL;
441 }
442 }
443 }
444 CFRelease(dict);
445 }
446
447 return address;
448 }
449
450
451 #define DHCP_OPTION_HOSTNAME 12
452
453 static CFStringRef
454 copy_dhcp_name(SCDynamicStoreRef store, CFStringRef serviceID)
455 {
456 CFDictionaryRef info;
457 CFStringRef name = NULL;
458
459 info = SCDynamicStoreCopyDHCPInfo(store, serviceID);
460 if (info != NULL) {
461 CFDataRef data;
462
463 data = DHCPInfoGetOptionData(info, DHCP_OPTION_HOSTNAME);
464 if (data != NULL) {
465 name = CFStringCreateFromExternalRepresentation(NULL, data, kCFStringEncodingUTF8);
466 }
467
468 CFRelease(info);
469 }
470
471 return name;
472 }
473
474
475 static void
476 reverseDNSComplete(int32_t status, char *host, char *serv, void *context)
477 {
478 struct timeval dnsQueryComplete;
479 struct timeval dnsQueryElapsed;
480 CFStringRef hostname;
481 SCDynamicStoreRef store = (SCDynamicStoreRef)context;
482
483 (void) gettimeofday(&dnsQueryComplete, NULL);
484 timersub(&dnsQueryComplete, &dnsQueryStart, &dnsQueryElapsed);
485 SCLog(_verbose, LOG_INFO,
486 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
487 ((status == 0) && (host != NULL)) ? "" : ", host not found",
488 dnsQueryElapsed.tv_sec,
489 dnsQueryElapsed.tv_usec / 1000);
490
491 // use reverse DNS name, if available
492
493 switch (status) {
494 case 0 :
495 /*
496 * if [reverse] DNS query was successful
497 */
498 if (host != NULL) {
499 hostname = CFStringCreateWithCString(NULL, host, kCFStringEncodingUTF8);
500 SCLog(TRUE, LOG_INFO, CFSTR("hostname (reverse DNS query) = %@"), hostname);
501 set_hostname(hostname);
502 CFRelease(hostname);
503 goto done;
504 }
505 break;
506
507 case EAI_NONAME :
508 /*
509 * if no name available
510 */
511 break;
512
513 default :
514 /*
515 * Hmmmm...
516 */
517 SCLog(TRUE, LOG_ERR, CFSTR("getnameinfo() failed: %s"), gai_strerror(status));
518 }
519
520 // get local (multicast DNS) name, if available
521
522 hostname = SCDynamicStoreCopyLocalHostName(store);
523 if (hostname != NULL) {
524 CFMutableStringRef localName;
525
526 SCLog(TRUE, LOG_INFO, CFSTR("hostname (multicast DNS) = %@"), hostname);
527 localName = CFStringCreateMutableCopy(NULL, 0, hostname);
528 CFStringAppend(localName, CFSTR(".local"));
529 set_hostname(localName);
530 CFRelease(localName);
531 CFRelease(hostname);
532 goto done;
533 }
534
535 // use "localhost" if not other name is available
536
537 set_hostname(CFSTR("localhost"));
538
539 done :
540
541 if (host != NULL) free(host);
542 if (serv != NULL) free(serv);
543 dnsActive = FALSE;
544 return;
545 }
546
547
548 static void
549 getnameinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info)
550 {
551 int32_t status;
552
553 status = getnameinfo_async_handle_reply(msg);
554 if ((status == 0) && dnsActive) {
555 // if request has been re-queued
556 return;
557 }
558
559 if (port == dnsPort) {
560 CFRunLoopSourceInvalidate(dnsRLS);
561 CFRelease(dnsRLS);
562 dnsRLS = NULL;
563 CFRelease(dnsPort);
564 dnsPort = NULL;
565 }
566
567 return;
568 }
569
570
571 static void
572 start_dns_query(SCDynamicStoreRef store, CFStringRef address)
573 {
574 char addr[64];
575 Boolean ok;
576 struct sockaddr *sa;
577 struct sockaddr_in sin;
578 struct sockaddr_in6 sin6;
579
580 if (_SC_cfstring_to_cstring(address, addr, sizeof(addr), kCFStringEncodingASCII) == NULL) {
581 SCLog(TRUE, LOG_ERR, CFSTR("could not convert [primary] address"));
582 return;
583 }
584
585 bzero(&sin, sizeof(sin));
586 sin.sin_len = sizeof(sin);
587 sin.sin_family = AF_INET;
588
589 bzero(&sin6, sizeof(sin6));
590 sin6.sin6_len = sizeof(sin6);
591 sin6.sin6_family = AF_INET6;
592
593 if (inet_aton(addr, &sin.sin_addr) == 1) {
594 /*
595 * if IPv4 address
596 */
597 sa = (struct sockaddr *)&sin;
598 } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr) == 1) {
599 /*
600 * if IPv6 address
601 */
602 char *p;
603
604 p = strchr(addr, '%');
605 if (p != NULL) {
606 sin6.sin6_scope_id = if_nametoindex(p+1);
607 }
608
609 sa = (struct sockaddr *)&sin6;
610 } else {
611 goto done;
612 }
613
614 ok = checkResolverReachabilityByAddress(store, sa);
615 if (ok) {
616 CFMachPortContext context = { 0, (void *)store, CFRetain, CFRelease, CFCopyDescription };
617 mach_port_t port;
618 int32_t error;
619
620 (void) gettimeofday(&dnsQueryStart, NULL);
621
622 error = getnameinfo_async_start(&port,
623 sa,
624 sa->sa_len,
625 0, // flags
626 reverseDNSComplete,
627 NULL);
628 if (error != 0) {
629 goto done;
630 }
631
632 dnsActive = TRUE;
633 dnsPort = CFMachPortCreateWithPort(NULL,
634 port,
635 getnameinfo_async_handleCFReply,
636 &context,
637 NULL);
638 dnsRLS = CFMachPortCreateRunLoopSource(NULL, dnsPort, 0);
639 CFRunLoopAddSource(CFRunLoopGetCurrent(), dnsRLS, kCFRunLoopDefaultMode);
640 }
641
642 done :
643
644 return;
645 }
646
647
648 static void
649 update_hostname(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
650 {
651 CFStringRef address = NULL;
652 CFStringRef hostname = NULL;
653 CFStringRef serviceID = NULL;
654
655 // if active, cancel any in-progress attempt to resolve the primary IP address
656
657 if (dnsPort != NULL) {
658 /* cancel the outstanding DNS query */
659 lu_async_call_cancel(CFMachPortGetPort(dnsPort));
660 CFRunLoopSourceInvalidate(dnsRLS);
661 CFRelease(dnsRLS);
662 dnsRLS = NULL;
663 CFRelease(dnsPort);
664 dnsPort = NULL;
665 }
666
667 // get static hostname, if available
668
669 hostname = copy_static_name();
670 if (hostname != NULL) {
671 SCLog(TRUE, LOG_INFO, CFSTR("hostname (static) = %@"), hostname);
672 set_hostname(hostname);
673 goto done;
674 }
675
676 // get [prefs] hostname, if available
677
678 hostname = copy_prefs_hostname(store);
679 if (hostname != NULL) {
680 SCLog(TRUE, LOG_INFO, CFSTR("hostname (prefs) = %@"), hostname);
681 set_hostname(hostname);
682 goto done;
683 }
684
685 // get primary service ID
686
687 serviceID = copy_primary_service(store);
688 if (serviceID == NULL) {
689 goto mDNS;
690 }
691
692 // get DHCP provided name, if available
693
694 hostname = copy_dhcp_name(store, serviceID);
695 if (hostname != NULL) {
696 SCLog(TRUE, LOG_INFO, CFSTR("hostname (DHCP) = %@"), hostname);
697 set_hostname(hostname);
698 goto done;
699 }
700
701 // get DNS name associated with primary IP, if available
702
703 address = copy_primary_ip(store, serviceID);
704 if (address != NULL) {
705 // start reverse DNS query using primary IP address
706 (void) start_dns_query(store, address);
707 goto done;
708 }
709
710 mDNS :
711
712 // get local (multicast DNS) name, if available
713
714 hostname = SCDynamicStoreCopyLocalHostName(store);
715 if (hostname != NULL) {
716 CFMutableStringRef localName;
717
718 SCLog(TRUE, LOG_INFO, CFSTR("hostname (multicast DNS) = %@"), hostname);
719 localName = CFStringCreateMutableCopy(NULL, 0, hostname);
720 CFStringAppend(localName, CFSTR(".local"));
721 set_hostname(localName);
722 CFRelease(localName);
723 goto done;
724 }
725
726 // use "localhost" if not other name is available
727
728 set_hostname(CFSTR("localhost"));
729
730 done :
731
732 if (address) CFRelease(address);
733 if (hostname) CFRelease(hostname);
734 if (serviceID) CFRelease(serviceID);
735
736 return;
737 }
738
739
740 __private_extern__
741 void
742 load_hostname(Boolean verbose)
743 {
744 CFStringRef key;
745 CFMutableArrayRef keys = NULL;
746 CFMutableArrayRef patterns = NULL;
747
748 if (verbose) {
749 _verbose = TRUE;
750 }
751
752 /* initialize a few globals */
753
754 store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), update_hostname, NULL);
755 if (store == NULL) {
756 SCLog(TRUE, LOG_ERR,
757 CFSTR("SCDynamicStoreCreate() failed: %s"),
758 SCErrorString(SCError()));
759 goto error;
760 }
761
762 /* establish notification keys and patterns */
763
764 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
765 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
766
767 /* ...watch for primary service / interface changes */
768 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
769 kSCDynamicStoreDomainState,
770 kSCEntNetIPv4);
771 CFArrayAppendValue(keys, key);
772 CFRelease(key);
773
774 /* ...watch for DNS configuration changes */
775 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
776 kSCDynamicStoreDomainState,
777 kSCEntNetDNS);
778 CFArrayAppendValue(keys, key);
779 CFRelease(key);
780
781 /* ...watch for (per-service) DHCP option changes */
782 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
783 kSCDynamicStoreDomainState,
784 kSCCompAnyRegex,
785 kSCEntNetDHCP);
786 CFArrayAppendValue(patterns, key);
787 CFRelease(key);
788
789 /* ...watch for (BSD) hostname changes */
790 key = SCDynamicStoreKeyCreateComputerName(NULL);
791 CFArrayAppendValue(keys, key);
792 CFRelease(key);
793
794 /* ...watch for local (multicast DNS) hostname changes */
795 key = SCDynamicStoreKeyCreateHostNames(NULL);
796 CFArrayAppendValue(keys, key);
797 CFRelease(key);
798
799 /* register the keys/patterns */
800 if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
801 SCLog(TRUE, LOG_ERR,
802 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
803 SCErrorString(SCError()));
804 goto error;
805 }
806
807 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
808 if (!rls) {
809 SCLog(TRUE, LOG_ERR,
810 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
811 SCErrorString(SCError()));
812 goto error;
813 }
814 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
815
816 CFRelease(keys);
817 CFRelease(patterns);
818 return;
819
820 error :
821
822 if (keys != NULL) CFRelease(keys);
823 if (patterns != NULL) CFRelease(patterns);
824 if (store != NULL) CFRelease(store);
825 return;
826 }
827
828
829 #ifdef MAIN
830 int
831 main(int argc, char **argv)
832 {
833
834 #ifdef DEBUG
835
836 _sc_log = FALSE;
837 if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) {
838 _sc_verbose = TRUE;
839 argv++;
840 argc--;
841 }
842
843 CFStringRef address;
844 CFStringRef hostname;
845 CFStringRef serviceID;
846 SCDynamicStoreRef store;
847
848 store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), NULL, NULL);
849 if (store == NULL) {
850 SCPrint(TRUE, stdout,
851 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
852 SCErrorString(SCError()));
853 exit(1);
854 }
855
856 // get static hostname
857 hostname = copy_static_name();
858 if (hostname != NULL) {
859 SCPrint(TRUE, stdout, CFSTR("hostname (static) = %@\n"), hostname);
860 CFRelease(hostname);
861 }
862
863 // get [prefs] hostname, if available
864 hostname = copy_prefs_hostname(store);
865 if (hostname != NULL) {
866 SCPrint(TRUE, stdout, CFSTR("hostname (prefs) = %@\n"), hostname);
867 CFRelease(hostname);
868 }
869
870 // get primary service
871 serviceID = copy_primary_service(store);
872 if (serviceID != NULL) {
873 SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID);
874 } else {
875 SCPrint(TRUE, stdout, CFSTR("No primary service\n"));
876 goto mDNS;
877 }
878
879 if ((argc == (2+1)) && (argv[1][0] == 's')) {
880 if (serviceID != NULL) CFRelease(serviceID);
881 serviceID = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
882 SCPrint(TRUE, stdout, CFSTR("alternate service ID = %@\n"), serviceID);
883 }
884
885 // get DHCP provided name
886 hostname = copy_dhcp_name(store, serviceID);
887 if (hostname != NULL) {
888 SCPrint(TRUE, stdout, CFSTR("hostname (DHCP) = %@\n"), hostname);
889 CFRelease(hostname);
890 }
891
892 // get primary IP address
893 address = copy_primary_ip(store, serviceID);
894 if (address != NULL) {
895 SCPrint(TRUE, stdout, CFSTR("primary address = %@\n"), address);
896
897 if ((argc == (2+1)) && (argv[1][0] == 'a')) {
898 if (address != NULL) CFRelease(address);
899 address = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
900 SCPrint(TRUE, stdout, CFSTR("alternate primary address = %@\n"), address);
901 }
902
903 // start reverse DNS query using primary IP address
904 start_dns_query(store, address);
905 CFRelease(address);
906 }
907
908 CFRelease(serviceID);
909
910 mDNS :
911
912 // get local (multicast DNS) name, if available
913
914 hostname = SCDynamicStoreCopyLocalHostName(store);
915 if (hostname != NULL) {
916 CFMutableStringRef localName;
917
918 SCPrint(TRUE, stdout, CFSTR("hostname (multicast DNS) = %@\n"), hostname);
919 localName = CFStringCreateMutableCopy(NULL, 0, hostname);
920 CFStringAppend(localName, CFSTR(".local"));
921 CFRelease(localName);
922 }
923
924 if (hostname != NULL) CFRelease(hostname);
925
926 update_hostname(store, NULL, NULL);
927
928 CFRelease(store);
929
930 CFRunLoopRun();
931
932 #else /* DEBUG */
933
934 _sc_log = FALSE;
935 _sc_verbose = (argc > 1) ? TRUE : FALSE;
936
937 load_hostname((argc > 1) ? TRUE : FALSE);
938 CFRunLoopRun();
939 /* not reached */
940
941 #endif /* DEBUG */
942
943 exit(0);
944 return 0;
945 }
946 #endif /* MAIN */