I'm trying to make an application that reads system information (on MacOS) and I've been able to read sysctl STRINGS like so:
func cpu() -> String {
var size = 0
sysctlbyname("machdep.cpu.brand_string", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: Int(size))
sysctlbyname("machdep.cpu.brand_string", &machine, &size, nil, 0)
return String(cString: machine)
}
but when I try to read integers like hw.cpufrequency like so:
func cpuFreq() -> String {
var size = 0
sysctlbyname("hw.cpufrequency", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: Int(size))
sysctlbyname("hw.cpufrequency", &machine, &size, nil, 0)
return String(cString: machine)
}
It returns absolutely nothing, any clues?
Your code is assuming that the return value will be a string, but it's not; it's actually an integer. If you look at the man page for sysctl(3) [type 'man 3 sysctl' in the Terminal to see it], you'll see that "hw.cpufrequency" returns an int64_t in C, which translates to an Int64 in Swift. So you want to read the value into an Int64, not a string. You can do that like this:
func cpuFreq() throws -> Int64 {
var frequency: Int64 = 0
var size = MemoryLayout<Int64>.size
if sysctlbyname("hw.cpufrequency", &frequency, &size, nil, 0) != 0 {
throw POSIXError.Code(rawValue: errno).map { POSIXError($0) } ?? CocoaError(.fileReadUnknown)
}
return frequency
}
In your case you are trying to retrive an integer value using code made t retrive string values, so it's not going to work.
Basically sysctl gives you 2 types of values (except for the value tables), which are integers and strings.
Here is some code i made for my library SwiftCPUDetect to retrive both:
//Strings
func getString(_ name: String) -> String?{
var size: size_t = 0
//gets the string size
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
//Returns nil if the specified name entry has not been found
return nil
}
//Allocates the appropriate vector (with extra termination just t be sure)
var ret = [CChar].init(repeating: 0, count: size + 1)
//retrives value
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
//Integers
func getInteger<T: FixedWidthInteger>(_ name: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret) //gets the size of the provided integer value
let res = sysctlbyname(name, &ret, &size, nil, 0) //fetches the value
return res == 0 ? ret : nil //returns the retrieved integer if the value name entry exists otherwise returns nil
}
And here are some example usages (working only on macOS):
///Gets the number of cores of the current CPU
func cores_count() -> UInt?{
return getInteger("machdep.cpu.core_count")
}
///Gets the brand name for the current CPU
func brand_string() -> String?{
return getString("machdep.cpu.brand_string")
}
Also sysctl can do boolean values using integers set to either 0 or 1, so you can use this simple function to retrive boolean values too:
//Boolean
func getBool(_ name: String) -> Bool?{
guard let res: Int32 = getInteger(name) else{
return nil
}
return res == 1
}
I hope this can be useful to you, and feel free to use the library i mentioned in your projects, it contains a lot of useful stuff like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With