1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24cc ioclasscount.c -o /tmp/ioclasscount -Wall -framework IOKit -framework CoreFoundation
25 */
26
27#include <sysexits.h>
28 #include <malloc/malloc.h>
29#include <CoreFoundation/CoreFoundation.h>
30#include <IOKit/IOKitLib.h>
31#include <IOKit/IOKitKeys.h>
32
33/*********************************************************************
34*********************************************************************/
35static int compareClassNames(const void * left, const void * right)
36{
37    switch (CFStringCompare(*((CFStringRef *)left), *((CFStringRef *)right),
38        (CFStringCompareFlags)kCFCompareCaseInsensitive)) {
39    case kCFCompareLessThan:
40        return -1;
41        break;
42    case kCFCompareEqualTo:
43        return 0;
44        break;
45    case kCFCompareGreaterThan:
46        return 1;
47        break;
48    default:
49        fprintf(stderr, "fatal error\n");
50        exit(EX_OSERR);
51        return 0;
52        break;
53    }
54}
55
56/*********************************************************************
57*********************************************************************/
58static Boolean printInstanceCount(
59    CFDictionaryRef dict,
60    CFStringRef     name,
61    char         ** nameCString,
62    Boolean         addNewlineFlag)
63{
64    int           result     = FALSE;
65    CFIndex       nameLength = 0;
66    static char * nameBuffer = NULL;  // free if nameCString is NULL
67    CFNumberRef   num        = NULL;  // do not release
68    SInt32	      num32      = 0;
69    Boolean       gotName    = FALSE;
70    Boolean       gotNum     = FALSE;
71
72    nameLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name),
73        kCFStringEncodingUTF8);
74    if (!nameCString || !*nameCString) {
75        nameBuffer = (char *)malloc((1 + nameLength) * sizeof(char));
76    } else if ((1 + nameLength) > malloc_size(nameBuffer)) {
77        nameBuffer = (char *)realloc(*nameCString,
78            (1 + nameLength) * sizeof(char));
79    }
80    if (nameBuffer) {
81        gotName = CFStringGetCString(name, nameBuffer, 1 + nameLength,
82            kCFStringEncodingUTF8);
83    } else {
84        fprintf(stderr, "Memory allocation failure.\n");
85        goto finish;
86    }
87
88   /* Note that errors displaying the name and value are not considered
89    * fatal and do not affect the exit status of the program.
90    */
91    printf("%s = ", gotName ? nameBuffer : "??");
92
93    num = (CFNumberRef)CFDictionaryGetValue(dict, name);
94    if (num) {
95
96        if (CFNumberGetTypeID() == CFGetTypeID(num)) {
97            gotNum = CFNumberGetValue(num, kCFNumberSInt32Type, &num32);
98        }
99        if (gotNum) {
100            printf("%lu", (unsigned long)num32);
101        } else {
102            printf("?? (error reading/converting value)");
103        }
104    } else {
105        printf("<no such class>");
106    }
107
108    if (addNewlineFlag) {
109        printf("\n");
110    } else {
111        printf(", ");
112    }
113
114    result = TRUE;
115
116finish:
117    if (nameCString) {
118        *nameCString = nameBuffer;
119    } else {
120        if (nameBuffer) free(nameBuffer);
121    }
122    return result;
123}
124
125/*********************************************************************
126*********************************************************************/
127int main(int argc, char ** argv)
128{
129    int                    result      = EX_OSERR;
130    kern_return_t          status      = KERN_FAILURE;
131    io_registry_entry_t    root        = IO_OBJECT_NULL;  // must IOObjectRelease()
132    CFMutableDictionaryRef rootProps   = NULL;            // must release
133    CFDictionaryRef        diagnostics = NULL;            // do not release
134    CFDictionaryRef        classes     = NULL;            // do not release
135    CFStringRef          * classNames  = NULL;            // must free
136    CFStringRef            className   = NULL;            // must release
137    char                 * nameCString = NULL;            // must free
138
139    // Obtain the registry root entry.
140
141    root = IORegistryGetRootEntry(kIOMasterPortDefault);
142    if (!root) {
143        fprintf(stderr, "Error: Can't get registry root.\n");
144        goto finish;
145    }
146
147    status = IORegistryEntryCreateCFProperties(root,
148        &rootProps, kCFAllocatorDefault, kNilOptions);
149    if (KERN_SUCCESS != status) {
150        fprintf(stderr, "Error: Can't read registry root properties.\n");
151        goto finish;
152    }
153    if (CFDictionaryGetTypeID() != CFGetTypeID(rootProps)) {
154        fprintf(stderr, "Error: Registry root properties not a dictionary.\n");
155        goto finish;
156    }
157
158    diagnostics = (CFDictionaryRef)CFDictionaryGetValue(rootProps,
159        CFSTR(kIOKitDiagnosticsKey));
160    if (!diagnostics) {
161        fprintf(stderr, "Error: Allocation information missing.\n");
162        goto finish;
163    }
164    if (CFDictionaryGetTypeID() != CFGetTypeID(diagnostics)) {
165        fprintf(stderr, "Error: Allocation information not a dictionary.\n");
166        goto finish;
167    }
168
169    classes = (CFDictionaryRef)CFDictionaryGetValue(diagnostics, CFSTR("Classes"));
170    if (!classes) {
171        fprintf(stderr, "Error: Class information missing.\n");
172        goto finish;
173    }
174    if (CFDictionaryGetTypeID() != CFGetTypeID(classes)) {
175        fprintf(stderr, "Error: Class information not a dictionary.\n");
176        goto finish;
177    }
178
179    if (argc < 2) {
180        CFIndex       index, count;
181
182        count = CFDictionaryGetCount(classes);
183        classNames = (CFStringRef *)calloc(count, sizeof(CFStringRef));
184        if (!classNames) {
185            fprintf(stderr, "Memory allocation failure.\n");
186            goto finish;
187        }
188        CFDictionaryGetKeysAndValues(classes, (const void **)classNames, NULL);
189        qsort(classNames, count, sizeof(CFStringRef), &compareClassNames);
190
191        for (index = 0; index < count; index++) {
192            printInstanceCount(classes, classNames[index], &nameCString,
193                /* addNewline? */ TRUE);
194        }
195
196    } else {
197        uint32_t index = 0;
198        for (index = 1; index < argc; index++ ) {
199
200            if (className) CFRelease(className);
201            className = NULL;
202
203            className = CFStringCreateWithCString(kCFAllocatorDefault,
204                argv[index], kCFStringEncodingUTF8);
205            if (!className) {
206                fprintf(stderr, "Error: Can't create CFString for '%s'.\n",
207                    argv[index]);
208                goto finish;
209            }
210            printInstanceCount(classes, className, &nameCString,
211                /* addNewline? */ (index + 1 == argc));
212        }
213    }
214
215    result = EX_OK;
216finish:
217    if (rootProps)              CFRelease(rootProps);
218    if (root != IO_OBJECT_NULL) IOObjectRelease(root);
219    if (classNames)             free(classNames);
220    if (className)              CFRelease(className);
221    if (nameCString)            free(nameCString);
222
223    return result;
224}
225
226