From 41a189444b980d1d80a488f09e1d230464ff3bff Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Thu, 15 Apr 2021 21:07:53 -0600 Subject: [PATCH] Add basic CLI support --- .github/workflows/main.yml | 20 +++++ CHANGELOG.md | 1 + OCLP-CLI.command | 155 +++++++++++++++++++++++++++++++++++++ OCLP-CLI.spec | 34 ++++++++ Resources/SysPatch.py | 30 ++++--- 5 files changed, 230 insertions(+), 10 deletions(-) create mode 100755 OCLP-CLI.command create mode 100644 OCLP-CLI.spec diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c11a33296..66cc99f6d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,3 +43,23 @@ jobs: file: OpenCore-Patcher.app.zip tag: ${{ github.ref }} file_glob: true + build: + name: Build CLI + runs-on: self-hosted + steps: + - uses: actions/checkout@v2 + - run: pyinstaller OCLP-GUI.spec + - run: cd dist; zip ../OCLP-GUI.zip OCLP-GUI + - name: Upload Binary to Artifacts + uses: actions/upload-artifact@v2 + with: + name: OCLP-GUI + path: OCLP-GUI.zip + - name: Upload to Release + if: github.event_name == 'release' + uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: OCLP-GUI.zip + tag: ${{ github.ref }} + file_glob: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0888caef6..965c5924e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Enhance HDMI audio support on Mac Pros and Xserves - Strip unused kext entries during build - Add gfxutil support for better DeviceProperty path detection +- Add basic CLI support ## 0.0.22 - Add ExFat support for models missing driver diff --git a/OCLP-CLI.command b/OCLP-CLI.command new file mode 100755 index 000000000..3bf51fbcc --- /dev/null +++ b/OCLP-CLI.command @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# Copyright (C) 2020-2021 Mykola Grymalyuk + +from __future__ import print_function + +import subprocess +import sys +import time +import platform +import argparse +from pathlib import Path + +from Resources import Build, ModelArray, Constants, SysPatch, Utilities, CliMenu + + +class OpenCoreLegacyPatcher(): + def __init__(self): + self.constants = Constants.Constants() + self.current_model: str = None + opencore_model: str = subprocess.run("nvram 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-product".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() + if not opencore_model.startswith("nvram: Error getting variable"): + opencore_model = [line.strip().split(":oem-product ", 1)[1] for line in opencore_model.split("\n") if line.strip().startswith("4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:")][0] + self.current_model = opencore_model + else: + self.current_model = subprocess.run("system_profiler SPHardwareDataType".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.current_model = [line.strip().split(": ", 1)[1] for line in self.current_model.stdout.decode().split("\n") if line.strip().startswith("Model Identifier")][0] + self.constants.detected_os = int(platform.uname().release.partition(".")[0]) + if self.current_model in ModelArray.NoAPFSsupport: + self.constants.serial_settings = "Moderate" + + # Logic for when user runs custom OpenCore build and do not expose it + # Note: This logic currently only applies for iMacPro1,1 users, see below threads on the culprits: + # - https://forums.macrumors.com/threads/2011-imac-graphics-card-upgrade.1596614/post-17425857 + # - https://forums.macrumors.com/threads/opencore-on-the-mac-pro.2207814/ + # PLEASE FOR THE LOVE OF GOD JUST SET ExposeSensitiveData CORRECTLY!!! + if self.current_model == "iMacPro1,1": + serial: str = subprocess.run("system_profiler SPHardwareDataType | grep Serial".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() + serial = [line.strip().split("Number (system): ", 1)[1] for line in serial.split("\n") if line.strip().startswith("Serial")][0] + true_model = subprocess.run([str(self.constants.macserial_path), "--info", str(serial)], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + true_model = [i.partition(" - ")[2] for i in true_model.stdout.decode().split("\n") if "Model: " in i][0] + print(f"True Model: {true_model}") + if not true_model.startswith("Unknown"): + self.current_model = true_model + + parser = argparse.ArgumentParser() + + # Generic building args + parser.add_argument('--build', help='Build OpenCore', action='store_true', required=False) + parser.add_argument('--verbose', help='Enable verbose boot', action='store_true', required=False) + parser.add_argument('--debug_oc', help='Enable OpenCore DEBUG', action='store_true', required=False) + parser.add_argument('--debug_kext', help='Enable kext DEBUG', action='store_true', required=False) + parser.add_argument('--skip_wifi', help='Skip wifi patches', action='store_true', required=False) + parser.add_argument('--hide_picker', help='Hide OpenCore picker', action='store_true', required=False) + parser.add_argument('--disable_sip', help='Disable SIP', action='store_true', required=False) + parser.add_argument('--disable_smb', help='Disable SecureBootModel', action='store_true', required=False) + parser.add_argument('--vault', help='Enable OpenCore Vaulting', action='store_true', required=False) + + # Building args requiring value values + parser.add_argument('--model', action='store', help='Set custom model', required=False) + parser.add_argument('--metal_gpu', action='store', help='Set Metal GPU Vendor', required=False) + parser.add_argument('--smbios_spoof', action='store', help='Set SMBIOS patching mode', required=False) + + # SysPatch args + parser.add_argument('--patch_sys_vol', help='Patches root volume', action='store_true', required=False) + parser.add_argument('--unpatch_sys_vol', help='Unpatches root volume, EXPERIMENTAL', action='store_true', required=False) + parser.add_argument('--custom_repo', action='store', help='Set SMBIOS patching mode', required=False) + + args = parser.parse_args() + + self.constants.gui_mode = True + self.constants.current_path = Path.cwd() + + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + print("- Rerouting payloads location") + self.constants.payload_path = sys._MEIPASS / Path("payloads") + else: + print("- Using default payloads location") + + if args.verbose: + print("- Set verbose configuration") + self.constants.verbose_debug = True + if args.debug_oc: + print("- Set OpenCore DEBUG configuration") + self.constants.opencore_debug = True + self.constants.opencore_build = "DEBUG" + if args.debug_kext: + print("- Set kext DEBUG configuration") + self.constants.kext_debug = True + if args.skip_wifi: + print("- Set wifi skip configuration") + self.constants.wifi_build = True + if args.hide_picker: + print("- Set HidePicker configuration") + self.constants.showpicker = False + if args.disable_sip: + print("- Set Disable SIP configuration") + self.constants.sip_status = False + if args.disable_smb: + print("- Set Disable SecureBootModel configuration") + self.constants.secure_status = False + if args.vault: + print("- Set Vault configuration") + self.constants.vault = True + if args.metal_gpu: + if args.metal_gpu == "Nvidia": + print("- Set Metal GPU patches to Nvidia") + self.constants.metal_build = True + self.constants.imac_vendor = "Nvidia" + elif args.metal_gpu == "AMD": + print("- Set Metal GPU patches to AMD") + self.constants.metal_build = True + self.constants.imac_vendor = "AMD" + else: + print(f"- Unknown GPU arg passed: {args.metal_gpu}") + self.constants.metal_build = False + self.constants.imac_vendor = "None" + if args.smbios_spoof: + if args.smbios_spoof == "Minimal": + self.constants.serial_settings = "Minimal" + elif args.smbios_spoof == "Moderate": + self.constants.serial_settings = "Moderate" + elif args.smbios_spoof == "Advanced": + self.constants.serial_settings = "Advanced" + else: + print(f"- Unknown SMBIOS arg passed: {args.smbios_spoof}") + + if args.build: + if args.model: + print(f"- Using custom model: {args.model}") + self.constants.custom_model = args.model + self.build_opencore() + else: + print(f"- Using detected model: {self.current_model}") + self.build_opencore() + if args.patch_sys_vol: + print("- Set System Volume patching") + if args.custom_repo: + self.constants.url_apple_binaries = args.custom_repo + print(f"- Custom set repo to: {self.constants.url_apple_binaries}") + SysPatch.PatchSysVolume(self.constants.custom_model or self.current_model, self.constants).start_patch() + elif args.unpatch_sys_vol: + print("- Set System Volume unpatching") + SysPatch.PatchSysVolume(self.constants.custom_model or self.current_model, self.constants).start_unpatch() + + + def build_opencore(self): + Build.BuildOpenCore(self.constants.custom_model or self.current_model, self.constants).build_opencore() + + def install_opencore(self): + Build.BuildOpenCore(self.constants.custom_model or self.current_model, self.constants).copy_efi() + +OpenCoreLegacyPatcher() + +# Example arg for OCLP command line +# ./OCLP --build --verbose --debug_oc --debug_kext --model iMac11,2 \ No newline at end of file diff --git a/OCLP-CLI.spec b/OCLP-CLI.spec new file mode 100644 index 000000000..31e1f3ce8 --- /dev/null +++ b/OCLP-CLI.spec @@ -0,0 +1,34 @@ +# -*- mode: python ; coding: utf-8 -*- +import sys, os +sys.path.append(os.path.abspath(os.getcwd())) +from Resources import Constants +block_cipher = None + + +a = Analysis(['OCLP-CLI.command'], + binaries=[], + datas=[('payloads', 'payloads'), ('Resources', 'Resources')], + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='OCLP-CLI', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True ) \ No newline at end of file diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 0e61ddbda..11efd638d 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -256,10 +256,12 @@ class PatchSysVolume: subprocess.run(f"sudo bless --mount {self.mount_location} --bootefi --last-sealed-snapshot".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode() def rebuild_snapshot(self): - input("Press [ENTER] to continue with cache rebuild") + if self.constants.gui_mode is False: + input("Press [ENTER] to continue with cache rebuild") print("- Rebuilding Kernel Cache (This may take some time)") subprocess.run(f"sudo kmutil install --volume-root {self.mount_location} --update-all".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode() - input("Press [ENTER] to continue with snapshotting") + if self.constants.gui_mode is False: + input("Press [ENTER] to continue with snapshotting") print("- Creating new APFS snapshot") subprocess.run(f"sudo bless --folder {self.mount_location}/System/Library/CoreServices --bootefi --create-snapshot".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode() @@ -295,9 +297,12 @@ class PatchSysVolume: def check_files(self): if Path(self.constants.payload_apple_root_path).exists(): print("- Found Apple Binaries") - patch_input = input("Would you like to redownload?(y/n): ") - if patch_input in {"y", "Y", "yes", "Yes"}: - shutil.rmtree(Path(self.constants.payload_apple_root_path)) + if self.constants.gui_mode is False: + patch_input = input("Would you like to redownload?(y/n): ") + if patch_input in {"y", "Y", "yes", "Yes"}: + shutil.rmtree(Path(self.constants.payload_apple_root_path)) + self.download_files() + else: self.download_files() else: print("- Apple binaries missing") @@ -319,7 +324,8 @@ class PatchSysVolume: os.rename(self.constants.payload_apple_root_path_unzip, self.constants.payload_apple_root_path) print("- Binaries downloaded to:") print(self.constants.payload_path) - input("Press [ENTER] to continue") + if self.constants.gui_mode is False: + input("Press [ENTER] to continue") except zipfile.BadZipFile: print("- Couldn't unzip") os.remove(self.constants.payload_apple_root_path_zip) @@ -342,7 +348,8 @@ class PatchSysVolume: Utilities.cls() if (self.sip_patch_status is False) and (self.smb_status is False): print("- Detected SIP and SecureBootModel are disabled, continuing") - input("\nPress [ENTER] to continue") + if self.constants.gui_mode is False: + input("\nPress [ENTER] to continue") self.check_files() if self.constants.payload_apple_root_path.exists(): self.find_mount_root_vol(True) @@ -362,7 +369,8 @@ class PatchSysVolume: print("FileVault enabled, unable to patch!") print("Please disable FileVault in System Preferences") print("") - input("Press [Enter] to go exit.") + if self.constants.gui_mode is False: + input("Press [Enter] to go exit.") def start_unpatch(self): if self.constants.custom_model is not None: @@ -374,7 +382,8 @@ class PatchSysVolume: Utilities.cls() if (self.sip_patch_status is False) and (self.smb_status is False): print("- Detected SIP and SecureBootModel are disabled, continuing") - input("\nPress [ENTER] to continue") + if self.constants.gui_mode is False: + input("\nPress [ENTER] to continue") self.find_mount_root_vol(False) self.unmount_drive() print("- Unpatching complete") @@ -392,4 +401,5 @@ class PatchSysVolume: print("FileVault enabled, unable to unpatch!") print("Please disable FileVault in System Preferences") print("") - input("Press [Enter] to go exit.") + if self.constants.gui_mode is False: + input("Press [Enter] to go exit.")