Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift sysctl get integer

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?

like image 573
user265889 Avatar asked Sep 17 '25 08:09

user265889


2 Answers

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
}
like image 195
Charles Srstka Avatar answered Sep 19 '25 10:09

Charles Srstka


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.

like image 38
Pietro Caruso Avatar answered Sep 19 '25 11:09

Pietro Caruso