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