mirror of
https://github.com/munki/munki.git
synced 2026-05-02 18:30:03 -05:00
Merge branch 'master' of https://github.com/munki/munki into new_design
# Conflicts: # code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj # code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift # code/apps/Managed Software Center/Managed Software Center/Info.plist # code/apps/Managed Software Center/Managed Software Center/de.lproj/Localizable.strings
This commit is contained in:
@@ -9,7 +9,7 @@ Munki is an open source project from [Walt Disney Animation Studios](https://www
|
||||
|
||||
Munki is a set of tools that, used together with a webserver-based repository of packages and package metadata, can be used by macOS administrators to manage software installs (and in many cases removals) on macOS client machines.
|
||||
|
||||
Munki can install software packaged in the Apple package format, and also supports Adobe CS3/CS4/CS5/CS6 Enterprise Deployment "packages", and drag-and-drop disk images as installer sources.
|
||||
Munki can install software packaged in the Apple package format, and also supports Adobe CS3/CS4/CS5/CS6/CC deployment packages, and drag-and-drop disk images as installer sources.
|
||||
|
||||
Additionally, Munki can be configured to install Apple Software Updates, either from Apple's server, or yours.
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
C0546BDC20FE961E003FE5A6 /* MSCPasswordAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0546BDB20FE961E003FE5A6 /* MSCPasswordAlertController.swift */; };
|
||||
C05F87932105587A000DB8D8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C05F87952105587A000DB8D8 /* Localizable.strings */; };
|
||||
C0B2E4D921460E7D00FA9806 /* MSCWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B2E4D821460E7D00FA9806 /* MSCWebView.swift */; };
|
||||
C0BCAD7D2442A0B3001D2FDD /* appleupdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0BCAD7C2442A0B3001D2FDD /* appleupdates.swift */; };
|
||||
C0C5DF6C20F5C27700CA0687 /* MSCStatusController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C5DF6B20F5C27700CA0687 /* MSCStatusController.swift */; };
|
||||
C0D30BEB20CA445A005E876E /* MunkiItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D30BEA20CA445A005E876E /* MunkiItems.swift */; };
|
||||
C0E2599E210AD8CE00C3A3D9 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E2599D210AD8CD00C3A3D9 /* Socket.swift */; };
|
||||
@@ -100,7 +101,7 @@
|
||||
C0546BDB20FE961E003FE5A6 /* MSCPasswordAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCPasswordAlertController.swift; sourceTree = "<group>"; };
|
||||
C05F878D210556C9000DB8D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C05F878E21055746000DB8D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Base; path = Base.lproj/Info.plist; sourceTree = "<group>"; };
|
||||
C05F879621055BFE000DB8D8 /* Base */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C05F879621055BFE000DB8D8 /* Base */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C05F879721055DAA000DB8D8 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C05F87A221055E7F000DB8D8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C05F87A321055E8F000DB8D8 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@@ -112,8 +113,10 @@
|
||||
C05F87A921055F04000DB8D8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C05F87AA21055F10000DB8D8 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C0B2E4D821460E7D00FA9806 /* MSCWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSCWebView.swift; sourceTree = "<group>"; };
|
||||
C0BCAD7C2442A0B3001D2FDD /* appleupdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = appleupdates.swift; sourceTree = "<group>"; };
|
||||
C0C5DF6B20F5C27700CA0687 /* MSCStatusController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSCStatusController.swift; sourceTree = "<group>"; };
|
||||
C0D30BEA20CA445A005E876E /* MunkiItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MunkiItems.swift; sourceTree = "<group>"; };
|
||||
C0E10E58245494C4003E04F2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C0E2599D210AD8CD00C3A3D9 /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = "<group>"; };
|
||||
C0F8E3DF2105622F00718259 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C0F8E3E221057A1500718259 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
@@ -198,6 +201,7 @@
|
||||
56C33E762173DC8E00D727DC /* MSCTableRowView.swift */,
|
||||
C04F829C20BB5B9E00F9C57D /* MSCToolbarButton.swift */,
|
||||
C04F829E20BB713B00F9C57D /* munki.swift */,
|
||||
C0BCAD7C2442A0B3001D2FDD /* appleupdates.swift */,
|
||||
C04F82A020BB77CF00F9C57D /* FoundationPlist.swift */,
|
||||
C03107C220C8B65E007FE337 /* msclib.swift */,
|
||||
C03107C420C8F220007FE337 /* msclog.swift */,
|
||||
@@ -284,7 +288,7 @@
|
||||
attributes = {
|
||||
CLASSPREFIX = MSC;
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1020;
|
||||
LastUpgradeCheck = 1140;
|
||||
ORGANIZATIONNAME = "The Munki Project";
|
||||
TargetAttributes = {
|
||||
C0196316210507DB0009F51A = {
|
||||
@@ -383,6 +387,7 @@
|
||||
C0E2599E210AD8CE00C3A3D9 /* Socket.swift in Sources */,
|
||||
C01603D120D0A7FA00DEF9E4 /* SelfService.swift in Sources */,
|
||||
C04F82A120BB77CF00F9C57D /* FoundationPlist.swift in Sources */,
|
||||
C0BCAD7D2442A0B3001D2FDD /* appleupdates.swift in Sources */,
|
||||
C04CA61E20E6ADA100711461 /* MainWindowController.swift in Sources */,
|
||||
C01603CF20CF8B6100DEF9E4 /* iconutils.swift in Sources */,
|
||||
56C33E812174D7A200D727DC /* MSCTableCellView.swift in Sources */,
|
||||
|
||||
@@ -15,10 +15,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
@IBOutlet weak var statusController: MSCStatusController!
|
||||
@IBOutlet weak var passwordAlertController: MSCPasswordAlertController!
|
||||
|
||||
var launchedViaURL = false
|
||||
var backdropOnlyMode = false
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
// NSApplication delegate method called at launch
|
||||
NSLog("%@", "Finished launching")
|
||||
NSLog("Additional arguments: %@", CommandLine.arguments)
|
||||
|
||||
if let info_dict = Bundle.main.infoDictionary {
|
||||
if let vers = info_dict["CFBundleShortVersionString"] as? String {
|
||||
//print(vers)
|
||||
@@ -35,6 +39,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
// a NSUserNotificationCenterDelegate
|
||||
NSLog("%@", "Launched via Notification interaction")
|
||||
userNotificationCenter(NSUserNotificationCenter.default, didActivate: ourNotification)
|
||||
launchedViaURL = true
|
||||
}
|
||||
}
|
||||
// Prevent automatic relaunching at login on Lion+
|
||||
@@ -49,22 +54,29 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
|
||||
// user may have launched the app manually, or it may have
|
||||
// been launched by /usr/local/munki/managedsoftwareupdate
|
||||
// to display available updates
|
||||
var lastcheck = pref("LastCheckDate") as? Date ?? Date.distantPast
|
||||
if thereAreUpdatesToBeForcedSoon(hours: 2) {
|
||||
// skip the check and just display the updates
|
||||
// by pretending the lastcheck is now
|
||||
lastcheck = Date()
|
||||
}
|
||||
let max_cache_age = pref("CheckResultsCacheSeconds") as? Int ?? 0
|
||||
if lastcheck.timeIntervalSinceNow * -1 > TimeInterval(max_cache_age) {
|
||||
// check for updates if the last check is over the
|
||||
// configured manualcheck cache age max.
|
||||
mainWindowController.checkForUpdates()
|
||||
} else if updateCheckNeeded() {
|
||||
//check for updates if we have optional items selected for install
|
||||
// or removal that have not yet been processed
|
||||
mainWindowController.checkForUpdates()
|
||||
// to display available updates, or via a munki: URL
|
||||
if !launchedViaURL {
|
||||
var lastcheck = pref("LastCheckDate") as? Date ?? Date.distantPast
|
||||
if thereAreUpdatesToBeForcedSoon(hours: 2) {
|
||||
// skip the check and just display the updates
|
||||
// by pretending the lastcheck is now
|
||||
lastcheck = Date()
|
||||
}
|
||||
if shouldAggressivelyNotifyAboutAppleUpdates() || shouldAggressivelyNotifyAboutMunkiUpdates() {
|
||||
// skip the check and just display the updates
|
||||
// by pretending the lastcheck is now
|
||||
lastcheck = Date()
|
||||
}
|
||||
let max_cache_age = pref("CheckResultsCacheSeconds") as? Int ?? 0
|
||||
if lastcheck.timeIntervalSinceNow * -1 > TimeInterval(max_cache_age) {
|
||||
// check for updates if the last check is over the
|
||||
// configured manualcheck cache age max.
|
||||
mainWindowController.checkForUpdates()
|
||||
} else if updateCheckNeeded() {
|
||||
//check for updates if we have optional items selected for install
|
||||
// or removal that have not yet been processed
|
||||
mainWindowController.checkForUpdates()
|
||||
}
|
||||
}
|
||||
// load the initial view only if we are not already loading something else.
|
||||
// enables launching the app to a specific panel, eg. from URL handler
|
||||
@@ -87,6 +99,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
let urlDescriptor = event.paramDescriptor(forKeyword: keyword)
|
||||
if let urlString = urlDescriptor?.stringValue {
|
||||
msc_log("MSC", "Called by external URL: \(urlString)")
|
||||
launchedViaURL = true
|
||||
if let url = URL(string: urlString) {
|
||||
mainWindowController.handleMunkiURL(url)
|
||||
} else {
|
||||
@@ -95,6 +108,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applicationDidResignActive(_ notification: Notification) {
|
||||
if self.mainWindowController.forceFrontmost == true {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ notification: Notification) {
|
||||
if self.backdropOnlyMode == true {
|
||||
// (re)launch Software Update prefs pane
|
||||
openSoftwareUpdatePrefsPane()
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
|
||||
BIN
Binary file not shown.
+201
@@ -13,6 +13,8 @@ class MSCAlertController: NSObject {
|
||||
// than to move a giant bunch of ugly code out of the WindowController
|
||||
|
||||
var window: NSWindow? // our parent window
|
||||
var timers: [Timer] = []
|
||||
var quitButton: NSButton?
|
||||
|
||||
func handlePossibleAuthRestart() {
|
||||
// Ask for and store a password for auth restart if needed/possible
|
||||
@@ -115,6 +117,119 @@ class MSCAlertController: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func alertToAppleUpdates() {
|
||||
// Notify user of pending Apple updates
|
||||
guard let mainWindow = window else {
|
||||
msc_debug_log("Could not get main window in alertToAppleUpdates")
|
||||
return
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString(
|
||||
"Important Apple Updates", comment: "Apple Software Updates Pending title")
|
||||
alert.addButton(withTitle: NSLocalizedString("Install now", comment: "Install now button title"))
|
||||
alert.addButton(withTitle: NSLocalizedString(
|
||||
"Skip these updates", comment: "Skip Apple updates button title"))
|
||||
let su_icon_file = "/System/Library/PreferencePanes/SoftwareUpdate.prefPane/Contents/Resources/SoftwareUpdate.icns"
|
||||
if let suIcon = NSImage.init(contentsOfFile: su_icon_file) {
|
||||
alert.icon = suIcon
|
||||
}
|
||||
if !userIsAdmin() && userMustBeAdminToInstallAppleUpdates() {
|
||||
// tell user they cannot install the updates
|
||||
msc_log("user", "apple_updates_user_cannot_install")
|
||||
alert.informativeText = NSLocalizedString(
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance.",
|
||||
comment: "Apple Software Updates Unable detail")
|
||||
// disable insstall now button
|
||||
alert.buttons[0].isEnabled = false
|
||||
} else {
|
||||
// prompt user to install using System Preferences
|
||||
msc_log("user", "apple_updates_pending")
|
||||
alert.informativeText = NSLocalizedString(
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences.",
|
||||
comment: "Apple Software Updates Pending detail")
|
||||
if shouldAggressivelyNotifyAboutAppleUpdates() {
|
||||
// disable the skip button
|
||||
alert.buttons[1].isEnabled = false
|
||||
}
|
||||
}
|
||||
alert.beginSheetModal(for: mainWindow, completionHandler: { (modalResponse) -> Void in
|
||||
self.appleUpdateAlertEnded(for: alert, withResponse: modalResponse)
|
||||
})
|
||||
}
|
||||
|
||||
func appleUpdateAlertEnded(for alert: NSAlert, withResponse modalResponse: NSApplication.ModalResponse) {
|
||||
// Called when Apple update alert ends
|
||||
if modalResponse == .alertFirstButtonReturn {
|
||||
msc_log("user", "agreed_apple_updates")
|
||||
// make sure this alert panel is gone before we proceed
|
||||
alert.window.orderOut(self)
|
||||
let appDelegate = NSApp.delegate! as! AppDelegate
|
||||
appDelegate.mainWindowController.forceFrontmost = false
|
||||
appDelegate.backdropOnlyMode = true
|
||||
if let mainWindow = window {
|
||||
mainWindow.level = .normal
|
||||
}
|
||||
let timer1 = Timer.scheduledTimer(timeInterval: 0.1,
|
||||
target: self,
|
||||
selector: #selector(self.openSoftwareUpdate),
|
||||
userInfo: nil,
|
||||
repeats: false)
|
||||
timers.append(timer1)
|
||||
let timer2 = Timer.scheduledTimer(timeInterval: 1.5,
|
||||
target: self,
|
||||
selector: #selector(self.closeMainWindow),
|
||||
userInfo: nil,
|
||||
repeats: false)
|
||||
timers.append(timer2)
|
||||
let timer3 = Timer.scheduledTimer(timeInterval: 9.5,
|
||||
target: self,
|
||||
selector: #selector(self.fadeOutBackdropWindows),
|
||||
userInfo: nil,
|
||||
repeats: false)
|
||||
timers.append(timer3)
|
||||
// wait 10 seconds, then quit
|
||||
let timer4 = Timer.scheduledTimer(timeInterval: 10.0,
|
||||
target: NSApp,
|
||||
selector: #selector(NSApp.terminate),
|
||||
userInfo: self,
|
||||
repeats: false)
|
||||
timers.append(timer4)
|
||||
} else {
|
||||
// cancelled
|
||||
msc_log("user", "deferred_apple_updates")
|
||||
alert.window.orderOut(self)
|
||||
clearMunkiItemsCache()
|
||||
if let mainWindowController = (NSApp.delegate! as! AppDelegate).mainWindowController {
|
||||
mainWindowController.load_page("updates.html")
|
||||
mainWindowController.displayUpdateCount()
|
||||
if shouldAggressivelyNotifyAboutMunkiUpdates() {
|
||||
mainWindowController._alertedUserToOutstandingUpdates = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openSoftwareUpdate() {
|
||||
// object method to call openSoftwareUpdatePrefsPane function
|
||||
openSoftwareUpdatePrefsPane()
|
||||
}
|
||||
|
||||
@objc func closeMainWindow() {
|
||||
// closes the main window, duh
|
||||
if let mainWindow = window {
|
||||
mainWindow.orderOut(self)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func fadeOutBackdropWindows() {
|
||||
// fades out the windows that block access to other apps
|
||||
let backdropWindows = (NSApp.delegate! as! AppDelegate).mainWindowController.backdropWindows
|
||||
for window in backdropWindows {
|
||||
window.animator().alphaValue = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func alertToExtraUpdates() {
|
||||
// Notify user of additional pending updates
|
||||
msc_log("user", "extra_updates_pending")
|
||||
@@ -416,4 +531,90 @@ class MSCAlertController: NSObject {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func alertToPendingUpdates(_ mwc: MainWindowController) {
|
||||
// Alert user to pending updates before quitting the application
|
||||
mwc._alertedUserToOutstandingUpdates = true
|
||||
// show the updates
|
||||
mwc.loadUpdatesPage(self)
|
||||
var alertTitle = ""
|
||||
var alertDetail = ""
|
||||
if thereAreUpdatesToBeForcedSoon() {
|
||||
alertTitle = NSLocalizedString("Mandatory Updates Pending",
|
||||
comment: "Mandatory Updates Pending text")
|
||||
if let deadline = earliestForceInstallDate() {
|
||||
let time_til_logout = deadline.timeIntervalSinceNow
|
||||
if time_til_logout > 0 {
|
||||
let deadline_str = stringFromDate(deadline)
|
||||
let formatString = NSLocalizedString(
|
||||
("One or more updates must be installed by %@. A logout " +
|
||||
"may be forced if you wait too long to update."),
|
||||
comment: "Mandatory Updates Pending detail")
|
||||
alertDetail = String(format: formatString, deadline_str)
|
||||
} else {
|
||||
alertDetail = NSLocalizedString(
|
||||
("One or more mandatory updates are overdue for " +
|
||||
"installation. A logout will be forced soon."),
|
||||
comment: "Mandatory Updates Imminent detail")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alertTitle = NSLocalizedString(
|
||||
"Pending updates", comment: "Pending Updates alert title")
|
||||
alertDetail = NSLocalizedString(
|
||||
"There are pending updates for this computer.",
|
||||
comment: "Pending Updates alert detail text")
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = alertTitle
|
||||
alert.informativeText = alertDetail
|
||||
var quitButton = NSApplication.ModalResponse.alertFirstButtonReturn
|
||||
var updateButton = NSApplication.ModalResponse.alertSecondButtonReturn
|
||||
if !shouldAggressivelyNotifyAboutMunkiUpdates() && !thereAreUpdatesToBeForcedSoon() {
|
||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: "Quit button title"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Update now", comment: "Update Now button title"))
|
||||
} else {
|
||||
// add the buttons in the opposite order so "Update now" is the default/primary
|
||||
alert.addButton(withTitle: NSLocalizedString("Update now", comment: "Update Now button title"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: "Quit button title"))
|
||||
// initially disable the Quit button
|
||||
self.quitButton = alert.buttons[1]
|
||||
alert.buttons[1].isEnabled = false
|
||||
let timer1 = Timer.scheduledTimer(timeInterval: 5.0,
|
||||
target: self,
|
||||
selector: #selector(self.activateQuitButton),
|
||||
userInfo: nil,
|
||||
repeats: false)
|
||||
timers.append(timer1)
|
||||
updateButton = NSApplication.ModalResponse.alertFirstButtonReturn
|
||||
quitButton = NSApplication.ModalResponse.alertSecondButtonReturn
|
||||
}
|
||||
alert.beginSheetModal(for: self.window!, completionHandler: { (modalResponse) -> Void in
|
||||
if modalResponse == quitButton {
|
||||
msc_log("user", "quit")
|
||||
NSApp.terminate(self)
|
||||
} else if modalResponse == updateButton {
|
||||
msc_log("user", "install_now_clicked")
|
||||
// make sure this alert panel is gone before we proceed
|
||||
// which might involve opening another alert sheet
|
||||
alert.window.orderOut(self)
|
||||
// invalidate any timers
|
||||
for timer in self.timers {
|
||||
timer.invalidate()
|
||||
}
|
||||
// initiate the updates
|
||||
mwc.updateNow()
|
||||
mwc.loadUpdatesPage(self)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@objc func activateQuitButton() {
|
||||
if let button = self.quitButton {
|
||||
button.isEnabled = true
|
||||
} else {
|
||||
NSLog("%@", "could not get the alert button reference")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -174,6 +174,7 @@ class MSCStatusController: NSObject {
|
||||
// Initialize the main window for update status
|
||||
statusWindowController._update_in_progress = true
|
||||
if statusWindowController.currentPageIsUpdatesPage() {
|
||||
statusWindowController.makeUsUnobnoxious()
|
||||
statusWindowController.load_page("updates.html")
|
||||
statusWindowController.displayUpdateCount()
|
||||
}
|
||||
|
||||
+193
-69
@@ -29,6 +29,10 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
var _status_title = ""
|
||||
var stop_requested = false
|
||||
var user_warned_about_extra_updates = false
|
||||
var should_filter_apple_updates = false
|
||||
var forceFrontmost = false
|
||||
|
||||
var backdropWindows: [NSWindow] = []
|
||||
|
||||
// Cocoa UI binding properties
|
||||
|
||||
@@ -74,18 +78,34 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
// no pending updates
|
||||
return .terminateNow
|
||||
}
|
||||
if (currentPageIsUpdatesPage() && !thereAreUpdatesToBeForcedSoon()) {
|
||||
// We're already at the updates view, so user is aware of the
|
||||
// pending updates, so OK to just terminate
|
||||
// (unless there are some updates to be forced soon)
|
||||
return .terminateNow
|
||||
if !should_filter_apple_updates && appleUpdatesRequireRestartOnMojaveAndUp() {
|
||||
if shouldAggressivelyNotifyAboutAppleUpdates(days: 2) {
|
||||
if !currentPageIsUpdatesPage() {
|
||||
loadUpdatesPage(self)
|
||||
}
|
||||
alert_controller.alertToAppleUpdates()
|
||||
should_filter_apple_updates = true
|
||||
return .terminateCancel
|
||||
}
|
||||
}
|
||||
if (currentPageIsUpdatesPage() && _alertedUserToOutstandingUpdates) {
|
||||
return .terminateNow
|
||||
if currentPageIsUpdatesPage() {
|
||||
if (!thereAreUpdatesToBeForcedSoon() && !shouldAggressivelyNotifyAboutMunkiUpdates()) {
|
||||
// We're already at the updates view, so user is aware of the
|
||||
// pending updates, so OK to just terminate
|
||||
// (unless there are some updates to be forced soon)
|
||||
return .terminateNow
|
||||
}
|
||||
if _alertedUserToOutstandingUpdates {
|
||||
if (thereAreUpdatesToBeForcedSoon() || shouldAggressivelyNotifyAboutMunkiUpdates()) {
|
||||
// user keeps avoiding; let's try at next logout or restart
|
||||
writeInstallAtStartupFlagFile(skipAppleUpdates: false)
|
||||
}
|
||||
return .terminateNow
|
||||
}
|
||||
}
|
||||
// we have pending updates and we have not yet warned the user
|
||||
// about them
|
||||
alertToPendingUpdates()
|
||||
alert_controller.alertToPendingUpdates(self)
|
||||
return .terminateCancel
|
||||
}
|
||||
|
||||
@@ -93,59 +113,105 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
// return true if current tab selected is Updates
|
||||
return sidebar.selectedRow == 3
|
||||
}
|
||||
|
||||
func newTranslucentWindow(screen: NSScreen) -> NSWindow {
|
||||
// makes a translucent masking window we use to prevent interaction with
|
||||
// other apps
|
||||
var windowRect = screen.frame
|
||||
windowRect.origin = NSMakePoint(0.0, 0.0)
|
||||
let thisWindow = NSWindow(
|
||||
contentRect: windowRect,
|
||||
styleMask: .borderless,
|
||||
backing: .buffered,
|
||||
defer: false,
|
||||
screen: screen
|
||||
)
|
||||
thisWindow.level = .normal
|
||||
thisWindow.backgroundColor = NSColor.black.withAlphaComponent(0.50)
|
||||
thisWindow.isOpaque = false
|
||||
thisWindow.ignoresMouseEvents = false
|
||||
thisWindow.alphaValue = 0.0
|
||||
thisWindow.orderFrontRegardless()
|
||||
thisWindow.animator().alphaValue = 1.0
|
||||
return thisWindow
|
||||
}
|
||||
|
||||
func alertToPendingUpdates() {
|
||||
// Alert user to pending updates before quitting the application
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
// show the updates
|
||||
loadUpdatesPage(self)
|
||||
var alertTitle = ""
|
||||
var alertDetail = ""
|
||||
if thereAreUpdatesToBeForcedSoon() {
|
||||
alertTitle = NSLocalizedString("Mandatory Updates Pending",
|
||||
comment: "Mandatory Updates Pending text")
|
||||
if let deadline = earliestForceInstallDate() {
|
||||
let time_til_logout = deadline.timeIntervalSinceNow
|
||||
if time_til_logout > 0 {
|
||||
let deadline_str = stringFromDate(deadline)
|
||||
let formatString = NSLocalizedString(
|
||||
("One or more updates must be installed by %@. A logout " +
|
||||
"may be forced if you wait too long to update."),
|
||||
comment: "Mandatory Updates Pending detail")
|
||||
alertDetail = String(format: formatString, deadline_str)
|
||||
} else {
|
||||
alertDetail = NSLocalizedString(
|
||||
("One or more mandatory updates are overdue for " +
|
||||
"installation. A logout will be forced soon."),
|
||||
comment: "Mandatory Updates Imminent detail")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alertTitle = NSLocalizedString(
|
||||
"Pending updates", comment: "Pending Updates alert title")
|
||||
alertDetail = NSLocalizedString(
|
||||
"There are pending updates for this computer.",
|
||||
comment: "Pending Updates alert detail text")
|
||||
func displayBackdropWindows() {
|
||||
for screen in NSScreen.screens {
|
||||
let newWindow = newTranslucentWindow(screen: screen)
|
||||
// add to our backdropWindows array so a reference stays around
|
||||
backdropWindows.append(newWindow)
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = alertTitle
|
||||
alert.informativeText = alertDetail
|
||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: "Quit button title"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Update now", comment: "Update Now button title"))
|
||||
alert.beginSheetModal(for: self.window!, completionHandler: { (modalResponse) -> Void in
|
||||
if modalResponse == .alertFirstButtonReturn {
|
||||
msc_log("user", "quit")
|
||||
NSApp.terminate(self)
|
||||
} else if modalResponse == .alertSecondButtonReturn {
|
||||
msc_log("user", "install_now_clicked")
|
||||
// make sure this alert panel is gone before we proceed
|
||||
// which might involve opening another alert sheet
|
||||
alert.window.orderOut(self)
|
||||
// initiate the updates
|
||||
self.updateNow()
|
||||
self.loadUpdatesPage(self)
|
||||
}
|
||||
|
||||
func makeUsUnobnoxious() {
|
||||
// reverse all the obnoxious changes
|
||||
msc_log("msc", "end_obnoxious_mode")
|
||||
|
||||
let options = NSApplication.PresentationOptions([])
|
||||
NSApp.presentationOptions = options
|
||||
|
||||
for window in self.backdropWindows {
|
||||
window.orderOut(self)
|
||||
}
|
||||
if let window = self.window {
|
||||
window.collectionBehavior = .fullScreenPrimary
|
||||
window.styleMask = [.titled, .closable, .miniaturizable, .resizable]
|
||||
window.level = .normal
|
||||
}
|
||||
self.forceFrontmost = false
|
||||
// enable/disable controls as needed
|
||||
enableOrDisableSoftwareViewControls()
|
||||
}
|
||||
|
||||
func makeUsObnoxious() {
|
||||
// makes this app and window impossible(?)/difficult to ignore
|
||||
msc_log("msc", "start_obnoxious_mode")
|
||||
|
||||
// make it very difficult to switch away from this app
|
||||
let options = NSApplication.PresentationOptions([.hideDock, .disableHideApplication, .disableProcessSwitching, .disableForceQuit])
|
||||
NSApp.presentationOptions = options
|
||||
|
||||
// alter some window properties to make the window harder to ignore
|
||||
if let window = self.window {
|
||||
window.center()
|
||||
window.collectionBehavior = .fullScreenNone
|
||||
window.styleMask = [.titled, .closable]
|
||||
window.level = .floating
|
||||
}
|
||||
|
||||
// disable all of the other controls
|
||||
softwareToolbarItem.isEnabled = false
|
||||
categoriesToolbarItem.isEnabled = false
|
||||
myItemsToolbarItem.isEnabled = false
|
||||
searchField.isEnabled = false
|
||||
findMenuItem.isEnabled = false
|
||||
softwareMenuItem.isEnabled = false
|
||||
categoriesMenuItem.isEnabled = false
|
||||
myItemsMenuItem.isEnabled = false
|
||||
|
||||
// set flag to cause us to always be brought to front
|
||||
self.forceFrontmost = true
|
||||
|
||||
// create translucent windows to mask all other apps
|
||||
displayBackdropWindows()
|
||||
}
|
||||
|
||||
func weShouldBeObnoxious() -> Bool {
|
||||
// returns a Bool to let us know if we should enter obnoxiousMode
|
||||
if thereAreUpdatesToBeForcedSoon() {
|
||||
return true
|
||||
}
|
||||
if shouldAggressivelyNotifyAboutMunkiUpdates() {
|
||||
return true
|
||||
}
|
||||
if shouldAggressivelyNotifyAboutAppleUpdates() {
|
||||
if userIsAdmin() || !userMustBeAdminToInstallAppleUpdates() {
|
||||
// only be obnoxious if the user can actually _do_ something
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadInitialView() {
|
||||
@@ -203,6 +269,11 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
// The managedsoftwareupdate run will have changed state preferences
|
||||
// in ManagedInstalls.plist. Load the new values.
|
||||
reloadPrefs()
|
||||
if tasktype == "" {
|
||||
// probably a background session, but not one initiated by the user here
|
||||
resetAndReload()
|
||||
return
|
||||
}
|
||||
let lastCheckResult = pref("LastCheckResult") as? Int ?? 0
|
||||
if sessionResult != 0 || lastCheckResult < 0 {
|
||||
var alertMessageText = NSLocalizedString(
|
||||
@@ -251,7 +322,13 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
detailText = "\(detailText)\n\n\(errorMessage)"
|
||||
}
|
||||
// show the alert sheet
|
||||
self.window!.makeKeyAndOrderFront(self)
|
||||
if let thisWindow = self.window {
|
||||
thisWindow.makeKeyAndOrderFront(self)
|
||||
if let attachedSheet = thisWindow.attachedSheet {
|
||||
// there's an existing sheet open; close it first
|
||||
thisWindow.endSheet(attachedSheet)
|
||||
}
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = alertMessageText
|
||||
alert.informativeText = detailText
|
||||
@@ -312,7 +389,9 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
case "updates":
|
||||
// updates page; just rebuild and reload it
|
||||
load_page("updates.html")
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
if !shouldAggressivelyNotifyAboutMunkiUpdates() {
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
}
|
||||
default:
|
||||
// should never get here
|
||||
msc_debug_log("Unexpected value for page name: \(filename)")
|
||||
@@ -490,6 +569,7 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
return
|
||||
}
|
||||
_update_in_progress = true
|
||||
should_filter_apple_updates = false
|
||||
displayUpdateCount()
|
||||
managedsoftwareupdate_task = "manualcheck"
|
||||
if let status_controller = (NSApp.delegate as? AppDelegate)?.statusController {
|
||||
@@ -653,7 +733,11 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
} else {
|
||||
msc_debug_log("updateCheck not needed")
|
||||
_alertedUserToOutstandingUpdates = false
|
||||
_status_title = NSLocalizedString(
|
||||
"Update in progress.",
|
||||
comment: "Update In Progress primary text") + ".."
|
||||
kickOffInstallSession()
|
||||
makeUsUnobnoxious()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,7 +746,7 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
if _update_in_progress {
|
||||
return 0
|
||||
}
|
||||
return getEffectiveUpdateList().count
|
||||
return getEffectiveUpdateList(should_filter_apple_updates).count
|
||||
}
|
||||
|
||||
func displayUpdateCount() {
|
||||
@@ -748,6 +832,10 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
if url_fragment == "updates.html" {
|
||||
// clear all earlier update notifications
|
||||
NSUserNotificationCenter.default.removeAllDeliveredNotifications()
|
||||
// record that the user has been presented pending updates
|
||||
if !_update_in_progress && !shouldAggressivelyNotifyAboutMunkiUpdates() && !thereAreUpdatesToBeForcedSoon() {
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,8 +854,31 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
if !(filename.hasSuffix(".html")) {
|
||||
filename += ".html"
|
||||
}
|
||||
// if the user has minimized the main window, deminiaturize it
|
||||
if let window = self.window {
|
||||
if window.isMiniaturized {
|
||||
window.deminiaturize(self)
|
||||
}
|
||||
}
|
||||
// try to build and load the page
|
||||
load_page(filename)
|
||||
if filename == "notify.html" {
|
||||
//resetAndReload()
|
||||
load_page("updates.html")
|
||||
if !_update_in_progress && getUpdateCount() > 0 {
|
||||
// we're notifying about pending updates. We might need to be obnoxious about it
|
||||
if let window = self.window {
|
||||
// don't let people move the window mostly off-screen so
|
||||
// they can ignore it
|
||||
window.center()
|
||||
}
|
||||
if weShouldBeObnoxious() {
|
||||
NSLog("%@", "Entering obnoxious mode")
|
||||
makeUsObnoxious()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
load_page(filename)
|
||||
}
|
||||
}
|
||||
|
||||
func setNoPageCache() {
|
||||
@@ -784,10 +895,17 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
}
|
||||
|
||||
func clearCache() {
|
||||
if #available(OSX 10.11, *) {
|
||||
let cacheDataTypes = Set([WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
|
||||
var osMinorVers = 9
|
||||
if #available(OSX 10.10, *) {
|
||||
osMinorVers = ProcessInfo().operatingSystemVersion.minorVersion
|
||||
}
|
||||
if osMinorVers >= 11 {
|
||||
if #available(OSX 10.11, *) {
|
||||
let cacheDataTypes = Set([WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
|
||||
|
||||
let dateFrom = Date.init(timeIntervalSince1970: 0)
|
||||
WKWebsiteDataStore.default().removeData(ofTypes: cacheDataTypes, modifiedSince: dateFrom, completionHandler: {})
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
URLCache.shared.removeAllCachedResponses()
|
||||
@@ -966,7 +1084,14 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
// we're on the Updates page, so users can see all the pending/
|
||||
// outstanding updates
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
updateNow()
|
||||
if !should_filter_apple_updates && appleUpdatesRequireRestartOnMojaveAndUp() {
|
||||
// if there are pending Apple updates, alert the user to
|
||||
// install via System Preferences
|
||||
alert_controller.alertToAppleUpdates()
|
||||
should_filter_apple_updates = true
|
||||
} else {
|
||||
updateNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1088,9 +1213,9 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
"Please contact your administrator for more details",
|
||||
comment: "Pre Install Uninstall Upgrade Alert Detail")
|
||||
let defaultOKLabel = NSLocalizedString(
|
||||
"OK", comment: "Pre Install Uninstall Upgrade OK Label")
|
||||
"OK", comment: "OK button title")
|
||||
let defaultCancelLabel = NSLocalizedString(
|
||||
"Cancel", comment: "Pre Install Uninstall Upgrade Cancel Label")
|
||||
"Cancel", comment: "Cancel button title/short action text")
|
||||
|
||||
let alertTitle = alert["alert_title"] as? String ?? defaultAlertTitle
|
||||
let alertDetail = alert["alert_detail"] as? String ?? defaultAlertDetail
|
||||
@@ -1227,7 +1352,7 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
// update the updates-to-install header to reflect the new list of
|
||||
// updates to install
|
||||
setInnerText(updateCountMessage(getUpdateCount()), elementID: "update-count-string")
|
||||
setInnerText(getWarningText(), elementID: "update-warning-text")
|
||||
setInnerText(getWarningText(should_filter_apple_updates), elementID: "update-warning-text")
|
||||
|
||||
// update text of Install All button
|
||||
setInnerText(getInstallAllButtonTextForCount(getUpdateCount()), elementID: "install-all-button-text")
|
||||
@@ -1340,7 +1465,6 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
|
||||
@IBAction func loadUpdatesPage(_ sender: Any) {
|
||||
// Called by Navigate menu item'''
|
||||
load_page("updates.html")
|
||||
_alertedUserToOutstandingUpdates = true
|
||||
}
|
||||
|
||||
@IBAction func softwareToolbarItemClicked(_ sender: Any) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<string>5.2.0</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -243,6 +243,12 @@ class GenericItem: BaseItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's an Apple Software Update, use the Software Update icon
|
||||
if let apple_update = my["apple_update"] as? Bool {
|
||||
if apple_update {
|
||||
return "static/SoftwareUpdate.png"
|
||||
}
|
||||
}
|
||||
// use the Generic package icon
|
||||
return "static/Generic.png"
|
||||
}
|
||||
@@ -326,7 +332,7 @@ class GenericItem: BaseItem {
|
||||
comment: "Update Will Be Installed status text"),
|
||||
"update-available":
|
||||
NSLocalizedString("Update available",
|
||||
comment: "Update Available status text"),
|
||||
comment: "Update available text"),
|
||||
"unavailable":
|
||||
NSLocalizedString("Unavailable",
|
||||
comment: "Unavailable status text")
|
||||
@@ -564,7 +570,7 @@ class GenericItem: BaseItem {
|
||||
let note = my["note"] as? String ?? ""
|
||||
if is_update {
|
||||
return NSLocalizedString("Update available",
|
||||
comment: "Update available display text")
|
||||
comment: "Update available text")
|
||||
} else if note.hasPrefix("Insufficient disk space to download and install") {
|
||||
return NSLocalizedString("Not enough disk space",
|
||||
comment: "Not Enough Disk Space display text")
|
||||
@@ -921,7 +927,8 @@ class UpdateItem: GenericItem {
|
||||
comment: "Managed Update type")
|
||||
}
|
||||
my["hide_cancel_button"] = "hidden"
|
||||
my ["dependent_items"] = dependentItems(name)
|
||||
my["dependent_items"] = dependentItems(name)
|
||||
my["days_available"] = getDaysPending(name)
|
||||
}
|
||||
|
||||
override func description() -> String {
|
||||
@@ -966,6 +973,15 @@ class UpdateItem: GenericItem {
|
||||
}
|
||||
start_text += "<span class=\"warning\">\(filtered_html(warning_text))</span><br/><br/>"
|
||||
}
|
||||
} else if let days_available = my["days_available"] as? Int {
|
||||
if days_available > 2 {
|
||||
let format_str = NSLocalizedString(
|
||||
"This update has been pending for %@ days.",
|
||||
comment: "Pending days message")
|
||||
let formatted_str = NSString(format: format_str as NSString,
|
||||
String(days_available) as NSString)
|
||||
start_text += "<span class=\"warning\">\(formatted_str)</span><br><br>"
|
||||
}
|
||||
}
|
||||
if !((my["dependent_items"] as? [String] ?? []).isEmpty) {
|
||||
start_text += dependency_description()
|
||||
@@ -982,6 +998,94 @@ func cachedInstallInfo() -> [String : Any] {
|
||||
return Cache.shared["InstallInfo"] as? [String : Any] ?? [String : Any]()
|
||||
}
|
||||
|
||||
func updateTrackingInfo() -> [String : Any] {
|
||||
if !Cache.shared.keys.contains("UpdateNotificationTrackingInfo") {
|
||||
Cache.shared["UpdateNotificationTrackingInfo"] = getUpdateNotificationTracking()
|
||||
}
|
||||
return Cache.shared["UpdateNotificationTrackingInfo"] as? [String : Any] ?? [String : Any]()
|
||||
}
|
||||
|
||||
func getDateFirstAvailable(_ itemname: String) -> Date? {
|
||||
// Uses UpdateNotificationTracking.plist data to determine when an item was first
|
||||
// "discovered"/presented as available
|
||||
let trackingInfo = updateTrackingInfo()
|
||||
for category in trackingInfo.keys {
|
||||
if let items = (trackingInfo[category] as? [String : Any]) {
|
||||
for name in items.keys {
|
||||
if name == itemname {
|
||||
return (items[name] as? Date)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDaysPending(_ itemname: String) -> Int {
|
||||
// Returns the number of days an item has been pending
|
||||
if let dateAvailable = getDateFirstAvailable(itemname) {
|
||||
let secondsInDay = 60 * 60 * 24
|
||||
let timeAvailable = dateAvailable.timeIntervalSinceNow * -1
|
||||
let daysAvailable = Int(timeAvailable as Double)/secondsInDay
|
||||
return daysAvailable
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func shouldAggressivelyNotifyAboutMunkiUpdates(days: Int = -1) -> Bool {
|
||||
// Do we have any Munki updates that have been pending a long time?
|
||||
var maxPendingDays = 0
|
||||
if let trackingInfo = updateTrackingInfo()["managed_installs"] as? [String: Any?] {
|
||||
for name in trackingInfo.keys {
|
||||
if let dateAvailable = trackingInfo[name] as? Date {
|
||||
let secondsInDay = 60 * 60 * 24
|
||||
let timeAvailable = dateAvailable.timeIntervalSinceNow * -1
|
||||
let daysAvailable = Int(timeAvailable as Double)/secondsInDay
|
||||
if daysAvailable > maxPendingDays {
|
||||
maxPendingDays = daysAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if days == -1 {
|
||||
let aggressiveNotificationDays = pref("AggressiveUpdateNotificationDays") as? Int ?? 14
|
||||
if aggressiveNotificationDays == 0 {
|
||||
// never get aggressive
|
||||
return false
|
||||
}
|
||||
return maxPendingDays > aggressiveNotificationDays
|
||||
} else {
|
||||
return maxPendingDays > days
|
||||
}
|
||||
}
|
||||
|
||||
func shouldAggressivelyNotifyAboutAppleUpdates(days: Int = -1) -> Bool {
|
||||
// Do we have any Apple updates that require a restart that have been pending
|
||||
// a long time?
|
||||
var maxPendingDays = 0
|
||||
let requiresRestartItems = getAppleUpdates().filter(
|
||||
{ ($0["RestartAction"] as? String ?? "").hasSuffix("Restart") }
|
||||
)
|
||||
for item in requiresRestartItems {
|
||||
if let itemname = item["name"] as? String {
|
||||
let thisItemDaysPending = getDaysPending(itemname)
|
||||
if thisItemDaysPending > maxPendingDays {
|
||||
maxPendingDays = thisItemDaysPending
|
||||
}
|
||||
}
|
||||
}
|
||||
if days == -1 {
|
||||
let aggressiveNotificationDays = pref("AggressiveUpdateNotificationDays") as? Int ?? 14
|
||||
if aggressiveNotificationDays == 0 {
|
||||
// never get aggressive
|
||||
return false
|
||||
}
|
||||
return maxPendingDays > aggressiveNotificationDays
|
||||
} else {
|
||||
return maxPendingDays > days
|
||||
}
|
||||
}
|
||||
|
||||
func optionalInstallsExist() -> Bool {
|
||||
let optional_items = cachedInstallInfo()["optional_installs"] as? [[String : Any]] ?? [[String : Any]]()
|
||||
return optional_items.count > 0
|
||||
@@ -994,7 +1098,7 @@ func getOptionalInstallItems() -> [OptionalItem] {
|
||||
}
|
||||
if !Cache.shared.keys.contains("optional_install_items") {
|
||||
let optional_items = cachedInstallInfo()["optional_installs"] as? [[String : Any]] ?? [[String : Any]]()
|
||||
var optional_install_items = optional_items.map({ OptionalItem($0) })
|
||||
let optional_install_items = optional_items.map({ OptionalItem($0) })
|
||||
let featured_items = cachedInstallInfo()["featured_items"] as? [String] ?? [String]()
|
||||
for index in 0..<optional_install_items.count {
|
||||
if let name = optional_install_items[index]["name"] as? String {
|
||||
@@ -1067,9 +1171,9 @@ func display_name(_ item_name: String) -> String {
|
||||
return item_name
|
||||
}
|
||||
|
||||
func getUpdateList() -> [UpdateItem] {
|
||||
func getUpdateList(_ filterAppleUpdates: Bool = false) -> [UpdateItem] {
|
||||
if !Cache.shared.keys.contains("update_list") {
|
||||
Cache.shared["update_list"] = _build_update_list()
|
||||
Cache.shared["update_list"] = _build_update_list(filterAppleUpdates)
|
||||
}
|
||||
return Cache.shared["update_list"] as? [UpdateItem] ?? [UpdateItem]()
|
||||
}
|
||||
@@ -1096,15 +1200,20 @@ func update_list_sort(_ lh: UpdateItem, _ rh: UpdateItem) -> Bool {
|
||||
}
|
||||
}
|
||||
|
||||
func _build_update_list() -> [UpdateItem] {
|
||||
func _build_update_list(_ filterAppleUpdates: Bool = false) -> [UpdateItem] {
|
||||
var update_items = [[String: Any]]()
|
||||
if !munkiUpdatesContainAppleItems() {
|
||||
if let apple_update_items = getAppleUpdates()["AppleUpdates"] as? [[String: Any]] {
|
||||
for var item in apple_update_items {
|
||||
item["developer"] = "Apple"
|
||||
item["status"] = "will-be-installed"
|
||||
update_items.append(item)
|
||||
for var item in getAppleUpdates() {
|
||||
if (filterAppleUpdates &&
|
||||
((item["RestartAction"] as? String ?? "").hasSuffix("Restart"))) {
|
||||
// skip this update because it requires a restart and we've been
|
||||
// directed to filter these out
|
||||
continue
|
||||
}
|
||||
item["developer"] = "Apple"
|
||||
item["status"] = "will-be-installed"
|
||||
item["apple_update"] = true
|
||||
update_items.append(item)
|
||||
}
|
||||
}
|
||||
let install_info = cachedInstallInfo()
|
||||
@@ -1144,6 +1253,21 @@ func updatesRequireRestart() -> Bool {
|
||||
return requiresRestart
|
||||
}
|
||||
|
||||
func appleUpdatesRequireRestartOnMojaveAndUp() -> Bool {
|
||||
// Return true if any item in the apple update list requires a restart
|
||||
var osMinorVers = 9
|
||||
if #available(OSX 10.10, *) {
|
||||
osMinorVers = ProcessInfo().operatingSystemVersion.minorVersion
|
||||
}
|
||||
if osMinorVers >= 14 {
|
||||
let requiresRestart = getAppleUpdates().filter(
|
||||
{ ($0["RestartAction"] as? String ?? "").hasSuffix("Restart") }
|
||||
).count > 0
|
||||
return requiresRestart
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func updatesContainNonUserSelectedItems() -> Bool {
|
||||
// Does the list of updates contain items not selected by the user?
|
||||
if !munkiUpdatesContainAppleItems() && getAppleUpdates().count > 0 {
|
||||
@@ -1168,7 +1292,7 @@ func updatesContainNonUserSelectedItems() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getEffectiveUpdateList() -> [GenericItem] {
|
||||
func getEffectiveUpdateList(_ filterAppleUpdates: Bool = false) -> [GenericItem] {
|
||||
// Combine the updates Munki has found with any optional choices to
|
||||
// make the effective list of updates
|
||||
let optional_installs = getOptionalWillBeInstalledItems() as [GenericItem]
|
||||
@@ -1178,7 +1302,7 @@ func getEffectiveUpdateList() -> [GenericItem] {
|
||||
)
|
||||
// filter out pending optional items from the list of all pending updates
|
||||
// so we can add in the items with additional optional detail
|
||||
let mandatory_updates = getUpdateList().filter(
|
||||
let mandatory_updates = getUpdateList(filterAppleUpdates).filter(
|
||||
{ !optional_item_names.contains($0["name"] as? String ?? "") }
|
||||
) as [GenericItem]
|
||||
return mandatory_updates + optional_installs + optional_removals
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
@@ -45,13 +45,8 @@ class UNIXDomainSocketClient {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
// make a CFData reference to the sockaddr struct
|
||||
let sockaddr_un_ptr = UnsafeMutableRawPointer(
|
||||
&socketAdr).bindMemory(to: UInt8.self,
|
||||
capacity: MemoryLayout<sockaddr_un>.size)
|
||||
return CFDataCreate(kCFAllocatorDefault,
|
||||
sockaddr_un_ptr,
|
||||
MemoryLayout<sockaddr_un>.size)
|
||||
return NSData(bytes: &socketAdr,
|
||||
length: MemoryLayout.size(ofValue: socketAdr)) as CFData
|
||||
}
|
||||
|
||||
func connect(to path: String) {
|
||||
@@ -108,7 +103,7 @@ class UNIXDomainSocketClient {
|
||||
}
|
||||
|
||||
private func fdSet(_ fd: Int32, set: inout fd_set) {
|
||||
/// Replacement for FD_SET macro
|
||||
// Replacement for FD_SET macro
|
||||
let intOffset = Int(fd / 32)
|
||||
let bitOffset = fd % 32
|
||||
let mask = Int32(1 << bitOffset)
|
||||
@@ -182,18 +177,16 @@ class UNIXDomainSocketClient {
|
||||
return ""
|
||||
}
|
||||
// allocate some space for the return message.
|
||||
let buffer = Array<CChar>(repeating: CChar(0), count: maxsize)
|
||||
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxsize)
|
||||
defer { buffer.deallocate() }
|
||||
// read from socket
|
||||
let msg_len = recv(CFSocketGetNative(socket),
|
||||
UnsafeMutableRawPointer(mutating: buffer),
|
||||
maxsize,
|
||||
0)
|
||||
if msg_len > 0 {
|
||||
if let text = String(utf8String: buffer) {
|
||||
return text
|
||||
}
|
||||
let msg_len = recv(CFSocketGetNative(socket), buffer, maxsize, 0)
|
||||
if let msg = NSString(bytes: buffer,
|
||||
length: msg_len,
|
||||
encoding: String.Encoding.utf8.rawValue) as String? {
|
||||
return msg
|
||||
}
|
||||
errCode = .writeError
|
||||
errCode = .readError
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// appleupdates.swift
|
||||
// Managed Software Center
|
||||
//
|
||||
// Created by Greg Neagle on 4/11/20.
|
||||
// Copyright © 2020 The Munki Project. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Foundation
|
||||
import OpenDirectory
|
||||
|
||||
|
||||
let INSTALLATSTARTUPFILE = "/Users/Shared/.com.googlecode.munki.installatstartup"
|
||||
|
||||
func writeInstallAtStartupFlagFile(skipAppleUpdates: Bool = true) {
|
||||
// writes out a file to trigger Munki to install Munki updates at next restart
|
||||
let plist = ["SkipAppleUpdates": skipAppleUpdates]
|
||||
do {
|
||||
try writePlist(plist, toFile: INSTALLATSTARTUPFILE)
|
||||
} catch {
|
||||
// unfortunate, but not fatal.
|
||||
msc_log("MSC", "cant_write_file", msg: "Couldn't write \(INSTALLATSTARTUPFILE) -- \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func killSystemPreferencesApp() {
|
||||
// force quits System Preferences if it's open
|
||||
let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.systempreferences")
|
||||
for app in runningApps {
|
||||
_ = app.forceTerminate()
|
||||
}
|
||||
}
|
||||
|
||||
func openSoftwareUpdatePrefsPane() {
|
||||
// kill it first in case it is open with a dialog/sheet
|
||||
//killSystemPreferencesApp() // nope, it reopens to previous pane
|
||||
writeInstallAtStartupFlagFile()
|
||||
if let softwareUpdatePrefsPane = URL(string: "x-apple.systempreferences:com.apple.preferences.softwareupdate") {
|
||||
NSWorkspace.shared.open(softwareUpdatePrefsPane)
|
||||
}
|
||||
}
|
||||
|
||||
func userMustBeAdminToInstallAppleUpdates() -> Bool {
|
||||
// returns a boolean telling if the user must be an admin to install Apple Updates
|
||||
let suMustBeAdmin = CFPreferencesCopyAppValue(
|
||||
"restrict-software-update-require-admin-to-install" as CFString,
|
||||
"com.apple.SoftwareUpdate" as CFString) as? Bool ?? false
|
||||
let suMustBeAdminIsForced = CFPreferencesAppValueIsForced(
|
||||
"restrict-software-update-require-admin-to-install" as CFString,
|
||||
"com.apple.SoftwareUpdate" as CFString)
|
||||
let appStoreMustBeAdmin = CFPreferencesCopyAppValue(
|
||||
"restrict-store-require-admin-to-install" as CFString,
|
||||
"com.apple.appstore" as CFString ) as? Bool ?? false
|
||||
let appStoreMustBeAdminIsForced = CFPreferencesAppValueIsForced(
|
||||
"restrict-store-require-admin-to-install" as CFString,
|
||||
"com.apple.appstore" as CFString)
|
||||
return (suMustBeAdmin && suMustBeAdminIsForced) || (appStoreMustBeAdmin && appStoreMustBeAdminIsForced)
|
||||
}
|
||||
|
||||
func findODgroupRecords(groupname: String, nodename: String = "/Search") throws -> [ODRecord] {
|
||||
// Uses OpenDirectory methods to find user records for username
|
||||
let searchNode = try ODNode(session: ODSession.default(), name: nodename)
|
||||
let query = try ODQuery(node: searchNode,
|
||||
forRecordTypes: kODRecordTypeGroups,
|
||||
attribute: kODAttributeTypeRecordName,
|
||||
matchType: ODMatchType(kODMatchEqualTo),
|
||||
queryValues: groupname,
|
||||
returnAttributes: kODAttributeTypeAllAttributes,
|
||||
maximumResults: 0)
|
||||
return (try query.resultsAllowingPartial(false) as! [ODRecord])
|
||||
}
|
||||
|
||||
func findODgroupRecord(groupname: String, nodename: String = "/Search") -> ODRecord? {
|
||||
// Returns first record found for groupname, or nil if not found
|
||||
do {
|
||||
let records = try findODgroupRecords(groupname: groupname)
|
||||
if records.isEmpty {
|
||||
return nil
|
||||
}
|
||||
return records[0]
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func userIsAdmin() -> Bool {
|
||||
let username = NSUserName()
|
||||
if let userRecord = findODuserRecord(username: username) {
|
||||
if let adminGroupRecord = findODgroupRecord(groupname: "admin", nodename: "/Local/Default") {
|
||||
do {
|
||||
try adminGroupRecord.isMemberRecord(userRecord)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func su_pref(_ prefName: String) -> Any? {
|
||||
// Return a com.apple.SoftwareUpdate preference.
|
||||
return CFPreferencesCopyValue(prefName as CFString,
|
||||
"com.apple.SoftwareUpdate" as CFString,
|
||||
kCFPreferencesAnyUser,
|
||||
kCFPreferencesCurrentHost)
|
||||
}
|
||||
|
||||
func suRecommendedUpdateIDs() -> [String] {
|
||||
// returns a list of productids for the SoftwareUpdate recommended ids
|
||||
var ids = [String]()
|
||||
if let recommendedUpdates = su_pref("RecommendedUpdates") as? [[String: Any]] {
|
||||
for update in recommendedUpdates {
|
||||
if let productKey = update["Product Key"] as? String {
|
||||
ids.append(productKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Programmer i brug af andre";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Vær opmærksom";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Annuller";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Hjælp er ikke tilgængelig for Managed Software Center.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Vigtige opdateringer fra Apple";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Information";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installer";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installering krævet";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installer nu";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installering ønsket";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installering krævet";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Installeringen mislykkedes";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Log ud krævet";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Kræver macOS-opdatering";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke søge efter opdateringer.\nPrøv igen senere. Hvis problemet består, skal du kontakte systemadministratoren.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Ingen ventende opdateringer";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Findes ikke";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Ikke tilgængeligt i øjeblikket";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Ikke nok plads på disken";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Findes ikke";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Ikke installeret";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Én eller flere vigtige opdateringer fra Apple skal installeres";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Et eller flere emner skal installeres senest %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Forbereder fjernelse";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Forbereder start af macOS-installation...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Problematiske opdateringer";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Str.:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Spring over";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Software";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Den software, der blev installeret eller fjernet, kræver en genstart. Du får mulighed for at gemme alle åbne dokumenter.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Denne software kræver opmærksomhed. Kontakt administratoren for mere information";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Starter Adobe installer...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Starter macOS-opgradering...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Starter...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problem med systemkonfigurationen";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Systemet vil genstarte og begynde installering af macOS.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Installation af software er gennemført.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Der er ingen emner i kategorien.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Der er én eller flere Apple-softwareopdateringer, der kræver en genstart.\n\nDu skal installere opdateringerne fra Softwareopdatering i Systemindstillinger.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Der er én eller flere Apple-softwareopdateringer, der kræver en genstart.\n\nAdministratoren har begrænset installationen af disse opdateringer. Kontakt din administrator for mere information.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Der er andre brugere logget ind på computeren.\nHvis du opdaterer nu, risikerer du, at de mister deres åbne dokumenter.\n\nPrøv igen senere når de andre brugere er logget ud.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Dette emne skal installeres senest %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Denne opdatering har afventet i %@ dage.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "For at tillade dette, skal du skrive din log ind-adgangskode.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Prøv at vælge en anden udvikler.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Type:";
|
||||
"Type" = "Type";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Ikke tilgængelig";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Opdater alle";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Opdatering kræves";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Kan opdateres";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Opdater nu";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Opdatering kræves";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Opdatering bliver installeret";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Softwaren er opdateret.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Kræver macOS-opdatering";
|
||||
|
||||
Executable → Regular
BIN
Binary file not shown.
+10
-1
@@ -1,5 +1,14 @@
|
||||
/* Failed Preflight Check detail */
|
||||
/* System configuration problem alert detail */
|
||||
"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "A systems configuration issue is preventing Managed Software Centre from operating correctly. The reported issue is: ";
|
||||
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Help isn't available for Managed Software Centre.";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Cannot Contact Server detail */
|
||||
"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Password prompt title */
|
||||
"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Centre wants to unlock the startup disk after restarting to complete all pending updates.";
|
||||
|
||||
+10
-1
@@ -1,5 +1,14 @@
|
||||
/* Failed Preflight Check detail */
|
||||
/* System configuration problem alert detail */
|
||||
"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "A systems configuration issue is preventing Managed Software Centre from operating correctly. The reported issue is: ";
|
||||
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Help isn't available for Managed Software Centre.";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Cannot Contact Server detail */
|
||||
"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Password prompt title */
|
||||
"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Centre wants to unlock the startup disk after restarting to complete all pending updates.";
|
||||
|
||||
+10
-1
@@ -1,5 +1,14 @@
|
||||
/* Failed Preflight Check detail */
|
||||
/* System configuration problem alert detail */
|
||||
"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "A systems configuration issue is preventing Managed Software Centre from operating correctly. The reported issue is: ";
|
||||
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Help isn't available for Managed Software Centre.";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Cannot Contact Server detail */
|
||||
"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator.";
|
||||
|
||||
/* Password prompt title */
|
||||
"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Centre wants to unlock the startup disk after restarting to complete all pending updates.";
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Binary file not shown.
@@ -1,4 +1,3 @@
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "1Dc-SU-RKH"; */
|
||||
"1Dc-SU-RKH.title" = "Navigate";
|
||||
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Applicaciones en uso por otros";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Atención";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Cancelar";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "No hay ayuda disponible para el Centro de aplicaciones.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Actualizaciones importantes de Apple";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Información";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Instalar";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Instalación requerida";
|
||||
/* Install now button title */
|
||||
"Install now" = "Install ahora";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Instalación solicitada";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Instalación requerida";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Error en la sesión de instalación";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Es necesario cerrar sesión";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Actualización de macOS requerida";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Centro de aplicaciones no puede comprobar las actualizaciones.\nPrueba más tarde. Si esta situación continua, contacta con el administrador de sistemas.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "No hay actualizaciones pendientes";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "No encontrado";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Actualmente no disponible";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "No hay suficiente espacion en el disco";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "No encontrado";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "No instalado";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "Aceptar";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Una o más actualizaciones importates de Apple deben ser instaladas";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Uno o mas ítems tienen que ser instalados para el %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Preparando desinstalación";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Preparando para ejecutar el instalador de macOS";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Actualización problematica";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Tamaño:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Saltar actualizaciones de Apple";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Aplicaciones";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "El software instalado necesita reiniciar el equipo. Tendrás oportunidad de guardar tus documentos";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Este software esta sujeto a algunas condiciones. Por favor contacta con tu administrador de sistemas para obtener más información";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Empezando el instalador de Adobe...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Empezando actualización completa de macOS";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Empezando...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problema en la configuración del sistema";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "El sistema se reiniciará y comenzará la intalación completa de macOS";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "El software se instaló correctamente.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "No hay ítems en esta categoria.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Hay una o mas actualizaciones de software de Apple que requieren un reinicio.\n\nDebes instalar estas actualizaciones usando Actualización de software en Preferencias del Sistema";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Hay una o mas actualizaciones de software de Apple que requieren un reinicio.\n\nTu administrador de sistemas ha restringido la instalación de estas actualizaciones. Contacta con tu administrador para recibir asistencia.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Hay otros usuarios con sesiones abiertas en este equipo.\nActualizar ahora puede causar perdidas en su trabajo.\n\nPor favor prueba más tarde cuando los otros usuarios hayan cerrado sus sesiones.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Tiene que estar instalado para el %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Esta actualización ha estado pendiente durante %@ days.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Para permitir esto, introduzca su contraseña de usuario.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Intentar seleccionar otro desarollador.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Tipo:";
|
||||
"Type" = "Tipo";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "No disponible";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Actualizar todo";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Requiere una actualización";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Actualización disponible";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Actualizar ahora";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Requiere una actualización";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Se instalará la actualización";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Tus aplicaciones están actualizadas.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Actualización de macOS requerida";
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Muiden käyttäjien käytössä olevat ohjelmat";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Huomio";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Kumoa";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Managed Software Center -ohjelmalle ei ole ohjetta.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Tärkeitä Applen päivityksiä";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Tietoja";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Asenna";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Pakollinen asennus";
|
||||
/* Install now button title */
|
||||
"Install now" = "Asenna nyt";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Asennusta pyydetty";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Pakollinen asennus";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Asennus epäonnistui";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Vaatii uloskirjautumisen";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS-päivitys vaaditaan";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center ei voi tarkistaa päivityksiä juuri nyt. Yritä myöhemmin uudelleen. Jos ongelma jatkuu, ota yhteyttä järjestelmän ylläpitäjään.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Ei päivityksiä";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Ei tuloksia";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Ei saatavilla tällä hetkellä";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Kovalevyllä ei ole tarpeeksi tilaa";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Ei tuloksia";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Ei asennettu";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Yksi tai useampi tärkeä Applen päivitys täytyy asentaa";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Yksi tai useampi kohde täytyy asentaa ennen %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Valmistellaan poistoa";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Valmistellaan macOS-asentajan käynnistämistä...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Epäonnistuneet päivitykset";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Koko:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Ohita nämä päivitykset";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Ohjelmistot";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Asennettu ohjelmisto tai poisto vaatii tietokoneen uudelleenkäynnistyksen. Avoinna olevien dokumenttien tallentaminen on mahdollista.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Tähän päivitykseen liittyy ehtoja. Ota yhteys ylläpitäjään";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Käynnistetään Adobe-asentajaa...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Aloitetaan macOS-päivitys...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Aloitetaan...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Järjestelmän määritysvirhe";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Järjestelmä käynnistyy uudelleen ja aloittaa macOS-päivityksen.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Ohjelmiston asennus onnistui.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Ei kohteita tässä kategoriassa.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Yksi tai useampi saatavilla oleva Applen päivitys vaatii uudelleenkäynnistyksen.\n\nPäivitykset täytyy asentaa avaamalla Järjestelmäasetukset ja valitsemalla Ohjelmiston päivitys.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Yksi tai useampi saatavilla oleva Applen päivitys vaatii uudelleenkäynnistyksen.\n\nYlläpitäjä on rajoittanut näiden päivitysten asentamista. Ota yhteyttä ylläpitäjään.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Tietokoneelle on kirjautuneena muita käyttäjiä.\nMuut käyttäjät saattavat menettää tallentamattomia muutoksia jos päivitys tehdään nyt.\n\nYritä uudelleen kun muut käyttäjät ovat kirjautuneet ulos.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Asennettava ennen %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Päivitys on ollut saatavilla %@ päivää.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Salli lukituksen avaaminen syöttämällä salasana.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Valitse toinen kehittäjä.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Tyyppi:";
|
||||
"Type" = "Tyyppi";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Ei saatavilla";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Päivitä kaikki";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Pakollinen päivitys";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Päivitys saatavilla";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Päivitä nyt";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Pakollinen päivitys";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Päivitys asennetaan";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Ohjelmisto on ajan tasalla.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS-päivitys vaaditaan";
|
||||
|
||||
+49
-14
@@ -47,12 +47,14 @@
|
||||
"An older version is currently installed. There is not enough disk space to download and install this update." = "Une ancienne version est actuellement installée. Il n'y a pas assez d'espace disque pour télécharger et installer cette mise à jour.";
|
||||
|
||||
/* Long update requires a higher OS version text */
|
||||
"An older version is currently installed. You must upgrade to macOS version %@ or higher to be able to install this update." = "Une ancienne version est actuellement installée. Vous devez mettre à jour vers une version macOS
|
||||
%@ ou plus pour pouvoir installer cette mise à jour.";
|
||||
"An older version is currently installed. You must upgrade to macOS version %@ or higher to be able to install this update." = "Une version antérieure est actuellement installée. Vous devez mettre à jour macOS à la version %@ ou plus récente pour pouvoir installer cette mise à jour.";
|
||||
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Application utilisée par d'autres";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Attention";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Annuler";
|
||||
|
||||
@@ -152,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "L'aide n'est pas disponible pour le Centre de gestion des logiciels.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Mises à jour Apple importantes";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Information";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installer";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installation obligatoire";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installer maintenant";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installation demandée";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installation obligatoire";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Échec de l'installation";
|
||||
|
||||
@@ -188,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Fermeture de session obligatoire";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Mise à jour MacOS requise";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Le Centre de gestion des logiciels ne peut vérifier les mises à jour maintenant.\nEssayez plus tard. Si cette situation perdure, contactez votre administrateur système.";
|
||||
|
||||
@@ -221,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Aucune mise à jour en attente";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Aucune correspondance";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Non disponible actuellement";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Pas assez d'espace disque";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Aucune correspondance";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Non installé";
|
||||
|
||||
@@ -239,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Une ou plusieurs mises à jour Apple importantes doivent être installées";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Un article doit être installé avant le %@";
|
||||
|
||||
@@ -275,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Préparation de la suppression";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Préparation du lancement de l'installeur macOS";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Mises à jour problématiques";
|
||||
|
||||
@@ -323,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Taille:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Ignorer ces mises à jour";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Logiciel";
|
||||
|
||||
@@ -332,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Les logiciels installés ou supprimés nécessitent un redémarrage. Vous aurez la possibilité de sauvegarder les documents ouverts.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Des conditions d'utilisation sont applicables à ce logiciel. Merci de contacter votre administrateur pour plus d'informations";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Démarrage du programme d'installation d'Adobe...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Démarrage de la mise à jour de macOS...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Démarrage...";
|
||||
|
||||
@@ -344,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problème de configuration du système";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Le système va redémarrer et lancer la mise à jour de macOS.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Le logiciel a été installé avec succès.";
|
||||
|
||||
@@ -359,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Il n'y a pas d'articles dans cette catégorie.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Une ou plusieurs mises à jour logicielles Apple importantes nécessitent un redémarrage.\n\nVous devez les installer en utilisant Mise à jour de logiciels dans les Préférences Système.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Une ou plusieurs mises à jour logicielles Apple importantes nécessitent un redémarrage.\n\nVotre administrateur a restreint l'installation de ces mises à jour. Contactez votre administrateur pour de l'assistance.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Il y a d'autres utilisateurs connectés sur l'ordinateur.\nMettre à jour peut entrainer la perte du travail des autres utilisateurs.\n\nMerci d'essayer plus tard après que les autres utilisateurs aient quitté leur session.";
|
||||
|
||||
@@ -383,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Cet article doit être installé avant le %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Cette mise à jour est en attente depuis %@ jours.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Pour autoriser cette opération, veuillez entrer votre mot de passe.";
|
||||
|
||||
@@ -399,7 +437,7 @@
|
||||
"Try selecting another developer." = "Essayez de sélectionner un autre développeur.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Type:";
|
||||
"Type" = "Type";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Non disponible";
|
||||
@@ -413,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Tout mettre à jour";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Mise à jour obligatoire";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Mise à jour disponible";
|
||||
|
||||
@@ -428,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Mettre à jour maintenant";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Mise à jour obligatoire";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "La mise à jour sera installée";
|
||||
|
||||
@@ -472,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Votre logiciel est à jour.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "Mise à jour MacOS requise";
|
||||
|
||||
+50
-14
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Applicazioni in uso da altri";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Attenzione";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Cancella";
|
||||
|
||||
@@ -110,7 +113,7 @@
|
||||
"Determining which filesystem items to remove" = "Determino elementi del filesystem da rimuovere";
|
||||
|
||||
/* Sidebar Developer label */
|
||||
"Developer:" = "Developer:";
|
||||
"Developer:" = "Sviluppatore:";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Done." = "Fatto.";
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Aiuto non è disponibile per Centro Gestione Applicazioni.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Aggiornamenti importanti da Apple";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Informazioni";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installazione";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installazione Necessaria";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installa adesso";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installazione richiesta";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installazione Necessaria";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Sessione di installazione fallita";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Logout Necessario";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "È richiesto un aggiornamento di Mac OS";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "In questo momento il Centro Gestione Applicazioni non riesce a controllare gli aggiornamenti.\nRiprovare più tardi. Se questa situazione persiste, contattare l'amministratore di sistema.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Nessun aggiornamento in sospeso";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Non Trovato";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Attualmente non disponibile";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Non c'è abbastanza spazio su disco";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Non Trovato";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Non Installato";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Uno o più aggiornamenti software importanti di Apple devono essere installati";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Uno o più elementi devono essere installati da %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Rimozione in preparazione";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Preparo l'installazione di macOS...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Aggiornamenti che richiedono attenzione";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Dimensione:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Ignora questi aggiornamenti";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Applicazione";
|
||||
|
||||
@@ -331,18 +352,27 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "L'applicazione installata o rimossa richiede il riavvio. Avrai la possibilità di salvare i documenti aperti.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Delle condizioni specifiche si applicano a questo software. Contatta il tuo amministratore di sistema per maggiori informazioni";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Avvio Adobe installer...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Inizio dell'aggiornamento di macOS...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Avvio...";
|
||||
|
||||
/* Sidebar Status label */
|
||||
"Status:" = "Status:";
|
||||
"Status:" = "Stato:";
|
||||
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problema di configurazione di sistema";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Il sistema si riavvierà e inizierà l'aggiornamento di macOS";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "L'applicazione è stata installata con successo.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Non ci sono elementi in questa categoria.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Ci sono uno o più aggiornamenti in sospeso che richiedono il riavvio del sistema.\n\nInstalla questi aggiornamenti usando Aggiornamento Software nelle Preferenze di Sistema.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Ci sono uno o più aggiornamenti in sospeso che richiedono il riavvio del sistema.\n\nL'amministratore di sistema controlla l'installazione di questi aggiornamenti. Contatta l'amministratore di sistema per richiedere assistenza.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Ci sono altri utenti collegati a questo computer.\nAggiornare ora può causare la perdita dei loro dati.\n\nRiprovare quando tutti gli utenti saranno scollegati.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Questo elemento deve essere installato da %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Questo aggiornamento è in sospeso da %@ giorni.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Per permettere questo, inserire la password di login.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Prova selezionando un altro developer.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Tipo:";
|
||||
"Type" = "Tipo";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Non disponibile";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Aggiorna Tutto";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Aggiornamento richiesto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Aggiornamento disponibile";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Aggiorna adesso";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Aggiornamento richiesto";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "L'aggiornamento verrà installato";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Le applicazioni sono aggiornate.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "È richiesto un aggiornamento di Mac OS";
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "他のユーザーが使用しているアプリケーションです";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "注意";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "キャンセル";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Managed Software Centerにヘルプは付属していません";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "重要な Apple ソフトウェアアップデート";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "情報";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "インストール";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "要請されたインストール";
|
||||
/* Install now button title */
|
||||
"Install now" = "今すぐインストール";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "要請されたインストール";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "要請されたインストール";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "インストールセッションに失敗しました";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "ログアウトが必要です";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOSアップデートが必要";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centerは現在アップーデートのチェックができません。\n後でもう一度実行してください。もしこの状況が改善されない場合は、システム管理者に連絡してください。";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "保留中のアップデートはありません";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "見つかりません";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "現在使用ができません";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "ディスクの空きが足りません";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "見つかりません";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "インストールされていません";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "重要な Apple ソフトウェアアップデートをインストールする必要があります";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "%@までに、1つまたそれ以上のアイテムのインストールが必要です";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "削除準備中です";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "macOS インストーラを実行する準備をしています...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "問題のアップデート";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "サイズ:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "ソフトウェアアップデートをスキップ";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "ソフトウェア";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "インストールまた削除したソフトウェアがあるため再起動が必要です。現在使用中のドキュメントの保存ができます。";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "このソフトウェアには、いくつかの条件が適用されます。詳しくは管理者にお問い合わせください";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Adobeインストーラーを開始…";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "macOS のアップグレードを開始しています...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "開始…";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "システムコンフィギュレーションに問題があります";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "システムが再起動し、macOS のアップグレードが始まります。";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "ソフトウェアのインストールが完了しました";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "このカテゴリにアイテムはありません";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "再起動が必要となる Apple ソフトウェアアップデートが保留中となっています。\n\nこのアップデートは、「システム環境設定」の「ソフトウェアアップデート」を使用してインストールする必要があります。";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "再起動が必要となる Apple ソフトウェアアップデートが保留中となっています。\n\nこのアップデートのインストールは制限されています。システム管理者にお問い合わせください。";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "他のユーザーがこのコンピューターにログインしています。\n今アップデートを実行すると、他のユーザーのワークが失われる原因になります。\n\n他のユーザーがログアウトしたのち、もう一度アップデートを実行してください。";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "このアイテムは%@までにインストールしなければいけません";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "この更新は、%@ 日間保留となっています。";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "これを許可する為、ログインパスワードを入力して下さい。";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "ほかのディベロッパを選択してしてください";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "種類:";
|
||||
"Type" = "種類";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "入手できません";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "すべてをアップデート";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "アップデートが必要です";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "アップデートがあります";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "今すぐアップデート";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "アップデートが必要です";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "アップデートをインストールします";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "ソフトウェアは最新です";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOSアップデートが必要";
|
||||
|
||||
@@ -613,7 +613,8 @@ func buildUpdatesPage() throws {
|
||||
try buildUpdateStatusPage()
|
||||
return
|
||||
}
|
||||
let item_list = getEffectiveUpdateList()
|
||||
let filterAppleUpdates = (NSApp.delegate! as! AppDelegate).mainWindowController.should_filter_apple_updates
|
||||
let item_list = getEffectiveUpdateList(filterAppleUpdates)
|
||||
let problem_updates = getProblemItems()
|
||||
for item in problem_updates {
|
||||
item["hide_cancel_button"] = "hidden"
|
||||
@@ -672,7 +673,7 @@ func buildUpdatesPage() throws {
|
||||
// in Python was count = len([item for item in item_list if item['status'] != 'problem-item'])
|
||||
page["update_count"] = updateCountMessage(count)
|
||||
page["install_btn_label"] = getInstallAllButtonTextForCount(count)
|
||||
page["warning_text"] = getWarningText()
|
||||
page["warning_text"] = getWarningText(filterAppleUpdates)
|
||||
|
||||
// build problem updates table
|
||||
page["problem_updates_header_message"] = NSLocalizedString(
|
||||
@@ -795,7 +796,7 @@ func getRestartActionForUpdateList(_ update_list: [GenericItem]) -> String {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getWarningText() -> String {
|
||||
func getWarningText(_ filterAppleUpdates: Bool) -> String {
|
||||
// Return localized text warning about forced installs and/or
|
||||
// logouts and/or restarts
|
||||
let item_list = getEffectiveUpdateList()
|
||||
@@ -807,6 +808,11 @@ func getWarningText() -> String {
|
||||
"One or more items must be installed by %@",
|
||||
comment: "Forced Install Date summary")
|
||||
warning_text = NSString(format: forced_date_text as NSString, date_str) as String
|
||||
} else if !filterAppleUpdates && shouldAggressivelyNotifyAboutAppleUpdates() {
|
||||
warning_text = NSLocalizedString(
|
||||
"One or more important Apple updates must be installed",
|
||||
comment: "Pending Apple Updates warning"
|
||||
)
|
||||
}
|
||||
let restart_text = getRestartActionForUpdateList(item_list)
|
||||
if !restart_text.isEmpty {
|
||||
|
||||
@@ -24,11 +24,17 @@ func is_safe_to_use(_ pathname: String) -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
let fref = open(UnsafePointer(pathname), O_RDWR | O_CREAT | O_NOFOLLOW, 0x0600)
|
||||
if fref != -1 {
|
||||
var st = stat()
|
||||
if fstat(fref, UnsafeMutablePointer<stat>(&st)) == 0 {
|
||||
safe = ((st.st_mode & S_IFREG) != 0) && (st.st_uid == getuid())
|
||||
pathname.withCString(){
|
||||
let fref = open($0, O_RDWR | O_CREAT | O_NOFOLLOW, 0x0600)
|
||||
if fref != 1 {
|
||||
var st = stat()
|
||||
var fstat_result : Int32 = 0
|
||||
withUnsafeMutablePointer(to: &st){
|
||||
fstat_result = fstat(fref, $0)
|
||||
}
|
||||
if fstat_result == 0 {
|
||||
safe = ((st.st_mode & S_IFREG) != 0) && (st.st_uid == getuid())
|
||||
}
|
||||
}
|
||||
close(fref)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// Copyright © 2018-2020 The Munki Project. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Foundation
|
||||
import SystemConfiguration
|
||||
import IOKit
|
||||
@@ -17,7 +18,7 @@ let UPDATECHECKLAUNCHFILE = "/private/tmp/.com.googlecode.munki.updatecheck.laun
|
||||
let INSTALLWITHOUTLOGOUTFILE = "/private/tmp/.com.googlecode.munki.managedinstall.launchd"
|
||||
|
||||
let BUNDLE_ID = "ManagedInstalls" as CFString
|
||||
let DEFAULT_GUI_CACHE_AGE_SECS = 600
|
||||
let DEFAULT_GUI_CACHE_AGE_SECS = 3600
|
||||
let WRITEABLE_SELF_SERVICE_MANIFEST_PATH = "/Users/Shared/.SelfServeManifest"
|
||||
|
||||
func exec(_ command: String, args: [String] = []) -> String {
|
||||
@@ -207,7 +208,7 @@ func getInstallInfo() -> PlistDict {
|
||||
return readPlistAsNSDictionary(installinfo_path)
|
||||
}
|
||||
|
||||
func getAppleUpdates() -> PlistDict {
|
||||
func getAppleUpdates() -> [PlistDict] {
|
||||
// Returns any available Apple update info
|
||||
let installAppleSoftwareUpdates = pythonishBool(pref("InstallAppleSoftwareUpdates"))
|
||||
let appleSoftwareUpdatesOnly = pythonishBool(pref("AppleSoftwareUpdatesOnly"))
|
||||
@@ -215,12 +216,36 @@ func getAppleUpdates() -> PlistDict {
|
||||
let managedinstallbase = pref("ManagedInstallDir") as! String
|
||||
let appleupdates_path = NSString.path(
|
||||
withComponents: [managedinstallbase, "AppleUpdates.plist"])
|
||||
return readPlistAsNSDictionary(appleupdates_path)
|
||||
let plistData = readPlistAsNSDictionary(appleupdates_path)
|
||||
let rawAppleUpdates = plistData["AppleUpdates"] as? [PlistDict] ?? []
|
||||
if pythonishBool(plistData["AppleUpdatesTesting"]) {
|
||||
// this lets us test MSC behavior with fake data
|
||||
return rawAppleUpdates
|
||||
}
|
||||
// since it's possible SoftwareUpdate has run since managedsoftwareupdate last
|
||||
// ran, we should filter these against the RecommendedUpdates in com.apple.SoftwareUpdate
|
||||
var filteredAppleUpdates = [PlistDict]()
|
||||
for item in rawAppleUpdates {
|
||||
if let productKey = item["productKey"] as? String {
|
||||
if suRecommendedUpdateIDs().contains(productKey) {
|
||||
filteredAppleUpdates.append(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredAppleUpdates
|
||||
} else {
|
||||
return PlistDict()
|
||||
return [PlistDict]()
|
||||
}
|
||||
}
|
||||
|
||||
func getUpdateNotificationTracking() -> PlistDict {
|
||||
// Returns a dictionary describing when items were first made available
|
||||
let managedinstallbase = pref("ManagedInstallDir") as! String
|
||||
let updatetracking_path = NSString.path(
|
||||
withComponents: [managedinstallbase, "UpdateNotificationTracking.plist"])
|
||||
return readPlistAsNSDictionary(updatetracking_path)
|
||||
}
|
||||
|
||||
func munkiUpdatesContainAppleItems() -> Bool {
|
||||
// Return true if there are any Apple items in the list of updates
|
||||
let installinfo = getInstallInfo()
|
||||
@@ -251,9 +276,7 @@ func thereAreUpdatesToBeForcedSoon(hours: Int = 72) -> Bool {
|
||||
// Return True if any updates need to be installed within the next
|
||||
// X hours, false otherwise
|
||||
var installinfo = getInstallInfo()["managed_installs"] as? [PlistDict] ?? [PlistDict]()
|
||||
let appleupdates = getAppleUpdates()["AppleUpdates"] as? [PlistDict] ?? [PlistDict]()
|
||||
installinfo = installinfo + appleupdates
|
||||
|
||||
installinfo = installinfo + getAppleUpdates()
|
||||
let now_xhours = Date(timeIntervalSinceNow: TimeInterval(hours * 3600))
|
||||
for item in installinfo {
|
||||
if var force_install_after_date = item["force_install_after_date"] as? Date {
|
||||
@@ -273,8 +296,7 @@ func earliestForceInstallDate(_ installinfo: [PlistDict]? = nil) -> Date? {
|
||||
var earliest_date: Date? = nil
|
||||
if installinfo == nil {
|
||||
let managed_installs = getInstallInfo()["managed_installs"] as? [PlistDict] ?? [PlistDict]()
|
||||
let appleupdates = getAppleUpdates()["AppleUpdates"] as? [PlistDict] ?? [PlistDict]()
|
||||
installinfo = managed_installs + appleupdates
|
||||
installinfo = managed_installs + getAppleUpdates()
|
||||
}
|
||||
for install in installinfo! {
|
||||
if var this_force_install_date = install["force_install_after_date"] as? Date {
|
||||
@@ -375,8 +397,9 @@ func startUpdateCheck(_ suppress_apple_update_check: Bool = false) throws {
|
||||
do {
|
||||
try writePlist(plist, toFile: UPDATECHECKLAUNCHFILE)
|
||||
} catch {
|
||||
throw ProcessStartError.error(
|
||||
description: "Could not create file \(UPDATECHECKLAUNCHFILE) -- \(error)")
|
||||
let message = "Could not create file \(UPDATECHECKLAUNCHFILE) -- \(error)"
|
||||
msc_log("MSC", "cant_write_file", msg: message)
|
||||
throw ProcessStartError.error(description: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Programmer som er i bruk av andre";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Merk følgende";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Avbryt";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Hjelp er ikke tilgjengelig for Managed Software Center.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Viktige Apple oppdateringer";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Informasjon";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installer";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installasjon påkrevd";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installer nå";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installasjon forespurt";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installasjon påkrevd";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Installasjonen feilet";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Utlogging påkrevd";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS-oppdatering nødvendig";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke søke etter oppdateringer.\nPrøv igjen senere. Hvis problemet fortsetter, kontakt din systemadministrator.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Ingen ventende oppdateringer";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Ikke funnet";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Ikke tilgjengelig for øyeblikket";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Ikke nok plass på disken";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Ikke funnet";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Ikke installert";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "En eller flere viktige Apple oppdateringer må installeres";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Et eller flere objekter må installeres senest %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Forbereder avinstallasjon";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Forbereder å kjøre macOS Installer...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Problematiske oppdateringer";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Størrelse:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Hopp over";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Programvare";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Programvaren som er blitt installert eller fjernet krever en omstart. Du får muligheten til å lagre åpne dokumenter.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Noen betingelser gjelder for denne programvaren. Kontakt administratoren din for mer informasjon";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Starter Adobe installer...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Starter oppgradering av macOS...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Starter...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problem med systemkonfigurasjon";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Systemet vil starte på nytt og begynne å oppgradere macOS.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Installasjon av programvaren er fullført.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Det finnes ingen objekter i kategorien.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "En eller flere ventende Apple-programvareoppdateringer krever en omstart.\n\nDu må installere disse fra Programvareoppdatering-valgpanelet i Systemvalg.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "En eller flere ventende Apple-programvareoppdateringer krever en omstart.\n\nAdministratoren har blokkert installasjon av disse oppdateringene. Kontakt administratoren for å få hjelp.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Det er andre påloggede brukere på datamaskinen.\nHvis du oppdaterer nå, risikerer du at de mister deres ulagrede arbeid.\n\nPrøv igjen senere når de andre brukerne har logget ut.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Dette objektet må installeres senest %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Denne oppdateringen har ventet i %@ dager.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "For å tillate dette, må du skrive inn ditt påloggingspassord.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Prøv å velge en annen utvikler.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Type:";
|
||||
"Type" = "Type";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Ikke tilgjengelig";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Oppdater alle";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Oppdatering påkrevd";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Kan oppdateres";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Oppdater nu";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Oppdatering påkrevd";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Oppdatering vil bli installert";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Din programvare er oppdateret.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS-oppdatering nødvendig";
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Applicaties in gebruik door anderen";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Attentie";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Annuleren";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Help is niet beschikbaar voor Managed Software Center.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Belangrijke Apple Updates";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Informatie";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installeer";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installatie Benodigd";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installeer nu";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installatie Verzocht";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installatie Benodigd";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Installatie-sessie niet succesvol";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Uitloggen Benodigd";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS update vereist";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan geen updates checken.\nProbeer later nog eens. Als deze situatie niet verandert, neem contact op met uw systeembeheerder.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Geen updates";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Niet Gevonden";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Op dit moment niet beschikbaar";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Onvoldoende ruimte op de harde schijf";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Niet Gevonden";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Niet geïnstalleerd";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Een of meerdere belangrijke Apple updates dienen geïnstalleerd worden";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Eén of meer items moeten voor %@ worden geïnstalleerd";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Verwijderen voorbereiden";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "macOS installatie voorbereiden...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Problematische updates";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Grootte:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Sla Apple updates over";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Software";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Geïnstalleerde of verwijderde software maakt een herstart nodig. U krijgt de kans om geopende documenten op te slaan.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Op deze software zijn enkele voorwaarden van toepassing. Neem contact op met uw beheerder voor meer informatie";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Adobe installer opstarten...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Starten met de macOS upgrade...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Beginnen...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Probleem met de systeemconfiguratie";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "De Mac zal opnieuw opstarten en de upgrade van macOS starten.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "De software is succesvol geïnstalleerd.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Er zijn geen items in deze categorie.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Er zijn een of meerdere Apple Software Updates die een herstart nodig hebben.\n\nInstalleer deze updates door gebruik te maken Software-update in Systeemvoorkeuren.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Er zijn een of meerdere Apple Software Updates die een herstart nodig hebben.\n\nDe beheerder van deze machine staat de installatie van deze updates niet toe. Neem contact op met de beheerder voor assistentie.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Er zijn andere gebruikers ingelogd op deze computer.\nAls U nu bijwerkt kunnen de andere gebruikers hun werk verliezen.\n\nProbeer opnieuw nadat de andere gebruikers zijn uitgelogd.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Dit item moet geïnstalleerd zijn voor %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Deze update wacht al %@ dagen.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Om dit toe te staan, vul je loginwachtwoord in.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Selecteer een andere ontwikkelaar.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Type:";
|
||||
"Type" = "Type";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Niet beschikbaar";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Update Alles";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Update benodigd";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Update beschikbaar";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Update nu";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Update benodigd";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Update wordt geïnstalleerd";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Uw software is up to date.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS update vereist";
|
||||
|
||||
+48
-12
@@ -52,6 +52,9 @@
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Приложения, которые используются другими";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Внимание";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Отменить";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Помощь недоступна для Центра Управления ПО.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Важные обновления Apple";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Информация";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Установить";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Установить Обязательные";
|
||||
/* Install now button title */
|
||||
"Install now" = "Установить сейчас";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Установить Требуемые";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Установить Обязательные";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Сбой процесса установки";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Необходимо выйти из системы";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "требуется обновление macOS";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Центр Управления ПО не может проверить наличие обновлений в данный момент.\nПопробуйте еще раз позже. Если эта ситуация продолжится, обратитесь к системному администратору.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Нет отложенных обновлений";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Не найдено";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "В настоящее время не доступно";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Недостаточно места на диске";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Не найдено";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Не установлено";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "Необходимо установить одно или более обновлений Apple";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Один или несколько элементов должны быть установлены до %@";
|
||||
|
||||
@@ -274,6 +289,9 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Подготовка удаления";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Подготовка к запуску Установщика macOS...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "проблемные обновления";
|
||||
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Размер:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Пропустить эти обновления";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Программы";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Установленное или удаленное ПО требует перезагрузки. У Вас будет возможность сохранить открытые документы.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "К этому ПО применяются некоторые условия. Обратитесь к администратору системы за подробной информацией";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Запуск Adobe installer...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Начало обновления macOS...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Запуск...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Проблема с конфигурацией системы";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Система перезагрузится, и начнётся обновление macOS";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Программное обеспечение было успешно установлено.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Нет элементов в этой категории.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Одно или более обновлений ПО Apple требуют перезагрузки.\n\nВам необходимо установить их через раздел «Обновление ПО» в Системных настройках.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Одно или более обновлений ПО Apple требуют перезагрузки.\n\nАдминистратор запретил установку этих обновлений. Обратитесь за помощью к администратору.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Другие пользователи сейчас в системе.\nОбновление может привести к потере их несохраненных документов.\n\nПовторите попытку после выхода других пользователей из системы.";
|
||||
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Этот элемент должен быть установлен до %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Это обновление доступно в течение %@ дн.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Для разрешения введите свой пароль для входа в систему.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Попробуйте выбрать другой разработчика.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Тип:";
|
||||
"Type" = "Тип";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Недоступно";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Обновить Все";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Обновить Обязательное";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Доступно обновление";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Обновить сейчас";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Обновить Обязательное";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Обновление будет установлено";
|
||||
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "У Вас самое новое ПО.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "требуется обновление macOS";
|
||||
|
||||
+54
-18
@@ -44,14 +44,17 @@
|
||||
"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Installationen misslyckades. Ytterligare installationsförsök kommer att göras.\nKontakta din datoradministratör om problemet återkommer.";
|
||||
|
||||
/* Long Not Enough Disk Space For Update display text */
|
||||
"An older version is currently installed. There is not enough disk space to download and install this update." = "An older version is currently installed. There is not enough disk space to download and install this update.";
|
||||
"An older version is currently installed. There is not enough disk space to download and install this update." = "En äldre version är installerad. Det finns inte tillräckligt med utrymme för att ladda ner och installera uppdateringen.";
|
||||
|
||||
/* Long update requires a higher OS version text */
|
||||
"An older version is currently installed. You must upgrade to macOS version %@ or higher to be able to install this update." = "An older version is currently installed. You must upgrade to macOS version %@ or higher to be able to install this update.";
|
||||
"An older version is currently installed. You must upgrade to macOS version %@ or higher to be able to install this update." = "En äldre version är installerad. Du behöver uppgradera till macOS version %@ eller nyare för att kunna installera uppdateringen.";
|
||||
|
||||
/* Other Users Blocking Apps Running title */
|
||||
"Applications in use by others" = "Andra användare på datorn har programmet öppet";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Title */
|
||||
"Attention" = "Observera";
|
||||
|
||||
/* Cancel button title/short action text */
|
||||
"Cancel" = "Avbryt";
|
||||
|
||||
@@ -151,18 +154,24 @@
|
||||
/* No help alert detail */
|
||||
"Help isn't available for Managed Software Center." = "Det finns ingen hjälp för Managed Software Center.";
|
||||
|
||||
/* Apple Software Updates Pending title */
|
||||
"Important Apple Updates" = "Viktiga Apple-uppdateringar";
|
||||
|
||||
/* Sidebar Information label */
|
||||
"Information" = "Information";
|
||||
|
||||
/* Install action text */
|
||||
"Install" = "Installera";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installation krävs";
|
||||
/* Install now button title */
|
||||
"Install now" = "Installera nu";
|
||||
|
||||
/* Install Requested status text */
|
||||
"Install requested" = "Installation begärd";
|
||||
|
||||
/* Install Required action text */
|
||||
"Install Required" = "Installation krävs";
|
||||
|
||||
/* Install Session Failed title */
|
||||
"Install session failed" = "Installationen misslyckades";
|
||||
|
||||
@@ -187,6 +196,9 @@
|
||||
/* Logout Required title */
|
||||
"Logout Required" = "Utloggning krävs";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS-uppdatering krävs";
|
||||
|
||||
/* Failed Preflight Check detail */
|
||||
"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan inte leta efter uppdateringar nu.\nFörsök igen senare. Kontakta din datoradministratör om problemet återkommer.";
|
||||
|
||||
@@ -220,15 +232,15 @@
|
||||
/* No Updates message */
|
||||
"No pending updates" = "Inga uppdateringar väntar.";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Kunde inte hittas";
|
||||
|
||||
/* Not Currently Available display text */
|
||||
"Not currently available" = "Inte tillgänglig just nu";
|
||||
|
||||
/* Not Enough Disk Space display text */
|
||||
"Not enough disk space" = "Inte tillräckligt med utrymme på skivan";
|
||||
|
||||
/* Item Not Found title */
|
||||
"Not Found" = "Kunde inte hittas";
|
||||
|
||||
/* Not Installed status text */
|
||||
"Not installed" = "Ej installerat";
|
||||
|
||||
@@ -238,6 +250,9 @@
|
||||
/* OK button title */
|
||||
"OK" = "OK";
|
||||
|
||||
/* Pending Apple Updates warning */
|
||||
"One or more important Apple updates must be installed" = "En eller flera viktiga Apple-uppdateringar måste installeras";
|
||||
|
||||
/* Forced Install Date summary */
|
||||
"One or more items must be installed by %@" = "Ett eller flera objekt måste installeras senast %@";
|
||||
|
||||
@@ -274,8 +289,11 @@
|
||||
/* Preparing Removal status text */
|
||||
"Preparing removal" = "Förbereder borttagning";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Preparing to run macOS Installer..." = "Förbereder installation av macOS...";
|
||||
|
||||
/* Problem Updates label */
|
||||
"Problem updates" = "Problem updates";
|
||||
"Problem updates" = "Problemuppdateringar";
|
||||
|
||||
/* Quit button title */
|
||||
"Quit" = "Avsluta";
|
||||
@@ -296,7 +314,7 @@
|
||||
"Removing receipt info" = "Tar bort kvittot";
|
||||
|
||||
/* Install Required action text */
|
||||
"Required" = "Required";
|
||||
"Required" = "Krävs";
|
||||
|
||||
/* Restart button title */
|
||||
"Restart" = "Omstart";
|
||||
@@ -322,6 +340,9 @@
|
||||
/* Sidebar Size label */
|
||||
"Size:" = "Storlek:";
|
||||
|
||||
/* Skip Apple updates button title */
|
||||
"Skip these updates" = "Hoppa över";
|
||||
|
||||
/* Software label */
|
||||
"Software" = "Programvara";
|
||||
|
||||
@@ -331,9 +352,15 @@
|
||||
/* Restart Required alert detail */
|
||||
"Software installed or removed requires a restart. You will have a chance to save open documents." = "Program som lades till eller togs bort kräver en omstart. Du kommer att få möjlighet att spara ditt arbete.";
|
||||
|
||||
/* Pre Install Uninstall Upgrade Alert Detail */
|
||||
"Some conditions apply to this software. Please contact your administrator for more details" = "Vissa villkor gäller för den här mjukvaran. Kontakta din administratör för mer information.";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting Adobe installer..." = "Startar Adobe installer...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting macOS upgrade..." = "Startar uppgradering av macOS...";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"Starting..." = "Startar...";
|
||||
|
||||
@@ -343,6 +370,9 @@
|
||||
/* System configuration problem alert title */
|
||||
"System configuration problem" = "Problem med systemkonfiguration";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"System will restart and begin upgrade of macOS." = "Systemet kommer att starta om och påbörja uppgraderingen av macOS";
|
||||
|
||||
/* managedsoftwareupdate message */
|
||||
"The software was successfully installed." = "Programvaran installerades.";
|
||||
|
||||
@@ -358,6 +388,12 @@
|
||||
/* No Category Results primary text */
|
||||
"There are no items in this category." = "Det finns inget i den här kategorin.";
|
||||
|
||||
/* Apple Software Updates Pending detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYou must install these updates using Software Update in System Preferences." = "Det finns en eller flera väntande Apple-uppdateringar som kräver att datorn startas om.\n\nDu måste installera uppdateringarna med inställningspanelen Programuppdatering i Systeminställningar.";
|
||||
|
||||
/* Apple Software Updates Unable detail */
|
||||
"There are one or more pending Apple Software Updates that require a restart.\n\nYour administrator has restricted installation of these updates. Contact your administrator for assistance." = "Det finns en eller flera väntande Apple-uppdateringar som kräver att datorn startas om.\n\nAdminstratören har begränsat möjligheten att installera uppdateringar. Kontakta din adminstratör för hjälp.";
|
||||
|
||||
/* Other Users Logged In detail */
|
||||
"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Det finns andra användare inloggade på datorn.\nOm du uppdaterar nu riskerar dom att förlora ej sparat arbete.\n\nProva igen när de andra användarna har loggat ut.";
|
||||
|
||||
@@ -374,7 +410,7 @@
|
||||
"There is no new software for your computer at this time." = "Det finns inga uppdateringar för närvarande.";
|
||||
|
||||
/* Long Not Enough Disk Space display text */
|
||||
"There is not enough disk space to download and install this item." = "There is not enough disk space to download and install this item.";
|
||||
"There is not enough disk space to download and install this item." = "Det finns inte tillräckligt med utrymme för att ladda ner och installera.";
|
||||
|
||||
/* Dependency List prologue text */
|
||||
"This item is required by:" = "Krävs av:";
|
||||
@@ -382,6 +418,9 @@
|
||||
/* Forced Date warning */
|
||||
"This item must be installed by %@" = "Måste installeras senast %@";
|
||||
|
||||
/* Pending days message */
|
||||
"This update has been pending for %@ days." = "Den här uppdateringen har väntat i %@ dagar.";
|
||||
|
||||
/* Password explanation */
|
||||
"To allow this, enter your login password." = "Ange ditt inloggningslösenord för att tillåta detta.";
|
||||
|
||||
@@ -398,7 +437,7 @@
|
||||
"Try selecting another developer." = "Försök med en annan utvecklare.";
|
||||
|
||||
/* Sidebar Type label */
|
||||
"Type:" = "Typ:";
|
||||
"Type" = "Typ";
|
||||
|
||||
/* Unavailable status text */
|
||||
"Unavailable" = "Ej tillgänglig";
|
||||
@@ -412,9 +451,6 @@
|
||||
/* Update All button title */
|
||||
"Update All" = "Uppdatera alla";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Uppdatering krävs";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update available" = "Uppdatering tillgänglig";
|
||||
|
||||
@@ -427,6 +463,9 @@
|
||||
/* Update Now button title */
|
||||
"Update now" = "Uppdatera nu";
|
||||
|
||||
/* Update Required long action text */
|
||||
"Update Required" = "Uppdatering krävs";
|
||||
|
||||
/* Update Will Be Installed status text */
|
||||
"Update will be installed" = "Uppdateringen kommer att installeras";
|
||||
|
||||
@@ -461,7 +500,7 @@
|
||||
"You must quit the following applications before proceeding with installation or removal:\n\n%@" = "Du måste avsluta följande program innan installationen eller avinstallationen kan fortsätta:\n\n%@";
|
||||
|
||||
/* Long item requires a higher OS version text */
|
||||
"You must upgrade to macOS version %@ to be able to install this item." = "You must upgrade to macOS version %@ to be able to install this item.";
|
||||
"You must upgrade to macOS version %@ to be able to install this item." = "Du behöver uppgradera till macOS version %@ för att kunna installera.";
|
||||
|
||||
/* No Power Source Warning text */
|
||||
"Your computer is not connected to a power source." = "Datorn är inte ansluten till en strömkälla.";
|
||||
@@ -471,6 +510,3 @@
|
||||
|
||||
/* No Pending Updates primary text */
|
||||
"Your software is up to date." = "Programvaran är uppdaterad.";
|
||||
|
||||
/* macOS update required text */
|
||||
"macOS update required" = "macOS update required";
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1020;
|
||||
LastUpgradeCheck = 1140;
|
||||
ORGANIZATIONNAME = "The Munki Project";
|
||||
TargetAttributes = {
|
||||
C0544D4720AF0EDA00DC86F6 = {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
NSString * const ManagedSoftwareCenterBundleID = @"com.googlecode.munki.ManagedSoftwareCenter";
|
||||
NSString * const NotificationCenterUIBundleID = @"com.apple.notificationcenterui";
|
||||
NSString * const MunkiUpdatesURL = @"munki://updates";
|
||||
NSString * const MunkiNotificationURL = @"munki://notify";
|
||||
long const DefaultUseNotificationCenterDays = 3;
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ InstallFakeBundleIdentifierHook()
|
||||
// to launch MSC.app
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications];
|
||||
}
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: MunkiUpdatesURL]];
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: MunkiNotificationURL]];
|
||||
[NSApp terminate: self];
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ InstallFakeBundleIdentifierHook()
|
||||
// Create options (userInfo) dictionary
|
||||
NSMutableDictionary *options = [NSMutableDictionary dictionary];
|
||||
options[@"action"] = @"open_url";
|
||||
options[@"value"] = MunkiUpdatesURL;
|
||||
options[@"value"] = MunkiNotificationURL;
|
||||
|
||||
// deliver the notification
|
||||
[self deliverNotificationWithTitle:title
|
||||
@@ -239,7 +239,7 @@ InstallFakeBundleIdentifierHook()
|
||||
if ([action isEqualToString:@"open_url"]){
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:value]];
|
||||
} else {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: MunkiUpdatesURL]];
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: MunkiNotificationURL]];
|
||||
}
|
||||
|
||||
[NSApp terminate: self];
|
||||
|
||||
@@ -95,6 +95,8 @@ class AppUsageClient(object):
|
||||
try:
|
||||
self.connect()
|
||||
result = self.send_request(request)
|
||||
except AppUsageClientError as err:
|
||||
return "ERROR:%s" % err
|
||||
finally:
|
||||
self.disconnect()
|
||||
return result
|
||||
|
||||
+21
-10
@@ -50,6 +50,13 @@ APPNAME = 'appusaged'
|
||||
VERSION = '0.1'
|
||||
|
||||
|
||||
def print_error_and_exit(errmsg):
|
||||
'''Prints an error message to stderr, sleeps, and exits'''
|
||||
print(errmsg, file=sys.stderr)
|
||||
time.sleep(10)
|
||||
return 1
|
||||
|
||||
|
||||
class AppUsageHandlerError(Exception):
|
||||
'''Exception to raise if there is any error in AppUsageHandler'''
|
||||
pass
|
||||
@@ -161,7 +168,14 @@ class RunHandler(SocketServer.StreamRequestHandler):
|
||||
self.log.debug('Handling request')
|
||||
|
||||
# Get uid and primary gid of connecting peer.
|
||||
uid, gids = self.getpeerid()
|
||||
try:
|
||||
uid, gids = self.getpeerid()
|
||||
except OSError as err:
|
||||
self.log.error(u'Peerid failure: %s' % unicode_or_str(err))
|
||||
self.request.send(
|
||||
(u'ERROR:Internal peerid error\n').encode('UTF-8'))
|
||||
return
|
||||
|
||||
gid = gids[0]
|
||||
self.log.debug(
|
||||
'Got request from uid %d gid %d' % (uid, gid))
|
||||
@@ -206,7 +220,6 @@ class RunHandler(SocketServer.StreamRequestHandler):
|
||||
self.log.error(u'Caught exception: %s' % repr(err))
|
||||
self.request.send(
|
||||
(u'ERROR:Caught exception: %s' % repr(err)).encode('UTF-8'))
|
||||
return
|
||||
|
||||
class AppUsageDaemonError(Exception):
|
||||
'''Exception to raise for AppUsageDaemon errors'''
|
||||
@@ -274,10 +287,7 @@ def main():
|
||||
'''Start our daemon, connect to socket and process events'''
|
||||
# Make sure we're launched as root
|
||||
if os.geteuid() != 0:
|
||||
print('%s must be run as root.' % APPNAME, file=sys.stderr)
|
||||
# Sleep to avoid respawn.
|
||||
time.sleep(10)
|
||||
return 1
|
||||
print_error_and_exit('%s must be run as root.' % APPNAME)
|
||||
|
||||
# Make sure that the executable and all containing directories are owned
|
||||
# by root:wheel or root:admin, and not writeable by other users.
|
||||
@@ -314,13 +324,14 @@ def main():
|
||||
# Get socket file descriptors from launchd.
|
||||
socket_fd = launchd.get_socket_fd(APPNAME.encode('UTF-8'))
|
||||
if not socket_fd:
|
||||
print('No socket provided to us by launchd', file=sys.stderr)
|
||||
time.sleep(10)
|
||||
return 1
|
||||
print_error_and_exit('No socket provided to us by launchd')
|
||||
|
||||
# Create the daemon object.
|
||||
daemon = AppUsageDaemon(socket_fd, RunHandler)
|
||||
daemon.setup_logging()
|
||||
try:
|
||||
daemon.setup_logging()
|
||||
except AppUsageDaemonError as err:
|
||||
print_error_and_exit('%s' % err)
|
||||
|
||||
daemon.log.debug('%s v%s starting', APPNAME, VERSION)
|
||||
|
||||
|
||||
@@ -101,6 +101,24 @@ class FDEUtil(object):
|
||||
self.log.info('Restart request denied')
|
||||
raise FDEUtilError('Restart may only be triggered by root')
|
||||
|
||||
if self.request['task'] == 'delayed_authrestart':
|
||||
# set up a delayed authrestart. Defaults to waiting indefinitely,
|
||||
# so the next "normal" restart becomes an authrestart.
|
||||
self.log.info('Delayed restart request from uid %s', self.uid)
|
||||
if self.uid == 0:
|
||||
self.log.info('Stored username for authrestart: %s',
|
||||
self.server.stored_username)
|
||||
delayminutes = self.request.get('delayminutes', -1)
|
||||
authrestart.perform_auth_restart(
|
||||
password=self.server.stored_password,
|
||||
username=self.server.stored_username,
|
||||
delayminutes=delayminutes)
|
||||
return 'DONE'
|
||||
else:
|
||||
self.log.info('Delayed restart request denied')
|
||||
raise FDEUtilError(
|
||||
'Delayed restart may only be triggered by root')
|
||||
|
||||
if self.request['task'] == 'store_password':
|
||||
# store a password for later fdesetup authrestart
|
||||
self.log.info('Store password request')
|
||||
|
||||
@@ -273,7 +273,9 @@ def doInstallTasks(do_apple_updates, only_unattended=False):
|
||||
'Restart not initiated by startosinstall; will initiate '
|
||||
'restart ourselves')
|
||||
if not authrestartd.restart():
|
||||
authrestart.do_authorized_or_normal_restart()
|
||||
authrestart.do_authorized_or_normal_restart(
|
||||
shutdown=osutils.bridgeos_update_staged()
|
||||
)
|
||||
|
||||
if do_apple_updates:
|
||||
# install Apple updates
|
||||
@@ -408,7 +410,6 @@ def munkiUpdatesContainStartOSInstallItem():
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def munkiUpdatesContainAppleItems():
|
||||
"""Return True if there are any Apple items in the list of updates"""
|
||||
install_info = os.path.join(
|
||||
@@ -711,11 +712,13 @@ def main():
|
||||
# triggered the update before logging out, or we triggered it before
|
||||
# restarting.
|
||||
user_triggered = False
|
||||
flagfiles = [constants.CHECKANDINSTALLATSTARTUPFLAG,
|
||||
flagfiles = (constants.CHECKANDINSTALLATSTARTUPFLAG,
|
||||
constants.INSTALLATSTARTUPFLAG,
|
||||
constants.INSTALLATLOGOUTFLAG]
|
||||
constants.INSTALLATLOGOUTFLAG)
|
||||
for filename in flagfiles:
|
||||
if os.path.exists(filename):
|
||||
munkilog.log(
|
||||
"managedsoftwareupdate run triggered by %s" % filename)
|
||||
user_triggered = True
|
||||
if filename == constants.CHECKANDINSTALLATSTARTUPFLAG:
|
||||
runtype = 'checkandinstallatstartup'
|
||||
@@ -729,9 +732,32 @@ def main():
|
||||
if networkUp():
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
# delete triggerfile if _not_ checkandinstallatstartup
|
||||
os.unlink(filename)
|
||||
break
|
||||
elif filename == constants.INSTALLATSTARTUPFLAG:
|
||||
runtype = 'installatstartup'
|
||||
# check to see if we should do some special handling;
|
||||
# we might have installed Apple updates right before a
|
||||
# restart
|
||||
try:
|
||||
launch_options = FoundationPlist.readPlist(filename)
|
||||
if launch_options.get("SkipAppleUpdates"):
|
||||
munkilog.log("SkipAppleUpdates is True")
|
||||
appleupdates.clearAppleUpdateInfo()
|
||||
options.munkipkgsonly = True
|
||||
except FoundationPlist.FoundationPlistException as err:
|
||||
pass
|
||||
break
|
||||
|
||||
# delete any triggerfile that isn't checkandinstallatstartup
|
||||
# so it's not hanging around at the next logout or restart
|
||||
for triggerfile in (constants.INSTALLATSTARTUPFLAG,
|
||||
constants.INSTALLATLOGOUTFLAG):
|
||||
if os.path.exists(triggerfile):
|
||||
try:
|
||||
os.unlink(triggerfile)
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
|
||||
if not user_triggered:
|
||||
# no trigger file was found -- how'd we get launched?
|
||||
osutils.cleanUpTmpDir()
|
||||
@@ -743,6 +769,10 @@ def main():
|
||||
launchdtriggerfile = \
|
||||
'/private/tmp/.com.googlecode.munki.managedinstall.launchd'
|
||||
if os.path.exists(launchdtriggerfile):
|
||||
munkilog.log(
|
||||
"managedsoftwareupdate run triggered by %s"
|
||||
% launchdtriggerfile
|
||||
)
|
||||
# remove it so we aren't automatically relaunched
|
||||
os.unlink(launchdtriggerfile)
|
||||
runtype = 'installwithnologout'
|
||||
@@ -756,6 +786,10 @@ def main():
|
||||
launchdtriggerfile = \
|
||||
'/private/tmp/.com.googlecode.munki.updatecheck.launchd'
|
||||
if os.path.exists(launchdtriggerfile):
|
||||
munkilog.log(
|
||||
"managedsoftwareupdate run triggered by %s"
|
||||
% launchdtriggerfile
|
||||
)
|
||||
try:
|
||||
launch_options = FoundationPlist.readPlist(launchdtriggerfile)
|
||||
options.munkipkgsonly = launch_options.get(
|
||||
@@ -890,12 +924,7 @@ def main():
|
||||
# we need to delete AppleUpdates.plist so that other code doesn't
|
||||
# mistakenly alert for forced installs it isn't actually going to
|
||||
# install.
|
||||
appleupdates_plist = os.path.join(
|
||||
prefs.pref('ManagedInstallDir'), 'AppleUpdates.plist')
|
||||
try:
|
||||
os.unlink(appleupdates_plist)
|
||||
except OSError:
|
||||
pass
|
||||
appleupdates.clearAppleUpdateInfo()
|
||||
else:
|
||||
# check the normal preferences
|
||||
should_do_apple_updates = prefs.pref('InstallAppleSoftwareUpdates')
|
||||
@@ -954,6 +983,28 @@ def main():
|
||||
mustlogout = False
|
||||
notify_user = False
|
||||
force_action = None
|
||||
|
||||
if runtype == 'installatstartup':
|
||||
# turn off options.installonly; we need options.auto behavior from here
|
||||
# on out because if FileVault is active we may actually be logged in
|
||||
# at this point!
|
||||
options.installonly = False
|
||||
options.auto = True
|
||||
|
||||
if runtype == 'checkandinstallatstartup':
|
||||
# we're in bootstrap mode
|
||||
if not updatesavailable and appleupdatesavailable:
|
||||
# there are only Apple updates, but we might not be able to install
|
||||
# some
|
||||
if not appleupdates.installableUpdates():
|
||||
# there are no Apple updates we can actually install without
|
||||
# user assistance, so clear bootstrapping mode so we don't loop
|
||||
# endlessly
|
||||
try:
|
||||
bootstrapping.clear_bootstrap_mode()
|
||||
except bootstrapping.SetupError as err:
|
||||
display.display_error(err)
|
||||
|
||||
if updatesavailable or appleupdatesavailable:
|
||||
if options.installonly or options.logoutinstall:
|
||||
# just install
|
||||
|
||||
@@ -494,41 +494,52 @@ class AppleUpdates(object):
|
||||
display.display_info(' *Logout required')
|
||||
reports.report['LogoutRequired'] = True
|
||||
|
||||
def installable_updates(self):
|
||||
"""Returns a list of installable Apple updates.
|
||||
This may filter out updates that require a restart."""
|
||||
try:
|
||||
pl_dict = FoundationPlist.readPlist(self.apple_updates_plist)
|
||||
except FoundationPlist.FoundationPlistException:
|
||||
# can't read it or it doesn't exist, therefore no installable
|
||||
# updates
|
||||
return []
|
||||
apple_updates = pl_dict.get('AppleUpdates', [])
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple >= (10, 14):
|
||||
# in Mojave and Catalina (and perhaps beyond), it's too risky to
|
||||
# install OS or Security updates because softwareupdate is just
|
||||
# not reliable at this any longer. So filter out updates that
|
||||
# require a restart
|
||||
filtered_apple_updates = [
|
||||
item for item in apple_updates
|
||||
if item.get('RestartAction', 'None') == 'None'
|
||||
]
|
||||
return filtered_apple_updates
|
||||
return apple_updates
|
||||
|
||||
def install_apple_updates(self, only_unattended=False):
|
||||
"""Uses softwareupdate to install previously downloaded updates.
|
||||
|
||||
Returns:
|
||||
Boolean. True if a restart is needed after install, False otherwise.
|
||||
restart_action -- an integer indicating the action to take later:
|
||||
none, logout, restart, shutdown
|
||||
"""
|
||||
# disable Stop button if we are presenting GUI status
|
||||
if display.munkistatusoutput:
|
||||
munkistatus.hideStopButton()
|
||||
|
||||
self.shutdown_instead_of_restart = False
|
||||
# Get list of unattended_installs
|
||||
if only_unattended:
|
||||
msg = 'Installing unattended Apple Software Updates...'
|
||||
unattended_install_items, unattended_install_product_ids = \
|
||||
self.get_unattended_installs()
|
||||
# ensure that we don't restart for unattended installations
|
||||
restart_action = POSTACTION_NONE
|
||||
if not unattended_install_items:
|
||||
return False # didn't find any unattended installs
|
||||
else:
|
||||
msg = 'Installing available Apple Software Updates...'
|
||||
restart_action = self.restart_action_for_updates()
|
||||
|
||||
display.display_status_major(msg)
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
|
||||
installlist = self.software_update_info()
|
||||
remaining_apple_updates = []
|
||||
installresults = {'installed': [], 'download': []}
|
||||
|
||||
su_options = ['-i']
|
||||
|
||||
if only_unattended:
|
||||
# Append list of unattended_install items
|
||||
su_options.extend(unattended_install_items)
|
||||
msg = 'Installing unattended Apple Software Updates...'
|
||||
restart_action = POSTACTION_NONE
|
||||
unattended_install_product_ids = self.get_unattended_installs()
|
||||
# Filter installlist to only include items
|
||||
# which we're attempting to install
|
||||
filtered_installlist = [item for item in installlist
|
||||
@@ -537,17 +548,51 @@ class AppleUpdates(object):
|
||||
# record items we aren't planning to attempt to install
|
||||
remaining_apple_updates = [item for item in installlist
|
||||
if item not in filtered_installlist]
|
||||
# set the list of items to install to our newly-filted list
|
||||
installlist = filtered_installlist
|
||||
|
||||
elif os_version_tuple >= (10, 14):
|
||||
msg = ('Installing Apple Software Updates that do not require '
|
||||
'restart...')
|
||||
restart_action = POSTACTION_NONE
|
||||
# in Mojave and Catalina (and perhaps beyond), it's too risky to
|
||||
# install OS or Security updates because softwareupdate is just
|
||||
# not reliable at this any longer. So skip any updates that require
|
||||
# a restart -- users will need to install these using Apple's GUI
|
||||
# tools. We can still install other updates that don't require a
|
||||
# restart (for now)
|
||||
filtered_installlist = [
|
||||
item for item in installlist
|
||||
if item.get('RestartAction', 'None') == 'None'
|
||||
]
|
||||
# record items we aren't planning to attempt to install
|
||||
remaining_apple_updates = [item for item in installlist
|
||||
if item not in filtered_installlist]
|
||||
for item in remaining_apple_updates:
|
||||
display.display_debug1(
|
||||
"Skipping install of %s-%s because it requires a restart.",
|
||||
item['name'], item['version_to_install']
|
||||
)
|
||||
# set the list of items to install to our newly-filtered list
|
||||
installlist = filtered_installlist
|
||||
|
||||
else:
|
||||
# We're installing all available updates; add all their names
|
||||
for item in installlist:
|
||||
su_options.append(
|
||||
item['name'] + '-' + item['version_to_install'])
|
||||
msg = 'Installing available Apple Software Updates...'
|
||||
restart_action = self.restart_action_for_updates()
|
||||
|
||||
if not installlist:
|
||||
return POSTACTION_NONE # our list of items to install is empty
|
||||
|
||||
display.display_status_major(msg)
|
||||
|
||||
# Add the current (possibly filtered) installlist items to the
|
||||
# softwareupdate install options
|
||||
for item in installlist:
|
||||
su_options.append(
|
||||
item['name'] + '-' + item['version_to_install'])
|
||||
|
||||
# new in 10.11: '--no-scan' flag to tell softwareupdate to just install
|
||||
# and not rescan for available updates.
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple >= (10, 11):
|
||||
try:
|
||||
# attempt to fetch the apple catalog to confirm connectivity
|
||||
@@ -578,8 +623,10 @@ class AppleUpdates(object):
|
||||
su_start_date = NSDate.new()
|
||||
installresults = su_tool.run(su_options, catalog_url=catalog_url)
|
||||
retcode = installresults.get('exit_code', 0)
|
||||
self.shutdown_instead_of_restart = installresults.get(
|
||||
'post_action') == POSTACTION_SHUTDOWN
|
||||
self.shutdown_instead_of_restart = (
|
||||
installresults.get('post_action') == POSTACTION_SHUTDOWN or
|
||||
osutils.bridgeos_update_staged()
|
||||
)
|
||||
su_end_date = NSDate.new()
|
||||
|
||||
# get the items that were just installed from InstallHistory.plist
|
||||
@@ -741,9 +788,12 @@ class AppleUpdates(object):
|
||||
metadata_to_copy = ['blocking_applications',
|
||||
'description',
|
||||
'display_name',
|
||||
'force_install_after_date',
|
||||
'unattended_install',
|
||||
'RestartAction']
|
||||
# we support force_install_after_date only on macOS earlier than Mojave
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple < (10, 14):
|
||||
metadata_to_copy.append('force_install_after_date')
|
||||
|
||||
# Mapping of supported restart_actions to
|
||||
# equal or greater auxiliary actions
|
||||
@@ -797,17 +847,15 @@ class AppleUpdates(object):
|
||||
|
||||
def get_unattended_installs(self):
|
||||
"""Processes AppleUpdates.plist to return a list
|
||||
of NAME-VERSION formatted items and a list of product_ids
|
||||
which are eligible for unattended installation.
|
||||
of product_ids which are eligible for unattended installation.
|
||||
"""
|
||||
item_list = []
|
||||
product_ids = []
|
||||
try:
|
||||
pl_dict = FoundationPlist.readPlist(self.apple_updates_plist)
|
||||
except FoundationPlist.FoundationPlistException:
|
||||
display.display_error(
|
||||
'Error reading: %s', self.apple_updates_plist)
|
||||
return item_list, product_ids
|
||||
return product_ids
|
||||
apple_updates = pl_dict.get('AppleUpdates', [])
|
||||
for item in apple_updates:
|
||||
if (item.get('unattended_install') or
|
||||
@@ -819,14 +867,12 @@ class AppleUpdates(object):
|
||||
'blocking application(s) running.'
|
||||
% item['display_name'])
|
||||
continue
|
||||
install_item = item['name'] + '-' + item['version_to_install']
|
||||
item_list.append(install_item)
|
||||
product_ids.append(item['productKey'])
|
||||
else:
|
||||
display.display_detail(
|
||||
'Skipping install of %s because it\'s not unattended.'
|
||||
% item['display_name'])
|
||||
return item_list, product_ids
|
||||
return product_ids
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -74,6 +74,12 @@ def appleSoftwareUpdatesAvailable(forcecheck=False, suppresscheck=False,
|
||||
force_check=forcecheck, suppress_check=suppresscheck)
|
||||
|
||||
|
||||
def installableUpdates():
|
||||
"""Returns the list of installable updates, which might not include updates
|
||||
that require a restart"""
|
||||
return getAppleUpdatesInstance().installable_updates()
|
||||
|
||||
|
||||
def displayAppleUpdateInfo():
|
||||
"""Method for drop-in appleupdates replacement; see primary method docs."""
|
||||
getAppleUpdatesInstance().display_apple_update_info()
|
||||
|
||||
@@ -148,7 +148,7 @@ def can_attempt_auth_restart(have_password=False):
|
||||
(get_auth_restart_key(quiet=True) != '' or have_password))
|
||||
|
||||
|
||||
def perform_auth_restart(username=None, password=None):
|
||||
def perform_auth_restart(username=None, password=None, delayminutes=0):
|
||||
"""When called this will perform an authorized restart. Before trying
|
||||
to perform an authorized restart it checks to see if the machine supports
|
||||
the feature. If supported it will look for the defined plist containing
|
||||
@@ -169,14 +169,23 @@ def perform_auth_restart(username=None, password=None):
|
||||
if username:
|
||||
keys['Username'] = username
|
||||
inputplist = FoundationPlist.writePlistToString(keys)
|
||||
display.display_info('Attempting an Authorized Restart now...')
|
||||
if delayminutes == 0:
|
||||
display.display_info('Attempting an Authorized Restart now...')
|
||||
else:
|
||||
display.display_info('Configuring a delayed Authorized Restart...')
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple >= (10, 12):
|
||||
cmd = ['/usr/bin/fdesetup', 'authrestart',
|
||||
'-delayminutes', str(delayminutes), '-inputplist']
|
||||
else:
|
||||
cmd = ['/usr/bin/fdesetup', 'authrestart', '-inputplist']
|
||||
proc = subprocess.Popen(
|
||||
['/usr/bin/fdesetup', 'authrestart', '-inputplist'],
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
err = proc.communicate(input=inputplist)[1].decode('UTF-8')
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple >= (10, 12) and 'System is being restarted' in err:
|
||||
return True
|
||||
if err:
|
||||
|
||||
@@ -27,6 +27,7 @@ import os
|
||||
import select
|
||||
import socket
|
||||
|
||||
from .. import prefs
|
||||
from ..wrappers import writePlistToString
|
||||
|
||||
AUTHRESTARTD_SOCKET = "/var/run/authrestartd"
|
||||
@@ -115,6 +116,13 @@ class AuthRestartClient(object):
|
||||
if not result.startswith('OK'):
|
||||
raise AuthRestartClientError(result)
|
||||
|
||||
def setup_delayed_authrestart(self, delayminutes=-1):
|
||||
'''Sets up a delayed auth restart'''
|
||||
request = {'task': 'delayed_authrestart', 'delayminutes': delayminutes}
|
||||
result = self.process(request)
|
||||
if not result.startswith('OK'):
|
||||
raise AuthRestartClientError(result)
|
||||
|
||||
|
||||
# Higher-level wrapper functions that swallow AuthRestartClientErrors
|
||||
def fv_is_active():
|
||||
@@ -172,15 +180,29 @@ def restart():
|
||||
return False
|
||||
|
||||
|
||||
def setup_delayed_authrestart():
|
||||
'''Sets up a delayed authrestart.
|
||||
Returns boolean to indicate success/failure'''
|
||||
try:
|
||||
AuthRestartClient().setup_delayed_authrestart()
|
||||
return True
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def test():
|
||||
'''A function for doing some basic testing'''
|
||||
import getpass
|
||||
import pwd
|
||||
from ..wrappers import get_input
|
||||
|
||||
print('PerformAuthRestarts preference is: %s'
|
||||
% prefs.pref('PerformAuthRestarts'))
|
||||
print('FileVault is active: %s' % fv_is_active())
|
||||
print('Recovery key is present: %s' % verify_recovery_key_present())
|
||||
username = pwd.getpwuid(os.getuid()).pw_name
|
||||
if username == 'root':
|
||||
username = get_input('Enter name of FV-enabled user: ')
|
||||
print('%s is FV user: %s' % (username, verify_user(username)))
|
||||
password = getpass.getpass('Enter password: ')
|
||||
if password:
|
||||
@@ -191,6 +213,10 @@ def test():
|
||||
else:
|
||||
print('store_password failed')
|
||||
print('Can attempt auth restart: %s' % verify_can_attempt_auth_restart())
|
||||
answer = get_input('Test setup of delayed auth restart (y/n)? ')
|
||||
if answer.lower().startswith('y'):
|
||||
print('Successfully set up delayed authrestart: %s'
|
||||
% setup_delayed_authrestart())
|
||||
answer = get_input('Test auth restart (y/n)? ')
|
||||
if answer.lower().startswith('y'):
|
||||
print('Attempting auth restart...')
|
||||
|
||||
@@ -177,7 +177,7 @@ def mount_points_for_disk_image(dmgpath):
|
||||
|
||||
|
||||
def mountdmg(dmgpath, use_shadow=False, use_existing_mounts=False,
|
||||
random_mountpoint=True):
|
||||
random_mountpoint=True, skip_verification=False):
|
||||
"""
|
||||
Attempts to mount the dmg at dmgpath
|
||||
and returns a list of mountpoints
|
||||
@@ -205,6 +205,8 @@ def mountdmg(dmgpath, use_shadow=False, use_existing_mounts=False,
|
||||
cmd.extend(['-mountRandom', '/tmp'])
|
||||
if use_shadow:
|
||||
cmd.append('-shadow')
|
||||
if skip_verification:
|
||||
cmd.append('-noverify')
|
||||
proc = subprocess.Popen(cmd,
|
||||
bufsize=-1, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
|
||||
@@ -70,6 +70,9 @@ def convertIconToPNG(icon_path, destination_path,
|
||||
try:
|
||||
properties = CGImageSourceCopyPropertiesAtIndex(
|
||||
image_source, index, None)
|
||||
# perform not empty check for properties to prevent crash as CGImageSourceCopyPropertiesAtIndex sometimes fails in 10.15.4 and above
|
||||
if not properties:
|
||||
return False
|
||||
dpi = int(properties.get(kCGImagePropertyDPIHeight, 0))
|
||||
height = int(properties.get(kCGImagePropertyPixelHeight, 0))
|
||||
if (not candidate or
|
||||
|
||||
@@ -56,7 +56,7 @@ try:
|
||||
_ = xrange # pylint: disable=xrange-builtin
|
||||
except NameError:
|
||||
# no xrange in Python 3
|
||||
xrange = range
|
||||
xrange = range # pylint: disable=redefined-builtin,invalid-name
|
||||
|
||||
# Always ignore these directories when discovering applications.
|
||||
APP_DISCOVERY_EXCLUSION_DIRS = set([
|
||||
@@ -548,9 +548,9 @@ def get_version():
|
||||
return vers
|
||||
|
||||
|
||||
def get_hardware_info():
|
||||
'''Uses system profiler to get hardware info for this machine'''
|
||||
cmd = ['/usr/sbin/system_profiler', 'SPHardwareDataType', '-xml']
|
||||
def get_sp_data(data_type):
|
||||
'''Uses system profiler to get info of data_type for this machine'''
|
||||
cmd = ['/usr/sbin/system_profiler', data_type, '-xml']
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@@ -560,12 +560,22 @@ def get_hardware_info():
|
||||
# system_profiler xml is an array
|
||||
sp_dict = plist[0]
|
||||
items = sp_dict['_items']
|
||||
sp_hardware_dict = items[0]
|
||||
return sp_hardware_dict
|
||||
sp_items_dict = items[0]
|
||||
return sp_items_dict
|
||||
except BaseException:
|
||||
return {}
|
||||
|
||||
|
||||
def get_hardware_info():
|
||||
'''Uses system profiler to get hardware info for this machine'''
|
||||
return get_sp_data('SPHardwareDataType')
|
||||
|
||||
|
||||
def get_ibridge_info():
|
||||
'''Uses system profiler to get iBridge info for this machine'''
|
||||
return get_sp_data('SPiBridgeDataType')
|
||||
|
||||
|
||||
def get_ip_addresses(kind):
|
||||
'''Uses system profiler to get active IP addresses for this machine
|
||||
kind must be one of 'IPv4' or 'IPv6' '''
|
||||
@@ -655,7 +665,9 @@ def getMachineFacts():
|
||||
machine['ipv4_address'] = get_ip_addresses('IPv4')
|
||||
machine['ipv6_address'] = get_ip_addresses('IPv6')
|
||||
machine['serial_number'] = hardware_info.get('serial_number', 'UNKNOWN')
|
||||
|
||||
ibridge_info = get_ibridge_info()
|
||||
machine['ibridge_model_name'] = ibridge_info.get(
|
||||
'ibridge_model_name', 'NO IBRIDGE CHIP')
|
||||
if machine['arch'] == 'x86_64':
|
||||
machine['x86_64_capable'] = True
|
||||
elif machine['arch'] == 'i386':
|
||||
|
||||
@@ -167,7 +167,8 @@ def handle_apple_package_install(item, itempath):
|
||||
mount_with_shadow = suppress_bundle_relocation
|
||||
# we need to mount the diskimage as read/write to be able to
|
||||
# modify the package to suppress bundle relocation
|
||||
mountpoints = dmgutils.mountdmg(itempath, use_shadow=mount_with_shadow)
|
||||
mountpoints = dmgutils.mountdmg(
|
||||
itempath, use_shadow=mount_with_shadow, skip_verification=True)
|
||||
if mountpoints == []:
|
||||
display.display_error(
|
||||
"No filesystems mounted from %s", item["installer_item"])
|
||||
|
||||
@@ -106,7 +106,8 @@ def create_missing_dirs(destpath):
|
||||
def remove_quarantine_from_item(some_path):
|
||||
'''Removes com.apple.quarantine from some_path'''
|
||||
try:
|
||||
if "com.apple.quarantine" in xattr.xattr(some_path).list():
|
||||
if ("com.apple.quarantine" in
|
||||
xattr.xattr(some_path).list(options=xattr.XATTR_NOFOLLOW)):
|
||||
xattr.xattr(some_path).remove("com.apple.quarantine",
|
||||
options=xattr.XATTR_NOFOLLOW)
|
||||
except BaseException as err:
|
||||
@@ -167,6 +168,50 @@ def validate_source_and_destination(mountpoint, item):
|
||||
return (0, source_itempath, full_destpath)
|
||||
|
||||
|
||||
def get_size(pathname):
|
||||
'''Recursively gets size of pathname in bytes'''
|
||||
if os.path.isdir(pathname):
|
||||
total_size = 0
|
||||
for dirpath, _, filenames in os.walk(pathname):
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
# skip if it is symbolic link
|
||||
if not os.path.islink(filepath):
|
||||
total_size += os.path.getsize(filepath)
|
||||
return total_size
|
||||
elif os.path.isfile(pathname):
|
||||
return os.path.getsize(pathname)
|
||||
return 0
|
||||
|
||||
|
||||
def ditto_with_progress(source_path, dest_path):
|
||||
'''Uses ditto to copy an item and provides progress output'''
|
||||
source_size = get_size(source_path)
|
||||
total_bytes_copied = 0
|
||||
|
||||
cmd = ["/usr/bin/ditto", "-V", "--noqtn", source_path, dest_path]
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = proc.stdout.readline().decode('UTF-8')
|
||||
if not output and (proc.poll() != None):
|
||||
break
|
||||
words = output.rstrip('\n').split()
|
||||
if len(words) > 1 and words[1] == "bytes":
|
||||
try:
|
||||
bytes_copied = int(words[0])
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
total_bytes_copied += bytes_copied
|
||||
display.display_percent_done(total_bytes_copied, source_size)
|
||||
|
||||
return proc.returncode
|
||||
|
||||
|
||||
def copy_items_from_mountpoint(mountpoint, itemlist):
|
||||
'''copies items from the mountpoint to the startup disk
|
||||
Returns 0 if no issues; some error code otherwise.
|
||||
@@ -191,8 +236,9 @@ def copy_items_from_mountpoint(mountpoint, itemlist):
|
||||
temp_destination_dir, os.path.basename(destination_path))
|
||||
# copy the file or directory, removing the quarantine xattr and
|
||||
# preserving HFS+ compression
|
||||
retcode = subprocess.call(["/usr/bin/ditto", "--noqtn",
|
||||
source_path, temp_destination_path])
|
||||
#retcode = subprocess.call(["/usr/bin/ditto", "--noqtn",
|
||||
# source_path, temp_destination_path])
|
||||
retcode = ditto_with_progress(source_path, temp_destination_path)
|
||||
if retcode:
|
||||
display.display_error(
|
||||
"Error copying %s to %s", source_path, temp_destination_path)
|
||||
@@ -277,7 +323,7 @@ def copy_from_dmg(dmgpath, itemlist):
|
||||
|
||||
display.display_status_minor(
|
||||
'Mounting disk image %s', os.path.basename(dmgpath))
|
||||
mountpoints = dmgutils.mountdmg(dmgpath)
|
||||
mountpoints = dmgutils.mountdmg(dmgpath, skip_verification=True)
|
||||
if mountpoints:
|
||||
mountpoint = mountpoints[0]
|
||||
retcode = copy_items_from_mountpoint(mountpoint, itemlist)
|
||||
|
||||
@@ -198,7 +198,7 @@ def _run_installer(cmd, env_vars, packagename):
|
||||
retcode = job.returncode()
|
||||
if retcode != 0:
|
||||
# append stdout to our installer output
|
||||
installeroutput.extend(job.stderr.read().splitlines())
|
||||
installeroutput.extend(job.stderr.read().decode("UTF-8").splitlines())
|
||||
display.display_status_minor(
|
||||
"Install of %s failed with return code %s" % (packagename, retcode))
|
||||
display.display_error("-"*78)
|
||||
@@ -277,7 +277,8 @@ def installall(dirpath, options=None):
|
||||
itempath = os.path.join(dirpath, item)
|
||||
if pkgutils.hasValidDiskImageExt(item):
|
||||
display.display_info("Mounting disk image %s" % item)
|
||||
mountpoints = dmgutils.mountdmg(itempath, use_shadow=True)
|
||||
mountpoints = dmgutils.mountdmg(
|
||||
itempath, use_shadow=True, skip_verification=True)
|
||||
if mountpoints == []:
|
||||
display.display_error("No filesystems mounted from %s", item)
|
||||
return (retcode, restartflag)
|
||||
|
||||
@@ -43,7 +43,7 @@ try:
|
||||
_ = xrange # pylint: disable=xrange-builtin
|
||||
except NameError:
|
||||
# no xrange in Python 3
|
||||
xrange = range
|
||||
xrange = range # pylint: disable=redefined-builtin,invalid-name
|
||||
|
||||
|
||||
# This many hours before a force install deadline, start notifying the user.
|
||||
@@ -239,10 +239,11 @@ def force_install_package_check():
|
||||
|
||||
managed_install_dir = prefs.pref('ManagedInstallDir')
|
||||
|
||||
installinfo_types = {
|
||||
'InstallInfo.plist' : 'managed_installs',
|
||||
'AppleUpdates.plist': 'AppleUpdates'
|
||||
}
|
||||
installinfo_types = {'InstallInfo.plist': 'managed_installs'}
|
||||
if (prefs.pref('InstallAppleSoftwareUpdates') or
|
||||
prefs.pref('AppleSoftwareUpdatesOnly')):
|
||||
# only consider Apple updates if the prefs say it's OK
|
||||
installinfo_types['AppleUpdates.plist'] = 'AppleUpdates'
|
||||
|
||||
now = NSDate.date()
|
||||
now_xhours = NSDate.dateWithTimeIntervalSinceNow_(
|
||||
|
||||
@@ -29,6 +29,7 @@ from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
|
||||
@@ -139,7 +140,13 @@ class Job(object):
|
||||
def __init__(self, cmd, environment_vars=None,
|
||||
job_label=None, cleanup_at_exit=True):
|
||||
'''Initialize our launchd job'''
|
||||
tmpdir = osutils.tmpdir()
|
||||
if cleanup_at_exit:
|
||||
# safe to use the same tmpdir as the calling tool
|
||||
# (usually managedsoftwareupdate) so it will get cleaned up
|
||||
tmpdir = osutils.tmpdir()
|
||||
else:
|
||||
# need to create our own tmpdir; may not be cleaned up
|
||||
tmpdir = tempfile.mkdtemp(prefix='munki.launchd-', dir='/tmp')
|
||||
|
||||
# label this job
|
||||
self.label = job_label or 'com.googlecode.munki.' + str(uuid.uuid1())
|
||||
@@ -174,6 +181,7 @@ class Job(object):
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
err = proc.communicate()[1].decode('UTF-8')
|
||||
if proc.returncode:
|
||||
raise LaunchdJobException(err)
|
||||
|
||||
@@ -110,6 +110,48 @@ def get_os_version(app_path):
|
||||
return ''
|
||||
|
||||
|
||||
def setup_authrestart_if_applicable():
|
||||
'''Sets up the ability to do an authrestart if applicable'''
|
||||
# ask authrestartd if we can do an auth restart, or look for a recovery
|
||||
# key (via munkilib.authrestart methods)
|
||||
if (authrestartd.verify_can_attempt_auth_restart() or
|
||||
authrestart.can_attempt_auth_restart()):
|
||||
display.display_info(
|
||||
'FileVault is active and we can do an authrestart')
|
||||
#os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if False: # was: os_version_tuple >= (10, 12):
|
||||
# setup delayed auth restart so that when startosinstall does a
|
||||
# restart, it completes without user credentials
|
||||
display.display_info('Setting up delayed authrestart...')
|
||||
authrestartd.setup_delayed_authrestart()
|
||||
# make sure the special secret InstallAssistant preference is not
|
||||
# set
|
||||
CFPreferencesSetValue(
|
||||
'IAQuitInsteadOfReboot', None, '.GlobalPreferences',
|
||||
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
|
||||
else:
|
||||
#
|
||||
# set an undocumented preference to tell the osinstaller
|
||||
# process to exit instead of restart
|
||||
# this is the equivalent of:
|
||||
# `defaults write /Library/Preferences/.GlobalPreferences
|
||||
# IAQuitInsteadOfReboot -bool YES`
|
||||
#
|
||||
# This preference is referred to in a framework inside the
|
||||
# Install macOS.app:
|
||||
# Contents/Frameworks/OSInstallerSetup.framework/Versions/A/
|
||||
# Frameworks/OSInstallerSetupInternal.framework/Versions/A/
|
||||
# OSInstallerSetupInternal
|
||||
#
|
||||
# It might go away in future versions of the macOS installer.
|
||||
#
|
||||
display.display_info(
|
||||
'Configuring startosinstall to quit instead of restart...')
|
||||
CFPreferencesSetValue(
|
||||
'IAQuitInsteadOfReboot', True, '.GlobalPreferences',
|
||||
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
|
||||
|
||||
|
||||
class StartOSInstallError(Exception):
|
||||
'''Exception to raise if starting the macOS install fails'''
|
||||
pass
|
||||
@@ -130,13 +172,9 @@ class StartOSInstallRunner(object):
|
||||
done setting up the macOS install and is ready and waiting to reboot'''
|
||||
display.display_debug1('Got SIGUSR1 from startosinstall')
|
||||
self.got_sigusr1 = True
|
||||
# do cleanup, record-keeping, notifications
|
||||
if self.installinfo and 'postinstall_script' in self.installinfo:
|
||||
# run the postinstall_script
|
||||
dummy_retcode = scriptutils.run_embedded_script(
|
||||
'postinstall_script', self.installinfo)
|
||||
if self.finishing_tasks:
|
||||
self.finishing_tasks()
|
||||
|
||||
setup_authrestart_if_applicable()
|
||||
|
||||
# set Munki to run at boot after the OS upgrade is complete
|
||||
try:
|
||||
bootstrapping.set_bootstrap_mode()
|
||||
@@ -144,34 +182,22 @@ class StartOSInstallRunner(object):
|
||||
display.display_error(
|
||||
'Could not set up Munki to run after OS upgrade is complete: '
|
||||
'%s', err)
|
||||
|
||||
# do cleanup, record-keeping, notifications
|
||||
if self.installinfo and 'postinstall_script' in self.installinfo:
|
||||
# run the postinstall_script
|
||||
dummy_retcode = scriptutils.run_embedded_script(
|
||||
'postinstall_script', self.installinfo)
|
||||
if self.finishing_tasks:
|
||||
self.finishing_tasks()
|
||||
|
||||
if pkgutils.hasValidDiskImageExt(self.installer):
|
||||
# remove the diskimage to free up more space for the actual install
|
||||
try:
|
||||
os.unlink(self.installer)
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
# ask authrestartd if we can do an auth restart, or look for a recovery
|
||||
# key (via munkilib.authrestart methods)
|
||||
if (authrestartd.verify_can_attempt_auth_restart() or
|
||||
authrestart.can_attempt_auth_restart()):
|
||||
#
|
||||
# set a secret preference to tell the osinstaller process to exit
|
||||
# instead of restart
|
||||
# this is the equivalent of:
|
||||
# `defaults write /Library/Preferences/.GlobalPreferences
|
||||
# IAQuitInsteadOfReboot -bool YES`
|
||||
#
|
||||
# This preference is referred to in a framework inside the
|
||||
# Install macOS.app:
|
||||
# Contents/Frameworks/OSInstallerSetup.framework/Versions/A/
|
||||
# Frameworks/OSInstallerSetupInternal.framework/Versions/A/
|
||||
# OSInstallerSetupInternal
|
||||
#
|
||||
# It might go away in future versions of the macOS installer.
|
||||
#
|
||||
CFPreferencesSetValue(
|
||||
'IAQuitInsteadOfReboot', True, '.GlobalPreferences',
|
||||
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
|
||||
|
||||
# now tell startosinstall it's OK to proceed
|
||||
subprocess.call(['/usr/bin/killall', '-SIGUSR1', 'startosinstall'])
|
||||
|
||||
@@ -217,7 +243,7 @@ class StartOSInstallRunner(object):
|
||||
'middle of a CoreStorage conversion.')
|
||||
|
||||
if self.installinfo and 'preinstall_script' in self.installinfo:
|
||||
# run the postinstall_script
|
||||
# run the preinstall_script
|
||||
retcode = scriptutils.run_embedded_script(
|
||||
'preinstall_script', self.installinfo)
|
||||
if retcode:
|
||||
@@ -339,13 +365,14 @@ class StartOSInstallRunner(object):
|
||||
startosinstall_output.append(info_output)
|
||||
|
||||
# parse output for useful progress info
|
||||
msg = info_output.rstrip('\n')
|
||||
msg = info_output.strip()
|
||||
if msg.startswith('Preparing to '):
|
||||
display.display_status_minor(msg)
|
||||
elif msg.startswith('Preparing '):
|
||||
elif msg.startswith(('Preparing ', 'Preparing: ')):
|
||||
# percent-complete messages
|
||||
percent_str = msg.split()[-1].rstrip('%.')
|
||||
try:
|
||||
percent = int(float(msg[10:].rstrip().rstrip('.')))
|
||||
percent = int(float(percent_str))
|
||||
except ValueError:
|
||||
percent = -1
|
||||
display.display_percent_done(percent, 100)
|
||||
@@ -410,9 +437,10 @@ class StartOSInstallRunner(object):
|
||||
CFPreferencesSetValue(
|
||||
'IAQuitInsteadOfReboot', None, '.GlobalPreferences',
|
||||
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
|
||||
# attempt to do an auth restart, or regular restart
|
||||
# attempt to do an auth restart, or regular restart, or shutdown
|
||||
if not authrestartd.restart():
|
||||
authrestart.do_authorized_or_normal_restart()
|
||||
authrestart.do_authorized_or_normal_restart(
|
||||
shutdown=osutils.bridgeos_update_staged())
|
||||
else:
|
||||
raise StartOSInstallError(
|
||||
'startosinstall did not complete successfully. '
|
||||
@@ -439,9 +467,21 @@ def get_catalog_info(mounted_dmgpath):
|
||||
# "14.3GB of available storage to perform upgrade"
|
||||
# http://www.apple.com/macos/how-to-upgrade/
|
||||
installed_size = int(14.3 * 1024 * 1024)
|
||||
elif vers.startswith('10.14'):
|
||||
# Mojave:
|
||||
# "12.5GB of available storage space, or up to 18.5GB of
|
||||
# storage space when upgrading from OS X Yosemite or earlier."
|
||||
# https://support.apple.com/en-us/HT210190
|
||||
installed_size = int(18.5 * 1024 * 1024)
|
||||
elif vers.startswith('10.15'):
|
||||
# Catalina:
|
||||
# "12.5GB of available storage space, or up to 18.5GB of
|
||||
# storage space when upgrading from OS X Yosemite or earlier."
|
||||
# https://support.apple.com/en-us/HT201475
|
||||
installed_size = int(18.5 * 1024 * 1024)
|
||||
else:
|
||||
# will need to modify for future macOS releases
|
||||
installed_size = int(14.3 * 1024 * 1024)
|
||||
installed_size = int(18.5 * 1024 * 1024)
|
||||
return {'RestartAction': 'RequireRestart',
|
||||
'apple_item': True,
|
||||
'description': description,
|
||||
|
||||
@@ -28,6 +28,8 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
|
||||
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
@@ -36,6 +38,7 @@ from SystemConfiguration import SCDynamicStoreCopyConsoleUser
|
||||
# pylint: enable=E0611
|
||||
|
||||
from . import display
|
||||
from . import munkilog
|
||||
|
||||
# we use lots of camelCase-style names. Deal with it.
|
||||
# pylint: disable=C0103
|
||||
@@ -184,5 +187,44 @@ def osascript(osastring):
|
||||
return u''
|
||||
|
||||
|
||||
def bridgeos_update_staged():
|
||||
'''Checks an undocumented nvram variable to see if a bridgeOS update
|
||||
has been staged. If so, we should shut down instead of restart.
|
||||
Returns a boolean.'''
|
||||
cmd = ["/usr/sbin/nvram", "BOSUpdateStarted"]
|
||||
proc = subprocess.Popen(cmd,
|
||||
shell=False,
|
||||
bufsize=-1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
output = proc.communicate()[0].decode('UTF-8')
|
||||
if proc.returncode == 0:
|
||||
munkilog.log("nvram output: %s" % output)
|
||||
lines = output.splitlines()
|
||||
parts = lines[0].split()
|
||||
try:
|
||||
timestamp = int(parts[1].split(',')[0])
|
||||
now = int(time.time())
|
||||
seconds_ago = now - timestamp
|
||||
if seconds_ago < 60 * 60:
|
||||
munkilog.log(
|
||||
"bridgeOS update staged %s seconds ago; shutdown required"
|
||||
% seconds_ago)
|
||||
return True
|
||||
#else
|
||||
munkilog.log(
|
||||
"bridgeOS update %s seconds ago; too long ago to trust"
|
||||
% seconds_ago)
|
||||
return False
|
||||
except (IndexError, ValueError):
|
||||
munkilog.log(
|
||||
"unexpected nvram output, can't detect bridgeos update")
|
||||
return False
|
||||
#else
|
||||
munkilog.log("No bridgeOS update staged")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('This is a library of support tools for the Munki Suite.')
|
||||
|
||||
@@ -49,6 +49,7 @@ DEFAULT_INSECURE_REPO_URL = 'http://munki/repo'
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
'AdditionalHttpHeaders': None,
|
||||
'AggressiveUpdateNotificationDays': 14,
|
||||
'AppleSoftwareUpdatesOnly': False,
|
||||
'CatalogURL': None,
|
||||
'ClientCertificatePath': None,
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.1.0</string>
|
||||
<string>5.0.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
+46
-2
@@ -68,6 +68,8 @@ class RepoCleaner(object):
|
||||
self.manifest_items = set()
|
||||
self.manifest_items_with_versions = set()
|
||||
self.pkginfodb = {}
|
||||
self.referenced_pkgs = set()
|
||||
self.orphaned_pkgs = []
|
||||
self.required_items = set()
|
||||
self.pkginfo_count = 0
|
||||
self.items_to_delete = []
|
||||
@@ -103,7 +105,7 @@ class RepoCleaner(object):
|
||||
return str(size_in_bytes) + " bytes"
|
||||
return str(round(size_in_bytes/float(limit/2**10), 1)) + suffix
|
||||
|
||||
count = 0
|
||||
count = len(self.orphaned_pkgs)
|
||||
pkginfo_total_size = 0
|
||||
pkg_total_size = 0
|
||||
for item in self.items_to_delete:
|
||||
@@ -196,6 +198,11 @@ class RepoCleaner(object):
|
||||
uninstallpkgpath = pkginfo.get('uninstaller_item_location', '')
|
||||
uninstallpkgsize = pkginfo.get('uninstaller_item_size', 0) * 1024
|
||||
|
||||
if pkgpath:
|
||||
self.referenced_pkgs.add(pkgpath)
|
||||
if uninstallpkgpath:
|
||||
self.referenced_pkgs.add(uninstallpkgpath)
|
||||
|
||||
# track required items; if these are in "Foo-1.0" format, we need
|
||||
# to note these so we don't delete the specific referenced version
|
||||
if 'requires' in pkginfo:
|
||||
@@ -261,6 +268,20 @@ class RepoCleaner(object):
|
||||
})
|
||||
self.pkginfo_count += 1
|
||||
|
||||
def find_orphaned_pkgs(self):
|
||||
'''Finds installer items that are not referred to by any pkginfo file'''
|
||||
print('Analyzing installer items...')
|
||||
try:
|
||||
pkgs_list = self.repo.itemlist('pkgs')
|
||||
except munkirepo.RepoError as err:
|
||||
self.errors.append(
|
||||
"Repo error getting list of pkgs: %s"
|
||||
% unicode_or_str(err))
|
||||
pkgs_list = []
|
||||
for pkg in pkgs_list:
|
||||
if pkg not in self.referenced_pkgs:
|
||||
self.orphaned_pkgs.append(pkg)
|
||||
|
||||
def find_cleanup_items(self):
|
||||
'''Using the info on manifests and pkgsinfo, find items to clean up.
|
||||
Populates self.items_to_delete: a list of pkginfo items to remove,
|
||||
@@ -325,6 +346,14 @@ class RepoCleaner(object):
|
||||
print("(%s)" % item['resource_identifier'])
|
||||
if print_this:
|
||||
print()
|
||||
if self.orphaned_pkgs:
|
||||
print('The following pkgs are not referred to by any pkginfo '
|
||||
'item:')
|
||||
for pkg in self.orphaned_pkgs:
|
||||
print("\t%s" % pkg)
|
||||
if print_this:
|
||||
print()
|
||||
|
||||
|
||||
print("Total pkginfo items: %s" % self.pkginfo_count)
|
||||
print("Item variants: %s" % len(list(self.pkginfodb.keys())))
|
||||
@@ -333,6 +362,10 @@ class RepoCleaner(object):
|
||||
print("pkgs to delete: %s" % pkg_count)
|
||||
print("pkginfo space savings: %s" % pkginfo_size)
|
||||
print("pkg space savings: %s" % pkg_size)
|
||||
if len(self.orphaned_pkgs):
|
||||
print(" "
|
||||
"(Unknown additional pkg space savings from %s orphaned pkgs)"
|
||||
% len(self.orphaned_pkgs))
|
||||
|
||||
if self.errors:
|
||||
print("\nErrors encountered when processing repo:\n",
|
||||
@@ -342,6 +375,7 @@ class RepoCleaner(object):
|
||||
|
||||
def delete_items(self):
|
||||
'''Deletes items from the repo'''
|
||||
# remove old pkginfo and referenced pkgs
|
||||
for item in self.items_to_delete:
|
||||
if 'resource_identifier' in item:
|
||||
print('Removing %s' % item['resource_identifier'],
|
||||
@@ -366,6 +400,15 @@ class RepoCleaner(object):
|
||||
self.repo.delete(pkg_to_remove)
|
||||
except munkirepo.RepoError as err:
|
||||
print(unicode_or_str(err), file=sys.stderr)
|
||||
# remove orphaned pkgs
|
||||
for pkg in self.orphaned_pkgs:
|
||||
pkg_to_remove = os.path.join('pkgs', pkg)
|
||||
print('Removing %s' % pkg_to_remove)
|
||||
try:
|
||||
self.repo.delete(pkg_to_remove)
|
||||
except munkirepo.RepoError as err:
|
||||
print(unicode_or_str(err), file=sys.stderr)
|
||||
|
||||
|
||||
def make_catalogs(self):
|
||||
"""Calls makecatalogs to rebuild our catalogs"""
|
||||
@@ -405,8 +448,9 @@ class RepoCleaner(object):
|
||||
'''Clean our repo!'''
|
||||
self.analyze_manifests()
|
||||
self.analyze_pkgsinfo()
|
||||
self.find_orphaned_pkgs()
|
||||
self.find_cleanup_items()
|
||||
if self.items_to_delete:
|
||||
if self.items_to_delete or self.orphaned_pkgs:
|
||||
print()
|
||||
if not self.options.auto:
|
||||
answer = get_input(
|
||||
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
|
||||
# relaunch MSC.app if it was running before we upgraded it
|
||||
MSC_APP="/Applications/Managed Software Center.app"
|
||||
|
||||
if [ "$3" == "/" ]; then
|
||||
if [ -e /tmp/com.googlecode.munki.relaunch_msc_app ] ; then
|
||||
FRONTMOST=$(cat /tmp/com.googlecode.munki.relaunch_msc_app)
|
||||
rm /tmp/com.googlecode.munki.relaunch_msc_app
|
||||
if [ "$FRONTMOST" == "frontmost" ] ; then
|
||||
OPENCMD="open -a"
|
||||
else
|
||||
OPENCMD="open -g -a"
|
||||
fi
|
||||
CONSOLEUSER=$(who | grep console | cut -d" " -f1)
|
||||
if [ "$CONSOLEUSER" == "" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
CONSOLE_UID=$(id -u "$CONSOLEUSER")
|
||||
os_major_version=$(sw_vers -productVersion | cut -d. -f2)
|
||||
for pid_uid in $(ps -axo pid,uid,args | grep -i "[l]oginwindow.app" | awk '{print $1 "," $2}'); do
|
||||
pid=$(echo $pid_uid | cut -d, -f1)
|
||||
uid=$(echo $pid_uid | cut -d, -f2)
|
||||
if [ "$uid" == "$CONSOLE_UID" ] ; then
|
||||
if [[ $os_major_version -lt 10 ]] ; then
|
||||
launchctl bsexec "$pid" chroot -u "$uid" / $OPENCMD "$MSC_APP"
|
||||
exit 0
|
||||
else
|
||||
launchctl asuser "$uid" $OPENCMD "$MSC_APP"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
@@ -1,13 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
# remove any Managed Software Update.app bundle since the installer
|
||||
# won't replace a bundle with a symlink, leading to yucky stuff like
|
||||
# "/Applications/Utilities/Managed Software Update.app 1"
|
||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
|
||||
if [ -d "$3/Applications/Utilities/Managed Software Update.app" ] ; then
|
||||
/bin/rm -r "$3/Applications/Utilities/Managed Software Update.app"
|
||||
fi
|
||||
MSC_APP_NAME="Managed Software Center"
|
||||
|
||||
if [ -d "$3/Applications/Utilities/Managed Software Update.app 1" ] ; then
|
||||
/bin/rm -r "$3/Applications/Utilities/Managed Software Update.app 1"
|
||||
# installing on the current boot volume? kill MSC.app if it's running
|
||||
if [ "$3" == "/" ]; then
|
||||
CONSOLEUSER=$(who | grep console | cut -d" " -f1)
|
||||
if [ "$CONSOLEUSER" != "" ] ; then
|
||||
# is the console user running MSC.app and is it in front?
|
||||
FRONT_APPID=$(lsappinfo info -only bundleid $(lsappinfo front) | cut -d= -f2 | cut -d\" -f2)
|
||||
if [ "$FRONT_APPID" == "com.googlecode.munki.ManagedSoftwareCenter" ] ; then
|
||||
# leave a flag that tells us to relaunch and bring to front
|
||||
echo "frontmost" >> /tmp/com.googlecode.munki.relaunch_msc_app
|
||||
else
|
||||
# is the console user running MSC.app at all?
|
||||
killall -s -u "$CONSOLEUSER" "$MSC_APP_NAME" &>/dev/null
|
||||
if [ $? -eq 0 ] ; then
|
||||
# leave a flag that tells us to relaunch and not bring to front
|
||||
echo "not frontmost" >> /tmp/com.googlecode.munki.relaunch_msc_app
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# kill all instances of Managed Software Center.app
|
||||
killall "$MSC_APP_NAME" &>/dev/null
|
||||
exit 0
|
||||
fi
|
||||
@@ -406,7 +406,7 @@ mkdir -p "$ADMINROOT/usr/local/munki"
|
||||
chmod -R 755 "$ADMINROOT/usr"
|
||||
# Copy command line admin utilities.
|
||||
# edit this if list of tools changes!
|
||||
for TOOL in makecatalogs makepkginfo manifestutil munkiimport iconimporter
|
||||
for TOOL in makecatalogs makepkginfo manifestutil munkiimport iconimporter repoclean
|
||||
do
|
||||
cp -X "$MUNKIROOT/code/client/$TOOL" "$ADMINROOT/usr/local/munki/" 2>&1
|
||||
done
|
||||
|
||||
@@ -418,7 +418,7 @@ mkdir -p "$ADMINROOT/usr/local/munki"
|
||||
chmod -R 755 "$ADMINROOT/usr"
|
||||
# Copy command line admin utilities.
|
||||
# edit this if list of tools changes!
|
||||
for TOOL in makecatalogs makepkginfo manifestutil munkiimport iconimporter
|
||||
for TOOL in makecatalogs makepkginfo manifestutil munkiimport iconimporter repoclean
|
||||
do
|
||||
cp -X "$MUNKIROOT/code/client/$TOOL" "$ADMINROOT/usr/local/munki/" 2>&1
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user