#include <CoreFoundation/CoreFoundation.h> // (CFDictionary, ...)
#include <IOKit/IOCFSerialize.h> // (IOCFSerialize, ...)
#include <IOKit/IOKitLib.h> // (IOMasterPort, ...)
#include <curses.h> // (tgetstr, ...)
#include <unistd.h> // (getopt, ...)
static void assertion(int condition, char * message); static void boldinit(); static void boldon(); static void boldoff(); static void printinit(int width); static void print(const char * format, ...); static void println(const char * format, ...);
static void CFArrayShow(CFArrayRef object); static void CFBooleanShow(CFBooleanRef object); static void CFDataShow(CFDataRef object); static void CFDictionaryShow(CFDictionaryRef object); static void CFNumberShow(CFNumberRef object); static void CFObjectShow(CFTypeRef object); static void CFSetShow(CFSetRef object); static void CFStringShow(CFStringRef object);
const UInt32 kIORegFlagShowBold = (1 << 0); const UInt32 kIORegFlagShowProperties = (1 << 1); const UInt32 kIORegFlagShowState = (1 << 2);
struct options
{
char * class; UInt32 flags; char * name; char * plane; UInt32 width; };
struct context
{
UInt32 depth;
UInt64 stackOfBits;
};
static void indent( Boolean isNode,
UInt32 serviceDepth,
UInt64 stackOfBits );
static void scan( io_registry_entry_t service,
Boolean serviceHasMoreSiblings,
UInt32 serviceDepth,
UInt64 stackOfBits, struct options options );
static void show( io_registry_entry_t service,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options );
static void showItem( const void * key,
const void * value,
void * parameter );
static void usage();
int main(int argc, char ** argv)
{
int argument = 0;
mach_port_t iokitPort = 0; struct options options;
io_registry_entry_t service = 0; kern_return_t status = KERN_SUCCESS;
initscr();
options.class = 0;
options.flags = 0;
options.name = 0;
options.plane = kIOServicePlane;
options.width = COLS;
while ( (argument = getopt(argc, argv, ":bc:ln:p:sw:")) != -1 )
{
switch (argument)
{
case 'b':
options.flags |= kIORegFlagShowBold;
break;
case 'c':
options.class = optarg;
break;
case 'l':
options.flags |= kIORegFlagShowProperties;
break;
case 'n':
options.name = optarg;
break;
case 'p':
options.plane = optarg;
break;
case 's':
options.flags |= kIORegFlagShowState;
break;
case 'w':
options.width = atoi(optarg);
assertion(options.width >= 0, "invalid width");
break;
default:
usage();
break;
}
}
printinit(options.width);
if (options.flags & kIORegFlagShowBold) boldinit();
status = IOMasterPort(bootstrap_port, &iokitPort);
assertion(status == KERN_SUCCESS, "can't obtain I/O Kit's master port");
service = IORegistryGetRootEntry(iokitPort);
assertion(service, "can't obtain I/O Kit's root service");
scan( service,
FALSE,
0,
0,
options );
IOObjectRelease(service); service = 0;
exit(0);
}
static void scan( io_registry_entry_t service,
Boolean serviceHasMoreSiblings,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options )
{
io_registry_entry_t child = 0; io_registry_entry_t childUpNext = 0; io_iterator_t children = 0; kern_return_t status = KERN_SUCCESS;
status = IORegistryEntryGetChildIterator(service, options.plane, &children);
assertion(status == KERN_SUCCESS, "can't obtain children");
childUpNext = IOIteratorNext(children);
if (serviceHasMoreSiblings)
stackOfBits |= (1 << serviceDepth);
else
stackOfBits &= ~(1 << serviceDepth);
if (childUpNext)
stackOfBits |= (2 << serviceDepth);
else
stackOfBits &= ~(2 << serviceDepth);
show(service, serviceDepth, stackOfBits, options);
while (childUpNext)
{
child = childUpNext;
childUpNext = IOIteratorNext(children);
scan( child,
(childUpNext) ? TRUE : FALSE,
serviceDepth + 1,
stackOfBits,
options );
IOObjectRelease(child); child = 0;
}
IOObjectRelease(children); children = 0;
}
static void show( io_registry_entry_t service,
UInt32 serviceDepth,
UInt64 stackOfBits,
struct options options )
{
io_name_t class; struct context context = { serviceDepth, stackOfBits };
int integer = 0; io_name_t name; io_string_t path; CFDictionaryRef properties = 0; kern_return_t status = KERN_SUCCESS;
status = IORegistryEntryGetNameInPlane(service, options.plane, name);
assertion(status == KERN_SUCCESS, "can't obtain name");
indent(TRUE, serviceDepth, stackOfBits);
if (options.flags & kIORegFlagShowBold) boldon();
print("%s", name);
if (options.flags & kIORegFlagShowBold) boldoff();
status = IORegistryEntryGetPath(service, options.plane, path);
if (status == KERN_SUCCESS)
{
const char * location = 0;
for (location = path + strlen(path) - 1; location >= path; location--)
{
if (*location == '@' || *location == '/')
{
if (*location == '@') print(location);
break;
}
}
}
status = IOObjectGetClass(service, class);
assertion(status == KERN_SUCCESS, "can't obtain class name");
print(" <class %s", class);
if (options.flags & kIORegFlagShowState)
{
if (IOObjectConformsTo(service, "IOService"))
{
status = IOServiceGetBusyState(service, &integer);
assertion(status == KERN_SUCCESS, "can't obtain busy state");
print(", busy %d", integer);
}
integer = IOObjectGetRetainCount(service);
assertion(integer >= 0, "can't obtain retain count");
print(", retain count %d", integer);
}
println(">");
if (options.class && IOObjectConformsTo(service, options.class))
options.flags |= kIORegFlagShowProperties;
if (options.name && !strcmp(name, options.name))
options.flags |= kIORegFlagShowProperties;
if (options.flags & kIORegFlagShowProperties)
{
indent(FALSE, serviceDepth, stackOfBits);
println("{");
status = IORegistryEntryCreateCFProperties(service,
&properties,
kCFAllocatorDefault,
kNilOptions);
assertion(status == KERN_SUCCESS, "can't obtain properties");
assertion(CFGetTypeID(properties) == CFDictionaryGetTypeID(), NULL);
CFDictionaryApplyFunction(properties, showItem, &context);
indent(FALSE, serviceDepth, stackOfBits);
println("}");
indent(FALSE, serviceDepth, stackOfBits);
println("");
CFRelease(properties);
}
}
static void showItem(const void * key, const void * value, void * parameter)
{
struct context * context = parameter;
indent(FALSE, context->depth, context->stackOfBits);
print(" ");
CFStringShow(key);
print(" = ");
CFObjectShow(value);
println("");
}
static void indent(Boolean isNode, UInt32 depth, UInt64 stackOfBits)
{
UInt32 index;
if (isNode)
{
for (index = 0; index < depth; index++)
print( (stackOfBits & (1 << index)) ? "| " : " " );
print("+-o ");
}
else {
for (index = 0; index <= depth + 1; index++)
print( (stackOfBits & (1 << index)) ? "| " : " " );
}
}
void usage()
{
fprintf( stderr,
"usage: ioreg [-b] [-c class | -l | -n name] [-p plane] [-s] [-w width]\n"
"where options are:\n"
"\t-b show object name in bold\n"
"\t-c list properties of objects with the given class\n"
"\t-l list properties of all objects\n"
"\t-n list properties of objects with the given name\n"
"\t-p traverse registry over the given plane (IOService is default)\n"
"\t-s show object state (eg. busy state, retain count)\n"
"\t-w clip output to the given line width (0 is unlimited)\n"
);
exit(1);
}
static void assertion(int condition, char * message)
{
if (condition == 0)
{
fprintf(stderr, "ioreg: error: %s.\n", message);
exit(1);
}
}
static char * termcapstr_boldon = 0;
static char * termcapstr_boldoff = 0;
static void termcapstr_outc(int c)
{
putchar(c);
}
static void boldinit()
{
static char termcapbuf[64];
char * termcapbufptr = termcapbuf;
termcapstr_boldon = tgetstr("md", &termcapbufptr);
termcapstr_boldoff = tgetstr("me", &termcapbufptr);
assertion(termcapbufptr - termcapbuf <= sizeof(termcapbuf), NULL);
if (termcapstr_boldon == 0) termcapstr_boldon = "";
if (termcapstr_boldoff == 0) termcapstr_boldoff = "";
}
static void boldon()
{
tputs(termcapstr_boldon, 1, termcapstr_outc);
}
static void boldoff()
{
tputs(termcapstr_boldoff, 1, termcapstr_outc);
}
static char * printbuf = 0;
static int printbufclip = FALSE;
static int printbufleft = 0;
static int printbufsize = 0;
static void printinit(int width)
{
if (width)
{
printbuf = malloc(width);
printbufleft = width;
printbufsize = width;
assertion(printbuf != NULL, "can't allocate buffer");
}
}
static void printva(const char * format, va_list arguments)
{
if (printbufsize)
{
char * c;
int count = vsnprintf(printbuf, printbufleft, format, arguments);
while ( (c = strchr(printbuf, '\n')) ) *c = ' ';
printf("%s", printbuf);
if (count >= printbufleft)
{
count = printbufleft - 1;
printbufclip = TRUE;
}
printbufleft -= count; }
else
{
vprintf(format, arguments);
}
}
static void print(const char * format, ...)
{
va_list arguments;
va_start(arguments, format);
printva(format, arguments);
va_end(arguments);
}
static void println(const char * format, ...)
{
va_list arguments;
va_start(arguments, format);
printva(format, arguments);
va_end(arguments);
if (printbufclip) printf("$");
printf("\n");
printbufclip = FALSE;
printbufleft = printbufsize;
}
static void CFArrayShow_Applier(const void * value, void * parameter)
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
CFObjectShow(value);
}
static void CFArrayShow(CFArrayRef object)
{
Boolean first = TRUE;
CFRange range = { 0, CFArrayGetCount(object) };
print("(");
CFArrayApplyFunction(object, range, CFArrayShow_Applier, &first);
print(")");
}
static void CFBooleanShow(CFBooleanRef object)
{
print(CFBooleanGetValue(object) ? "Yes" : "No");
}
static void CFDataShow(CFDataRef object)
{
UInt32 asciiNormalCount = 0;
UInt32 asciiSymbolCount = 0;
const UInt8 * bytes;
CFIndex index;
CFIndex length;
print("<");
length = CFDataGetLength(object);
bytes = CFDataGetBytePtr(object);
for (index = 0; index < length; index++) {
if (bytes[index] == 0) { for (; index < length && bytes[index] == 0; index++) { }
break; }
else {
for (; index < length; index++)
{
if (isprint(bytes[index]))
asciiNormalCount++;
else if (bytes[index] >= 128 && bytes[index] <= 254)
asciiSymbolCount++;
else
break;
}
if (index < length && bytes[index] == 0) continue;
else break;
}
}
if ((asciiNormalCount >> 2) < asciiSymbolCount) index = 0;
else if (length == 1) index = 0;
if (index >= length && asciiNormalCount) {
Boolean quoted = FALSE;
for (index = 0; index < length; index++)
{
if (bytes[index])
{
if (quoted == FALSE)
{
quoted = TRUE;
if (index)
print(",\"");
else
print("\"");
}
print("%c", bytes[index]);
}
else
{
if (quoted == TRUE)
{
quoted = FALSE;
print("\"");
}
else
break;
}
}
if (quoted == TRUE)
print("\"");
}
else {
for (index = 0; index < length; index++) print("%02x", bytes[index]);
}
print(">");
}
static void CFDictionaryShow_Applier( const void * key,
const void * value,
void * parameter )
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
CFObjectShow(key);
print("=");
CFObjectShow(value);
}
static void CFDictionaryShow(CFDictionaryRef object)
{
Boolean first = TRUE;
print("{");
CFDictionaryApplyFunction(object, CFDictionaryShow_Applier, &first);
print("}");
}
static void CFNumberShow(CFNumberRef object)
{
long long number;
if (CFNumberGetValue(object, kCFNumberLongLongType, &number))
{
print("%qd", number);
}
}
static void CFObjectShow(CFTypeRef object)
{
CFTypeID type = CFGetTypeID(object);
if ( type == CFArrayGetTypeID() ) CFArrayShow(object);
else if ( type == CFBooleanGetTypeID() ) CFBooleanShow(object);
else if ( type == CFDataGetTypeID() ) CFDataShow(object);
else if ( type == CFDictionaryGetTypeID() ) CFDictionaryShow(object);
else if ( type == CFNumberGetTypeID() ) CFNumberShow(object);
else if ( type == CFSetGetTypeID() ) CFSetShow(object);
else if ( type == CFStringGetTypeID() ) CFStringShow(object);
else print("<unknown object>");
}
static void CFSetShow_Applier(const void * value, void * parameter)
{
Boolean * first = (Boolean *) parameter;
if (*first)
*first = FALSE;
else
print(",");
CFObjectShow(value);
}
static void CFSetShow(CFSetRef object)
{
Boolean first = TRUE;
print("[");
CFSetApplyFunction(object, CFSetShow_Applier, &first);
print("]");
}
static void CFStringShow(CFStringRef object)
{
const char * c = CFStringGetCStringPtr(object, kCFStringEncodingMacRoman);
if (c)
print("\"%s\"", c);
else
{
CFIndex bufferSize = CFStringGetLength(object) + 1;
char * buffer = malloc(bufferSize);
if (buffer)
{
if ( CFStringGetCString(
object,
buffer,
bufferSize,
kCFStringEncodingMacRoman ) )
print("\"%s\"", buffer);
free(buffer);
}
}
}