#include <assert.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IONetworkLib.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <mach/mach.h>
#define IF_NAME_KEY "BSD Name"
#define kIOMediumDictionary "IOMediumDictionary"
#define kIODefaultMedium "IODefaultMedium"
#define kIOCurrentMedium "IOCurrentMedium"
#define kIOActiveMedium "IOActiveMedium"
#define kIOLinkSpeed "IOLinkSpeed"
#define kIOLinkStatus "IOLinkStatus"
#define kIOLinkData "IOLinkData"
static void CFNumberShow(CFNumberRef object)
{
long long number = 0;
if (CFNumberGetValue(object, kCFNumberLongLongType, &number))
{
printf("%qd", number);
}
}
static void CFStringShow(CFStringRef object)
{
const char * c = CFStringGetCStringPtr(object,
kCFStringEncodingMacRoman);
if (c)
printf(c);
else
{
CFIndex bufferSize = CFStringGetLength(object) + 1;
char * buffer = (char *) malloc(bufferSize);
if (buffer)
{
if ( CFStringGetCString(
object,
buffer,
bufferSize,
kCFStringEncodingMacRoman) )
printf(buffer);
free(buffer);
}
}
}
static int matchBSDNameProperty(io_object_t obj, CFStringRef ifname)
{
int match = 0;
kern_return_t kr;
CFMutableDictionaryRef properties;
CFStringRef string;
kr = IORegistryEntryCreateCFProperties(obj,
&properties,
kCFAllocatorDefault,
kNilOptions);
if ((kr != KERN_SUCCESS) || !properties) {
printf("IORegistryEntryCreateCFProperties error %x\n", kr);
return false;
}
string = (CFStringRef) CFDictionaryGetValue(properties,
CFSTR(IF_NAME_KEY));
if (string) {
if (CFStringCompare(ifname, string, kNilOptions) == kCFCompareEqualTo)
{
printf("[ ");
CFStringShow(string);
printf(" ]\n");
match = 1;
}
}
CFRelease(properties);
return match; }
static io_object_t
getInterfaceWithBSDName(mach_port_t masterPort, CFStringRef ifname)
{
kern_return_t kr;
io_iterator_t ite;
io_object_t obj = 0;
const char * className = "IONetworkInterface";
kr = IORegistryCreateIterator(masterPort,
kIOServicePlane,
true,
&ite);
if (kr != kIOReturnSuccess) {
printf("IORegistryCreateIterator() error %x\n", kr);
return 0;
}
while ((obj = IOIteratorNext(ite))) {
if (IOObjectConformsTo(obj, (char *) className) &&
(matchBSDNameProperty(obj, ifname)))
break;
IOObjectRelease(obj);
obj = 0;
}
IORegistryDisposeEnumerator(ite);
return obj;
}
static void
printLinkProperties(io_object_t controller)
{
kern_return_t kr;
CFMutableDictionaryRef properties = 0;
CFStringRef string;
CFNumberRef number;
do {
kr = IORegistryEntryCreateCFProperties(controller,
&properties,
kCFAllocatorDefault,
kNilOptions);
if (kr != kIOReturnSuccess) {
printf("Error: cannot get properties %x\n", kr);
break;
}
string = (CFStringRef) CFDictionaryGetValue(properties,
CFSTR(kIOActiveMedium));
printf("Active medium : ");
if (string) {
CFStringShow(string);
printf("\n");
}
else {
printf("None\n");
}
string = (CFStringRef) CFDictionaryGetValue(properties,
CFSTR(kIOCurrentMedium));
printf("Current medium : ");
if (string) {
CFStringShow(string);
printf("\n");
}
else {
printf("None\n");
}
number = (CFNumberRef) CFDictionaryGetValue(properties,
CFSTR(kIOLinkSpeed));
if (number) {
printf("Link speed bps : ");
CFNumberShow(number);
printf("\n");
}
number = (CFNumberRef) CFDictionaryGetValue(properties,
CFSTR(kIOLinkStatus));
if (number) {
long status;
if (CFNumberGetValue(number, kCFNumberLongType, &status))
{
printf("Link status : ");
if (status & kIONetworkLinkValid)
{
printf("%s\n", (status & kIONetworkLinkActive) ? "Active" :
"Inactive");
}
else
printf("Not reported\n");
}
}
}
while (0);
if (properties)
CFRelease(properties);
}
static io_object_t
getControllerForInterface(io_object_t netif)
{
io_iterator_t ite;
kern_return_t kr;
io_object_t controller = 0;
kr = IORegistryEntryGetParentIterator(netif, kIOServicePlane, &ite);
if (kr == kIOReturnSuccess) {
controller = IOIteratorNext(ite); IORegistryDisposeEnumerator(ite);
}
return controller; }
static void
waitForNotification(mach_port_t port)
{
kern_return_t kr;
struct {
IONetworkNotifyMsg hdr;
mach_msg_trailer_t trailer;
} msg;
kr = mach_msg(&msg.hdr.h, MACH_RCV_MSG,
0, sizeof(msg), port, 0, MACH_PORT_NULL);
if (kr != KERN_SUCCESS)
printf("Error: mach_msg %x\n", kr);
}
static void usage(void)
{
printf("usage: monitorlink [-i ifname] [-w]\n");
printf("option flags:\n");
printf(" -i ifname : specify interface. Default is en0.\n");
printf(" -w : wait for link event notification.\n");
printf("\n");
exit(1);
}
int
main(int argc, char ** argv)
{
mach_port_t masterPort;
mach_port_t notifyPort;
kern_return_t kr;
io_object_t netif;
io_object_t controller;
io_connect_t con;
int ch;
const char * name = "en0"; CFStringRef ifname;
int wait = 0;
while ((ch = getopt(argc, argv, ":i:w")) != -1)
{
switch ((char) ch)
{
case 'i':
if (!optarg)
usage();
name = optarg;
break;
case 'w':
wait = 1;
break;
default:
usage();
}
}
if (name == 0)
usage();
kr = IOMasterPort(bootstrap_port, &masterPort);
if (kr != KERN_SUCCESS)
printf("IOMasterPort() failed: %x\n", kr);
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
¬ifyPort);
if (kr != KERN_SUCCESS) {
printf("Error: mach_port_allocate %x\n", kr);
exit(1);
}
ifname = CFStringCreateWithCString(kCFAllocatorDefault,
name,
kCFStringEncodingMacRoman);
netif = getInterfaceWithBSDName(masterPort, ifname);
if (netif)
{
kr = IONetworkOpen(netif, &con);
if (kr != kIOReturnSuccess)
{
printf("Error: IONetworkOpen error %x\n", kr);
exit(1);
}
kr = IOConnectSetNotificationPort(con,
kIONUCNotificationTypeLinkChange, notifyPort, 0);
if (kr != kIOReturnSuccess) {
printf("Error: IOConnectSetNotificationPort %x\n", kr);
exit(1);
}
controller = getControllerForInterface(netif);
if (controller)
{
do {
printLinkProperties(controller);
printf("\n");
if (wait)
waitForNotification(notifyPort);
}
while (wait);
IOObjectRelease(controller);
}
IONetworkClose(con);
IOObjectRelease(netif);
}
exit(0);
return 0;
}