Skip to content

Commit

Permalink
Merge pull request #32 from macadmins/development
Browse files Browse the repository at this point in the history
Outset 4.0.4
  • Loading branch information
bartreardon authored Jul 26, 2023
2 parents 0e5167d + 721d871 commit 008229a
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 51 deletions.
8 changes: 4 additions & 4 deletions Outset.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.2;
MARKETING_VERSION = 4.0.4;
PRODUCT_BUNDLE_IDENTIFIER = io.macadmins.Outset;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -505,7 +505,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.2;
MARKETING_VERSION = 4.0.4;
PRODUCT_BUNDLE_IDENTIFIER = io.macadmins.Outset;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -543,7 +543,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.2;
MARKETING_VERSION = 4.0.4;
PRODUCT_BUNDLE_IDENTIFIER = io.macadmins.Outset;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -585,7 +585,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.2;
MARKETING_VERSION = 4.0.4;
PRODUCT_BUNDLE_IDENTIFIER = io.macadmins.Outset;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
34 changes: 27 additions & 7 deletions Outset/Functions/FileUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// Created by Bart Reardon on 3/12/2022.
//
// swiftlint:disable large_tuple line_length function_body_length
// swiftlint:disable large_tuple line_length force_cast file_length cyclomatic_complexity function_body_length

import Foundation
import CommonCrypto
Expand Down Expand Up @@ -112,9 +112,10 @@ func ensureWorkingFolders() {
}

