<– is that OK??
Nobody helped me (see the Original Post below). Snif. But I figured it out by myself. What finally got me started was the addition of ‘Carbon’ in my googles searches (I had tried several times before with no luck).
Of course, I still put the code here with links to where I found the code (see The Solution below). Please feel free to add more in the code section…
–CharlesParnot
Original post - nobody helped me )`:
I am trying to get some basic profile of the machine running my Cocoa program, in particular: http://goo.gl/OeSCu
So I want something like a light system profile. I have found some info out there and have some working code for the number of processors, and the type of processor (see GettingTheProcessor). I can get the hostnames through NSHost. The rest, I don’t know! I looked hard, and could not find anything obvious. Even the RendezVous thing is quite obscure It seems IOKit / IORegistry could help for some of that. Useful links eith some tutorials/example would also help. Maybe somebody has written some Cocoa wrapper for all that stuff.
Thanks for any help you can provide. I will put all the code I can get to work back here, of course.
CharlesParnot
The Solution
Dont’ forget in Xcode: it seems you have to link against the Carbon, SystemConfiguration and IOKit frameworks.
CPSystemInformation.h
@interface CPSystemInformation : NSObject {}
//all the info at once!
(NSDictionary *)miniSystemProfile;
(NSString *)humanMachineTypeAlternate;
(NSString *)powerPCTypeString;
(NSString *)computerSerialNumber;
CPSystemInformation.m
#import “CPSystemInformation.h”
#import <Carbon/Carbon.h> #import <SystemConfiguration/SystemConfiguration.h>
@implementation CPSystemInformation
//get everything!
#pragma mark ** Getting the Human Name for the Machine Type **
/* adapted from http://nilzero.com/cgi-bin/mt-comments.cgi?entry_id=1300 / /see below ‘humanMachineNameFromNilZeroCom()’ for the original code / /this code used a dictionary insted - see ‘translationDictionary()’ below */
//non-human readable machine type/model
//dictionary used to make the machine type human-readable static NSDictionary *translationDictionary=nil;
}
}
//for some reason, this does not work //probably old stuff still around
#pragma mark ** Getting Processor info **
(long)processorClockSpeed { OSErr err; long result; err=Gestalt(gestaltProcClkSpeed,&result); if (err!=nil) return 0; else return result; }
(long)processorClockSpeedInMHz { return [self processorClockSpeed]/1000000; }
#include <mach/mach_host.h> #include <mach/host_info.h>
}
#include <mach/mach.h> #include <mach/machine.h>
// the following methods were more or less copied from // http://developer.apple.com/technotes/tn/tn2086.html // http://www.cocoadev.com/index.pl?GettingTheProcessor // and can be better understood with a look at // file:///usr/include/mach/machine.h
}
}
}
#ifndef CPU_SUBTYPE_POWERPC_970 #define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) #endif
}
}
#pragma mark ** Machine information **
//this used to be called ‘Rendezvous name’ (X.2), now just ‘Computer name’ (X.3) //see here for why: http://developer.apple.com/qa/qa2001/qa1228.html //this is the name set in the Sharing pref pane
/* copied from http://cocoa.mamasam.com/COCOADEV/2003/07/1/68334.php / / and modified by http://nilzero.com/cgi-bin/mt-comments.cgi?entry_id=1300 / / and by http://cocoa.mamasam.com/COCOADEV/2003/07/1/68337.php/ */
nil, kIORegistryIterateRecursively);
if (prop == nil) {
result = @"null";
} else {
propID = CFGetTypeID(prop);
}
if (propID == CFDataGetTypeID()) {
propData = (CFDataRef)prop;
bufSize = CFDataGetLength(propData);
if (bufSize > 0) {
data = CFDataGetBytePtr(propData);
if (data) {
i = 0;
s = data;
t = firstPart;
while (i < bufSize) {
i++;
if (*s != '\0') {
*t++ = *s++;
} else {
break;
}
}
*t = '\0';
}
#pragma mark ** System version **
(NSString *)operatingSystemString { NSProcessInfo *procInfo = [NSProcessInfo processInfo]; return [procInfo operatingSystemName]; }
(NSString *)systemVersionString { NSProcessInfo *procInfo = [NSProcessInfo processInfo]; return [procInfo operatingSystemVersionString]; }
@end
Some more code from when this topic came up on Cocoa-Dev:
The first gets you the physical RAM size in MBs. You could also get it in bytes, but since Gestalt returns a signed 32-bit number, this would not be able to return any RAM above 2GB, which is an amount that increasingly more and more Macs these days seem to have.
unsigned UKPhysicalRAMSize() // In MBs. { long ramSize;
if( Gestalt( gestaltPhysicalRAMSizeInMegabytes, &ramSize ) == noErr )
return ramSize;
else
return 0; }
And here’s another approach to get the system version:
NSString* UKSystemVersion() { long sysVersion;
if( Gestalt( gestaltSystemVersion, &sysVersion ) != noErr )
return nil;
int majorHiNib, majorLoNib, minorNib, bugNib;
majorHiNib = (sysVersion & 0x0000F000) >> 12;
majorLoNib = (sysVersion & 0x00000F00) >> 8;
minorNib = (sysVersion & 0x000000F0) >> 4;
bugNib = sysVersion & 0x0000000F;
if( majorHiNib == 0 )
return [NSString stringWithFormat: @"%ld.%ld.%ld", majorLoNib, minorNib, bugNib];
else
return [NSString stringWithFormat: @"%ld%ld.%ld.%ld", majorHiNib, majorLoNib, minorNib, bugNib]; }
This is also a nice example on how to decode BCD numbers as Carbon uses them to store version numbers. It’s unlikely you’ll ever hit the case where you have a one-digit number in front of the system version, but maybe if your app parses ‘vers’ resources or gets some other version number in BCD you’ll want to borrow this code.
Oh, and finally, here’s a variation of the serial number getting code. I think my version is a little more readable:
NSString* UKSystemSerialNumber() { mach_port_t masterPort; kern_return_t kr = noErr; io_registry_entry_t entry; CFTypeRef prop; CFTypeID propID; NSString* str = nil;
kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
if( kr != noErr )
goto cleanup;
entry = IORegistryGetRootEntry( masterPort );
if( entry == MACH_PORT_NULL )
goto cleanup;
prop = IORegistryEntrySearchCFProperty(entry, kIODeviceTreePlane, CFSTR("serial-number"), nil, kIORegistryIterateRecursively);
if( prop == nil )
goto cleanup;
propID = CFGetTypeID( prop );
if( propID != CFDataGetTypeID() )
goto cleanup;
const char* buf = [(NSData*)prop bytes];
int len = [(NSData*)prop length],
x;
char secondPart[256];
char firstPart[256];
char* currStr = secondPart; // Version number starts with second part, then NULLs, then first part.
int y = 0;
for( x = 0; x < len; x++ )
{
if( buf[x] > 0 && (y < 255) )
currStr[y++] = buf[x];
else if( currStr == secondPart )
{
currStr[y] = 0; // Terminate string.
currStr = firstPart;
y = 0;
}
}
currStr[y] = 0; // Terminate string.
str = [NSString stringWithFormat: @"%s%s", firstPart, secondPart];
cleanup: mach_port_deallocate( mach_task_self(), masterPort );
return str; }
The above serial number code produced incorrect results on my new MacBook, so I did some searching. Starting with Panther, the serial number is available in the IOPlatformSerialNumber property of the the IOPlatformExpertDevice node in the I/O registry. See: http://developer.apple.com/technotes/tn/tn1103.html -DanTreiman
Do you know <sys/sysctl.h> ? It has some features to get various informations about the installed hardware and is (afaik) portable (runs not only on Darwin). For an example see the manpage by Apple: http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html Another example shows, how to get the physical RAM of your machine:
#import <sys/sysctl.h>
#define GB (102410241024) // 1 Gigabyte of RAM
int error = 0 ; int value = 0 ; unsigned long length = sizeof(value) ;
int selection[2] = { CTL_HW, HW_PHYSMEM } ;
error = sysctl(selection, 2, &value, &length, NULL, 0) ;
if ( error == 0 ) { NSLog(@”Your machine has %i Bytes of RAM (%f GB)”, value, (value / GB)) ; } else NSLog(@”sysctl error occured!”) ;
LorenzHipp
Anyone got a function for disk space or remaining disk space?
~J
getmntinfo(3), getfsstat(2) and statfs(2) will give you information about mounted file systems.
I had to fix the +machineType method. It was buggy. For one, gestaltUserVisibleMachineName returns a Pascal string, so it may not be zero-terminated, and there’s a length byte at the start. I’m surprised the original code even worked, because the dictionary couldn’t possibly match on the string with the length byte at the start.
Also, the code was comparing an OSErr against NIL, which isn’t good style, so I changed that to noErr for correctness’ sake.
Finally, I added the Mac Mini, because that’s what I’m running on. Anyone have other Mac names? This seems to be missing a huge bunch of the Intel Macs…
– UliKusterer
Oh, of course. sysctlbyname() works:
char modelBuffer[256]; size_t sz = sizeof(modelBuffer); if (0 == sysctlbyname(“hw.model”, modelBuffer, &sz, NULL, 0)) { modelBuffer[sizeof(modelBuffer) - 1] = 0; printf(“%s”, modelBuffer); }
A (slightly) more orderly and up-to-date list of Mac names is found at MacintoshModels.
Same solution as previous, but avoiding a fixed buffer.
// Return an autoreleased string containing this system’s model name, // e.g., “MacBookPro5,5”, or nil if we were unable to determine the model // name.
NSString * GetModelString() { NSString * modelString = nil; int modelInfo[2] = { CTL_HW, HW_MODEL }; size_t modelSize;
if (sysctl(modelInfo,
2,
NULL,
&modelSize,
NULL, 0) == 0)
{
void * modelData = malloc(modelSize);
if (modelData)
{
if (sysctl(modelInfo,
2,
modelData,
&modelSize,
NULL, 0) == 0)
{
modelString = [NSString stringWithUTF8String:modelData];
}
free(modelData);
}
}
return modelString; }
In order to get the machine name I use this format:
OSErr err;
StringPtr machineNamePascal = NULL;
err = Gestalt(gestaltUserVisibleMachineName, (SInt32 *)&machineNamePascal);
NSString *machineName;
if(err == noErr)
machineName = [(NSString *)CFStringCreateWithPascalString(NULL,
machineNamePascal,
NSMacOSRomanStringEncoding)
autorelease];
Can anyone tell me why I don’t need to free machineNamePascal?
Thanks,
Avi
Yet another interesting C snippet showing how to retrieve hardware information on Mac OS X.
/*
C snippet posted by Guiyon on Jun 12, 2009, 09:38 AM at:
“Getting Hardware Details from Objective-C”, http://forums.macrumors.com/showthread.php?t=718118
compile with:
gcc -Wall -O3 -x objective-c -fobjc-exceptions -fnested-functions -framework Foundation
-framework CoreFoundation -framework IOKit -o machardwareinfo machardwareinfo.c
./machardwareinfo
Should run on Mac OS X 10.4 and later.
ioreg -l -w0 | awk -F’= ‘ ‘/”IOCPUID” = /{print $2; exit}’ ioreg -l -w0 | grep ‘device_type’ | grep -E -i ‘cpu|processor’
See also:
*/
#import <Foundation/Foundation.h> #import <IOKit/IOKitLib.h>
#include <sys/sysctl.h> #include <sys/resource.h> #include <sys/vm.h>
/* ADDED !!! */ #ifndef MAC_OS_X_VERSION_10_4 #define MAC_OS_X_VERSION_10_4 1040 #endif
#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) #define MAC_OS_X_VERSION_10_5_AND_LATER 1 #define MATCHSTR “processor” #else #define MAC_OS_X_VERSION_10_5_AND_LATER 0 #define MATCHSTR “cpu” #endif
#ifndef KERN_OSVERSION //#define KERN_OSVERSION 8110 #define KERN_OSVERSION “Darwin” #endif
static void objc_println( NSString* format, … ); static void printSysctlInfo( void ); static NSDictionary* getCpuIds( void ); static NSString* getPlatformSerialNumber( void );
int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
objc_println(@”Beginning system data collection…”);
NSProcessInfo* pinfo = [NSProcessInfo processInfo];
objc_println( @”NSProcessInfo:” ); objc_println( @”\tProcess Name: %@”, [pinfo processName] ); objc_println( @”\tPID: %d”, [pinfo processIdentifier] ); objc_println( @”\tProcess GUID: %@”, [pinfo globallyUniqueString] ); objc_println( @”\tOS Version: %@”, [pinfo operatingSystemVersionString] ); if (MAC_OS_X_VERSION_10_5_AND_LATER) /* ADDED !!! */ { objc_println( @”\tTotal Processors: %d”, [pinfo processorCount] ); objc_println( @”\tActive Processors: %d”, [pinfo activeProcessorCount] ); objc_println( @”\tTotal RAM: %ull bytes”, [pinfo physicalMemory] ); }else { objc_println( @”\tTotal Processors: [not available]”); objc_println( @”\tActive Processors: [not available]”); objc_println( @”\tTotal RAM: XXX bytes [not available]”); }
printSysctlInfo();
objc_println( @”IOKit:” ); NSDictionary cpuInfo = getCpuIds(); NSArray keys = cpuInfo allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
/* for( id key in keys ) { objc_println( @”\t%@ has ID: 0x%8.8x”, key, [([[NSNumber*)[cpuInfo objectForKey: key] unsignedIntValue] ); } */
/* ADDED !!! /
/ more portable code than: for( id key in keys ) { … /
int i, num;
num = [keys count];
for (i = 0; i < num; i++)
{
objc_println( @”\t%@ has CPU ID:\n\tdec: %d\n\thex: 0x%8.8x”, [keys objectAtIndex: i],
[(NSNumber)[cpuInfo objectForKey: [keys objectAtIndex: i]] unsignedIntValue],
[(NSNumber*)[cpuInfo objectForKey: [keys objectAtIndex: i]] unsignedIntValue] );
}
objc_println( @”\tSerial Number: %@”, getPlatformSerialNumber() );
/* [pool drain]; */ [pool release];
return 0; }
static void objc_println( NSString* format, … ) { va_list args;
if (![format hasSuffix: @”\n”]) { format = [format stringByAppendingString: @”\n”]; }
va_start (args, format); NSString *body = [[NSString alloc] initWithFormat: format arguments: args]; va_end (args);
fprintf(stderr,”%s”,[body UTF8String]);
[body release]; }
static void printSysctlInfo( void ) { objc_println( @”sysctl:” );
int mib[2]; size_t len = 0; char *rstring = NULL; unsigned int rint = 0;
/* Setup the MIB data */ mib[0] = CTL_KERN; mib[1] = KERN_OSTYPE;
/* Get the length of the string */ sysctl( mib, 2, NULL, &len, NULL, 0 );
/* Now allocate space for the string and grab it */ rstring = malloc( len ); sysctl( mib, 2, rstring, &len, NULL, 0 ); objc_println( @”\tkern.ostype: %s”, rstring );
/* Make sure we clean up afterwards! */ free( rstring ); rstring = NULL;
/* Get the kernel release number */ mib[0] = CTL_KERN; mib[1] = KERN_OSRELEASE; sysctl( mib, 2, NULL, &len, NULL, 0 ); rstring = malloc( len ); sysctl( mib, 2, rstring, &len, NULL, 0 ); objc_println( @”\tkern.osrelease: %s”, rstring ); free( rstring ); rstring = NULL;
/* Get the Mac OS X Build number */ mib[0] = CTL_KERN; mib[1] = KERN_OSVERSION; sysctl( mib, 2, NULL, &len, NULL, 0 ); rstring = malloc( len ); sysctl( mib, 2, rstring, &len, NULL, 0 ); objc_println( @”\tkern.osversion: %s”, rstring ); free( rstring ); rstring = NULL;
mib[0] = CTL_HW; mib[1] = HW_MODEL; sysctl( mib, 2, NULL, &len, NULL, 0 ); rstring = malloc( len ); sysctl( mib, 2, rstring, &len, NULL, 0 ); objc_println( @”\thw.model: %s”, rstring ); free( rstring ); rstring = NULL;
sysctlbyname( “machdep.cpu.brand_string”, NULL, &len, NULL, 0 ); rstring = malloc( len ); sysctlbyname( “machdep.cpu.brand_string”, rstring, &len, NULL, 0 ); objc_println( @”\tmachdep.cpu.brand_string: %s”, rstring ); free( rstring ); rstring = NULL;
mib[0] = CTL_HW; mib[1] = HW_CPU_FREQ; len = sizeof( rint ); sysctl( mib, 2, &rint, &len, NULL, 0 ); objc_println( @”\thw.cpufrequency: %u”, rint ); }
static NSString* getPlatformSerialNumber( void ) { io_registry_entry_t rootEntry = IORegistryEntryFromPath( kIOMasterPortDefault, “IOService:/” ); CFTypeRef serialAsCFString = NULL;
serialAsCFString = IORegistryEntryCreateCFProperty( rootEntry, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
IOObjectRelease( rootEntry ); return (NULL != serialAsCFString) ? [(NSString*)serialAsCFString autorelease] : nil; }
static NSDictionary* getCpuIds( void ) { NSMutableDictionary* cpuInfo = [[NSMutableDictionary alloc] init]; CFMutableDictionaryRef matchClasses = NULL; kern_return_t kernResult = KERN_FAILURE; mach_port_t machPort; io_iterator_t serviceIterator;
io_object_t cpuService;
kernResult = IOMasterPort( MACH_PORT_NULL, &machPort ); if( KERN_SUCCESS != kernResult ) { printf( “IOMasterPort failed: %d\n”, kernResult ); }
/* ADDED !!! / matchClasses = IOServiceNameMatching( MATCHSTR ); / matchClasses = IOServiceNameMatching( “processor” ); */
if( NULL == matchClasses ) { printf( “IOServiceMatching returned a NULL dictionary” ); }
kernResult = IOServiceGetMatchingServices( machPort, matchClasses, &serviceIterator ); if( KERN_SUCCESS != kernResult ) { printf( “IOServiceGetMatchingServices failed: %d\n”, kernResult ); }
while( (cpuService = IOIteratorNext( serviceIterator )) ) {
CFTypeRef CPUIDAsCFNumber = NULL;
io_name_t nameString;
IORegistryEntryGetNameInPlane( cpuService, kIOServicePlane, nameString );
CPUIDAsCFNumber = IORegistryEntrySearchCFProperty( cpuService,
kIOServicePlane,
CFSTR( "IOCPUID" ),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if( NULL != CPUIDAsCFNumber ) {
NSString* cpuName = [NSString stringWithCString:nameString];
[cpuInfo setObject:(NSNumber*)CPUIDAsCFNumber forKey:cpuName];
}
if( NULL != CPUIDAsCFNumber ) {
CFRelease( CPUIDAsCFNumber );
} }
IOObjectRelease( serviceIterator );
return [cpuInfo autorelease]; }
Note that you can make use of the system_profiler command line to gather full information about the system. Its -xml option generates a plist file that can be used easily in any Cocoa application:
system_profiler reports on the hardware and software configuration of the system. It can generate plain text reports or XML reports which can be opened with SystemProfiler.app
-xml Generates a report in XML format. If the XML report is redirected to a file with a ".spx" suffix that file can be opened with System Profiler.app.
-listDataTypes Lists the available datatypes.
-detailLevel level Specifies the level of detail for the report:
mini report with no personal information
basic basic hardware and network information
full all available information