在 Linux Swift 包中使用 sysctlbyname 函数
Use the sysctlbyname function within a Linux Swift Package
我正在尝试为我的 swift 包库添加 linux 支持以获取系统信息,但我不知道如何访问 [=29] 上的 sysctlbyname
功能=] 在 Swift 包中。
对于它的所有检测,该库依赖于 sysctlbyname
函数,可以通过在 Apple 平台上导入 Dariwn.sys.sysctl
轻松访问该函数,但是我找不到任何 Swift 方法来在 linux 上访问该函数,尽管事实上您可以在 C 中通过在基本上任何 unix 平台上导入 sys/sysctl.h 来访问它。
所以我想知道如何在 linux 上的 Swift 库中访问该函数,以及是否可以在不使用 C 或其他非 Swift 的情况下访问该函数东西,也是因为我想让我的代码与苹果系统的 Swift playgrounds 应用程序兼容,它不支持具有 C 导入功能的 SPM 库。
作为参考,我在这里留下了我项目中负责与 sysctlbyname
接口的代码部分:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
以及如何在代码中使用它的示例(该死的它被用来检索更多的东西):
///Kernel info
final class KernelInfo: SysctlFetch{
static var namePrefix: String{
#if os(Linux)
return "kernel."
#else
return "kern."
#endif
}
///The os kernel name
static var ostype: String?{
return Self.getString("ostype")
}
/* Other static vars here */
}
所以可以将 C、C++ 或 Objective-C 目标添加到 Swift 包中,这样就可以导入所需的系统头文件,然后创建一些包装函数,从而实现所需的功能, Swift 可访问,但这破坏了 Swift playgrounds 应用程序开发兼容性,因为它支持 Swift-only 目标(一个可能的解决方法是将 C/C++ 目标放在单独的 swift 包有条件地将其用作 linux 的依赖项,有关更多详细信息,请参阅相关的 swift 包文档。
所以添加一个 C/C++ 目标本来可以解决问题,但问题是在 Linux 内核版本 5.5 及以后的版本中 sysctl
函数已被弃用并且即使在较旧的内核上,它们也不适用于所有 cpu 架构 Linux 支持,因此在计算机上 运行 最近的内核或某些特定的非 x86 cpu体系结构,这样的 Swift 包将无法成功构建。
当前访问 sysctl
函数过去提供的信息的方法是直接从 /proc/sys
目录内的文件系统读取它,它适用于所有支持的 cpu 架构,它是 sysctl
命令行实用程序获取该数据。
所以只有 linux 代码必须像这样修改,才能在所有平台上成功收集该数据:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
#if !os(Linux)
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
#else
///Gets a `String` from `/proc/sys`
static func getString(_ valueName: String) -> String?{
let path = "/proc/sys/" + (namePrefix + valueName).replacingOccurrences(of: ".", with: "/")
var contents = ""
do{
contents = try String(contentsOfFile: path)
}catch let err{
return nil
}
if contents.last == "\n"{
contents.removeLast()
}
return contents
}
///Gets an Integer value from `/proc/sys`
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
guard let str = getString(valueName) else { return nil }
return T(str)
}
#endif
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
所以最后我自己弄明白了,我希望这对任何必须做同样事情的人都有用。
我正在尝试为我的 swift 包库添加 linux 支持以获取系统信息,但我不知道如何访问 [=29] 上的 sysctlbyname
功能=] 在 Swift 包中。
对于它的所有检测,该库依赖于 sysctlbyname
函数,可以通过在 Apple 平台上导入 Dariwn.sys.sysctl
轻松访问该函数,但是我找不到任何 Swift 方法来在 linux 上访问该函数,尽管事实上您可以在 C 中通过在基本上任何 unix 平台上导入 sys/sysctl.h 来访问它。
所以我想知道如何在 linux 上的 Swift 库中访问该函数,以及是否可以在不使用 C 或其他非 Swift 的情况下访问该函数东西,也是因为我想让我的代码与苹果系统的 Swift playgrounds 应用程序兼容,它不支持具有 C 导入功能的 SPM 库。
作为参考,我在这里留下了我项目中负责与 sysctlbyname
接口的代码部分:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
以及如何在代码中使用它的示例(该死的它被用来检索更多的东西):
///Kernel info
final class KernelInfo: SysctlFetch{
static var namePrefix: String{
#if os(Linux)
return "kernel."
#else
return "kern."
#endif
}
///The os kernel name
static var ostype: String?{
return Self.getString("ostype")
}
/* Other static vars here */
}
所以可以将 C、C++ 或 Objective-C 目标添加到 Swift 包中,这样就可以导入所需的系统头文件,然后创建一些包装函数,从而实现所需的功能, Swift 可访问,但这破坏了 Swift playgrounds 应用程序开发兼容性,因为它支持 Swift-only 目标(一个可能的解决方法是将 C/C++ 目标放在单独的 swift 包有条件地将其用作 linux 的依赖项,有关更多详细信息,请参阅相关的 swift 包文档。
所以添加一个 C/C++ 目标本来可以解决问题,但问题是在 Linux 内核版本 5.5 及以后的版本中 sysctl
函数已被弃用并且即使在较旧的内核上,它们也不适用于所有 cpu 架构 Linux 支持,因此在计算机上 运行 最近的内核或某些特定的非 x86 cpu体系结构,这样的 Swift 包将无法成功构建。
当前访问 sysctl
函数过去提供的信息的方法是直接从 /proc/sys
目录内的文件系统读取它,它适用于所有支持的 cpu 架构,它是 sysctl
命令行实用程序获取该数据。
所以只有 linux 代码必须像这样修改,才能在所有平台上成功收集该数据:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
#if !os(Linux)
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
#else
///Gets a `String` from `/proc/sys`
static func getString(_ valueName: String) -> String?{
let path = "/proc/sys/" + (namePrefix + valueName).replacingOccurrences(of: ".", with: "/")
var contents = ""
do{
contents = try String(contentsOfFile: path)
}catch let err{
return nil
}
if contents.last == "\n"{
contents.removeLast()
}
return contents
}
///Gets an Integer value from `/proc/sys`
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
guard let str = getString(valueName) else { return nil }
return T(str)
}
#endif
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
所以最后我自己弄明白了,我希望这对任何必须做同样事情的人都有用。