func migrateLegacyPreferences() {
let newoldRootUserDefaults = "/var/root/Library/Preferences/io.macadmins.Outset.plist"
// shared folder should not contain any executable content, iterate and update as required
if checkFileExists(path: shareDirectory) {
writeLog("\(shareDirectory) exists. Migrating prefrences to user defaults", logLevel: .debug)
if checkFileExists(path: shareDirectory) || checkFileExists(path: newoldRootUserDefaults) {
writeLog("Legacy preferences exist. Migrating to user defaults", logLevel: .debug)

let legacyOutsetPreferencesFile = "\(shareDirectory)com.chilcote.outset.plist"
let legacyRootRunOncePlistFile = "com.github.outset.once.\(getConsoleUserInfo().userID).plist"
Expand All @@ -126,6 +127,7 @@ func migrateLegacyPreferences() {
shareFiles.append(legacyOutsetPreferencesFile)
shareFiles.append(legacyRootRunOncePlistFile)
shareFiles.append(legacyUserRunOncePlistFile)
shareFiles.append(newoldRootUserDefaults)

for filename in shareFiles where checkFileExists(path: filename) {

Expand All @@ -134,7 +136,22 @@ func migrateLegacyPreferences() {
let data = try Data(contentsOf: url)
switch filename {

case newoldRootUserDefaults:
if isRoot() {
writeLog("\(newoldRootUserDefaults) migration", logLevel: .debug)
let legacyDefaultKeys = CFPreferencesCopyKeyList(Bundle.main.bundleIdentifier! as CFString, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)
for key in legacyDefaultKeys as! [CFString] {
let keyValue = CFPreferencesCopyValue(key, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)
CFPreferencesSetValue(key as CFString,
keyValue as CFPropertyList,
Bundle.main.bundleIdentifier! as CFString,
kCFPreferencesAnyUser,
kCFPreferencesAnyHost)
}
deletePath(newoldRootUserDefaults)
}
case legacyOutsetPreferencesFile:
writeLog("\(legacyOutsetPreferencesFile) migration", logLevel: .debug)
do {
let legacyPreferences = try PropertyListDecoder().decode(OutsetPreferences.self, from: data)
writePreferences(prefs: legacyPreferences)
Expand All @@ -145,6 +162,7 @@ func migrateLegacyPreferences() {
}

case legacyRootRunOncePlistFile, legacyUserRunOncePlistFile:
writeLog("\(legacyRootRunOncePlistFile) and \(legacyUserRunOncePlistFile) migration", logLevel: .debug)
do {
let legacyRunOncePlistData = try PropertyListDecoder().decode([String: Date].self, from: data)
writeRunOnce(runOnceData: legacyRunOncePlistData)
Expand All @@ -167,7 +185,7 @@ func migrateLegacyPreferences() {

}

if folderContents(path: shareDirectory).isEmpty {
if checkFileExists(path: shareDirectory) && folderContents(path: shareDirectory).isEmpty {
deletePath(shareDirectory)
}
}
Expand Down Expand Up @@ -333,14 +351,14 @@ func sha256(for url: URL) -> String? {
}
}

func shaAllFiles() {
// compute sha256sum for all files in the outset directory
func checksumAllFiles() {
// compute checksum (SHA256) for all files in the outset directory
// returns data in two formats to stdout:
// plaintext
// as plist format ready for import into an MDM or converting to a .mobileconfig

let url = URL(fileURLWithPath: outsetDirectory)
writeLog("SHASUM", logLevel: .info)
writeLog("CHECKSUM", logLevel: .info)
var shasumPlist = FileHashes()
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
for case let fileURL as URL in enumerator {
Expand Down Expand Up @@ -392,3 +410,5 @@ extension URL {
(try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
}
}

// swiftlint:enable large_tuple line_length force_cast file_length cyclomatic_complexity function_body_length
17 changes: 13 additions & 4 deletions Outset/Functions/Processing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove
var scripts: [String] = [] // array of scripts once they have passed checks
var runOnceDict: [String: Date] = [:]

let shasumFileList = shasumLoadApprovedFileHashList()
let shasumsAvailable = !shasumFileList.isEmpty
let checksumList = checksumLoadApprovedFiles()
let checksumsAvailable = !checksumList.isEmpty

// See if there's any old stuff to migrate
// Perform this each processing run to pick up individual user preferences as well
migrateLegacyPreferences()

// Get a list of all the files to process
Expand All @@ -50,7 +51,7 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove

// loop through the packages list and process installs.
for package in packages {
if shasumsAvailable && !verifySHASUMForFile(filename: package, shasumArray: shasumFileList) {
if checksumsAvailable && !verifySHASUMForFile(filename: package, shasumArray: checksumList) {
continue
}

Expand Down Expand Up @@ -80,14 +81,16 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove

// loop through the scripts list and process.
for script in scripts {
if shasumsAvailable && !verifySHASUMForFile(filename: script, shasumArray: shasumFileList) {
if checksumsAvailable && !verifySHASUMForFile(filename: script, shasumArray: checksumList) {
continue
}

if once {
writeLog("Processing run-once \(script)", logLevel: .debug)
// If this is supposed to be a runonce item then we want to check to see if has an existing runonce entry
// looks for a key with the full script path. Writes the full path and run date when done
if !runOnceDict.contains(where: {$0.key == script}) {
writeLog("run-once not yet processed. proceeding", logLevel: .debug)
let (output, error, status) = runShellCommand(script, args: [consoleUser], verbose: true)
if status != 0 {
writeLog(error, logLevel: .error)
Expand All @@ -97,6 +100,7 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove
}
} else {
// there's a run-once plist entry for this script. Check to see if there's an override
writeLog("checking for override", logLevel: .debug)
if override.contains(where: {$0.key == script}) {
writeLog("override for \(script) dated \(override[script]!)", logLevel: .debug)
if override[script]! > runOnceDict[script]! {
Expand All @@ -111,9 +115,12 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove
}
}
}
} else {
writeLog("no override for \(script)", logLevel: .debug)
}
}
} else {
writeLog("Processing script \(script)", logLevel: .debug)
let (_, error, status) = runShellCommand(script, args: [consoleUser], verbose: true)
if status != 0 {
writeLog(error, logLevel: .error)
Expand All @@ -129,3 +136,5 @@ func processItems(_ path: String, deleteItems: Bool=false, once: Bool=false, ove
}

}

// swiftlint:enable function_body_length cyclomatic_complexity
2 changes: 2 additions & 0 deletions Outset/Functions/Services.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,5 @@ class ServiceManager {
status(loginWindowAgent)
}
}

// swiftlint:enable line_length
73 changes: 60 additions & 13 deletions Outset/Functions/SystemUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import Foundation
import SystemConfiguration
import OSLog
import IOKit
import CoreFoundation

struct OutsetPreferences: Codable {
var waitForNetwork: Bool = false
Expand Down Expand Up @@ -136,7 +138,17 @@ func writePreferences(prefs: OutsetPreferences) {
// Use the name of each property as the key, and save its value to UserDefaults
if let propertyName = child.label {
let key = propertyName.camelCaseToUnderscored()
defaults.set(child.value, forKey: key)
if isRoot() {
// write the preference to /Library/Preferences/
CFPreferencesSetValue(key as CFString,
child.value as CFPropertyList,
Bundle.main.bundleIdentifier! as CFString,
kCFPreferencesAnyUser,
kCFPreferencesAnyHost)
} else {
// write the preference to ~/Library/Preferences/
defaults.set(child.value, forKey: key)
}
}
}
}
Expand All @@ -150,11 +162,19 @@ func loadPreferences() -> OutsetPreferences {
let defaults = UserDefaults.standard
var outsetPrefs = OutsetPreferences()

outsetPrefs.networkTimeout = defaults.integer(forKey: "network_timeout")
outsetPrefs.ignoredUsers = defaults.array(forKey: "ignored_users") as? [String] ?? []
outsetPrefs.overrideLoginOnce = defaults.object(forKey: "override_login_once") as? [String: Date] ?? [:]
outsetPrefs.waitForNetwork = defaults.bool(forKey: "wait_for_network")

if isRoot() {
// force preferences to be read from /Library/Preferences instead of root's preferences
outsetPrefs.networkTimeout = CFPreferencesCopyValue("network_timeout" as CFString, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost) as? Int ?? 180
outsetPrefs.ignoredUsers = CFPreferencesCopyValue("ignored_users" as CFString, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost) as? [String] ?? []
outsetPrefs.overrideLoginOnce = CFPreferencesCopyValue("override_login_once" as CFString, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost) as? [String: Date] ?? [:]
outsetPrefs.waitForNetwork = (CFPreferencesCopyValue("wait_for_network" as CFString, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost) != nil)
} else {
// load preferences for the current user, which includes /Library/Preferences
outsetPrefs.networkTimeout = defaults.integer(forKey: "network_timeout")
outsetPrefs.ignoredUsers = defaults.array(forKey: "ignored_users") as? [String] ?? []
outsetPrefs.overrideLoginOnce = defaults.object(forKey: "override_login_once") as? [String: Date] ?? [:]
outsetPrefs.waitForNetwork = defaults.bool(forKey: "wait_for_network")
}
return outsetPrefs
}

Expand All @@ -169,8 +189,10 @@ func loadRunOnce() -> [String: Date] {

if isRoot() {
runOnceKey += "-"+getConsoleUserInfo().username
return CFPreferencesCopyValue(runOnceKey as CFString, Bundle.main.bundleIdentifier! as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost) as? [String: Date] ?? [:]
} else {
return defaults.object(forKey: runOnceKey) as? [String: Date] ?? [:]
}
return defaults.object(forKey: runOnceKey) as? [String: Date] ?? [:]
}

func writeRunOnce(runOnceData: [String: Date]) {
Expand All @@ -184,17 +206,28 @@ func writeRunOnce(runOnceData: [String: Date]) {

if isRoot() {
runOnceKey += "-"+getConsoleUserInfo().username
CFPreferencesSetValue(runOnceKey as CFString,
runOnceData as CFPropertyList,
Bundle.main.bundleIdentifier! as CFString,
kCFPreferencesAnyUser,
kCFPreferencesAnyHost)
} else {
defaults.set(runOnceData, forKey: runOnceKey)
}
defaults.set(runOnceData, forKey: runOnceKey)
}

func showPrefrencePath(_ action: String) {
let path = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
let prefsPath = path[0].appending("/Preferences").appending("/\(Bundle.main.bundleIdentifier!).plist")
var prefsPath: String
if isRoot() {
prefsPath = "/Library/Preferences".appending("/\(Bundle.main.bundleIdentifier!).plist")
} else {
let path = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
prefsPath = path[0].appending("/Preferences").appending("/\(Bundle.main.bundleIdentifier!).plist")
}
writeLog("\(action)ing preference file: \(prefsPath)", logLevel: .debug)
}

func shasumLoadApprovedFileHashList() -> [String: String] {
func checksumLoadApprovedFiles() -> [String: String] {
// imports the list of file hashes that are approved to run
var outsetFileHashList = FileHashes()

Expand Down Expand Up @@ -255,8 +288,8 @@ func waitForNetworkUp(timeout: Double) -> Bool {
}

func loginWindowUpdateState(_ action: Action) {
var cmd : String
var loginWindowPlist : String = "/System/Library/LaunchDaemons/com.apple.loginwindow.plist"
var cmd: String
let loginWindowPlist: String = "/System/Library/LaunchDaemons/com.apple.loginwindow.plist"
switch action {
case .enable:
writeLog("Enabling loginwindow process", logLevel: .debug)
Expand All @@ -277,6 +310,17 @@ func getDeviceHardwareModel() -> String {
return String(cString: model)
}

func getMarketingModel() -> String {
let appleSiliconProduct = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/AppleARMPE/product")
let cfKeyValue = IORegistryEntryCreateCFProperty(appleSiliconProduct, "product-description" as CFString, kCFAllocatorDefault, 0)
IOObjectRelease(appleSiliconProduct)
let keyValue: AnyObject? = cfKeyValue?.takeUnretainedValue()
if keyValue != nil, let data = keyValue as? Data {
return String(data: data, encoding: String.Encoding.utf8)?.trimmingCharacters(in: CharacterSet(["\0"])) ?? ""
}
return ""
}

func getDeviceSerialNumber() -> String {
// Returns the current devices serial number
let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice") )
Expand Down Expand Up @@ -311,7 +355,10 @@ func writeSysReport() {
// Logs system information to log file
writeLog("User: \(getConsoleUserInfo())", logLevel: .debug)
writeLog("Model: \(getDeviceHardwareModel())", logLevel: .debug)
writeLog("Marketing Model: \(getMarketingModel())", logLevel: .debug)
writeLog("Serial: \(getDeviceSerialNumber())", logLevel: .debug)
writeLog("OS: \(getOSVersion())", logLevel: .debug)
writeLog("Build: \(getOSBuildVersion())", logLevel: .debug)
}

// swiftlint:enable line_length
Loading

0 comments on commit 008229a

Please sign in to comment.