diff --git a/.github/workflows/build-app-wxpython.yml b/.github/workflows/build-app-wxpython.yml index 000198293..858a32e61 100644 --- a/.github/workflows/build-app-wxpython.yml +++ b/.github/workflows/build-app-wxpython.yml @@ -9,26 +9,49 @@ on: jobs: build: name: Build wxPython - runs-on: x86_64_mojave + runs-on: x86_64_monterey if: github.repository_owner == 'dortania' env: branch: ${{ github.ref }} commiturl: ${{ github.event.head_commit.url }}${{ github.event.release.html_url }} commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }} + MAC_CODESIGN_IDENTITY: ${{ secrets.MAC_CODESIGN_IDENTITY }} + MAC_CODESIGN_CERT: ${{ secrets.MAC_CODESIGN_CERT }} MAC_NOTARIZATION_USERNAME: ${{ secrets.MAC_NOTARIZATION_USERNAME }} MAC_NOTARIZATION_PASSWORD: ${{ secrets.MAC_NOTARIZATION_PASSWORD }} + MAC_NOTARIZATION_TEAM_ID: ${{ secrets.MAC_NOTARIZATION_TEAM_ID }} ANALYTICS_KEY: ${{ secrets.ANALYTICS_KEY }} ANALYTICS_SITE: ${{ secrets.ANALYTICS_SITE }} steps: - uses: actions/checkout@v3 - - run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 Build-Binary.command --reset_binaries --branch "${{ env.branch }}" --commit "${{ env.commiturl }}" --commit_date "${{ env.commitdate }}" --key "${{ env.ANALYTICS_KEY }}" --site "${{ env.ANALYTICS_SITE }}" - - run: 'codesign -s "Developer ID Application: Mykola Grymalyuk (S74BDJXQMD)" -v --force --deep --timestamp --entitlements ./payloads/entitlements.plist -o runtime "dist/OpenCore-Patcher.app"' - - run: cd dist; ditto -c -k --sequesterRsrc --keepParent OpenCore-Patcher.app ../OpenCore-Patcher-wxPython.app.zip - - run: xcrun altool --notarize-app --primary-bundle-id "com.dortania.opencore-legacy-patcher" --username "${{ env.MAC_NOTARIZATION_USERNAME }}" --password "${{ env.MAC_NOTARIZATION_PASSWORD }}" --file OpenCore-Patcher-wxPython.app.zip - - run: packagesbuild ./payloads/InstallPackage/AutoPkg-Assets-Setup.pkgproj - - run: mv ./OpenCore-Patcher-wxPython.app.zip ./OpenCore-Patcher-GUI.app.zip + + - name: Build Binary + run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 Build-Binary.command --reset_binaries --branch "${{ env.branch }}" --commit "${{ env.commiturl }}" --commit_date "${{ env.commitdate }}" --key "${{ env.ANALYTICS_KEY }}" --site "${{ env.ANALYTICS_SITE }}" + + # Uncomment when using Github Runners or first run on self-hosted + # - name: Import Certificate + # uses: apple-actions/import-codesign-certs@v1 + # with: + # p12-file-base64: ${{ secrets.MAC_CODESIGN_CERT }} + # p12-password: ${{ secrets.MAC_NOTARIZATION_PASSWORD }} + + - name: Codesign Binary + run: 'codesign -s "${{ env.MAC_CODESIGN_IDENTITY }}" -v --force --deep --timestamp --entitlements ./payloads/entitlements.plist -o runtime "dist/OpenCore-Patcher.app"' + + - name: Package Binary + run: cd dist; ditto -c -k --sequesterRsrc --keepParent OpenCore-Patcher.app ../OpenCore-Patcher-wxPython.app.zip + + - name: Notarize Binary + run: xcrun notarytool submit OpenCore-Patcher-wxPython.app.zip --apple-id "${{ env.MAC_NOTARIZATION_USERNAME }}" --password "${{ env.MAC_NOTARIZATION_PASSWORD }}" --team-id "${{ env.MAC_NOTARIZATION_TEAM_ID }}" + + - name: Generate support package + run: packagesbuild ./payloads/InstallPackage/AutoPkg-Assets-Setup.pkgproj + + - name: Prepare App for Upload + run: mv ./OpenCore-Patcher-wxPython.app.zip ./OpenCore-Patcher-GUI.app.zip + - name: Upload App to Artifacts uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 52496e234..a02bcc59f 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -9,7 +9,7 @@ on: jobs: build: name: Validate - runs-on: x86_64_mojave + runs-on: x86_64_monterey if: github.repository_owner == 'dortania' env: branch: ${{ github.ref }} @@ -17,4 +17,5 @@ jobs: commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }} steps: - uses: actions/checkout@v3 - - run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 OpenCore-Patcher-GUI.command --validate + - name: Validate + run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 OpenCore-Patcher-GUI.command --validate diff --git a/Build-Binary.command b/Build-Binary.command index 9e23a7ee9..f481b87a4 100755 --- a/Build-Binary.command +++ b/Build-Binary.command @@ -231,6 +231,7 @@ class CreateBinary: whitelist_files = [ "com.dortania.opencore-legacy-patcher.auto-patch.plist", + "com.dortania.opencore-legacy-patcher.rsr-monitor.plist", "entitlements.plist", "launcher.sh", "OC-Patcher-TUI.icns", diff --git a/CHANGELOG.md b/CHANGELOG.md index 888a84c41..b80f7c111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # OpenCore Legacy Patcher changelog +## 0.6.6 +- Implement option to disable ColorSync downgrade on HD 3000 Macs + - Allows for Display Profiles support on some units + - Note: black box rendering issues will likely appear + - Thanks [@jazzzny](https://github.com/Jazzzny) + ## 0.6.5 - Update 3802 Patchset Binaries: - Resolves additional 3rd party app crashes on Metal with macOS 13.3+ @@ -11,10 +17,25 @@ - Restores USB 3.0 expansion card support on USB 1.1 machines such as MacPro5,1 - Resolve OpenCL rendering on Nvidia Web Drivers - thanks [@jazzzny](https://github.com/Jazzzny) -- Implement option to disable ColorSync downgrade on HD 3000 Macs - - Allows for Display Profiles support +- Resolve UI unable to download macOS installers on unknown models + - ex. M2 Macs and Hackintoshes +- Implement minimum OS check for installer creation + - Prevents vague errors when creating Ventura installers on Yosemite +- Resolve WindowServer crashing with Rapid Security Response (RSR) installation + - Primarily applicable for Haswell iGPUs on 13.3.1 (a) +- Update legacy Wireless binaries + - Resolve wifi crashing on 13.4 with BCM94322, BCM943224 and Atheros chipsets +- Backend changes: + - macos_installer_handler.py: + - Expand OS support for IA parsing in SUCatalog + - gui_main.py: + - Fix spacing regression introduced with `.AppleSystemUIFont` implementation - Increment Binaries: - - PatcherSupportPkg 0.9.6 - release + - PatcherSupportPkg 0.9.7 - release +- Build Server Changes: + - Upgrade CI Host to macOS Monterey + - Upgrade Xcode to 14.2 + - Switch from `altool` to `notarytool` for notarization ## 0.6.4 - Backend changes: diff --git a/data/os_data.py b/data/os_data.py index 8f86929bc..fbf7fcc93 100644 --- a/data/os_data.py +++ b/data/os_data.py @@ -28,22 +28,51 @@ class os_data(enum.IntEnum): class os_conversion: - def os_to_kernel(os): - # Convert OS version to major XNU version + def os_to_kernel(os: str) -> int: + """ + Convert OS version to major XNU version + + Parameters: + os (str): OS version + + Returns: + int: Major XNU version + """ if os.startswith("10."): return (int(os.split(".")[1]) + 4) else: return (int(os.split(".")[0]) + 9) - def kernel_to_os(kernel): - # Convert major XNU version to OS version + + def kernel_to_os(kernel: int) -> str: + """ + Convert major XNU version to OS version + + Parameters: + kernel (int): Major XNU version + + Returns: + str: OS version + """ if kernel >= os_data.big_sur: return str((kernel - 9)) else: return str((f"10.{kernel - 4}")) - def is_os_newer(source_major, source_minor, target_major, target_minor): - # Check if OS version 1 is newer than OS version 2 + + def is_os_newer(source_major: int, source_minor: int, target_major: int, target_minor: int) -> bool: + """ + Check if OS version 1 is newer than OS version 2 + + Parameters: + source_major (int): Major XNU version of OS version 1 + source_minor (int): Minor XNU version of OS version 1 + target_major (int): Major XNU version of OS version 2 + target_minor (int): Minor XNU version of OS version 2 + + Returns: + bool: True if OS version 1 is newer than OS version 2 + """ if source_major < target_major: return True elif source_major == target_major: @@ -52,8 +81,17 @@ class os_conversion: else: return False - def convert_kernel_to_marketing_name(kernel): - # Convert major XNU version to Marketing Name + + def convert_kernel_to_marketing_name(kernel: int) -> str: + """ + Convert major XNU version to Marketing name + + Parameters: + kernel (int): Major XNU version + + Returns: + str: Marketing name of OS + """ try: # Find os_data enum name os_name = os_data(kernel).name @@ -70,8 +108,17 @@ class os_conversion: return os_name - def convert_marketing_name_to_kernel(marketing_name): - # Convert Marketing Name to major XNU version + + def convert_marketing_name_to_kernel(marketing_name: str) -> int: + """ + Convert Marketing Name to major XNU version + + Parameters: + marketing_name (str): Marketing Name of OS + + Returns: + int: Major XNU version + """ try: # Find os_data enum value os_kernel = os_data[marketing_name.lower().replace(" ", "_")] @@ -81,16 +128,18 @@ class os_conversion: return int(os_kernel) - def find_largest_build(build_array): - # Find the newest version within an array of versions - # These builds will have both numbers and letters in the version - # ex: - # [ - # "22A5295i", - # "22A5266r", - # "22A5286j", - # "22A5295h", - # ] + def find_largest_build(build_array: list) -> str: + """ + Find the newest version within an array of versions + These builds will have both numbers and letters in the version + ex: + [ + "22A5295i", + "22A5266r", + "22A5286j", + "22A5295h", + ] + """ max_length = 0 # Length of the longest build build_array_split = [] # 'build_array', converted into individual array of elements diff --git a/docs/MODELS.md b/docs/MODELS.md index 3e83a18e9..4e78001c9 100644 --- a/docs/MODELS.md +++ b/docs/MODELS.md @@ -18,13 +18,13 @@ The below table will list all supported and unsupported functions of the patcher Regarding OS support, see below: -* Machines listing `YES - Ventura and older` means they cannot run macOS Ventura at this time. Machines with only `YES` can run all of the supported macOS versions offered by OpenCore Legacy Patcher. - | Support Entry | Supported OSes | Description | Comment | | :--- | :--- | :--- | :--- | -| HostOS | macOS 10.9 - macOS 13 | Refers to OSes where running OpenCore-Patcher.app are supported | Supports 10.7+ if [Python 3.9 or higher](https://www.python.org/downloads/) is manually installed, simply run the `OpenCore-Patcher-GUI.command` located in the repo | +| HostOS | macOS 10.10 - macOS 13 | Refers to OSes where running OpenCore-Patcher.app are supported | Supports 10.7+ if [Python 3.9 or higher](https://www.python.org/downloads/) is manually installed, simply run the `OpenCore-Patcher-GUI.command` located in the repo | | TargetOS | macOS 11 - macOS 13 | Refers to OSes that can be patched to run with OpenCore | May support 10.4 and newer (in a potentially broken state). No support provided. | +* macOS Ventura installer creation requires 10.11 or later + ### MacBook | SMBIOS | Year | Supported | Comment | diff --git a/payloads/com.dortania.opencore-legacy-patcher.rsr-monitor.plist b/payloads/com.dortania.opencore-legacy-patcher.rsr-monitor.plist new file mode 100644 index 000000000..2a94cd019 --- /dev/null +++ b/payloads/com.dortania.opencore-legacy-patcher.rsr-monitor.plist @@ -0,0 +1,20 @@ + + + + + AssociatedBundleIdentifiers + com.dortania.opencore-legacy-patcher + Label + com.dortania.opencore-legacy-patcher.rsr-monitor + ProgramArguments + + rm + -rf + /Library/Extensions/example.kext + + WatchPaths + + /System/Volumes/Preboot/UUID/cryptex1/OS.dmg + + + diff --git a/resources/constants.py b/resources/constants.py index f838424c7..23cec43fe 100644 --- a/resources/constants.py +++ b/resources/constants.py @@ -12,8 +12,8 @@ from data import os_data class Constants: def __init__(self) -> None: # Patcher Versioning - self.patcher_version: str = "0.6.5" # OpenCore-Legacy-Patcher - self.patcher_support_pkg_version: str = "0.9.6" # PatcherSupportPkg + self.patcher_version: str = "0.6.6" # OpenCore-Legacy-Patcher + self.patcher_support_pkg_version: str = "0.9.7" # PatcherSupportPkg self.copyright_date: str = "Copyright © 2020-2023 Dortania" # URLs @@ -239,6 +239,10 @@ class Constants: def auto_patch_launch_agent_path(self): return self.payload_path / Path("com.dortania.opencore-legacy-patcher.auto-patch.plist") + @property + def rsr_monitor_launch_daemon_path(self): + return self.payload_path / Path("com.dortania.opencore-legacy-patcher.rsr-monitor.plist") + # ACPI @property def pci_ssdt_path(self): diff --git a/resources/gui/gui_main.py b/resources/gui/gui_main.py index 977d56be5..75c09566a 100644 --- a/resources/gui/gui_main.py +++ b/resources/gui/gui_main.py @@ -1782,7 +1782,7 @@ class wx_python_gui: if model in ["MacPro3,1", "MacPro4,1", "MacPro5,1"]: has_legacy_usb = True issues_list = "- Lack of Keyboard/Mouse in macOS installer without a USB hub\n" - elif model in smbios_data.smbios_dictionary[model]: + elif model in smbios_data.smbios_dictionary: if "CPU Generation" in smbios_data.smbios_dictionary[model]: if smbios_data.smbios_dictionary[model]["CPU Generation"] <= cpu_data.cpu_data.penryn: has_legacy_usb = True @@ -2060,7 +2060,17 @@ class wx_python_gui: logging.info("Installer(s) found:") for app in available_installers: logging.info(f"- {available_installers[app]['Short Name']}: {available_installers[app]['Version']} ({available_installers[app]['Build']})") - self.install_selection = wx.Button(self.frame, label=f"{available_installers[app]['Short Name']}: {available_installers[app]['Version']} ({available_installers[app]['Build']})", size=(320, 30)) + + app_str = f"{available_installers[app]['Short Name']}" + unsupported: bool = available_installers[app]['Minimum Host OS'] > self.constants.detected_os + + if unsupported: + min_str = os_data.os_conversion.convert_kernel_to_marketing_name(available_installers[app]['Minimum Host OS']) + app_str += f" (Requires {min_str})" + else: + app_str += f": {available_installers[app]['Version']} ({available_installers[app]['Build']})" + + self.install_selection = wx.Button(self.frame, label=app_str, size=(320, 30)) i = i + 25 self.install_selection.SetPosition( wx.Point( @@ -2070,6 +2080,9 @@ class wx_python_gui: ) self.install_selection.Bind(wx.EVT_BUTTON, lambda event, temp=app: self.format_usb_menu(available_installers[temp]['Short Name'], available_installers[temp]['Path'])) self.install_selection.Centre(wx.HORIZONTAL) + + if unsupported: + self.install_selection.Disable() else: logging.info("No installers found") # Label: No Installers Found @@ -2099,6 +2112,7 @@ class wx_python_gui: def format_usb_menu(self, installer_name, installer_path): self.frame.DestroyChildren() logging.info(installer_path) + self.frame.SetSize(370, -1) # Header self.header = wx.StaticText(self.frame, label="Format USB") @@ -2171,7 +2185,7 @@ class wx_python_gui: def format_usb_progress(self, disk, installer_name, installer_path): self.frame.DestroyChildren() - self.frame.SetSize(500, -1) + self.frame.SetSize(520, -1) # Header self.header = wx.StaticText(self.frame, label=f"Creating Installer: {installer_name}") self.header.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont")) diff --git a/resources/macos_installer_handler.py b/resources/macos_installer_handler.py index 368b798ef..2632b481d 100644 --- a/resources/macos_installer_handler.py +++ b/resources/macos_installer_handler.py @@ -13,10 +13,24 @@ from resources import network_handler, utilities APPLICATION_SEARCH_PATH: str = "/Applications" SFR_SOFTWARE_UPDATE_PATH: str = "SFR/com_apple_MobileAsset_SFRSoftwareUpdate/com_apple_MobileAsset_SFRSoftwareUpdate.xml" - -CATALOG_URL_BASE: str = "https://swscan.apple.com/content/catalogs/others/index" -CATALOG_URL_EXTENSION: str = "13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog" -CATALOG_URL_VERSION: str = "13" +CATALOG_URL_BASE: str = "https://swscan.apple.com/content/catalogs/others/index" +CATALOG_URL_EXTENSION: str = ".merged-1.sucatalog" +CATALOG_URL_VARIANTS: list = [ + "13", + "12", + "10.16", + "10.15", + "10.14", + "10.13", + "10.12", + "10.11", + "10.10", + "10.9", + "mountainlion", + "lion", + "snowleopard", + "leopard", +] tmp_dir = tempfile.TemporaryDirectory() @@ -222,15 +236,15 @@ class RemoteInstallerCatalog: Parses Apple's Software Update catalog and finds all macOS installers. """ - def __init__(self, seed_override: SeedType = SeedType.PublicRelease) -> None: + def __init__(self, seed_override: SeedType = SeedType.PublicRelease, os_override: int = os_data.os_data.ventura) -> None: - self.catalog_url: str = self._construct_catalog_url(seed_override) + self.catalog_url: str = self._construct_catalog_url(seed_override, os_override) self.available_apps: dict = self._parse_catalog() self.available_apps_latest: dict = self._list_newest_installers_only() - def _construct_catalog_url(self, seed_type: SeedType) -> str: + def _construct_catalog_url(self, seed_type: SeedType, os_kernel: int) -> str: """ Constructs the catalog URL based on the seed type @@ -241,17 +255,30 @@ class RemoteInstallerCatalog: str: The catalog URL """ + url: str = CATALOG_URL_BASE - url: str = "" + os_version: str = os_data.os_conversion.kernel_to_os(os_kernel) + os_version = "10.16" if os_version == "11" else os_version + if os_version not in CATALOG_URL_VARIANTS: + logging.error(f"OS version {os_version} is not supported, defaulting to latest") + os_version = CATALOG_URL_VARIANTS[0] + url += f"-{os_version}" if seed_type == SeedType.DeveloperSeed: - url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}seed-{CATALOG_URL_EXTENSION}" + url += f"seed" elif seed_type == SeedType.PublicSeed: - url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}beta-{CATALOG_URL_EXTENSION}" + url += f"beta" elif seed_type == SeedType.CustomerSeed: - url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}customerseed-{CATALOG_URL_EXTENSION}" - else: - url = f"{CATALOG_URL_BASE}-{CATALOG_URL_EXTENSION}" + url += f"customerseed" + + did_find_variant: bool = False + for variant in CATALOG_URL_VARIANTS: + if variant in url: + did_find_variant = True + if did_find_variant: + url += f"-{variant}" + + url += f"{CATALOG_URL_EXTENSION}" return url @@ -356,13 +383,10 @@ class RemoteInstallerCatalog: continue if "IntegrityDataURL" not in ia_package: continue - if "Size" not in ia_package: - size = 0 download_link = ia_package["URL"] integrity = ia_package["IntegrityDataURL"] - size = ia_package["Size"] - + size = ia_package["Size"] if ia_package["Size"] else 0 if any([version, build, download_link, size, integrity]) is None: continue @@ -518,28 +542,34 @@ class LocalInstallerCatalog: if "CFBundleDisplayName" not in application_info_plist: continue - app_version = application_info_plist["DTPlatformVersion"] - clean_name = application_info_plist["CFBundleDisplayName"] + app_version: str = application_info_plist["DTPlatformVersion"] + clean_name: str = application_info_plist["CFBundleDisplayName"] + app_sdk: str = application_info_plist["DTSDKBuild"] if "DTSDKBuild" in application_info_plist else "Unknown" + min_required: str = application_info_plist["LSMinimumSystemVersion"] if "LSMinimumSystemVersion" in application_info_plist else "Unknown" - if "DTSDKBuild" in application_info_plist: - app_sdk = application_info_plist["DTSDKBuild"] - else: - app_sdk = "Unknown" + kernel: int = 0 + try: + kernel = int(app_sdk[:2]) + except ValueError: + pass + + min_required = os_data.os_conversion.os_to_kernel(min_required) if min_required != "Unknown" else 0 + + if min_required == os_data.os_data.sierra and kernel == os_data.os_data.ventura: + # Ventura's installer requires El Capitan minimum + # Ref: https://github.com/dortania/OpenCore-Legacy-Patcher/discussions/1038 + min_required = os_data.os_data.el_capitan # app_version can sometimes report GM instead of the actual version # This is a workaround to get the actual version if app_version.startswith("GM"): - try: - app_version = int(app_sdk[:2]) - if app_version < 20: - app_version = f"10.{app_version - 4}" - else: - app_version = f"{app_version - 9}.0" - except ValueError: + if kernel == 0: app_version = "Unknown" + else: + app_version = os_data.os_conversion.kernel_to_os(kernel) # Check if App Version is High Sierra or newer - if os_data.os_conversion.os_to_kernel(app_version) < os_data.os_data.high_sierra: + if kernel < os_data.os_data.high_sierra: continue results = self._parse_sharedsupport_version(Path(APPLICATION_SEARCH_PATH) / Path(application)/ Path("Contents/SharedSupport/SharedSupport.dmg")) @@ -554,6 +584,7 @@ class LocalInstallerCatalog: "Version": app_version, "Build": app_sdk, "Path": application, + "Minimum Host OS": min_required, } }) diff --git a/resources/sys_patch/sys_patch_auto.py b/resources/sys_patch/sys_patch_auto.py index e71d235cc..545c55a7a 100644 --- a/resources/sys_patch/sys_patch_auto.py +++ b/resources/sys_patch/sys_patch_auto.py @@ -312,9 +312,71 @@ class AutomaticSysPatch: utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + # Copy over our launch daemon + if self._create_rsr_monitor_daemon() is True: + logging.info("- Copying rsr-monitor.plist Launch Daemon to /Library/LaunchDaemons/") + if Path("/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist").exists(): + logging.info("- Deleting existing rsr-monitor.plist") + utilities.process_status(utilities.elevated(["rm", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + if not Path("/Library/LaunchDaemons/").exists(): + logging.info("- Creating /Library/LaunchDaemons/") + utilities.process_status(utilities.elevated(["mkdir", "-p", "/Library/LaunchDaemons/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + utilities.process_status(utilities.elevated(["cp", self.constants.rsr_monitor_launch_daemon_path, "/Library/LaunchDaemons/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + # Set the permissions on the com.dortania.opencore-legacy-patcher.rsr-monitor.plist + logging.info("- Setting permissions on rsr-monitor.plist") + utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + # Making app alias # Simply an easy way for users to notice the app # If there's already an alias or exiting app, skip if not Path("/Applications/OpenCore-Patcher.app").exists(): logging.info("- Making app alias") - utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) \ No newline at end of file + utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + + def _create_rsr_monitor_daemon(self) -> bool: + # Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property + # This is used to determine if we need to run the RSRMonitor + logging.info("- Checking if RSRMonitor is needed") + + cryptex_path = f"/System/Volumes/Preboot/{utilities.get_preboot_uuid()}/cryptex1/current/OS.dmg" + if not Path(cryptex_path).exists(): + logging.info("- No OS.dmg, skipping RSRMonitor") + return False + + kexts = [] + for kext in Path("/Library/Extensions").glob("*.kext"): + if not Path(f"{kext}/Contents/Info.plist").exists(): + continue + kext_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb")) + if "GPUCompanionBundles" not in kext_plist: + continue + logging.info(f" - Found kext with GPUCompanionBundles: {kext.name}") + kexts.append(kext.name) + + # If we have no kexts, we don't need to run the RSRMonitor + if not kexts: + logging.info("- No kexts found with GPUCompanionBundles, skipping RSRMonitor") + return False + + # Load the RSRMonitor plist + rsr_monitor_plist = plistlib.load(open(self.constants.rsr_monitor_launch_daemon_path, "rb")) + + arguments = ["rm", "-Rfv"] + arguments += [f"/Library/Extensions/{kext}" for kext in kexts] + + # Add the arguments to the RSRMonitor plist + rsr_monitor_plist["ProgramArguments"] = arguments + + # Next add monitoring for '/System/Volumes/Preboot/{UUID}/cryptex1/OS.dmg' + logging.info(f" - Adding monitor: {cryptex_path}") + rsr_monitor_plist["WatchPaths"] = [ + cryptex_path, + ] + + # Write the RSRMonitor plist + plistlib.dump(rsr_monitor_plist, Path(self.constants.rsr_monitor_launch_daemon_path).open("wb")) + + return True diff --git a/resources/utilities.py b/resources/utilities.py index 335268453..e2b4b903c 100644 --- a/resources/utilities.py +++ b/resources/utilities.py @@ -451,6 +451,15 @@ def monitor_disk_output(disk): return output +def get_preboot_uuid() -> str: + """ + Get the UUID of the Preboot volume + """ + args = ["ioreg", "-a", "-n", "chosen", "-p", "IODeviceTree", "-r"] + output = plistlib.loads(subprocess.run(args, stdout=subprocess.PIPE).stdout) + return output[0]["apfs-preboot-uuid"].strip(b"\0").decode() + + def block_os_updaters(): # Disables any processes that would be likely to mess with # the root volume while we're working with it.