X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/be9975404a963d1fd2172ca83ade1cc3c58efc2e..a40a14f8bcc57d8bed0203ddee43e8d64db39796:/Plugins/Logger/logger.c diff --git a/Plugins/Logger/logger.c b/Plugins/Logger/logger.c new file mode 100644 index 0000000..3abe51d --- /dev/null +++ b/Plugins/Logger/logger.c @@ -0,0 +1,1832 @@ +/* + * 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 + * - initial revision + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* 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 +#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 */