mirror of
https://github.com/munki/munki.git
synced 2026-04-30 17:29:21 -05:00
Convert more comments to documentation
This commit is contained in:
@@ -20,13 +20,13 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Do spotlight search for type applications within the
|
||||
/// list of directories provided. Returns a list of paths to applications.
|
||||
/// dirList is actually a list of NSMetadataQuery search scopes, which
|
||||
/// can be paths, or certain constants
|
||||
/// Typically we'll use NSMetadataQueryLocalComputerScope to search the
|
||||
/// local computer (skipping mounted network volumes)
|
||||
func findAppsInDirs(_ dirList: [String]) -> [[String: String]] {
|
||||
// Do spotlight search for type applications within the
|
||||
// list of directories provided. Returns a list of paths to applications
|
||||
// dirList is actually a list of NSMetadataQuery search scopes, which
|
||||
// can be paths, or certain constants
|
||||
// Typically we'll use NSMetadataQueryLocalComputerScope to search the
|
||||
// local computer (skipping mounted network volumes)
|
||||
var appList = [[String: String]]()
|
||||
let query = NSMetadataQuery()
|
||||
query.predicate = NSPredicate(format: "(kMDItemKind = \"Application\")")
|
||||
@@ -79,14 +79,14 @@ func findAppsInDirs(_ dirList: [String]) -> [[String: String]] {
|
||||
return appList
|
||||
}
|
||||
|
||||
/// Get paths of currently installed applications per Spotlight.
|
||||
/// Return value is list of paths.
|
||||
/// Currently searches only the local computer, but not network volumes
|
||||
func spotlightInstalledApps() -> [[String: String]] {
|
||||
// Get paths of currently installed applications per Spotlight.
|
||||
// Return value is list of paths.
|
||||
// Currently searches only the local computer, but not network volumes
|
||||
return findAppsInDirs([NSMetadataQueryLocalComputerScope])
|
||||
}
|
||||
|
||||
// private, undocumented LaunchServices function
|
||||
/// private, undocumented LaunchServices function
|
||||
@_silgen_name("_LSCopyAllApplicationURLs") func LSCopyAllApplicationURLs(_: UnsafeMutablePointer<NSMutableArray?>) -> OSStatus
|
||||
func launchServicesInstalledApps() -> [String] {
|
||||
var apps: NSMutableArray?
|
||||
@@ -99,9 +99,9 @@ func launchServicesInstalledApps() -> [String] {
|
||||
return [String]()
|
||||
}
|
||||
|
||||
/// Uses system profiler to get application info for this machine
|
||||
/// Returns a dictionary with app paths as keys
|
||||
func spApplicationData() async -> PlistDict {
|
||||
// Uses system profiler to get application info for this machine
|
||||
// Returns a dictionary with app paths as keys
|
||||
var applicationData = PlistDict()
|
||||
let tool = "/usr/sbin/system_profiler"
|
||||
let arguments = ["SPApplicationsDataType", "-xml"]
|
||||
@@ -141,21 +141,15 @@ func spApplicationData() async -> PlistDict {
|
||||
return applicationData
|
||||
}
|
||||
|
||||
/// Gets info on currently installed apps.
|
||||
/// Returns a list of dicts containing path, name, version and bundleid
|
||||
func getAppData() -> [[String: String]] {
|
||||
// Gets info on currently installed apps.
|
||||
// Returns a list of dicts containing path, name, version and bundleid
|
||||
|
||||
// one thing I'm not at all sure about is what iOS/iPadOS apps
|
||||
// installed on Apple silicon Macs look like and how/if
|
||||
// Launch Services, Spotlight, and system_profiler report them
|
||||
|
||||
displayDebug1("Getting info on currently installed applications...")
|
||||
// async let spAppData = spApplicationData()
|
||||
let lsApps = launchServicesInstalledApps()
|
||||
// print("LaunchServices found \(lsApps.count) apps")
|
||||
let spotlightApps = spotlightInstalledApps()
|
||||
// print("Spotlight found \(spotlightApps.count) apps")
|
||||
// print("system_profiler found \(await spAppData.count) apps")
|
||||
|
||||
// find apps that are unique to the LaunchServices list
|
||||
let spotlightAppPaths = spotlightApps.map { $0["path"] ?? "" }.filter { !$0.isEmpty }
|
||||
@@ -192,9 +186,8 @@ func getAppData() -> [[String: String]] {
|
||||
return applicationData
|
||||
}
|
||||
|
||||
/// A Singleton class for application inventory info, since it's expensive to generate
|
||||
class ApplicationInventory {
|
||||
// a Singleton class for application inventory info, since it's expensive
|
||||
// to generate
|
||||
static let shared = ApplicationInventory()
|
||||
|
||||
var inventory: [[String: String]]
|
||||
@@ -213,22 +206,23 @@ class ApplicationInventory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return (possibly cached) installed application data
|
||||
func appData() -> [[String: String]] {
|
||||
return ApplicationInventory.shared.get()
|
||||
}
|
||||
|
||||
/// Returns a filtered version of app_data, filtering out apps in user
|
||||
/// home directories for use by compare_application_version()
|
||||
func filteredAppData() -> [[String: String]] {
|
||||
// Returns a filtered version of app_data, filtering out apps in user
|
||||
// home directories for use by compare_application_version()
|
||||
return appData().filter {
|
||||
!(($0["path"] ?? "").hasPrefix("/Users") && !($0["path"] ?? "").hasPrefix("/Users/Shared"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Save installed application data
|
||||
/// data from appData() is meant for use by updatecheck
|
||||
/// we need to massage it a bit for more general usage
|
||||
func saveAppData() {
|
||||
// Save installed application data
|
||||
// data from appData() is meant for use by updatecheck
|
||||
// we need to massage it a bit for more general usage
|
||||
munkiLog("Saving application inventory...")
|
||||
var appInventory = [[String: String]]()
|
||||
for item in appData() {
|
||||
|
||||
@@ -20,10 +20,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Gets some facts about this machine we use to determine if a given
|
||||
/// installer is applicable to this OS or hardware
|
||||
func generateMachineFacts() async -> PlistDict {
|
||||
// Gets some facts about this machine we use to determine if a given
|
||||
// installer is applicable to this OS or hardware
|
||||
|
||||
// these all call system_profiler so see if we can do
|
||||
// them concurrently
|
||||
async let ip4addresses = await getIPAddresses("IPv4")
|
||||
@@ -66,9 +65,8 @@ func generateMachineFacts() async -> PlistDict {
|
||||
return machine
|
||||
}
|
||||
|
||||
/// A Singleton class for machine facts, since they are expensive to generate
|
||||
class MachineFacts {
|
||||
// a Singleton class for machine facts, since they are expensive
|
||||
// to generate
|
||||
static let shared = MachineFacts()
|
||||
|
||||
var facts: PlistDict
|
||||
@@ -85,21 +83,20 @@ class MachineFacts {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return 'facts' about this machine
|
||||
func getMachineFacts() async -> PlistDict {
|
||||
// return 'facts' about this machine
|
||||
return await MachineFacts.shared.get()
|
||||
}
|
||||
|
||||
/// Returns the path to the conditional scripts dir
|
||||
private func conditionalScriptsDir() -> String {
|
||||
// returns the path to the conditional scripts dir
|
||||
// TODO: make this relative to the managedsoftwareupdate binary
|
||||
return "/usr/local/munki/conditions"
|
||||
}
|
||||
|
||||
/// Fetches key/value pairs from condition scripts
|
||||
/// which can be placed into /usr/local/munki/conditions
|
||||
func getConditions() async -> PlistDict {
|
||||
// Fetches key/value pairs from condition scripts
|
||||
// which can be placed into /usr/local/munki/conditions
|
||||
|
||||
let conditionalScriptDir = conditionalScriptsDir()
|
||||
let conditionalItemsPath = managedInstallsDir(subpath: "ConditionalItems.plist")
|
||||
let filemanager = FileManager.default
|
||||
@@ -151,17 +148,16 @@ func getConditions() async -> PlistDict {
|
||||
return PlistDict() // empty results
|
||||
}
|
||||
|
||||
/// Input: NSDate object
|
||||
/// Output: NSDate object with same date and time as the UTC.
|
||||
/// In Los Angeles (PDT), '2011-06-20T12:00:00Z' becomes
|
||||
/// '2011-06-20 12:00:00 -0700'.
|
||||
/// In New York (EDT), it becomes '2011-06-20 12:00:00 -0400'.
|
||||
/// This allows a pkginfo item to reference a time in UTC that
|
||||
/// gets translated to the same relative local time.
|
||||
/// A force_install_after_date for '2011-06-20T12:00:00Z' will happen
|
||||
/// after 2011-06-20 12:00:00 local time.
|
||||
func subtractTZOffsetFromDate(_ date: Date) -> Date {
|
||||
// Input: NSDate object
|
||||
// Output: NSDate object with same date and time as the UTC.
|
||||
// In Los Angeles (PDT), '2011-06-20T12:00:00Z' becomes
|
||||
// '2011-06-20 12:00:00 -0700'.
|
||||
// In New York (EDT), it becomes '2011-06-20 12:00:00 -0400'.
|
||||
// This allows a pkginfo item to reference a time in UTC that
|
||||
// gets translated to the same relative local time.
|
||||
// A force_install_after_date for '2011-06-20T12:00:00Z' will happen
|
||||
// after 2011-06-20 12:00:00 local time.
|
||||
|
||||
// find our time zone offset in seconds
|
||||
let timezone = NSTimeZone.default
|
||||
let secondsOffset = Double(timezone.secondsFromGMT(for: date))
|
||||
@@ -169,17 +165,16 @@ func subtractTZOffsetFromDate(_ date: Date) -> Date {
|
||||
return Date(timeInterval: -secondsOffset, since: date)
|
||||
}
|
||||
|
||||
/// Input: NSDate object
|
||||
/// Output: NSDate object with timezone difference added
|
||||
/// to the date. This allows conditional_item conditions to
|
||||
/// be written like so:
|
||||
///
|
||||
/// <key>condition</key>
|
||||
/// <string>date > CAST("2012-12-17T16:00:00Z", "NSDate")</string>
|
||||
///
|
||||
/// with the intent being that the comparison is against local time.
|
||||
func addTZOffsetToDate(_ date: Date) -> Date {
|
||||
// Input: NSDate object
|
||||
// Output: NSDate object with timezone difference added
|
||||
// to the date. This allows conditional_item conditions to
|
||||
// be written like so:
|
||||
//
|
||||
// <key>condition</key>
|
||||
// <string>date > CAST("2012-12-17T16:00:00Z", "NSDate")</string>
|
||||
//
|
||||
// with the intent being that the comparison is against local time.
|
||||
|
||||
// find our time zone offset in seconds
|
||||
let timezone = NSTimeZone.default
|
||||
let secondsOffset = Double(timezone.secondsFromGMT(for: date))
|
||||
@@ -187,9 +182,9 @@ func addTZOffsetToDate(_ date: Date) -> Date {
|
||||
return Date(timeInterval: secondsOffset, since: date)
|
||||
}
|
||||
|
||||
/// Returns our info object used for predicate comparisons
|
||||
func generatePredicateInfo() async -> PlistDict {
|
||||
// Returns our info object used for predicate comparisons
|
||||
|
||||
// let's do some stuff concurrently
|
||||
async let machine = getMachineFacts()
|
||||
async let conditions = getConditions()
|
||||
var infoObject = await machine
|
||||
@@ -223,9 +218,8 @@ func generatePredicateInfo() async -> PlistDict {
|
||||
return infoObject
|
||||
}
|
||||
|
||||
/// A Singleton class for predicate info, since it's expensive to generate
|
||||
class PredicateInfo {
|
||||
// a Singleton class for predicate info, since it's expensive
|
||||
// to generate
|
||||
static let shared = PredicateInfo()
|
||||
|
||||
var info: PlistDict
|
||||
@@ -242,19 +236,19 @@ class PredicateInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return our (possibly cached) info object
|
||||
func predicateInfoObject() async -> PlistDict {
|
||||
return await PredicateInfo.shared.get()
|
||||
}
|
||||
|
||||
/// Evaluates predicate against the info object; returns a boolean
|
||||
/// Calls out to an Objective-C function because NSPrediacte methods can
|
||||
/// raise NSExecption, whcih Swift cannot catch
|
||||
func predicateEvaluatesAsTrue(
|
||||
_ predicateString: String,
|
||||
infoObject: PlistDict,
|
||||
additionalInfo: PlistDict? = nil
|
||||
) -> Bool {
|
||||
// Evaluates predicate against the info object; returns a boolean
|
||||
// Calls out to an Objective-C function because NSPrediacte methods can
|
||||
// raise NSExecption, whcih Swift cannot catch
|
||||
|
||||
var ourObject = infoObject
|
||||
if let additionalInfo {
|
||||
ourObject.merge(additionalInfo) { _, new in new }
|
||||
|
||||
@@ -23,8 +23,8 @@ import Darwin
|
||||
import Foundation
|
||||
import IOKit
|
||||
|
||||
/// Uses system profiler to get info of dataType for this machine
|
||||
func getSystemProfilerData(_ dataType: String) async -> PlistDict {
|
||||
// Uses system profiler to get info of data_type for this machine
|
||||
let tool = "/usr/sbin/system_profiler"
|
||||
let arguments = [dataType, "-xml"]
|
||||
let result = await runCliAsync(tool, arguments: arguments)
|
||||
@@ -46,20 +46,20 @@ func getSystemProfilerData(_ dataType: String) async -> PlistDict {
|
||||
return PlistDict()
|
||||
}
|
||||
|
||||
/// Uses system profiler to get hardware info for this machine
|
||||
func getHardwareInfo() async -> PlistDict {
|
||||
// Uses system profiler to get hardware info for this machine
|
||||
return await getSystemProfilerData("SPHardwareDataType")
|
||||
}
|
||||
|
||||
/// Uses system profiler to get iBridge info for this machine
|
||||
func getIBridgeInfo() async -> PlistDict {
|
||||
// Uses system profiler to get iBridge info for this machine
|
||||
return await getSystemProfilerData("SPiBridgeDataType")
|
||||
}
|
||||
|
||||
/// Uses system profiler to get active IP addresses for this machine
|
||||
/// kind must be one of 'IPv4' or 'IPv6'
|
||||
/// NOTE this does not return any utun addresses.
|
||||
func getIPAddresses(_ kind: String) async -> [String] {
|
||||
// Uses system profiler to get active IP addresses for this machine
|
||||
// kind must be one of 'IPv4' or 'IPv6'
|
||||
// NOTE this does not return any utun addresses.
|
||||
var ipAddresses = [String]()
|
||||
let tool = "/usr/sbin/system_profiler"
|
||||
let arguments = ["SPNetworkDataType", "-xml"]
|
||||
@@ -86,25 +86,25 @@ func getIPAddresses(_ kind: String) async -> [String] {
|
||||
return ipAddresses
|
||||
}
|
||||
|
||||
// IOKit helpers
|
||||
// MARK: IOKit helpers
|
||||
|
||||
/// Returns a reference to an IOKit service matching on IOService class name,
|
||||
/// typically something like "IOPlatformExpertDevice"
|
||||
private func serviceMatching(_ className: String) -> io_registry_entry_t {
|
||||
// returns a reference to an IOKit service matching on IOService class name,
|
||||
// typically something like "IOPlatformExpertDevice"
|
||||
return IOServiceGetMatchingService(
|
||||
kIOMasterPortDefault, IOServiceMatching(className)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a reference to an IOKit service matching on IOService name
|
||||
private func serviceNameMatching(_ name: String) -> io_registry_entry_t {
|
||||
// returns a reference to an IOKit service matching on IOService name
|
||||
return IOServiceGetMatchingService(
|
||||
kIOMasterPortDefault, IOServiceNameMatching(name)
|
||||
)
|
||||
}
|
||||
|
||||
/// Attempts to return a string value for the given property key
|
||||
private func stringValueForIOServiceProperty(service: io_registry_entry_t, key: String) -> String? {
|
||||
// attempts to return a string value for the given property key
|
||||
let rawData = IORegistryEntryCreateCFProperty(
|
||||
service, key as CFString, kCFAllocatorDefault, 0
|
||||
)
|
||||
@@ -116,11 +116,11 @@ private func stringValueForIOServiceProperty(service: io_registry_entry_t, key:
|
||||
encoding: .utf8)?.trimmingCharacters(in: ["\0"])
|
||||
}
|
||||
|
||||
// info functions that call IOKit
|
||||
// MARK: info functions that call IOKit
|
||||
|
||||
/// Returns mouse/keyboard idel time in nanoseconds
|
||||
/// (1/1000000000) of a second
|
||||
func hidIdleTime() -> Int {
|
||||
// returns mouse/keyboard idel time in nanoseconds
|
||||
// (1/1000000000) of a second
|
||||
let idleTime = IORegistryEntryCreateCFProperty(
|
||||
serviceMatching("IOHIDSystem"),
|
||||
"HIDIdleTime" as CFString,
|
||||
@@ -134,8 +134,8 @@ func hidIdleTime() -> Int {
|
||||
return nanoSeconds as! Int
|
||||
}
|
||||
|
||||
/// Returns the serial number of this Mac
|
||||
func serialNumber() -> String {
|
||||
// Returns the serial number of this Mac
|
||||
let serial = IORegistryEntryCreateCFProperty(
|
||||
serviceMatching("IOPlatformExpertDevice"),
|
||||
kIOPlatformSerialNumberKey as CFString,
|
||||
@@ -148,34 +148,34 @@ func serialNumber() -> String {
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
/// Returns the product name from IORegistry
|
||||
func productName() -> String {
|
||||
// Returns the product name from IORegistry
|
||||
return stringValueForIOServiceProperty(
|
||||
service: serviceNameMatching("product"),
|
||||
key: "product-name"
|
||||
) ?? "Intel Mac"
|
||||
}
|
||||
|
||||
/// Returns board-id from IORegistry
|
||||
func boardID() -> String {
|
||||
// Returns board-id from IORegistry
|
||||
return stringValueForIOServiceProperty(
|
||||
service: serviceMatching("IOPlatformExpertDevice"),
|
||||
key: "board-id"
|
||||
) ?? "<none>"
|
||||
}
|
||||
|
||||
/// Returns device id from IORegistry
|
||||
func deviceID() -> String {
|
||||
// Returns board-id from IORegistry
|
||||
return stringValueForIOServiceProperty(
|
||||
service: serviceMatching("IOPlatformExpertDevice"),
|
||||
key: "target-sub-type"
|
||||
) ?? "<none>"
|
||||
}
|
||||
|
||||
// info functions that use sysctlbyname
|
||||
// MARK: info functions that use sysctlbyname
|
||||
|
||||
/// Returns model (like 'Mac1,2')
|
||||
func hardwareModel() -> String {
|
||||
// returns model (Mac1,2)
|
||||
var size = 0
|
||||
// call sysctlbyname to get the size of the returned string
|
||||
let err1 = sysctlbyname("hw.model", nil, &size, nil, 0)
|
||||
@@ -196,8 +196,8 @@ func hardwareModel() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns true if this Mac has an Intel processor that supports 64bit code
|
||||
func hasIntel64Support() -> Bool {
|
||||
// returns true if this Mac has an Intel processor that supports 64bit code
|
||||
var size = 0
|
||||
// call sysctlbyname to get the size of the returned value
|
||||
let err1 = sysctlbyname("hw.optional.x86_64", nil, &size, nil, 0)
|
||||
@@ -215,10 +215,10 @@ func hasIntel64Support() -> Bool {
|
||||
return buffer[0] == 1
|
||||
}
|
||||
|
||||
/// Returns available diskspace in KBytes.
|
||||
/// Value should be very close to `df -k` output
|
||||
/// Returns negative values of there is an error
|
||||
func availableDiskSpace(volumePath _: String = "/") -> Int {
|
||||
// Returns available diskspace in KBytes.
|
||||
// Value should be very close to `df -k` output
|
||||
// Returns negative values of there is an error
|
||||
let buffer = UnsafeMutablePointer<statvfs>.allocate(capacity: 1024)
|
||||
defer { buffer.deallocate() }
|
||||
let err = statvfs("/", buffer)
|
||||
@@ -230,8 +230,8 @@ func availableDiskSpace(volumePath _: String = "/") -> Int {
|
||||
return Int(f_frsize * f_bavail / 1024)
|
||||
}
|
||||
|
||||
/// Returns uname's version of hostname
|
||||
func hostname() -> String {
|
||||
// returns uname's version of hostname
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let size = Int(_SYS_NAMELEN) // is 256 on Darwin
|
||||
@@ -244,8 +244,8 @@ func hostname() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns platform (arch) ("x86_64", "arm64")
|
||||
func platform() -> String {
|
||||
// returns platform (arch) ("x86_64", "arm64")
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let size = Int(_SYS_NAMELEN) // is 256 on Darwin
|
||||
@@ -258,8 +258,8 @@ func platform() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns uname's version string
|
||||
func uname_version() -> String {
|
||||
// returns uname's version string
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let size = Int(_SYS_NAMELEN) // is 256 on Darwin
|
||||
@@ -272,9 +272,8 @@ func uname_version() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns uname's system string. (Pretty much always returns "Darwin")
|
||||
func uname_sysname() -> String {
|
||||
// returns uname's system string
|
||||
// Pretty much always returns "Darwin"
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let size = Int(_SYS_NAMELEN) // is 256 on Darwin
|
||||
@@ -287,9 +286,8 @@ func uname_sysname() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns uname's release string (Darwin version)
|
||||
func uname_release() -> String {
|
||||
// returns uname's system string
|
||||
// Pretty much always returns "Darwin"
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let size = Int(_SYS_NAMELEN) // is 256 on Darwin
|
||||
@@ -302,13 +300,13 @@ func uname_release() -> String {
|
||||
return str
|
||||
}
|
||||
|
||||
/// Returns true if we're running on Apple silicon
|
||||
func isAppleSilicon() -> Bool {
|
||||
// Returns true if we're running on Apple silicon"
|
||||
return platform() == "arm64"
|
||||
}
|
||||
|
||||
/// Returns the OS Build "number" (example 16G1212).
|
||||
func getOSBuild() -> String {
|
||||
// Returns the OS Build "number" (example 16G1212).
|
||||
do {
|
||||
if let systemVersion = try readPlist(
|
||||
fromFile: "/System/Library/CoreServices/SystemVersion.plist"
|
||||
|
||||
Reference in New Issue
Block a user