mirror of
https://github.com/munki/munki.git
synced 2026-04-22 12:38:23 -05:00
New PyObjC-based version of Managed Software Update.app.
git-svn-id: http://munki.googlecode.com/svn/trunk@458 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 146 B |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""FoundationPlist.py -- a tool to generate and parse MacOSX .plist files.
|
||||
|
||||
This is intended as a drop-in replacement for Python's included plistlib,
|
||||
with a few caveats:
|
||||
- readPlist() and writePlist() operate only on a filepath,
|
||||
not a file object.
|
||||
- there is no support for the deprecated functions:
|
||||
readPlistFromResource()
|
||||
writePlistToResource()
|
||||
- there is no support for the deprecated Plist class.
|
||||
|
||||
The Property List (.plist) file format is a simple XML pickle supporting
|
||||
basic object types, like dictionaries, lists, numbers and strings.
|
||||
Usually the top level object is a dictionary.
|
||||
|
||||
To write out a plist file, use the writePlist(rootObject, filepath)
|
||||
function. 'rootObject' is the top level object, 'filepath' is a
|
||||
filename.
|
||||
|
||||
To parse a plist from a file, use the readPlist(filepath) function,
|
||||
with a file name. It returns the top level object (again, usually a
|
||||
dictionary).
|
||||
|
||||
To work with plist data in strings, you can use readPlistFromString()
|
||||
and writePlistToString().
|
||||
"""
|
||||
|
||||
from Foundation import NSData, \
|
||||
NSPropertyListSerialization, \
|
||||
NSPropertyListMutableContainers, \
|
||||
NSPropertyListXMLFormat_v1_0
|
||||
|
||||
class NSPropertyListSerializationException(Exception):
|
||||
pass
|
||||
|
||||
def readPlist(filepath):
|
||||
"""
|
||||
Read a .plist file from filepath. Return the unpacked root object
|
||||
(which is usually a dictionary).
|
||||
"""
|
||||
plistData = NSData.dataWithContentsOfFile_(filepath)
|
||||
dataObject, plistFormat, error = \
|
||||
NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
|
||||
plistData, NSPropertyListMutableContainers, None, None)
|
||||
if error:
|
||||
errmsg = "%s in file %s" % (error, filepath)
|
||||
raise NSPropertyListSerializationException(errmsg)
|
||||
else:
|
||||
return dataObject
|
||||
|
||||
|
||||
def readPlistFromString(data):
|
||||
'''Read a plist data from a string. Return the root object.'''
|
||||
plistData = buffer(data)
|
||||
dataObject, plistFormat, error = \
|
||||
NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
|
||||
plistData, NSPropertyListMutableContainers, None, None)
|
||||
if error:
|
||||
raise NSPropertyListSerializationException(error)
|
||||
else:
|
||||
return dataObject
|
||||
|
||||
|
||||
def writePlist(dataObject, filepath):
|
||||
'''
|
||||
Write 'rootObject' as a plist to filepath.
|
||||
'''
|
||||
plistData, error = \
|
||||
NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(
|
||||
dataObject, NSPropertyListXMLFormat_v1_0, None)
|
||||
if error:
|
||||
raise NSPropertyListSerializationException(error)
|
||||
else:
|
||||
if plistData.writeToFile_atomically_(filepath, True):
|
||||
return
|
||||
else:
|
||||
raise Exception("Failed to write plist data to %s" % filepath)
|
||||
|
||||
|
||||
def writePlistToString(rootObject):
|
||||
'''Return 'rootObject' as a plist-formatted string.'''
|
||||
plistData, error = \
|
||||
NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(
|
||||
rootObject, NSPropertyListXMLFormat_v1_0, None)
|
||||
if error:
|
||||
raise NSPropertyListSerializationException(error)
|
||||
else:
|
||||
return str(plistData)
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.googlecode.munki.ManagedSoftwareUpdate</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>2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# MSUWebViewPolicyDelegate.py
|
||||
# ManagedSoftwareUpdate
|
||||
#
|
||||
# Created by Greg Neagle on 2/10/10.
|
||||
# Copyright 2009-2010 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from Foundation import *
|
||||
from WebKit import *
|
||||
|
||||
class MSUWebViewPolicyDelegate(NSObject):
|
||||
# needed to intercept clicks on weblinks in descriptions
|
||||
# and pass them onto the user's default web browser.
|
||||
# without this the link will load inside the webview in our app.
|
||||
def webView_decidePolicyForNavigationAction_request_frame_decisionListener_(self, webView, actionInformation, request, frame, listener):
|
||||
if actionInformation.objectForKey_(u"WebActionNavigationTypeKey").intValue() == WebNavigationTypeLinkClicked:
|
||||
listener.ignore()
|
||||
NSWorkspace.sharedWorkspace().openURL_(request.URL())
|
||||
else:
|
||||
listener.use()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# MSUWindowController.py
|
||||
# Managed Software Update
|
||||
#
|
||||
# Created by Greg Neagle on 2/11/10.
|
||||
# Copyright 2009-2010 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from objc import YES, NO, IBAction, IBOutlet
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
|
||||
class MSUWindowController(NSWindowController):
|
||||
|
||||
restartInfoFld = IBOutlet()
|
||||
restartImageFld = IBOutlet()
|
||||
descriptionView = IBOutlet()
|
||||
tableView = IBOutlet()
|
||||
theWindow = IBOutlet()
|
||||
|
||||
_updatelist = NSArray.arrayWithArray_([{"image": NSImage.imageNamed_("Empty.png"), "name": "", "version": "", "description": ""}])
|
||||
|
||||
def updatelist(self):
|
||||
return self._updatelist
|
||||
objc.accessor(updatelist) # PyObjC KVO hack
|
||||
|
||||
def setUpdatelist_(self, newlist):
|
||||
self._updatelist = NSArray.arrayWithArray_(newlist)
|
||||
objc.accessor(setUpdatelist_) # PyObjC KVO hack
|
||||
|
||||
@IBAction
|
||||
def laterBtnClicked_(self, sender):
|
||||
NSApp.terminate_(self)
|
||||
|
||||
@IBAction
|
||||
def updateNowBtnClicked_(self, sender):
|
||||
# alert the user to logout, proceed without logout, or cancel
|
||||
NSApp.delegate().confirmInstallUpdates()
|
||||
|
||||
def windowShouldClose_(self, sender):
|
||||
# just quit
|
||||
NSApp.terminate_(self)
|
||||
Binary file not shown.
@@ -0,0 +1,339 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 44;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
77631A270C06C501005415CB /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77631A260C06C501005415CB /* Python.framework */; };
|
||||
77631A3F0C0748CF005415CB /* main.py in Resources */ = {isa = PBXBuildFile; fileRef = 77631A3E0C0748CF005415CB /* main.py */; };
|
||||
7790198F0C07548A00326F66 /* Managed_Software_UpdateAppDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = 7790198E0C07548A00326F66 /* Managed_Software_UpdateAppDelegate.py */; };
|
||||
77C8C1F90C07829500965286 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77C8C1F70C07829500965286 /* MainMenu.xib */; };
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||
C0828C801123DBC5003D5807 /* MSUWebViewPolicyDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = C0828C7F1123DBC5003D5807 /* MSUWebViewPolicyDelegate.py */; };
|
||||
C0828C901123DCFF003D5807 /* ShutDownReq.tif in Resources */ = {isa = PBXBuildFile; fileRef = C0828C871123DCFF003D5807 /* ShutDownReq.tif */; };
|
||||
C0828C911123DCFF003D5807 /* RestartRec.tif in Resources */ = {isa = PBXBuildFile; fileRef = C0828C881123DCFF003D5807 /* RestartRec.tif */; };
|
||||
C0828C921123DCFF003D5807 /* LogOutReq.tif in Resources */ = {isa = PBXBuildFile; fileRef = C0828C891123DCFF003D5807 /* LogOutReq.tif */; };
|
||||
C0828C931123DCFF003D5807 /* Managed Software Update.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8A1123DCFF003D5807 /* Managed Software Update.icns */; };
|
||||
C0828C941123DCFF003D5807 /* Empty.png in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8B1123DCFF003D5807 /* Empty.png */; };
|
||||
C0828C951123DCFF003D5807 /* RestartReq.tif in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8C1123DCFF003D5807 /* RestartReq.tif */; };
|
||||
C0828C961123DCFF003D5807 /* Restart.tif in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8D1123DCFF003D5807 /* Restart.tif */; };
|
||||
C0828C971123DCFF003D5807 /* package.tiff in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8E1123DCFF003D5807 /* package.tiff */; };
|
||||
C0828C981123DCFF003D5807 /* Installer.tiff in Resources */ = {isa = PBXBuildFile; fileRef = C0828C8F1123DCFF003D5807 /* Installer.tiff */; };
|
||||
C0828CA2112461C0003D5807 /* MSUWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0828CA1112461C0003D5807 /* MSUWindowController.py */; };
|
||||
C0828D1011247A37003D5807 /* FoundationPlist.py in Resources */ = {isa = PBXBuildFile; fileRef = C0828D0E11247A37003D5807 /* FoundationPlist.py */; };
|
||||
C0828D1211247B3A003D5807 /* munki.py in Resources */ = {isa = PBXBuildFile; fileRef = C0828D1111247B3A003D5807 /* munki.py */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
||||
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
32CA4F630368D1EE00C91783 /* Managed_Software_Update_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Managed_Software_Update_Prefix.pch; sourceTree = "<group>"; };
|
||||
77631A260C06C501005415CB /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = /System/Library/Frameworks/Python.framework; sourceTree = "<absolute>"; };
|
||||
77631A3E0C0748CF005415CB /* main.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = main.py; sourceTree = "<group>"; };
|
||||
7790198E0C07548A00326F66 /* Managed_Software_UpdateAppDelegate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = Managed_Software_UpdateAppDelegate.py; sourceTree = "<group>"; };
|
||||
77C8C1F80C07829500965286 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D1107320486CEB800E47090 /* Managed Software Update.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Managed Software Update.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C0828C7F1123DBC5003D5807 /* MSUWebViewPolicyDelegate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSUWebViewPolicyDelegate.py; sourceTree = "<group>"; };
|
||||
C0828C871123DCFF003D5807 /* ShutDownReq.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = ShutDownReq.tif; sourceTree = "<group>"; };
|
||||
C0828C881123DCFF003D5807 /* RestartRec.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = RestartRec.tif; sourceTree = "<group>"; };
|
||||
C0828C891123DCFF003D5807 /* LogOutReq.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = LogOutReq.tif; sourceTree = "<group>"; };
|
||||
C0828C8A1123DCFF003D5807 /* Managed Software Update.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Managed Software Update.icns"; sourceTree = "<group>"; };
|
||||
C0828C8B1123DCFF003D5807 /* Empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Empty.png; sourceTree = "<group>"; };
|
||||
C0828C8C1123DCFF003D5807 /* RestartReq.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = RestartReq.tif; sourceTree = "<group>"; };
|
||||
C0828C8D1123DCFF003D5807 /* Restart.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Restart.tif; sourceTree = "<group>"; };
|
||||
C0828C8E1123DCFF003D5807 /* package.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = package.tiff; sourceTree = "<group>"; };
|
||||
C0828C8F1123DCFF003D5807 /* Installer.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Installer.tiff; sourceTree = "<group>"; };
|
||||
C0828CA1112461C0003D5807 /* MSUWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSUWindowController.py; sourceTree = "<group>"; };
|
||||
C0828D0E11247A37003D5807 /* FoundationPlist.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = FoundationPlist.py; sourceTree = "<group>"; };
|
||||
C0828D1111247B3A003D5807 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = munki.py; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8D11072E0486CEB800E47090 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
|
||||
77631A270C06C501005415CB /* Python.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C0828C7F1123DBC5003D5807 /* MSUWebViewPolicyDelegate.py */,
|
||||
7790198E0C07548A00326F66 /* Managed_Software_UpdateAppDelegate.py */,
|
||||
C0828CA1112461C0003D5807 /* MSUWindowController.py */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
77631A260C06C501005415CB /* Python.framework */,
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
|
||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FACFE9D520D11CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D1107320486CEB800E47090 /* Managed Software Update.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97314FDCFA39411CA2CEA /* Managed Software Update */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
080E96DDFE201D6D7F000001 /* Classes */,
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */,
|
||||
29B97317FDCFA39411CA2CEA /* Resources */,
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */,
|
||||
19C28FACFE9D520D11CA2CBB /* Products */,
|
||||
);
|
||||
name = "Managed Software Update";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C0828D0E11247A37003D5807 /* FoundationPlist.py */,
|
||||
32CA4F630368D1EE00C91783 /* Managed_Software_Update_Prefix.pch */,
|
||||
29B97316FDCFA39411CA2CEA /* main.m */,
|
||||
77631A3E0C0748CF005415CB /* main.py */,
|
||||
C0828D1111247B3A003D5807 /* munki.py */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C0828C871123DCFF003D5807 /* ShutDownReq.tif */,
|
||||
C0828C881123DCFF003D5807 /* RestartRec.tif */,
|
||||
C0828C891123DCFF003D5807 /* LogOutReq.tif */,
|
||||
C0828C8A1123DCFF003D5807 /* Managed Software Update.icns */,
|
||||
C0828C8B1123DCFF003D5807 /* Empty.png */,
|
||||
C0828C8C1123DCFF003D5807 /* RestartReq.tif */,
|
||||
C0828C8D1123DCFF003D5807 /* Restart.tif */,
|
||||
C0828C8E1123DCFF003D5807 /* package.tiff */,
|
||||
C0828C8F1123DCFF003D5807 /* Installer.tiff */,
|
||||
77C8C1F70C07829500965286 /* MainMenu.xib */,
|
||||
8D1107310486CEB800E47090 /* Info.plist */,
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8D1107260486CEB800E47090 /* Managed Software Update */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Managed Software Update" */;
|
||||
buildPhases = (
|
||||
8D1107290486CEB800E47090 /* Resources */,
|
||||
8D11072C0486CEB800E47090 /* Sources */,
|
||||
8D11072E0486CEB800E47090 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Managed Software Update";
|
||||
productInstallPath = "$(HOME)/Applications";
|
||||
productName = "Managed Software Update";
|
||||
productReference = 8D1107320486CEB800E47090 /* Managed Software Update.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Managed Software Update" */;
|
||||
compatibilityVersion = "Xcode 3.0";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* Managed Software Update */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8D1107260486CEB800E47090 /* Managed Software Update */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8D1107290486CEB800E47090 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
|
||||
77631A3F0C0748CF005415CB /* main.py in Resources */,
|
||||
7790198F0C07548A00326F66 /* Managed_Software_UpdateAppDelegate.py in Resources */,
|
||||
77C8C1F90C07829500965286 /* MainMenu.xib in Resources */,
|
||||
C0828C801123DBC5003D5807 /* MSUWebViewPolicyDelegate.py in Resources */,
|
||||
C0828C901123DCFF003D5807 /* ShutDownReq.tif in Resources */,
|
||||
C0828C911123DCFF003D5807 /* RestartRec.tif in Resources */,
|
||||
C0828C921123DCFF003D5807 /* LogOutReq.tif in Resources */,
|
||||
C0828C931123DCFF003D5807 /* Managed Software Update.icns in Resources */,
|
||||
C0828C941123DCFF003D5807 /* Empty.png in Resources */,
|
||||
C0828C951123DCFF003D5807 /* RestartReq.tif in Resources */,
|
||||
C0828C961123DCFF003D5807 /* Restart.tif in Resources */,
|
||||
C0828C971123DCFF003D5807 /* package.tiff in Resources */,
|
||||
C0828C981123DCFF003D5807 /* Installer.tiff in Resources */,
|
||||
C0828CA2112461C0003D5807 /* MSUWindowController.py in Resources */,
|
||||
C0828D1011247A37003D5807 /* FoundationPlist.py in Resources */,
|
||||
C0828D1211247B3A003D5807 /* munki.py in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8D11072C0486CEB800E47090 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C165DFE840E0CC02AAC07 /* English */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
77C8C1F70C07829500965286 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
77C8C1F80C07829500965286 /* English */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C01FCF4B08A954540054247B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Managed_Software_Update_Prefix.pch;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "Managed Software Update";
|
||||
WRAPPER_EXTENSION = app;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C01FCF4C08A954540054247B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Managed_Software_Update_Prefix.pch;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "Managed Software Update";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C01FCF4F08A954540054247B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C01FCF5008A954540054247B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Managed Software Update" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4B08A954540054247B /* Debug */,
|
||||
C01FCF4C08A954540054247B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Managed Software Update" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4F08A954540054247B /* Debug */,
|
||||
C01FCF5008A954540054247B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
#
|
||||
# Managed_Software_UpdateAppDelegate.py
|
||||
# Managed Software Update
|
||||
#
|
||||
# Created by Greg Neagle on 2/10/10.
|
||||
# Copyright 2009-2010 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
import munki
|
||||
import PyObjCTools
|
||||
|
||||
class Managed_Software_UpdateAppDelegate(NSObject):
|
||||
|
||||
window_controller = objc.IBOutlet()
|
||||
array_controller = objc.IBOutlet()
|
||||
|
||||
_emptyImage = NSImage.imageNamed_("Empty.png")
|
||||
_restartImage = NSImage.imageNamed_("RestartReq.tif")
|
||||
_logoutImage = NSImage.imageNamed_("LogOutReq.tif")
|
||||
_listofupdates = []
|
||||
|
||||
restart_required = False
|
||||
logout_required = False
|
||||
|
||||
|
||||
def applicationDidFinishLaunching_(self, sender):
|
||||
pass
|
||||
|
||||
def applicationDidBecomeActive_(self, sender):
|
||||
# display updates if available; if no available updates
|
||||
# trigger an update check
|
||||
if not self._listofupdates:
|
||||
self.getAvailableUpdates()
|
||||
if self._listofupdates:
|
||||
self.buildTableData()
|
||||
self.window_controller.theWindow.makeKeyAndOrderFront_(self)
|
||||
else:
|
||||
# no updates available. Should we check for some?
|
||||
result = munki.checkForUpdates()
|
||||
if result == 0:
|
||||
self.window_controller.theWindow.makeKeyAndOrderFront_(self)
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Your software is up to date.", u"Quit", objc.nil, objc.nil, "There is no new software for your computer at this time.")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif result == -1:
|
||||
self.window_controller.theWindow.makeKeyAndOrderFront_(self)
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Cannot check for updates", u"Quit", objc.nil, objc.nil, "Managed Software Update cannot contact the update server at this time.\nIf this situtation continues, contact your systems administrator.")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif result == -2:
|
||||
self.window_controller.theWindow.makeKeyAndOrderFront_(self)
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Update check failed", u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. Could not start the update check process. Contact your systems administrator.")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
def getAvailableUpdates(self):
|
||||
updatelist = []
|
||||
installinfo = munki.getInstallInfo()
|
||||
if installinfo:
|
||||
updatelist = installinfo.get("managed_installs", [])
|
||||
if installinfo.get("removals"):
|
||||
removallist = installinfo.get("removals")
|
||||
restartNeeded = False
|
||||
showRemovalDetail = munki.getRemovalDetailPrefs()
|
||||
for item in removallist:
|
||||
if item.get("RestartAction") == "RequireRestart" or item.get("RestartAction") == "RecommendRestart":
|
||||
restartNeeded = True
|
||||
if showRemovalDetail:
|
||||
if display_name in item:
|
||||
item["display_name"] = item["display_name"] + " (will be removed)"
|
||||
elif name in item:
|
||||
item["display_name"] = item["name"] + " (will be removed)"
|
||||
updatelist.append(item)
|
||||
if not showRemovalDetail:
|
||||
row = {}
|
||||
row["display_name"] = "Software removals"
|
||||
row["version"] = ""
|
||||
row["description"] = "Scheduled removal of managed software."
|
||||
if restartNeeded:
|
||||
row["RestartAction"] = "RequireRestart"
|
||||
updatelist.append(row)
|
||||
|
||||
if updatelist:
|
||||
self._listofupdates = updatelist
|
||||
else:
|
||||
appleupdates = munki.getAppleUpdates()
|
||||
if appleupdates:
|
||||
self._listofupdates = appleupdates.get("AppleUpdates", [])
|
||||
|
||||
|
||||
def buildTableData(self):
|
||||
table = []
|
||||
self.restart_required = False
|
||||
self.logout_required = False
|
||||
for item in self._listofupdates:
|
||||
row = {}
|
||||
if item.get("RestartAction") == "RequireRestart" or item.get("RestartAction") == "RecommendRestart":
|
||||
row['image'] = self._restartImage
|
||||
self.restart_required = True
|
||||
elif item.get("RestartAction") == "RequireLogout" or item.get("RestartAction") == "RecommendLogout":
|
||||
row['image'] = self._logoutImage
|
||||
self.logout_required = True
|
||||
else:
|
||||
row['image'] = self._emptyImage
|
||||
row['name'] = item.get("display_name") or item.get("name","")
|
||||
row['version'] = munki.trimVersionString(item.get("version_to_install"),3)
|
||||
row['description'] = item.get("description","")
|
||||
row_dict = NSDictionary.dictionaryWithDictionary_(row)
|
||||
table.append(row_dict)
|
||||
|
||||
self.window_controller.setUpdatelist_(table)
|
||||
self.window_controller.tableView.deselectAll_(self)
|
||||
if self.restart_required:
|
||||
self.window_controller.restartInfoFld.setStringValue_(u"Restart will be required.")
|
||||
self.window_controller.restartImageFld.setImage_(self._restartImage)
|
||||
elif self.logout_required:
|
||||
self.window_controller.restartInfoFld.setStringValue_(u"Logout will be required.")
|
||||
self.window_controller.restartImageFld.setImage_(self._logoutImage)
|
||||
|
||||
|
||||
|
||||
def tableViewSelectionDidChange_(self, sender):
|
||||
if self.array_controller.selectedObjects():
|
||||
row = self.array_controller.selectedObjects()[0]
|
||||
self.window_controller.descriptionView.mainFrame().loadHTMLString_baseURL_(row.get("description",""), None)
|
||||
else:
|
||||
self.window_controller.descriptionView.mainFrame().loadHTMLString_baseURL_(u"", None)
|
||||
|
||||
|
||||
def confirmInstallUpdates(self):
|
||||
if len(munki.currentGUIusers()) > 1:
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Other users logged in", u"Cancel", objc.nil, objc.nil, "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.")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.multipleUserAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif self.restart_required:
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Restart Required", u"Log out and update", u"Cancel", objc.nil, "A restart is required after updating. Log out and update now?")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
elif self.logout_required or munki.installRequiresLogout():
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Logout Required", u"Log out and update", u"Cancel", objc.nil, "A logout is required before updating. Log out and update now?")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
else:
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Logout Recommended", u"Log out and update", u"Cancel", u"Update without logging out", "A logout is recommended before updating. Log out and update now?")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
|
||||
def installSessionErrorAlert(self):
|
||||
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Cannot start installation session", u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. Could not start the install session. Contact your systems administrator.")
|
||||
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.window_controller.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
|
||||
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def logoutAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
if returncode == 0:
|
||||
NSLog("User cancelled")
|
||||
elif returncode == 1:
|
||||
NSLog("User chose to log out")
|
||||
result = munki.logoutAndUpdate()
|
||||
if result:
|
||||
self.installSessionErrorAlert()
|
||||
elif returncode == -1:
|
||||
NSLog("User chose to update without logging out")
|
||||
result = munki.justUpdate()
|
||||
if result:
|
||||
self.installSessionErrorAlert()
|
||||
else:
|
||||
NSApp.terminate_(self)
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def multipleUserAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
pass
|
||||
|
||||
@PyObjCTools.AppHelper.endSheetMethod
|
||||
def quitAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
|
||||
NSApp.terminate_(self)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'Managed Software Update' target in the 'Managed Software Update' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// main.m
|
||||
// Managed Software Update
|
||||
//
|
||||
|
||||
|
||||
#import <Python/Python.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
NSBundle *mainBundle = [NSBundle mainBundle];
|
||||
NSString *resourcePath = [mainBundle resourcePath];
|
||||
NSArray *pythonPathArray = [NSArray arrayWithObjects: resourcePath, [resourcePath stringByAppendingPathComponent:@"PyObjC"], nil];
|
||||
|
||||
setenv("PYTHONPATH", [[pythonPathArray componentsJoinedByString:@":"] UTF8String], 1);
|
||||
|
||||
NSArray *possibleMainExtensions = [NSArray arrayWithObjects: @"py", @"pyc", @"pyo", nil];
|
||||
NSString *mainFilePath = nil;
|
||||
|
||||
for (NSString *possibleMainExtension in possibleMainExtensions) {
|
||||
mainFilePath = [mainBundle pathForResource: @"main" ofType: possibleMainExtension];
|
||||
if ( mainFilePath != nil ) break;
|
||||
}
|
||||
|
||||
if ( !mainFilePath ) {
|
||||
[NSException raise: NSInternalInconsistencyException format: @"%s:%d main() Failed to find the Main.{py,pyc,pyo} file in the application wrapper's Resources directory.", __FILE__, __LINE__];
|
||||
}
|
||||
|
||||
Py_SetProgramName("/usr/bin/python");
|
||||
Py_Initialize();
|
||||
PySys_SetArgv(argc, (char **)argv);
|
||||
|
||||
const char *mainFilePathPtr = [mainFilePath UTF8String];
|
||||
FILE *mainFile = fopen(mainFilePathPtr, "r");
|
||||
int result = PyRun_SimpleFile(mainFile, (char *)[[mainFilePath lastPathComponent] UTF8String]);
|
||||
|
||||
if ( result != 0 )
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"%s:%d main() PyRun_SimpleFile failed with file '%@'. See console for errors.", __FILE__, __LINE__, mainFilePath];
|
||||
|
||||
[pool drain];
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# main.py
|
||||
# Managed Software Update
|
||||
#
|
||||
# Created by Greg Neagle on 2/10/10.
|
||||
# Copyright 2009-2010 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
#import modules required by application
|
||||
import objc
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
from PyObjCTools import AppHelper
|
||||
|
||||
# import modules containing classes required to start application and load MainMenu.nib
|
||||
import Managed_Software_UpdateAppDelegate
|
||||
import MSUWindowController
|
||||
import MSUWebViewPolicyDelegate
|
||||
|
||||
# pass control to AppKit
|
||||
AppHelper.runEventLoop()
|
||||
@@ -0,0 +1,225 @@
|
||||
# encoding: utf-8
|
||||
#
|
||||
# munki.py
|
||||
# Managed Software Update
|
||||
#
|
||||
# Created by Greg Neagle on 2/11/10.
|
||||
# Copyright 2009-2010 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import FoundationPlist
|
||||
import time
|
||||
|
||||
from Foundation import NSDate
|
||||
from ScriptingBridge import SBApplication
|
||||
|
||||
_updatechecklaunchfile = "/private/tmp/.com.googlecode.munki.updatecheck.launchd"
|
||||
_MunkiStatusIdentifier = "com.googlecode.munki.MunkiStatus"
|
||||
|
||||
def getManagedInstallsPrefs():
|
||||
# define default values
|
||||
prefs = {}
|
||||
prefs['ManagedInstallDir'] = "/Library/Managed Installs"
|
||||
prefs['InstallAppleSoftwareUpdates'] = False
|
||||
prefs['ShowRemovalDetail'] = False
|
||||
prefs['InstallRequiresLogout'] = False
|
||||
|
||||
prefsfile = "/Library/Preferences/ManagedInstalls.plist"
|
||||
pl = {}
|
||||
if os.path.exists(prefsfile):
|
||||
try:
|
||||
pl = FoundationPlist.readPlist(prefsfile)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
pass
|
||||
try:
|
||||
for key in pl.keys():
|
||||
if type(pl[key]).__name__ == "__NSCFDate":
|
||||
# convert NSDate/CFDates to strings
|
||||
prefs[key] = str(pl[key])
|
||||
else:
|
||||
prefs[key] = pl[key]
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return prefs
|
||||
|
||||
def getRemovalDetailPrefs():
|
||||
return getManagedInstallsPrefs().get('ShowRemovalDetail', False)
|
||||
|
||||
def installRequiresLogout():
|
||||
return getManagedInstallsPrefs().get('InstallRequiresLogout', False)
|
||||
|
||||
|
||||
def getInstallInfo():
|
||||
prefs = getManagedInstallsPrefs()
|
||||
managedinstallbase = prefs['ManagedInstallDir']
|
||||
pl = {}
|
||||
installinfo = os.path.join(managedinstallbase, 'InstallInfo.plist')
|
||||
if os.path.exists(installinfo):
|
||||
try:
|
||||
pl = FoundationPlist.readPlist(installinfo)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
pass
|
||||
return pl
|
||||
|
||||
|
||||
def startUpdateCheck():
|
||||
# does launchd magic to run managedsoftwareupdate as root
|
||||
cmd = ["/usr/bin/touch", _updatechecklaunchfile]
|
||||
if subprocess.call(cmd):
|
||||
return -1
|
||||
else:
|
||||
time.sleep(1)
|
||||
# check to see if we were successful in starting the update
|
||||
return updateInProgress()
|
||||
|
||||
|
||||
def updateInProgress():
|
||||
# if MunkiStatus is running, we're doing an update right now
|
||||
MunkiStatus = SBApplication.applicationWithBundleIdentifier_(_MunkiStatusIdentifier)
|
||||
if MunkiStatus and MunkiStatus.isRunning():
|
||||
# bring it back to the front
|
||||
MunkiStatus.activate()
|
||||
return 1
|
||||
elif os.path.exists(_updatechecklaunchfile):
|
||||
# we tried to trigger the update, but it failed?
|
||||
try:
|
||||
# this might fail if we don't own it
|
||||
os.unlink(_updatechecklaunchfile)
|
||||
except:
|
||||
pass
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def checkForUpdates():
|
||||
# returns 1 if we've kicked off an update check (or one is in progress),
|
||||
# returns 0 if we're not going to check (because we just did)
|
||||
# returns -1 if the munki server is unavailable
|
||||
# returns -2 if there's an unexpected problem
|
||||
|
||||
# are we checking right now (MunkiStatus.app is running)?
|
||||
update = updateInProgress()
|
||||
if update == 1:
|
||||
return 1
|
||||
elif update == -1:
|
||||
return -2
|
||||
|
||||
# when did we last check?
|
||||
now = NSDate.new()
|
||||
prefs = getManagedInstallsPrefs()
|
||||
lastCheckedDateString = prefs.get("LastCheckDate")
|
||||
if lastCheckedDateString:
|
||||
lastCheckedDate = NSDate.dateWithString_(lastCheckedDateString)
|
||||
if (not lastCheckedDateString) or now.timeIntervalSinceDate_(lastCheckedDate) > 10:
|
||||
# we haven't checked in more than 10 seconds
|
||||
result = startUpdateCheck()
|
||||
if result == 0:
|
||||
return 1
|
||||
else:
|
||||
return -2
|
||||
else:
|
||||
# we just finished checking
|
||||
lastCheckResult = prefs.get("LastCheckResult")
|
||||
if lastCheckResult == -1:
|
||||
# check failed
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def getAppleUpdates():
|
||||
prefs = getManagedInstallsPrefs()
|
||||
managedinstallbase = prefs['ManagedInstallDir']
|
||||
pl = {}
|
||||
appleUpdatesFile = os.path.join(managedinstallbase, 'AppleUpdates.plist')
|
||||
if os.path.exists(appleUpdatesFile):
|
||||
try:
|
||||
pl = FoundationPlist.readPlist(appleUpdatesFile)
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
pass
|
||||
return pl
|
||||
|
||||
def trimVersionString(versString,tupleCount):
|
||||
if versString == None or versString == "":
|
||||
return ""
|
||||
components = str(versString).split(".")
|
||||
if len(components) > tupleCount:
|
||||
components = components[0:tupleCount]
|
||||
return ".".join(components)
|
||||
|
||||
def currentGUIusers():
|
||||
'''Gets a list of GUI users by parsing the output of /usr/bin/who'''
|
||||
gui_users = []
|
||||
p = subprocess.Popen("/usr/bin/who", shell=False, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
lines = output.splitlines()
|
||||
for line in lines:
|
||||
if "console" in line:
|
||||
parts = line.split()
|
||||
gui_users.append(parts[0])
|
||||
|
||||
return gui_users
|
||||
|
||||
def logoutNow():
|
||||
# uses oscascript to run an AppleScript
|
||||
# to tell loginwindow to logout
|
||||
# ugly, but it works.
|
||||
|
||||
script = """
|
||||
ignoring application responses
|
||||
tell application "loginwindow"
|
||||
«event aevtrlgo»
|
||||
end tell
|
||||
end ignoring
|
||||
"""
|
||||
cmd = ["/usr/bin/osascript"]
|
||||
for line in script.splitlines():
|
||||
line = line.rstrip().lstrip()
|
||||
if line:
|
||||
cmd.append("-e")
|
||||
cmd.append(line)
|
||||
|
||||
p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
|
||||
|
||||
|
||||
def logoutAndUpdate():
|
||||
# touch a flag so the process that runs after
|
||||
# logout knows it's OK to install everything
|
||||
cmd = ["/usr/bin/touch", "/private/tmp/com.googlecode.munki.installatlogout"]
|
||||
result = subprocess.call(cmd)
|
||||
if result == 0:
|
||||
logoutNow()
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
def justUpdate():
|
||||
# trigger managedinstaller via launchd KeepAlive path trigger
|
||||
# we touch a file that launchd is is watching
|
||||
# launchd, in turn, launches managedsoftwareupdate --installwithnologout as root
|
||||
cmd = ["/usr/bin/touch", "/private/tmp/.com.googlecode.munki.managedinstall.launchd"]
|
||||
return subprocess.call(cmd)
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user