logger.c   [plain text]


/*
 * 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, &notify, 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
					      &notification);
	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, &notify_port, 0, &notify_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,
					   &notify_port,
					   0,
					   &notify_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,
					   &notify_port,
					   0,
					   &notify_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, &notify_port, 0, &notify_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 */