Convert more comments to documentation

This commit is contained in:
Greg Neagle
2024-09-11 11:41:58 -07:00
parent 7ba4266a3d
commit 190a024f26
4 changed files with 72 additions and 92 deletions

View File

@@ -7,9 +7,9 @@
import Foundation
/// Sets owner, group and mode for path from info in itemInfo.
/// Returns 0 on success, non-zero otherwise.
func setPermissions(_ itemInfo: PlistDict, path: String) -> Int {
// Sets owner, group and mode for path from info in itemInfo.
// Returns 0 on success, non-zero otherwise.
// Yes, we could call FileManager methods like setAttributes(_:ofItemAtPath:),
// But the user and group might be names or numbers, and the mode is
// supposed to be symbolic (but could also be in the format of "777",
@@ -40,9 +40,9 @@ func setPermissions(_ itemInfo: PlistDict, path: String) -> Int {
return 0
}
/// Creates any missing intermediate directories so we can copy item.
/// Returns boolean to indicate success or failure
func createMissingDirs(_ path: String) -> Bool {
// Creates any missing intermediate directories so we can copy item.
// Returns boolean to indicate success or failure
let filemanager = FileManager.default
if filemanager.fileExists(atPath: path) {
// the path exists; don't need to create anything
@@ -75,8 +75,8 @@ func createMissingDirs(_ path: String) -> Bool {
}
}
/// Removes com.apple.quarantine xattr from a path
func removeQuarantineXattrFromItem(_ path: String) {
// Removes com.apple.quarantine xattr from a path
do {
let xattrs = try listXattrs(atPath: path)
if xattrs.contains("com.apple.quarantine") {
@@ -89,8 +89,8 @@ func removeQuarantineXattrFromItem(_ path: String) {
}
}
/// Removes com.apple.quarantine xattr from a path, recursively if needed
func removeQuarantineXattrsRecursively(_ path: String) {
// Removes com.apple.quarantine xattr from a path, recursively if needed
removeQuarantineXattrFromItem(path)
if pathIsDirectory(path) {
let dirEnum = FileManager.default.enumerator(atPath: path)
@@ -101,11 +101,9 @@ func removeQuarantineXattrsRecursively(_ path: String) {
}
}
/// Validates source and destination for item to be copied from a mounted disk image.
/// Returns a tuple of (success, source_path, destination_path)
func validateSourceAndDestination(mountpoint: String, item: PlistDict) -> (Bool, String, String) {
// Validates source and destination for item to be copied from a mounted
// disk image.
// Returns a tuple of (success, source_path, destination_path)
// Ensure source item is defined
guard let sourceItemName = item["source_item"] as? String else {
displayError("Missing name of item to copy!")
@@ -147,8 +145,8 @@ func validateSourceAndDestination(mountpoint: String, item: PlistDict) -> (Bool,
return (true, sourceItemPath, fullDestinationPath)
}
/// Recursively gets size of pathname in bytes
func getSize(_ path: String) -> Int {
// Recursively gets size of pathname in bytes
if pathIsDirectory(path) {
return getSizeOfDirectory(path)
}
@@ -160,10 +158,8 @@ func getSize(_ path: String) -> Int {
return 0
}
/// Subclass of AsyncProcessRunner that handles the progress output from /usr/bin/ditto
class DittoRunner: AsyncProcessRunner {
// subclass of AsyncProcessRunner that handles the progress output from
// /usr/bin/ditto
var remainingErrorOutput = ""
var totalBytesCopied = 0
var sourceItemSize = 1
@@ -202,16 +198,16 @@ class DittoRunner: AsyncProcessRunner {
}
}
/// Uses ditto to copy an item and provides progress output
func dittoWithProgress(sourcePath: String, destinationPath: String) async -> Int {
// Uses ditto to copy an item and provides progress output
let proc = DittoRunner(sourcePath: sourcePath, destinationPath: destinationPath)
await proc.run()
return proc.results.exitcode
}
/// Copies items from the mountpoint to the startup disk
/// Returns 0 if no issues; some error code otherwise.
func copyItemsFromMountpoint(_ mountpoint: String, itemList: [PlistDict]) async -> Int {
// copies items from the mountpoint to the startup disk
// Returns 0 if no issues; some error code otherwise.
guard let tempDestinationDir = TempDir.shared.makeTempDir() else {
displayError("Could not create a temporary directory!")
return -1
@@ -268,8 +264,8 @@ func copyItemsFromMountpoint(_ mountpoint: String, itemList: [PlistDict]) async
return 0
}
/// Copies items from disk image to local disk
func copyFromDmg(dmgPath: String, itemList: [PlistDict]) async -> Int {
// Copies items from disk image to local disk
if itemList.isEmpty {
displayError("No items to copy!")
return -1

View File

@@ -7,12 +7,11 @@
import Foundation
/// Removes filesystem items based on info in itemlist.
/// These items were typically installed via copy_from_dmg
/// This current aborts and returns false on the first error;
/// might it make sense to try to continue and remove as much as we can?
func removeCopiedItems(_ itemList: [PlistDict]) -> Bool {
// Removes filesystem items based on info in itemlist.
// These items were typically installed via copy_from_dmg
// This current aborts and returns false on the first error;
// might it make sense to try to continue and remove as much
// as we can?
if itemList.isEmpty {
displayError("Nothing to remove!")
return false
@@ -61,9 +60,9 @@ func removeCopiedItems(_ itemList: [PlistDict]) -> Bool {
return true
}
/// Looks for item prerequisites (requires and update_for) in the list of skipped items.
/// Returns a list of matches.
func itemPrereqsInSkippedItems(currentItem: PlistDict, skippedItems: [PlistDict]) -> [String] {
// Looks for item prerequisites (requires and update_for) in the list
// of skipped items. Returns a list of matches.
var matchedPrereqs = [String]()
if skippedItems.isEmpty {
return matchedPrereqs
@@ -112,14 +111,14 @@ func itemPrereqsInSkippedItems(currentItem: PlistDict, skippedItems: [PlistDict]
return matchedPrereqs
}
/// Returns boolean to indicate if the item needs a restart
func requiresRestart(_ item: PlistDict) -> Bool {
// Returns boolean to indicate if the item needs a restart
let restartAction = item["RestartAction"] as? String ?? ""
return ["RequireRestart", "RecommendRestart"].contains(restartAction)
}
/// Process an Apple package for install. Returns retcode, needs_restart
func handleApplePackageInstall(pkginfo: PlistDict, itemPath: String) async -> (Int, Bool) {
// Process an Apple package for install. Returns retcode, needs_restart
if pkginfo["suppress_bundle_relocation"] as? Bool ?? false {
displayWarning("Item has 'suppress_bundle_relocation' attribute. This feature is no longer supported.")
}
@@ -163,9 +162,9 @@ func handleApplePackageInstall(pkginfo: PlistDict, itemPath: String) async -> (I
return (-99, false)
}
/// Attempt to install a single item from the installList
/// Returns an exitcode for the attempted install and a flag to indicate the need to restart
func installItem(_ item: PlistDict) async -> (Int, Bool) {
// Attempt to install a single item from the installList
// returns an exitcode for the attempted install and a flag to indicate the need to restart
var needToRestart = false
let itemName = item["name"] as? String ?? "<unknown>"
let installerType = item["installer_type"] as? String ?? "pkg_install"
@@ -238,12 +237,10 @@ func installItem(_ item: PlistDict) async -> (Int, Bool) {
return (retcode, needToRestart)
}
/// Uses the installInfo installs list to install items in the correct order and with additional options
func installWithInstallInfo(
installList: [PlistDict], onlyUnattended: Bool = false
) async -> (Bool, [PlistDict]) {
// Uses the installInfo installs list to install items in the
// correct order and with additional options
var restartFlag = false
var itemIndex = 0
var skippedInstalls = [PlistDict]()
@@ -396,10 +393,9 @@ func installWithInstallInfo(
return (restartFlag, skippedInstalls)
}
/// Looks for items in the skipped_items that require or are update_for
/// the current item. Returns a list of matches.
func skippedItemsThatRequire(_ thisItem: PlistDict, skippedItems: [PlistDict]) -> [String] {
// Looks for items in the skipped_items that require or are update_for
// the current item. Returns a list of matches.
var matchedSkippedItems = [String]()
if skippedItems.isEmpty {
return matchedSkippedItems
@@ -422,10 +418,9 @@ func skippedItemsThatRequire(_ thisItem: PlistDict, skippedItems: [PlistDict]) -
return matchedSkippedItems
}
/// Attempts to uninstall a single item from the removalList
/// returns an exitcode for the attempted install and a flag to indicate the need to restart
func uninstallItem(_ item: PlistDict) async -> (Int, Bool) {
// Attempts to uninstall a single item from the removalList
// returns an exitcode for the attempted install and a flag to indicate the need to restart
var needToRestart = false
let itemName = item["display_name"] as? String ?? "<unknown>"
let displayName = item["display_name"] as? String ?? itemName
@@ -512,8 +507,8 @@ func uninstallItem(_ item: PlistDict) async -> (Int, Bool) {
return (retcode, needToRestart)
}
/// Processes removals from the removal list
func processRemovals(_ removalList: [PlistDict], onlyUnattended: Bool = false) async -> (Bool, [PlistDict]) {
// Processes removals from the removal list
var restartFlag = false
var index = 0
var skippedRemovals = [PlistDict]()
@@ -581,11 +576,11 @@ func processRemovals(_ removalList: [PlistDict], onlyUnattended: Bool = false) a
return (restartFlag, skippedRemovals)
}
/// Runs the install/removal session.
///
/// Args:
/// only_unattended: Boolean. If True, only do unattended_(un)install pkgs.
func doInstallsAndRemovals(onlyUnattended: Bool = false) async -> Int {
// Runs the install/removal session.
//
// Args:
// only_unattended: Boolean. If True, only do unattended_(un)install pkgs.
var removalsNeedRestart = false
var installsNeedRestart = false

View File

@@ -20,16 +20,13 @@
import Foundation
/// Stub function
func removeBundleRelocationInfo() {
// this function existed in the Python version of Munki
// but could work only on bundle-style packages
// May not be worth porting.
displayWarning("'suppress_bundle_relocation' is no longer supported. Ignoring.")
}
/// Query a package for its RestartAction. Returns true if a restart is needed, false otherwise
func pkgNeedsRestart(_ pkgpath: String, options: PlistDict) -> Bool {
// Query a package for its RestartAction. Returns true if a restart is
// needed, false otherwise
let tool = "/usr/sbin/installer"
var arguments = ["-query", "RestartAction", "-pkg", pkgpath, "-plist"]
if let choicesXML = options["installer_choices_xml"] as? PlistDict,
@@ -84,10 +81,8 @@ func getInstallerEnvironment(_ customEnv: [String: String]?) -> [String: String]
return env
}
/// Parses a line of output from installer, displays it as progress output and logs it
func displayInstallerOutput(_ text: String) {
// Parses a line of output from installer, displays it as progress output
// and logs it
if !text.hasPrefix("installer:") {
// this should not have been sent to this function!
return
@@ -119,10 +114,8 @@ func displayInstallerOutput(_ text: String) {
}
}
/// Subclass of AsyncProcessRunner that handles the progress output from /usr/sbin/installer
class installerRunner: AsyncProcessRunner {
// subclass of AsyncProcessRunner that handles the progress output from
// /usr/sbin/installer
var remainingOutput = ""
var lastProcessedOutputLine = ""
@@ -161,10 +154,8 @@ class installerRunner: AsyncProcessRunner {
}
}
/// Runs /usr/sbin/installer, parses and displays the output, and returns the process exit code
func runInstaller(arguments: [String], environment: [String: String], pkgName: String) async -> Int {
// Runs /usr/sbin/installer, parses and displays the output, and returns
// the process exit code
let proc = installerRunner(arguments: arguments, environment: environment)
await proc.run()
let results = proc.results
@@ -182,11 +173,10 @@ func runInstaller(arguments: [String], environment: [String: String], pkgName: S
return results.exitcode
}
// Uses the Apple installer to install the package or metapackage at pkgpath.
// Returns a tuple:
// the installer return code and restart needed as a boolean.
func install(_ pkgpath: String, options: PlistDict = [:]) async -> (Int, Bool) {
// Uses the Apple installer to install the package or metapackage at pkgpath.
// Returns a tuple:
// the installer return code and restart needed as a boolean.
var restartNeeded = false
let packageName = (pkgpath as NSString).lastPathComponent
let displayName = options["display_name"] as? String ?? options["name"] as? String ?? packageName
@@ -237,13 +227,13 @@ func install(_ pkgpath: String, options: PlistDict = [:]) async -> (Int, Bool) {
return (retcode, restartNeeded)
}
// The Python version of Munki would actually install _all_ the pkgs from a given
// directory (which was usually the root of a mounted disk image). This was rarely
// was was actaully wanted. This version just installs the first installable item in
// the directory.
// Returns a tuple containing the exit code of the installer process and a boolean
// indicating if a restart is needed
func installFromDirectory(_ directoryPath: String, options: PlistDict = [:]) async -> (Int, Bool) {
// The Python version of Munki would actually install _all_ the pkgs from a given
// directory (which was usually the root of a mounted disk image. This was rarely
// was was actaully wanted. This version just installs the first installable item in
// the directory.
// Returns a tuple containing the exit code of the installer process and a boolean
// indicating if a restart is needed
if stopRequested() {
return (0, false)
}

View File

@@ -52,15 +52,13 @@ import Foundation
#################################################################
*/
/// Returns path to our package DB
func pkgDBPath() -> String {
// returns path to our package DB
// let dbDir = managedInstallsDir()
let dbDir = "/tmp"
return (dbDir as NSString).appendingPathComponent("b.receiptdb")
return managedInstallsDir(subpath: "b.receiptdb")
}
/// Checks to see if our internal package (receipt) DB should be rebuilt.
func shouldRebuildReceiptDB() -> Bool {
// Checks to see if our internal package (receipt) DB should be rebuilt.
let dbPath = pkgDBPath()
let filemanager = FileManager.default
if !filemanager.fileExists(atPath: dbPath) {
@@ -84,8 +82,8 @@ func shouldRebuildReceiptDB() -> Bool {
return true
}
/// Creates the tables needed for our internal package database.
func createReceiptDBTables(_ conn: SQL3Connection) throws {
// Creates the tables needed for our internal package database.
try conn.execute("""
CREATE TABLE paths
(path_key INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -121,10 +119,10 @@ struct PkgData {
var files: [String] = []
}
/// Inserts a pkg row into the db, returns rowid
/// (which should be an alias for the integer primary key "pkg_key")
func insertPkgDataIntoPkgDB(pkgdata: PkgData
) throws {
// inserts a pkg row into the db, returns rowid (which should be an alias for
// the integer primary key "pkg_key"
let connection = try SQL3Connection(pkgDBPath())
try connection.execute("PRAGMA journal_mode = WAL;")
try connection.execute("PRAGMA synchronous = normal;")
@@ -148,10 +146,10 @@ func insertPkgDataIntoPkgDB(pkgdata: PkgData
try connection.close()
}
/// Inserts info into paths and pkg_paths tables
func insertFileInfoIntoPkgDB(
connection: SQL3Connection, pkgKey: Int64, pkgdata: PkgData
) throws {
// inserts info into paths and pkg_paths tables
let pathsQuery = try SQL3Statement(
connection: connection,
SQLString: "SELECT path_key FROM paths WHERE path = ?"
@@ -202,6 +200,7 @@ func insertFileInfoIntoPkgDB(
}
}
/// Gets info about pkg from pkgutil
func getPkgMetaData(_ pkg: String) async throws -> PkgData {
let result = await runCliAsync(
"/usr/sbin/pkgutil", arguments: ["--pkg-info-plist", pkg]
@@ -241,8 +240,8 @@ func getPkgMetaData(_ pkg: String) async throws -> PkgData {
)
}
/// Returns a list of files installed by pkg
func getFilesForPkg(_ pkg: String) async throws -> [String] {
// Returns a list of files installed by pkg
let result = await runCliAsync("/usr/sbin/pkgutil", arguments: ["--files", pkg])
if result.exitcode != 0 {
throw MunkiError("Error calling pkgutil: \(result.error)")
@@ -250,6 +249,7 @@ func getFilesForPkg(_ pkg: String) async throws -> [String] {
return result.output.components(separatedBy: "\n").filter { !$0.isEmpty }
}
/// Adds metadata for pkgid to our database
func getPkgDataAndAddtoDB(pkgid: String) async throws {
async let tempPkgdata = try getPkgMetaData(pkgid)
async let fileList = try getFilesForPkg(pkgid)
@@ -258,8 +258,8 @@ func getPkgDataAndAddtoDB(pkgid: String) async throws {
try insertPkgDataIntoPkgDB(pkgdata: pkgdata)
}
/// Imports package data from pkgutil into our internal package database.
func importFromPkgutil() async throws {
// Imports package data from pkgutil into our internal package database.
let result = await runCliAsync("/usr/sbin/pkgutil", arguments: ["--pkgs"])
if result.exitcode != 0 {
throw MunkiError("Error calling pkgutil: \(result.error)")
@@ -279,9 +279,8 @@ func importFromPkgutil() async throws {
}
}
/// Builds or rebuilds our internal package database.
func initReceiptDB(forcerebuild: Bool = false) async throws {
// Builds or rebuilds our internal package database.
if !shouldRebuildReceiptDB(), !forcerebuild {
// we'll use existing db
return
@@ -307,15 +306,15 @@ func initReceiptDB(forcerebuild: Bool = false) async throws {
try await importFromPkgutil()
}
/// Prepares a list of values for use in a SQL query
func quoteAndJoin(_ stringList: [String]) -> String {
// prepares a list of values for use in a SQL query
let quotedStrings = stringList.map { "\"\($0)\"" }
return "(" + quotedStrings.joined(separator: ",") + ")"
}
/// Given a list of package ids, returns
/// a list of pkg_keys from the pkgs table in our database.
func getPkgKeysFromPkgDB(pkgids: [String]) throws -> [String] {
// Given a list of package ids, returns
// a list of pkg_keys from the pkgs table in our database.
var keys = [String]()
let sqlString = "SELECT pkg_key FROM pkgs WHERE pkgid IN " + quoteAndJoin(pkgids)
let connection = try SQL3Connection(pkgDBPath())
@@ -326,8 +325,8 @@ func getPkgKeysFromPkgDB(pkgids: [String]) throws -> [String] {
return keys
}
/// Queries our database for paths to remove.
func getPathsToRemove(pkgKeys: [String]) throws -> [String] {
// Queries our database for paths to remove.
var pathsToRemove = [String]()
let keyList = quoteAndJoin(pkgKeys)
let selectedPkgs = "SELECT DISTINCT path_key FROM pkgs_paths WHERE pkg_key IN " + keyList
@@ -364,6 +363,7 @@ func deletePkgKeyFromDB(connection: SQL3Connection, pkgKey: Int) throws {
}
}
/// Removes info about pkgid from Apple's pkgutil database
func forgetPkgFromAppleDB(_ pkgid: String) {
let result = runCLI("/usr/sbin/pkgutil", arguments: ["--forget", pkgid])
if result.exitcode == 0 {
@@ -375,9 +375,9 @@ func forgetPkgFromAppleDB(_ pkgid: String) {
}
}
/// Removes receipt data from our internal package database,
/// and optionally Apple's package database.
func removePkgReceipts(pkgKeys: [String], updateApplePkgDB: Bool = true) throws {
// Removes receipt data from our internal package database,
// and optionally Apple's package database.
let taskCount = pkgKeys.count
displayMinorStatus("Removing receipt info")
@@ -418,8 +418,8 @@ func removePkgReceipts(pkgKeys: [String], updateApplePkgDB: Bool = true) throws
displayPercentDone(current: taskCount, maximum: taskCount)
}
/// Returns true if path is a bundle-style directory.
func pathIsBundle(_ path: String) -> Bool {
// Returns true if path is a bundle-style directory.
let bundleExtensions = [".action",
".app",
".bundle",
@@ -452,8 +452,8 @@ func pathIsBundle(_ path: String) -> Bool {
bundleExtensions.contains((path as NSString).pathExtension)
}
/// Check the path to see if it's inside a bundle.
func pathIsInsideBundle(_ path: String) -> Bool {
// Check the path to see if it's inside a bundle.
var currentPath = path
while currentPath.count > 1 {
if pathIsBundle(currentPath) {
@@ -466,9 +466,8 @@ func pathIsInsideBundle(_ path: String) -> Bool {
return false
}
/// Attempts to remove all the paths in the pathsToRemove list
func removeFilesystemItems(pathsToRemove: [String], forceDeleteBundles: Bool) {
// Attempts to remove all the paths in the pathsToRemove list
var removalErrors = [String]()
let itemCount = pathsToRemove.count
displayMajorStatus("Removing \(itemCount) filesystem items")
@@ -545,6 +544,8 @@ func removeFilesystemItems(pathsToRemove: [String], forceDeleteBundles: Bool) {
}
}
/// Our main function, called to remove items based on receipt info.
/// if listFiles is true, this is a dry run
func removePackages(
_ pkgids: [String],
forceDeleteBundles: Bool = false,
@@ -553,8 +554,6 @@ func removePackages(
noRemoveReceipts: Bool = false,
noUpdateApplePkgDB: Bool = false
) async -> Int {
// Our main function, called to remove items based on receipt info.
// if listFiles is true, this is a dry run
if pkgids.isEmpty {
displayError("You must specify at least one package to remove!")
return -2