+/*
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Modification History
+ *
+ * January 15, 2005 Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/filio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <sys/kern_event.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+
+#include <TargetConditionals.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOMessage.h>
+#include <IOKit/pwr_mgt/IOPM.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+
+#include <dnsinfo.h>
+#include <notify.h>
+#include <utmpx.h>
+
+
+/* generic MessageTracer keys */
+#define MSGTRACER_KEY_DOMAIN "com.apple.message.domain"
+#define MSGTRACER_KEY_SIG "com.apple.message.signature"
+#define MSGTRACER_KEY_UUID "com.apple.message.uuid"
+#define MSGTRACER_KEY_VALUE1 "com.apple.message.value"
+
+
+#define MY_ASL_FACILITY "com.apple.SystemConfiguration.Logger"
+#define MY_MSGTRACER_DOMAIN "com.apple.network.log"
+
+
+static aslmsg log_msg = NULL;
+static io_connect_t power = MACH_PORT_NULL;
+static Boolean verbose = FALSE;
+
+
+static char *
+elapsed()
+{
+ static char str[128];
+ struct tm tm_diff;
+ struct tm tm_now;
+ struct timeval tv_diff;
+ struct timeval tv_now;
+ static struct timeval tv_then = { 0, 0 };
+
+ (void)gettimeofday(&tv_now, NULL);
+
+ (void)localtime_r(&tv_now.tv_sec, &tm_now);
+
+ timersub(&tv_now, &tv_then, &tv_diff);
+ (void)localtime_r(&tv_diff.tv_sec, &tm_diff);
+#ifdef MAIN
+ sprintf(str, "%2d:%02d:%02d.%03d (+%ld.%03d)",
+ tm_now.tm_hour,
+ tm_now.tm_min,
+ tm_now.tm_sec,
+ tv_now.tv_usec / 1000,
+ tv_diff.tv_sec,
+ tv_diff.tv_usec / 1000);
+#else
+ sprintf(str, ".%03d (+%ld.%03d)",
+ tv_now.tv_usec / 1000,
+ tv_diff.tv_sec,
+ tv_diff.tv_usec / 1000);
+#endif
+
+ tv_then = tv_now;
+ return str;
+}
+
+
+#pragma mark -
+#pragma mark [Network] Kernel Events
+
+
+static CFStringRef
+copyInterfaceFlags(const char *if_name)
+{
+ const char * iff_up = "? ";
+ struct ifreq ifr;
+ const char *ifm_active = "? ";
+ int sock;
+ CFStringRef str = NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("socket() failed"));
+ return NULL;
+ }
+
+ bzero((char *)&ifr, sizeof(ifr));
+ (void) strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+ if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == 0) {
+ struct ifmediareq ifm;
+
+ iff_up = (ifr.ifr_flags & IFF_UP) ? "yes" : "no ";
+
+ bzero((char *)&ifm, sizeof(ifm));
+ (void) strncpy(ifm.ifm_name, if_name, sizeof(ifm.ifm_name));
+ if ((ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) == 0) &&
+ (ifm.ifm_count > 0) &&
+ (ifm.ifm_status & IFM_AVALID)) {
+ ifm_active = (ifm.ifm_status & IFM_ACTIVE) ? "yes" : "no ";
+ }
+
+ str = CFStringCreateWithFormat(NULL,
+ NULL,
+ CFSTR("\n%-5s: IFF_UP = %s IFM_ACTIVE = %s"),
+ if_name,
+ iff_up,
+ ifm_active);
+ }
+
+ (void)close(sock);
+
+ return str;
+}
+
+
+static int
+prefixLength(struct sockaddr_in6 *sin6)
+{
+ register u_int8_t *name = &sin6->sin6_addr.s6_addr[0];
+ register int byte;
+ register int bit;
+ int plen = 0;
+
+ for (byte = 0; byte < sizeof(struct in6_addr); byte++, plen += 8) {
+ if (name[byte] != 0xff) {
+ break;
+ }
+ }
+
+ if (byte == sizeof(struct in6_addr)) {
+ return plen;
+ }
+
+ for (bit = 7; bit != 0; bit--, plen++) {
+ if (!(name[byte] & (1 << bit))) {
+ break;
+ }
+ }
+
+ for (; bit != 0; bit--) {
+ if (name[byte] & (1 << bit)) {
+ return 0;
+ }
+ }
+
+ byte++;
+ for (; byte < sizeof(struct in6_addr); byte++) {
+ if (name[byte]) {
+ return 0;
+ }
+ }
+
+ return plen;
+}
+
+
+static void
+KernelEvent_notification(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
+{
+ int so = CFSocketGetNative(s);
+ int status;
+ char buf[1024];
+ struct kern_event_msg *ev_msg = (struct kern_event_msg *)&buf[0];
+ int offset = 0;
+
+ status = recv(so, &buf, sizeof(buf), 0);
+ if (status == -1) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("recv() failed: %s"), strerror(errno));
+ CFSocketInvalidate(s);
+ return;
+ }
+
+ while (offset < status) {
+ if ((offset + ev_msg->total_size) > status) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("missed SYSPROTO_EVENT event, buffer not big enough"));
+ break;
+ }
+
+ switch (ev_msg->vendor_code) {
+ case KEV_VENDOR_APPLE :
+ switch (ev_msg->kev_class) {
+ case KEV_NETWORK_CLASS : {
+ void *event_data = &ev_msg->event_data[0];
+
+ switch (ev_msg->kev_subclass) {
+ case KEV_DL_SUBCLASS : {
+ struct net_event_data *ev;
+ char if_name[IFNAMSIZ+1];
+
+ ev = (struct net_event_data *)event_data;
+
+ bzero(&if_name, sizeof(if_name));
+ snprintf(if_name, IFNAMSIZ, "%s%d",
+ ev->if_name,
+ ev->if_unit);
+
+ switch (ev_msg->event_code) {
+ case KEV_DL_IF_ATTACHED : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: attached"),
+ elapsed(),
+ if_name);
+ break;
+ }
+ case KEV_DL_IF_DETACHING : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: detaching"),
+ elapsed(),
+ if_name);
+ break;
+ }
+ case KEV_DL_IF_DETACHED : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: detached"),
+ elapsed(),
+ if_name);
+ break;
+ }
+ case KEV_DL_LINK_OFF : {
+ CFStringRef str;
+
+ str = verbose ? copyInterfaceFlags(if_name) : NULL;
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: link down%@"),
+ elapsed(),
+ if_name,
+ str != NULL ? str : CFSTR(""));
+ if (str != NULL) CFRelease(str);
+ break;
+ }
+ case KEV_DL_LINK_ON : {
+ CFStringRef str;
+
+ str = verbose ? copyInterfaceFlags(if_name) : NULL;
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: link up%@"),
+ elapsed(),
+ if_name,
+ str != NULL ? str : CFSTR(""));
+ if (str != NULL) CFRelease(str);
+ break;
+ }
+ default :
+ break;
+ }
+ break;
+ }
+ case KEV_INET_SUBCLASS : {
+ char addr[128];
+ struct kev_in_data *ev;
+ char if_name[IFNAMSIZ+1];
+ char mask[128];
+
+ ev = (struct kev_in_data *)event_data;
+
+ bzero(&if_name, sizeof(if_name));
+ snprintf(if_name, IFNAMSIZ, "%s%d",
+ ev->link_data.if_name,
+ ev->link_data.if_unit);
+
+ switch (ev_msg->event_code) {
+ case KEV_INET_NEW_ADDR :
+ case KEV_INET_CHANGED_ADDR :
+ case KEV_INET_ADDR_DELETED : {
+ struct sockaddr_in sin;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr = ev->ia_addr;
+ _SC_sockaddr_to_string((struct sockaddr *)&sin, addr, sizeof(addr));
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ntohl(ev->ia_subnetmask);
+ _SC_sockaddr_to_string((struct sockaddr *)&sin, mask, sizeof(mask));
+ break;
+ }
+ default :
+ break;
+ }
+
+ switch (ev_msg->event_code) {
+ case KEV_INET_NEW_ADDR : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv4 address added (%s/%s)"),
+ elapsed(),
+ if_name,
+ addr,
+ mask);
+ break;
+ }
+ case KEV_INET_CHANGED_ADDR : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv4 address changed (%s/%s)"),
+ elapsed(),
+ if_name,
+ addr,
+ mask);
+ break;
+ }
+ case KEV_INET_ADDR_DELETED : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv4 address removed (%s/%s)"),
+ elapsed(),
+ if_name,
+ addr,
+ mask);
+ break;
+ }
+ default :
+ break;
+ }
+ break;
+ }
+ case KEV_INET6_SUBCLASS : {
+ char addr[128];
+ struct kev_in6_data *ev;
+ char if_name[IFNAMSIZ+1];
+ int plen = 0;
+
+ ev = (struct kev_in6_data *)event_data;
+
+ bzero(&if_name, sizeof(if_name));
+ snprintf(if_name, IFNAMSIZ, "%s%d",
+ ev->link_data.if_name,
+ ev->link_data.if_unit);
+
+ switch (ev_msg->event_code) {
+ case KEV_INET6_NEW_USER_ADDR :
+ case KEV_INET6_NEW_LL_ADDR :
+ case KEV_INET6_CHANGED_ADDR :
+ case KEV_INET6_ADDR_DELETED : {
+ _SC_sockaddr_to_string((struct sockaddr *)&ev->ia_addr, addr, sizeof(addr));
+ plen = prefixLength(&ev->ia_prefixmask);
+ break;
+ }
+ default :
+ break;
+ }
+
+ switch (ev_msg->event_code) {
+ case KEV_INET6_NEW_USER_ADDR :
+ case KEV_INET6_NEW_LL_ADDR : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv6 address added (%s/%d)"),
+ elapsed(),
+ if_name,
+ addr,
+ plen);
+ break;
+ }
+ case KEV_INET6_CHANGED_ADDR : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv6 address changed (%s/%d)"),
+ elapsed(),
+ if_name,
+ addr,
+ plen);
+ break;
+ }
+ case KEV_INET6_ADDR_DELETED : {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s kernel event: %s: IPv6 address removed"),
+ elapsed(),
+ if_name);
+ break;
+ }
+ default :
+ break;
+ }
+ break;
+ }
+ default :
+ break;
+ }
+ break;
+ }
+ default :
+ break;
+ }
+ break;
+ default :
+ /* unrecognized vendor code */
+ break;
+ }
+ offset += ev_msg->total_size;
+ ev_msg = (struct kern_event_msg *)&buf[offset];
+ }
+
+ return;
+}
+
+
+static void
+add_KernelEvent_notification()
+{
+ CFSocketRef es;
+ CFSocketContext es_context = { 0, NULL, NULL, NULL, NULL };
+ struct kev_request kev_req;
+ CFRunLoopSourceRef rls;
+ int so;
+ int yes = 1;
+
+ /* Open an event socket */
+ so = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
+ if (so == -1) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("socket() failed"));
+ return;
+ }
+
+ /* establish filter to return all events */
+ kev_req.vendor_code = 0;
+ kev_req.kev_class = 0; /* Not used if vendor_code is 0 */
+ kev_req.kev_subclass = 0; /* Not used if either kev_class OR vendor_code are 0 */
+ if (ioctl(so, SIOCSKEVFILT, &kev_req) == -1) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("ioctl(, SIOCSKEVFILT, ) failed"));
+ (void)close(so);
+ return;
+ }
+
+ if (ioctl(so, FIONBIO, &yes) == -1) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("ioctl(, FIONBIO, ) failed"));
+ (void)close(so);
+ return;
+ }
+
+ /* Create a CFSocketRef for the PF_SYSTEM kernel event socket */
+ es = CFSocketCreateWithNative(NULL,
+ so,
+ kCFSocketReadCallBack,
+ KernelEvent_notification,
+ &es_context);
+
+ /* Create and add a run loop source for the event socket */
+ rls = CFSocketCreateRunLoopSource(NULL, es, -1);
+ CFRelease(es);
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ return;
+}
+
+
+#pragma mark -
+#pragma mark Power Management Events
+
+
+static void
+power_notification(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ switch (messageType) {
+ case kIOMessageCanDevicePowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: can device power off?"),
+ elapsed());
+ break;
+ case kIOMessageDeviceWillPowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: device will power off"),
+ elapsed());
+ break;
+ case kIOMessageDeviceWillNotPowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: device will not power off"),
+ elapsed());
+ break;
+ case kIOMessageDeviceHasPoweredOn :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: device has powered on"),
+ elapsed());
+ break;
+ case kIOMessageCanSystemPowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: can system power off?"),
+ elapsed());
+ break;
+ case kIOMessageSystemWillPowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will power off"),
+ elapsed());
+ break;
+ case kIOMessageSystemWillNotPowerOff :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will not power off"),
+ elapsed());
+ break;
+ case kIOMessageCanSystemSleep :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: can system sleep?"),
+ elapsed());
+ /*
+ * Idle sleep is about to kick in, but applications have
+ * a chance to allow sleep (by calling IOAllowPowerChange)
+ * or to prevent sleep (by calling IOCancelPowerChange).
+ */
+ IOAllowPowerChange(power, (long)messageArgument);
+ break;
+ case kIOMessageSystemWillSleep :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will sleep"),
+ elapsed());
+ IOAllowPowerChange(power, (long)messageArgument);
+ break;
+ case kIOMessageSystemWillNotSleep :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will not sleep"),
+ elapsed());
+ break;
+ case kIOMessageSystemHasPoweredOn :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system has powered on"),
+ elapsed());
+ break;
+ case kIOMessageSystemWillRestart :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will restart"),
+ elapsed());
+ break;
+ case kIOMessageSystemWillPowerOn :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: system will power on"),
+ elapsed());
+ break;
+ default :
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s IORegisterForSystemPower: message=%08lx"),
+ elapsed(),
+ (long unsigned int)messageType);
+ break;
+ }
+
+ return;
+}
+
+
+static void
+add_power_notification()
+{
+ io_object_t iterator;
+ IONotificationPortRef notify;
+
+ power = IORegisterForSystemPower(0, ¬ify, power_notification, &iterator);
+ if (power == MACH_PORT_NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("IORegisterForSystemPower() failed"));
+ return;
+ }
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(notify),
+ kCFRunLoopCommonModes);
+
+ return;
+}
+
+
+#ifdef kIOPMMessageSleepWakeUUIDChange
+static void
+wake_uuid_notification(void *refcon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ CFStringRef wake_uuid = NULL;
+
+ if (messageType == kIOPMMessageSleepWakeUUIDChange) {
+ if (messageArgument == kIOPMMessageSleepWakeUUIDSet) {
+ wake_uuid = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPMSleepWakeUUIDKey), NULL, 0);
+ }
+
+ if (wake_uuid != NULL) {
+ char uuid[256];
+
+ _SC_cfstring_to_cstring(wake_uuid, uuid, sizeof(uuid), kCFStringEncodingUTF8);
+ asl_set(log_msg, MSGTRACER_KEY_DOMAIN, MY_MSGTRACER_DOMAIN);
+ asl_set(log_msg, MSGTRACER_KEY_UUID , uuid);
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s wake UUID notification: UUID set (%@)"),
+ elapsed(),
+ wake_uuid);
+
+ CFRelease(wake_uuid);
+ } else {
+ asl_unset(log_msg, MSGTRACER_KEY_DOMAIN);
+ asl_unset(log_msg, MSGTRACER_KEY_UUID);
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s wake UUID notification: UUID not set"),
+ elapsed());
+ }
+ }
+
+ return;
+}
+
+
+static void
+add_wake_uuid_notification()
+{
+ kern_return_t kr;
+ io_object_t notification = IO_OBJECT_NULL;
+ IONotificationPortRef notifyPort;
+ io_service_t service;
+
+ notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
+ service = IORegistryEntryFromPath(kIOMasterPortDefault,
+ kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
+ kr = IOServiceAddInterestNotification(notifyPort,
+ service,
+ kIOGeneralInterest,
+ wake_uuid_notification,
+ NULL, // refCon
+ ¬ification);
+ if (kr != KERN_SUCCESS) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR,
+ CFSTR("IOServiceAddInterestNotification() failed, kr=0x%x"),
+ kr);
+ return;
+ }
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(notifyPort),
+ kCFRunLoopDefaultMode);
+
+ wake_uuid_notification(NULL,
+ service,
+ kIOPMMessageSleepWakeUUIDChange,
+ kIOPMMessageSleepWakeUUIDSet);
+
+ return;
+}
+#endif // kIOPMMessageSleepWakeUUIDChange
+
+
+#pragma mark -
+#pragma mark SCDynamicStore "network" Events
+
+
+static void
+NetworkChange_notification(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
+{
+ CFIndex i;
+ CFIndex n;
+ CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
+
+ CFStringAppendFormat(str,
+ NULL,
+ CFSTR("%s SCDynamicStore \"network\" notification"),
+ elapsed());
+
+ n = CFArrayGetCount(changedKeys);
+ for (i = 0; i < n; i++) {
+ CFStringRef key;
+
+ key = CFArrayGetValueAtIndex(changedKeys, i);
+ if (CFStringHasSuffix(key, kSCEntNetLink)) {
+ CFDictionaryRef dict;
+ const char *val = "?";
+
+ dict = SCDynamicStoreCopyValue(store, key);
+ if (dict != NULL) {
+ CFBooleanRef link;
+
+ link = CFDictionaryGetValue(dict, kSCPropNetLinkActive);
+ if (link != NULL) {
+ val = CFBooleanGetValue(link) ? "up" : "down";
+ }
+
+ CFRelease(dict);
+ }
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@ (%s)"), key, val);
+ } else if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
+ CFStringHasSuffix(key, kSCEntNetIPv6)) {
+ CFDictionaryRef dict;
+
+ dict = SCDynamicStoreCopyValue(store, key);
+ if (dict != NULL) {
+ CFStringRef val;
+
+ val = _SCCopyDescription(dict, NULL);
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@ : %@"), key, val);
+ CFRelease(val);
+ CFRelease(dict);
+ } else {
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@ : removed"), key);
+ }
+ } else {
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@"), key);
+ }
+ }
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO, CFSTR("%@"), str);
+ CFRelease(str);
+ return;
+}
+
+
+static void
+add_NetworkChange_notification()
+{
+ CFStringRef dns_key;
+ CFStringRef key;
+ CFMutableArrayRef keys;
+ Boolean ok;
+ CFStringRef pattern;
+ CFMutableArrayRef patterns;
+ SCDynamicStoreRef store;
+ CFRunLoopSourceRef rls;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("Logger.bundle-NetworkChange"), NetworkChange_notification, NULL);
+ if (store == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ // Interface list
+
+ key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ // IPv4
+
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ // IPv6
+
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ // Link
+
+ pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetLink);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ // AirPort (e.g. BSSID)
+
+ pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetAirPort);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+
+ // DNS
+
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ dns_key = CFStringCreateWithCString(NULL,
+ dns_configuration_notify_key(),
+ kCFStringEncodingASCII);
+ key = CFStringCreateWithFormat(NULL, NULL, CFSTR("Notify:%@"), dns_key);
+ CFRelease(dns_key);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ // Proxies
+
+ key = SCDynamicStoreKeyCreateProxies(NULL);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ // ComputerName, LocalHostName
+
+ key = SCDynamicStoreKeyCreateComputerName(NULL);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ key = SCDynamicStoreKeyCreateHostNames(NULL);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns);
+ CFRelease(keys);
+ CFRelease(patterns);
+ if (!ok) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
+ CFRelease(store);
+ return;
+ }
+
+ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(store);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(store);
+ return;
+}
+
+
+static void
+PrimaryService_notification(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
+{
+ CFDictionaryRef entity;
+ CFStringRef key;
+ static CFStringRef oldPrimary = NULL;
+ CFStringRef newPrimary = NULL;
+
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+ entity = SCDynamicStoreCopyValue(store, key);
+ CFRelease(key);
+ if (isA_CFDictionary(entity) &&
+ CFDictionaryGetValueIfPresent(entity,
+ kSCDynamicStorePropNetPrimaryService,
+ (const void **)&newPrimary) &&
+ isA_CFString(newPrimary)) {
+ CFRetain(newPrimary);
+ } else {
+ newPrimary = NULL;
+ }
+
+ if (!_SC_CFEqual(oldPrimary, newPrimary)) {
+ if (newPrimary != NULL) {
+ CFStringRef newInterface;
+
+ newInterface = CFDictionaryGetValue(entity, kSCDynamicStorePropNetPrimaryInterface);
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s Primary service: %@ (%@)"),
+ elapsed(),
+ newPrimary,
+ newInterface != NULL ? newInterface : CFSTR("?"));
+ } else {
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s Primary service: removed"),
+ elapsed());
+ }
+ }
+
+ if (oldPrimary != NULL) CFRelease(oldPrimary);
+ oldPrimary = newPrimary;
+
+ if (entity != NULL) CFRelease(entity);
+ return;
+}
+
+
+static void
+add_PrimaryService_notification()
+{
+ CFStringRef key;
+ CFMutableArrayRef keys;
+ Boolean ok;
+ SCDynamicStoreRef store;
+ CFRunLoopSourceRef rls;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("Logger.bundle-PrimaryService"), PrimaryService_notification, NULL);
+ if (store == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+ CFRelease(keys);
+ if (!ok) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
+ CFRelease(store);
+ return;
+ }
+
+ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(store);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(store);
+ return;
+}
+
+
+#pragma mark -
+#pragma mark Reachability Events
+
+
+static void
+reachability_notification(SCNetworkReachabilityRef ref, SCNetworkReachabilityFlags flags, void *info)
+{
+ CFStringRef hostname = (CFStringRef)info;
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s reachability changed: %@: flags=0x%08x"),
+ elapsed(),
+ hostname,
+ flags);
+ return;
+}
+
+
+static void
+add_reachability_notification(CFArrayRef hosts)
+{
+ SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
+ CFIndex i;
+ CFIndex n;
+ SCNetworkReachabilityRef target;
+
+ struct watch {
+ in_addr_t addr;
+ CFStringRef name;
+ } watchAddresses[] = { { 0, CFSTR("0.0.0.0") },
+ { IN_LINKLOCALNETNUM, CFSTR("169.254.0.0") },
+ { (u_int32_t)0xe00000fb, CFSTR("224.0.0.251") },
+ };
+
+ for (i = 0; i < sizeof(watchAddresses)/sizeof(watchAddresses[0]); i++) {
+ struct sockaddr_in sin;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(watchAddresses[i].addr);
+
+ target = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin);
+ if (target == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilityCreateWithAddress() failed"));
+ return;
+ }
+
+ context.info = (void *)watchAddresses[i].name;
+ if (!SCNetworkReachabilitySetCallback(target, reachability_notification, &context)) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilitySetCallback() failed"));
+ CFRelease(target);
+ return;
+ }
+
+ if (!SCNetworkReachabilityScheduleWithRunLoop(target, CFRunLoopGetCurrent(), kCFRunLoopCommonModes)) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilityScheduleWithRunLoop() failed"));
+ CFRelease(target);
+ return;
+ }
+
+ CFRelease(target);
+ }
+
+ n = (hosts != NULL) ? CFArrayGetCount(hosts) : 0;
+ for (i = 0; i < n; i++) {
+ CFStringRef host;
+ char *nodename;
+
+ host = CFArrayGetValueAtIndex(hosts, i);
+ if (!isA_CFString(host) || (CFStringGetLength(host) == 0)) {
+ continue;
+ }
+
+ nodename = _SC_cfstring_to_cstring(host, NULL, 0, kCFStringEncodingUTF8);
+ target = SCNetworkReachabilityCreateWithName(NULL, nodename);
+ CFAllocatorDeallocate(NULL, nodename);
+ if (target == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilityCreateWithName() failed"));
+ return;
+ }
+
+ context.info = (void *)host;
+ if (!SCNetworkReachabilitySetCallback(target, reachability_notification, &context)) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilitySetCallback() failed"));
+ CFRelease(target);
+ return;
+ }
+
+ if (!SCNetworkReachabilityScheduleWithRunLoop(target, CFRunLoopGetCurrent(), kCFRunLoopCommonModes)) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCNetworkReachabilityScheduleWithRunLoop() failed"));
+ CFRelease(target);
+ return;
+ }
+
+ CFRelease(target);
+ }
+
+ return;
+}
+
+
+#pragma mark -
+#pragma mark Console User/Information Events
+
+
+#if !TARGET_OS_EMBEDDED
+static void
+console_notification(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
+{
+ gid_t gid;
+ CFArrayRef info;
+ CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
+ uid_t uid;
+ CFStringRef user;
+
+ CFStringAppendFormat(str,
+ NULL,
+ CFSTR("%s SCDynamicStore console notification"),
+ elapsed());
+
+ user = SCDynamicStoreCopyConsoleUser(store, &uid, &gid);
+ if (user != NULL) {
+ CFStringAppendFormat(str, NULL, CFSTR("\nconsole user = %@"), user);
+ CFRelease(user);
+ } else {
+ CFStringAppendFormat(str, NULL, CFSTR("\nno console user"));
+ }
+
+ info = SCDynamicStoreCopyConsoleInformation(store);
+ if (info != NULL) {
+ CFIndex i;
+ CFIndex n;
+
+ n = CFArrayGetCount(info);
+ for (i = 0; i < n; i++) {
+ CFDictionaryRef session;
+ CFNumberRef sessionID;
+ CFStringRef sessionUserName;
+ CFBooleanRef sessionOnConsole;
+
+ session = CFArrayGetValueAtIndex(info, i);
+ sessionID = CFDictionaryGetValue(session, kSCConsoleSessionID);
+ sessionUserName = CFDictionaryGetValue(session, kSCConsoleSessionUserName);
+ sessionOnConsole = CFDictionaryGetValue(session, kSCConsoleSessionOnConsole);
+
+ CFStringAppendFormat(str, NULL, CFSTR("\n%d : id=%@, user=%@, console=%s"),
+ i,
+ sessionID,
+ sessionUserName != NULL ? sessionUserName : CFSTR("?"),
+ sessionOnConsole != NULL ? CFBooleanGetValue(sessionOnConsole) ? "yes" : "no" : "?");
+ }
+
+ CFRelease(info);
+ }
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO, CFSTR("%@"), str);
+ CFRelease(str);
+ return;
+}
+
+
+static void
+add_console_notification()
+{
+ CFStringRef key;
+ CFMutableArrayRef keys;
+ Boolean ok;
+ SCDynamicStoreRef store;
+ CFRunLoopSourceRef rls;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("Logger.bundle-console"), console_notification, NULL);
+ if (store == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ key = SCDynamicStoreKeyCreateConsoleUser(NULL);
+ CFArrayAppendValue(keys, key);
+ CFRelease(key);
+
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+ CFRelease(keys);
+ if (!ok) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
+ CFRelease(store);
+ return;
+ }
+
+ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(store);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(store);
+ return;
+}
+#endif // !TARGET_OS_EMBEDDED
+
+
+#pragma mark -
+#pragma mark Directory Services Events
+
+
+//#include <DirectoryServices/DirServicesPriv.h>
+#ifndef kDSStdNotifySearchPolicyChanged
+#define kDSStdNotifySearchPolicyChanged "com.apple.DirectoryService.NotifyTypeStandard:SearchPolicyChanged"
+#endif
+
+
+#if !TARGET_OS_EMBEDDED
+static void
+directoryServices_notification(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
+{
+ CFIndex i;
+ CFIndex n;
+ CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
+
+ CFStringAppendFormat(str,
+ NULL,
+ CFSTR("%s SCDynamicStore DirectoryServices notification"),
+ elapsed());
+
+ n = CFArrayGetCount(changedKeys);
+ for (i = 0; i < n; i++) {
+ CFStringRef key;
+
+ key = CFArrayGetValueAtIndex(changedKeys, i);
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@"), key);
+ }
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO, CFSTR("%@"), str);
+ CFRelease(str);
+ return;
+}
+
+
+static void
+add_DirectoryServices_notification()
+{
+ CFStringRef key;
+ CFMutableArrayRef keys;
+ Boolean ok;
+ SCDynamicStoreRef store;
+ CFRunLoopSourceRef rls;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("Logger.bundle-directoryServices"), directoryServices_notification, NULL);
+ if (store == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ key = CFSTR(kDSStdNotifySearchPolicyChanged);
+ CFArrayAppendValue(keys, key);
+// CFRelease(key);
+
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+ CFRelease(keys);
+ if (!ok) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
+ CFRelease(store);
+ return;
+ }
+
+ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(store);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(store);
+ return;
+}
+#endif // !TARGET_OS_EMBEDDED
+
+
+#pragma mark -
+#pragma mark DNS Configuration Events
+
+
+static void
+dnsinfo_notification(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s dnsinfo notification"),
+ elapsed());
+
+ return;
+}
+
+
+static void
+add_dnsinfo_notification()
+{
+ const char *key;
+ CFMachPortRef mp;
+ mach_port_t notify_port;
+ int notify_token;
+ CFRunLoopSourceRef rls;
+ uint32_t status;
+
+ key = dns_configuration_notify_key();
+ status = notify_register_mach_port(key, ¬ify_port, 0, ¬ify_token);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_register_mach_port() failed"));
+ return;
+ }
+
+ mp = CFMachPortCreateWithPort(NULL, notify_port, dnsinfo_notification, NULL, NULL);
+ if (mp == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("CFMachPortCreateWithPort() failed"));
+ (void)notify_cancel(notify_token);
+ return;
+ }
+
+ rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(mp);
+ (void)notify_cancel(notify_token);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(mp);
+ return;
+}
+
+
+#pragma mark -
+#pragma mark Network Configuration Change Events
+
+
+#define NETWORKCHANGED_NOTIFY_KEY "com.apple.system.config.network_change"
+
+static void
+network_notification(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s network_change notification"),
+ elapsed());
+
+ return;
+}
+
+
+static void
+add_network_notification()
+{
+ CFMachPortRef mp;
+ mach_port_t notify_port;
+ int notify_token;
+ CFRunLoopSourceRef rls;
+ uint32_t status;
+
+ status = notify_register_mach_port(NETWORKCHANGED_NOTIFY_KEY,
+ ¬ify_port,
+ 0,
+ ¬ify_token);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_register_mach_port() failed"));
+ return;
+ }
+
+ mp = CFMachPortCreateWithPort(NULL, notify_port, network_notification, NULL, NULL);
+ if (mp == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("CFMachPortCreateWithPort() failed"));
+ (void)notify_cancel(notify_token);
+ return;
+ }
+
+ rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(mp);
+ (void)notify_cancel(notify_token);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(mp);
+ return;
+}
+
+
+#pragma mark -
+#pragma mark SMB Configuration Events
+
+
+#define SMBCONFIGURATION_NOTIFY_KEY "com.apple.system.SystemConfiguration.smb_configuration"
+
+
+#if !TARGET_OS_EMBEDDED
+static void
+smbconf_notification(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO,
+ CFSTR("%s smb.conf notification"),
+ elapsed());
+
+ return;
+}
+
+
+static void
+add_smbconf_notification()
+{
+ CFMachPortRef mp;
+ mach_port_t notify_port;
+ int notify_token;
+ CFRunLoopSourceRef rls;
+ uint32_t status;
+
+ status = notify_register_mach_port(SMBCONFIGURATION_NOTIFY_KEY,
+ ¬ify_port,
+ 0,
+ ¬ify_token);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_register_mach_port() failed"));
+ return;
+ }
+
+ mp = CFMachPortCreateWithPort(NULL, notify_port, smbconf_notification, NULL, NULL);
+ if (mp == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("CFMachPortCreateWithPort() failed"));
+ (void)notify_cancel(notify_token);
+ return;
+ }
+
+ rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(mp);
+ (void)notify_cancel(notify_token);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(mp);
+ return;
+}
+#endif // !TARGET_OS_EMBEDDED
+
+
+#pragma mark -
+#pragma mark pututxline Events
+
+
+#if !TARGET_OS_EMBEDDED
+static const char *
+ut_time(struct utmpx *utmpx)
+{
+ static char str[16];
+ struct tm tm;
+
+ (void)localtime_r(&utmpx->ut_tv.tv_sec, &tm);
+ snprintf(str, sizeof(str), "%2d:%02d:%02d.%03d",
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ utmpx->ut_tv.tv_usec / 1000);
+
+ return str;
+}
+
+
+static const char *
+ut_id(struct utmpx *utmpx)
+{
+ char *cp;
+ static char str[16];
+
+ cp = utmpx->ut_id + sizeof(utmpx->ut_id);
+ while(--cp >= utmpx->ut_id && isprint(*cp)) {}
+ if(cp < utmpx->ut_id) {
+ snprintf(str, sizeof(str), "%-4.4s", utmpx->ut_id);
+ } else {
+ snprintf(str, sizeof(str),
+ "0x%2.2x%2.2x%2.2x%2.2x",
+ utmpx->ut_id[0],
+ utmpx->ut_id[1],
+ utmpx->ut_id[2],
+ utmpx->ut_id[3]);
+ }
+
+ return str;
+}
+
+
+static const char *
+ut_pid(struct utmpx *utmpx)
+{
+ static char pid[16];
+
+ snprintf(pid, sizeof(pid), "%d", utmpx->ut_pid);
+
+ return pid;
+}
+
+
+static void
+pututxline_notification(CFMachPortRef port, void *msg, CFIndex size, void *info)
+{
+ CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
+ struct utmpx *utmpx;
+
+ CFStringAppendFormat(str,
+ NULL,
+ CFSTR("%s pututxline notification"),
+ elapsed());
+
+ setutxent();
+ while ((utmpx = getutxent()) != NULL) {
+ const char * entry_id = NULL;
+ const char * entry_line = NULL;
+ const char * entry_pid = NULL;
+ const char * entry_tv = NULL;
+ const char * entry_type;
+ const char * entry_user = NULL;
+ char line[128];
+ int n;
+
+ switch (utmpx->ut_type) {
+ case BOOT_TIME : // Time of a system boot.
+ entry_type = "Boot";
+ entry_tv = ut_time(utmpx);
+ break;
+ case DEAD_PROCESS : // A session leader exited.
+ entry_type = "Dead process";
+ entry_id = ut_id (utmpx);
+ entry_pid = ut_pid (utmpx);
+ entry_tv = ut_time(utmpx);
+ break;
+ case EMPTY : // No valid user accounting information.
+ continue;
+ case INIT_PROCESS : // A process spawned by init(8).
+ entry_type = "Init process";
+ entry_id = ut_id (utmpx);
+ entry_pid = ut_pid (utmpx);
+ entry_tv = ut_time(utmpx);
+ break;
+ case LOGIN_PROCESS : // The session leader of a logged-in user.
+ entry_type = "Login";
+ entry_id = ut_id (utmpx);
+ entry_user = utmpx->ut_user;
+ entry_pid = ut_pid (utmpx);
+ entry_tv = ut_time(utmpx);
+ break;
+ case NEW_TIME : // Time after system clock change.
+ entry_type = "New time";
+ entry_tv = ut_time(utmpx);
+ break;
+ case OLD_TIME : // Time before system clock change.
+ entry_type = "Old time";
+ entry_tv = ut_time(utmpx);
+ break;
+ case RUN_LVL : // Run level. Provided for compatibility, not used.
+ entry_type = "Run level";
+ break;
+ case USER_PROCESS : // A user process.
+ entry_type = "User Process";
+ entry_id = ut_id (utmpx);
+ entry_user = utmpx->ut_user;
+ entry_line = utmpx->ut_line;
+ entry_pid = ut_pid (utmpx);
+ entry_tv = ut_time(utmpx);
+ break;
+ case SHUTDOWN_TIME : // Time of system shutdown
+ entry_type = "Shutdown time";
+ entry_tv = ut_time(utmpx);
+ break;
+ default :
+ entry_type = "Unknown";
+ break;
+ }
+
+ snprintf(line, sizeof(line),
+ // type time id=0x12345678 pid=12345 user=abcdefgh line
+ "\n%-13s %2s%12s %3s%-10s %4s%-5s %5s%-8s %5s%s",
+ entry_type,
+ entry_tv != NULL ? "@ " : "",
+ entry_tv != NULL ? entry_tv : "", // hh:mm:ss.ddd
+ entry_id != NULL ? "id=" : "",
+ entry_id != NULL ? entry_id : "", // 0x12345678
+ entry_pid != NULL ? "pid=" : "",
+ entry_pid != NULL ? entry_pid : "", // #####
+ entry_user != NULL ? "user=" : "",
+ entry_user != NULL ? entry_user : "", // <=256 chars
+ entry_line != NULL ? "line=" : "",
+ entry_line != NULL ? entry_line : "" // <= 32 chars
+ );
+
+ n = strlen(line) - 1;
+ while ((n > 0) && (line[n] == ' ')) {
+ line[n] = '\0';
+ --n;
+ }
+
+ CFStringAppendFormat(str, NULL, CFSTR("%s"), line);
+ }
+ endutxent();
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO, CFSTR("%@"), str);
+ CFRelease(str);
+ return;
+}
+
+
+static void
+add_pututxline_notification()
+{
+ CFMachPortRef mp;
+ mach_port_t notify_port;
+ int notify_token;
+ CFRunLoopSourceRef rls;
+ uint32_t status;
+
+ status = notify_register_mach_port(UTMPX_CHANGE_NOTIFICATION, ¬ify_port, 0, ¬ify_token);
+ if (status != NOTIFY_STATUS_OK) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_register_mach_port() failed"));
+ return;
+ }
+
+ mp = CFMachPortCreateWithPort(NULL, notify_port, pututxline_notification, NULL, NULL);
+ if (mp == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("CFMachPortCreateWithPort() failed"));
+ (void)notify_cancel(notify_token);
+ return;
+ }
+
+ rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(mp);
+ (void)notify_cancel(notify_token);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(mp);
+ return;
+}
+#endif // !TARGET_OS_EMBEDDED
+
+
+#pragma mark -
+#pragma mark BackToMyMac Status Events
+
+
+#ifndef kDSStdNotifyBTMMStatusChanged
+#define kDSStdNotifyBTMMStatusChanged "State:/Network/BackToMyMac"
+#endif
+
+
+#if !TARGET_OS_EMBEDDED
+static void
+BTMM_notification(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
+{
+ CFIndex i;
+ CFIndex n;
+ CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
+
+ CFStringAppendFormat(str,
+ NULL,
+ CFSTR("%s SCDynamicStore Back to My Mac notification"),
+ elapsed());
+
+ n = CFArrayGetCount(changedKeys);
+ for (i = 0; i < n; i++) {
+ CFStringRef key;
+ CFDictionaryRef dict;
+
+ key = CFArrayGetValueAtIndex(changedKeys, i);
+ dict = SCDynamicStoreCopyValue(store, key);
+ if (dict != NULL) {
+ CFStringRef val;
+
+ val = _SCCopyDescription(dict, NULL);
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@ : %@"), key, val);
+ CFRelease(val);
+ CFRelease(dict);
+ } else {
+ CFStringAppendFormat(str, NULL, CFSTR("\n%@ : removed"), key);
+ }
+ }
+
+ SCLOG(NULL, log_msg, ~ASL_LEVEL_INFO, CFSTR("%@"), str);
+ CFRelease(str);
+ return;
+}
+
+
+static void
+add_BTMM_notification()
+{
+ CFStringRef key;
+ CFMutableArrayRef keys;
+ Boolean ok;
+ SCDynamicStoreRef store;
+ CFRunLoopSourceRef rls;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("Logger.bundle-BackToMyMac"), BTMM_notification, NULL);
+ if (store == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreate() failed"));
+ return;
+ }
+
+ keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ key = CFSTR(kDSStdNotifyBTMMStatusChanged);
+ CFArrayAppendValue(keys, key);
+
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+ CFRelease(keys);
+ if (!ok) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
+ CFRelease(store);
+ return;
+ }
+
+ rls = SCDynamicStoreCreateRunLoopSource(NULL, store, -1);
+ if (rls == NULL) {
+ SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCDynamicStoreCreateRunLoopSource() failed"));
+ CFRelease(store);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ CFRelease(store);
+ return;
+}
+#endif // !TARGET_OS_EMBEDDED
+
+
+#pragma mark -
+
+
+static inline Boolean
+bValFromDictionary(CFDictionaryRef dict, CFStringRef key)
+{
+ CFBooleanRef bVal;
+ Boolean result = FALSE;
+
+ if ((dict != NULL) &&
+ CFDictionaryGetValueIfPresent(dict, key, (const void **)&bVal) &&
+ isA_CFBoolean(bVal)) {
+ result = CFBooleanGetValue(bVal);
+ }
+
+ return result;
+}
+
+
+void
+load(CFBundleRef bundle, Boolean bundleVerbose)
+{
+ CFDictionaryRef config;
+ Boolean log_all;
+
+ verbose = bundleVerbose;
+
+ log_msg = asl_new(ASL_TYPE_MSG);
+ asl_set(log_msg, ASL_KEY_FACILITY, MY_ASL_FACILITY);
+
+ elapsed();
+
+ config = CFBundleGetInfoDictionary(bundle);
+ config = isA_CFDictionary(config);
+ log_all = bValFromDictionary(config, CFSTR("LOG_ALL"));
+
+#ifdef kIOPMMessageSleepWakeUUIDChange
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_IO_WAKEUUID_EVENTS"))) {
+ add_wake_uuid_notification();
+ }
+#endif // kIOPMMessageSleepWakeUUIDChange
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_IO_SYSTEMPOWER_EVENTS"))) {
+ add_power_notification();
+ }
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_NETWORK_KERNEL_EVENTS"))) {
+ add_KernelEvent_notification();
+ }
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_NOTIFY_DNS_CONFIGURATION"))) {
+ add_dnsinfo_notification();
+ }
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_NOTIFY_NETWORK_CHANGE"))) {
+ add_network_notification();
+ }
+
+#if !TARGET_OS_EMBEDDED
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_NOTIFY_SMB_CONFIGURATION"))) {
+ add_smbconf_notification();
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+#if !TARGET_OS_EMBEDDED
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_NOTIFY_UTMPX_CHANGE"))) {
+ add_pututxline_notification();
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+#if !TARGET_OS_EMBEDDED
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_BTMM_CONFIGURATION"))) {
+ add_BTMM_notification();
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+#if !TARGET_OS_EMBEDDED
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_CONSOLEUSER"))) {
+ add_console_notification();
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+#if !TARGET_OS_EMBEDDED
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_DIRECTORYSERVICES_SEARCHPOLICY"))) {
+ add_DirectoryServices_notification();
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_NETWORKCHANGE"))) {
+ add_NetworkChange_notification();
+ }
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_PRIMARYSERVICE"))) {
+ add_PrimaryService_notification();
+ }
+
+ if (log_all || bValFromDictionary(config, CFSTR("LOG_SC_REACHABILITY"))) {
+ CFArrayRef hosts = NULL;
+
+ if ((config == NULL) ||
+ !CFDictionaryGetValueIfPresent(config, CFSTR("LOG_SC_REACHABILITY_HOSTS"), (const void **)&hosts) ||
+ !isA_CFArray(hosts) ||
+ (CFArrayGetCount(hosts) == 0)) {
+ hosts = NULL;
+ }
+
+ add_reachability_notification(hosts);
+ }
+
+ return;
+}
+
+#ifdef MAIN
+
+int
+main(int argc, char **argv)
+{
+ _sc_log = FALSE;
+ _sc_verbose = (argc > 1) ? TRUE : FALSE;
+ _sc_debug = TRUE;
+
+ load(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
+ CFRunLoopRun();
+ /* not reached */
+ exit(0);
+ return 0;
+}
+
+#endif /* MAIN */