├── LICENSE ├── README.md ├── check-10.10-yosemite-compatibility.py ├── check-10.11-elcapitan-compatibility.py ├── check-10.12-sierra-compatibility.py ├── check-10.13-highsierra-compatibility.py ├── check-10.14-mojave-compatibility.py ├── check-10.15-catalina-compatibility.py ├── check-10.8-mountainlion-compatibility.py ├── check-10.9-mavericks-compatibility.py ├── check-if-virtual-machine.py ├── chrome-disable-autoupdates.py ├── chrome-enable-autoupdates.py ├── download-logicprox-content.py ├── export-macos-man-pages.sh ├── office2011-installed-language.py ├── old ├── check-for-osx-flashback.K.sh ├── check-for-osx-malware.sh └── imac-hd-check.sh ├── reposado-add-new-products.sh ├── reposado-remove-deprecated-products.sh └── wrap-dmg.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hannes Juutilainen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is a repo for my miscellaneous mac admin scripts. 4 | 5 | # License 6 | 7 | Scripts in this repo are licensed under the [MIT License](https://github.com/hjuutilainen/adminscripts/blob/master/LICENSE) 8 | -------------------------------------------------------------------------------- /check-10.10-yosemite-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-yosemite-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with OS X 10.10 Yosemite. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # /Applications/Install OS X Yosemite.app/Contents/SharedSupport/OSInstall.mpkg/Distribution 10 | # 11 | # The checks used in this script are: 12 | # - Machine has a specific supported board-id or is a virtual machine 13 | # - 64 bit capable CPU 14 | # - At least 2 GB of memory 15 | # - Current system version is less than 10.10 16 | # - Current system version is at least 10.6.6 or newer 17 | # 18 | # Exit codes: 19 | # 0 = Yosemite is supported 20 | # 1 = Yosemite is not supported 21 | # 22 | # 23 | # Hannes Juutilainen 24 | # https://github.com/hjuutilainen/adminscripts 25 | # 26 | # ================================================================================ 27 | 28 | import sys 29 | import subprocess 30 | import os 31 | import re 32 | import plistlib 33 | from distutils.version import StrictVersion 34 | from Foundation import CFPreferencesCopyAppValue 35 | 36 | 37 | # ================================================================================ 38 | # Start configuration 39 | # ================================================================================ 40 | 41 | # Set this to False if you don't want any output, just the exit codes 42 | verbose = True 43 | 44 | # Set this to True if you want to add "yosemite_supported" custom conditional to 45 | # /Library/Managed Installs/ConditionalItems.plist 46 | update_munki_conditional_items = False 47 | 48 | # ================================================================================ 49 | # End configuration 50 | # ================================================================================ 51 | 52 | 53 | def logger(message, status, info): 54 | if verbose: 55 | print "%14s: %-40s [%s]" % (message, status, info) 56 | pass 57 | 58 | 59 | def conditional_items_path(): 60 | # 61 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 62 | bundle_id = 'ManagedInstalls' 63 | pref_name = 'ManagedInstallDir' 64 | managed_installs_dir = CFPreferencesCopyAppValue(pref_name, bundle_id) 65 | # Make sure we're outputting our information to "ConditionalItems.plist" 66 | if managed_installs_dir: 67 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 68 | else: 69 | # Munki default 70 | return "/Library/Managed Installs/ConditionalItems.plist" 71 | 72 | 73 | def munki_installed(): 74 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 75 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 76 | output = p.communicate()[0] 77 | if p.returncode == 0: 78 | return True 79 | else: 80 | return False 81 | 82 | 83 | def is_system_version_supported(): 84 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 85 | product_name = system_version_plist['ProductName'] 86 | product_version = system_version_plist['ProductVersion'] 87 | if StrictVersion(product_version) >= StrictVersion('10.10'): 88 | logger("System", 89 | "%s %s" % (product_name, product_version), 90 | "Failed") 91 | return False 92 | elif StrictVersion(product_version) >= StrictVersion('10.6.6'): 93 | logger("System", 94 | "%s %s" % (product_name, product_version), 95 | "OK") 96 | return True 97 | else: 98 | logger("System", 99 | "%s %s" % (product_name, product_version), 100 | "Failed") 101 | return False 102 | 103 | 104 | def get_board_id(): 105 | cmd = ["/usr/sbin/ioreg", 106 | "-p", "IODeviceTree", 107 | "-r", 108 | "-n", "/", 109 | "-d", "1"] 110 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 111 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 112 | (results, err) = p2.communicate() 113 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 114 | board_id = board_id.strip() 115 | if board_id.startswith('Mac'): 116 | return board_id 117 | else: 118 | return None 119 | 120 | 121 | def is_64bit_capable(): 122 | cmd = ["/usr/sbin/sysctl", "-n", "hw.cpu64bit_capable"] 123 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 124 | (results, err) = p.communicate() 125 | if bool(results): 126 | logger("CPU", 127 | "64 bit capable", 128 | "OK") 129 | return True 130 | else: 131 | logger("CPU", 132 | "not 64 bit capable", 133 | "Failed") 134 | return False 135 | 136 | 137 | def has_required_amount_of_memory(): 138 | minimum_memory = int(2048 * 1024 * 1024) 139 | cmd = ["/usr/sbin/sysctl", "-n", "hw.memsize"] 140 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 141 | (results, err) = p.communicate() 142 | actual_memory = int(results) 143 | actual_memory_gigabytes = actual_memory / 1024 / 1024 / 1024 144 | if actual_memory >= minimum_memory: 145 | logger("Memory", 146 | "%i GB physical memory installed" % actual_memory_gigabytes, 147 | "OK") 148 | return True 149 | else: 150 | logger("Memory", 151 | "%i GB installed, 2 GB required" % actual_memory_gigabytes, 152 | "Failed") 153 | return False 154 | 155 | 156 | def is_virtual_machine(): 157 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 158 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 159 | (results, err) = p.communicate() 160 | for feature in results.split(): 161 | if feature == "VMM": 162 | logger("Board ID", 163 | "Virtual machine", 164 | "OK") 165 | return True 166 | return False 167 | 168 | 169 | def is_supported_board_id(): 170 | if is_virtual_machine(): 171 | return True 172 | platform_support_values = [ 173 | "Mac-00BE6ED71E35EB86", 174 | "Mac-031AEE4D24BFF0B1", 175 | "Mac-031B6874CF7F642A", 176 | "Mac-189A3D4F975D5FFC", 177 | "Mac-27ADBB7B4CEE8E61", 178 | "Mac-2BD1B31983FE1663", 179 | "Mac-2E6FAB96566FE58C", 180 | "Mac-35C1E88140C3E6CF", 181 | "Mac-35C5E08120C7EEAF", 182 | "Mac-3CBD00234E554E41", 183 | "Mac-42FD25EABCABB274", 184 | "Mac-4B7AC7E43945597E", 185 | "Mac-4BC72D62AD45599E", 186 | "Mac-50619A408DB004DA", 187 | "Mac-66F35F19FE2A0D05", 188 | "Mac-6F01561E16C75D06", 189 | "Mac-742912EFDBEE19B3", 190 | "Mac-77EB7D7DAF985301", 191 | "Mac-7BA5B2794B2CDB12", 192 | "Mac-7DF21CB3ED6977E5", 193 | "Mac-7DF2A3B5E5D671ED", 194 | "Mac-81E3E92DD6088272", 195 | "Mac-8ED6AF5B48C039E1", 196 | "Mac-942452F5819B1C1B", 197 | "Mac-942459F5819B171B", 198 | "Mac-94245A3940C91C80", 199 | "Mac-94245B3640C91C81", 200 | "Mac-942B59F58194171B", 201 | "Mac-942B5BF58194151B", 202 | "Mac-942C5DF58193131B", 203 | "Mac-AFD8A9D944EA4843", 204 | "Mac-C08A6BB70A942AC2", 205 | "Mac-C3EC7CD22292981F", 206 | "Mac-F2208EC8", 207 | "Mac-F2218EA9", 208 | "Mac-F2218EC8" 209 | "Mac-F2218FA9", 210 | "Mac-F2218FC8", 211 | "Mac-F221BEC8", 212 | "Mac-F221DCC8", 213 | "Mac-F222BEC8", 214 | "Mac-F2238AC8", 215 | "Mac-F2238BAE", 216 | "Mac-F223BEC8", 217 | "Mac-F22586C8", 218 | "Mac-F22587A1", 219 | "Mac-F22587C8", 220 | "Mac-F22589C8", 221 | "Mac-F2268AC8", 222 | "Mac-F2268CC8", 223 | "Mac-F2268DAE", 224 | "Mac-F2268DC8", 225 | "Mac-F2268EC8", 226 | "Mac-F226BEC8", 227 | "Mac-F22788AA", 228 | "Mac-F227BEC8", 229 | "Mac-F22C86C8", 230 | "Mac-F22C89C8", 231 | "Mac-F22C8AC8", 232 | "Mac-F42386C8", 233 | "Mac-F42388C8", 234 | "Mac-F4238BC8", 235 | "Mac-F4238CC8", 236 | "Mac-F42C86C8", 237 | "Mac-F42C88C8", 238 | "Mac-F42C89C8", 239 | "Mac-F42D86A9", 240 | "Mac-F42D86C8", 241 | "Mac-F42D88C8", 242 | "Mac-F42D89A9", 243 | "Mac-F42D89C8", 244 | "Mac-F60DEB81FF30ACF6", 245 | "Mac-F65AE981FFA204ED", 246 | "Mac-FA842E06C61E91C5", 247 | "Mac-FC02E91DDD3FA6A4"] 248 | board_id = get_board_id() 249 | if board_id in platform_support_values: 250 | logger("Board ID", 251 | board_id, 252 | "OK") 253 | return True 254 | else: 255 | logger("Board ID", 256 | "\"%s\" is not supported" % board_id, 257 | "Failed") 258 | return False 259 | 260 | 261 | def append_conditional_items(dictionary): 262 | current_conditional_items_path = conditional_items_path() 263 | if os.path.exists(current_conditional_items_path): 264 | existing_dict = plistlib.readPlist(current_conditional_items_path) 265 | output_dict = dict(existing_dict.items() + dictionary.items()) 266 | else: 267 | output_dict = dictionary 268 | plistlib.writePlist(output_dict, current_conditional_items_path) 269 | pass 270 | 271 | 272 | 273 | def main(argv=None): 274 | yosemite_supported_dict = {} 275 | yosemite_needs_fw_update_dict = {} 276 | 277 | # Run the checks 278 | board_id_passed = is_supported_board_id() 279 | memory_passed = has_required_amount_of_memory() 280 | cpu_passed = is_64bit_capable() 281 | system_version_passed = is_system_version_supported() 282 | 283 | if board_id_passed and memory_passed and cpu_passed and system_version_passed: 284 | yosemite_supported = 0 285 | yosemite_supported_dict = {'yosemite_supported': True} 286 | else: 287 | yosemite_supported = 1 288 | yosemite_supported_dict = {'yosemite_supported': False} 289 | 290 | # Update "ConditionalItems.plist" if munki is installed 291 | if munki_installed() and update_munki_conditional_items: 292 | append_conditional_items(yosemite_supported_dict) 293 | 294 | # Exit codes: 295 | # 0 = Yosemite is supported 296 | # 1 = Yosemite is not supported 297 | return yosemite_supported 298 | 299 | 300 | if __name__ == '__main__': 301 | sys.exit(main()) 302 | -------------------------------------------------------------------------------- /check-10.11-elcapitan-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-elcapitan-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with OS X 10.11 El Capitan. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # /Applications/Install OS X El Capitan.app/Contents/SharedSupport/OSInstall.mpkg/Distribution 10 | # 11 | # The checks used in this script are: 12 | # - Machine has a specific supported board-id or is a virtual machine 13 | # - 64 bit capable CPU 14 | # - At least 2 GB of memory 15 | # - Current system version is less than 10.11 16 | # - Current system version is at least 10.6.8 or newer 17 | # 18 | # Exit codes: 19 | # 0 = El Capitan is supported 20 | # 1 = El Capitan is not supported 21 | # 22 | # 23 | # Hannes Juutilainen 24 | # https://github.com/hjuutilainen/adminscripts 25 | # 26 | # ================================================================================ 27 | 28 | import sys 29 | import subprocess 30 | import os 31 | import re 32 | import plistlib 33 | from distutils.version import StrictVersion 34 | from Foundation import CFPreferencesCopyAppValue 35 | 36 | 37 | # ================================================================================ 38 | # Start configuration 39 | # ================================================================================ 40 | 41 | # Set this to False if you don't want any output, just the exit codes 42 | verbose = True 43 | 44 | # Set this to True if you want to add "elcapitan_supported" custom conditional to 45 | # /Library/Managed Installs/ConditionalItems.plist 46 | update_munki_conditional_items = False 47 | 48 | # ================================================================================ 49 | # End configuration 50 | # ================================================================================ 51 | 52 | 53 | def logger(message, status, info): 54 | if verbose: 55 | print "%14s: %-40s [%s]" % (message, status, info) 56 | pass 57 | 58 | 59 | def conditional_items_path(): 60 | # 61 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 62 | bundle_id = 'ManagedInstalls' 63 | pref_name = 'ManagedInstallDir' 64 | managed_installs_dir = CFPreferencesCopyAppValue(pref_name, bundle_id) 65 | # Make sure we're outputting our information to "ConditionalItems.plist" 66 | if managed_installs_dir: 67 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 68 | else: 69 | # Munki default 70 | return "/Library/Managed Installs/ConditionalItems.plist" 71 | 72 | 73 | def munki_installed(): 74 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 75 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 76 | output = p.communicate()[0] 77 | if p.returncode == 0: 78 | return True 79 | else: 80 | return False 81 | 82 | 83 | def is_system_version_supported(): 84 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 85 | product_name = system_version_plist['ProductName'] 86 | product_version = system_version_plist['ProductVersion'] 87 | if StrictVersion(product_version) >= StrictVersion('10.11'): 88 | logger("System", 89 | "%s %s" % (product_name, product_version), 90 | "Failed") 91 | return False 92 | elif StrictVersion(product_version) >= StrictVersion('10.6.8'): 93 | logger("System", 94 | "%s %s" % (product_name, product_version), 95 | "OK") 96 | return True 97 | else: 98 | logger("System", 99 | "%s %s" % (product_name, product_version), 100 | "Failed") 101 | return False 102 | 103 | 104 | def get_board_id(): 105 | cmd = ["/usr/sbin/ioreg", 106 | "-p", "IODeviceTree", 107 | "-r", 108 | "-n", "/", 109 | "-d", "1"] 110 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 111 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 112 | (results, err) = p2.communicate() 113 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 114 | board_id = board_id.strip() 115 | if board_id.startswith('Mac'): 116 | return board_id 117 | else: 118 | return None 119 | 120 | 121 | def is_64bit_capable(): 122 | cmd = ["/usr/sbin/sysctl", "-n", "hw.cpu64bit_capable"] 123 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 124 | (results, err) = p.communicate() 125 | if bool(results): 126 | logger("CPU", 127 | "64 bit capable", 128 | "OK") 129 | return True 130 | else: 131 | logger("CPU", 132 | "not 64 bit capable", 133 | "Failed") 134 | return False 135 | 136 | 137 | def has_required_amount_of_memory(): 138 | minimum_memory = int(2048 * 1024 * 1024) 139 | cmd = ["/usr/sbin/sysctl", "-n", "hw.memsize"] 140 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 141 | (results, err) = p.communicate() 142 | actual_memory = int(results) 143 | actual_memory_gigabytes = actual_memory / 1024 / 1024 / 1024 144 | if actual_memory >= minimum_memory: 145 | logger("Memory", 146 | "%i GB physical memory installed" % actual_memory_gigabytes, 147 | "OK") 148 | return True 149 | else: 150 | logger("Memory", 151 | "%i GB installed, 2 GB required" % actual_memory_gigabytes, 152 | "Failed") 153 | return False 154 | 155 | 156 | def is_virtual_machine(): 157 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 158 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 159 | (results, err) = p.communicate() 160 | for feature in results.split(): 161 | if feature == "VMM": 162 | logger("Board ID", 163 | "Virtual machine", 164 | "OK") 165 | return True 166 | return False 167 | 168 | 169 | def is_supported_board_id(): 170 | if is_virtual_machine(): 171 | return True 172 | platform_support_values = [ 173 | "Mac-00BE6ED71E35EB86", 174 | "Mac-031AEE4D24BFF0B1", 175 | "Mac-031B6874CF7F642A", 176 | "Mac-06F11F11946D27C5", 177 | "Mac-06F11FD93F0323C5", 178 | "Mac-189A3D4F975D5FFC", 179 | "Mac-27ADBB7B4CEE8E61", 180 | "Mac-2BD1B31983FE1663", 181 | "Mac-2E6FAB96566FE58C", 182 | "Mac-35C1E88140C3E6CF", 183 | "Mac-35C5E08120C7EEAF", 184 | "Mac-3CBD00234E554E41", 185 | "Mac-42FD25EABCABB274", 186 | "Mac-4B7AC7E43945597E", 187 | "Mac-4BC72D62AD45599E", 188 | "Mac-50619A408DB004DA", 189 | "Mac-66F35F19FE2A0D05", 190 | "Mac-6F01561E16C75D06", 191 | "Mac-742912EFDBEE19B3", 192 | "Mac-77EB7D7DAF985301", 193 | "Mac-7BA5B2794B2CDB12", 194 | "Mac-7DF21CB3ED6977E5", 195 | "Mac-7DF2A3B5E5D671ED", 196 | "Mac-81E3E92DD6088272", 197 | "Mac-8ED6AF5B48C039E1", 198 | "Mac-937CB26E2E02BB01", 199 | "Mac-942452F5819B1C1B", 200 | "Mac-942459F5819B171B", 201 | "Mac-94245A3940C91C80", 202 | "Mac-94245B3640C91C81", 203 | "Mac-942B59F58194171B", 204 | "Mac-942B5BF58194151B", 205 | "Mac-942C5DF58193131B", 206 | "Mac-9F18E312C5C2BF0B", 207 | "Mac-AFD8A9D944EA4843", 208 | "Mac-BE0E8AC46FE800CC", 209 | "Mac-C08A6BB70A942AC2", 210 | "Mac-C3EC7CD22292981F", 211 | "Mac-E43C1C25D4880AD6", 212 | "Mac-F2208EC8", 213 | "Mac-F2218EA9", 214 | "Mac-F2218EC8", 215 | "Mac-F2218FA9", 216 | "Mac-F2218FC8", 217 | "Mac-F221BEC8", 218 | "Mac-F221DCC8", 219 | "Mac-F222BEC8", 220 | "Mac-F2238AC8", 221 | "Mac-F2238BAE", 222 | "Mac-F223BEC8", 223 | "Mac-F22586C8", 224 | "Mac-F22587A1", 225 | "Mac-F22587C8", 226 | "Mac-F22589C8", 227 | "Mac-F2268AC8", 228 | "Mac-F2268CC8", 229 | "Mac-F2268DAE", 230 | "Mac-F2268DC8", 231 | "Mac-F2268EC8", 232 | "Mac-F226BEC8", 233 | "Mac-F22788AA", 234 | "Mac-F227BEC8", 235 | "Mac-F22C86C8", 236 | "Mac-F22C89C8", 237 | "Mac-F22C8AC8", 238 | "Mac-F305150B0C7DEEEF", 239 | "Mac-F42386C8", 240 | "Mac-F42388C8", 241 | "Mac-F4238BC8", 242 | "Mac-F4238CC8", 243 | "Mac-F42C86C8", 244 | "Mac-F42C88C8", 245 | "Mac-F42C89C8", 246 | "Mac-F42D86A9", 247 | "Mac-F42D86C8", 248 | "Mac-F42D88C8", 249 | "Mac-F42D89A9", 250 | "Mac-F42D89C8", 251 | "Mac-F60DEB81FF30ACF6", 252 | "Mac-F65AE981FFA204ED", 253 | "Mac-FA842E06C61E91C5", 254 | "Mac-FC02E91DDD3FA6A4"] 255 | board_id = get_board_id() 256 | if board_id in platform_support_values: 257 | logger("Board ID", 258 | board_id, 259 | "OK") 260 | return True 261 | else: 262 | logger("Board ID", 263 | "\"%s\" is not supported" % board_id, 264 | "Failed") 265 | return False 266 | 267 | 268 | def append_conditional_items(dictionary): 269 | current_conditional_items_path = conditional_items_path() 270 | if os.path.exists(current_conditional_items_path): 271 | existing_dict = plistlib.readPlist(current_conditional_items_path) 272 | output_dict = dict(existing_dict.items() + dictionary.items()) 273 | else: 274 | output_dict = dictionary 275 | plistlib.writePlist(output_dict, current_conditional_items_path) 276 | pass 277 | 278 | 279 | 280 | def main(argv=None): 281 | elcapitan_supported_dict = {} 282 | elcapitan_needs_fw_update_dict = {} 283 | 284 | # Run the checks 285 | board_id_passed = is_supported_board_id() 286 | memory_passed = has_required_amount_of_memory() 287 | cpu_passed = is_64bit_capable() 288 | system_version_passed = is_system_version_supported() 289 | 290 | if board_id_passed and memory_passed and cpu_passed and system_version_passed: 291 | elcapitan_supported = 0 292 | elcapitan_supported_dict = {'elcapitan_supported': True} 293 | else: 294 | elcapitan_supported = 1 295 | elcapitan_supported_dict = {'elcapitan_supported': False} 296 | 297 | # Update "ConditionalItems.plist" if munki is installed 298 | if munki_installed() and update_munki_conditional_items: 299 | append_conditional_items(elcapitan_supported_dict) 300 | 301 | # Exit codes: 302 | # 0 = El Capitan is supported 303 | # 1 = El Capitan is not supported 304 | return elcapitan_supported 305 | 306 | 307 | if __name__ == '__main__': 308 | sys.exit(main()) 309 | -------------------------------------------------------------------------------- /check-10.12-sierra-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-1012-sierra-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with macOS 10.12 Sierra. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # distribution file of OSInstall.mpkg installer package. The OSInstall.mpkg can be 10 | # found in the root of InstallESD disk image. 11 | # 12 | # The checks done by this script are (in order): 13 | # - Machine is a virtual machine or has a specific supported board-id 14 | # - Machine model is not in a list of unsupported models 15 | # - Current system version is less than 10.12 and at least 10.7.5 16 | # 17 | # Exit codes: 18 | # 0 = Sierra is supported 19 | # 1 = Sierra is not supported 20 | # 21 | # 22 | # Hannes Juutilainen 23 | # https://github.com/hjuutilainen/adminscripts 24 | # 25 | # ================================================================================ 26 | 27 | import sys 28 | import subprocess 29 | import os 30 | import re 31 | import plistlib 32 | from distutils.version import StrictVersion 33 | 34 | 35 | # ================================================================================ 36 | # Start configuration 37 | # ================================================================================ 38 | 39 | # Set this to False if you don't want any output, just the exit codes 40 | verbose = True 41 | 42 | # Set this to True if you want to add "sierra_supported" custom conditional to 43 | # /Library/Managed Installs/ConditionalItems.plist 44 | update_munki_conditional_items = False 45 | 46 | # ================================================================================ 47 | # End configuration 48 | # ================================================================================ 49 | 50 | 51 | def logger(message, status, info): 52 | if verbose: 53 | print "%14s: %-40s [%s]" % (message, status, info) 54 | pass 55 | 56 | 57 | def conditional_items_path(): 58 | # 59 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 60 | 61 | cmd = [ 62 | "/usr/bin/defaults", 63 | "read", 64 | "/Library/Preferences/ManagedInstalls", 65 | "ManagedInstallDir" 66 | ] 67 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 68 | (results, err) = p.communicate() 69 | managed_installs_dir = results.strip() 70 | 71 | # Make sure we're outputting our information to "ConditionalItems.plist" 72 | if managed_installs_dir: 73 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 74 | else: 75 | # Munki default 76 | return "/Library/Managed Installs/ConditionalItems.plist" 77 | 78 | 79 | def munki_installed(): 80 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 81 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 82 | output = p.communicate()[0] 83 | if p.returncode == 0: 84 | return True 85 | else: 86 | return False 87 | 88 | 89 | def is_system_version_supported(): 90 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 91 | product_name = system_version_plist['ProductName'] 92 | product_version = system_version_plist['ProductVersion'] 93 | if StrictVersion(product_version) >= StrictVersion('10.12'): 94 | logger("System", 95 | "%s %s" % (product_name, product_version), 96 | "Failed") 97 | return False 98 | elif StrictVersion(product_version) >= StrictVersion('10.7.5'): 99 | logger("System", 100 | "%s %s" % (product_name, product_version), 101 | "OK") 102 | return True 103 | else: 104 | logger("System", 105 | "%s %s" % (product_name, product_version), 106 | "Failed") 107 | return False 108 | 109 | 110 | def get_board_id(): 111 | cmd = ["/usr/sbin/ioreg", 112 | "-p", "IODeviceTree", 113 | "-r", 114 | "-n", "/", 115 | "-d", "1"] 116 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 117 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 118 | (results, err) = p2.communicate() 119 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 120 | board_id = board_id.strip() 121 | if board_id.startswith('Mac'): 122 | return board_id 123 | else: 124 | return None 125 | 126 | 127 | def is_virtual_machine(): 128 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 129 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 130 | (results, err) = p.communicate() 131 | for feature in results.split(): 132 | if feature == "VMM": 133 | logger("Board ID", 134 | "Virtual machine", 135 | "OK") 136 | return True 137 | return False 138 | 139 | 140 | def get_current_model(): 141 | cmd = ["/usr/sbin/sysctl", "-n", "hw.model"] 142 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 143 | (results, err) = p.communicate() 144 | return results.strip() 145 | 146 | 147 | def is_supported_model(): 148 | non_supported_models = [ 149 | 'iMac4,1', 150 | 'iMac4,2', 151 | 'iMac5,1', 152 | 'iMac5,2', 153 | 'iMac6,1', 154 | 'iMac7,1', 155 | 'iMac8,1', 156 | 'iMac9,1', 157 | 'MacBook1,1', 158 | 'MacBook2,1', 159 | 'MacBook3,1', 160 | 'MacBook4,1', 161 | 'MacBook5,1', 162 | 'MacBook5,2', 163 | 'MacBookAir1,1', 164 | 'MacBookAir2,1', 165 | 'MacBookPro1,1', 166 | 'MacBookPro1,2', 167 | 'MacBookPro2,1', 168 | 'MacBookPro2,2', 169 | 'MacBookPro3,1', 170 | 'MacBookPro4,1', 171 | 'MacBookPro5,1', 172 | 'MacBookPro5,2', 173 | 'MacBookPro5,3', 174 | 'MacBookPro5,4', 175 | 'MacBookPro5,5', 176 | 'Macmini1,1', 177 | 'Macmini2,1', 178 | 'Macmini3,1', 179 | 'MacPro1,1', 180 | 'MacPro2,1', 181 | 'MacPro3,1', 182 | 'MacPro4,1', 183 | 'Xserve1,1', 184 | 'Xserve2,1', 185 | 'Xserve3,1'] 186 | current_model = get_current_model() 187 | if current_model in non_supported_models: 188 | logger("Model", 189 | "\"%s\" is not supported" % current_model, 190 | "Failed") 191 | return False 192 | else: 193 | logger("Model", 194 | current_model, 195 | "OK") 196 | return True 197 | 198 | 199 | def is_supported_board_id(): 200 | platform_support_values = [ 201 | 'Mac-00BE6ED71E35EB86', 202 | 'Mac-031AEE4D24BFF0B1', 203 | 'Mac-031B6874CF7F642A', 204 | 'Mac-06F11F11946D27C5', 205 | 'Mac-06F11FD93F0323C5', 206 | 'Mac-189A3D4F975D5FFC', 207 | 'Mac-27ADBB7B4CEE8E61', 208 | 'Mac-2BD1B31983FE1663', 209 | 'Mac-2E6FAB96566FE58C', 210 | 'Mac-35C1E88140C3E6CF', 211 | 'Mac-35C5E08120C7EEAF', 212 | 'Mac-3CBD00234E554E41', 213 | 'Mac-42FD25EABCABB274', 214 | 'Mac-473D31EABEB93F9B', 215 | 'Mac-4B682C642B45593E', 216 | 'Mac-4B7AC7E43945597E', 217 | 'Mac-4BC72D62AD45599E', 218 | 'Mac-50619A408DB004DA', 219 | 'Mac-551B86E5744E2388', 220 | 'Mac-65CE76090165799A', 221 | 'Mac-66E35819EE2D0D05', 222 | 'Mac-66F35F19FE2A0D05', 223 | 'Mac-6F01561E16C75D06', 224 | 'Mac-742912EFDBEE19B3', 225 | 'Mac-77EB7D7DAF985301', 226 | 'Mac-77F17D7DA9285301', 227 | 'Mac-7BA5B2794B2CDB12', 228 | 'Mac-7DF21CB3ED6977E5', 229 | 'Mac-7DF2A3B5E5D671ED', 230 | 'Mac-81E3E92DD6088272', 231 | 'Mac-8ED6AF5B48C039E1', 232 | 'Mac-937CB26E2E02BB01', 233 | 'Mac-942452F5819B1C1B', 234 | 'Mac-942459F5819B171B', 235 | 'Mac-94245A3940C91C80', 236 | 'Mac-94245B3640C91C81', 237 | 'Mac-942B59F58194171B', 238 | 'Mac-942B5BF58194151B', 239 | 'Mac-942C5DF58193131B', 240 | 'Mac-9AE82516C7C6B903', 241 | 'Mac-9F18E312C5C2BF0B', 242 | 'Mac-A369DDC4E67F1C45', 243 | 'Mac-A5C67F76ED83108C', 244 | 'Mac-AFD8A9D944EA4843', 245 | 'Mac-B4831CEBD52A0C4C', 246 | 'Mac-B809C3757DA9BB8D', 247 | 'Mac-BE088AF8C5EB4FA2', 248 | 'Mac-BE0E8AC46FE800CC', 249 | 'Mac-C08A6BB70A942AC2', 250 | 'Mac-C3EC7CD22292981F', 251 | 'Mac-CAD6701F7CEA0921', 252 | 'Mac-DB15BD556843C820', 253 | 'Mac-E43C1C25D4880AD6', 254 | 'Mac-EE2EBD4B90B839A8', 255 | 'Mac-F2208EC8', 256 | 'Mac-F221BEC8', 257 | 'Mac-F221DCC8', 258 | 'Mac-F222BEC8', 259 | 'Mac-F2238AC8', 260 | 'Mac-F2238BAE', 261 | 'Mac-F22586C8', 262 | 'Mac-F22589C8', 263 | 'Mac-F2268CC8', 264 | 'Mac-F2268DAE', 265 | 'Mac-F2268DC8', 266 | 'Mac-F22C89C8', 267 | 'Mac-F22C8AC8', 268 | 'Mac-F305150B0C7DEEEF', 269 | 'Mac-F60DEB81FF30ACF6', 270 | 'Mac-F65AE981FFA204ED', 271 | 'Mac-FA842E06C61E91C5', 272 | 'Mac-FC02E91DDD3FA6A4', 273 | 'Mac-FFE5EF870D7BA81A'] 274 | board_id = get_board_id() 275 | if board_id in platform_support_values: 276 | logger("Board ID", 277 | board_id, 278 | "OK") 279 | return True 280 | else: 281 | logger("Board ID", 282 | "\"%s\" is not supported" % board_id, 283 | "Failed") 284 | return False 285 | 286 | 287 | def append_conditional_items(dictionary): 288 | current_conditional_items_path = conditional_items_path() 289 | if os.path.exists(current_conditional_items_path): 290 | existing_dict = plistlib.readPlist(current_conditional_items_path) 291 | output_dict = dict(existing_dict.items() + dictionary.items()) 292 | else: 293 | output_dict = dictionary 294 | plistlib.writePlist(output_dict, current_conditional_items_path) 295 | pass 296 | 297 | 298 | def main(argv=None): 299 | sierra_supported_dict = {} 300 | 301 | # Run the checks 302 | model_passed = is_supported_model() 303 | board_id_passed = is_supported_board_id() 304 | system_version_passed = is_system_version_supported() 305 | 306 | if is_virtual_machine(): 307 | sierra_supported = 0 308 | sierra_supported_dict = {'sierra_supported': True} 309 | elif model_passed and board_id_passed and system_version_passed: 310 | sierra_supported = 0 311 | sierra_supported_dict = {'sierra_supported': True} 312 | else: 313 | sierra_supported = 1 314 | sierra_supported_dict = {'sierra_supported': False} 315 | 316 | # Update "ConditionalItems.plist" if munki is installed 317 | if munki_installed() and update_munki_conditional_items: 318 | append_conditional_items(sierra_supported_dict) 319 | 320 | # Exit codes: 321 | # 0 = Sierra is supported 322 | # 1 = Sierra is not supported 323 | return sierra_supported 324 | 325 | 326 | if __name__ == '__main__': 327 | sys.exit(main()) 328 | -------------------------------------------------------------------------------- /check-10.13-highsierra-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-10.13-highsierra-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with macOS 10.13 High Sierra. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # distribution file of OSInstall.mpkg installer package. 10 | 11 | # The OSInstall.mpkg can be found in the Packages directory of InstallESD disk image: 12 | # /Applications/Install macOS High Sierra.app/Contents/SharedSupport/InstallESD.dmg 13 | # -> /Volumes/InstallESD/Packages/OSInstall.mpkg 14 | # 15 | # The checks done by this script are (in order): 16 | # - Machine is a virtual machine or has a specific supported board-id 17 | # - Machine model is not in a list of unsupported models 18 | # - Current system version is less than 10.13 and at least 10.8 19 | # 20 | # Exit codes: 21 | # 0 = High Sierra is supported 22 | # 1 = High Sierra is not supported 23 | # 24 | # 25 | # Hannes Juutilainen 26 | # https://github.com/hjuutilainen/adminscripts 27 | # 28 | # Thanks to Ralph Cyranka, Ed Bobak and @tcinbis 29 | # 30 | # ================================================================================ 31 | 32 | import sys 33 | import subprocess 34 | import os 35 | import re 36 | import plistlib 37 | from distutils.version import StrictVersion 38 | 39 | 40 | # ================================================================================ 41 | # Start configuration 42 | # ================================================================================ 43 | 44 | # Set this to False if you don't want any output, just the exit codes 45 | verbose = True 46 | 47 | # Set this to True if you want to add "high_sierra_supported" custom conditional to 48 | # /Library/Managed Installs/ConditionalItems.plist 49 | update_munki_conditional_items = False 50 | 51 | # ================================================================================ 52 | # End configuration 53 | # ================================================================================ 54 | 55 | 56 | def logger(message, status, info): 57 | if verbose: 58 | print "%14s: %-40s [%s]" % (message, status, info) 59 | pass 60 | 61 | 62 | def conditional_items_path(): 63 | # 64 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 65 | 66 | cmd = [ 67 | "/usr/bin/defaults", 68 | "read", 69 | "/Library/Preferences/ManagedInstalls", 70 | "ManagedInstallDir" 71 | ] 72 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 73 | (results, err) = p.communicate() 74 | managed_installs_dir = results.strip() 75 | 76 | # Make sure we're outputting our information to "ConditionalItems.plist" 77 | if managed_installs_dir: 78 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 79 | else: 80 | # Munki default 81 | return "/Library/Managed Installs/ConditionalItems.plist" 82 | 83 | 84 | def munki_installed(): 85 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 86 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 87 | output = p.communicate()[0] 88 | if p.returncode == 0: 89 | return True 90 | else: 91 | return False 92 | 93 | 94 | def is_system_version_supported(): 95 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 96 | product_name = system_version_plist['ProductName'] 97 | product_version = system_version_plist['ProductVersion'] 98 | if StrictVersion(product_version) >= StrictVersion('10.13'): 99 | logger("System", 100 | "%s %s" % (product_name, product_version), 101 | "Failed") 102 | return False 103 | elif StrictVersion(product_version) >= StrictVersion('10.8'): 104 | logger("System", 105 | "%s %s" % (product_name, product_version), 106 | "OK") 107 | return True 108 | else: 109 | logger("System", 110 | "%s %s" % (product_name, product_version), 111 | "Failed") 112 | return False 113 | 114 | 115 | def get_board_id(): 116 | cmd = ["/usr/sbin/ioreg", 117 | "-p", "IODeviceTree", 118 | "-r", 119 | "-n", "/", 120 | "-d", "1"] 121 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 122 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 123 | (results, err) = p2.communicate() 124 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 125 | board_id = board_id.strip() 126 | if board_id.startswith('Mac'): 127 | return board_id 128 | else: 129 | return None 130 | 131 | 132 | def is_virtual_machine(): 133 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 134 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 135 | (results, err) = p.communicate() 136 | for feature in results.split(): 137 | if feature == "VMM": 138 | logger("Board ID", 139 | "Virtual machine", 140 | "OK") 141 | return True 142 | return False 143 | 144 | 145 | def get_current_model(): 146 | cmd = ["/usr/sbin/sysctl", "-n", "hw.model"] 147 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 148 | (results, err) = p.communicate() 149 | return results.strip() 150 | 151 | 152 | def is_supported_model(): 153 | non_supported_models = [ 154 | 'iMac4,1', 155 | 'iMac4,2', 156 | 'iMac5,1', 157 | 'iMac5,2', 158 | 'iMac6,1', 159 | 'iMac7,1', 160 | 'iMac8,1', 161 | 'iMac9,1', 162 | 'MacBook1,1', 163 | 'MacBook2,1', 164 | 'MacBook3,1', 165 | 'MacBook4,1', 166 | 'MacBook5,1', 167 | 'MacBook5,2', 168 | 'MacBookAir1,1', 169 | 'MacBookAir2,1', 170 | 'MacBookPro1,1', 171 | 'MacBookPro1,2', 172 | 'MacBookPro2,1', 173 | 'MacBookPro2,2', 174 | 'MacBookPro3,1', 175 | 'MacBookPro4,1', 176 | 'MacBookPro5,1', 177 | 'MacBookPro5,2', 178 | 'MacBookPro5,3', 179 | 'MacBookPro5,4', 180 | 'MacBookPro5,5', 181 | 'Macmini1,1', 182 | 'Macmini2,1', 183 | 'Macmini3,1', 184 | 'MacPro1,1', 185 | 'MacPro2,1', 186 | 'MacPro3,1', 187 | 'MacPro4,1', 188 | 'Xserve1,1', 189 | 'Xserve2,1', 190 | 'Xserve3,1'] 191 | current_model = get_current_model() 192 | if current_model in non_supported_models: 193 | logger("Model", 194 | "\"%s\" is not supported" % current_model, 195 | "Failed") 196 | return False 197 | else: 198 | logger("Model", 199 | current_model, 200 | "OK") 201 | return True 202 | 203 | 204 | def is_supported_board_id(): 205 | platform_support_values = [ 206 | 'Mac-00BE6ED71E35EB86', 207 | 'Mac-031AEE4D24BFF0B1', 208 | 'Mac-031B6874CF7F642A', 209 | 'Mac-06F11F11946D27C5', 210 | 'Mac-06F11FD93F0323C5', 211 | 'Mac-189A3D4F975D5FFC', 212 | 'Mac-27ADBB7B4CEE8E61', 213 | 'Mac-2BD1B31983FE1663', 214 | 'Mac-2E6FAB96566FE58C', 215 | 'Mac-35C1E88140C3E6CF', 216 | 'Mac-35C5E08120C7EEAF', 217 | 'Mac-3CBD00234E554E41', 218 | 'Mac-42FD25EABCABB274', 219 | 'Mac-473D31EABEB93F9B', 220 | 'Mac-4B682C642B45593E', 221 | 'Mac-4B7AC7E43945597E', 222 | 'Mac-4BC72D62AD45599E', 223 | 'Mac-50619A408DB004DA', 224 | 'Mac-551B86E5744E2388', 225 | 'Mac-65CE76090165799A', 226 | 'Mac-66E35819EE2D0D05', 227 | 'Mac-66F35F19FE2A0D05', 228 | 'Mac-6F01561E16C75D06', 229 | 'Mac-742912EFDBEE19B3', 230 | 'Mac-77EB7D7DAF985301', 231 | 'Mac-77F17D7DA9285301', 232 | 'Mac-7BA5B2794B2CDB12', 233 | 'Mac-7BA5B2D9E42DDD94', 234 | 'Mac-7DF21CB3ED6977E5', 235 | 'Mac-7DF2A3B5E5D671ED', 236 | 'Mac-81E3E92DD6088272', 237 | 'Mac-8ED6AF5B48C039E1', 238 | 'Mac-90BE64C3CB5A9AEB', 239 | 'Mac-937CB26E2E02BB01', 240 | 'Mac-942452F5819B1C1B', 241 | 'Mac-942459F5819B171B', 242 | 'Mac-94245A3940C91C80', 243 | 'Mac-94245B3640C91C81', 244 | 'Mac-942B59F58194171B', 245 | 'Mac-942B5BF58194151B', 246 | 'Mac-942C5DF58193131B', 247 | 'Mac-9AE82516C7C6B903', 248 | 'Mac-9F18E312C5C2BF0B', 249 | 'Mac-A369DDC4E67F1C45', 250 | 'Mac-A5C67F76ED83108C', 251 | 'Mac-AFD8A9D944EA4843', 252 | 'Mac-B4831CEBD52A0C4C', 253 | 'Mac-B809C3757DA9BB8D', 254 | 'Mac-BE088AF8C5EB4FA2', 255 | 'Mac-BE0E8AC46FE800CC', 256 | 'Mac-C08A6BB70A942AC2', 257 | 'Mac-C3EC7CD22292981F', 258 | 'Mac-CAD6701F7CEA0921', 259 | 'Mac-CF21D135A7D34AA6', 260 | 'Mac-DB15BD556843C820', 261 | 'Mac-E43C1C25D4880AD6', 262 | 'Mac-EE2EBD4B90B839A8', 263 | 'Mac-F2208EC8', 264 | 'Mac-F221BEC8', 265 | 'Mac-F221DCC8', 266 | 'Mac-F222BEC8', 267 | 'Mac-F2238AC8', 268 | 'Mac-F2238BAE', 269 | 'Mac-F22586C8', 270 | 'Mac-F22589C8', 271 | 'Mac-F2268CC8', 272 | 'Mac-F2268DAE', 273 | 'Mac-F2268DC8', 274 | 'Mac-F22C89C8', 275 | 'Mac-F22C8AC8', 276 | 'Mac-F305150B0C7DEEEF', 277 | 'Mac-F60DEB81FF30ACF6', 278 | 'Mac-F65AE981FFA204ED', 279 | 'Mac-FA842E06C61E91C5', 280 | 'Mac-FC02E91DDD3FA6A4', 281 | 'Mac-FFE5EF870D7BA81A'] 282 | board_id = get_board_id() 283 | if board_id in platform_support_values: 284 | logger("Board ID", 285 | board_id, 286 | "OK") 287 | return True 288 | else: 289 | logger("Board ID", 290 | "\"%s\" is not supported" % board_id, 291 | "Failed") 292 | return False 293 | 294 | 295 | def append_conditional_items(dictionary): 296 | current_conditional_items_path = conditional_items_path() 297 | if os.path.exists(current_conditional_items_path): 298 | existing_dict = plistlib.readPlist(current_conditional_items_path) 299 | output_dict = dict(existing_dict.items() + dictionary.items()) 300 | else: 301 | output_dict = dictionary 302 | plistlib.writePlist(output_dict, current_conditional_items_path) 303 | pass 304 | 305 | 306 | def main(argv=None): 307 | high_sierra_supported_dict = {} 308 | 309 | # Run the checks 310 | model_passed = is_supported_model() 311 | board_id_passed = is_supported_board_id() 312 | system_version_passed = is_system_version_supported() 313 | 314 | if is_virtual_machine(): 315 | high_sierra_supported = 0 316 | high_sierra_supported_dict = {'high_sierra_supported': True} 317 | elif model_passed and board_id_passed and system_version_passed: 318 | high_sierra_supported = 0 319 | high_sierra_supported_dict = {'high_sierra_supported': True} 320 | else: 321 | high_sierra_supported = 1 322 | high_sierra_supported_dict = {'high_sierra_supported': False} 323 | 324 | # Update "ConditionalItems.plist" if munki is installed 325 | if munki_installed() and update_munki_conditional_items: 326 | append_conditional_items(high_sierra_supported_dict) 327 | 328 | # Exit codes: 329 | # 0 = High Sierra is supported 330 | # 1 = High Sierra is not supported 331 | return high_sierra_supported 332 | 333 | 334 | if __name__ == '__main__': 335 | sys.exit(main()) 336 | -------------------------------------------------------------------------------- /check-10.14-mojave-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-10.14-mojave-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with macOS 10.14 Mojave. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # distribution file of OSInstall.mpkg installer package. 10 | 11 | # The OSInstall.mpkg can be found in the Packages directory of InstallESD disk image: 12 | # /Applications/Install macOS Mojave.app/Contents/SharedSupport/InstallESD.dmg 13 | # -> /Volumes/InstallESD/Packages/OSInstall.mpkg 14 | # 15 | # The checks done by this script are (in order): 16 | # - Machine is a virtual machine or has a specific supported board-id 17 | # - Machine model is not in a list of unsupported models 18 | # - Current system version is less than 10.13 and at least 10.8 19 | # 20 | # Exit codes: 21 | # 0 = Mojave is supported 22 | # 1 = Mojave is not supported 23 | # 24 | # 25 | # Hannes Juutilainen 26 | # https://github.com/hjuutilainen/adminscripts 27 | # 28 | # Thanks to Ralph Cyranka, Ed Bobak and @tcinbis 29 | # 30 | # ================================================================================ 31 | 32 | import sys 33 | import subprocess 34 | import os 35 | import re 36 | import plistlib 37 | from distutils.version import StrictVersion 38 | 39 | 40 | # ================================================================================ 41 | # Start configuration 42 | # ================================================================================ 43 | 44 | # Set this to False if you don't want any output, just the exit codes 45 | verbose = True 46 | 47 | # Set this to True if you want to add "mojave_supported" custom conditional to 48 | # /Library/Managed Installs/ConditionalItems.plist 49 | update_munki_conditional_items = False 50 | 51 | # ================================================================================ 52 | # End configuration 53 | # ================================================================================ 54 | 55 | 56 | def logger(message, status, info): 57 | if verbose: 58 | print "%14s: %-40s [%s]" % (message, status, info) 59 | pass 60 | 61 | 62 | def conditional_items_path(): 63 | # 64 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 65 | 66 | cmd = [ 67 | "/usr/bin/defaults", 68 | "read", 69 | "/Library/Preferences/ManagedInstalls", 70 | "ManagedInstallDir" 71 | ] 72 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 73 | (results, err) = p.communicate() 74 | managed_installs_dir = results.strip() 75 | 76 | # Make sure we're outputting our information to "ConditionalItems.plist" 77 | if managed_installs_dir: 78 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 79 | else: 80 | # Munki default 81 | return "/Library/Managed Installs/ConditionalItems.plist" 82 | 83 | 84 | def munki_installed(): 85 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 86 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 87 | output = p.communicate()[0] 88 | if p.returncode == 0: 89 | return True 90 | else: 91 | return False 92 | 93 | 94 | def is_system_version_supported(): 95 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 96 | product_name = system_version_plist['ProductName'] 97 | product_version = system_version_plist['ProductVersion'] 98 | if StrictVersion(product_version) >= StrictVersion('10.14'): 99 | logger("System", 100 | "%s %s" % (product_name, product_version), 101 | "Failed") 102 | return False 103 | elif StrictVersion(product_version) >= StrictVersion('10.8'): 104 | logger("System", 105 | "%s %s" % (product_name, product_version), 106 | "OK") 107 | return True 108 | else: 109 | logger("System", 110 | "%s %s" % (product_name, product_version), 111 | "Failed") 112 | return False 113 | 114 | 115 | def get_board_id(): 116 | cmd = ["/usr/sbin/ioreg", 117 | "-p", "IODeviceTree", 118 | "-r", 119 | "-n", "/", 120 | "-d", "1"] 121 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 122 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 123 | (results, err) = p2.communicate() 124 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 125 | board_id = board_id.strip() 126 | if board_id.startswith('Mac'): 127 | return board_id 128 | else: 129 | return None 130 | 131 | 132 | def is_virtual_machine(): 133 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 134 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 135 | (results, err) = p.communicate() 136 | for feature in results.split(): 137 | if feature == "VMM": 138 | logger("Board ID", 139 | "Virtual machine", 140 | "OK") 141 | return True 142 | return False 143 | 144 | 145 | def get_current_model(): 146 | cmd = ["/usr/sbin/sysctl", "-n", "hw.model"] 147 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 148 | (results, err) = p.communicate() 149 | return results.strip() 150 | 151 | 152 | def is_supported_model(): 153 | non_supported_models = [ 154 | 'MacBookPro4,1', 155 | 'MacPro2,1', 156 | 'Macmini5,2', 157 | 'Macmini5,1', 158 | 'MacBookPro5,1', 159 | 'MacBookPro1,1', 160 | 'MacBookPro5,3', 161 | 'MacBookPro5,2', 162 | 'iMac8,1', 163 | 'MacBookPro5,4', 164 | 'MacBookAir4,2', 165 | 'Macmini2,1', 166 | 'iMac5,2', 167 | 'iMac11,3', 168 | 'MacBookPro8,2', 169 | 'MacBookPro3,1', 170 | 'Macmini5,3', 171 | 'MacBookPro1,2', 172 | 'Macmini4,1', 173 | 'iMac9,1', 174 | 'iMac6,1', 175 | 'Macmini3,1', 176 | 'Macmini1,1', 177 | 'MacBookPro6,1', 178 | 'MacBookPro2,2', 179 | 'MacBookPro2,1', 180 | 'iMac12,2', 181 | 'MacBook3,1', 182 | 'MacPro3,1', 183 | 'MacBook5,1', 184 | 'MacBook5,2', 185 | 'iMac11,1', 186 | 'iMac10,1', 187 | 'MacBookPro7,1', 188 | 'MacBook2,1', 189 | 'MacBookAir4,1', 190 | 'MacPro4,1', 191 | 'MacBookPro6,2', 192 | 'iMac12,1', 193 | 'MacBook1,1', 194 | 'MacBookPro5,5', 195 | 'iMac11,2', 196 | 'iMac4,2', 197 | 'Xserve2,1', 198 | 'MacBookAir3,1', 199 | 'MacBookAir3,2', 200 | 'MacBookAir1,1', 201 | 'Xserve3,1', 202 | 'iMac4,1', 203 | 'MacBookAir2,1', 204 | 'Xserve1,1', 205 | 'iMac5,1', 206 | 'MacBookPro8,1', 207 | 'MacBook7,1', 208 | 'MacBookPro8,3', 209 | 'iMac7,1', 210 | 'MacBook6,1', 211 | 'MacBook4,1', 212 | 'MacPro1,1', 213 | ] 214 | current_model = get_current_model() 215 | if current_model in non_supported_models: 216 | logger("Model", 217 | "\"%s\" is not supported" % current_model, 218 | "Failed") 219 | return False 220 | else: 221 | logger("Model", 222 | current_model, 223 | "OK") 224 | return True 225 | 226 | 227 | def is_supported_board_id(): 228 | platform_support_values = [ 229 | 'Mac-06F11F11946D27C5', 230 | 'Mac-031B6874CF7F642A', 231 | 'Mac-CAD6701F7CEA0921', 232 | 'Mac-50619A408DB004DA', 233 | 'Mac-7BA5B2D9E42DDD94', 234 | 'Mac-473D31EABEB93F9B', 235 | 'Mac-AFD8A9D944EA4843', 236 | 'Mac-B809C3757DA9BB8D', 237 | 'Mac-7DF2A3B5E5D671ED', 238 | 'Mac-35C1E88140C3E6CF', 239 | 'Mac-77EB7D7DAF985301', 240 | 'Mac-2E6FAB96566FE58C', 241 | 'Mac-827FB448E656EC26', 242 | 'Mac-BE0E8AC46FE800CC', 243 | 'Mac-00BE6ED71E35EB86', 244 | 'Mac-4B7AC7E43945597E', 245 | 'Mac-5A49A77366F81C72', 246 | 'Mac-35C5E08120C7EEAF', 247 | 'Mac-FFE5EF870D7BA81A', 248 | 'Mac-C6F71043CEAA02A6', 249 | 'Mac-4B682C642B45593E', 250 | 'Mac-90BE64C3CB5A9AEB', 251 | 'Mac-66F35F19FE2A0D05', 252 | 'Mac-189A3D4F975D5FFC', 253 | 'Mac-B4831CEBD52A0C4C', 254 | 'Mac-FA842E06C61E91C5', 255 | 'Mac-FC02E91DDD3FA6A4', 256 | 'Mac-06F11FD93F0323C5', 257 | 'Mac-9AE82516C7C6B903', 258 | 'Mac-27ADBB7B4CEE8E61', 259 | 'Mac-6F01561E16C75D06', 260 | 'Mac-F60DEB81FF30ACF6', 261 | 'Mac-81E3E92DD6088272', 262 | 'Mac-7DF21CB3ED6977E5', 263 | 'Mac-937CB26E2E02BB01', 264 | 'Mac-3CBD00234E554E41', 265 | 'Mac-F221BEC8', 266 | 'Mac-9F18E312C5C2BF0B', 267 | 'Mac-65CE76090165799A', 268 | 'Mac-CF21D135A7D34AA6', 269 | 'Mac-F65AE981FFA204ED', 270 | 'Mac-112B0A653D3AAB9C', 271 | 'Mac-DB15BD556843C820', 272 | 'Mac-937A206F2EE63C01', 273 | 'Mac-77F17D7DA9285301', 274 | 'Mac-C3EC7CD22292981F', 275 | 'Mac-BE088AF8C5EB4FA2', 276 | 'Mac-551B86E5744E2388', 277 | 'Mac-A5C67F76ED83108C', 278 | 'Mac-031AEE4D24BFF0B1', 279 | 'Mac-EE2EBD4B90B839A8', 280 | 'Mac-42FD25EABCABB274', 281 | 'Mac-F305150B0C7DEEEF', 282 | 'Mac-2BD1B31983FE1663', 283 | 'Mac-66E35819EE2D0D05', 284 | 'Mac-A369DDC4E67F1C45', 285 | 'Mac-E43C1C25D4880AD6', 286 | ] 287 | board_id = get_board_id() 288 | if board_id in platform_support_values: 289 | logger("Board ID", 290 | board_id, 291 | "OK") 292 | return True 293 | else: 294 | logger("Board ID", 295 | "\"%s\" is not supported" % board_id, 296 | "Failed") 297 | return False 298 | 299 | 300 | def append_conditional_items(dictionary): 301 | current_conditional_items_path = conditional_items_path() 302 | if os.path.exists(current_conditional_items_path): 303 | existing_dict = plistlib.readPlist(current_conditional_items_path) 304 | output_dict = dict(existing_dict.items() + dictionary.items()) 305 | else: 306 | output_dict = dictionary 307 | plistlib.writePlist(output_dict, current_conditional_items_path) 308 | pass 309 | 310 | 311 | def main(argv=None): 312 | mojave_supported_dict = {} 313 | 314 | # Run the checks 315 | model_passed = is_supported_model() 316 | board_id_passed = is_supported_board_id() 317 | system_version_passed = is_system_version_supported() 318 | 319 | if is_virtual_machine(): 320 | mojave_supported = 0 321 | mojave_supported_dict = {'mojave_supported': True} 322 | elif model_passed and board_id_passed and system_version_passed: 323 | mojave_supported = 0 324 | mojave_supported_dict = {'mojave_supported': True} 325 | else: 326 | mojave_supported = 1 327 | mojave_supported_dict = {'mojave_supported': False} 328 | 329 | # Update "ConditionalItems.plist" if munki is installed 330 | if munki_installed() and update_munki_conditional_items: 331 | append_conditional_items(mojave_supported_dict) 332 | 333 | # Exit codes: 334 | # 0 = Mojave is supported 335 | # 1 = Mojave is not supported 336 | return mojave_supported 337 | 338 | 339 | if __name__ == '__main__': 340 | sys.exit(main()) 341 | -------------------------------------------------------------------------------- /check-10.15-catalina-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-10.15-catalina-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with macOS 10.15 Catalina. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # distribution file of OSInstall.mpkg installer package. 10 | 11 | # The OSInstall.mpkg can be found in the Packages directory of InstallESD disk image: 12 | # /Applications/Install macOS Catalina.app/Contents/SharedSupport/InstallESD.dmg 13 | # -> /Volumes/InstallESD/Packages/OSInstall.mpkg 14 | # 15 | # The checks done by this script are (in order): 16 | # - Machine is a virtual machine or has a specific supported board-id 17 | # - Machine model is not in a list of unsupported models 18 | # - Current system version is less than 10.15 and at least 10.9 19 | # 20 | # Exit codes: 21 | # 0 = Catalina is supported 22 | # 1 = Catalina is not supported 23 | # 24 | # 25 | # Hannes Juutilainen 26 | # https://github.com/hjuutilainen/adminscripts 27 | # 28 | # Thanks to Ralph Cyranka, Ed Bobak and @tcinbis 29 | # 30 | # ================================================================================ 31 | 32 | import sys 33 | import subprocess 34 | import os 35 | import re 36 | import plistlib 37 | from distutils.version import StrictVersion 38 | 39 | 40 | # ================================================================================ 41 | # Start configuration 42 | # ================================================================================ 43 | 44 | # Set this to False if you don't want any output, just the exit codes 45 | verbose = True 46 | 47 | # Set this to True if you want to add "catalina_supported" custom conditional to 48 | # /Library/Managed Installs/ConditionalItems.plist 49 | update_munki_conditional_items = False 50 | 51 | # ================================================================================ 52 | # End configuration 53 | # ================================================================================ 54 | 55 | 56 | def logger(message, status, info): 57 | if verbose: 58 | print "%14s: %-40s [%s]" % (message, status, info) 59 | pass 60 | 61 | 62 | def conditional_items_path(): 63 | # 64 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 65 | 66 | cmd = [ 67 | "/usr/bin/defaults", 68 | "read", 69 | "/Library/Preferences/ManagedInstalls", 70 | "ManagedInstallDir" 71 | ] 72 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 73 | (results, err) = p.communicate() 74 | managed_installs_dir = results.strip() 75 | 76 | # Make sure we're outputting our information to "ConditionalItems.plist" 77 | if managed_installs_dir: 78 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 79 | else: 80 | # Munki default 81 | return "/Library/Managed Installs/ConditionalItems.plist" 82 | 83 | 84 | def munki_installed(): 85 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 86 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 87 | output = p.communicate()[0] 88 | if p.returncode == 0: 89 | return True 90 | else: 91 | return False 92 | 93 | 94 | def is_system_version_supported(): 95 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 96 | product_name = system_version_plist['ProductName'] 97 | product_version = system_version_plist['ProductVersion'] 98 | if StrictVersion(product_version) >= StrictVersion('10.15'): 99 | logger("System", 100 | "%s %s" % (product_name, product_version), 101 | "Failed") 102 | return False 103 | elif StrictVersion(product_version) >= StrictVersion('10.9'): 104 | logger("System", 105 | "%s %s" % (product_name, product_version), 106 | "OK") 107 | return True 108 | else: 109 | logger("System", 110 | "%s %s" % (product_name, product_version), 111 | "Failed") 112 | return False 113 | 114 | 115 | def get_board_id(): 116 | cmd = ["/usr/sbin/ioreg", 117 | "-p", "IODeviceTree", 118 | "-r", 119 | "-n", "/", 120 | "-d", "1"] 121 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 122 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 123 | (results, err) = p2.communicate() 124 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 125 | board_id = board_id.strip() 126 | if board_id.startswith('Mac'): 127 | return board_id 128 | else: 129 | return None 130 | 131 | 132 | def is_virtual_machine(): 133 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 134 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 135 | (results, err) = p.communicate() 136 | for feature in results.split(): 137 | if feature == "VMM": 138 | logger("Board ID", 139 | "Virtual machine", 140 | "OK") 141 | return True 142 | return False 143 | 144 | 145 | def get_current_model(): 146 | cmd = ["/usr/sbin/sysctl", "-n", "hw.model"] 147 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 148 | (results, err) = p.communicate() 149 | return results.strip() 150 | 151 | 152 | def is_supported_model(): 153 | non_supported_models = [ 154 | 'iMac4,1', 155 | 'iMac4,2', 156 | 'iMac5,1', 157 | 'iMac5,2', 158 | 'iMac6,1', 159 | 'iMac7,1', 160 | 'iMac8,1', 161 | 'iMac9,1', 162 | 'iMac10,1', 163 | 'iMac11,1', 164 | 'iMac11,2', 165 | 'iMac11,3', 166 | 'iMac12,1', 167 | 'iMac12,2', 168 | 'MacBook1,1', 169 | 'MacBook2,1', 170 | 'MacBook3,1', 171 | 'MacBook4,1', 172 | 'MacBook5,1', 173 | 'MacBook5,2', 174 | 'MacBook6,1', 175 | 'MacBook7,1', 176 | 'MacBookAir1,1', 177 | 'MacBookAir2,1', 178 | 'MacBookAir3,1', 179 | 'MacBookAir3,2', 180 | 'MacBookAir4,1', 181 | 'MacBookAir4,2', 182 | 'MacBookPro1,1', 183 | 'MacBookPro1,2', 184 | 'MacBookPro2,1', 185 | 'MacBookPro2,2', 186 | 'MacBookPro3,1', 187 | 'MacBookPro4,1', 188 | 'MacBookPro5,1', 189 | 'MacBookPro5,2', 190 | 'MacBookPro5,3', 191 | 'MacBookPro5,4', 192 | 'MacBookPro5,5', 193 | 'MacBookPro6,1', 194 | 'MacBookPro6,2', 195 | 'MacBookPro7,1', 196 | 'MacBookPro8,1', 197 | 'MacBookPro8,2', 198 | 'MacBookPro8,3', 199 | 'Macmini1,1', 200 | 'Macmini2,1', 201 | 'Macmini3,1', 202 | 'Macmini4,1', 203 | 'Macmini5,1', 204 | 'Macmini5,2', 205 | 'Macmini5,3', 206 | 'MacPro1,1', 207 | 'MacPro2,1', 208 | 'MacPro3,1', 209 | 'MacPro4,1', 210 | 'MacPro5,1', 211 | 'Xserve1,1', 212 | 'Xserve2,1', 213 | 'Xserve3,1', 214 | ] 215 | current_model = get_current_model() 216 | if current_model in non_supported_models: 217 | logger("Model", 218 | "\"%s\" is not supported" % current_model, 219 | "Failed") 220 | return False 221 | else: 222 | logger("Model", 223 | current_model, 224 | "OK") 225 | return True 226 | 227 | 228 | def is_supported_board_id(): 229 | platform_support_values = [ 230 | 'Mac-00BE6ED71E35EB86', 231 | 'Mac-1E7E29AD0135F9BC', 232 | 'Mac-2BD1B31983FE1663', 233 | 'Mac-2E6FAB96566FE58C', 234 | 'Mac-3CBD00234E554E41', 235 | 'Mac-4B7AC7E43945597E', 236 | 'Mac-4B682C642B45593E', 237 | 'Mac-5A49A77366F81C72', 238 | 'Mac-06F11F11946D27C5', 239 | 'Mac-06F11FD93F0323C5', 240 | 'Mac-6F01561E16C75D06', 241 | 'Mac-7BA5B2D9E42DDD94', 242 | 'Mac-7BA5B2DFE22DDD8C', 243 | 'Mac-7DF2A3B5E5D671ED', 244 | 'Mac-7DF21CB3ED6977E5', 245 | 'Mac-9AE82516C7C6B903', 246 | 'Mac-9F18E312C5C2BF0B', 247 | 'Mac-27AD2F918AE68F61', 248 | 'Mac-27ADBB7B4CEE8E61', 249 | 'Mac-031AEE4D24BFF0B1', 250 | 'Mac-031B6874CF7F642A', 251 | 'Mac-35C1E88140C3E6CF', 252 | 'Mac-35C5E08120C7EEAF', 253 | 'Mac-42FD25EABCABB274', 254 | 'Mac-53FDB3D8DB8CA971', 255 | 'Mac-65CE76090165799A', 256 | 'Mac-66E35819EE2D0D05', 257 | 'Mac-66F35F19FE2A0D05', 258 | 'Mac-77EB7D7DAF985301', 259 | 'Mac-77F17D7DA9285301', 260 | 'Mac-81E3E92DD6088272', 261 | 'Mac-90BE64C3CB5A9AEB', 262 | 'Mac-112B0A653D3AAB9C', 263 | 'Mac-189A3D4F975D5FFC', 264 | 'Mac-226CB3C6A851A671', 265 | 'Mac-473D31EABEB93F9B', 266 | 'Mac-551B86E5744E2388', 267 | 'Mac-747B1AEFF11738BE', 268 | 'Mac-827FAC58A8FDFA22', 269 | 'Mac-827FB448E656EC26', 270 | 'Mac-937A206F2EE63C01', 271 | 'Mac-937CB26E2E02BB01', 272 | 'Mac-9394BDF4BF862EE7', 273 | 'Mac-50619A408DB004DA', 274 | 'Mac-63001698E7A34814', 275 | 'Mac-112818653D3AABFC', 276 | 'Mac-A5C67F76ED83108C', 277 | 'Mac-A369DDC4E67F1C45', 278 | 'Mac-AA95B1DDAB278B95', 279 | 'Mac-AFD8A9D944EA4843', 280 | 'Mac-B809C3757DA9BB8D', 281 | 'Mac-B4831CEBD52A0C4C', 282 | 'Mac-BE0E8AC46FE800CC', 283 | 'Mac-BE088AF8C5EB4FA2', 284 | 'Mac-C3EC7CD22292981F', 285 | 'Mac-C6F71043CEAA02A6', 286 | 'Mac-CAD6701F7CEA0921', 287 | 'Mac-CF21D135A7D34AA6', 288 | 'Mac-DB15BD556843C820', 289 | 'Mac-E43C1C25D4880AD6', 290 | 'Mac-EE2EBD4B90B839A8', 291 | 'Mac-F60DEB81FF30ACF6', 292 | 'Mac-F65AE981FFA204ED', 293 | 'Mac-F305150B0C7DEEEF', 294 | 'Mac-FA842E06C61E91C5', 295 | 'Mac-FC02E91DDD3FA6A4', 296 | 'Mac-FFE5EF870D7BA81A', 297 | ] 298 | board_id = get_board_id() 299 | if board_id in platform_support_values: 300 | logger("Board ID", 301 | board_id, 302 | "OK") 303 | return True 304 | else: 305 | logger("Board ID", 306 | "\"%s\" is not supported" % board_id, 307 | "Failed") 308 | return False 309 | 310 | 311 | def append_conditional_items(dictionary): 312 | current_conditional_items_path = conditional_items_path() 313 | if os.path.exists(current_conditional_items_path): 314 | existing_dict = plistlib.readPlist(current_conditional_items_path) 315 | output_dict = dict(existing_dict.items() + dictionary.items()) 316 | else: 317 | output_dict = dictionary 318 | plistlib.writePlist(output_dict, current_conditional_items_path) 319 | pass 320 | 321 | 322 | def main(argv=None): 323 | catalina_supported_dict = {} 324 | 325 | # Run the checks 326 | model_passed = is_supported_model() 327 | board_id_passed = is_supported_board_id() 328 | system_version_passed = is_system_version_supported() 329 | 330 | if is_virtual_machine(): 331 | catalina_supported = 0 332 | catalina_supported_dict = {'catalina_supported': True} 333 | elif model_passed and board_id_passed and system_version_passed: 334 | catalina_supported = 0 335 | catalina_supported_dict = {'catalina_supported': True} 336 | else: 337 | catalina_supported = 1 338 | catalina_supported_dict = {'catalina_supported': False} 339 | 340 | # Update "ConditionalItems.plist" if munki is installed 341 | if munki_installed() and update_munki_conditional_items: 342 | append_conditional_items(catalina_supported_dict) 343 | 344 | # Exit codes: 345 | # 0 = Catalina is supported 346 | # 1 = Catalina is not supported 347 | return catalina_supported 348 | 349 | 350 | if __name__ == '__main__': 351 | sys.exit(main()) 352 | -------------------------------------------------------------------------------- /check-10.8-mountainlion-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-mountainlion-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with OS X Mountain Lion. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # InstallESD.dmg -> ./Packages/OSInstall.mpkg/Distribution 10 | # 11 | # The checks are: 12 | # - Machine has a specific board-id or is a virtual machine 13 | # - 64 bit capable CPU 14 | # - At least 2GB of memory 15 | # - System version earlier than 10.8 but at least 10.6.6 16 | # 17 | # Exit codes: 18 | # 0 = Mountain Lion is supported 19 | # 1 = Mountain Lion is not supported 20 | # 21 | # 22 | # Hannes Juutilainen 23 | # https://github.com/hjuutilainen/adminscripts 24 | # 25 | # Version history: 26 | # ---------------- 27 | # 2012-10-10, Hannes Juutilainen 28 | # - First version 29 | # 30 | # ================================================================================ 31 | 32 | import sys 33 | import subprocess 34 | import os 35 | import re 36 | import plistlib 37 | import platform 38 | from distutils.version import StrictVersion 39 | from Foundation import CFPreferencesCopyAppValue 40 | 41 | # ================================================================================ 42 | # Start configuration 43 | # ================================================================================ 44 | # Set this to False if you don't want any output, just the exit codes 45 | verbose = True 46 | # Set this to True if you want to add "mountainlion_supported" custom conditional to 47 | # /Library/Managed Installs/ConditionalItems.plist 48 | updateMunkiConditionalItems = False 49 | # ================================================================================ 50 | # End configuration 51 | # ================================================================================ 52 | 53 | 54 | def logger(message, status, info): 55 | if verbose: 56 | print "%10s: %-40s [%s]" % (message, status, info) 57 | pass 58 | 59 | 60 | def conditionalItemsPath(): 61 | # 62 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 63 | BUNDLE_ID = 'ManagedInstalls' 64 | pref_name = 'ManagedInstallDir' 65 | managedinstalldir = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID) 66 | # Make sure we're outputting our information to "ConditionalItems.plist" 67 | if managedinstalldir: 68 | return os.path.join(managedinstalldir, 'ConditionalItems.plist') 69 | else: 70 | # Munki default 71 | return "/Library/Managed Installs/ConditionalItems.plist" 72 | 73 | 74 | def munkiInstalled(): 75 | pkgutilProcess = [ "pkgutil", "--pkg-info", "com.googlecode.munki.core" ] 76 | p = subprocess.Popen(pkgutilProcess, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 77 | output = p.communicate()[0] 78 | if p.returncode == 0: 79 | return True 80 | else: 81 | return False 82 | 83 | 84 | def isSystemVersionSupported(): 85 | systemVersionPlist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 86 | productName = systemVersionPlist['ProductName'] 87 | productVersion = systemVersionPlist['ProductVersion'] 88 | if StrictVersion(productVersion) > StrictVersion('10.8'): 89 | logger("System", 90 | "%s %s" % (productName, productVersion), 91 | "Failed") 92 | return False 93 | elif StrictVersion(productVersion) >= StrictVersion('10.6.6'): 94 | logger("System", 95 | "%s %s" % (productName, productVersion), 96 | "OK") 97 | return True 98 | else: 99 | logger("System", 100 | "%s %s" % (productName, productVersion), 101 | "Failed") 102 | return False 103 | 104 | 105 | def getBoardID(): 106 | ioregProcess = [ "/usr/sbin/ioreg", 107 | "-p", "IODeviceTree", 108 | "-r", 109 | "-n", "/", 110 | "-d", "1" ] 111 | p1 = subprocess.Popen(ioregProcess, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 112 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 113 | (results, err) = p2.communicate() 114 | boardID = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 115 | boardID = boardID.strip() 116 | if boardID.startswith('Mac'): 117 | return boardID 118 | else: 119 | return None 120 | 121 | 122 | def is64BitCapable(): 123 | sysctlProcess = [ "sysctl", "-n", "hw.cpu64bit_capable" ] 124 | p = subprocess.Popen(sysctlProcess, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 125 | (results, err) = p.communicate() 126 | if bool(results): 127 | logger("CPU", 128 | "64 bit capable", 129 | "OK") 130 | return True 131 | else: 132 | logger("CPU", 133 | "not 64 bit capable", 134 | "Failed") 135 | return False 136 | 137 | 138 | def hasRequiredAmountOfRAM(): 139 | minimumRam = int(2048 * 1024 * 1024) 140 | sysctlProcess = [ "sysctl", "-n", "hw.memsize" ] 141 | p = subprocess.Popen(sysctlProcess, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 142 | (results, err) = p.communicate() 143 | actualRAM = int(results) 144 | actualRAMGigabytes = actualRAM / 1024 / 1024 / 1024 145 | if actualRAM >= minimumRam: 146 | logger("Memory", 147 | "%i GB physical memory installed" % (actualRAMGigabytes), 148 | "OK") 149 | return True 150 | else: 151 | logger("Memory", 152 | "%i GB installed, 2 GB required" % (actualRAMGigabytes), 153 | "Failed") 154 | return False 155 | 156 | 157 | def isVirtualMachine(): 158 | sysctlProcess = [ "sysctl", "-n", "machdep.cpu.features" ] 159 | p = subprocess.Popen(sysctlProcess, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 160 | (results, err) = p.communicate() 161 | for aFeature in results.split(): 162 | if aFeature == "VMM": 163 | logger("Board ID", 164 | "Virtual machine", 165 | "OK") 166 | return True 167 | return False 168 | 169 | 170 | def isSupportedBoardID(): 171 | if isVirtualMachine(): 172 | return True 173 | platformSupportValues = [ 174 | "Mac-F42D88C8", 175 | "Mac-F2218EA9", 176 | "Mac-F42D86A9", 177 | "Mac-F22C8AC8", 178 | "Mac-942B59F58194171B", 179 | "Mac-F226BEC8", 180 | "Mac-F2268DC8", 181 | "Mac-2E6FAB96566FE58C", 182 | "Mac-7BA5B2794B2CDB12", 183 | "Mac-4B7AC7E43945597E", 184 | "Mac-F22C89C8", 185 | "Mac-942459F5819B171B", 186 | "Mac-F42388C8", 187 | "Mac-F223BEC8", 188 | "Mac-F4238CC8", 189 | "Mac-F222BEC8", 190 | "Mac-F227BEC8", 191 | "Mac-F2208EC8", 192 | "Mac-66F35F19FE2A0D05", 193 | "Mac-F4238BC8", 194 | "Mac-F221BEC8", 195 | "Mac-C08A6BB70A942AC2", 196 | "Mac-8ED6AF5B48C039E1", 197 | "Mac-F2238AC8", 198 | "Mac-F22586C8", 199 | "Mac-6F01561E16C75D06", 200 | "Mac-F2268EC8", 201 | "Mac-F22589C8", 202 | "Mac-F22587A1", 203 | "Mac-F22788AA", 204 | "Mac-F42C86C8", 205 | "Mac-942C5DF58193131B", 206 | "Mac-F2238BAE", 207 | "Mac-F22C86C8", 208 | "Mac-F2268CC8", 209 | "Mac-F2218FC8", 210 | "Mac-742912EFDBEE19B3", 211 | "Mac-F42D89C8", 212 | "Mac-F22587C8", 213 | "Mac-F42D89A9", 214 | "Mac-F2268AC8", 215 | "Mac-F42C89C8", 216 | "Mac-942452F5819B1C1B", 217 | "Mac-F2218FA9", 218 | "Mac-F221DCC8", 219 | "Mac-94245B3640C91C81", 220 | "Mac-F42D86C8", 221 | "Mac-4BC72D62AD45599E", 222 | "Mac-F2268DAE", 223 | "Mac-F42C88C8", 224 | "Mac-94245A3940C91C80", 225 | "Mac-F42386C8", 226 | "Mac-C3EC7CD22292981F", 227 | "Mac-942B5BF58194151B", 228 | "Mac-F2218EC8" ] 229 | boardID = getBoardID() 230 | if boardID in platformSupportValues: 231 | logger("Board ID", 232 | boardID, 233 | "OK") 234 | return True 235 | else: 236 | logger("Board ID", 237 | "\"%s\" is not supported" % boardID, 238 | "Failed") 239 | return False 240 | 241 | 242 | def appendConditionalItems(aDict): 243 | conditionalitemspath = conditionalItemsPath() 244 | if os.path.exists(conditionalItemsPath()): 245 | existing_dict = plistlib.readPlist(conditionalitemspath) 246 | output_dict = dict(existing_dict.items() + aDict.items()) 247 | else: 248 | output_dict = aDict 249 | plistlib.writePlist(output_dict, conditionalitemspath) 250 | pass 251 | 252 | def main(argv=None): 253 | mountainlion_supported_dict = {} 254 | 255 | # Run the checks 256 | boardIDPassed = isSupportedBoardID() 257 | memoryPassed = hasRequiredAmountOfRAM() 258 | cpuPassed = is64BitCapable() 259 | systemVersionPassed = isSystemVersionSupported() 260 | 261 | if ( boardIDPassed and memoryPassed and cpuPassed and systemVersionPassed ): 262 | mountainLionSupported = 0 263 | mountainlion_supported_dict = { 'mountainlion_supported': True } 264 | else: 265 | mountainLionSupported = 1 266 | mountainlion_supported_dict = { 'mountainlion_supported': False } 267 | 268 | # Update "ConditionalItems.plist" if munki is installed 269 | if ( munkiInstalled() and updateMunkiConditionalItems ): 270 | appendConditionalItems(mountainlion_supported_dict) 271 | 272 | # Exit codes: 273 | # 0 = Mountain Lion is supported 274 | # 1 = Mountain Lion is not supported 275 | return mountainLionSupported 276 | 277 | 278 | if __name__ == '__main__': 279 | sys.exit(main()) 280 | -------------------------------------------------------------------------------- /check-10.9-mavericks-compatibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-mavericks-compatibility.py 6 | # 7 | # This script checks if the current system is compatible with OS X 10.9 Mavericks. 8 | # These checks are based on the installCheckScript and volCheckScript in 9 | # /Applications/Install OS X Mavericks.app/Contents/SharedSupport/OSInstall.mpkg/Distribution 10 | # 11 | # The checks used in this script are: 12 | # - Machine has a specific supported board-id or is a virtual machine 13 | # - 64 bit capable CPU 14 | # - At least 2 GB of memory 15 | # - Current system version is less than 10.9 16 | # - Current system version is at least 10.6.6 or newer 17 | # 18 | # Exit codes: 19 | # 0 = Mavericks is supported 20 | # 1 = Mavericks is not supported 21 | # 22 | # 23 | # Hannes Juutilainen 24 | # https://github.com/hjuutilainen/adminscripts 25 | # 26 | # Version history: 27 | # ---------------- 28 | # 2013-10-25, Hannes Juutilainen 29 | # - Minor refactoring 30 | # 2013-10-20, Hannes Juutilainen 31 | # - Updated for Mavericks GM 32 | # 2013-09-06, Hannes Juutilainen 33 | # - First version 34 | # 35 | # ================================================================================ 36 | 37 | import sys 38 | import subprocess 39 | import os 40 | import re 41 | import plistlib 42 | from distutils.version import StrictVersion 43 | from Foundation import CFPreferencesCopyAppValue 44 | 45 | # ================================================================================ 46 | # Start configuration 47 | # ================================================================================ 48 | # Set this to False if you don't want any output, just the exit codes 49 | verbose = True 50 | # Set this to True if you want to add "mavericks_supported" custom conditional to 51 | # /Library/Managed Installs/ConditionalItems.plist 52 | update_munki_conditional_items = False 53 | # ================================================================================ 54 | # End configuration 55 | # ================================================================================ 56 | 57 | 58 | def logger(message, status, info): 59 | if verbose: 60 | print "%14s: %-40s [%s]" % (message, status, info) 61 | pass 62 | 63 | 64 | def conditional_items_path(): 65 | # 66 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 67 | bundle_id = 'ManagedInstalls' 68 | pref_name = 'ManagedInstallDir' 69 | managed_installs_dir = CFPreferencesCopyAppValue(pref_name, bundle_id) 70 | # Make sure we're outputting our information to "ConditionalItems.plist" 71 | if managed_installs_dir: 72 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 73 | else: 74 | # Munki default 75 | return "/Library/Managed Installs/ConditionalItems.plist" 76 | 77 | 78 | def munki_installed(): 79 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 80 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 81 | output = p.communicate()[0] 82 | if p.returncode == 0: 83 | return True 84 | else: 85 | return False 86 | 87 | 88 | def is_system_version_supported(): 89 | system_version_plist = plistlib.readPlist("/System/Library/CoreServices/SystemVersion.plist") 90 | product_name = system_version_plist['ProductName'] 91 | product_version = system_version_plist['ProductVersion'] 92 | if StrictVersion(product_version) >= StrictVersion('10.9'): 93 | logger("System", 94 | "%s %s" % (product_name, product_version), 95 | "Failed") 96 | return False 97 | elif StrictVersion(product_version) >= StrictVersion('10.6.6'): 98 | logger("System", 99 | "%s %s" % (product_name, product_version), 100 | "OK") 101 | return True 102 | else: 103 | logger("System", 104 | "%s %s" % (product_name, product_version), 105 | "Failed") 106 | return False 107 | 108 | 109 | def get_board_id(): 110 | cmd = ["/usr/sbin/ioreg", 111 | "-p", "IODeviceTree", 112 | "-r", 113 | "-n", "/", 114 | "-d", "1"] 115 | p1 = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 116 | p2 = subprocess.Popen(["/usr/bin/grep", "board-id"], stdin=p1.stdout, stdout=subprocess.PIPE) 117 | (results, err) = p2.communicate() 118 | board_id = re.sub(r"^\s*\"board-id\" = <\"(.*)\">$", r"\1", results) 119 | board_id = board_id.strip() 120 | if board_id.startswith('Mac'): 121 | return board_id 122 | else: 123 | return None 124 | 125 | 126 | def is_64bit_capable(): 127 | cmd = ["/usr/sbin/sysctl", "-n", "hw.cpu64bit_capable"] 128 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 129 | (results, err) = p.communicate() 130 | if bool(results): 131 | logger("CPU", 132 | "64 bit capable", 133 | "OK") 134 | return True 135 | else: 136 | logger("CPU", 137 | "not 64 bit capable", 138 | "Failed") 139 | return False 140 | 141 | 142 | def has_required_amount_of_memory(): 143 | minimum_memory = int(2048 * 1024 * 1024) 144 | cmd = ["/usr/sbin/sysctl", "-n", "hw.memsize"] 145 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 146 | (results, err) = p.communicate() 147 | actual_memory = int(results) 148 | actual_memory_gigabytes = actual_memory / 1024 / 1024 / 1024 149 | if actual_memory >= minimum_memory: 150 | logger("Memory", 151 | "%i GB physical memory installed" % actual_memory_gigabytes, 152 | "OK") 153 | return True 154 | else: 155 | logger("Memory", 156 | "%i GB installed, 2 GB required" % actual_memory_gigabytes, 157 | "Failed") 158 | return False 159 | 160 | 161 | def is_virtual_machine(): 162 | cmd = ["/usr/sbin/sysctl", "-n", "machdep.cpu.features"] 163 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 164 | (results, err) = p.communicate() 165 | for feature in results.split(): 166 | if feature == "VMM": 167 | logger("Board ID", 168 | "Virtual machine", 169 | "OK") 170 | return True 171 | return False 172 | 173 | 174 | def is_supported_board_id(): 175 | if is_virtual_machine(): 176 | return True 177 | platform_support_values = [ 178 | "Mac-00BE6ED71E35EB86", 179 | "Mac-031AEE4D24BFF0B1", 180 | "Mac-031B6874CF7F642A", 181 | "Mac-27ADBB7B4CEE8E61", 182 | "Mac-2E6FAB96566FE58C", 183 | "Mac-35C1E88140C3E6CF", 184 | "Mac-4B7AC7E43945597E", 185 | "Mac-4BC72D62AD45599E", 186 | "Mac-50619A408DB004DA", 187 | "Mac-66F35F19FE2A0D05", 188 | "Mac-6F01561E16C75D06", 189 | "Mac-742912EFDBEE19B3", 190 | "Mac-77EB7D7DAF985301", 191 | "Mac-7BA5B2794B2CDB12", 192 | "Mac-7DF21CB3ED6977E5", 193 | "Mac-7DF2A3B5E5D671ED", 194 | "Mac-8ED6AF5B48C039E1", 195 | "Mac-942452F5819B1C1B", 196 | "Mac-942459F5819B171B", 197 | "Mac-94245A3940C91C80", 198 | "Mac-94245B3640C91C81", 199 | "Mac-942B59F58194171B", 200 | "Mac-942B5BF58194151B", 201 | "Mac-942C5DF58193131B", 202 | "Mac-AFD8A9D944EA4843", 203 | "Mac-C08A6BB70A942AC2", 204 | "Mac-C3EC7CD22292981F", 205 | "Mac-F2208EC8", 206 | "Mac-F2218EA9", 207 | "Mac-F2218EC8" 208 | "Mac-F2218FA9", 209 | "Mac-F2218FC8", 210 | "Mac-F221BEC8", 211 | "Mac-F221DCC8", 212 | "Mac-F222BEC8", 213 | "Mac-F2238AC8", 214 | "Mac-F2238BAE", 215 | "Mac-F223BEC8", 216 | "Mac-F22586C8", 217 | "Mac-F22587A1", 218 | "Mac-F22587C8", 219 | "Mac-F22589C8", 220 | "Mac-F2268AC8", 221 | "Mac-F2268CC8", 222 | "Mac-F2268DAE", 223 | "Mac-F2268DC8", 224 | "Mac-F2268EC8", 225 | "Mac-F226BEC8", 226 | "Mac-F22788AA", 227 | "Mac-F227BEC8", 228 | "Mac-F22C86C8", 229 | "Mac-F22C89C8", 230 | "Mac-F22C8AC8", 231 | "Mac-F42386C8", 232 | "Mac-F42388C8", 233 | "Mac-F4238BC8", 234 | "Mac-F4238CC8", 235 | "Mac-F42C86C8", 236 | "Mac-F42C88C8", 237 | "Mac-F42C89C8", 238 | "Mac-F42D86A9", 239 | "Mac-F42D86C8", 240 | "Mac-F42D88C8", 241 | "Mac-F42D89A9", 242 | "Mac-F42D89C8", 243 | "Mac-F65AE981FFA204ED", 244 | "Mac-FC02E91DDD3FA6A4"] 245 | board_id = get_board_id() 246 | if board_id in platform_support_values: 247 | logger("Board ID", 248 | board_id, 249 | "OK") 250 | return True 251 | else: 252 | logger("Board ID", 253 | "\"%s\" is not supported" % board_id, 254 | "Failed") 255 | return False 256 | 257 | 258 | def append_conditional_items(dictionary): 259 | current_conditional_items_path = conditional_items_path() 260 | if os.path.exists(current_conditional_items_path): 261 | existing_dict = plistlib.readPlist(current_conditional_items_path) 262 | output_dict = dict(existing_dict.items() + dictionary.items()) 263 | else: 264 | output_dict = dictionary 265 | plistlib.writePlist(output_dict, current_conditional_items_path) 266 | pass 267 | 268 | 269 | def check_firmware_version(): 270 | """docstring for check_firmware_version""" 271 | cmd = ["/usr/sbin/ioreg", "-p", "IOService", 272 | "-n", "AppleAHCIDiskDriver", 273 | "-r", 274 | "-l", 275 | "-d", "1", 276 | "-w", "0"] 277 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 278 | (results, err) = p.communicate() 279 | disk_dict = {} 280 | for line in results.splitlines(): 281 | m = re.match(r"^\s*\"(?P.*)\" = \"(?P.*)\"$", line) 282 | if m: 283 | disk_dict[m.group('key')] = m.group('value').strip() 284 | model = disk_dict.get('Model', '') 285 | revision = disk_dict.get('Revision', '') 286 | return model, revision 287 | 288 | 289 | def hardware_model(): 290 | cmd = ["/usr/sbin/sysctl", "-n", "hw.model"] 291 | p = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 292 | (results, err) = p.communicate() 293 | return results.strip() 294 | 295 | 296 | def is_firmware_compatible(): 297 | """docstring for needs_ssd_fw_update""" 298 | if is_virtual_machine(): 299 | return True 300 | if hardware_model() not in ['MacBookAir5,1', 'MacBookAir5,2']: 301 | return True 302 | 303 | revisions = ['TPSABBF0', 'TPVABBF0'] 304 | models = ['TS064E', 'TS128E'] 305 | 306 | model, revision = check_firmware_version() 307 | if any(mod in model for mod in models) and any(rev in revision for rev in revisions): 308 | logger("SSD Model", 309 | model, 310 | "OK") 311 | logger("SSD Revision", 312 | revision, 313 | "Failed") 314 | return False 315 | else: 316 | logger("SSD Model", 317 | model, 318 | "OK") 319 | logger("SSD Revision", 320 | revision, 321 | "OK") 322 | return True 323 | 324 | 325 | def main(argv=None): 326 | mavericks_supported_dict = {} 327 | mavericks_needs_fw_update_dict = {} 328 | 329 | # Run the checks 330 | board_id_passed = is_supported_board_id() 331 | firmware_passed = is_firmware_compatible() 332 | memory_passed = has_required_amount_of_memory() 333 | cpu_passed = is_64bit_capable() 334 | system_version_passed = is_system_version_supported() 335 | 336 | if board_id_passed and memory_passed and cpu_passed and system_version_passed and firmware_passed: 337 | mavericks_supported = 0 338 | mavericks_needs_fw_update = 0 339 | mavericks_supported_dict = {'mavericks_supported': True} 340 | mavericks_needs_fw_update_dict = {'mavericks_needs_fw_update': False} 341 | elif board_id_passed and memory_passed and cpu_passed and system_version_passed and not firmware_passed: 342 | mavericks_supported = 0 343 | mavericks_needs_fw_update = 1 344 | mavericks_supported_dict = {'mavericks_supported': True} 345 | mavericks_needs_fw_update_dict = {'mavericks_needs_fw_update': True} 346 | else: 347 | mavericks_supported = 1 348 | mavericks_needs_fw_update = 1 349 | mavericks_supported_dict = {'mavericks_supported': False} 350 | mavericks_needs_fw_update_dict = {'mavericks_needs_fw_update': False} 351 | 352 | # Update "ConditionalItems.plist" if munki is installed 353 | if munki_installed() and update_munki_conditional_items: 354 | append_conditional_items(mavericks_supported_dict) 355 | append_conditional_items(mavericks_needs_fw_update_dict) 356 | 357 | # Exit codes: 358 | # 0 = Mavericks is supported 359 | # 1 = Mavericks is not supported 360 | return mavericks_supported and mavericks_needs_fw_update 361 | 362 | 363 | if __name__ == '__main__': 364 | sys.exit(main()) 365 | -------------------------------------------------------------------------------- /check-if-virtual-machine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # check-if-virtual-machine.py 6 | # 7 | # This script checks if the current system is running virtualized. This is done 8 | # by checking if machdep.cpu.features from sysctl contains VMM. OS X Mountain 9 | # Lion installer performs this same check when determining if it can be installed. 10 | # 11 | # Hannes Juutilainen 12 | # https://github.com/hjuutilainen/adminscripts 13 | # 14 | # Version history: 15 | # ---------------- 16 | # 2013-03-27, Hannes Juutilainen 17 | # - First version 18 | # 19 | # ================================================================================ 20 | 21 | import sys 22 | import os 23 | import subprocess 24 | import plistlib 25 | from Foundation import CFPreferencesCopyAppValue 26 | 27 | # ================================================================================ 28 | # Start configuration 29 | # ================================================================================ 30 | # Set this to False if you don't want any output, just the exit codes 31 | verbose = True 32 | # Set this to True if you want to add "virtual_machine" custom conditional to 33 | # /Library/Managed Installs/ConditionalItems.plist 34 | update_munki_conditional_items = True 35 | # ================================================================================ 36 | # End configuration 37 | # ================================================================================ 38 | 39 | def logger(message, status, info): 40 | if verbose: 41 | print "%10s: %-40s [%s]" % (message, status, info) 42 | pass 43 | 44 | 45 | def conditional_items_path(): 46 | # 47 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 48 | BUNDLE_ID = 'ManagedInstalls' 49 | pref_name = 'ManagedInstallDir' 50 | managed_installs_dir = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID) 51 | # Make sure we're outputting our information to "ConditionalItems.plist" 52 | if managed_installs_dir: 53 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 54 | else: 55 | # Munki default 56 | return "/Library/Managed Installs/ConditionalItems.plist" 57 | 58 | 59 | def munki_installed(): 60 | pkgutil_process = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 61 | p = subprocess.Popen(pkgutil_process, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 62 | output = p.communicate()[0] 63 | if p.returncode == 0: 64 | return True 65 | else: 66 | return False 67 | 68 | 69 | def is_virtual_machine(): 70 | sysctl_process = ["sysctl", "-n", "machdep.cpu.features"] 71 | p = subprocess.Popen(sysctl_process, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 72 | (results, err) = p.communicate() 73 | for feature in results.split(): 74 | if feature == "VMM": 75 | return True 76 | return False 77 | 78 | 79 | def append_conditional_items(conditionals_dict): 80 | conditionals_path = conditional_items_path() 81 | if os.path.exists(conditionals_path): 82 | existing_dict = plistlib.readPlist(conditionals_path) 83 | output_dict = dict(existing_dict.items() + conditionals_dict.items()) 84 | else: 85 | output_dict = conditionals_dict 86 | plistlib.writePlist(output_dict, conditionals_path) 87 | pass 88 | 89 | 90 | def main(argv=None): 91 | new_conditional_items = {} 92 | 93 | if is_virtual_machine(): 94 | print "This system is virtual" 95 | machine_type = 0 96 | new_conditional_items = {'virtual_machine': True} 97 | else: 98 | print "This system is not virtual" 99 | machine_type = 1 100 | new_conditional_items = {'virtual_machine': False} 101 | 102 | # Update "ConditionalItems.plist" if munki is installed 103 | if munki_installed() and update_munki_conditional_items: 104 | append_conditional_items(new_conditional_items) 105 | 106 | # Exit codes: 107 | # 0 = This machine is virtual 108 | # 1 = This machine is not virtual 109 | return machine_type 110 | 111 | 112 | if __name__ == '__main__': 113 | sys.exit(main()) 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /chrome-disable-autoupdates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | chrome-disable-autoupdates.py 5 | 6 | This script disables system wide automatic updates for Google Chrome. It can 7 | optionally remove the whole Keystone and its ticket store too. It should work 8 | for Chrome versions 18 and later. 9 | 10 | Created by Hannes Juutilainen, hjuutilainen@mac.com 11 | 12 | History: 13 | 2012-05-30, Hannes Juutilainen 14 | - First version 15 | 16 | """ 17 | 18 | import sys 19 | import os 20 | import getopt 21 | import subprocess 22 | 23 | # ========================================= 24 | # Set this to 'True' 25 | # to automatically remove the whole Keystone 26 | # ========================================= 27 | remove_keystone = False 28 | 29 | google_software_update_bundle = "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle" 30 | chrome_bundle_id = "com.google.Chrome" 31 | 32 | 33 | class Usage(Exception): 34 | def __init__(self, msg): 35 | self.msg = msg 36 | 37 | 38 | def keystone_installed(): 39 | """Check if Keystone is installed""" 40 | if os.path.exists(google_software_update_bundle): 41 | return True 42 | else: 43 | return False 44 | 45 | 46 | def keystone_nuke(): 47 | """Nuke the installed Keystone""" 48 | agent_path = os.path.join( 49 | google_software_update_bundle, 50 | "Contents/Resources/GoogleSoftwareUpdateAgent.app/Contents/Resources" 51 | ) 52 | install_script = os.path.join(agent_path, "install.py") 53 | if os.path.exists(install_script): 54 | p = subprocess.Popen( 55 | [install_script, "--nuke"], 56 | stdout=subprocess.PIPE, 57 | stderr=subprocess.PIPE 58 | ) 59 | (results, error) = p.communicate() 60 | if error: 61 | print >> sys.stderr, "%s" % error 62 | if results: 63 | print results 64 | if p.returncode == 0: 65 | return True 66 | else: 67 | return False 68 | else: 69 | print >> sys.stderr, "Error: KeystoneRegistration.framework not found" 70 | return False 71 | 72 | 73 | def remove_chrome_from_keystone(): 74 | """Removes Chrome from Keystone. Only return False if ksadmin fails.""" 75 | ksadmin = os.path.join(google_software_update_bundle, "Contents/MacOS/ksadmin") 76 | if os.path.exists(ksadmin): 77 | ksadmin_process = [ 78 | ksadmin, 79 | '--delete', 80 | '--productid', 81 | chrome_bundle_id 82 | ] 83 | p = subprocess.Popen( 84 | ksadmin_process, 85 | stdout=subprocess.PIPE, 86 | stderr=subprocess.PIPE 87 | ) 88 | (results, error) = p.communicate() 89 | if error: 90 | print >> sys.stderr, "%s" % error 91 | if results: 92 | print results 93 | if p.returncode == 0: 94 | print "Removed Chrome from Keystone" 95 | else: 96 | print >> sys.stderr, "Warning: ksadmin exited with code %i" % p.returncode 97 | 98 | else: 99 | print >> sys.stderr, "Warning: %s not found" % ksadmin 100 | if not os.path.exists("/Library/Google/GoogleSoftwareUpdate/TicketStore/"): 101 | print >> sys.stderr, "Warning: No ticket store either." 102 | 103 | 104 | def print_usage(): 105 | print "Options: " 106 | print " [ -c | --chromeonly ] Only remove Chrome ticket (default)" 107 | print " [ -k | --keystoneNuke ] Remove the whole Keystone" 108 | print " [ -h | --help ] Print this message" 109 | 110 | 111 | def main(argv=None): 112 | if argv is None: 113 | argv = sys.argv 114 | try: 115 | try: 116 | long_args = ["help", "keystoneNuke", "chromeonly"] 117 | opts, args = getopt.getopt(argv[1:], "hkc", long_args) 118 | except getopt.error, msg: 119 | print_usage() 120 | return 1 121 | 122 | global remove_keystone 123 | for option, value in opts: 124 | if option in ("-c", "--chromeonly"): 125 | remove_keystone = False 126 | if option in ("-k", "--keystoneNuke"): 127 | remove_keystone = True 128 | if option in ("-h", "--help"): 129 | print_usage() 130 | return 1 131 | 132 | # Check for root 133 | if os.geteuid() != 0: 134 | print >> sys.stderr, "This script must be run as root" 135 | return 1 136 | 137 | # Check if Keystone is actually installed 138 | if not keystone_installed(): 139 | print "Nothing to do. Keystone is not installed on this computer" 140 | return 0 141 | 142 | if remove_keystone: 143 | if keystone_nuke(): 144 | print "Keystone removed" 145 | return 0 146 | else: 147 | print >> sys.stderr, "Error: Keystone nuke failed" 148 | return 0 149 | else: 150 | remove_chrome_from_keystone() 151 | return 0 152 | 153 | except Usage, err: 154 | print >>sys.stderr, err.msg 155 | print >>sys.stderr, "for help use --help" 156 | return 2 157 | 158 | 159 | if __name__ == "__main__": 160 | sys.exit(main()) 161 | -------------------------------------------------------------------------------- /chrome-enable-autoupdates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | chrome-enable-autoupdates.py 5 | 6 | This script enables system wide automatic updates for Google Chrome. 7 | It should work for Chrome versions 18 and later. No configuration needed 8 | as this is originally intended as a munki postinstall script. 9 | 10 | Created by Hannes Juutilainen, hjuutilainen@mac.com 11 | 12 | History: 13 | -------- 14 | 2019-08-05, Andy Duss 15 | - Fix keystone_registration_framework_path to point to correct directory 16 | 17 | 2017-09-01, Hannes Juutilainen 18 | - Ignore errors when installing keystone 19 | 20 | 2015-09-25, Niklas Blomdalen 21 | - Modifications to include old KeystoneRegistration installation (python version) 22 | 23 | 2014-11-20, Hannes Juutilainen 24 | - Modifications for Chrome 39 25 | 26 | 2012-08-31, Hannes Juutilainen 27 | - Added --force flag to keystone install as suggested by Riley Shott 28 | 29 | 2012-05-29, Hannes Juutilainen 30 | - Added more error checking 31 | 32 | 2012-05-25, Hannes Juutilainen 33 | - Added some error checking in main 34 | 35 | 2012-05-24, Hannes Juutilainen 36 | - First version 37 | 38 | """ 39 | 40 | import sys 41 | import os 42 | import subprocess 43 | import plistlib 44 | from distutils.version import LooseVersion 45 | 46 | chrome_path = "/Applications/Google Chrome.app" 47 | info_plist_path = os.path.realpath(os.path.join(chrome_path, 'Contents/Info.plist')) 48 | brand_path = "/Library/Google/Google Chrome Brand.plist" 49 | brand_key = "KSBrandID" 50 | tag_path = info_plist_path 51 | tag_key = "KSChannelID" 52 | version_path = info_plist_path 53 | version_key = "KSVersion" 54 | 55 | 56 | class Usage(Exception): 57 | def __init__(self, msg): 58 | self.msg = msg 59 | 60 | 61 | def chrome_installed(): 62 | """Check if Chrome is installed""" 63 | if os.path.exists(chrome_path): 64 | return True 65 | else: 66 | return False 67 | 68 | 69 | def chrome_version(): 70 | """Returns Chrome version""" 71 | info_plist = plistlib.readPlist(info_plist_path) 72 | bundle_short_version = info_plist["CFBundleShortVersionString"] 73 | return bundle_short_version 74 | 75 | 76 | def chrome_update_url(): 77 | """Returns KSUpdateURL from Chrome Info.plist""" 78 | info_plist = plistlib.readPlist(info_plist_path) 79 | update_url = info_plist["KSUpdateURL"] 80 | return update_url 81 | 82 | 83 | def chrome_product_id(): 84 | """Returns KSProductID from Chrome Info.plist""" 85 | info_plist = plistlib.readPlist(info_plist_path) 86 | product_id = info_plist["KSProductID"] 87 | return product_id 88 | 89 | 90 | def keystone_registration_framework_path(): 91 | """Returns KeystoneRegistration.framework path""" 92 | if LooseVersion(chrome_version()) >= LooseVersion("76"): 93 | keystone_registration = os.path.join(chrome_path, 'Contents', 'Frameworks') 94 | keystone_registration = os.path.join(keystone_registration, 'Google Chrome Framework.framework') 95 | keystone_registration = os.path.join(keystone_registration, 'Frameworks', 'KeystoneRegistration.framework') 96 | keystone_registration = os.path.join(keystone_registration, 'Versions', 'Current') 97 | elif LooseVersion(chrome_version()) >= LooseVersion("75") and LooseVersion(chrome_version()) < LooseVersion("76"): 98 | keystone_registration = os.path.join(chrome_path, 'Contents/Frameworks/') 99 | keystone_registration = os.path.join(keystone_registration, 'Google Chrome Framework.framework/Versions') 100 | keystone_registration = os.path.join(keystone_registration, chrome_version()) 101 | keystone_registration = os.path.join(keystone_registration, 'Frameworks/KeystoneRegistration.framework') 102 | else: 103 | keystone_registration = os.path.join(chrome_path, 'Contents/Versions') 104 | keystone_registration = os.path.join(keystone_registration, chrome_version()) 105 | keystone_registration = os.path.join(keystone_registration, 'Google Chrome Framework.framework') 106 | keystone_registration = os.path.join(keystone_registration, 'Frameworks/KeystoneRegistration.framework') 107 | return keystone_registration 108 | 109 | 110 | def keystone_install(): 111 | """Install the current Keystone""" 112 | install_script = os.path.join(keystone_registration_framework_path(), 'Resources/ksinstall') 113 | if LooseVersion(chrome_version()) >= LooseVersion("80"): 114 | install_script = os.path.join(keystone_registration_framework_path(), 'Helpers/ksinstall') 115 | if not os.path.exists(install_script): 116 | install_script = os.path.join(keystone_registration_framework_path(), 'Resources/install.py') 117 | keystone_payload = os.path.join(keystone_registration_framework_path(), 'Resources/Keystone.tbz') 118 | if os.path.exists(install_script) and os.path.exists(keystone_payload): 119 | ksinstall_process = [ 120 | install_script, 121 | '--install', keystone_payload, 122 | '--force' 123 | ] 124 | p = subprocess.Popen(ksinstall_process, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 125 | (results, error) = p.communicate() 126 | if results: 127 | print results 128 | if p.returncode != 0: 129 | if error: 130 | print >> sys.stderr, "%s" % error 131 | print >> sys.stderr, "Keystone install exited with code %i" % p.returncode 132 | 133 | # Since we used --force argument, succeed no matter what the exit code was. 134 | return True 135 | else: 136 | print >> sys.stderr, "Error: KeystoneRegistration.framework not found" 137 | return False 138 | 139 | 140 | def register_chrome_with_keystone(): 141 | """Registers Chrome with Keystone""" 142 | ksadmin = "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin" 143 | if os.path.exists(ksadmin): 144 | ksadmin_process = [ 145 | ksadmin, 146 | '--register', 147 | '--productid', chrome_product_id(), 148 | '--version', chrome_version(), 149 | '--xcpath', chrome_path, 150 | '--url', chrome_update_url(), 151 | '--tag-path', tag_path, 152 | '--tag-key', tag_key, 153 | '--brand-path', brand_path, 154 | '--brand-key', brand_key, 155 | '--version-path', version_path, 156 | '--version-key', version_key 157 | ] 158 | p = subprocess.Popen(ksadmin_process, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 159 | (results, error) = p.communicate() 160 | if error: 161 | print >> sys.stderr, "%s" % error 162 | if results: 163 | print results 164 | if p.returncode == 0: 165 | return True 166 | else: 167 | return False 168 | else: 169 | print >> sys.stderr, "Error: %s doesn't exist" % ksadmin 170 | return False 171 | 172 | 173 | def main(argv=None): 174 | if argv is None: 175 | argv = sys.argv 176 | try: 177 | # Check for root 178 | if os.geteuid() != 0: 179 | print >> sys.stderr, "This script must be run as root" 180 | return 1 181 | 182 | if not chrome_installed(): 183 | print >> sys.stderr, "Error: Chrome is not installed on this computer" 184 | return 1 185 | if keystone_install(): 186 | print "Keystone installed" 187 | else: 188 | print >> sys.stderr, "Error: Keystone install failed" 189 | return 1 190 | if register_chrome_with_keystone(): 191 | print "Registered Chrome with Keystone" 192 | return 0 193 | else: 194 | print >> sys.stderr, "Error: Failed to register Chrome with Keystone" 195 | return 1 196 | 197 | except Usage, err: 198 | print >> sys.stderr, err.msg 199 | print >> sys.stderr, "for help use --help" 200 | return 2 201 | 202 | 203 | if __name__ == "__main__": 204 | sys.exit(main()) 205 | -------------------------------------------------------------------------------- /download-logicprox-content.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # download-logicprox-content.py 6 | # 7 | # This script downloads the content packages for Logic Pro X. It also arranges 8 | # them in subdirectories the same way as displayed in the Logic Pro first run window. 9 | # 10 | # Logic Pro X Version: 10.3.2 11 | # 12 | # List package URLs: 13 | # $ ./download-logicprox-content.py list 14 | # 15 | # Download packages: 16 | # $ ./download-logicprox-content.py download -o ~/Downloads/LogicProContent 17 | # 18 | # 19 | # Hannes Juutilainen 20 | # https://github.com/hjuutilainen/adminscripts 21 | # 22 | # ================================================================================ 23 | 24 | import sys 25 | import subprocess 26 | import os 27 | import re 28 | import plistlib 29 | import urllib2 30 | import shutil 31 | import argparse 32 | import objc 33 | 34 | # Logic Pro X 10.1 update changed the remote plist file format to binary. 35 | # Since plistlib is not able to deal with binary property lists, we're 36 | # using FoundationPlist from Munki instead. 37 | if not os.path.exists('/usr/local/munki/munkilib'): 38 | print >> sys.stderr, ("ERROR: munkilib not found") 39 | sys.exit(1) 40 | else: 41 | sys.path.append('/usr/local/munki/munkilib') 42 | try: 43 | from munkilib import FoundationPlist as plistlib 44 | except ImportError: 45 | try: 46 | import FoundationPlist as plistlib 47 | except ImportError: 48 | # maybe we're not on an OS X machine... 49 | print >> sys.stderr, ("ERROR: FoundationPlist is not available") 50 | sys.exit(1) 51 | 52 | 53 | # The URL and the plist file name can be found with the strings command. For example: 54 | # 55 | # $ cd "/Applications/Logic Pro X.app/Contents/MacOS" 56 | # $ strings "Logic Pro X" | egrep -B 100 -A 10 ContentBaseURL | egrep 'plist|http' 57 | # 58 | base_url = "http://audiocontentdownload.apple.com/lp10_ms3_content_2016/" 59 | base_url_2013 = "http://audiocontentdownload.apple.com/lp10_ms3_content_2013/" 60 | version = "1032" 61 | logicpro_plist_name = "logicpro%s.plist" % version 62 | 63 | 64 | download_urls_temp = {} 65 | 66 | def human_readable_size(bytes): 67 | """ 68 | Converts bytes to human readable string 69 | """ 70 | for x in ['bytes','KB','MB','GB']: 71 | if bytes < 1024.0: 72 | return "%3.1f %s" % (bytes, x) 73 | bytes /= 1000.0 # This seems to be the Apple default 74 | return "%3.1f %s" % (bytes, 'TB') 75 | 76 | 77 | def download_package_as(url, output_file): 78 | """ 79 | Downloads an URL to the specified file path 80 | """ 81 | if not url or not output_file: 82 | return False 83 | 84 | try: 85 | req = urllib2.urlopen(url) 86 | with open(output_file, 'wb') as fp: 87 | shutil.copyfileobj(req, fp) 88 | except urllib2.HTTPError as e: 89 | print "HTTP Error:", e.code, url 90 | 91 | return True 92 | 93 | 94 | def download_logicpro_plist(): 95 | """ 96 | Downloads the Logic Pro Content property list and 97 | returns a dictionary 98 | """ 99 | plist_url = ''.join([base_url, logicpro_plist_name]) 100 | try: 101 | f = urllib2.urlopen(plist_url) 102 | plist_data = f.read() 103 | f.close() 104 | except urllib2.HTTPError as e: 105 | print "HTTP Error:", e.code, url 106 | 107 | info_plist = plistlib.readPlistFromString(plist_data) 108 | return info_plist 109 | 110 | 111 | def process_package_download(download_url, save_path, download_size, download_name): 112 | """ 113 | Downloads the URL if it doesn't already exist 114 | """ 115 | global download_urls_temp 116 | existing_item = download_urls_temp.get(download_url, None) 117 | if existing_item: 118 | existing_item["savepaths"].append(save_path) 119 | else: 120 | download_urls_temp[download_url] = {"savepaths": [save_path], "download_name": download_name, "download_size": download_size} 121 | 122 | pass 123 | 124 | def process_package_dict(package_dict): 125 | """ 126 | Processes information from a single package dictionary. 127 | Returns the download URL, file name and size 128 | """ 129 | download_name = package_dict.get('DownloadName', None) 130 | download_size = package_dict.get('DownloadSize', None) 131 | if not download_name.startswith('../'): 132 | download_url = ''.join([base_url, download_name]) 133 | else: 134 | download_name = os.path.basename(download_name) 135 | download_url = ''.join([base_url_2013, download_name]) 136 | return (download_url, download_name, download_size) 137 | 138 | 139 | def process_content_item(content_item, parent_items, list_only=False): 140 | """ 141 | Extracts and processes information from a single Content item 142 | """ 143 | # Get the _LOCALIZABLE_ key which contains the human readable name 144 | localizable_items = content_item.get('_LOCALIZABLE_', []) 145 | display_name = localizable_items[0].get('DisplayName') 146 | new_parent_items = None 147 | 148 | # Check if this item is a child of another Content item 149 | if parent_items: 150 | display_names = [] 151 | for parent_item in parent_items: 152 | localizable_parent_items = parent_item.get('_LOCALIZABLE_', []) 153 | parent_display_name = localizable_parent_items[0].get('DisplayName') 154 | display_names.append(parent_display_name) 155 | display_names.append(display_name) 156 | display_names.insert(0, download_directory) 157 | relative_path = os.path.join(*display_names) 158 | new_parent_items = list(parent_items) 159 | new_parent_items.append(content_item) 160 | else: 161 | relative_path = os.path.join(download_directory, display_name) 162 | new_parent_items = list([content_item]) 163 | 164 | # Check if this item contains child Content items and process them 165 | subcontent = content_item.get('SubContent', None) 166 | if subcontent: 167 | for subcontent_item in subcontent: 168 | process_content_item(subcontent_item, new_parent_items, list_only) 169 | 170 | # We don't have any subcontent so get the package references and download 171 | else: 172 | package_name = content_item.get('Packages', None) 173 | if not os.path.exists(relative_path) and not list_only: 174 | #print "Creating dir %s" % relative_path 175 | os.makedirs(relative_path) 176 | 177 | # There can be only one package defined as a string 178 | # or an array of packages 179 | 180 | if type(package_name) == objc.pyobjc_unicode: 181 | package_dict = packages.get(package_name, {}) 182 | (download_url, download_name, download_size) = process_package_dict(package_dict) 183 | save_path = "".join([relative_path, '/', download_name]) 184 | process_package_download(download_url, save_path, download_size, download_name) 185 | 186 | else: 187 | for i in package_name: 188 | package_dict = packages.get(i, {}) 189 | (download_url, download_name, download_size) = process_package_dict(package_dict) 190 | save_path = "".join([relative_path, '/', download_name]) 191 | process_package_download(download_url, save_path, download_size, download_name) 192 | 193 | 194 | def main(argv=None): 195 | # ================ 196 | # Arguments 197 | # ================ 198 | 199 | # Create the top-level parser 200 | parser = argparse.ArgumentParser() 201 | subparsers = parser.add_subparsers(title='subcommands', dest='subparser_name') 202 | 203 | # List 204 | parser_install = subparsers.add_parser('list', help='List package URLs') 205 | 206 | # Raw 207 | parser_install = subparsers.add_parser('raw', help='Print the full update property list') 208 | 209 | # Download 210 | parser_activate = subparsers.add_parser('download', help='Download packages') 211 | parser_activate.add_argument('-o', '--output', nargs=1, required=True, help='Download location. For example ~/Downloads/LogicProContent') 212 | 213 | # Parse arguments 214 | args = vars(parser.parse_args()) 215 | 216 | # ================================================================= 217 | # Download the property list which contains the package references 218 | # ================================================================= 219 | logicpro_plist = download_logicpro_plist() 220 | 221 | # Raw requested, just print the property list and exit 222 | if args['subparser_name'] == 'raw': 223 | print logicpro_plist 224 | return 0 225 | 226 | global download_directory 227 | if args.get('output', None): 228 | download_directory = os.path.abspath(args['output'][0]) 229 | else: 230 | home = os.path.expanduser('~') 231 | download_directory = os.path.join(home, 'Downloads/LogicProContent') 232 | 233 | # ===================================== 234 | # Parse the property list for packages 235 | # ===================================== 236 | global packages 237 | packages = logicpro_plist['Packages'] 238 | content_dict = logicpro_plist['Content'] 239 | content = content_dict.get('en', []) 240 | for content_item in content: 241 | if args['subparser_name'] == 'list': 242 | process_content_item(content_item, None, list_only=True) 243 | else: 244 | process_content_item(content_item, None, list_only=False) 245 | 246 | # ====================================== 247 | # Download and link the items 248 | # ====================================== 249 | temp_download_dir = os.path.join(download_directory, "__Downloaded Items") 250 | if not os.path.exists(temp_download_dir) and not args['subparser_name'] == 'list': 251 | os.makedirs(temp_download_dir) 252 | for key in sorted(download_urls_temp): 253 | value = download_urls_temp[key] 254 | download_url = key 255 | if args['subparser_name'] == 'list': 256 | print download_url 257 | continue 258 | save_path = os.path.join(temp_download_dir, value["download_name"]) 259 | 260 | # Since Logic Pro X 10.2.3, the download size can be either an integer 261 | # or a string ("123.456.789"). The following is only an assumption 262 | # how to parse the latter... 263 | download_size = value.get("download_size", 0) 264 | download_size_int = 0 265 | if type(download_size) == objc.pyobjc_unicode: 266 | # Strip anything that isn't a digit 267 | download_size_string = re.sub(r"\D", "", download_size) 268 | download_size_int = int(download_size_string) 269 | else: 270 | download_size_int = int(download_size) 271 | 272 | # Now convert the bytes to a human readable string 273 | download_size_string = human_readable_size(download_size_int) 274 | 275 | if os.path.exists(save_path): 276 | # Check the local file size and download if it's smaller. 277 | # TODO: Get a better way for this. The 'DownloadSize' key in logicpro_plist 278 | # seems to be wrong for a number of packages. 279 | if os.path.getsize(save_path) < download_size_int: 280 | print "Remote file is larger. Downloading %s from %s" % (download_size_string, download_url) 281 | download_package_as(download_url, save_path) 282 | else: 283 | print "Skipping already downloaded package %s" % download_url 284 | else: 285 | print "Downloading %s" % (download_url) 286 | download_package_as(download_url, save_path) 287 | 288 | for item in value["savepaths"]: 289 | if os.path.exists(item): 290 | os.unlink(item) 291 | os.link(save_path, item) 292 | print "---> Linked %s" % item 293 | 294 | return 0 295 | 296 | 297 | if __name__ == '__main__': 298 | sys.exit(main()) 299 | -------------------------------------------------------------------------------- /export-macos-man-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ================================================================================ 4 | # export-macos-man-pages.sh 5 | # 6 | # This script exports all available man pages to a folder in ~/Desktop 7 | # 8 | # Hannes Juutilainen 9 | # https://github.com/hjuutilainen/adminscripts 10 | # 11 | # ================================================================================ 12 | 13 | set -o pipefail 14 | 15 | # Get OS version and build 16 | PRODUCT_VERSION=$(defaults read /System/Library/CoreServices/SystemVersion.plist ProductVersion) 17 | PRODUCT_BUILD_VERSION=$(defaults read /System/Library/CoreServices/SystemVersion.plist ProductBuildVersion) 18 | NOW=$(date +"%Y%m%d%H%M%S") 19 | 20 | # This directory will contain all of the created files 21 | WORK_DIR=$(mktemp -d "${HOME}/Desktop/man-${PRODUCT_VERSION}-${PRODUCT_BUILD_VERSION}-${NOW}.XXXXXX") 22 | echo "Working in ${WORK_DIR}" 23 | 24 | for SECTION_NUMBER in {1..8}; do 25 | OUTPUT_DIR="${WORK_DIR}/${SECTION_NUMBER}" 26 | mkdir -p "${OUTPUT_DIR}" 27 | for j in $(man -aWS ${SECTION_NUMBER} \* | xargs basename | sed 's/\.[^.]*$//' | sort -u); do 28 | COMMAND_NAME=$(basename $j) 29 | echo "Getting section ${SECTION_NUMBER} manual page for ${COMMAND_NAME}" 30 | man "${COMMAND_NAME}" 2> /dev/null | col -bx > "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 31 | sed -e '1d' -e '$d' -i '' "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 32 | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' -i '' "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 33 | done 34 | done 35 | 36 | 37 | OUTPUT_DIR="${WORK_DIR}/compgen" 38 | mkdir -p "${OUTPUT_DIR}" 39 | for COMMAND_NAME in $(compgen -c); do 40 | man "${COMMAND_NAME}" 2> /dev/null | col -bx > "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 41 | sed -e '1d' -e '$d' -i '' "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 42 | sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' -i '' "${OUTPUT_DIR}/${COMMAND_NAME}.txt" 43 | done 44 | 45 | 46 | open "${WORK_DIR}" 47 | 48 | -------------------------------------------------------------------------------- /office2011-installed-language.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | # ================================================================================ 5 | # office-2011-installed-language.py 6 | # 7 | # Exit codes: 8 | # 0 = Office is installed 9 | # 1 = Office is not installed at all 10 | # 11 | # Hannes Juutilainen 12 | # https://github.com/hjuutilainen/adminscripts 13 | # 14 | # ================================================================================ 15 | 16 | import sys 17 | import subprocess 18 | import os 19 | import re 20 | import plistlib 21 | import datetime 22 | from distutils.version import StrictVersion 23 | from operator import itemgetter, attrgetter 24 | from Foundation import CFPreferencesCopyAppValue 25 | 26 | # ================================================================================ 27 | # Start configuration 28 | # ================================================================================ 29 | # Set this to False if you don't want any output, just the exit codes 30 | verbose = True 31 | # Set this to True if you want to add "office_2011_language" custom conditional to 32 | # /Library/Managed Installs/ConditionalItems.plist 33 | update_munki_conditional_items = False 34 | # ================================================================================ 35 | # End configuration 36 | # ================================================================================ 37 | 38 | def info_for_package_identifier(identifier): 39 | """ 40 | Returns an info dictionary for a given package identifier 41 | by running /usr/sbin/pkgutil --pkg-info-plist 42 | """ 43 | if not identifier: 44 | return None 45 | 46 | cmd = ["/usr/sbin/pkgutil", "--pkg-info-plist", identifier] 47 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 48 | (results, err) = p.communicate() 49 | if p.returncode != 0: 50 | return None 51 | 52 | package_info_dict = {} 53 | package_info_dict = plistlib.readPlistFromString(results) 54 | return package_info_dict 55 | 56 | 57 | def installed_core_resource_packages(): 58 | """ 59 | Returns a list of installed Office core resource packages 60 | 61 | These packages have the following identifier format: 62 | com.microsoft.office..core_resources.pkg. 63 | """ 64 | cmd = ["/usr/sbin/pkgutil", "--pkgs-plist"] 65 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 66 | (results, err) = p.communicate() 67 | if p.returncode != 0: 68 | return [] 69 | all_package_identifiers = plistlib.readPlistFromString(results) 70 | re_core_resource = re.compile(r'^com\.microsoft\.office\.(?P.*)\.core_resources\.pkg\.(?P[0-9\.]+)(.update$|$)') 71 | matching_packages = [] 72 | for identifier in all_package_identifiers: 73 | m = re.match(re_core_resource, identifier) 74 | if m and m.group('language_code'): 75 | item_info = info_for_package_identifier(identifier) 76 | item_info['language'] = m.group('language_code') 77 | matching_packages.append(item_info) 78 | return matching_packages 79 | 80 | 81 | def conditional_items_path(): 82 | # 83 | # Read the location of the ManagedInstallDir from ManagedInstall.plist 84 | bundle_id = 'ManagedInstalls' 85 | pref_name = 'ManagedInstallDir' 86 | managed_installs_dir = CFPreferencesCopyAppValue(pref_name, bundle_id) 87 | # Make sure we're outputting our information to "ConditionalItems.plist" 88 | if managed_installs_dir: 89 | return os.path.join(managed_installs_dir, 'ConditionalItems.plist') 90 | else: 91 | # Munki default 92 | return "/Library/Managed Installs/ConditionalItems.plist" 93 | 94 | 95 | def munki_installed(): 96 | cmd = ["pkgutil", "--pkg-info", "com.googlecode.munki.core"] 97 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 98 | output = p.communicate()[0] 99 | if p.returncode == 0: 100 | return True 101 | else: 102 | return False 103 | 104 | 105 | def append_conditional_items(dictionary): 106 | current_conditional_items_path = conditional_items_path() 107 | if os.path.exists(current_conditional_items_path): 108 | existing_dict = plistlib.readPlist(current_conditional_items_path) 109 | output_dict = dict(existing_dict.items() + dictionary.items()) 110 | else: 111 | output_dict = dictionary 112 | plistlib.writePlist(output_dict, current_conditional_items_path) 113 | pass 114 | 115 | 116 | def main(argv=None): 117 | # Get all Office core resource packages 118 | packages = installed_core_resource_packages() 119 | 120 | if len(packages) == 0: 121 | # Office is not installed 122 | return 1 123 | 124 | # Sort the packages by install time 125 | packages_sorted = sorted(packages, key=attrgetter('install-time', 'pkg-version'), reverse=True) 126 | 127 | # Installed language is the language of the latest package 128 | latest_package_info = packages_sorted[0] 129 | latest_lang = latest_package_info.get('language', None) 130 | 131 | if verbose: 132 | if latest_lang: 133 | print latest_lang 134 | else: 135 | print "Could not determine installed language" 136 | 137 | # Update "ConditionalItems.plist" if munki is installed 138 | if munki_installed() and update_munki_conditional_items and latest_lang: 139 | append_conditional_items({'office_2011_language': latest_lang}) 140 | 141 | 142 | return 0 143 | 144 | 145 | if __name__ == '__main__': 146 | sys.exit(main()) 147 | -------------------------------------------------------------------------------- /old/check-for-osx-flashback.K.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ================================================================================ 4 | # check-for-osx-flashback.K.sh 5 | # 6 | # Script to check system for any signs of OSX/Flashback.K trojan 7 | # Checks are based on information from F-Secure's website: 8 | # http://www.f-secure.com/v-descs/trojan-downloader_osx_flashback_k.shtml 9 | # 10 | # Hannes Juutilainen, hjuutilainen@mac.com 11 | # 12 | # History: 13 | # 2012-04-10, Hannes Juutilainen 14 | # - Added support for checking multiple browsers 15 | # - Changes in output formatting 16 | # 2012-04-03, Hannes Juutilainen 17 | # - First version 18 | # ================================================================================ 19 | 20 | # ================================================================================ 21 | # Apps that need to be checked for the LSEnvironment key 22 | # If you need to check additional paths, add them here 23 | # ================================================================================ 24 | APPLICATIONS_TO_CHECK=( 25 | "/Applications/Safari.app" 26 | "/Applications/Firefox.app" 27 | "/Applications/Google Chrome.app" 28 | "/Applications/Opera.app" 29 | ) 30 | 31 | SCAN_RESULTS=0 32 | 33 | # ================================================================================ 34 | # Check for root 35 | # ================================================================================ 36 | if [[ $EUID -ne 0 ]]; then 37 | echo "This script must be run as root" 2>&1 38 | exit 1 39 | fi 40 | 41 | 42 | # ================================================================================ 43 | echo "Checking for LSEnvironment key in application bundles" 44 | # ================================================================================ 45 | for APPLICATION in "${APPLICATIONS_TO_CHECK[@]}" 46 | do 47 | if [[ -e "$APPLICATION/Contents/Info.plist" ]]; then 48 | defaults read "$APPLICATION/Contents/Info" LSEnvironment > /dev/null 2>&1 49 | if [[ $? -eq 0 ]]; then 50 | printf "%b\n" "===> WARNING: Found LSEnvironment in $APPLICATION/Contents/Info.plist" 51 | SCAN_RESULTS=1 52 | else 53 | printf "%b\n" "---> Key not found: $APPLICATION/Contents/Info.plist" 54 | fi 55 | #else 56 | #printf "%b\n" "---> File doesn't exist: $APPLICATION/Contents/Info.plist" 57 | fi 58 | done 59 | 60 | 61 | # ================================================================================ 62 | printf "\n%b\n" "Checking for /Users/Shared/.libgmalloc.dylib" 63 | # ================================================================================ 64 | if [[ -e /Users/Shared/.libgmalloc.dylib ]]; then 65 | printf "%b\n" "===> WARNING: Found /Users/Shared/.libgmalloc.dylib" 66 | SCAN_RESULTS=1 67 | else 68 | printf "%b\n" "---> File doesn't exist" 69 | fi 70 | 71 | 72 | # ================================================================================ 73 | printf "\n%b\n" "Checking for DYLD_INSERT_LIBRARIES key in /Users/*/.MacOSX/environment.plist" 74 | # ================================================================================ 75 | shopt -s nullglob 76 | USER_HOMES=/Users/* 77 | for f in $USER_HOMES 78 | do 79 | if [[ -f $f/.MacOSX/environment.plist ]]; then 80 | defaults read $f/.MacOSX/environment DYLD_INSERT_LIBRARIES > /dev/null 2>&1 81 | if [[ $? -eq 0 ]]; then 82 | printf "%b\n" "===> WARNING: Found DYLD_INSERT_LIBRARIES key in $f/.MacOSX/environment" 83 | SCAN_RESULTS=1 84 | fi 85 | else 86 | printf "%b\n" "---> File doesn't exist in $f/.MacOSX/environment.plist" 87 | fi 88 | done 89 | shopt -u nullglob 90 | printf "%b\n" "---> Done" 91 | 92 | 93 | # ================================================================================ 94 | printf "\n%b" "Results: " 95 | # ================================================================================ 96 | if [[ $SCAN_RESULTS -ne 0 ]]; then 97 | printf "%b\n\n" "WARNING: System tested positive on at least one of the tests." 98 | else 99 | printf "%b\n\n" "System is clean." 100 | fi 101 | 102 | exit 0 103 | -------------------------------------------------------------------------------- /old/check-for-osx-malware.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ================================================================================ 4 | # check-for-osx-malware.sh 5 | # 6 | # Simple script to check the existance of files used by: 7 | # - Backdoor:OSX/MacKontrol.A 8 | # - Backdoor:OSX/Olyx.C 9 | # - Backdoor:OSX/Sabpab.A 10 | # 11 | # Checks are based on information from F-Secure's database: 12 | # http://www.f-secure.com/v-descs/ 13 | # 14 | # 15 | # Hannes Juutilainen, hjuutilainen@mac.com 16 | # 17 | # History: 18 | # 2012-04-18, Hannes Juutilainen 19 | # - First version 20 | # ================================================================================ 21 | 22 | VERBOSE=false 23 | DID_FIND_FILES=false 24 | 25 | FILES_TO_CHECK=( 26 | "/Applications/Automator.app/Contents/MacOS/DockLight" # Backdoor:OSX/Olyx.C 27 | "/Library/launched" # Backdoor:OSX/MacKontrol.A 28 | ) 29 | 30 | USERFILES_TO_CHECK=( 31 | "Library/Preferences/com.apple.PubSabAgent.pfile" # Backdoor:OSX/Sabpab.A 32 | "Library/LaunchAgents/com.apple.PubSabAgent.plist" # Backdoor:OSX/Sabpab.A 33 | "Library/LaunchAgents/com.apple.FolderActionsxl.plist" # Backdoor:OSX/MacKontrol.A 34 | "Library/LaunchAgents/com.apple.DockActions.plist" # Backdoor:OSX/Olyx.C 35 | ) 36 | 37 | 38 | # ================================================================================ 39 | # Check for root 40 | # ================================================================================ 41 | if [[ $EUID -ne 0 ]]; then 42 | echo "This script must be run as root" 2>&1 43 | exit 1 44 | fi 45 | 46 | 47 | # ================================================================================ 48 | # Arguments 49 | # ================================================================================ 50 | while [[ -n "$1" ]]; do 51 | case $1 in 52 | -v | --verbose ) 53 | shift 54 | VERBOSE=true 55 | ;; 56 | * ) 57 | printf "Unrecognized arguments\n" 58 | exit 1 59 | esac 60 | shift 61 | done 62 | 63 | 64 | # ================================================================================ 65 | for INFECTION_FILE in "${FILES_TO_CHECK[@]}" 66 | # ================================================================================ 67 | do 68 | if $VERBOSE; then 69 | printf "\n%b\n" "Checking for $INFECTION_FILE" 70 | fi 71 | if [[ -f "$INFECTION_FILE" ]]; then 72 | printf "%b\n" "===> WARNING: Found $INFECTION_FILE" 73 | DID_FIND_FILES=true 74 | elif $VERBOSE; then 75 | printf "%b\n" "---> File doesn't exist in $INFECTION_FILE" 76 | fi 77 | done 78 | 79 | 80 | # ================================================================================ 81 | for USERFILE in "${USERFILES_TO_CHECK[@]}" 82 | # ================================================================================ 83 | do 84 | if $VERBOSE; then 85 | printf "\n%b\n" "Checking for /Users/*/$USERFILE" 86 | fi 87 | shopt -s nullglob 88 | USER_HOMES=/Users/* 89 | for f in $USER_HOMES 90 | do 91 | if [[ -f "$f/$USERFILE" ]]; then 92 | printf "%b\n" "===> WARNING: Found $f/$USERFILE" 93 | DID_FIND_FILES=true 94 | elif $VERBOSE; then 95 | printf "%b\n" "---> File doesn't exist in $f/$USERFILE" 96 | fi 97 | done 98 | shopt -u nullglob 99 | done 100 | if $VERBOSE; then 101 | printf "\n" 102 | fi 103 | 104 | 105 | # ================================================================================ 106 | printf "%b" "Results: " 107 | # ================================================================================ 108 | if $DID_FIND_FILES; then 109 | printf "%b\n" "WARNING: System tested positive on at least one of the tests." 110 | else 111 | printf "%b\n" "System is clean." 112 | fi 113 | 114 | exit 0 115 | -------------------------------------------------------------------------------- /old/imac-hd-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ================================================================================ 4 | # imac-hd-check.sh 5 | # Hannes Juutilainen 6 | # 7 | # This script checks if the __current__ machine is part of Apple's iMac 1TB Seagate 8 | # Hard Drive Replacement Program . 9 | # The script is ready to be copy/pasted to Apple Remote Desktop or some other 10 | # remote management system. 11 | # 12 | # The script is adapted from iMac_Warranty_Check.rb by Riley Shott 13 | # https://github.com/Ginja/Admin_Scripts/blob/master/iMac_Warranty_Check.rb 14 | # 15 | # 16 | # Version history: 17 | # 2012-11-06, Hannes Juutilainen 18 | # - First version 19 | # ================================================================================ 20 | 21 | # ================================================================================ 22 | # Exit codes, customize if needed. 23 | # For example, ARD outputs a green task status with 0 and red task status with 1 24 | # ================================================================================ 25 | HARD_DRIVE_REPLACEMENT_NEEDED=1 26 | HARD_DRIVE_REPLACEMENT_NOT_NEEDED=0 27 | NOT_APPLICABLE_FOR_THIS_MODEL=0 28 | 29 | # ================================================================================ 30 | # First of all, we need to be running on an iMac 31 | # ================================================================================ 32 | MODEL=$(system_profiler SPHardwareDataType | sed -n -e 's/[ \t][ \t]*Model Name: \(.*\)/\1/g p') 33 | if [[ $MODEL != "iMac" ]]; then 34 | echo "Not applicable for this model: $MODEL" 35 | exit $NOT_APPLICABLE_FOR_THIS_MODEL 36 | fi 37 | 38 | # ================================================================================ 39 | # Get the hostname and serial number 40 | # ================================================================================ 41 | HOSTNAME=$(hostname) 42 | SERIAL=$(system_profiler SPHardwareDataType | awk '/Serial Number/ {print $4}') 43 | 44 | # ================================================================================ 45 | # Construct the curl command 46 | # ================================================================================ 47 | CHECK_URL="https://supportform.apple.com/201107/SerialNumberEligibilityAction.do?cb=iMacHDCheck.response&sn=${SERIAL}" 48 | CURL="curl --silent --max-time 30" 49 | 50 | # ================================================================================ 51 | # Check if this machine is part of the replacement program 52 | # ================================================================================ 53 | VALID_SN_STRING="Valid iMac SN has Seagate HDD - covered by program" 54 | WARRANTYSTATUS=$($CURL "${CHECK_URL}" | grep "${VALID_SN_STRING}") 55 | if [[ $? == 0 ]]; then 56 | echo "Host $HOSTNAME with serial number $SERIAL needs a replacement disk" 57 | exit $HARD_DRIVE_REPLACEMENT_NEEDED 58 | fi 59 | 60 | exit $HARD_DRIVE_REPLACEMENT_NOT_NEEDED 61 | -------------------------------------------------------------------------------- /reposado-add-new-products.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ====================================== 4 | # reposado-add-new-products.sh 5 | # 6 | # Script to add new products to a branch 7 | # ====================================== 8 | 9 | REPOUTIL="/var/git/reposado/code/repoutil" 10 | 11 | function usage () { 12 | echo "Usage: $0 ..." 13 | exit 14 | } 15 | 16 | # We need a branch. 17 | if [ -z $1 ]; then 18 | echo "Missing branch" 19 | usage 20 | exit 1 21 | fi 22 | 23 | # Print the products to be added 24 | echo "Getting a list of new products..." 25 | IFS=" 26 | " 27 | NEW_PRODUCTS=( $("${REPOUTIL}" --products | grep -e " \[\] \$") ) 28 | if [[ ${#NEW_PRODUCTS[@]} -eq 0 ]]; then 29 | echo "No new products" 30 | exit 0 31 | fi 32 | for (( i=0; i<${#NEW_PRODUCTS[@]}; i++ )); do 33 | echo ${NEW_PRODUCTS[$i]} 34 | done 35 | unset IFS 36 | echo "" 37 | 38 | # Ask for confirmation 39 | while true; do 40 | if [ $# -gt 1 ]; then 41 | read -p "Add products to catalog $1? [y]n: " yn 42 | else 43 | read -p "Add products to catalogs: $*? [y]n: " yn 44 | fi 45 | case $yn in 46 | [Yy]* ) 47 | NEW_PRODUCT_IDS=( $("${REPOUTIL}" --products | grep -e " \[\] \$" | awk '{print $1}') ) 48 | while [ "$1" != "" ]; do 49 | echo "$REPOUTIL --add-products ${NEW_PRODUCT_IDS[@]} $1" 50 | #"${REPOUTIL}" --add-products ${NEW_PRODUCT_IDS[@]} $1 51 | echo "" 52 | shift 53 | done 54 | break;; 55 | [Nn]* ) 56 | exit;; 57 | * ) echo "Please answer yes or no.";; 58 | esac 59 | done 60 | -------------------------------------------------------------------------------- /reposado-remove-deprecated-products.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ================================================== 4 | # reposado-remove-deprecated-products.sh 5 | # 6 | # Script to remove deprecated products from a branch 7 | # ================================================== 8 | 9 | REPOUTIL="/var/git/reposado/code/repoutil" 10 | 11 | function usage () { 12 | echo "Usage: $0 " 13 | exit 14 | } 15 | 16 | # We need a branch. 17 | if [ -z $1 ]; then 18 | echo "Missing branch" 19 | usage 20 | exit 1 21 | fi 22 | 23 | # Print the products to be removed 24 | echo "Getting a list of deprecated products..." 25 | IFS=" 26 | " 27 | DEPRECATED_PRODUCTS=( $("${REPOUTIL}" --list-branch=${1} | grep Deprecated) ) 28 | if [[ ${#DEPRECATED_PRODUCTS[@]} -eq 0 ]]; then 29 | echo "No deprecated products" 30 | exit 0 31 | fi 32 | for (( i=0; i<${#DEPRECATED_PRODUCTS[@]}; i++ )); do 33 | echo ${DEPRECATED_PRODUCTS[$i]} 34 | done 35 | unset IFS 36 | echo "" 37 | 38 | # Ask for confirmation 39 | while true; do 40 | read -p "Remove products from catalog $1? [y]n: " yn 41 | case $yn in 42 | [Yy]* ) 43 | DEPRECATED_PRODUCT_IDS=( $("${REPOUTIL}" --list-branch=${1} | grep Deprecated | awk '{print $1}') ) 44 | echo "${REPOUTIL} --remove-product ${DEPRECATED_PRODUCT_IDS[@]} $1" 45 | #"${REPOUTIL}" --remove-product ${DEPRECATED_PRODUCT_IDS[@]} $1 46 | break;; 47 | [Nn]* ) 48 | exit;; 49 | * ) echo "Please answer yes or no.";; 50 | esac 51 | done 52 | -------------------------------------------------------------------------------- /wrap-dmg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | wrap-dmg.py 5 | 6 | Created by Hannes Juutilainen on 2010-10-18. 7 | """ 8 | 9 | import sys 10 | import os 11 | import getopt 12 | import subprocess 13 | import tempfile 14 | import shutil 15 | import plistlib 16 | 17 | # =================================================== 18 | # Options for creating the disk image 19 | # =================================================== 20 | dmg_format = 'UDZO' # UDIF zlib-compressed 21 | # dmg_format = 'UDRO' # UDIF read-only image 22 | 23 | # Formats that make sense in this context: 24 | # UDRO - UDIF read-only image 25 | # UDCO - UDIF ADC-compressed image 26 | # UDZO - UDIF zlib-compressed image 27 | # UDBZ - UDIF bzip2-compressed image (OS X 10.4+ only) 28 | # UFBI - UDIF entire image with MD5 checksum 29 | # UDTO - DVD/CD-R master for export 30 | 31 | dmg_uid = '99' # Who ever is mounting 32 | dmg_gid = '99' # Who ever is mounting 33 | dmg_mode = '555' # Read-only 34 | dmg_fs = 'HFS+' # Filesystem 35 | 36 | # =================================================== 37 | # Globals 38 | # =================================================== 39 | input_item = "" 40 | input_item_name = "" 41 | input_item_type = "" 42 | use_contents = False 43 | hdiutil_output_name = "" 44 | hdiutil_volume_name = "" 45 | hdiutil_output_path = "" 46 | input_parent = "" 47 | item_name = "" 48 | verbose = False 49 | quiet = False 50 | auto = False 51 | 52 | help_message = ''' 53 | Usage: wrap-dmg.py 54 | 55 | Options: 56 | -h | --help Display this message 57 | -i | --input Path to file/folder to process 58 | -n | --name Optional, custom name for the disk image 59 | -v | --verbose Show operation details 60 | -a | --auto Don't ask any question, just use the defaults 61 | 62 | ''' 63 | 64 | 65 | class Usage(Exception): 66 | def __init__(self, msg): 67 | self.msg = msg 68 | 69 | 70 | def create_disk_image(): 71 | """Wrap the temp directory in a disk image""" 72 | if verbose: 73 | print "\nCreating disk image with properties:" 74 | print "---> %-20s%-20s" % ("Filename:", os.path.split(hdiutil_output_path)[1]) 75 | print "---> %-20s%-20s" % ("Output path:", hdiutil_output_path) 76 | else: 77 | if not quiet: print "\nCreating disk image from %s" % input_item 78 | 79 | hdiutil_process = ['/usr/bin/hdiutil', 80 | 'create', 81 | '-srcFolder', temp_dir, 82 | '-format', dmg_format, 83 | '-fs', dmg_fs, 84 | '-volname', hdiutil_volume_name, 85 | '-uid', dmg_uid, 86 | '-gid', dmg_gid, 87 | '-mode', dmg_mode, 88 | '-noscrub', 89 | # '-verbose', 90 | hdiutil_output_path] 91 | if not os.path.exists(hdiutil_output_path): 92 | p = subprocess.Popen(hdiutil_process, 93 | bufsize=1, 94 | stdout=subprocess.PIPE, 95 | stderr=subprocess.PIPE) 96 | (plist, err) = p.communicate() 97 | if err: 98 | print >> sys.stderr, "%s" % (err) 99 | if not quiet: print "---> Done" 100 | if p.returncode == 0: 101 | return True 102 | else: 103 | return False 104 | else: 105 | if not quiet: print "---> %s exists." % hdiutil_output_path 106 | return False 107 | 108 | 109 | def copy_item(): 110 | """Copy target item(s) to temporary directory""" 111 | if verbose: print "\nCopying target to temp directory" 112 | global use_contents 113 | src = input_item 114 | (file_name, file_extension) = os.path.splitext(input_item_name) 115 | if os.path.isdir(input_item): 116 | if file_extension == ".app" or file_extension == ".pkg" or file_extension == ".mpkg": 117 | dst = os.path.join(temp_dir, os.path.split(input_item)[1]) 118 | elif not use_contents: 119 | dst = os.path.join(temp_dir, os.path.split(input_item)[1]) 120 | else: 121 | dst = temp_dir 122 | else: 123 | dst = os.path.join(temp_dir, os.path.split(input_item)[1]) 124 | if os.path.exists(temp_dir): 125 | if verbose: print "---> %-20s%-20s" % ("Source:", src) 126 | if verbose: print "---> %-20s%-20s" % ("Destination:", dst) 127 | return_code = 1 128 | if os.path.isfile(input_item): 129 | if verbose: print "---> %-20s%-20s" % ("Source type:", "File") 130 | return_code = subprocess.call(["/bin/cp", src, dst]) 131 | elif os.path.isdir(input_item): 132 | if verbose: print "---> %-20s%-20s" % ("Source type:", "Directory") 133 | return_code = subprocess.call(["/usr/bin/ditto", src, dst]) 134 | if return_code == 0: 135 | if verbose: print "---> Done" 136 | clear_quarantine(dst) 137 | return True 138 | else: 139 | return False 140 | 141 | 142 | def create_temp_dir(): 143 | """Create a secure temp directory""" 144 | if verbose: print "\nCreating a temp directory" 145 | global temp_dir 146 | temp_dir = tempfile.mkdtemp() 147 | if os.path.exists(temp_dir): 148 | if verbose: print "---> %s" % temp_dir 149 | if verbose: print "---> Done" 150 | return True 151 | else: 152 | return False 153 | 154 | 155 | def clear_temp_dir(): 156 | """Remove the temp directory""" 157 | if verbose: print "\nRemoving temp directory" 158 | if os.path.exists(temp_dir): 159 | shutil.rmtree(temp_dir) 160 | if not os.path.exists(temp_dir): 161 | if verbose: print "---> Removed %s" % temp_dir 162 | if verbose: print "---> Done" 163 | return True 164 | else: 165 | return False 166 | 167 | 168 | def create_link(destination): 169 | """ Create a symbolic link in disk image""" 170 | ln_source = destination 171 | ln_destination = os.path.join(temp_dir, os.path.split(ln_source)[1]) 172 | subprocess.call(["/bin/ln", "-s", ln_source, ln_destination]) 173 | 174 | 175 | def defaults_read(key_name, file_name): 176 | """Read the specified key from specified file""" 177 | defaults_process = ["/usr/bin/defaults", "read", file_name, key_name] 178 | p = subprocess.Popen(defaults_process, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 179 | (results, err) = p.communicate() 180 | return results.strip() 181 | 182 | 183 | def new_title_for_app_bundle(bundle_path): 184 | """Given a path, tries to create a versioned title by reading from [path]/Contents/Info.plist""" 185 | original_name = os.path.split(bundle_path)[1] 186 | (original_name, file_extension) = os.path.splitext(original_name) 187 | info_plist_path_for_defaults = os.path.realpath(os.path.join(bundle_path, 'Contents/Info')) 188 | bundle_short_version_string = "" 189 | bundle_version = "" 190 | bundle_short_version_string = defaults_read('CFBundleShortVersionString', info_plist_path_for_defaults) 191 | bundle_version = defaults_read('CFBundleVersion', info_plist_path_for_defaults) 192 | if bundle_short_version_string != "" and not original_name.endswith(bundle_short_version_string): 193 | if verbose: print "---> %-20s%-20s" % ("Version:", bundle_short_version_string) 194 | return "-".join([original_name, bundle_short_version_string]) 195 | elif bundle_version != "" and not original_name.endswith(bundle_version): 196 | if verbose: print "---> %-20s%-20s" % ("Version:", bundle_version) 197 | return "-".join([original_name, bundle_version]) 198 | else: 199 | return original_name 200 | 201 | 202 | def clear_quarantine(file_path): 203 | """Check and clear com.apple.quarantine""" 204 | if verbose: print "\nClearing quarantine attributes on %s" % input_item_name 205 | xattr_process = ["/usr/bin/xattr", file_path] 206 | xattr_process = ["/usr/bin/xattr", "-r", "-d", "com.apple.quarantine", file_path] 207 | p = subprocess.Popen(xattr_process, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 208 | (results, err) = p.communicate() 209 | if verbose: print "---> Done" 210 | pass 211 | 212 | 213 | def main(argv=None): 214 | if argv is None: 215 | argv = sys.argv 216 | try: 217 | try: 218 | long_args = ["help", "input=", "name=", "verbose", "auto"] 219 | opts, args = getopt.getopt(argv[1:], "hi:n:va", long_args) 220 | except getopt.error, msg: 221 | raise Usage(msg) 222 | 223 | # =================================================== 224 | # option processing 225 | # =================================================== 226 | for option, value in opts: 227 | if option in ("-v", "--verbose"): 228 | global verbose 229 | verbose = True 230 | if option in ("-q", "--quiet"): 231 | global quiet 232 | quiet = True 233 | if option in ("-a", "--auto"): 234 | global auto 235 | auto = True 236 | if option in ("-h", "--help"): 237 | raise Usage(help_message) 238 | if option in ("-i", "--input"): 239 | global input_item 240 | input_item = os.path.abspath(value) 241 | global use_contents 242 | if value.endswith("/") and not input_item.endswith("/"): 243 | use_contents = True 244 | else: 245 | use_contents = False 246 | global input_parent 247 | input_parent = os.path.split(input_item)[0] 248 | if option in ("-n", "--name"): 249 | global item_name 250 | item_name = value 251 | 252 | # =================================================== 253 | # We need at least input_item to process 254 | # =================================================== 255 | if not input_item: 256 | raise Usage(help_message) 257 | 258 | # =================================================== 259 | # Construct the needed names and paths 260 | # =================================================== 261 | else: 262 | global input_item_name 263 | global hdiutil_output_name 264 | global hdiutil_volume_name 265 | global hdiutil_output_path 266 | global input_item_type 267 | 268 | # =================================================== 269 | # Get filename and extension 270 | # =================================================== 271 | if verbose: print "\nAnalyzing %s" % input_item 272 | input_item_name = os.path.split(input_item)[1] 273 | (file_name, file_extension) = os.path.splitext(input_item_name) 274 | 275 | # =================================================== 276 | # Try to get a good name based on input file type 277 | # =================================================== 278 | if file_extension == ".app": 279 | if verbose: print "---> %-20s%-20s" % ("Type:", "Application") 280 | input_item_type = "Application" 281 | file_name = new_title_for_app_bundle(input_item) 282 | elif file_extension == ".pkg" or file_extension == ".mpkg": 283 | input_item_type = "Installer package" 284 | if os.path.isdir(input_item): 285 | if verbose: print "---> %-20s%-20s" % ("Type:", "Installer package") 286 | file_name = new_title_for_app_bundle(input_item) 287 | else: 288 | if verbose: print "---> %-20s%-20s" % ("Type:", "Generic") 289 | input_item_type = "Generic" 290 | file_name = new_title_for_app_bundle(input_item) 291 | 292 | # =================================================== 293 | # Replace whitespace with dashes 294 | # =================================================== 295 | file_name = file_name.replace(" ", "-") 296 | 297 | if verbose: print "---> %-20s%-20s" % ("Basename:", file_name) 298 | if not item_name: 299 | hdiutil_output_name = ".".join([file_name, "dmg"]) 300 | hdiutil_volume_name = file_name 301 | else: 302 | hdiutil_output_name = ".".join([item_name, "dmg"]) 303 | hdiutil_volume_name = item_name 304 | 305 | # =================================================== 306 | # If the input file is not within the user home dir, 307 | # point the output path to ~/Downloads 308 | # =================================================== 309 | home_directory = os.path.expanduser('~') 310 | if not input_parent.startswith(home_directory): 311 | hdiutil_output_path = os.path.join(home_directory, 'Downloads', hdiutil_output_name) 312 | else: 313 | hdiutil_output_path = os.path.join(input_parent, hdiutil_output_name) 314 | if verbose: print "---> %-20s%-20s" % ("Volume name:", hdiutil_volume_name) 315 | if verbose: print "---> %-20s%-20s" % ("Output path:", hdiutil_output_path) 316 | 317 | if not auto: 318 | print "\nPlease provide some additional details for the disk image:" 319 | 320 | # =================================================== 321 | # Ask for volume name 322 | # =================================================== 323 | question = "---> Name of the volume? [%s]: " % hdiutil_volume_name 324 | answer = raw_input(question) 325 | if answer: 326 | hdiutil_volume_name = answer 327 | hdiutil_output_name = ".".join([hdiutil_volume_name, "dmg"]) 328 | 329 | # =================================================== 330 | # Ask for filename 331 | # =================================================== 332 | question = "---> Filename for the disk image? [%s]: " % hdiutil_output_name 333 | answer = raw_input(question) 334 | if answer: 335 | hdiutil_output_name = answer 336 | 337 | # =================================================== 338 | # Ask for save path 339 | # =================================================== 340 | question = "---> Output directory? [%s]: " % os.path.split(hdiutil_output_path)[0] 341 | answer = raw_input(question) 342 | if answer: 343 | hdiutil_output_path = os.path.join(answer, hdiutil_output_name) 344 | else: 345 | hdiutil_output_path = os.path.join(os.path.split(hdiutil_output_path)[0], hdiutil_output_name) 346 | 347 | # =================================================== 348 | # Start working 349 | # =================================================== 350 | # clear_quarantine(input_item) 351 | 352 | if not create_temp_dir(): 353 | print >> sys.stderr, "Error. Creating a temp directory failed" 354 | return 2 355 | if not copy_item(): 356 | print >> sys.stderr, "Error. Copying items to temp directory failed" 357 | clear_temp_dir() 358 | return 2 359 | if input_item_type == "Application": 360 | create_link("/Applications") 361 | create_link("/Applications/Utilities") 362 | if not create_disk_image(): 363 | print >> sys.stderr, "Error. Creating the disk image failed" 364 | clear_temp_dir() 365 | return 2 366 | if not clear_temp_dir(): 367 | print >> sys.stderr, "Error. Cleaning the temp directory failed" 368 | return 2 369 | if not quiet: print "" 370 | 371 | return 0 372 | 373 | except Usage, err: 374 | print >> sys.stderr, str(err.msg) 375 | return 2 376 | 377 | 378 | if __name__ == "__main__": 379 | sys.exit(main()) 380 | --------------------------------------------------------------------------------